#include #include #include #include "../flydata.h" extern "C" { #ifdef _WIN32 _declspec(dllexport) #endif void Init_json_ext(); } inline void write_escape_str(const char*& buf, long& buf_len, const char* escape_str, long escape_str_len, std::stringstream& ss) { if (buf_len > 0) { ss.write(buf, buf_len); } ss.write(escape_str, escape_str_len); buf = 0; buf_len = 0; } inline void add_json_value(const char* buf, long buf_len, bool quote, std::stringstream& ss) { if (quote) { ss.put('\"'); } const char* ptr = 0; long len = 0; const char* esc_str = 0; for (long i = 0; i < buf_len; i++) { if (!ptr) { ptr = buf + i; len = 0; } if ((buf[i] >= 0x5d && buf[i] < 0x7f) || // small letter alphabet (buf[i] >= 0x30 && buf[i] < 0x5c) || // digit and capital letter alphabet (buf[i] >= 0x80) || // multi-byte utf-8 (buf[i] >= 0x23 && buf[i] < 0x2f) || // signs (buf[i] == 0x21) // exclamation mark ) { len++; continue; } // Handle JSON special characters. See http://www.json.org/ switch (buf[i]) { case '\"': esc_str = "\\\""; write_escape_str(ptr, len, esc_str, 2, ss); break; case '\\': esc_str = "\\\\"; write_escape_str(ptr, len, esc_str, 2, ss); break; case '/': esc_str = "\\/"; write_escape_str(ptr, len, esc_str, 2, ss); break; case '\t': esc_str = "\\t"; write_escape_str(ptr, len, esc_str, 2, ss); break; case '\n': esc_str = "\\n"; write_escape_str(ptr, len, esc_str, 2, ss); break; case 0x00: esc_str = "\\u0000"; write_escape_str(ptr, len, esc_str, 6, ss); break; case 0x01: esc_str = "\\u0001"; write_escape_str(ptr, len, esc_str, 6, ss); break; case 0x02: esc_str = "\\u0002"; write_escape_str(ptr, len, esc_str, 6, ss); break; case 0x03: esc_str = "\\u0003"; write_escape_str(ptr, len, esc_str, 6, ss); break; case 0x04: esc_str = "\\u0004"; write_escape_str(ptr, len, esc_str, 6, ss); break; case 0x05: esc_str = "\\u0005"; write_escape_str(ptr, len, esc_str, 6, ss); break; case 0x06: esc_str = "\\u0006"; write_escape_str(ptr, len, esc_str, 6, ss); break; case '\a': esc_str = "\\u0007"; write_escape_str(ptr, len, esc_str, 6, ss); break; case '\b': esc_str = "\\b"; write_escape_str(ptr, len, esc_str, 2, ss); break; case 0x0b: esc_str = "\\u000b"; write_escape_str(ptr, len, esc_str, 6, ss); break; case '\f': esc_str = "\\f"; write_escape_str(ptr, len, esc_str, 2, ss); break; case '\r': esc_str = "\\r"; write_escape_str(ptr, len, esc_str, 2, ss); break; case 0x0e: esc_str = "\\u000e"; write_escape_str(ptr, len, esc_str, 6, ss); break; case 0x0f: esc_str = "\\u000f"; write_escape_str(ptr, len, esc_str, 6, ss); break; case 0x10: esc_str = "\\u0010"; write_escape_str(ptr, len, esc_str, 6, ss); break; case 0x11: esc_str = "\\u0011"; write_escape_str(ptr, len, esc_str, 6, ss); break; case 0x12: esc_str = "\\u0012"; write_escape_str(ptr, len, esc_str, 6, ss); break; case 0x13: esc_str = "\\u0013"; write_escape_str(ptr, len, esc_str, 6, ss); break; case 0x14: esc_str = "\\u0014"; write_escape_str(ptr, len, esc_str, 6, ss); break; case 0x15: esc_str = "\\u0015"; write_escape_str(ptr, len, esc_str, 6, ss); break; case 0x16: esc_str = "\\u0016"; write_escape_str(ptr, len, esc_str, 6, ss); break; case 0x17: esc_str = "\\u0017"; write_escape_str(ptr, len, esc_str, 6, ss); break; case 0x18: esc_str = "\\u0018"; write_escape_str(ptr, len, esc_str, 6, ss); break; case 0x19: esc_str = "\\u0019"; write_escape_str(ptr, len, esc_str, 6, ss); break; case 0x1a: esc_str = "\\u001a"; write_escape_str(ptr, len, esc_str, 6, ss); break; case 0x1b: esc_str = "\\u001b"; write_escape_str(ptr, len, esc_str, 6, ss); break; case 0x1c: esc_str = "\\u001c"; write_escape_str(ptr, len, esc_str, 6, ss); break; case 0x1d: esc_str = "\\u001d"; write_escape_str(ptr, len, esc_str, 6, ss); break; case 0x1e: esc_str = "\\u001e"; write_escape_str(ptr, len, esc_str, 6, ss); break; case 0x1f: esc_str = "\\u001f"; write_escape_str(ptr, len, esc_str, 6, ss); break; case 0x7f: esc_str = "\\u007f"; write_escape_str(ptr, len, esc_str, 6, ss); break; default: len++; break; } } if (len > 0) { ss.write(ptr, len); } if (quote) { ss.put('\"'); } } inline void add_json_value(VALUE value, std::stringstream& ss) { switch(TYPE(value)) { case T_STRING: add_json_value(RSTRING_PTR(value), RSTRING_LEN(value), true, ss); break; case T_NIL: add_json_value("null", 4, false, ss); break; case T_TRUE: add_json_value("true", 4, false, ss); break; case T_FALSE: add_json_value("false", 5, false, ss); break; default: VALUE v = rb_funcall(value, rb_intern("to_s"), 0); add_json_value(RSTRING_PTR(v), RSTRING_LEN(v), true, ss); break; } } inline void add_kv_pair(VALUE key, VALUE value, std::stringstream& ss) { if (TYPE(key) != T_STRING) { key = rb_funcall(key, rb_intern("to_s"), 0); } add_json_value(key, ss); ss.put(':'); add_json_value(value, ss); } static VALUE rb_json_generate_kv_pairs(VALUE self, VALUE key_ary, VALUE value_ary) { long key_len = RARRAY_LEN(key_ary); long value_len = RARRAY_LEN(value_ary); if (key_len != value_len) { rb_raise(rb_eArgError, "length of arrays must match"); } std::stringstream* ss = new std::stringstream(); *ss << "{"; for (long i = 0; i < key_len; i++) { if (i > 0) { *ss << ","; } VALUE key = rb_ary_entry(key_ary, i); VALUE value = rb_ary_entry(value_ary, i); add_kv_pair(key, value, *ss); } *ss << "}"; const std::string& str = ss->str(); VALUE json = rb_str_new(str.data(), str.size()); delete ss; ss = 0; int enc = rb_enc_find_index("UTF-8"); rb_enc_associate_index(json, enc); return json; } void Init_json_ext() { VALUE mJSON = rb_define_module("JSON"); rb_define_singleton_method(mJSON, "generate_kv_pairs", __F(&rb_json_generate_kv_pairs), 2); }