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