#include #include static VALUE mFFI_Yajl, mExt, mEncoder, mEncoder2, cEncodeError; static VALUE cDate, cTime, cDateTime, cStringIO; static VALUE cYajl_Gen; /* FIXME: the json gem does a whole bunch of indirection around monkeypatching... not sure if we need to as well... */ static VALUE mEncoder_do_yajl_encode(VALUE self, VALUE obj, VALUE yajl_gen_opts, VALUE json_opts) { ID sym_ffi_yajl = rb_intern("ffi_yajl"); VALUE sym_yajl_gen_beautify = ID2SYM(rb_intern("yajl_gen_beautify")); VALUE sym_yajl_gen_validate_utf8 = ID2SYM(rb_intern("yajl_gen_validate_utf8")); VALUE sym_yajl_gen_indent_string = ID2SYM(rb_intern("yajl_gen_indent_string")); yajl_gen yajl_gen; const unsigned char *buf; size_t len; VALUE state; VALUE ret; VALUE indent_string; VALUE rb_yajl_gen; yajl_gen = yajl_gen_alloc(NULL); if ( rb_hash_aref(yajl_gen_opts, sym_yajl_gen_beautify) == Qtrue ) { yajl_gen_config(yajl_gen, yajl_gen_beautify, 1); } if ( rb_hash_aref(yajl_gen_opts, sym_yajl_gen_validate_utf8) == Qtrue ) { yajl_gen_config(yajl_gen, yajl_gen_validate_utf8, 1); } indent_string = rb_hash_aref(yajl_gen_opts, sym_yajl_gen_indent_string); if (indent_string != Qnil) { yajl_gen_config(yajl_gen, yajl_gen_indent_string, RSTRING_PTR(indent_string)); } else { yajl_gen_config(yajl_gen, yajl_gen_indent_string, " "); } state = rb_hash_new(); rb_hash_aset(state, rb_str_new2("processing_key"), Qfalse); rb_hash_aset(state, rb_str_new2("json_opts"), json_opts); rb_yajl_gen = Data_Wrap_Struct(cYajl_Gen, NULL, NULL, yajl_gen); rb_funcall(obj, sym_ffi_yajl, 2, rb_yajl_gen, state); yajl_gen_get_buf(yajl_gen, &buf, &len); ret = rb_str_new2((char *)buf); yajl_gen_free(yajl_gen); return ret; } int rb_cHash_ffi_yajl_callback(VALUE key, VALUE val, VALUE extra) { ID sym_ffi_yajl = rb_intern("ffi_yajl"); VALUE state = rb_hash_aref(extra, rb_str_new2("state")); VALUE rb_yajl_gen = rb_hash_aref(extra, rb_str_new2("yajl_gen")); rb_hash_aset(state, rb_str_new2("processing_key"), Qtrue); rb_funcall(key, sym_ffi_yajl, 2, rb_yajl_gen, state); rb_hash_aset(state, rb_str_new2("processing_key"), Qfalse); rb_funcall(val, sym_ffi_yajl, 2, rb_yajl_gen, state); return 0; } #define RB_FUNC0(call) rb_funcall(self, rb_intern(call), 0) /* * wrappers around yajl_gen_* functions */ /* encode a c-string as a yajl string */ VALUE gen_cstring(VALUE rb_yajl_gen, char *cptr, int len) { yajl_gen_status status; struct yajl_gen_t *yajl_gen; Data_Get_Struct(rb_yajl_gen, struct yajl_gen_t, yajl_gen); if ((status = yajl_gen_string(yajl_gen, (unsigned char *)cptr, len)) != yajl_gen_status_ok) { rb_funcall(mEncoder2, rb_intern("raise_error_for_status"), 2, INT2FIX(status), rb_str_new(cptr, len)); } return Qnil; } /* encode a ruby-sring as a yajl string */ VALUE gen_string(VALUE rb_yajl_gen, VALUE str) { char *cptr = RSTRING_PTR(str); int len = RSTRING_LEN(str); return gen_cstring(rb_yajl_gen, cptr, len); } /* calls #to_s on an object to encode it as a yajl string */ static VALUE gen_string_to_s(VALUE rb_yajl_gen, VALUE self) { return gen_string(rb_yajl_gen, RB_FUNC0("to_s")); } /* encode a ruby string as a yajl number (also used to embed already-rendered json from #to_json) */ VALUE gen_number(VALUE rb_yajl_gen, VALUE str) { yajl_gen_status status; struct yajl_gen_t *yajl_gen; Data_Get_Struct(rb_yajl_gen, struct yajl_gen_t, yajl_gen); char *cptr = RSTRING_PTR(str); int len = RSTRING_LEN(str); if ((status = yajl_gen_number(yajl_gen, cptr, len)) != yajl_gen_status_ok) { rb_funcall(mEncoder2, rb_intern("raise_error_for_status"), 2, INT2FIX(status), str); } return Qnil; } /* encode hash open */ VALUE gen_map_open(VALUE rb_yajl_gen) { yajl_gen_status status; struct yajl_gen_t *yajl_gen; Data_Get_Struct(rb_yajl_gen, struct yajl_gen_t, yajl_gen); if ((status = yajl_gen_map_open(yajl_gen)) != yajl_gen_status_ok) { rb_funcall(mEncoder2, rb_intern("raise_error_for_status"), 2, INT2FIX(status), rb_str_new2("{")); } return Qnil; } /* encode a hash close */ VALUE gen_map_close(VALUE rb_yajl_gen) { yajl_gen_status status; struct yajl_gen_t *yajl_gen; Data_Get_Struct(rb_yajl_gen, struct yajl_gen_t, yajl_gen); if ((status = yajl_gen_map_close(yajl_gen)) != yajl_gen_status_ok) { rb_funcall(mEncoder2, rb_intern("raise_error_for_status"), 2, INT2FIX(status), rb_str_new2("}")); } return Qnil; } /* encode an array open */ VALUE gen_array_open(VALUE rb_yajl_gen) { yajl_gen_status status; struct yajl_gen_t *yajl_gen; Data_Get_Struct(rb_yajl_gen, struct yajl_gen_t, yajl_gen); if ((status = yajl_gen_array_open(yajl_gen)) != yajl_gen_status_ok) { rb_funcall(mEncoder2, rb_intern("raise_error_for_status"), 2, INT2FIX(status), rb_str_new2("[")); } return Qnil; } /* encode an array close */ VALUE gen_array_close(VALUE rb_yajl_gen) { yajl_gen_status status; struct yajl_gen_t *yajl_gen; Data_Get_Struct(rb_yajl_gen, struct yajl_gen_t, yajl_gen); if ((status = yajl_gen_array_close(yajl_gen)) != yajl_gen_status_ok) { rb_funcall(mEncoder2, rb_intern("raise_error_for_status"), 2, INT2FIX(status), rb_str_new2("]")); } return Qnil; } /* encode a json null */ VALUE gen_null(VALUE rb_yajl_gen) { yajl_gen_status status; struct yajl_gen_t *yajl_gen; Data_Get_Struct(rb_yajl_gen, struct yajl_gen_t, yajl_gen); if ((status = yajl_gen_null(yajl_gen)) != yajl_gen_status_ok) { rb_funcall(mEncoder2, rb_intern("raise_error_for_status"), 2, INT2FIX(status), rb_str_new2("null")); } return Qnil; } /* encode a true value */ VALUE gen_true(VALUE rb_yajl_gen) { yajl_gen_status status; struct yajl_gen_t *yajl_gen; Data_Get_Struct(rb_yajl_gen, struct yajl_gen_t, yajl_gen); if ((status = yajl_gen_bool(yajl_gen, 1)) != yajl_gen_status_ok) { rb_funcall(mEncoder2, rb_intern("raise_error_for_status"), 2, INT2FIX(status), rb_str_new2("true")); } return Qnil; } /* encode a false value */ VALUE gen_false(VALUE rb_yajl_gen) { yajl_gen_status status; struct yajl_gen_t *yajl_gen; Data_Get_Struct(rb_yajl_gen, struct yajl_gen_t, yajl_gen); if ((status = yajl_gen_bool(yajl_gen, 0)) != yajl_gen_status_ok) { rb_funcall(mEncoder2, rb_intern("raise_error_for_status"), 2, INT2FIX(status), rb_str_new2("false")); } return Qnil; } /* * #to_ffi_yajl() method calls */ static VALUE rb_cHash_ffi_yajl(VALUE self, VALUE rb_yajl_gen, VALUE state) { if ( rb_hash_aref(state, rb_str_new2("processing_key")) == Qtrue ) { gen_string(rb_yajl_gen, rb_funcall(self, rb_intern("to_s"), 0)); } else { /* FIXME: i think this got refactored from something else and it is now pointless -- should just pass rb_yajl_gen directly instead of this 'extra' hash -- i don't *think* this indirection is doing anything useful to mark memory against the GC */ VALUE extra = rb_hash_new(); rb_hash_aset(extra, rb_str_new2("yajl_gen"), rb_yajl_gen); rb_hash_aset(extra, rb_str_new2("state"), state); gen_map_open(rb_yajl_gen); rb_hash_foreach(self, rb_cHash_ffi_yajl_callback, extra); gen_map_close(rb_yajl_gen); } return Qnil; } static VALUE rb_cArray_ffi_yajl(VALUE self, VALUE rb_yajl_gen, VALUE state) { if ( rb_hash_aref(state, rb_str_new2("processing_key")) == Qtrue ) { gen_string(rb_yajl_gen, rb_funcall(self, rb_intern("to_s"), 0)); } else { VALUE val; long i; ID sym_ffi_yajl = rb_intern("ffi_yajl"); gen_array_open(rb_yajl_gen); for(i=0; i