00001 00002 #include "skype-string.h" 00003 00004 #ifndef _GNU_SOURCE 00005 #define _GNU_SOURCE 00006 #endif 00007 00008 #include <stdlib.h> 00009 #include <string.h> 00010 #include <stdio.h> 00011 00012 #ifdef _WIN32 00013 #ifndef snprintf 00014 #define snprintf _snprintf 00015 #endif 00016 #ifndef strtoull 00017 #define strtoull _strtoui64 00018 #endif 00019 #endif 00020 00021 /** \cond INTERNAL */ 00022 // Tune for "malloc count" vs. "total memory allocation" tradeoff 00023 // Affects mostly string concatenation 00024 // 0 means no preallocation 00025 #ifndef SE_STRING_DELTA 00026 #define SE_STRING_DELTA 0 00027 #endif 00028 00029 // Used to adjust the malloc-ed size to the highest multiple of 16 00030 #define CALC_SIZE(x) ((x) / 16 + 1) * 16 + 16 * SE_STRING_DELTA 00031 // NB: original version: #define CALC_SIZE(x) (x) + 25 00032 00033 class SEString::Data 00034 { 00035 public: 00036 unsigned int ref; 00037 char* str; 00038 size_t real_size; 00039 bool isBin; // when true, the string contains binary data 00040 SEMutex lock; 00041 }; 00042 00043 SEString::SEString(unsigned int buf) 00044 { 00045 d = new Data(); 00046 00047 d->ref = 1; 00048 // we don't add DELTA since this constructor is always called with the exact size of the string 00049 d->str = (char*)malloc(buf + 1); 00050 d->real_size = buf; 00051 d->isBin = false; 00052 } 00053 00054 // char escaped 00055 // " \" 00056 // , \, (don't forget to escape commas as they are used to mark lists) 00057 // \ \ \ (slash slash) 00058 // 0x00 \0 (all null characters are translated to slash and zero (number zero)) 00059 00060 SEString SEString::escape() const 00061 { 00062 if (isNull()) 00063 return *this; 00064 00065 size_t new_size = 0; 00066 size_t mylen = length(); 00067 00068 for (size_t n = 0; n < mylen; n++) { 00069 if ((',' == d->str[n]) || ('"' == d->str[n]) ||('\\' == d->str[n])) 00070 new_size++; 00071 } 00072 00073 new_size += mylen; 00074 00075 SEString str(new_size + 1); 00076 00077 char* s_from = d->str; 00078 char* to = str.d->str; 00079 00080 while (*s_from) { 00081 if ((',' == *s_from) || ('"' == *s_from) || ('\\' == *s_from)) { 00082 *to = '\\'; 00083 to++; 00084 } 00085 00086 *to = *s_from; 00087 00088 to++; 00089 s_from++; 00090 } 00091 00092 *to = '\0'; 00093 00094 return str; 00095 } 00096 00097 SEString SEString::unescape() const 00098 { 00099 if (isNull()) 00100 return *this; 00101 00102 size_t mylen = length(); 00103 00104 SEString str(mylen + 1); 00105 00106 size_t to = 0; 00107 size_t cur = 0; 00108 size_t len = mylen; 00109 00110 while (cur < len) { 00111 if ('\\' == d->str[cur]) { 00112 cur++; 00113 } 00114 00115 str.d->str[to++] = d->str[cur++]; 00116 } 00117 00118 str.d->str[to++] = '\0'; 00119 00120 return str; 00121 } 00122 00123 // custom function to optimize SEStringDict::format 00124 const SEString SEString::keyValue(const SEString& key, const SEString& value) // static 00125 { 00126 if (key.isNull()) 00127 return SEString(); 00128 00129 size_t keylen = key.length(); 00130 size_t vallen = value.length(); 00131 00132 SEString str(keylen + 2 + vallen + 3); 00133 00134 char * ptr = str.d->str; 00135 00136 strcpy(ptr, key.d->str); 00137 ptr += keylen; 00138 strcpy(ptr, "=\""); 00139 ptr += 2; 00140 if (!value.isNull()) { // or should we not send the pair at all? 00141 strcpy(ptr, value.d->str); 00142 ptr += vallen; 00143 } 00144 strcpy(ptr, "\" "); 00145 00146 return str; 00147 } 00148 00149 void SEString::markAsBinary() 00150 { 00151 if (d) d->isBin = true; 00152 } 00153 /** \endcond */ 00154 00155 SEString::operator const char*() const 00156 { 00157 static const char* str_null = ""; 00158 00159 if (isNull()) 00160 return str_null; 00161 else 00162 return d->str; 00163 } 00164 00165 size_t SEString::length() const 00166 { 00167 if (isNull()) 00168 return 0; 00169 else 00170 return strlen(d->str); 00171 } 00172 00173 bool SEString::isBinary() const 00174 { 00175 return (d && d->isBin); 00176 } 00177 00178 bool SEString::startWith(const SEString& str) const 00179 { 00180 if (isNull()) 00181 return false; 00182 00183 size_t strlen = str.length(); 00184 00185 if (!strlen) // same as (str.isEmpty()) 00186 return true; 00187 00188 return (0 == strncmp(d->str, str.d->str, strlen)); 00189 } 00190 00191 SEString SEString::substr(int s_from, int to) const 00192 { 00193 if (isNull()) 00194 return *this; 00195 00196 if (s_from < 0) 00197 s_from = 0; 00198 00199 size_t len = length(); 00200 00201 if (to >= int(len)) 00202 to = len - 1 ; 00203 00204 if (to < 0) { 00205 to = len + to - 1; 00206 } 00207 00208 if (to < s_from) { 00209 int tmp = to; 00210 to = s_from; 00211 s_from = tmp; 00212 } 00213 00214 int newl = to - s_from + 1; 00215 00216 SEString tmp(newl + 1); 00217 00218 strncpy(tmp.d->str, d->str + s_from, newl); 00219 tmp.d->str[newl] = 0; 00220 00221 return tmp; 00222 } 00223 00224 SEString SEString::right(unsigned int len) const 00225 { 00226 if (isNull()) 00227 return *this; 00228 00229 size_t mylen = length(); 00230 if (len > mylen) 00231 len = mylen; 00232 00233 SEString tmp(len + 1); 00234 00235 strcpy(tmp.d->str, d->str + mylen - len); 00236 00237 return tmp; 00238 } 00239 00240 SEString SEString::trim(const SEString& str) const 00241 { 00242 size_t strlen = str.length(); 00243 00244 if (!strlen || !startWith(str)) // same as (str.isEmpty() || !startWith(str)) 00245 return *this; 00246 else 00247 return right(length() - strlen); 00248 } 00249 00250 const SEString SEString::from(int n) // static 00251 { 00252 char res[12]; 00253 00254 snprintf(res, 12, "%d", n); 00255 00256 return SEString(res); 00257 } 00258 00259 const SEString SEString::from(uint64 n) // static 00260 { 00261 char res[24]; 00262 00263 snprintf(res, 24, "%llu", n); 00264 00265 return SEString(res); 00266 } 00267 00268 const SEString SEString::from(unsigned char chr) // static 00269 { 00270 char res[2]; 00271 00272 res[0] = chr; 00273 res[1] = '\0'; 00274 00275 return SEString(res); 00276 } 00277 00278 const SEString SEString::from(unsigned int u, unsigned int base) // static 00279 { 00280 char res[11]; 00281 00282 if (16 == base) 00283 snprintf(res, 11, "%X", u); 00284 else 00285 snprintf(res, 11, "%u", u); 00286 00287 return SEString(res); 00288 } 00289 00290 const SEString SEString::from(bool b) // static 00291 { 00292 char res[2]; 00293 00294 res[1] = '\0'; 00295 00296 if (b) 00297 res[0] = '1'; 00298 else 00299 res[0] = '0'; 00300 00301 return SEString(res); 00302 } 00303 00304 const SEString SEString::from(char *bin, unsigned int len) // static 00305 { 00306 // For binaries, we escape immediately to allow it to survive all the string operations 00307 size_t new_size = 0; 00308 unsigned int i; 00309 00310 for (i = 0; i < len; i++) { 00311 if (('\0' == bin[i]) || ('\\' == bin[i]) || (',' == bin[i]) || ('"' == bin[i])) 00312 new_size++; 00313 } 00314 new_size += len; 00315 00316 SEString str(new_size + 1); // 1 for end of string null char 00317 str.markAsBinary(); 00318 00319 char* to = str.d->str; 00320 00321 for (i = 0; i < len; i++) { 00322 if ('\0' == bin[i]) { // null is escaped differently 00323 *to = '\\'; 00324 to++; 00325 *to = '0'; 00326 to++; 00327 continue; 00328 } 00329 // normal escaping like in escape() 00330 if ((',' == bin[i]) || ('"' == bin[i]) || ('\\' == bin[i])) { 00331 *to = '\\'; 00332 to++; 00333 } 00334 *to = bin[i]; 00335 to++; 00336 } 00337 00338 *to = '\0'; 00339 00340 return str; 00341 } 00342 00343 bool SEString::toBool() const 00344 { 00345 if (isNull()) 00346 return false; 00347 00348 return atoi(d->str) != 0; 00349 } 00350 00351 int SEString::toInt() const 00352 { 00353 if (isNull()) 00354 return 0; 00355 00356 return atoi(d->str); 00357 } 00358 00359 unsigned int SEString::toUInt() const 00360 { 00361 if (isNull()) 00362 return 0; 00363 00364 return strtoul(d->str, NULL, 10); 00365 } 00366 00367 SEString::uint64 SEString::toUInt64() const 00368 { 00369 if (isNull()) 00370 return 0; 00371 00372 return strtoull(d->str, NULL, 10); 00373 } 00374 00375 size_t SEString::toBinary(char *bin) const 00376 { 00377 if (isNull()) 00378 return 0; 00379 00380 // Check we actually have a binary 00381 if (!isBinary()) 00382 return 0; 00383 00384 // For a binary, we didn't unescape in the low-level SEStringDict::parse, but we do it here only 00385 size_t to = 0; 00386 size_t cur = 0; 00387 size_t len = length(); 00388 00389 while (cur < len) { 00390 if ('\\' == d->str[cur]) { 00391 cur++; 00392 00393 if ('0' == d->str[cur]) { // escaped null 00394 bin[to++] = '\0'; 00395 cur++; 00396 continue; 00397 } 00398 } 00399 00400 bin[to++] = d->str[cur++]; 00401 } 00402 00403 return to; 00404 } 00405 00406 SEString SEString::getHexRepresentation() const 00407 { 00408 // check we actually have a binary 00409 if (!isBinary()) return SEString(); 00410 // convert to binary 00411 char * ps = (char *)malloc(length()); 00412 size_t len = 2 * toBinary(ps); 00413 // convert to hexa 00414 static const char hex[17] = "0123456789abcdef"; 00415 SEString str(len); 00416 unsigned char *ptr = (unsigned char *)ps; 00417 unsigned int count = 0; 00418 for (; count < len; ptr++) { 00419 str.d->str[count++] = hex[*ptr >> 4]; 00420 str.d->str[count++] = hex[*ptr & 0xf]; 00421 } 00422 str.d->str[count] = 0; 00423 // free temp buf 00424 free(ps); 00425 return str; 00426 } 00427 00428 SEString& SEString::operator+=(const SEString &str) 00429 { 00430 if (isNull()) 00431 return (*this = str); 00432 if (str.isNull()) 00433 return (*this); 00434 00435 detach(); 00436 00437 size_t l = length(); 00438 00439 char* str_new = se_realloc(l + str.length() + 1); 00440 00441 if (str_new) { 00442 strcpy(str_new + l, str.d->str); 00443 00444 d->str = str_new; 00445 } 00446 00447 return *this; 00448 } 00449 00450 SEString& SEString::operator+=(const char* str) 00451 { 00452 if (isNull()) 00453 return (*this = str); 00454 if (str == NULL) 00455 return (*this); 00456 00457 detach(); 00458 00459 size_t l = length(); 00460 size_t l2 = strlen(str); 00461 00462 char* str_new = se_realloc(l + l2 + 1); 00463 00464 if (str_new) { 00465 strcpy(str_new + l, str); 00466 00467 d->str = str_new; 00468 } 00469 00470 return *this; 00471 } 00472 00473 SEString& SEString::operator=(const SEString& str) 00474 { 00475 Data* strd = const_cast<SEString&>(str).d_ref(); // shall be mutable 00476 d_unref(); 00477 d = strd; 00478 00479 return *this; 00480 } 00481 00482 SEString& SEString::operator=(const char* str) 00483 { 00484 d_unref(); 00485 00486 if (str) { 00487 d = new Data(); 00488 d->ref = 1; 00489 d->real_size = CALC_SIZE(strlen(str) + 1); 00490 d->str = (char*)malloc(d->real_size); 00491 strcpy(d->str, str); 00492 d->isBin = false; 00493 } else { 00494 d = 0; 00495 } 00496 00497 return *this; 00498 } 00499 00500 /** \cond INTERNAL */ 00501 SEString::Data* SEString::d_ref() 00502 { 00503 SEMutexLock lock_string(mutex); 00504 if (0 == d) 00505 return d; 00506 00507 d->lock.Acquire(); 00508 00509 d->ref++; 00510 00511 d->lock.Release(); 00512 return d; 00513 } 00514 00515 void SEString::d_unref() 00516 { 00517 SEMutexLock lock_string(mutex); 00518 if (0 == d) 00519 return; 00520 00521 SEMutex* lock = &d->lock; 00522 00523 lock->Acquire(); 00524 00525 if (d->ref > 1) 00526 d->ref--; 00527 else { 00528 Data* tmp_d = d; 00529 d = 0; 00530 lock->Release(); 00531 free(tmp_d->str); 00532 delete tmp_d; 00533 return; 00534 } 00535 00536 lock->Release(); 00537 } 00538 00539 void SEString::detach() 00540 { 00541 SEMutexLock lock_string(mutex); 00542 if (0 == d) 00543 return; 00544 00545 SEMutex* lock = &d->lock; 00546 lock->Acquire(); 00547 00548 if (1 != d->ref) { 00549 00550 d->ref--; 00551 00552 size_t sz = CALC_SIZE(strlen(d->str) + 1); 00553 char* buf = (char*)malloc(sz); 00554 strcpy(buf, d->str); 00555 lock->Release(); 00556 00557 Data* d_new = new Data(); 00558 d_new->ref = 1; 00559 d_new->real_size = sz; 00560 d_new->str = buf; 00561 d_new->isBin = d->isBin; 00562 00563 d = d_new; 00564 return; 00565 } 00566 00567 lock->Release(); 00568 } 00569 /** \endcond */ 00570 00571 SEString SEString::deepCopy() const 00572 { 00573 SEString tmp = *this; 00574 00575 tmp.detach(); 00576 00577 return tmp; 00578 } 00579 00580 unsigned int SEString::hash(unsigned int hash_size) const 00581 { 00582 if (isNull()) 00583 return 0; 00584 00585 const char *ptr; 00586 unsigned int val; 00587 00588 val = 0; 00589 ptr = d->str; 00590 00591 while (*ptr) { 00592 int tmp; 00593 val = (val << 4) + (*ptr); 00594 tmp = (val & 0xf0000000); 00595 if (tmp) { 00596 val = val ^ (tmp >> 24); 00597 val = val ^ tmp; 00598 } 00599 00600 ptr++; 00601 } 00602 00603 return val % hash_size; 00604 } 00605 00606 bool SEString::equals(const SEString& str) const 00607 { 00608 if (isNull() && str.isNull()) 00609 return true; 00610 00611 else if (isNull() || str.isNull()) 00612 return false; 00613 00614 return (0 == strcmp(d->str, str.d->str)); 00615 } 00616 00617 bool SEString::equals(const char* str) const 00618 { 00619 if (isNull() && (0 == str)) 00620 return true; 00621 00622 if (isNull() || (0 == str)) 00623 return false; 00624 00625 return (0 == strcmp(str, d->str)); 00626 } 00627 00628 int SEString::find(char c) const 00629 { 00630 if (isNull()) 00631 return -1; 00632 00633 char* found = strchr(d->str, c); 00634 00635 if (0 == found) 00636 return -1; 00637 00638 return (found - d->str); 00639 } 00640 00641 int SEString::find(int startpos, char c) const 00642 { 00643 if (isNull()) 00644 return -1; 00645 00646 char* found = strchr(d->str+startpos, c); 00647 00648 if (0 == found) 00649 return -1; 00650 00651 return (found - d->str); 00652 } 00653 00654 /** \cond INTERNAL */ 00655 char* SEString::se_realloc(size_t new_size) 00656 { 00657 if (d->real_size >= new_size) 00658 return d->str; 00659 else { 00660 d->real_size = CALC_SIZE(new_size); 00661 00662 return (char*)::realloc(d->str, d->real_size); 00663 } 00664 } 00665 /** \endcond */ 00666 00667 void SEString::Format(const char *format, va_list arglist) 00668 { 00669 const unsigned int bufsize = 1024; 00670 char buffer [bufsize], *buf = buffer; 00671 unsigned int requested = vsnprintf (buf, bufsize, format, arglist); 00672 if (requested >= bufsize) { 00673 buf = new char[requested + 1]; 00674 requested = vsnprintf(buf, requested + 1, format, arglist); 00675 *this = buf; //copy 00676 delete [] buf; 00677 } else { 00678 *this = buf; //copy 00679 } 00680 } 00681 00682 #ifdef SE_STRING_TEST_PROGRAM 00683 int main(char **, int) 00684 { 00685 int i; 00686 SEString strings[] = { 00687 "Hello World!", 00688 "Hel,lo\\ World!", 00689 "Hel\"lo Wo\\9rld!", 00690 "Hel,lo Wo\\0rld!", 00691 SEString() 00692 }; 00693 for (i = 0; !strings[i].isNull(); i++) { 00694 SEString escaped = strings[i].escape(); 00695 SEString unescaped = escaped.unescape(); 00696 if (strcmp((const char*)strings[i], (const char*)unescaped)) 00697 printf("String Failed:\n%s\n%s\n%s\n\n", (const char*)strings[i], (const char*)escaped, (const char*)unescaped); 00698 } 00699 00700 // 0x5c = '\\', 0x30 = '0', 0x00 = 0, 0x22 = '"' 00701 const char bins[] = { 00702 '1', '2', '3', '4', '5', '6', '7', '8' , 00703 '1', '2', 0x5c, 0x30, '5', '6', '7', '8' , 00704 '1', '2', 0x5c, 0x30, 0x5c, 0x5c, 0x5c, '8' , 00705 '1', '2', 0x5c, 0x30, '5', '6', '7', '8' , 00706 '1', '2', 0x00, '4', '5', '6', '7', '8' , 00707 '1', 0x5c, 0x00, 0x5c, '5', '6', 0x5c, 0x5c , 00708 '1', 0x5c, 0x00, 0x00, '5', 0x00, 0x5c, '8' , 00709 0x00, '2', 0x5c, 0x30, '5', 0x22, 0x22, '8' , 00710 0x5c, '2', 0x5c, 0x30, '5', 0x22, 0x22, 0x00 , 00711 0x00, 0x00, 0x5c, 0x30, '5', 0x22, 0x22, 0x5c , 00712 'E' 00713 }; 00714 size_t bins_len = 8; 00715 for (i = 0; bins[i] != 'E'; i += bins_len) { 00716 SEString escaped = SEString::from((char*)(bins + i), bins_len); 00717 char *unescaped = (char *)malloc(escaped.length()); 00718 size_t unescaped_len = escaped.toBinary(unescaped); 00719 //printf("%d, escaped.length()=%d unescaped_len=%d\n", i/bins_len, escaped.length(), unescaped_len); 00720 if ( (memcmp((char*)(bins + i), unescaped, bins_len)) || (unescaped_len != bins_len) ) 00721 printf("Binary Failed: index %d\n", i/bins_len); 00722 //printf("%s\n", (const char*)escaped.getHexRepresentation()); 00723 free(unescaped); 00724 } 00725 00726 } 00727 #endif
(c) Skype Technologies S.A. Confidential/Proprietary
Last updated: Fri Jan 27 2012