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(size_t 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 int len = length(); 00200 00201 if (to >= 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(size_t 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(unsigned char chr) // static 00260 { 00261 char res[2]; 00262 00263 res[0] = chr; 00264 res[1] = '\0'; 00265 00266 return SEString(res); 00267 } 00268 00269 const SEString SEString::from(unsigned int u, unsigned int base) // static 00270 { 00271 char res[11]; 00272 00273 if (16 == base) 00274 snprintf(res, 11, "%X", u); 00275 else 00276 snprintf(res, 11, "%u", u); 00277 00278 return SEString(res); 00279 } 00280 00281 const SEString SEString::from(bool b) // static 00282 { 00283 char res[2]; 00284 00285 res[1] = '\0'; 00286 00287 if (b) 00288 res[0] = '1'; 00289 else 00290 res[0] = '0'; 00291 00292 return SEString(res); 00293 } 00294 00295 const SEString SEString::from(char *bin, unsigned int len) // static 00296 { 00297 // For binaries, we escape immediately to allow it to survive all the string operations 00298 size_t new_size = 0; 00299 unsigned int i; 00300 00301 for (i = 0; i < len; i++) { 00302 if (('\0' == bin[i]) || ('\\' == bin[i]) || (',' == bin[i]) || ('"' == bin[i])) 00303 new_size++; 00304 } 00305 new_size += len; 00306 00307 SEString str(new_size + 1); // 1 for end of string null char 00308 str.markAsBinary(); 00309 00310 char* to = str.d->str; 00311 00312 for (i = 0; i < len; i++) { 00313 if ('\0' == bin[i]) { // null is escaped differently 00314 *to = '\\'; 00315 to++; 00316 *to = '0'; 00317 to++; 00318 continue; 00319 } 00320 // normal escaping like in escape() 00321 if ((',' == bin[i]) || ('"' == bin[i]) || ('\\' == bin[i])) { 00322 *to = '\\'; 00323 to++; 00324 } 00325 *to = bin[i]; 00326 to++; 00327 } 00328 00329 *to = '\0'; 00330 00331 return str; 00332 } 00333 00334 bool SEString::toBool() const 00335 { 00336 if (isNull()) 00337 return false; 00338 00339 return atoi(d->str) != 0; 00340 } 00341 00342 int SEString::toInt() const 00343 { 00344 if (isNull()) 00345 return 0; 00346 00347 return atoi(d->str); 00348 } 00349 00350 unsigned int SEString::toUInt() const 00351 { 00352 if (isNull()) 00353 return 0; 00354 00355 return (unsigned int)strtoul(d->str, NULL, 10); 00356 } 00357 00358 SEString::uint64 SEString::toUInt64() const 00359 { 00360 if (isNull()) 00361 return 0; 00362 00363 return strtoull(d->str, NULL, 10); 00364 } 00365 00366 size_t SEString::toBinary(char *bin) const 00367 { 00368 if (isNull()) 00369 return 0; 00370 00371 // Check we actually have a binary 00372 if (!isBinary()) 00373 return 0; 00374 00375 // For a binary, we didn't unescape in the low-level SEStringDict::parse, but we do it here only 00376 size_t to = 0; 00377 size_t cur = 0; 00378 size_t len = length(); 00379 00380 while (cur < len) { 00381 if ('\\' == d->str[cur]) { 00382 cur++; 00383 00384 if ('0' == d->str[cur]) { // escaped null 00385 bin[to++] = '\0'; 00386 cur++; 00387 continue; 00388 } 00389 } 00390 00391 bin[to++] = d->str[cur++]; 00392 } 00393 00394 return to; 00395 } 00396 00397 SEString SEString::getHexRepresentation() const 00398 { 00399 // check we actually have a binary 00400 if (!isBinary()) return SEString(); 00401 // convert to binary 00402 char * ps = (char *)malloc(length()); 00403 size_t len = 2 * toBinary(ps); 00404 // convert to hexa 00405 static const char hex[17] = "0123456789abcdef"; 00406 SEString str(len); 00407 unsigned char *ptr = (unsigned char *)ps; 00408 unsigned int count = 0; 00409 for (; count < len; ptr++) { 00410 str.d->str[count++] = hex[*ptr >> 4]; 00411 str.d->str[count++] = hex[*ptr & 0xf]; 00412 } 00413 str.d->str[count] = 0; 00414 // free temp buf 00415 free(ps); 00416 return str; 00417 } 00418 00419 SEString& SEString::operator+=(const SEString &str) 00420 { 00421 if (isNull()) 00422 return (*this = str); 00423 if (str.isNull()) 00424 return (*this); 00425 00426 detach(); 00427 00428 size_t l = length(); 00429 00430 char* str_new = se_realloc(l + str.length() + 1); 00431 00432 if (str_new) { 00433 strcpy(str_new + l, str.d->str); 00434 00435 d->str = str_new; 00436 } 00437 00438 return *this; 00439 } 00440 00441 SEString& SEString::operator+=(const char* str) 00442 { 00443 if (isNull()) 00444 return (*this = str); 00445 if (str == NULL) 00446 return (*this); 00447 00448 detach(); 00449 00450 size_t l = length(); 00451 size_t l2 = strlen(str); 00452 00453 char* str_new = se_realloc(l + l2 + 1); 00454 00455 if (str_new) { 00456 strcpy(str_new + l, str); 00457 00458 d->str = str_new; 00459 } 00460 00461 return *this; 00462 } 00463 00464 SEString& SEString::operator=(const SEString& str) 00465 { 00466 Data* strd = const_cast<SEString&>(str).d_ref(); // shall be mutable 00467 d_unref(); 00468 d = strd; 00469 00470 return *this; 00471 } 00472 00473 SEString& SEString::operator=(const char* str) 00474 { 00475 d_unref(); 00476 00477 if (str) { 00478 d = new Data(); 00479 d->ref = 1; 00480 d->real_size = CALC_SIZE(strlen(str) + 1); 00481 d->str = (char*)malloc(d->real_size); 00482 strcpy(d->str, str); 00483 d->isBin = false; 00484 } else { 00485 d = 0; 00486 } 00487 00488 return *this; 00489 } 00490 00491 /** \cond INTERNAL */ 00492 SEString::Data* SEString::d_ref() 00493 { 00494 SEMutexLock lock_string(mutex); 00495 if (0 == d) 00496 return d; 00497 00498 d->lock.Acquire(); 00499 00500 d->ref++; 00501 00502 d->lock.Release(); 00503 return d; 00504 } 00505 00506 void SEString::d_unref() 00507 { 00508 SEMutexLock lock_string(mutex); 00509 if (0 == d) 00510 return; 00511 00512 SEMutex* lock = &d->lock; 00513 00514 lock->Acquire(); 00515 00516 if (d->ref > 1) 00517 d->ref--; 00518 else { 00519 Data* tmp_d = d; 00520 d = 0; 00521 lock->Release(); 00522 free(tmp_d->str); 00523 delete tmp_d; 00524 return; 00525 } 00526 00527 lock->Release(); 00528 } 00529 00530 void SEString::detach() 00531 { 00532 SEMutexLock lock_string(mutex); 00533 if (0 == d) 00534 return; 00535 00536 SEMutex* lock = &d->lock; 00537 lock->Acquire(); 00538 00539 if (1 != d->ref) { 00540 00541 d->ref--; 00542 00543 size_t sz = CALC_SIZE(strlen(d->str) + 1); 00544 char* buf = (char*)malloc(sz); 00545 strcpy(buf, d->str); 00546 lock->Release(); 00547 00548 Data* d_new = new Data(); 00549 d_new->ref = 1; 00550 d_new->real_size = sz; 00551 d_new->str = buf; 00552 d_new->isBin = d->isBin; 00553 00554 d = d_new; 00555 return; 00556 } 00557 00558 lock->Release(); 00559 } 00560 /** \endcond */ 00561 00562 SEString SEString::deepCopy() const 00563 { 00564 SEString tmp = *this; 00565 00566 tmp.detach(); 00567 00568 return tmp; 00569 } 00570 00571 unsigned int SEString::hash(unsigned int hash_size) const 00572 { 00573 if (isNull()) 00574 return 0; 00575 00576 const char *ptr; 00577 unsigned int val; 00578 00579 val = 0; 00580 ptr = d->str; 00581 00582 while (*ptr) { 00583 int tmp; 00584 val = (val << 4) + (*ptr); 00585 tmp = (val & 0xf0000000); 00586 if (tmp) { 00587 val = val ^ (tmp >> 24); 00588 val = val ^ tmp; 00589 } 00590 00591 ptr++; 00592 } 00593 00594 return val % hash_size; 00595 } 00596 00597 bool SEString::equals(const SEString& str) const 00598 { 00599 if (isNull() && str.isNull()) 00600 return true; 00601 00602 else if (isNull() || str.isNull()) 00603 return false; 00604 00605 return (0 == strcmp(d->str, str.d->str)); 00606 } 00607 00608 bool SEString::equals(const char* str) const 00609 { 00610 if (isNull() && (0 == str)) 00611 return true; 00612 00613 if (isNull() || (0 == str)) 00614 return false; 00615 00616 return (0 == strcmp(str, d->str)); 00617 } 00618 00619 int SEString::find(char c) const 00620 { 00621 if (isNull()) 00622 return -1; 00623 00624 char* found = strchr(d->str, c); 00625 00626 if (0 == found) 00627 return -1; 00628 00629 return int(found - d->str); 00630 } 00631 00632 int SEString::find(int startpos, char c) const 00633 { 00634 if (isNull()) 00635 return -1; 00636 00637 char* found = strchr(d->str+startpos, c); 00638 00639 if (0 == found) 00640 return -1; 00641 00642 return int(found - d->str); 00643 } 00644 00645 /** \cond INTERNAL */ 00646 char* SEString::se_realloc(size_t new_size) 00647 { 00648 if (d->real_size >= new_size) 00649 return d->str; 00650 else { 00651 d->real_size = CALC_SIZE(new_size); 00652 00653 return (char*)::realloc(d->str, d->real_size); 00654 } 00655 } 00656 /** \endcond */ 00657 00658 void SEString::Format(const char *format, va_list arglist) 00659 { 00660 const unsigned int bufsize = 1024; 00661 char buffer [bufsize], *buf = buffer; 00662 unsigned int requested = vsnprintf (buf, bufsize, format, arglist); 00663 if (requested >= bufsize) { 00664 buf = new char[requested + 1]; 00665 requested = vsnprintf(buf, requested + 1, format, arglist); 00666 *this = buf; //copy 00667 delete [] buf; 00668 } else { 00669 *this = buf; //copy 00670 } 00671 } 00672 00673 #ifdef SE_STRING_TEST_PROGRAM 00674 int main(char **, int) 00675 { 00676 int i; 00677 SEString strings[] = { 00678 "Hello World!", 00679 "Hel,lo\\ World!", 00680 "Hel\"lo Wo\\9rld!", 00681 "Hel,lo Wo\\0rld!", 00682 SEString() 00683 }; 00684 for (i = 0; !strings[i].isNull(); i++) { 00685 SEString escaped = strings[i].escape(); 00686 SEString unescaped = escaped.unescape(); 00687 if (strcmp((const char*)strings[i], (const char*)unescaped)) 00688 printf("String Failed:\n%s\n%s\n%s\n\n", (const char*)strings[i], (const char*)escaped, (const char*)unescaped); 00689 } 00690 00691 // 0x5c = '\\', 0x30 = '0', 0x00 = 0, 0x22 = '"' 00692 const char bins[] = { 00693 '1', '2', '3', '4', '5', '6', '7', '8' , 00694 '1', '2', 0x5c, 0x30, '5', '6', '7', '8' , 00695 '1', '2', 0x5c, 0x30, 0x5c, 0x5c, 0x5c, '8' , 00696 '1', '2', 0x5c, 0x30, '5', '6', '7', '8' , 00697 '1', '2', 0x00, '4', '5', '6', '7', '8' , 00698 '1', 0x5c, 0x00, 0x5c, '5', '6', 0x5c, 0x5c , 00699 '1', 0x5c, 0x00, 0x00, '5', 0x00, 0x5c, '8' , 00700 0x00, '2', 0x5c, 0x30, '5', 0x22, 0x22, '8' , 00701 0x5c, '2', 0x5c, 0x30, '5', 0x22, 0x22, 0x00 , 00702 0x00, 0x00, 0x5c, 0x30, '5', 0x22, 0x22, 0x5c , 00703 'E' 00704 }; 00705 size_t bins_len = 8; 00706 for (i = 0; bins[i] != 'E'; i += bins_len) { 00707 SEString escaped = SEString::from((char*)(bins + i), bins_len); 00708 char *unescaped = (char *)malloc(escaped.length()); 00709 size_t unescaped_len = escaped.toBinary(unescaped); 00710 //printf("%d, escaped.length()=%d unescaped_len=%d\n", i/bins_len, escaped.length(), unescaped_len); 00711 if ( (memcmp((char*)(bins + i), unescaped, bins_len)) || (unescaped_len != bins_len) ) 00712 printf("Binary Failed: index %d\n", i/bins_len); 00713 //printf("%s\n", (const char*)escaped.getHexRepresentation()); 00714 free(unescaped); 00715 } 00716 00717 } 00718 #endif
(c) Skype Technologies S.A. Confidential/Proprietary
Last updated: Fri Mar 16 2012