static json_t *read_bser_pdu(w_jbuffer_t *jr, w_stm_t stm, json_error_t *jerr) { json_int_t needed; json_int_t val; uint32_t ideal; json_int_t need; int r; json_t *obj; jr->rpos += 2; // We don't handle EAGAIN cleanly in here w_stm_set_nonblock(stm, false); if (!w_bser_decode_pdu_len(jr, stm, &val, jerr)) { return NULL; } // val tells us exactly how much storage we need for this PDU need = val - (jr->allocd - jr->wpos); if (need > 0) { ideal = jr->allocd; while (ideal < (uint32_t)need) { ideal *= 2; } if (ideal > jr->allocd) { char *buf = realloc(jr->buf, ideal); if (!buf) { snprintf(jerr->text, sizeof(jerr->text), "out of memory while allocating %" PRIu32 " bytes", ideal); return NULL; } jr->buf = buf; jr->allocd = ideal; } } // We have enough room for the whole thing, let's read it in while ((jr->wpos - jr->rpos) < val) { r = w_stm_read(stm, jr->buf + jr->wpos, jr->allocd - jr->wpos); if (r <= 0) { snprintf(jerr->text, sizeof(jerr->text), "error reading PDU: %s", strerror(errno)); return NULL; } jr->wpos += r; } obj = bunser(jr->buf + jr->rpos, jr->buf + jr->wpos, &needed, jerr); // Ensure that we move the read position to the wpos; we consumed it all jr->rpos = jr->wpos; w_stm_set_nonblock(stm, true); return obj; }
static json_t *bunser_array(const char *buf, const char *end, json_int_t *used, json_error_t *jerr) { json_int_t needed; json_int_t total = 0; json_int_t i, nelems; json_t *arrval; buf++; total++; if (!bunser_int(buf, end - buf, &needed, &nelems)) { if (needed == -1) { snprintf(jerr->text, sizeof(jerr->text), "invalid integer encoding 0x%02x for array length. buf=%p\n", (int)buf[0], buf); return NULL; } *used = needed + total; snprintf(jerr->text, sizeof(jerr->text), "invalid array length encoding 0x%02x (needed %d but have %d)", (int)buf[0], (int)needed, (int)(end - buf)); return NULL; } total += needed; buf += needed; arrval = json_array(); for (i = 0; i < nelems; i++) { json_t *item; needed = 0; item = bunser(buf, end, &needed, jerr); total += needed; buf += needed; if (!item) { json_decref(arrval); *used = total; return NULL; } if (json_array_append_new(arrval, item)) { json_decref(arrval); *used = total; snprintf(jerr->text, sizeof(jerr->text), "failed to append array item"); return NULL; } } *used = total; return arrval; }
static bool check_roundtrip(const char *input, const char *template_text, json_t **expect_p, json_t **got_p) { char *dump_buf, *end; char *jdump; json_t *expected, *decoded, *templ = NULL; json_error_t jerr; json_int_t needed; expected = json_loads(input, 0, &jerr); ok(expected != NULL, "loaded %s: %s", input, jerr.text); if (!expected) { return false; } if (template_text) { templ = json_loads(template_text, 0, &jerr); json_array_set_template(expected, templ); } dump_buf = bdumps(expected, &end); ok(dump_buf != NULL, "dumped something"); if (!dump_buf) { return false;; } hexdump(dump_buf, end); memset(&jerr, 0, sizeof(jerr)); decoded = bunser(dump_buf, end, &needed, &jerr); ok(decoded != NULL, "decoded something (err = %s)", jerr.text); jdump = json_dumps(decoded, 0); ok(jdump != NULL, "dumped %s", jdump); ok(json_equal(expected, decoded), "round-tripped json_equal"); ok(!strcmp(jdump, input), "round-tripped strcmp"); *expect_p = expected; *got_p = decoded; return true; }
static json_t *bunser_object(const char *buf, const char *end, json_int_t *used, json_error_t *jerr) { json_int_t needed; json_int_t total = 0; json_int_t i, nelems; json_t *objval; char keybuf[128]; total = 1; buf++; if (!bunser_int(buf, end - buf, &needed, &nelems)) { *used = needed + total; snprintf(jerr->text, sizeof(jerr->text), "invalid object property count encoding"); return NULL; } total += needed; buf += needed; objval = json_object(); for (i = 0; i < nelems; i++) { const char *start; json_int_t slen; json_t *item; // Read key if (!bunser_bytestring(buf, end - buf, &needed, &start, &slen)) { *used = total + needed; json_decref(objval); snprintf(jerr->text, sizeof(jerr->text), "invalid bytestring for object key"); return NULL; } total += needed; buf += needed; // Saves us allocating a string when the library is going to // do that anyway if ((uint16_t)slen > sizeof(keybuf) - 1) { json_decref(objval); snprintf(jerr->text, sizeof(jerr->text), "object key is too long"); return NULL; } memcpy(keybuf, start, (size_t)slen); keybuf[slen] = '\0'; // Read value item = bunser(buf, end, &needed, jerr); total += needed; buf += needed; if (!item) { json_decref(objval); *used = total; return NULL; } if (json_object_set_new_nocheck(objval, keybuf, item)) { json_decref(item); json_decref(objval); *used = total; snprintf(jerr->text, sizeof(jerr->text), "failed to add object property"); return NULL; } } *used = total; return objval; }
static json_t *bunser_template(const char *buf, const char *end, json_int_t *used, json_error_t *jerr) { json_int_t needed = 0; json_int_t total = 0; json_int_t i, nelems; json_int_t ip, np; json_t *templ = NULL, *arrval, *ret = NULL; buf++; total++; if (*buf != BSER_ARRAY) { snprintf(jerr->text, sizeof(jerr->text), "Expected array encoding, but found 0x%02x", *buf); *used = total; return NULL; } // Load in the property names template templ = bunser_array(buf, end, &needed, jerr); if (!templ) { *used = needed + total; goto bail; } total += needed; buf += needed; // And the number of objects needed = 0; if (!bunser_int(buf, end - buf, &needed, &nelems)) { *used = needed + total; snprintf(jerr->text, sizeof(jerr->text), "invalid object number encoding (needed %d but have %d)", (int)needed, (int)(end - buf)); goto bail; } total += needed; buf += needed; np = json_array_size(templ); // Now load up the array with object values arrval = json_array_of_size((size_t)nelems); for (i = 0; i < nelems; i++) { json_t *item, *val; item = json_object_of_size((size_t)np); for (ip = 0; ip < np; ip++) { if (*buf == BSER_SKIP) { buf++; total++; continue; } needed = 0; val = bunser(buf, end, &needed, jerr); if (!val) { *used = needed + total; goto bail; } buf += needed; total += needed; json_object_set_new_nocheck(item, json_string_value(json_array_get(templ, (size_t)ip)), val); } json_array_append_new(arrval, item); } *used = total; ret = arrval; bail: json_decref(templ); return ret; }