test_cstr.c (14794B)
1 /* Copyright (C) 2013-2023, 2025 Vincent Forest (vaplv@free.fr) 2 * 3 * The RSys library is free software: you can redistribute it and/or modify 4 * it under the terms of the GNU General Public License as published 5 * by the Free Software Foundation, either version 3 of the License, or 6 * (at your option) any later version. 7 * 8 * The RSys library is distributed in the hope that it will be useful, 9 * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 * GNU General Public License for more details. 12 * 13 * You should have received a copy of the GNU General Public License 14 * along with the RSys library. If not, see <http://www.gnu.org/licenses/>. */ 15 16 #define _POSIX_C_SOURCE 200112L /* strtok_r support */ 17 18 #include "cstr.h" 19 #include "math.h" 20 21 #include <string.h> 22 23 static void 24 test_double(void) 25 { 26 double d; 27 CHK(cstr_to_double(NULL, &d) == RES_BAD_ARG); 28 CHK(cstr_to_double("a", &d) == RES_BAD_ARG); 29 CHK(cstr_to_double(STR(PI), &d) == RES_OK); 30 CHK(d == PI); 31 CHK(cstr_to_double(" 1.e-3", &d) == RES_OK); 32 CHK(d == 1.e-3); 33 CHK(cstr_to_double("+1.E+3 ", &d) == RES_OK); 34 CHK(d == 1.e3); 35 CHK(cstr_to_double("INF", &d) == RES_OK); 36 CHK(d == INF); 37 CHK(cstr_to_double("INFINITY", &d) == RES_OK); 38 CHK(d == INF); 39 CHK(cstr_to_double("", &d) == RES_BAD_ARG); 40 } 41 42 static void 43 test_float(void) 44 { 45 float f; 46 CHK(cstr_to_float(NULL, &f) == RES_BAD_ARG); 47 CHK(cstr_to_float("a", &f) == RES_BAD_ARG); 48 CHK(cstr_to_float(STR(PI), &f) == RES_OK); 49 CHK(f == (float)PI); 50 CHK(cstr_to_float("1.e-1", &f) == RES_OK); 51 CHK(f == 1.e-1f); 52 CHK(cstr_to_float("+1.E+3", &f) == RES_OK); 53 CHK(f == 1.e3f); 54 CHK(cstr_to_float("INF", &f) == RES_OK); 55 CHK(f == (float)INF); 56 CHK(cstr_to_float("INFINITY", &f) == RES_OK); 57 CHK(f == (float)INF); 58 CHK(cstr_to_float(STR(DBL_MAX), &f) == RES_BAD_ARG); 59 CHK(cstr_to_float(STR(DBL_MIN), &f) == RES_BAD_ARG); 60 CHK(cstr_to_float("0.0", &f) == RES_OK); 61 CHK(f == 0.f); 62 CHK(cstr_to_float("-0.0", &f) == RES_OK); 63 CHK(f == 0.f); 64 } 65 66 static void 67 test_long(void) 68 { 69 long l; 70 CHK(cstr_to_long(NULL, &l) == RES_BAD_ARG); 71 CHK(cstr_to_long("a", &l) == RES_BAD_ARG); 72 CHK(cstr_to_long("1.e-3", &l) == RES_BAD_ARG); 73 CHK(cstr_to_long("1", &l) == RES_OK); 74 CHK(l == 1); 75 CHK(cstr_to_long("-1", &l) == RES_OK); 76 CHK(l == -1); 77 CHK(cstr_to_long("+1234567890", &l) == RES_OK); 78 CHK(l == 1234567890); 79 CHK(cstr_to_long(" \t+1234567890 \t ", &l) == RES_OK); 80 CHK(l == 1234567890); 81 CHK(cstr_to_long(" \t+1234567890 \t a", &l) == RES_BAD_ARG); 82 } 83 84 static void 85 test_int(void) 86 { 87 char buf[128]; 88 int i; 89 90 CHK(cstr_to_int(NULL, &i) == RES_BAD_ARG); 91 CHK(cstr_to_int("a", &i) == RES_BAD_ARG); 92 CHK(cstr_to_int("1.e-1", &i) == RES_BAD_ARG); 93 CHK(cstr_to_int("1", &i) == RES_OK); 94 CHK(i == 1); 95 CHK(cstr_to_int("-2", &i) == RES_OK); 96 CHK(i == -2); 97 CHK(cstr_to_int("\t-2 ", &i) == RES_OK); 98 CHK(i == -2); 99 sprintf(buf, "%d", INT_MAX); 100 CHK(cstr_to_int(buf, &i) == RES_OK); 101 CHK(i == INT_MAX); 102 sprintf(buf, "%d", INT_MIN); 103 CHK(cstr_to_int(buf, &i) == RES_OK); 104 CHK(i == INT_MIN); 105 sprintf(buf, "%ld", (int64_t)INT_MAX+1); 106 CHK(cstr_to_int(buf, &i) == RES_BAD_ARG); 107 sprintf(buf, "%ld", (int64_t)INT_MIN-1); 108 CHK(cstr_to_int(buf, &i) == RES_BAD_ARG); 109 } 110 111 static void 112 test_uint(void) 113 { 114 char buf[128]; 115 unsigned u; 116 117 CHK(cstr_to_uint(NULL, &u) == RES_BAD_ARG); 118 CHK(cstr_to_uint("a", &u) == RES_BAD_ARG); 119 CHK(cstr_to_uint("-1", &u) == RES_OK); 120 CHK(u == UINT_MAX); 121 CHK(cstr_to_uint("0.", &u) == RES_BAD_ARG); 122 CHK(cstr_to_uint("0", &u) == RES_OK); 123 CHK(u == 0); 124 CHK(cstr_to_uint("+2", &u) == RES_OK); 125 CHK(u == 2); 126 CHK(cstr_to_uint(" \t+123 \t ", &u) == RES_OK); 127 CHK(u == 123); 128 sprintf(buf, "%u", UINT_MAX); 129 CHK(cstr_to_uint(buf, &u) == RES_OK); 130 CHK(u == UINT_MAX); 131 sprintf(buf, "%lu", (uint64_t)UINT_MAX+1); 132 CHK(cstr_to_uint(buf, &u) == RES_BAD_ARG); 133 } 134 135 static void 136 test_ulong(void) 137 { 138 char buf[128]; 139 unsigned long ul; 140 141 CHK(cstr_to_ulong(NULL, &ul) == RES_BAD_ARG); 142 CHK(cstr_to_ulong("a", &ul) == RES_BAD_ARG); 143 CHK(cstr_to_ulong("-1", &ul) == RES_OK); 144 CHK(ul == ULONG_MAX); 145 CHK(cstr_to_ulong("0.", &ul) == RES_BAD_ARG); 146 CHK(cstr_to_ulong("0", &ul) == RES_OK); 147 CHK(ul == 0); 148 CHK(cstr_to_ulong("+2", &ul) == RES_OK); 149 CHK(ul == 2); 150 CHK(cstr_to_ulong(" \t+123 \t ", &ul) == RES_OK); 151 CHK(ul == 123); 152 sprintf(buf, "%lu", ULONG_MAX); 153 CHK(cstr_to_ulong(buf, &ul) == RES_OK); 154 CHK(ul == ULONG_MAX); 155 sprintf(buf, "%lu%c", ULONG_MAX, '0'); 156 CHK(cstr_to_ulong(buf, &ul) == RES_BAD_ARG); 157 } 158 159 static res_T 160 count_elements(const char* str, void* ptr) 161 { 162 int* counter = ptr; 163 *counter += 1; 164 CHK(str && str[0] != '\0'); 165 return RES_OK; 166 } 167 168 static res_T 169 parse_elmt(const char* str, void* ptr) 170 { 171 char buf[32]; 172 char* key = NULL; 173 char* val = NULL; 174 char* tk_ctx = NULL; 175 int i; 176 (void)ptr; 177 178 CHK(str && str[0] != '\0'); 179 CHK(strlen(str)+1/*Null char*/ < sizeof(buf)); 180 181 strncpy(buf, str, sizeof(buf)); 182 key = strtok_r(buf, "=", &tk_ctx); 183 val = strtok_r(NULL, "=", &tk_ctx); 184 CHK(key); 185 CHK(val); 186 187 if(!strcmp(key, "good")) { 188 CHK(cstr_to_int(val, &i) == RES_OK); 189 } else if(!strcmp(key, "bad")) { 190 CHK(cstr_to_int(val, &i) == RES_OK); 191 i = !i; 192 } else { 193 FATAL("Unreachable code.\n"); 194 } 195 return i ? RES_OK : RES_BAD_ARG; 196 } 197 198 static void 199 test_list(void) 200 { 201 int n = 0; 202 CHK(cstr_parse_list(NULL, ':', count_elements, &n) == RES_BAD_ARG); 203 CHK(cstr_parse_list("", ':', NULL, NULL) == RES_BAD_ARG); 204 CHK(cstr_parse_list("", ':', count_elements, &n) == RES_OK); 205 CHK(n == 0); 206 CHK(cstr_parse_list("Hello", ':', count_elements, &n) == RES_OK); 207 CHK(n == 1); 208 n = 0; 209 CHK(cstr_parse_list("Hello, world!", ':', count_elements, &n) == RES_OK); 210 CHK(n == 1); 211 n = 0; 212 CHK(cstr_parse_list("Hello, world!", ' ', count_elements, &n) == RES_OK); 213 CHK(n == 2); 214 n = 0; 215 CHK(cstr_parse_list("1;2;3;1e-7;abcdef;0x32;key=value", ';', count_elements, &n) == RES_OK); 216 CHK(n == 7); 217 218 CHK(cstr_parse_list("good=1", ',', parse_elmt, NULL) == RES_OK); 219 CHK(cstr_parse_list("bad=0", ',', parse_elmt, NULL) == RES_OK); 220 CHK(cstr_parse_list("good=1,bad=0", ',', parse_elmt, NULL) == RES_OK); 221 CHK(cstr_parse_list("good=0,bad=0", ',', parse_elmt, NULL) == RES_BAD_ARG); 222 CHK(cstr_parse_list("good=1,bad=1", ',', parse_elmt, NULL) == RES_BAD_ARG); 223 CHK(cstr_parse_list("good=0,bad=1", ',', parse_elmt, NULL) == RES_BAD_ARG); 224 CHK(cstr_parse_list("bad=0,good=0", ',', parse_elmt, NULL) == RES_BAD_ARG); 225 CHK(cstr_parse_list("bad=0,good=1", ',', parse_elmt, NULL) == RES_OK); 226 } 227 228 static void 229 test_list_double(void) 230 { 231 double dlist[4]; 232 size_t len; 233 234 CHK(cstr_to_list_double(NULL, ':', dlist, NULL, 3) == RES_BAD_ARG); 235 CHK(cstr_to_list_double("a", ':', dlist, NULL, 3) == RES_BAD_ARG); 236 CHK(cstr_to_list_double("1.e-3:2.0:"STR(PI), ':', dlist, NULL, 3) == RES_OK); 237 CHK(dlist[0] == 1.e-3); 238 CHK(dlist[1] == 2.0); 239 CHK(dlist[2] == PI); 240 CHK(cstr_to_list_double("1.e-3:2.0:", ':', dlist, &len, 3) == RES_OK); 241 CHK(len == 2); 242 CHK(dlist[0] == 1.e-3); 243 CHK(dlist[1] == 2.0); 244 CHK(cstr_to_list_double("-1.0:0.5:1.2:4.3", ':', dlist, &len, 2) == RES_BAD_ARG); 245 CHK(cstr_to_list_double("-1.0:0.5:1.2:4.3a", ':', NULL, &len, 0) == RES_BAD_ARG); 246 CHK(cstr_to_list_double("-1.0:0.5:1.2:4.3", ':', NULL, &len, 0) == RES_OK); 247 CHK(len == 4); 248 CHK(cstr_to_list_double("-1.0:0.5:1.2:4.3", ':', dlist, NULL, len) == RES_OK); 249 CHK(dlist[0] == -1.0); 250 CHK(dlist[1] == 0.5); 251 CHK(dlist[2] == 1.2); 252 CHK(dlist[3] == 4.3); 253 CHK(cstr_to_list_double(" \t -1.0,0.5,1.2,INF \t", ':', dlist, NULL, 4) == RES_BAD_ARG); 254 CHK(cstr_to_list_double(" \t -1.0,0.5,1.2,INF \t", ',', dlist, NULL, 4) == RES_OK); 255 CHK(dlist[0] == -1.0); 256 CHK(dlist[1] == 0.5); 257 CHK(dlist[2] == 1.2); 258 CHK(dlist[3] == INF); 259 CHK(cstr_to_list_double("-1.0.2.3", '.', dlist, NULL, 4) == RES_OK); 260 CHK(dlist[0] == -1); 261 CHK(dlist[1] == 0); 262 CHK(dlist[2] == 2); 263 CHK(dlist[3] == 3); 264 CHK(cstr_to_list_double(" \t -1.0:0.5:1.2:4.3 \ta", ':', dlist, NULL, 4) == RES_BAD_ARG); 265 dlist[1] = dlist[2] = dlist[3] = -1.0; 266 CHK(cstr_to_list_double("1.0", ':', dlist, NULL, 1) == RES_OK); 267 CHK(dlist[0] == 1.0); 268 CHK(dlist[1] == -1.0); 269 CHK(dlist[2] == -1.0); 270 CHK(dlist[3] == -1.0); 271 } 272 273 static void 274 test_list_float(void) 275 { 276 float flist[4]; 277 size_t len; 278 279 CHK(cstr_to_list_float(NULL, ':', flist, NULL, 3) == RES_BAD_ARG); 280 CHK(cstr_to_list_float("a", ':', flist, NULL, 3) == RES_BAD_ARG); 281 CHK(cstr_to_list_float("1.e-3:2.0:"STR(PI), ':', flist, NULL, 3) == RES_OK); 282 CHK(flist[0] == 1.e-3f); 283 CHK(flist[1] == 2.0f); 284 CHK(flist[2] == (float)PI); 285 CHK(cstr_to_list_float("1.e-3:2.0:", ':', flist, &len, 3) == RES_OK); 286 CHK(len == 2); 287 CHK(flist[0] == 1.e-3f); 288 CHK(flist[1] == 2.0f); 289 CHK(cstr_to_list_float("-1.0:0.5:1.2:4.3", ':', flist, &len, 2) == RES_BAD_ARG); 290 CHK(cstr_to_list_float("-1.0:0.5:1.2:4.3a", ':', NULL, &len, 0) == RES_BAD_ARG); 291 CHK(cstr_to_list_float("-1.0:0.5:1.2:4.3", ':', NULL, &len, 0) == RES_OK); 292 CHK(len == 4); 293 CHK(cstr_to_list_float("-1.0:0.5:1.2:4.3", ':', flist, NULL, len) == RES_OK); 294 CHK(flist[0] == -1.0f); 295 CHK(flist[1] == 0.5f); 296 CHK(flist[2] == 1.2f); 297 CHK(flist[3] == 4.3f); 298 CHK(cstr_to_list_float(" \t -1.0,0.5,1.2,INF \t", ':', flist, NULL, 4) == RES_BAD_ARG); 299 CHK(cstr_to_list_float(" \t -1.0,0.5,1.2,INF \t", ',', flist, NULL, 4) == RES_OK); 300 CHK(flist[0] == -1.0f); 301 CHK(flist[1] == 0.5f); 302 CHK(flist[2] == 1.2f); 303 CHK(flist[3] == (float)INF); 304 } 305 306 static void 307 test_list_uint(void) 308 { 309 unsigned ulist[4]; 310 size_t len; 311 312 CHK(cstr_to_list_uint(NULL, ':', ulist, NULL, 3) == RES_BAD_ARG); 313 CHK(cstr_to_list_uint("a", ':', ulist, NULL, 3) == RES_BAD_ARG); 314 CHK(cstr_to_list_uint("1:4:-1", ':', ulist, NULL, 3) == RES_OK); 315 CHK(ulist[0] == 1); 316 CHK(ulist[1] == 4); 317 CHK(ulist[2] == (unsigned)-1); 318 CHK(cstr_to_list_uint("1:2::", ':', ulist, &len, 3) == RES_OK); 319 CHK(len == 2); 320 CHK(ulist[0] == 1); 321 CHK(ulist[1] == 2); 322 CHK(cstr_to_list_uint("1:5:2:4", ':', ulist, &len, 2) == RES_BAD_ARG); 323 CHK(cstr_to_list_uint("1:5:2:4.2", ':', NULL, &len, 0) == RES_BAD_ARG); 324 CHK(cstr_to_list_uint("1:5:2:4", ':', NULL, &len, 0) == RES_OK); 325 CHK(len == 4); 326 CHK(cstr_to_list_uint("-1.5.2.3", ':', ulist, NULL, len) == RES_BAD_ARG); 327 CHK(cstr_to_list_uint("-1.5.2.3", '.', ulist, NULL, len) == RES_OK); 328 CHK(ulist[0] == (unsigned)-1); 329 CHK(ulist[1] == 5); 330 CHK(ulist[2] == 2); 331 CHK(ulist[3] == 3); 332 } 333 334 static void 335 test_res_to_cstr(void) 336 { 337 printf("%s\n", res_to_cstr(RES_OK)); 338 printf("%s\n", res_to_cstr(RES_BAD_ARG)); 339 printf("%s\n", res_to_cstr(RES_MEM_ERR)); 340 printf("%s\n", res_to_cstr(RES_IO_ERR)); 341 printf("%s\n", res_to_cstr(RES_UNKNOWN_ERR)); 342 printf("%s\n", res_to_cstr(RES_BAD_OP)); 343 printf("%s\n", res_to_cstr(RES_EOF)); 344 } 345 346 static INLINE size_t 347 size 348 (const size_t teras, 349 const size_t gigas, 350 const size_t megas, 351 const size_t kilos, 352 const size_t bytes) 353 { 354 return (size_t)1024*((size_t)1024*((size_t)1024*((size_t)1024* 355 teras + gigas) + megas) + kilos) + bytes; 356 } 357 358 static void 359 test_size_to_cstr(void) 360 { 361 char dump[512]; 362 size_t len; 363 size_t sz; 364 size_t dump_len; 365 char* tk = 0; 366 367 sz = size(2, 450, 987, 243, 42); 368 369 size_to_cstr(sz, SIZE_ALL, &len, NULL, 0); 370 CHK(len == 30); 371 size_to_cstr(sz, SIZE_ALL, NULL, dump, sizeof(dump)); 372 printf("%s\n", dump); 373 dump_len = strlen(dump); 374 CHK(len == dump_len); 375 376 CHK((tk = strtok(dump, " ")) && !strcmp(tk, "2")); 377 CHK((tk = strtok(NULL, " ")) && !strcmp(tk, "TB")); 378 CHK((tk = strtok(NULL, " ")) && !strcmp(tk, "450")); 379 CHK((tk = strtok(NULL, " ")) && !strcmp(tk, "GB")); 380 CHK((tk = strtok(NULL, " ")) && !strcmp(tk, "987")); 381 CHK((tk = strtok(NULL, " ")) && !strcmp(tk, "MB")); 382 CHK((tk = strtok(NULL, " ")) && !strcmp(tk, "243")); 383 CHK((tk = strtok(NULL, " ")) && !strcmp(tk, "KB")); 384 CHK((tk = strtok(NULL, " ")) && !strcmp(tk, "42")); 385 CHK((tk = strtok(NULL, " ")) && !strcmp(tk, "B")); 386 CHK((tk = strtok(NULL, " ")) == NULL); 387 388 /* Check string truncation */ 389 size_to_cstr(sz, SIZE_ALL, &len, dump, dump_len - 3 + 1/*null char*/); 390 CHK(len == 30); 391 CHK((tk = strtok(dump, " ")) && !strcmp(tk, "2")); 392 CHK((tk = strtok(NULL, " ")) && !strcmp(tk, "TB")); 393 CHK((tk = strtok(NULL, " ")) && !strcmp(tk, "450")); 394 CHK((tk = strtok(NULL, " ")) && !strcmp(tk, "GB")); 395 CHK((tk = strtok(NULL, " ")) && !strcmp(tk, "987")); 396 CHK((tk = strtok(NULL, " ")) && !strcmp(tk, "MB")); 397 CHK((tk = strtok(NULL, " ")) && !strcmp(tk, "243")); 398 CHK((tk = strtok(NULL, " ")) && !strcmp(tk, "KB")); 399 CHK((tk = strtok(NULL, " ")) && !strcmp(tk, "4")); 400 CHK((tk = strtok(NULL, " ")) == NULL); 401 402 size_to_cstr(sz, SIZE_GBYTE|SIZE_MBYTE, &len, dump, sizeof(dump)); 403 CHK(len == strlen(dump)); 404 CHK((tk = strtok(dump, " ")) && !strcmp(tk, "2498")); 405 CHK((tk = strtok(NULL, " ")) && !strcmp(tk, "GB")); 406 CHK((tk = strtok(NULL, " ")) && !strcmp(tk, "987")); 407 CHK((tk = strtok(NULL, " ")) && !strcmp(tk, "MB")); 408 CHK((tk = strtok(NULL, " ")) == NULL); 409 410 size_to_cstr(sz, SIZE_GBYTE|SIZE_KBYTE|SIZE_BYTE, &len, dump, sizeof(dump)); 411 CHK(len == strlen(dump)); 412 CHK((tk = strtok(dump, " ")) && !strcmp(tk, "2498")); 413 CHK((tk = strtok(NULL, " ")) && !strcmp(tk, "GB")); 414 CHK((tk = strtok(NULL, " ")) && !strcmp(tk, "1010931")); 415 CHK((tk = strtok(NULL, " ")) && !strcmp(tk, "KB")); 416 CHK((tk = strtok(NULL, " ")) && !strcmp(tk, "42")); 417 CHK((tk = strtok(NULL, " ")) && !strcmp(tk, "B")); 418 CHK((tk = strtok(NULL, " ")) == NULL); 419 420 size_to_cstr(sz, 0, &len, dump, sizeof(dump)); 421 CHK(len == 0 && dump[0] == '\0'); 422 423 size_to_cstr(0, SIZE_ALL, NULL, dump, sizeof(dump)); 424 CHK((tk = strtok(dump, " ")) && !strcmp(tk, "0")); 425 CHK((tk = strtok(NULL, " ")) && !strcmp(tk, "B")); 426 CHK((tk = strtok(NULL, " ")) == NULL); 427 428 size_to_cstr(0, SIZE_TBYTE, NULL, dump, sizeof(dump)); 429 CHK((tk = strtok(dump, " ")) && !strcmp(tk, "0")); 430 CHK((tk = strtok(NULL, " ")) && !strcmp(tk, "TB")); 431 CHK((tk = strtok(NULL, " ")) == NULL); 432 433 sz = size(0, 3, 0, 17, 0); 434 size_to_cstr(sz, SIZE_ALL, NULL, dump, sizeof(dump)); 435 CHK((tk = strtok(dump, " ")) && !strcmp(tk, "3")); 436 CHK((tk = strtok(NULL, " ")) && !strcmp(tk, "GB")); 437 CHK((tk = strtok(NULL, " ")) && !strcmp(tk, "17")); 438 CHK((tk = strtok(NULL, " ")) && !strcmp(tk, "KB")); 439 CHK((tk = strtok(NULL, " ")) == NULL); 440 } 441 442 int 443 main(int argc, char** argv) 444 { 445 (void)argc, (void)argv; 446 test_double(); 447 test_float(); 448 test_long(); 449 test_int(); 450 test_uint(); 451 test_ulong(); 452 test_list(); 453 test_list_double(); 454 test_list_float(); 455 test_list_uint(); 456 test_res_to_cstr(); 457 test_size_to_cstr(); 458 return 0; 459 }