1 /* Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved.
2 
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License, version 2.0,
5 as published by the Free Software Foundation.
6 
7 This program is also distributed with certain software (including
8 but not limited to OpenSSL) that is licensed under separate terms,
9 as designated in a particular file or component or in included license
10 documentation.  The authors of MySQL hereby grant you an additional
11 permission to link the program and your derivative works with the
12 separately licensed software that they have included with MySQL.
13 
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 GNU General Public License, version 2.0, for more details.
18 
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA */
22 
23 #include <string.h>
24 #include <sys/types.h>
25 
26 #include <mysql/components/minimal_chassis.h>
27 #include "m_ctype.h"
28 #include "my_inttypes.h"
29 #include "my_sys.h"
30 #include "mysql/components/service_implementation.h"
31 #include "mysql/components/services/mysql_string.h"
32 #include "mysql/psi/psi_memory.h"
33 #include "mysql/service_mysql_alloc.h"
34 #include "mysql_string_service_imp.h"
35 #include "sql_string.h"
36 
37 PSI_memory_key key_memory_string_service_iterator;
38 
39 /**
40   The string functions as a service to the mysql_server component.
41   So, that by default this service is available to all the components
42   register to the server.
43 */
44 
45 struct my_h_string_imp {};
46 
47 struct my_h_string_iterator_imp {};
48 
49 /**
50   Creates a new instance of string object
51 
52   @param out_string holds pointer to newly created string object.
53   @return Status of performed operation
54   @retval false success
55   @retval true failure
56 */
57 DEFINE_BOOL_METHOD(mysql_string_imp::create, (my_h_string * out_string)) {
58   try {
59     String *res = new String[1];
60     *out_string = (my_h_string)res;
61     return false;
62   } catch (...) {
63     mysql_components_handle_std_exception(__func__);
64   }
65   return true;
66 }
67 
68 /**
69   Convert a String pointed by handle to lower case. Conversion depends on the
70   client character set info
71 
72   @param out_string Holds the converted lower case string object.
73   @param in_string Pointer to string object to be converted.
74   @return Status of performed operation
75   @retval false success
76   @retval true failure
77 */
78 DEFINE_BOOL_METHOD(mysql_string_imp::tolower,
79                    (my_h_string * out_string, my_h_string in_string)) {
80   try {
81     String *str = reinterpret_cast<String *>(in_string);
82     if (str == nullptr) return true;
83     String *res = reinterpret_cast<String *>(*out_string);
84     const CHARSET_INFO *cs = str->charset();
85     if (cs->casedn_multiply == 1) {
86       res->copy(*str);
87       my_casedn_str(cs, res->c_ptr_quick());
88     } else {
89       size_t len = str->length() * cs->casedn_multiply;
90       res->set_charset(cs);
91       res->alloc(len);
92       len = cs->cset->casedn(cs, str->ptr(), str->length(), res->ptr(), len);
93       res->length(len);
94     }
95     *out_string = (my_h_string)res;
96     return false;
97   } catch (...) {
98     mysql_components_handle_std_exception(__func__);
99   }
100   return true;
101 }
102 
103 /**
104   Convert a String pointed by handle to upper case. Conversion depends on the
105   client character set info
106 
107   @param out_string Holds the converted lower case string object.
108   @param in_string Pointer to string object to be converted.
109   @return Status of performed operation
110   @retval false success
111   @retval true failure
112 */
113 DEFINE_BOOL_METHOD(mysql_string_imp::toupper,
114                    (my_h_string * out_string, my_h_string in_string)) {
115   try {
116     String *str = reinterpret_cast<String *>(in_string);
117     if (str == nullptr) return true;
118     String *res = reinterpret_cast<String *>(*out_string);
119     const CHARSET_INFO *cs = str->charset();
120     if (cs->caseup_multiply == 1) {
121       res->copy(*str);
122       my_caseup_str(cs, res->c_ptr_quick());
123     } else {
124       size_t len = str->length() * cs->caseup_multiply;
125       res->set_charset(cs);
126       res->alloc(len);
127       len = cs->cset->caseup(cs, str->ptr(), str->length(), res->ptr(), len);
128       res->length(len);
129     }
130     *out_string = (my_h_string)res;
131     return false;
132   } catch (...) {
133     mysql_components_handle_std_exception(__func__);
134   }
135   return true;
136 }
137 
138 /**
139   Allocates a string object and converts the character buffer to string
140   and just sets the specified charset_name in the string object. It does
141   not performs the conversion of buffer into the specified character set.
142   Caller must free the allocated string by calling destroy() api.
143 
144   @param [out] out_string Pointer to string object handle to set new string
145     to.
146   @param in_buffer Pointer to the buffer with data to be interpreted as
147     string.
148   @param length Length of the buffer to copy in bytes, not in character count.
149   @param charset_name Handle to charset that is used for convertion.
150   @return Status of performed operation
151   @retval false success
152   @retval true failure
153 */
154 DEFINE_BOOL_METHOD(mysql_string_imp::convert_from_buffer,
155                    (my_h_string * out_string, const char *in_buffer,
156                     uint64 length, const char *charset_name)) {
157   try {
158     if (in_buffer == nullptr || length == 0 || length > strlen(in_buffer))
159       return true;
160 
161     String *res = new String[1];
162     CHARSET_INFO *cs =
163         get_charset_by_csname(charset_name, MY_CS_PRIMARY, MYF(0));
164 
165     if (!cs || res->copy(in_buffer, length, cs)) return true;
166     *out_string = (my_h_string)res;
167     return false;
168   } catch (...) {
169     mysql_components_handle_std_exception(__func__);
170   }
171   return true;
172 }
173 
174 /**
175   converts the mysql_string to the character buffer specified by
176   charset_name parameter.
177 
178   @param [out] out_buffer Pointer to char buffer used to hold the converted
179     string.
180   @param in_string pointer to string handle which will be converted to char
181     data.
182   @param length Length of the buffer to copy in bytes, not in character count.
183   @param charset_name Handle to charset that is used for convertion.
184   @return Status of performed operation
185   @retval false success
186   @retval true failure
187 */
188 DEFINE_BOOL_METHOD(mysql_string_imp::convert_to_buffer,
189                    (my_h_string in_string, char *out_buffer, uint64 length,
190                     const char *charset_name)) {
191   try {
192     String *str = reinterpret_cast<String *>(in_string);
193     uint error;
194     CHARSET_INFO *cs =
195         get_charset_by_csname(charset_name, MY_CS_PRIMARY, MYF(0));
196     if (str == nullptr || cs == nullptr || length == 0) return true;
197     size_t len = my_convert(out_buffer, length - 1, cs, str->ptr(),
198                             str->length(), str->charset(), &error);
199     out_buffer[len] = '\0';
200 
201     return false;
202   } catch (...) {
203     mysql_components_handle_std_exception(__func__);
204   }
205   return true;
206 }
207 
208 /**
209   Destroys specified string object and data contained by it.
210 
211   @param string String object handle to release.
212   @return Status of performed operation
213   @retval false success
214   @retval true failure
215 */
216 DEFINE_METHOD(void, mysql_string_imp::destroy, (my_h_string string)) {
217   try {
218     String *str = reinterpret_cast<String *>(string);
219     if (str == nullptr) return;
220     str->mem_free();
221     delete[] str;
222   } catch (...) {
223     mysql_components_handle_std_exception(__func__);
224   }
225 }
226 
227 /**
228   Gets character code of character on specified index position in
229   string to a specified buffer.
230 
231   @param string String object handle to get character from.
232   @param index Index, position of character to query.
233   @param [out] out_char Pointer to long value to store character to.
234   @return Status of performed operation
235   @retval false success
236   @retval true failure
237 */
238 DEFINE_BOOL_METHOD(mysql_string_imp::get_char,
239                    (my_h_string string, uint index, ulong *out_char)) {
240   try {
241     String *str = reinterpret_cast<String *>(string);
242     if (str == nullptr || index >= str->length()) return true;
243     my_charset_conv_mb_wc mb_wc = (str->charset())->cset->mb_wc;
244     int ret = str->charpos(index);
245     if (ret < 0) return true;
246     const char *ptr = (str->ptr() + ret);
247     if ((*mb_wc)(str->charset(), out_char, pointer_cast<const uchar *>(ptr),
248                  (const uchar *)(str->ptr() + str->length())) <= 0)
249       return true;
250 
251     return false;
252   } catch (...) {
253     mysql_components_handle_std_exception(__func__);
254   }
255   return true;
256 }
257 
258 /**
259   Gets length of specified string expressed as number of characters.
260 
261   @param string String object handle to get length of.
262   @param [out] out_length Pointer to 32bit value to store length of string to.
263   @return Status of performed operation
264   @retval false success
265   @retval true failure
266 */
267 DEFINE_BOOL_METHOD(mysql_string_imp::get_char_length,
268                    (my_h_string string, uint *out_length)) {
269   try {
270     String *str = reinterpret_cast<String *>(string);
271     if (str == nullptr) return true;
272     *out_length = str->numchars();
273     return false;
274   } catch (...) {
275     mysql_components_handle_std_exception(__func__);
276   }
277   return true;
278 }
279 
280 /**
281   Gets byte code of string on specified index position in
282   string to a specified 32-bit buffer.
283 
284   @param string String object handle to get character from.
285   @param index Index, position of character to query.
286   @param [out] out_char Pointer to 32bit value to store byte to.
287   @return Status of performed operation
288   @retval false success
289   @retval true failure
290 */
291 DEFINE_BOOL_METHOD(mysql_string_imp::get_byte,
292                    (my_h_string string, uint index, uint *out_char)) {
293   try {
294     String *str = reinterpret_cast<String *>(string);
295     if (str == nullptr || index >= str->length()) return true;
296 
297     const char *ptr = str->ptr();
298     if (ptr == nullptr) return true;
299     *out_char = ptr[index];
300     return false;
301   } catch (...) {
302     mysql_components_handle_std_exception(__func__);
303   }
304   return true;
305 }
306 
307 /**
308   Gets length of specified string expressed as number of bytes.
309 
310   @param string String object handle to get length of.
311   @param [out] out_length Pointer to 32bit value to store length of string to.
312   @return Status of performed operation
313   @retval false success
314   @retval true failure
315 */
316 DEFINE_BOOL_METHOD(mysql_string_imp::get_byte_length,
317                    (my_h_string string, uint *out_length)) {
318   try {
319     String *str = reinterpret_cast<String *>(string);
320     if (str == nullptr) return true;
321     *out_length = str->length();
322     return false;
323   } catch (...) {
324     mysql_components_handle_std_exception(__func__);
325   }
326   return true;
327 }
328 
329 /**
330   Creates an iterator for a specified string to allow iteration through all
331     characters in the string.
332 
333   @param string String object handle to get iterator to.
334   @param [out] out_iterator Pointer to string iterator handle to store result
335     object to.
336   @return Status of performed operation
337   @retval false success
338   @retval true failure
339 */
340 DEFINE_BOOL_METHOD(mysql_string_imp::iterator_create,
341                    (my_h_string string, my_h_string_iterator *out_iterator)) {
342   try {
343     String *str = reinterpret_cast<String *>(string);
344     if (str == nullptr) return true;
345     st_string_iterator *iterator = (st_string_iterator *)my_malloc(
346         key_memory_string_service_iterator, sizeof(st_string_iterator), MYF(0));
347     iterator->iterator_str = str;
348     iterator->iterator_ptr = str->ptr();
349     iterator->ctype = 0;
350     *out_iterator = (my_h_string_iterator)iterator;
351 
352     return false;
353   } catch (...) {
354     mysql_components_handle_std_exception(__func__);
355   }
356   return true;
357 }
358 
359 /**
360   Retrieves character code at current iterator position and advances the
361     iterator.
362 
363   @param iter String iterator object handle to advance.
364   @param [out] out_char Pointer to 32bit value to store character to. May be
365     NULL to omit retrieval of character and just advance the iterator.
366   @return Status of performed operation
367   @retval false success
368   @retval true failure
369 */
370 DEFINE_BOOL_METHOD(mysql_string_imp::iterator_get_next,
371                    (my_h_string_iterator iter, int *out_char)) {
372   try {
373     int char_len, tmp_len;
374     st_string_iterator *iterator = (st_string_iterator *)iter;
375     if (iterator == nullptr) return true;
376     const String *str = iterator->iterator_str;
377     const CHARSET_INFO *cs = str->charset();
378     const char *end = str->ptr() + str->length();
379     *out_char = 0;
380     if (iterator->iterator_ptr >= end) return true;
381     char_len = (cs->cset->ctype(
382         cs, out_char, pointer_cast<const uchar *>(iterator->iterator_ptr),
383         pointer_cast<const uchar *>(end)));
384     iterator->ctype = *out_char;
385     tmp_len = (char_len > 0 ? char_len : (char_len < 0 ? -char_len : 1));
386     if (iterator->iterator_ptr + tmp_len > end)
387       return true;
388     else
389       iterator->iterator_ptr += tmp_len;
390     return false;
391   } catch (...) {
392     mysql_components_handle_std_exception(__func__);
393   }
394   return true;
395 }
396 
397 /**
398   Releases the string iterator object specified.
399 
400   @param iter String iterator object handle te release.
401   @return Status of performed operation
402   @retval false success
403   @retval true failure
404 */
405 DEFINE_METHOD(void, mysql_string_imp::iterator_destroy,
406               (my_h_string_iterator iter)) {
407   try {
408     if (iter == nullptr) return;
409     my_free((st_string_iterator *)iter);
410   } catch (...) {
411     mysql_components_handle_std_exception(__func__);
412   }
413 }
414 
415 /**
416   Checks if character on current position the iterator points to is an upper
417   case.
418 
419   @param iter String iterator object handle to advance.
420   @param [out] out Pointer to bool value to store if character is an upper
421     case.
422   @return Status of performed operation
423   @retval false success
424   @retval true failure
425 */
426 DEFINE_BOOL_METHOD(mysql_string_imp::is_upper,
427                    (my_h_string_iterator iter, bool *out)) {
428   try {
429     st_string_iterator *iterator = (st_string_iterator *)iter;
430     if (iterator == nullptr) return true;
431     *out = (iterator->ctype & _MY_U);
432     return false;
433   } catch (...) {
434     mysql_components_handle_std_exception(__func__);
435   }
436   return true;
437 }
438 
439 /**
440   Checks if character on current position the iterator points to is a lower
441   case.
442 
443   @param iter String iterator object handle to advance.
444   @param [out] out Pointer to bool value to store if character is a lower
445     case.
446   @return Status of performed operation
447   @retval false success
448   @retval true failure
449 */
450 DEFINE_BOOL_METHOD(mysql_string_imp::is_lower,
451                    (my_h_string_iterator iter, bool *out)) {
452   try {
453     st_string_iterator *iterator = (st_string_iterator *)iter;
454     if (iterator == nullptr) return true;
455     *out = (iterator->ctype & _MY_L);
456     return false;
457   } catch (...) {
458     mysql_components_handle_std_exception(__func__);
459   }
460   return true;
461 }
462 
463 /**
464   Checks if character on current position the iterator points to is a digit.
465 
466   @param iter String iterator object handle to advance.
467   @param [out] out Pointer to bool value to store if character is a digit.
468   @return Status of performed operation
469   @retval false success
470   @retval true failure
471 */
472 DEFINE_BOOL_METHOD(mysql_string_imp::is_digit,
473                    (my_h_string_iterator iter, bool *out)) {
474   try {
475     st_string_iterator *iterator = (st_string_iterator *)iter;
476     if (iterator == nullptr) return true;
477     *out = (iterator->ctype & _MY_NMR);
478     return false;
479   } catch (...) {
480     mysql_components_handle_std_exception(__func__);
481   }
482   return true;
483 }
484