resp.c (18291B)
1 #include "resp.h" 2 3 #include <errno.h> 4 #include <stdio.h> 5 #include <stdlib.h> 6 #include <string.h> 7 #include <unistd.h> 8 9 #define MAX_BULK_LEN (256 * 1024) 10 #define LINE_BUF 4096 11 12 static void resp_free_internal(resp_object *o); 13 14 int resp_read_buf(const char *buf, size_t len, resp_object **out_obj) { 15 if (!out_obj) return -1; 16 17 const char *start = buf; 18 const char *p = buf; 19 size_t remaining = len; 20 21 // We need at least 1 byte 22 if (len <= 0) return -1; 23 24 // Ensure we have memory to place data in 25 resp_object *output = *out_obj; 26 if (!output) { 27 *out_obj = output = calloc(1, sizeof(resp_object)); 28 if (!output) return -1; 29 } 30 31 // Skip empty lines (only \r\n) 32 if (p[0] == '\r' || p[0] == '\n') { 33 while (remaining > 0 && (p[0] == '\r' || p[0] == '\n')) { 34 p++; 35 remaining--; 36 } 37 if (remaining == 0) return 0; // only whitespace, need more data 38 } 39 40 // Consume first character for object type detection 41 int type_c = p[0]; 42 remaining--; 43 p++; 44 45 // And act accordingly 46 switch ((char)type_c) { 47 case '+': 48 output->type = output->type ? output->type : RESPT_SIMPLE; 49 if (output->type != RESPT_SIMPLE) { 50 return -2; // Mismatching types 51 } 52 // Read until \r\n, don't include \r\n in string 53 { 54 size_t i = 0; 55 char line[LINE_BUF]; 56 int found_crlf = 0; 57 while (i + 1 < LINE_BUF && remaining > 0) { 58 if (remaining >= 2 && p[0] == '\r' && p[1] == '\n') { 59 p += 2; 60 remaining -= 2; 61 found_crlf = 1; 62 break; 63 } 64 line[i++] = p[0]; 65 p++; 66 remaining--; 67 } 68 if (!found_crlf) { 69 return -1; // Incomplete, need more data 70 } 71 line[i] = '\0'; 72 if (output->u.s) free(output->u.s); 73 output->u.s = strdup(line); 74 } 75 break; 76 77 case '-': 78 output->type = output->type ? output->type : RESPT_ERROR; 79 if (output->type != RESPT_ERROR) { 80 return -2; // Mismatching types 81 } 82 // Read until \r\n, don't include \r\n in string 83 { 84 size_t i = 0; 85 char line[LINE_BUF]; 86 int found_crlf = 0; 87 while (i + 1 < LINE_BUF && remaining > 0) { 88 if (remaining >= 2 && p[0] == '\r' && p[1] == '\n') { 89 p += 2; 90 remaining -= 2; 91 found_crlf = 1; 92 break; 93 } 94 line[i++] = p[0]; 95 p++; 96 remaining--; 97 } 98 if (!found_crlf) { 99 return -1; // Incomplete, need more data 100 } 101 line[i] = '\0'; 102 if (output->u.s) free(output->u.s); 103 output->u.s = strdup(line); 104 } 105 break; 106 107 case ':': 108 output->type = output->type ? output->type : RESPT_INT; 109 if (output->type != RESPT_INT) { 110 return -2; // Mismatching types 111 } 112 // Read until \r\n, don't include \r\n in string 113 // value = strtoll(line); 114 { 115 size_t i = 0; 116 char line[LINE_BUF]; 117 int found_crlf = 0; 118 while (i + 1 < LINE_BUF && remaining > 0) { 119 if (remaining >= 2 && p[0] == '\r' && p[1] == '\n') { 120 p += 2; 121 remaining -= 2; 122 found_crlf = 1; 123 break; 124 } 125 line[i++] = p[0]; 126 p++; 127 remaining--; 128 } 129 if (!found_crlf) { 130 return -1; // Incomplete, need more data 131 } 132 line[i] = '\0'; 133 output->u.i = strtoll(line, NULL, 10); 134 } 135 break; 136 137 case '$': 138 output->type = output->type ? output->type : RESPT_BULK; 139 if (output->type != RESPT_BULK) { 140 return -2; // Mismatching types 141 } 142 // Read until \r\n, don't include \r\n in string 143 // data_length = strtoll(line); 144 { 145 size_t i = 0; 146 char line[LINE_BUF]; 147 int found_crlf = 0; 148 while (i + 1 < LINE_BUF && remaining > 0) { 149 if (remaining >= 2 && p[0] == '\r' && p[1] == '\n') { 150 p += 2; 151 remaining -= 2; 152 found_crlf = 1; 153 break; 154 } 155 line[i++] = p[0]; 156 p++; 157 remaining--; 158 } 159 if (!found_crlf) { 160 return -1; // Incomplete, need more data 161 } 162 line[i] = '\0'; 163 long data_length = strtol(line, NULL, 10); 164 165 if (data_length < 0) { 166 output->u.s = NULL; 167 } else if (data_length == 0) { 168 // Null bulk string or empty string - need \r\n 169 170 if (remaining >= 2 && p[0] == '\r' && p[1] == '\n') { 171 p += 2; 172 remaining -= 2; 173 } else { 174 return -1; // Incomplete, need more data 175 } 176 if (output->u.s) free(output->u.s); 177 output->u.s = strdup(""); 178 } else { 179 // Read data_length bytes 180 if ((size_t)data_length > remaining) { 181 return -1; // not enough data 182 } 183 if (output->u.s) free(output->u.s); 184 output->u.s = malloc((size_t)data_length + 1); 185 if (!output->u.s) return -1; 186 memcpy(output->u.s, p, (size_t)data_length); 187 output->u.s[data_length] = '\0'; 188 p += data_length; 189 remaining -= data_length; 190 // Skip \r\n 191 if (remaining >= 2 && p[0] == '\r' && p[1] == '\n') { 192 p += 2; 193 remaining -= 2; 194 } else { 195 free(output->u.s); 196 output->u.s = NULL; 197 return -1; // Incomplete, need more data 198 } 199 } 200 } 201 break; 202 203 case '*': 204 output->type = output->type ? output->type : RESPT_ARRAY; 205 if (output->type != RESPT_ARRAY) { 206 return -2; // Mismatching types 207 } 208 // Read until \r\n, don't include \r\n in string 209 // items = strtoll(line); 210 { 211 size_t i = 0; 212 char line[LINE_BUF]; 213 int found_crlf = 0; 214 while (i + 1 < LINE_BUF && remaining > 0) { 215 if (remaining >= 2 && p[0] == '\r' && p[1] == '\n') { 216 p += 2; 217 remaining -= 2; 218 found_crlf = 1; 219 break; 220 } 221 line[i++] = p[0]; 222 p++; 223 remaining--; 224 } 225 if (!found_crlf) { 226 return -1; // Incomplete, need more data 227 } 228 line[i] = '\0'; 229 long items = strtol(line, NULL, 10); 230 231 if (items < 0 || items > 65536) { 232 return -1; 233 } 234 235 // Initialize array if needed 236 if (!output->u.arr.elem) { 237 output->u.arr.n = 0; 238 output->u.arr.elem = NULL; 239 } 240 241 for (size_t j = 0; j < (size_t)items; j++) { 242 if (remaining == 0) { 243 return -1; 244 } 245 resp_object *element = NULL; 246 int element_consumed = resp_read_buf(p, remaining, &element); 247 if (element_consumed <= 0) { 248 return -1; 249 } 250 if (resp_array_append_obj(output, element) != 0) { 251 resp_free(element); 252 return -1; 253 } 254 p += element_consumed; 255 remaining -= element_consumed; 256 } 257 } 258 break; 259 260 default: 261 return -1; 262 } 263 264 return (int)(p - start); 265 } 266 267 static int resp_read_byte(int fd) { 268 unsigned char c; 269 ssize_t n = read(fd, &c, 1); 270 if (n != 1) { 271 if (n < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) return -2; 272 return -1; 273 } 274 return (int)c; 275 } 276 277 static int resp_read_line(int fd, char *buf, size_t buf_size) { 278 size_t i = 0; 279 int prev = -1; 280 while (i + 1 < buf_size) { 281 int b = resp_read_byte(fd); 282 if (b < 0) return -1; 283 if (prev == '\r' && b == '\n') { 284 buf[i - 1] = '\0'; 285 return 0; 286 } 287 prev = b; 288 buf[i++] = (char)b; 289 } 290 return -1; 291 } 292 293 resp_object *resp_read(int fd) { 294 int type_c = resp_read_byte(fd); 295 if (type_c < 0) return NULL; 296 if (type_c == -2) return NULL; 297 resp_object *o = calloc(1, sizeof(resp_object)); 298 if (!o) return NULL; 299 char line[LINE_BUF]; 300 switch ((char)type_c) { 301 case '+': 302 o->type = RESPT_SIMPLE; 303 if (resp_read_line(fd, line, sizeof(line)) != 0) { 304 free(o); 305 return NULL; 306 } 307 o->u.s = strdup(line); 308 break; 309 case '-': 310 o->type = RESPT_ERROR; 311 if (resp_read_line(fd, line, sizeof(line)) != 0) { 312 free(o); 313 return NULL; 314 } 315 o->u.s = strdup(line); 316 break; 317 case ':': 318 { 319 if (resp_read_line(fd, line, sizeof(line)) != 0) { 320 free(o); 321 return NULL; 322 } 323 o->type = RESPT_INT; 324 o->u.i = (long long)strtoll(line, NULL, 10); 325 break; 326 } 327 case '$': 328 { 329 if (resp_read_line(fd, line, sizeof(line)) != 0) { 330 free(o); 331 return NULL; 332 } 333 long len = strtol(line, NULL, 10); 334 if (len < 0 || len > (long)MAX_BULK_LEN) { 335 free(o); 336 return NULL; 337 } 338 o->type = RESPT_BULK; 339 if (len == 0) { 340 o->u.s = strdup(""); 341 if (resp_read_line(fd, line, sizeof(line)) != 0) { 342 free(o->u.s); 343 free(o); 344 return NULL; 345 } 346 } else { 347 o->u.s = malloc((size_t)len + 1); 348 if (!o->u.s) { 349 free(o); 350 return NULL; 351 } 352 if (read(fd, o->u.s, (size_t)len) != (ssize_t)len) { 353 free(o->u.s); 354 free(o); 355 return NULL; 356 } 357 o->u.s[len] = '\0'; 358 if (resp_read_byte(fd) != '\r' || resp_read_byte(fd) != '\n') { 359 free(o->u.s); 360 free(o); 361 return NULL; 362 } 363 } 364 break; 365 } 366 case '*': 367 { 368 if (resp_read_line(fd, line, sizeof(line)) != 0) { 369 free(o); 370 return NULL; 371 } 372 long n = strtol(line, NULL, 10); 373 if (n < 0 || n > 65536) { 374 free(o); 375 return NULL; 376 } 377 o->type = RESPT_ARRAY; 378 o->u.arr.n = (size_t)n; 379 o->u.arr.elem = n ? calloc((size_t)n, sizeof(resp_object)) : NULL; 380 if (n && !o->u.arr.elem) { 381 free(o); 382 return NULL; 383 } 384 for (size_t i = 0; i < (size_t)n; i++) { 385 resp_object *sub = resp_read(fd); 386 if (!sub) { 387 for (size_t j = 0; j < i; j++) resp_free_internal(&o->u.arr.elem[j]); 388 free(o->u.arr.elem); 389 free(o); 390 return NULL; 391 } 392 o->u.arr.elem[i] = *sub; 393 free(sub); 394 } 395 break; 396 } 397 default: 398 free(o); 399 return NULL; 400 } 401 return o; 402 } 403 404 static void resp_free_internal(resp_object *o) { 405 if (!o) return; 406 if (o->type == RESPT_SIMPLE || o->type == RESPT_ERROR || o->type == RESPT_BULK) { 407 free(o->u.s); 408 } else if (o->type == RESPT_ARRAY) { 409 for (size_t i = 0; i < o->u.arr.n; i++) resp_free_internal(&o->u.arr.elem[i]); 410 free(o->u.arr.elem); 411 } 412 } 413 414 void resp_free(resp_object *o) { 415 resp_free_internal(o); 416 free(o); 417 } 418 419 resp_object *resp_deep_copy(const resp_object *o) { 420 if (!o) return NULL; 421 resp_object *c = (resp_object *)calloc(1, sizeof(resp_object)); 422 if (!c) return NULL; 423 c->type = o->type; 424 if (o->type == RESPT_SIMPLE || o->type == RESPT_ERROR || o->type == RESPT_BULK) { 425 c->u.s = o->u.s ? strdup(o->u.s) : NULL; 426 if (o->u.s && !c->u.s) { 427 free(c); 428 return NULL; 429 } 430 return c; 431 } 432 if (o->type == RESPT_INT) { 433 c->u.i = o->u.i; 434 return c; 435 } 436 if (o->type == RESPT_ARRAY) { 437 c->u.arr.n = o->u.arr.n; 438 c->u.arr.elem = o->u.arr.n ? (resp_object *)calloc(o->u.arr.n, sizeof(resp_object)) : NULL; 439 if (o->u.arr.n && !c->u.arr.elem) { 440 free(c); 441 return NULL; 442 } 443 for (size_t i = 0; i < o->u.arr.n; i++) { 444 resp_object *sub = resp_deep_copy(&o->u.arr.elem[i]); 445 if (!sub) { 446 for (size_t j = 0; j < i; j++) resp_free_internal(&c->u.arr.elem[j]); 447 free(c->u.arr.elem); 448 free(c); 449 return NULL; 450 } 451 c->u.arr.elem[i] = *sub; 452 free(sub); 453 } 454 return c; 455 } 456 free(c); 457 return NULL; 458 } 459 460 resp_object *resp_map_get(const resp_object *o, const char *key) { 461 if (!o || !key || o->type != RESPT_ARRAY) return NULL; 462 size_t n = o->u.arr.n; 463 if (n & 1) return NULL; 464 for (size_t i = 0; i < n; i += 2) { 465 const resp_object *k = &o->u.arr.elem[i]; 466 const char *s = (k->type == RESPT_BULK || k->type == RESPT_SIMPLE) ? k->u.s : NULL; 467 if (s && strcmp(s, key) == 0 && i + 1 < n) return (resp_object *)&o->u.arr.elem[i + 1]; 468 } 469 return NULL; 470 } 471 472 const char *resp_map_get_string(const resp_object *o, const char *key) { 473 resp_object *val = resp_map_get(o, key); 474 if (!val) return NULL; 475 if (val->type == RESPT_BULK || val->type == RESPT_SIMPLE) return val->u.s; 476 return NULL; 477 } 478 479 void resp_map_set(resp_object *o, const char *key, resp_object *value) { 480 if (!o || !key || o->type != RESPT_ARRAY) return; 481 for (size_t i = 0; i + 1 < o->u.arr.n; i += 2) { 482 const resp_object *k = &o->u.arr.elem[i]; 483 const char *s = (k->type == RESPT_BULK || k->type == RESPT_SIMPLE) ? k->u.s : NULL; 484 if (s && strcmp(s, key) == 0 && i + 1 < o->u.arr.n) { 485 resp_free(&o->u.arr.elem[i + 1]); 486 o->u.arr.elem[i + 1] = *value; 487 free(value); 488 return; 489 } 490 } 491 resp_array_append_bulk(o, key); 492 resp_array_append_obj(o, value); 493 } 494 495 static int resp_append_object(char **buf, size_t *cap, size_t *len, const resp_object *o) { 496 if (!o) return -1; 497 size_t need = *len + 256; 498 if (o->type == RESPT_BULK || o->type == RESPT_SIMPLE || o->type == RESPT_ERROR) { 499 size_t slen = o->u.s ? strlen(o->u.s) : 0; 500 need = *len + 32 + slen + 2; 501 } else if (o->type == RESPT_ARRAY) { 502 need = *len + 32; 503 for (size_t i = 0; i < o->u.arr.n; i++) need += 64; 504 } 505 if (need > *cap) { 506 size_t newcap = need + 4096; 507 char *n = realloc(*buf, newcap); 508 if (!n) return -1; 509 *buf = n; 510 *cap = newcap; 511 } 512 switch (o->type) { 513 case RESPT_SIMPLE: 514 { 515 const char *s = o->u.s ? o->u.s : ""; 516 *len += (size_t)snprintf(*buf + *len, *cap - *len, "+%s\r\n", s); 517 break; 518 } 519 case RESPT_ERROR: 520 { 521 const char *s = o->u.s ? o->u.s : ""; 522 *len += (size_t)snprintf(*buf + *len, *cap - *len, "-%s\r\n", s); 523 break; 524 } 525 case RESPT_INT: 526 *len += (size_t)snprintf(*buf + *len, *cap - *len, ":%lld\r\n", (long long)o->u.i); 527 break; 528 case RESPT_BULK: 529 { 530 const char *s = o->u.s ? o->u.s : ""; 531 size_t slen = strlen(s); 532 *len += (size_t)snprintf(*buf + *len, *cap - *len, "$%zu\r\n%s\r\n", slen, s); 533 break; 534 } 535 case RESPT_ARRAY: 536 { 537 size_t n = o->u.arr.n; 538 *len += (size_t)snprintf(*buf + *len, *cap - *len, "*%zu\r\n", n); 539 for (size_t i = 0; i < n; i++) { 540 if (resp_append_object(buf, cap, len, &o->u.arr.elem[i]) != 0) return -1; 541 } 542 break; 543 } 544 default: 545 return -1; 546 } 547 return 0; 548 } 549 550 int resp_encode_array(int argc, const resp_object *const *argv, char **out_buf, size_t *out_len) { 551 size_t cap = 64; 552 size_t len = 0; 553 char *buf = malloc(cap); 554 if (!buf) return -1; 555 len += (size_t)snprintf(buf + len, cap - len, "*%d\r\n", argc); 556 if (len >= cap) { 557 free(buf); 558 return -1; 559 } 560 for (int i = 0; i < argc; i++) { 561 if (resp_append_object(&buf, &cap, &len, argv[i]) != 0) { 562 free(buf); 563 return -1; 564 } 565 } 566 *out_buf = buf; 567 *out_len = len; 568 return 0; 569 } 570 571 int resp_serialize(const resp_object *o, char **out_buf, size_t *out_len) { 572 size_t cap = 64; 573 size_t len = 0; 574 char *buf = malloc(cap); 575 if (!buf) return -1; 576 if (resp_append_object(&buf, &cap, &len, o) != 0) { 577 free(buf); 578 return -1; 579 } 580 *out_buf = buf; 581 *out_len = len; 582 return 0; 583 } 584 585 resp_object *resp_array_init(void) { 586 resp_object *o = calloc(1, sizeof(resp_object)); 587 if (!o) return NULL; 588 o->type = RESPT_ARRAY; 589 o->u.arr.n = 0; 590 o->u.arr.elem = NULL; 591 return o; 592 } 593 594 resp_object *resp_simple_init(const char *value) { 595 resp_object *o = calloc(1, sizeof(resp_object)); 596 if (!o) return NULL; 597 o->type = RESPT_SIMPLE; 598 o->u.s = value ? strdup(value) : NULL; 599 return o; 600 } 601 602 int resp_array_append_obj(resp_object *destination, resp_object *value) { 603 if (!destination || destination->type != RESPT_ARRAY || !value) return -1; 604 size_t n = destination->u.arr.n; 605 resp_object *new_elem = realloc(destination->u.arr.elem, (n + 1) * sizeof(resp_object)); 606 if (!new_elem) return -1; 607 destination->u.arr.elem = new_elem; 608 destination->u.arr.elem[n] = *value; 609 destination->u.arr.n++; 610 free(value); 611 return 0; 612 } 613 614 resp_object *resp_error_init(const char *value) { 615 resp_object *o = calloc(1, sizeof(resp_object)); 616 if (!o) return NULL; 617 o->type = RESPT_ERROR; 618 o->u.s = strdup(value ? value : ""); 619 if (!o->u.s) { 620 free(o); 621 return NULL; 622 } 623 return o; 624 } 625 626 int resp_array_append_simple(resp_object *destination, const char *str) { 627 resp_object *o = calloc(1, sizeof(resp_object)); 628 if (!o) return -1; 629 o->type = RESPT_SIMPLE; 630 o->u.s = strdup(str ? str : ""); 631 if (!o->u.s) { 632 free(o); 633 return -1; 634 } 635 if (resp_array_append_obj(destination, o) != 0) { 636 free(o->u.s); 637 free(o); 638 return -1; 639 } 640 return 0; 641 } 642 643 int resp_array_append_error(resp_object *destination, const char *str) { 644 resp_object *o = calloc(1, sizeof(resp_object)); 645 if (!o) return -1; 646 o->type = RESPT_ERROR; 647 o->u.s = strdup(str ? str : ""); 648 if (!o->u.s) { 649 free(o); 650 return -1; 651 } 652 if (resp_array_append_obj(destination, o) != 0) { 653 free(o->u.s); 654 free(o); 655 return -1; 656 } 657 return 0; 658 } 659 660 int resp_array_append_bulk(resp_object *destination, const char *str) { 661 resp_object *o = calloc(1, sizeof(resp_object)); 662 if (!o) return -1; 663 o->type = RESPT_BULK; 664 o->u.s = strdup(str ? str : ""); 665 if (!o->u.s) { 666 free(o); 667 return -1; 668 } 669 if (resp_array_append_obj(destination, o) != 0) { 670 free(o->u.s); 671 free(o); 672 return -1; 673 } 674 return 0; 675 } 676 677 int resp_array_append_int(resp_object *destination, long long i) { 678 resp_object *o = malloc(sizeof(resp_object)); 679 if (!o) return -1; 680 o->type = RESPT_INT; 681 o->u.i = i; 682 return resp_array_append_obj(destination, o); 683 }