nxstring.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756
  1. #include "nxstring.h"
  2. #include "foundation/error.h"
  3. #include <shlwapi.h>
  4. #include "foundation/atomics.h"
  5. #include <wchar.h>
  6. #include <stdarg.h>
  7. #include <stdio.h>
  8. #include <assert.h>
  9. #pragma comment(lib, "shlwapi.lib")
  10. //#define NX_STRING_STRICT_HEAP
  11. HANDLE string_heap = 0;
  12. int NXStringSetHeap(HANDLE _string_heap)
  13. {
  14. if (!string_heap)
  15. {
  16. string_heap = _string_heap;
  17. return NErr_Success;
  18. }
  19. else
  20. {
  21. return NErr_NoAction;
  22. }
  23. }
  24. // don't include null terminator here
  25. static size_t NXStringMallocSize(size_t characters)
  26. {
  27. /* TODO: overflow check? */
  28. const nx_string_t dummy=NULL;
  29. size_t header = (size_t)&dummy->string[0] - (size_t)dummy;
  30. return header + (characters+1) * sizeof(wchar_t);
  31. }
  32. // don't include null terminator here
  33. nx_string_t NXStringMalloc(size_t characters)
  34. {
  35. if (!string_heap)
  36. {
  37. string_heap = GetProcessHeap();
  38. }
  39. return NXStringMallocWithHeap(string_heap, characters);
  40. }
  41. nx_string_t NXStringRealloc(nx_string_t str, size_t characters)
  42. {
  43. nx_string_t new_str = (nx_string_t)HeapReAlloc(string_heap, 0, str, NXStringMallocSize(characters));
  44. // on failure, kick back the original block (TODO need to review this)
  45. if (!new_str)
  46. {
  47. return str;
  48. }
  49. return new_str;
  50. }
  51. nx_string_t NXStringMallocWithHeap(HANDLE heap, size_t characters)
  52. {
  53. #ifdef NX_STRING_STRICT_HEAP
  54. nx_string_t str;
  55. size_t string_size = NXStringMallocSize(characters);
  56. size_t allocated_size = (string_size + 8191) & ~4095;
  57. size_t offset = 4096 - (string_size & 4095);
  58. size_t pages = allocated_size / 4096;
  59. uint8_t *protect_start;
  60. void *mem = VirtualAlloc(0, allocated_size, MEM_COMMIT, PAGE_READWRITE);
  61. if (!mem)
  62. return 0;
  63. protect_start = (uint8_t *)mem + (pages-1)*4096;
  64. VirtualProtect(protect_start, 4096, PAGE_NOACCESS, 0);
  65. str = (nx_string_t)((uint8_t *)mem + offset);
  66. str->ref_count = 1;
  67. str->len = characters;
  68. return str;
  69. #else
  70. nx_string_t str = (nx_string_t)HeapAlloc(heap, 0, NXStringMallocSize(characters));
  71. if (str)
  72. {
  73. str->ref_count = 1;
  74. str->len = characters;
  75. }
  76. return str;
  77. #endif
  78. }
  79. int NXStringFree(HANDLE heap, nx_string_t str)
  80. {
  81. #ifdef NX_STRING_STRICT_HEAP
  82. uint8_t *mem = (uint8_t *)((size_t)str & 4095);
  83. VirtualProtect(mem, 4096, PAGE_NOACCESS, 0);
  84. assert(_heapchk() == _HEAPOK);
  85. return NErr_Success;
  86. #else
  87. if (HeapFree(heap, 0, str))
  88. {
  89. return NErr_Success;
  90. }
  91. else
  92. {
  93. return NErr_Error;
  94. }
  95. #endif
  96. }
  97. nx_string_t NXStringCreate(const wchar_t *str)
  98. {
  99. size_t size;
  100. nx_string_t nxstr;
  101. if (!str || (size_t)str <= 65536)
  102. {
  103. return 0;
  104. }
  105. size = wcslen(str);
  106. nxstr = NXStringMalloc(size);
  107. if (nxstr)
  108. {
  109. memcpy(nxstr->string, str, size*sizeof(wchar_t));
  110. nxstr->string[size]=0;
  111. }
  112. return nxstr;
  113. }
  114. int NXStringCreateEmpty(nx_string_t *new_string)
  115. {
  116. nx_string_t nxstr = NXStringMalloc(0);
  117. if (nxstr)
  118. {
  119. nxstr->string[0]=0;
  120. *new_string = nxstr;
  121. return NErr_Success;
  122. }
  123. else
  124. {
  125. return NErr_OutOfMemory;
  126. }
  127. }
  128. nx_string_t NXStringCreateWithHeap(HANDLE heap, const wchar_t *str)
  129. {
  130. size_t size = wcslen(str);
  131. nx_string_t nxstr = NXStringMallocWithHeap(heap, size);
  132. if (nxstr)
  133. {
  134. memcpy(nxstr->string, str, size*sizeof(wchar_t));
  135. nxstr->string[size]=0;
  136. }
  137. return nxstr;
  138. }
  139. nx_string_t NXStringCreateFromUTF8(const char *str)
  140. {
  141. nx_string_t nxstr;
  142. size_t size = MultiByteToWideChar(CP_UTF8, 0, str, -1, 0,0);
  143. if (!size)
  144. {
  145. return 0;
  146. }
  147. nxstr = NXStringMalloc(size-1);
  148. if (nxstr)
  149. {
  150. if (!MultiByteToWideChar(CP_UTF8, 0, str, -1, nxstr->string, (int)size))
  151. {
  152. NXStringFree(string_heap, nxstr);
  153. return 0;
  154. }
  155. }
  156. return nxstr;
  157. }
  158. int NXStringCreateWithUTF8(nx_string_t *new_value, const char *str)
  159. {
  160. size_t size;
  161. nx_string_t nxstr;
  162. if (!str)
  163. {
  164. return NErr_Empty;
  165. }
  166. size = MultiByteToWideChar(CP_UTF8, 0, str, -1, 0,0);
  167. if (!size)
  168. {
  169. return NErr_Error;
  170. }
  171. nxstr = NXStringMalloc(size-1);
  172. if (!nxstr)
  173. {
  174. return NErr_OutOfMemory;
  175. }
  176. if (!MultiByteToWideChar(CP_UTF8, 0, str, -1, nxstr->string, (int)size))
  177. {
  178. NXStringFree(string_heap, nxstr);
  179. return NErr_Error;
  180. }
  181. *new_value = nxstr;
  182. return NErr_Success;
  183. }
  184. int NXStringCreateWithUTF16(nx_string_t *new_value, const wchar_t *str)
  185. {
  186. size_t size;
  187. nx_string_t nxstr;
  188. if (!str)
  189. {
  190. return NErr_Empty;
  191. }
  192. size = wcslen(str);
  193. nxstr = NXStringMalloc(size);
  194. if (!nxstr)
  195. {
  196. return NErr_OutOfMemory;
  197. }
  198. memcpy(nxstr->string, str, size*sizeof(wchar_t));
  199. nxstr->string[size]=0;
  200. *new_value = nxstr;
  201. return NErr_Success;
  202. }
  203. int NXStringCreateWithCString(nx_string_t *new_value, const char *str, nx_charset_t charset)
  204. {
  205. nx_string_t nxstr;
  206. size_t size = MultiByteToWideChar(charset, 0, str, -1, 0,0);
  207. if (!size)
  208. {
  209. return NErr_Error;
  210. }
  211. nxstr = NXStringMalloc(size-1);
  212. if (!nxstr)
  213. {
  214. return NErr_OutOfMemory;
  215. }
  216. if (!MultiByteToWideChar(charset, 0, str, -1, nxstr->string, (int)size))
  217. {
  218. NXStringFree(string_heap, nxstr);
  219. return NErr_Error;
  220. }
  221. *new_value = nxstr;
  222. return NErr_Success;
  223. }
  224. nx_string_t NXStringRetain(nx_string_t string)
  225. {
  226. if (!string)
  227. {
  228. return 0;
  229. }
  230. nx_atomic_inc(&string->ref_count);
  231. return string;
  232. }
  233. void NXStringRelease(nx_string_t string)
  234. {
  235. if (string)
  236. {
  237. if (nx_atomic_dec(&string->ref_count) == 0)
  238. {
  239. NXStringFree(string_heap, string);
  240. }
  241. }
  242. }
  243. nx_string_t NXStringCreateFromPath(const wchar_t *folder, const wchar_t *filename)
  244. {
  245. nx_string_t pathstr = NXStringMalloc(MAX_PATH);
  246. if (pathstr)
  247. {
  248. PathCombineW(pathstr->string, folder, filename);
  249. pathstr->len = wcslen(pathstr->string);
  250. }
  251. return pathstr;
  252. }
  253. nx_string_t NXStringCreateFromUInt64(uint64_t value)
  254. {
  255. nx_string_t intstr = NXStringMalloc(21);
  256. if (intstr)
  257. {
  258. _ui64tow(value, intstr->string, 10);
  259. intstr->len = wcslen(intstr->string);
  260. }
  261. return intstr;
  262. }
  263. int NXStringCreateWithUInt64(nx_string_t *new_value, uint64_t value)
  264. {
  265. nx_string_t intstr = NXStringMalloc(21);
  266. if (!intstr)
  267. {
  268. return NErr_OutOfMemory;
  269. }
  270. _ui64tow(value, intstr->string, 10);
  271. intstr->len = wcslen(intstr->string);
  272. *new_value = intstr;
  273. return NErr_Success;
  274. }
  275. int NXStringCreateWithInt64(nx_string_t *new_value, int64_t value)
  276. {
  277. nx_string_t intstr = NXStringMalloc(21);
  278. if (!intstr)
  279. {
  280. return NErr_OutOfMemory;
  281. }
  282. _i64tow(value, intstr->string, 10);
  283. intstr->len = wcslen(intstr->string);
  284. *new_value = intstr;
  285. return NErr_Success;
  286. }
  287. int NXStringCreateWithBytes(nx_string_t *new_string, const void *data, size_t len, nx_charset_t charset)
  288. {
  289. nx_string_t nxstr;
  290. if (!len)
  291. {
  292. return NXStringCreateEmpty(new_string);
  293. }
  294. if (charset == nx_charset_utf16le)
  295. {
  296. nxstr = NXStringMalloc(len/2);
  297. if (nxstr)
  298. {
  299. memcpy(nxstr->string, data, len);
  300. nxstr->string[len/2]=0;
  301. nxstr->len = len/2;
  302. *new_string = nxstr;
  303. return NErr_Success;
  304. }
  305. else
  306. {
  307. return NErr_OutOfMemory;
  308. }
  309. }
  310. else if (charset == nx_charset_utf16be)
  311. {
  312. nxstr = NXStringMalloc(len/2);
  313. if (nxstr)
  314. {
  315. LCMapString(LOCALE_INVARIANT, LCMAP_BYTEREV, (LPCWSTR)data, (int)len/2, nxstr->string, (int)len/2);
  316. nxstr->string[len/2]=0;
  317. nxstr->len = len/2;
  318. *new_string = nxstr;
  319. return NErr_Success;
  320. }
  321. else
  322. {
  323. return NErr_OutOfMemory;
  324. }
  325. }
  326. else
  327. {
  328. int size = MultiByteToWideChar((UINT)charset, 0, (const char *)data, (int)len, 0, 0);
  329. if (!size)
  330. {
  331. return NErr_Error;
  332. }
  333. nxstr = NXStringMalloc(size);
  334. if (nxstr)
  335. {
  336. if (!MultiByteToWideChar((UINT)charset, 0, (const char *)data, (int)len, nxstr->string, size))
  337. {
  338. NXStringFree(string_heap, nxstr);
  339. return NErr_Error;
  340. }
  341. nxstr->string[size]=0;
  342. nxstr->len = size;
  343. *new_string = nxstr;
  344. return NErr_Success;
  345. }
  346. else
  347. {
  348. return NErr_OutOfMemory;
  349. }
  350. }
  351. }
  352. size_t NXStringGetLength(nx_string_t string)
  353. {
  354. return (string ? string->len : 0);
  355. }
  356. /* --- Keyword (ASCII) comparison --- */
  357. int NXStringKeywordCompareWithCString(nx_string_t string, const char *compare_to)
  358. {
  359. const wchar_t *src = string->string;
  360. const char *dst = compare_to;
  361. int ret = 0 ;
  362. while( ! (ret = (int)((*src & ~0x20) - (*dst & ~0x20))) && *dst)
  363. {
  364. ++src, ++dst;
  365. }
  366. if ( ret < 0 )
  367. {
  368. ret = -1 ;
  369. }
  370. else if ( ret > 0 )
  371. {
  372. ret = 1 ;
  373. }
  374. return( ret );
  375. }
  376. int NXStringKeywordCompare(nx_string_t string, nx_string_t compare_to)
  377. {
  378. const wchar_t *src = string->string;
  379. const wchar_t *dst = compare_to->string;
  380. int ret = 0 ;
  381. while( ! (ret = (int)((*src & ~0x20) - (*dst & ~0x20))) && *dst)
  382. {
  383. ++src, ++dst;
  384. }
  385. if ( ret < 0 )
  386. {
  387. ret = -1 ;
  388. }
  389. else if ( ret > 0 )
  390. {
  391. ret = 1 ;
  392. }
  393. return( ret );
  394. }
  395. int NXStringKeywordCaseCompare(nx_string_t string, nx_string_t compare_to)
  396. {
  397. const wchar_t *src = string->string;
  398. const wchar_t *dst = compare_to->string;
  399. int ret = 0 ;
  400. while( ! (ret = (int)(*src - (wchar_t)*dst)) && *dst)
  401. {
  402. ++src, ++dst;
  403. }
  404. if ( ret < 0 )
  405. {
  406. ret = -1 ;
  407. }
  408. else if ( ret > 0 )
  409. {
  410. ret = 1 ;
  411. }
  412. return( ret );
  413. }
  414. int NXStringCreateBasePathFromFilename(nx_string_t filename, nx_string_t *basepath)
  415. {
  416. nx_string_t nxstr;
  417. size_t len = filename->len;
  418. while (len && filename->string[len-1] != '\\' && filename->string[len-1] != '/')
  419. {
  420. len--;
  421. }
  422. if (!len)
  423. {
  424. return NErr_Empty;
  425. }
  426. nxstr = NXStringMalloc(len);
  427. if (!nxstr)
  428. {
  429. return NErr_OutOfMemory;
  430. }
  431. memcpy(nxstr->string, filename->string, sizeof(wchar_t)*len);
  432. nxstr->string[len]=0;
  433. *basepath = nxstr;
  434. return NErr_Success;
  435. }
  436. int NXStringGetCString(nx_string_t string, char *user_buffer, size_t user_buffer_length, const char **out_cstring, size_t *out_cstring_length)
  437. {
  438. size_t size;
  439. /* TODO: error check this with large strings and small user_buffer_length sizes */
  440. if (!string)
  441. {
  442. return NErr_NullPointer;
  443. }
  444. if (user_buffer_length == 0)
  445. return NErr_Insufficient;
  446. size = WideCharToMultiByte(CP_ACP, 0, string->string, (int)string->len, user_buffer, (int)user_buffer_length-1, NULL, NULL);
  447. if (size == 0 && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
  448. size = user_buffer_length-1;
  449. user_buffer[size]=0;
  450. *out_cstring = user_buffer;
  451. *out_cstring_length = (size_t)size;
  452. return NErr_Success;
  453. }
  454. int NXStringGetDoubleValue(nx_string_t string, double *value)
  455. {
  456. if (!string)
  457. return NErr_NullPointer;
  458. *value = wcstod(string->string, 0);
  459. return NErr_Success;
  460. }
  461. int NXStringGetBytesSize(size_t *byte_count, nx_string_t string, nx_charset_t charset, int flags)
  462. {
  463. if (charset == nx_charset_utf16le)
  464. {
  465. if (flags & nx_string_get_bytes_size_null_terminate)
  466. *byte_count = (string->len + 1)* sizeof(wchar_t);
  467. else
  468. *byte_count = string->len * sizeof(wchar_t);
  469. return NErr_DirectPointer;
  470. }
  471. else
  472. {
  473. size_t size=0;
  474. /*if (flags & nx_string_get_bytes_size_null_terminate)
  475. size = WideCharToMultiByte(charset, 0, string->string, string->len, 0, 0, NULL, NULL);
  476. else*/
  477. size = WideCharToMultiByte(charset, 0, string->string, (int)string->len, 0, 0, NULL, NULL);
  478. if (!size)
  479. return NErr_Error;
  480. if (flags & nx_string_get_bytes_size_null_terminate)
  481. *byte_count = size+1;
  482. else
  483. *byte_count = size;
  484. return NErr_Success;
  485. }
  486. }
  487. int NXStringGetBytesDirect(const void **bytes, size_t *length, nx_string_t string, nx_charset_t charset, int flags)
  488. {
  489. if (charset == nx_charset_utf16le)
  490. {
  491. *bytes = string->string;
  492. if (length)
  493. {
  494. if (flags & nx_string_get_bytes_size_null_terminate)
  495. *length = (string->len+1) * sizeof(wchar_t); // TODO: overflow check
  496. else
  497. *length = string->len * sizeof(wchar_t); // TODO: overflow check
  498. }
  499. return NErr_Success;
  500. }
  501. else
  502. {
  503. return NErr_Error;
  504. }
  505. }
  506. int NXStringGetBytes(size_t *bytes_copied, nx_string_t string, void *bytes, size_t length, nx_charset_t charset, int flags)
  507. {
  508. if (charset == nx_charset_utf16le)
  509. {
  510. length/=2;
  511. if (flags & nx_string_get_bytes_size_null_terminate)
  512. {
  513. if (length == 0)
  514. return NErr_Insufficient;
  515. length--;
  516. }
  517. if (length > string->len)
  518. length = string->len;
  519. wmemcpy((wchar_t *)bytes, string->string, length);
  520. if (flags & nx_string_get_bytes_size_null_terminate)
  521. ((wchar_t *)bytes)[length++]=0;
  522. if (bytes_copied)
  523. *bytes_copied = length * 2;
  524. return NErr_Success;
  525. }
  526. else
  527. {
  528. size_t size=0;
  529. if (flags & nx_string_get_bytes_size_null_terminate)
  530. {
  531. size = WideCharToMultiByte(charset, 0, string->string, (int)string->len, (LPSTR)bytes, (int)length-1, NULL, NULL);
  532. ((char *)bytes)[size]=0;
  533. }
  534. else
  535. {
  536. size = WideCharToMultiByte(charset, 0, string->string, (int)string->len, (LPSTR)bytes, (int)length, NULL, NULL);
  537. }
  538. if (!size)
  539. {
  540. if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
  541. {
  542. if (flags & nx_string_get_bytes_size_null_terminate)
  543. size = length-1;
  544. else
  545. size=length;
  546. }
  547. else
  548. {
  549. return NErr_Error;
  550. }
  551. }
  552. if (bytes_copied)
  553. {
  554. if (flags & nx_string_get_bytes_size_null_terminate)
  555. {
  556. if (size)
  557. *bytes_copied = size+1;
  558. else
  559. *bytes_copied = length+1;
  560. }
  561. else
  562. {
  563. if (size)
  564. *bytes_copied = size;
  565. else
  566. *bytes_copied = length;
  567. }
  568. }
  569. return NErr_Success;
  570. }
  571. }
  572. int NXStringGetIntegerValue(nx_string_t string, int *value)
  573. {
  574. *value = wcstol(string->string, 0, 10);
  575. return NErr_Success;
  576. }
  577. int NXStringGetGUIDValue(nx_string_t string, GUID *out_guid)
  578. {
  579. /* TODO: it'd be nice if this was a bit more flexible on input, e.g. no dashes vs dashes */
  580. GUID guid = GUID_NULL;
  581. size_t offset = 0;
  582. int Data1, Data2, Data3;
  583. int Data4[8] = {0};
  584. for (;;)
  585. {
  586. if (string->string[offset] == '{')
  587. {
  588. offset++;
  589. }
  590. else if (string->string[offset] == ' ')
  591. {
  592. offset++;
  593. }
  594. else
  595. {
  596. break;
  597. }
  598. }
  599. //{ 0x1b3ca60c, 0xda98, 0x4826, { 0xb4, 0xa9, 0xd7, 0x97, 0x48, 0xa5, 0xfd, 0x73 } };
  600. swscanf( string->string, L"%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X",
  601. &Data1, &Data2, &Data3, Data4 + 0, Data4 + 1,
  602. Data4 + 2, Data4 + 3, Data4 + 4, Data4 + 5, Data4 + 6, Data4 + 7 );
  603. // Cross assign all the values
  604. guid.Data1 = Data1;
  605. guid.Data2 = Data2;
  606. guid.Data3 = Data3;
  607. guid.Data4[0] = Data4[0];
  608. guid.Data4[1] = Data4[1];
  609. guid.Data4[2] = Data4[2];
  610. guid.Data4[3] = Data4[3];
  611. guid.Data4[4] = Data4[4];
  612. guid.Data4[5] = Data4[5];
  613. guid.Data4[6] = Data4[6];
  614. guid.Data4[7] = Data4[7];
  615. *out_guid = guid;
  616. return NErr_Success;
  617. }
  618. nx_compare_result NXStringCompare(nx_string_t string1, nx_string_t string2, nx_compare_options options)
  619. {
  620. int compareFlags = 0;
  621. if (0 != (nx_compare_case_insensitive & options))
  622. {
  623. compareFlags |= NORM_IGNORECASE;
  624. }
  625. return CompareString(LOCALE_USER_DEFAULT, compareFlags, string1->string, -1, string2->string, -1) - 2;
  626. }
  627. int NXStringCreateWithFormatting(nx_string_t *new_string, const char *format, ...)
  628. {
  629. size_t cch, ret;
  630. char *temp = 0;
  631. va_list v;
  632. va_start(v, format);
  633. cch = _vscprintf(format, v);
  634. if (cch == -1)
  635. {
  636. return NErr_Error;
  637. }
  638. if (cch > 256)
  639. {
  640. temp = (char *)malloc(cch+1);
  641. if (!temp)
  642. {
  643. return NErr_OutOfMemory;
  644. }
  645. vsprintf(temp, format, v);
  646. ret = NXStringCreateWithUTF8(new_string, temp);
  647. free(temp);
  648. }
  649. else
  650. {
  651. temp = (char *)_malloca(cch+1);
  652. if (!temp)
  653. {
  654. return NErr_OutOfMemory;
  655. }
  656. vsprintf(temp, format, v);
  657. ret = NXStringCreateWithUTF8(new_string, temp);
  658. }
  659. va_end(v);
  660. return (int)ret;
  661. }