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);