// Copyright (c) 2012, 2017 Peter Ohler. All rights reserved. // Licensed under the MIT License. See LICENSE file in the project root for license details. #include #include "dump.h" #include "oj.h" static void dump_leaf(Leaf leaf, int depth, Out out); static void grow(Out out, size_t len) { size_t size = out->end - out->buf; long pos = out->cur - out->buf; char * buf; size *= 2; if (size <= len * 2 + pos) { size += len; } if (out->allocated) { buf = REALLOC_N(out->buf, char, (size + BUFFER_EXTRA)); } else { buf = ALLOC_N(char, (size + BUFFER_EXTRA)); out->allocated = true; memcpy(buf, out->buf, out->end - out->buf + BUFFER_EXTRA); } if (0 == buf) { rb_raise(rb_eNoMemError, "Failed to create string. [%d:%s]\n", ENOSPC, strerror(ENOSPC)); } out->buf = buf; out->end = buf + size; out->cur = out->buf + pos; } inline static void dump_chars(const char *s, size_t size, Out out) { if (out->end - out->cur <= (long)size) { grow(out, size); } memcpy(out->cur, s, size); out->cur += size; *out->cur = '\0'; } static void dump_leaf_str(Leaf leaf, Out out) { switch (leaf->value_type) { case STR_VAL: oj_dump_cstr(leaf->str, strlen(leaf->str), 0, 0, out); break; case RUBY_VAL: oj_dump_cstr(rb_string_value_cstr(&leaf->value), (int)RSTRING_LEN(leaf->value), 0, 0, out); break; case COL_VAL: default: rb_raise(rb_eTypeError, "Unexpected value type %02x.\n", leaf->value_type); break; } } static void dump_leaf_fixnum(Leaf leaf, Out out) { switch (leaf->value_type) { case STR_VAL: dump_chars(leaf->str, strlen(leaf->str), out); break; case RUBY_VAL: if (T_BIGNUM == rb_type(leaf->value)) { oj_dump_bignum(leaf->value, 0, out, false); } else { oj_dump_fixnum(leaf->value, 0, out, false); } break; case COL_VAL: default: rb_raise(rb_eTypeError, "Unexpected value type %02x.\n", leaf->value_type); break; } } static void dump_leaf_float(Leaf leaf, Out out) { switch (leaf->value_type) { case STR_VAL: dump_chars(leaf->str, strlen(leaf->str), out); break; case RUBY_VAL: oj_dump_float(leaf->value, 0, out, false); break; case COL_VAL: default: rb_raise(rb_eTypeError, "Unexpected value type %02x.\n", leaf->value_type); break; } } static void dump_leaf_array(Leaf leaf, int depth, Out out) { size_t size; int d2 = depth + 1; size = 2; if (out->end - out->cur <= (long)size) { grow(out, size); } *out->cur++ = '['; if (0 == leaf->elements) { *out->cur++ = ']'; } else { Leaf first = leaf->elements->next; Leaf e = first; size = d2 * out->indent + 2; do { if (out->end - out->cur <= (long)size) { grow(out, size); } fill_indent(out, d2); dump_leaf(e, d2, out); if (e->next != first) { *out->cur++ = ','; } e = e->next; } while (e != first); size = depth * out->indent + 1; if (out->end - out->cur <= (long)size) { grow(out, size); } fill_indent(out, depth); *out->cur++ = ']'; } *out->cur = '\0'; } static void dump_leaf_hash(Leaf leaf, int depth, Out out) { size_t size; int d2 = depth + 1; size = 2; if (out->end - out->cur <= (long)size) { grow(out, size); } *out->cur++ = '{'; if (0 == leaf->elements) { *out->cur++ = '}'; } else { Leaf first = leaf->elements->next; Leaf e = first; size = d2 * out->indent + 2; do { if (out->end - out->cur <= (long)size) { grow(out, size); } fill_indent(out, d2); oj_dump_cstr(e->key, strlen(e->key), 0, 0, out); *out->cur++ = ':'; dump_leaf(e, d2, out); if (e->next != first) { *out->cur++ = ','; } e = e->next; } while (e != first); size = depth * out->indent + 1; if (out->end - out->cur <= (long)size) { grow(out, size); } fill_indent(out, depth); *out->cur++ = '}'; } *out->cur = '\0'; } static void dump_leaf(Leaf leaf, int depth, Out out) { switch (leaf->rtype) { case T_NIL: oj_dump_nil(Qnil, 0, out, false); break; case T_TRUE: oj_dump_true(Qtrue, 0, out, false); break; case T_FALSE: oj_dump_false(Qfalse, 0, out, false); break; case T_STRING: dump_leaf_str(leaf, out); break; case T_FIXNUM: dump_leaf_fixnum(leaf, out); break; case T_FLOAT: dump_leaf_float(leaf, out); break; case T_ARRAY: dump_leaf_array(leaf, depth, out); break; case T_HASH: dump_leaf_hash(leaf, depth, out); break; default: rb_raise(rb_eTypeError, "Unexpected type %02x.\n", leaf->rtype); break; } } void oj_dump_leaf_to_json(Leaf leaf, Options copts, Out out) { if (0 == out->buf) { out->buf = ALLOC_N(char, 4096); out->end = out->buf + 4095 - BUFFER_EXTRA; // 1 less than end plus extra for possible errors out->allocated = true; } out->cur = out->buf; out->circ_cnt = 0; out->opts = copts; out->hash_cnt = 0; out->indent = copts->indent; dump_leaf(leaf, 0, out); } void oj_write_leaf_to_file(Leaf leaf, const char *path, Options copts) { char buf[4096]; struct _out out; size_t size; FILE * f; out.buf = buf; out.end = buf + sizeof(buf) - BUFFER_EXTRA; out.allocated = false; out.omit_nil = copts->dump_opts.omit_nil; oj_dump_leaf_to_json(leaf, copts, &out); size = out.cur - out.buf; if (0 == (f = fopen(path, "w"))) { rb_raise(rb_eIOError, "%s\n", strerror(errno)); } if (size != fwrite(out.buf, 1, size, f)) { int err = ferror(f); rb_raise(rb_eIOError, "Write failed. [%d:%s]\n", err, strerror(err)); } if (out.allocated) { xfree(out.buf); } fclose(f); }