ncdval_test.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380
  1. /**
  2. * @file ncdval_test.c
  3. * @author Ambroz Bizjak <ambrop7@gmail.com>
  4. *
  5. * @section LICENSE
  6. *
  7. * Redistribution and use in source and binary forms, with or without
  8. * modification, are permitted provided that the following conditions are met:
  9. * 1. Redistributions of source code must retain the above copyright
  10. * notice, this list of conditions and the following disclaimer.
  11. * 2. Redistributions in binary form must reproduce the above copyright
  12. * notice, this list of conditions and the following disclaimer in the
  13. * documentation and/or other materials provided with the distribution.
  14. * 3. Neither the name of the author nor the
  15. * names of its contributors may be used to endorse or promote products
  16. * derived from this software without specific prior written permission.
  17. *
  18. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
  19. * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  20. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  21. * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
  22. * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  23. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  24. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  25. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  26. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  27. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  28. */
  29. #include <stdio.h>
  30. #include <ncd/NCDVal.h>
  31. #include <ncd/NCDStringIndex.h>
  32. #include <ncd/static_strings.h>
  33. #include <base/BLog.h>
  34. #include <misc/debug.h>
  35. #include <misc/balloc.h>
  36. #include <misc/offset.h>
  37. #define FORCE(cmd) if (!(cmd)) { fprintf(stderr, "failed\n"); exit(1); }
  38. struct composed_string {
  39. BRefTarget ref_target;
  40. size_t length;
  41. size_t chunk_size;
  42. char **chunks;
  43. };
  44. static void composed_string_ref_target_func_release (BRefTarget *ref_target)
  45. {
  46. struct composed_string *cs = UPPER_OBJECT(ref_target, struct composed_string, ref_target);
  47. size_t num_chunks = cs->length / cs->chunk_size;
  48. if (cs->length % cs->chunk_size) {
  49. num_chunks++;
  50. }
  51. for (size_t i = 0; i < num_chunks; i++) {
  52. BFree(cs->chunks[i]);
  53. }
  54. BFree(cs->chunks);
  55. BFree(cs);
  56. }
  57. static void composed_string_func_getptr (void *user, size_t offset, const char **out_data, size_t *out_length)
  58. {
  59. struct composed_string *cs = user;
  60. ASSERT(offset < cs->length)
  61. *out_data = cs->chunks[offset / cs->chunk_size] + (offset % cs->chunk_size);
  62. *out_length = cs->chunk_size - (offset % cs->chunk_size);
  63. }
  64. static NCDValRef build_composed_string (NCDValMem *mem, const char *data, size_t length, size_t chunk_size)
  65. {
  66. ASSERT(chunk_size > 0)
  67. struct composed_string *cs = BAlloc(sizeof(*cs));
  68. if (!cs) {
  69. goto fail0;
  70. }
  71. cs->length = length;
  72. cs->chunk_size = chunk_size;
  73. size_t num_chunks = cs->length / cs->chunk_size;
  74. if (cs->length % cs->chunk_size) {
  75. num_chunks++;
  76. }
  77. cs->chunks = BAllocArray(num_chunks, sizeof(cs->chunks[0]));
  78. if (!cs->chunk_size) {
  79. goto fail1;
  80. }
  81. size_t i;
  82. for (i = 0; i < num_chunks; i++) {
  83. cs->chunks[i] = BAlloc(cs->chunk_size);
  84. if (!cs->chunks[i]) {
  85. goto fail2;
  86. }
  87. size_t to_copy = length;
  88. if (to_copy > cs->chunk_size) {
  89. to_copy = cs->chunk_size;
  90. }
  91. memcpy(cs->chunks[i], data, to_copy);
  92. data += to_copy;
  93. length -= to_copy;
  94. }
  95. BRefTarget_Init(&cs->ref_target, composed_string_ref_target_func_release);
  96. NCDValComposedStringResource resource;
  97. resource.func_getptr = composed_string_func_getptr;
  98. resource.user = cs;
  99. resource.ref_target = &cs->ref_target;
  100. NCDValRef val = NCDVal_NewComposedString(mem, resource, 0, cs->length);
  101. BRefTarget_Deref(&cs->ref_target);
  102. return val;
  103. fail2:
  104. while (i-- > 0) {
  105. BFree(cs->chunks[i]);
  106. }
  107. BFree(cs->chunks);
  108. fail1:
  109. BFree(cs);
  110. fail0:
  111. return NCDVal_NewInvalid();
  112. }
  113. static void test_string (NCDValRef str, const char *data, size_t length)
  114. {
  115. FORCE( !NCDVal_IsInvalid(str) )
  116. FORCE( NCDVal_IsString(str) )
  117. FORCE( NCDVal_StringLength(str) == length )
  118. FORCE( NCDVal_StringHasNulls(str) == !!memchr(data, '\0', length) )
  119. FORCE( NCDVal_IsStringNoNulls(str) == !memchr(data, '\0', length) )
  120. FORCE( NCDVal_StringRegionEquals(str, 0, length, data) )
  121. b_cstring cstr = NCDVal_StringCstring(str);
  122. for (size_t i = 0; i < length; i++) {
  123. size_t chunk_length;
  124. const char *chunk_data = b_cstring_get(cstr, i, length - i, &chunk_length);
  125. FORCE( chunk_length > 0 )
  126. FORCE( chunk_length <= length - i )
  127. FORCE( !memcmp(chunk_data, data + i, chunk_length) )
  128. FORCE( NCDVal_StringRegionEquals(str, i, chunk_length, data + i) )
  129. FORCE( b_cstring_memcmp(cstr, b_cstring_make_buf(data, length), i, i, chunk_length) == 0 )
  130. FORCE( b_cstring_memcmp(cstr, b_cstring_make_buf(data + i, length - i), i, 0, chunk_length) == 0 )
  131. }
  132. }
  133. static void print_indent (int indent)
  134. {
  135. for (int i = 0; i < indent; i++) {
  136. printf(" ");
  137. }
  138. }
  139. static void print_value (NCDValRef val, unsigned int indent)
  140. {
  141. switch (NCDVal_Type(val)) {
  142. case NCDVAL_STRING: {
  143. NCDValNullTermString nts;
  144. FORCE( NCDVal_StringNullTerminate(val, &nts) )
  145. print_indent(indent);
  146. printf("string(%zu) %s\n", NCDVal_StringLength(val), nts.data);
  147. NCDValNullTermString_Free(&nts);
  148. } break;
  149. case NCDVAL_LIST: {
  150. size_t count = NCDVal_ListCount(val);
  151. print_indent(indent);
  152. printf("list(%zu)\n", NCDVal_ListCount(val));
  153. for (size_t i = 0; i < count; i++) {
  154. NCDValRef elem_val = NCDVal_ListGet(val, i);
  155. print_value(elem_val, indent + 1);
  156. }
  157. } break;
  158. case NCDVAL_MAP: {
  159. print_indent(indent);
  160. printf("map(%zu)\n", NCDVal_MapCount(val));
  161. for (NCDValMapElem e = NCDVal_MapOrderedFirst(val); !NCDVal_MapElemInvalid(e); e = NCDVal_MapOrderedNext(val, e)) {
  162. NCDValRef ekey = NCDVal_MapElemKey(val, e);
  163. NCDValRef eval = NCDVal_MapElemVal(val, e);
  164. print_indent(indent + 1);
  165. printf("key=\n");
  166. print_value(ekey, indent + 2);
  167. print_indent(indent + 1);
  168. printf("val=\n");
  169. print_value(eval, indent + 2);
  170. }
  171. } break;
  172. }
  173. }
  174. int main ()
  175. {
  176. int res;
  177. BLog_InitStdout();
  178. NCDStringIndex string_index;
  179. FORCE( NCDStringIndex_Init(&string_index) )
  180. // Some basic usage of values.
  181. NCDValMem mem;
  182. NCDValMem_Init(&mem);
  183. NCDValRef s1 = NCDVal_NewString(&mem, "Hello World");
  184. test_string(s1, "Hello World", 11);
  185. ASSERT( NCDVal_IsString(s1) )
  186. ASSERT( !NCDVal_IsIdString(s1) )
  187. ASSERT( NCDVal_Type(s1) == NCDVAL_STRING )
  188. NCDValRef s2 = NCDVal_NewString(&mem, "This is reeeeeeeeeeeeeallllllllyyyyy fun!");
  189. FORCE( !NCDVal_IsInvalid(s2) )
  190. NCDValRef l1 = NCDVal_NewList(&mem, 10);
  191. FORCE( !NCDVal_IsInvalid(l1) )
  192. FORCE( NCDVal_ListAppend(l1, s1) )
  193. FORCE( NCDVal_ListAppend(l1, s2) )
  194. print_value(s1, 0);
  195. print_value(s2, 0);
  196. print_value(l1, 0);
  197. NCDValRef k1 = NCDVal_NewString(&mem, "K1");
  198. FORCE( !NCDVal_IsInvalid(k1) )
  199. NCDValRef v1 = NCDVal_NewString(&mem, "V1");
  200. FORCE( !NCDVal_IsInvalid(v1) )
  201. NCDValRef k2 = NCDVal_NewString(&mem, "K2");
  202. FORCE( !NCDVal_IsInvalid(k2) )
  203. NCDValRef v2 = NCDVal_NewString(&mem, "V2");
  204. FORCE( !NCDVal_IsInvalid(v2) )
  205. NCDValRef m1 = NCDVal_NewMap(&mem, 3);
  206. FORCE( !NCDVal_IsInvalid(m1) )
  207. FORCE( NCDVal_MapInsert(m1, k1, v1, &res) && res )
  208. FORCE( NCDVal_MapInsert(m1, k2, v2, &res) && res )
  209. ASSERT( NCDVal_MapGetValue(m1, "K1").idx == v1.idx )
  210. ASSERT( NCDVal_MapGetValue(m1, "K2").idx == v2.idx )
  211. ASSERT( NCDVal_IsInvalid(NCDVal_MapGetValue(m1, "K3")) )
  212. NCDValRef ids1 = NCDVal_NewIdString(&mem, NCD_STRING_ARG1, &string_index);
  213. test_string(ids1, "_arg1", 5);
  214. ASSERT( !memcmp(NCDVal_StringData(ids1), "_arg1", 5) )
  215. ASSERT( NCDVal_StringLength(ids1) == 5 )
  216. ASSERT( !NCDVal_StringHasNulls(ids1) )
  217. ASSERT( NCDVal_StringEquals(ids1, "_arg1") )
  218. ASSERT( NCDVal_Type(ids1) == NCDVAL_STRING )
  219. ASSERT( NCDVal_IsIdString(ids1) )
  220. NCDValRef ids2 = NCDVal_NewIdString(&mem, NCD_STRING_ARG2, &string_index);
  221. test_string(ids2, "_arg2", 5);
  222. ASSERT( !memcmp(NCDVal_StringData(ids2), "_arg2", 5) )
  223. ASSERT( NCDVal_StringLength(ids2) == 5 )
  224. ASSERT( !NCDVal_StringHasNulls(ids2) )
  225. ASSERT( NCDVal_StringEquals(ids2, "_arg2") )
  226. ASSERT( NCDVal_Type(ids2) == NCDVAL_STRING )
  227. ASSERT( NCDVal_IsIdString(ids2) )
  228. FORCE( NCDVal_MapInsert(m1, ids1, ids2, &res) && res )
  229. ASSERT( NCDVal_MapGetValue(m1, "_arg1").idx == ids2.idx )
  230. print_value(m1, 0);
  231. NCDValRef copy = NCDVal_NewCopy(&mem, m1);
  232. FORCE( !NCDVal_IsInvalid(copy) )
  233. ASSERT( NCDVal_Compare(copy, m1) == 0 )
  234. NCDValMem_Free(&mem);
  235. // Try to make copies of a string within the same memory object.
  236. // This is an evil test because we cannot simply copy a string using e.g.
  237. // NCDVal_NewStringBin() - it requires that the buffer passed
  238. // be outside the memory object of the new string.
  239. // We use NCDVal_NewCopy(), which takes care of this by creating
  240. // an uninitialized string using NCDVal_NewStringUninitialized() and
  241. // then copyng the data.
  242. NCDValMem_Init(&mem);
  243. NCDValRef s[100];
  244. s[0] = NCDVal_NewString(&mem, "Eeeeeeeeeeeevil.");
  245. FORCE( !NCDVal_IsInvalid(s[0]) )
  246. for (int i = 1; i < 100; i++) {
  247. s[i] = NCDVal_NewCopy(&mem, s[i - 1]);
  248. FORCE( !NCDVal_IsInvalid(s[i]) )
  249. ASSERT( NCDVal_StringEquals(s[i - 1], "Eeeeeeeeeeeevil.") )
  250. ASSERT( NCDVal_StringEquals(s[i], "Eeeeeeeeeeeevil.") )
  251. }
  252. for (int i = 0; i < 100; i++) {
  253. ASSERT( NCDVal_StringEquals(s[i], "Eeeeeeeeeeeevil.") )
  254. }
  255. NCDValMem_Free(&mem);
  256. NCDValMem_Init(&mem);
  257. NCDValRef cstr1 = build_composed_string(&mem, "Hello World", 11, 3);
  258. test_string(cstr1, "Hello World", 11);
  259. FORCE( NCDVal_IsComposedString(cstr1) )
  260. FORCE( !NCDVal_IsContinuousString(cstr1) )
  261. FORCE( NCDVal_StringEquals(cstr1, "Hello World") )
  262. FORCE( !NCDVal_StringEquals(cstr1, "Hello World ") )
  263. FORCE( !NCDVal_StringEquals(cstr1, "Hello WorlD") )
  264. NCDValRef cstr2 = build_composed_string(&mem, "GoodBye", 7, 1);
  265. test_string(cstr2, "GoodBye", 7);
  266. FORCE( NCDVal_IsComposedString(cstr2) )
  267. FORCE( !NCDVal_IsContinuousString(cstr2) )
  268. FORCE( NCDVal_StringEquals(cstr2, "GoodBye") )
  269. FORCE( !NCDVal_StringEquals(cstr2, " GoodBye") )
  270. FORCE( !NCDVal_StringEquals(cstr2, "goodBye") )
  271. NCDValRef cstr3 = build_composed_string(&mem, "Bad\x00String", 10, 4);
  272. test_string(cstr3, "Bad\x00String", 10);
  273. FORCE( NCDVal_IsComposedString(cstr3) )
  274. FORCE( !NCDVal_IsContinuousString(cstr3) )
  275. FORCE( NCDVal_StringMemCmp(cstr1, cstr2, 1, 2, 3) < 0 )
  276. FORCE( NCDVal_StringMemCmp(cstr1, cstr2, 7, 1, 4) > 0 )
  277. char buf[10];
  278. NCDVal_StringCopyOut(cstr1, 1, 10, buf);
  279. FORCE( !memcmp(buf, "ello World", 10) )
  280. NCDValRef clist1 = NCDVal_NewList(&mem, 3);
  281. FORCE( !NCDVal_IsInvalid(clist1) )
  282. FORCE( NCDVal_ListAppend(clist1, cstr1) )
  283. FORCE( NCDVal_ListAppend(clist1, cstr2) )
  284. FORCE( NCDVal_ListAppend(clist1, cstr3) )
  285. FORCE( NCDVal_ListCount(clist1) == 3 )
  286. FORCE( NCDValMem_ConvertNonContinuousStrings(&mem, &clist1) )
  287. FORCE( NCDVal_ListCount(clist1) == 3 )
  288. NCDValRef fixed_str1 = NCDVal_ListGet(clist1, 0);
  289. NCDValRef fixed_str2 = NCDVal_ListGet(clist1, 1);
  290. NCDValRef fixed_str3 = NCDVal_ListGet(clist1, 2);
  291. FORCE( NCDVal_IsContinuousString(fixed_str1) )
  292. FORCE( NCDVal_IsContinuousString(fixed_str2) )
  293. FORCE( NCDVal_IsContinuousString(fixed_str3) )
  294. test_string(fixed_str1, "Hello World", 11);
  295. test_string(fixed_str2, "GoodBye", 7);
  296. test_string(fixed_str3, "Bad\x00String", 10);
  297. NCDValMem_Free(&mem);
  298. NCDStringIndex_Free(&string_index);
  299. return 0;
  300. }