ext/yajl/yajl_ext.c in yajl-ruby-1.4.1 vs ext/yajl/yajl_ext.c in yajl-ruby-1.4.2

- old
+ new

@@ -91,11 +91,15 @@ rb_raise(cEncodeError, "YAJL internal error: attempted to encode to an already-complete document"); case yajl_gen_invalid_number: rb_raise(cEncodeError, "Invalid number: cannot encode Infinity, -Infinity, or NaN"); case yajl_gen_no_buf: rb_raise(cEncodeError, "YAJL internal error: yajl_gen_get_buf was called, but a print callback was specified, so no internal buffer is available"); + case yajl_gen_alloc_error: + rb_raise(cEncodeError, "YAJL internal error: failed to allocate memory"); default: + // fixme: why wasn't this already here?? + rb_raise(cEncodeError, "Encountered unknown YAJL status %d during JSON generation", status); return NULL; } } static void yajl_set_static_value(void * ctx, VALUE val) { @@ -153,22 +157,51 @@ rb_gc_mark(w->on_progress_callback); rb_gc_mark(w->terminator); } } +static VALUE yajl_key_to_string(VALUE obj) { + switch (TYPE(obj)) { + case T_STRING: + return obj; + case T_SYMBOL: + return rb_sym2str(obj); + default: + return rb_funcall(obj, intern_to_s, 0); + } +} + +void yajl_encode_part(void * wrapper, VALUE obj, VALUE io); +struct yajl_encode_hash_iter { + void *w; + VALUE io; +}; + +static int yajl_encode_part_hash_i(VALUE key, VALUE val, VALUE iter_v) { + struct yajl_encode_hash_iter *iter = (struct yajl_encode_hash_iter *)iter_v; + /* key must be a string */ + VALUE keyStr = yajl_key_to_string(key); + + /* the key */ + yajl_encode_part(iter->w, keyStr, iter->io); + /* the value */ + yajl_encode_part(iter->w, val, iter->io); + + return ST_CONTINUE; +} + #define CHECK_STATUS(call) \ if ((status = (call)) != yajl_gen_status_ok) { break; } void yajl_encode_part(void * wrapper, VALUE obj, VALUE io) { - VALUE str, outBuff, otherObj; + VALUE str, outBuff; yajl_encoder_wrapper * w = wrapper; yajl_gen_status status; int idx = 0; const unsigned char * buffer; const char * cptr; unsigned int len; - VALUE keys, entry, keyStr; if (io != Qnil || w->on_progress_callback != Qnil) { status = yajl_gen_get_buf(w->encoder, &buffer, &len); if (status != yajl_gen_status_ok) { yajl_raise_encode_error_for_status(status, obj); @@ -186,28 +219,23 @@ switch (TYPE(obj)) { case T_HASH: CHECK_STATUS(yajl_gen_map_open(w->encoder)); - /* TODO: itterate through keys in the hash */ - keys = rb_funcall(obj, intern_keys, 0); - for(idx=0; idx<RARRAY_LEN(keys); idx++) { - entry = rb_ary_entry(keys, idx); - keyStr = rb_funcall(entry, intern_to_s, 0); /* key must be a string */ - /* the key */ - yajl_encode_part(w, keyStr, io); - /* the value */ - yajl_encode_part(w, rb_hash_aref(obj, entry), io); - } + struct yajl_encode_hash_iter iter; + iter.w = w; + iter.io = io; + rb_hash_foreach(obj, yajl_encode_part_hash_i, (VALUE)&iter); CHECK_STATUS(yajl_gen_map_close(w->encoder)); break; case T_ARRAY: CHECK_STATUS(yajl_gen_array_open(w->encoder)); + + VALUE *ptr = RARRAY_PTR(obj); for(idx=0; idx<RARRAY_LEN(obj); idx++) { - otherObj = rb_ary_entry(obj, idx); - yajl_encode_part(w, otherObj, io); + yajl_encode_part(w, ptr[idx], io); } CHECK_STATUS(yajl_gen_array_close(w->encoder)); break; case T_NIL: CHECK_STATUS(yajl_gen_null(w->encoder)); @@ -217,10 +245,12 @@ break; case T_FALSE: CHECK_STATUS(yajl_gen_bool(w->encoder, 0)); break; case T_FIXNUM: + CHECK_STATUS(yajl_gen_long(w->encoder, FIX2LONG(obj))); + break; case T_FLOAT: case T_BIGNUM: str = rb_funcall(obj, intern_to_s, 0); cptr = RSTRING_PTR(str); len = (unsigned int)RSTRING_LEN(str); @@ -232,10 +262,16 @@ case T_STRING: cptr = RSTRING_PTR(obj); len = (unsigned int)RSTRING_LEN(obj); CHECK_STATUS(yajl_gen_string(w->encoder, (const unsigned char *)cptr, len)); break; + case T_SYMBOL: + str = rb_sym2str(obj); + cptr = RSTRING_PTR(str); + len = (unsigned int)RSTRING_LEN(str); + CHECK_STATUS(yajl_gen_string(w->encoder, (const unsigned char *)cptr, len)); + break; default: if (rb_respond_to(obj, intern_to_json)) { str = rb_funcall(obj, intern_to_json, 0); Check_Type(str, T_STRING); cptr = RSTRING_PTR(str); @@ -276,15 +312,21 @@ void yajl_parse_chunk(const unsigned char * chunk, unsigned int len, yajl_handle parser) { yajl_status stat; stat = yajl_parse(parser, chunk, len); - if (stat != yajl_status_ok && stat != yajl_status_insufficient_data) { + if (stat == yajl_status_ok || stat == yajl_status_insufficient_data) { + // success + } else if (stat == yajl_status_error) { unsigned char * str = yajl_get_error(parser, 1, chunk, len); VALUE errobj = rb_exc_new2(cParseError, (const char*) str); yajl_free_error(parser, str); rb_exc_raise(errobj); + } else { + const char * str = yajl_status_to_string(stat); + VALUE errobj = rb_exc_new2(cParseError, (const char*) str); + rb_exc_raise(errobj); } } /* YAJL Callbacks */ static int yajl_found_null(void * ctx) { @@ -473,17 +515,17 @@ /* * Document-method: parse * * call-seq: - * parse(input, buffer_size=8092) - * parse(input, buffer_size=8092) { |obj| ... } + * parse(input, buffer_size=8192) + * parse(input, buffer_size=8192) { |obj| ... } * * +input+ can either be a string or an IO to parse JSON from * * +buffer_size+ is the size of chunk that will be parsed off the input (if it's an IO) for each loop of the parsing process. - * 8092 is a good balance between the different types of streams (off disk, off a socket, etc...), but this option + * 8192 is a good balance between the different types of streams (off disk, off a socket, etc...), but this option * is here so the caller can better tune their parsing depending on the type of stream being passed. * A larger read buffer will perform better for files off disk, where as a smaller size may be more efficient for * reading off of a socket directly. * * If a block was passed, it's called when an object has been parsed off the stream. This is especially @@ -845,11 +887,11 @@ case yajl_tok_null:; return Qnil; case yajl_tok_bool:; if (memcmp(event.buf, "true", 4) == 0) { return Qtrue; - } else if (memcmp(event.buf, "false", 4) == 0) { + } else if (memcmp(event.buf, "false", 5) == 0) { return Qfalse; } else { rb_raise(cStandardError, "unknown boolean token %s", event.buf); } case yajl_tok_integer:; @@ -882,11 +924,11 @@ case yajl_tok_colon: rb_raise(cParseError, "unexpected colon while constructing value"); default:; - assert(0); + rb_bug("we should never get here"); } } static VALUE rb_yajl_projector_build_string(yajl_event_stream_t parser, yajl_event_t event) { switch (event.token) { @@ -905,10 +947,13 @@ case yajl_tok_string_with_escapes:; { //printf("decoding string with escapes\n"); yajl_buf strBuf = yajl_buf_alloc(parser->funcs); yajl_string_decode(strBuf, (const unsigned char *)event.buf, event.len); + if (yajl_buf_err(strBuf)) { + rb_raise(cParseError, "YAJL internal error: failed to allocate memory"); + } VALUE str = rb_str_new((const char *)yajl_buf_data(strBuf), yajl_buf_len(strBuf)); rb_enc_associate(str, utf8Encoding); yajl_buf_free(strBuf); @@ -920,11 +965,11 @@ return str; } default:; { - assert(0); + rb_bug("we should never get here"); } } } static VALUE rb_protected_yajl_projector_filter(VALUE pointer) { @@ -1100,10 +1145,11 @@ static VALUE rb_yajl_encoder_encode(int argc, VALUE * argv, VALUE self) { yajl_encoder_wrapper * wrapper; const unsigned char * buffer; unsigned int len; VALUE obj, io, blk, outBuff; + yajl_gen_status status; GetEncoder(self, wrapper); rb_scan_args(argc, argv, "11&", &obj, &io, &blk); @@ -1113,10 +1159,14 @@ /* begin encode process */ yajl_encode_part(wrapper, obj, io); /* just make sure we output the remaining buffer */ - yajl_gen_get_buf(wrapper->encoder, &buffer, &len); + status = yajl_gen_get_buf(wrapper->encoder, &buffer, &len); + if (status != yajl_gen_status_ok) { + yajl_raise_encode_error_for_status(status, obj); + } + outBuff = rb_str_new((const char *)buffer, len); #ifdef HAVE_RUBY_ENCODING_H rb_enc_associate(outBuff, utf8Encoding); #endif yajl_gen_clear(wrapper->encoder);