summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorskeshari12 <skeshari@msystechnologies.com>2022-04-12 15:50:38 +0530
committerskeshari12 <skeshari@msystechnologies.com>2022-04-12 15:50:38 +0530
commit0f05b88d19bf180499d4401c41918ff4a87b4d67 (patch)
tree9324430034066a5d77b7baae60de2f19c289735b
parent66ca90cec495a545638d65ca307dee35b382d322 (diff)
downloadffi-yajl-0f05b88d19bf180499d4401c41918ff4a87b4d67.tar.gz
fix error
Signed-off-by: skeshari12 <skeshari@msystechnologies.com>
-rw-r--r--Gemfile14
-rw-r--r--ext/dlopen/dlopen.c40
-rw-r--r--ext/dlopen/extconf.rb16
-rw-r--r--ext/encoder/encoder.c394
-rw-r--r--ext/encoder/extconf.rb65
-rw-r--r--ext/parser/extconf.rb65
-rw-r--r--ext/parser/parser.c240
7 files changed, 830 insertions, 4 deletions
diff --git a/Gemfile b/Gemfile
index 30fe201..6859635 100644
--- a/Gemfile
+++ b/Gemfile
@@ -6,13 +6,19 @@ group :development do
# for testing loading concurrently with yajl-ruby, not on jruby
# gem 'yajl-ruby', platforms: [ :ruby, :mswin, :mingw ]
gem "ffi"
- gem "rake", ">= 10.1"
- gem "rspec", "~> 3.0"
+ gem "rake"#, ">= 10.1"
+ gem "rspec"#, "~> 3.0"
gem "pry", "~> 0.9"
- gem "rake-compiler", "~> 1.0"
- gem "rack", "~> 2.0"
+ gem "rake-compiler"#, "~> 1.0"
+ gem "rack"#, "~> 2.0"
end
group :development_extras do
gem "chefstyle"
end
+
+instance_eval(ENV["GEMFILE_MOD"]) if ENV["GEMFILE_MOD"]
+
+# If you want to load debugging tools into the bundle exec sandbox,
+# add these additional dependencies into Gemfile.local
+eval_gemfile(__FILE__ + ".local") if File.exist?(__FILE__ + ".local") \ No newline at end of file
diff --git a/ext/dlopen/dlopen.c b/ext/dlopen/dlopen.c
new file mode 100644
index 0000000..eb81a21
--- /dev/null
+++ b/ext/dlopen/dlopen.c
@@ -0,0 +1,40 @@
+#include <ruby.h>
+
+#if defined(HAVE_DLFCN_H)
+# include <dlfcn.h>
+#ifndef RTLD_LAZY
+#define RTLD_LAZY 0
+#endif
+#ifndef RTLD_GLOBAL
+#define RTLD_GLOBAL 0
+#endif
+#ifndef RTLD_NOW
+#define RTLD_NOW 0
+#endif
+#else
+# if defined(_WIN32)
+# include <windows.h>
+# define dlopen(name,flag) ((void*)LoadLibrary(name))
+# define dlerror() strerror(rb_w32_map_errno(GetLastError()))
+# define dlsym(handle,name) ((void*)GetProcAddress((handle),(name)))
+# define RTLD_LAZY -1
+# define RTLD_NOW -1
+# define RTLD_GLOBAL -1
+# endif
+#endif
+
+static VALUE mFFI_Yajl, mDlopen, mExt;
+
+static VALUE mDlopen_dlopen(VALUE self, VALUE file) {
+ if (dlopen(RSTRING_PTR(file), RTLD_NOW|RTLD_GLOBAL) == NULL) {
+ rb_raise(rb_eArgError, "%s", dlerror());
+ }
+ return Qnil;
+}
+
+void Init_dlopen() {
+ mFFI_Yajl = rb_define_module("FFI_Yajl");
+ mExt = rb_define_module_under(mFFI_Yajl, "Ext");
+ mDlopen = rb_define_module_under(mExt, "Dlopen");
+ rb_define_method(mDlopen, "dlopen", mDlopen_dlopen, 1);
+}
diff --git a/ext/dlopen/extconf.rb b/ext/dlopen/extconf.rb
new file mode 100644
index 0000000..803d326
--- /dev/null
+++ b/ext/dlopen/extconf.rb
@@ -0,0 +1,16 @@
+# rubocop:disable Style/GlobalVars
+require "mkmf"
+require "rubygems"
+
+RbConfig::MAKEFILE_CONFIG["CC"] = ENV["CC"] if ENV["CC"]
+
+puts $CFLAGS
+puts $LDFLAGS
+
+have_header("dlfcn.h")
+
+have_library("dl", "dlopen")
+
+dir_config "dlopen"
+
+create_makefile "ffi_yajl/ext/dlopen"
diff --git a/ext/encoder/encoder.c b/ext/encoder/encoder.c
new file mode 100644
index 0000000..6f1492c
--- /dev/null
+++ b/ext/encoder/encoder.c
@@ -0,0 +1,394 @@
+#include <ruby.h>
+#include <yajl/yajl_gen.h>
+
+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;
+}
+
+/*
+ * <Object>#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<RARRAY_LEN(self); i++) {
+ val = rb_ary_entry(self, i);
+ rb_funcall(val, sym_ffi_yajl, 2, rb_yajl_gen, state);
+ }
+
+ gen_array_close(rb_yajl_gen);
+ }
+
+ return Qnil;
+}
+
+static VALUE rb_cNilClass_ffi_yajl(VALUE self, VALUE rb_yajl_gen, VALUE state) {
+ if ( rb_hash_aref(state, rb_str_new2("processing_key")) == Qtrue ) {
+ gen_cstring(rb_yajl_gen, "", sizeof("")-1);
+ } else {
+ gen_null(rb_yajl_gen);
+ }
+
+ return Qnil;
+}
+
+static VALUE rb_cTrueClass_ffi_yajl(VALUE self, VALUE rb_yajl_gen, VALUE state) {
+ if ( rb_hash_aref(state, rb_str_new2("processing_key")) == Qtrue ) {
+ gen_cstring(rb_yajl_gen, "true", sizeof("true")-1);
+ } else {
+ gen_true(rb_yajl_gen);
+ }
+
+ return Qnil;
+}
+
+static VALUE rb_cFalseClass_ffi_yajl(VALUE self, VALUE rb_yajl_gen, VALUE state) {
+ if ( rb_hash_aref(state, rb_str_new2("processing_key")) == Qtrue ) {
+ gen_cstring(rb_yajl_gen, "false", sizeof("false")-1);
+ } else {
+ gen_false(rb_yajl_gen);
+ }
+
+ return Qnil;
+}
+
+static VALUE rb_cFixnum_ffi_yajl(VALUE self, VALUE rb_yajl_gen, VALUE state) {
+ VALUE str = rb_funcall(self, rb_intern("to_s"), 0);
+ char *cptr = RSTRING_PTR(str);
+
+ if (memcmp(cptr, "NaN", sizeof("NaN")) == 0 || memcmp(cptr, "Infinity", sizeof("Infinity")) == 0 || memcmp(cptr, "-Infinity", sizeof("-Infinity")) == 0) {
+ rb_raise(cEncodeError, "'%s' is an invalid number", cptr);
+ }
+
+ if ( rb_hash_aref(state, rb_str_new2("processing_key")) == Qtrue ) {
+ gen_string(rb_yajl_gen, str);
+ } else {
+ gen_number(rb_yajl_gen, str);
+ }
+
+ return Qnil;
+}
+
+static VALUE rb_cBignum_ffi_yajl(VALUE self, VALUE rb_yajl_gen, VALUE state) {
+ return rb_cFixnum_ffi_yajl(self, rb_yajl_gen, state);
+}
+
+static VALUE rb_cFloat_ffi_yajl(VALUE self, VALUE rb_yajl_gen, VALUE state) {
+ return rb_cFixnum_ffi_yajl(self, rb_yajl_gen, state);
+}
+
+static VALUE rb_cString_ffi_yajl(VALUE self, VALUE rb_yajl_gen, VALUE state) {
+ return gen_string(rb_yajl_gen, self);
+}
+
+static VALUE rb_cSymbol_ffi_yajl(VALUE self, VALUE rb_yajl_gen, VALUE state) {
+ return gen_string_to_s(rb_yajl_gen, self);
+}
+
+static VALUE rb_cDate_ffi_yajl(VALUE self, VALUE rb_yajl_gen, VALUE state) {
+ return gen_string_to_s(rb_yajl_gen, self);
+}
+
+static VALUE rb_cTime_ffi_yajl(VALUE self, VALUE rb_yajl_gen, VALUE state) {
+ ID sym_strftime = rb_intern("strftime");
+ VALUE str = rb_funcall(self, sym_strftime, 1, rb_str_new2("%Y-%m-%d %H:%M:%S %z"));
+
+ return gen_string(rb_yajl_gen, str);
+}
+
+static VALUE rb_cStringIO_ffi_yajl(VALUE self, VALUE rb_yajl_gen, VALUE state) {
+ return gen_string(rb_yajl_gen, RB_FUNC0("read"));
+}
+
+static VALUE rb_cDateTime_ffi_yajl(VALUE self, VALUE rb_yajl_gen, VALUE state) {
+ return gen_string_to_s(rb_yajl_gen, self);
+}
+
+static VALUE rb_cObject_ffi_yajl(VALUE self, VALUE rb_yajl_gen, VALUE state) {
+ ID sym_to_json = rb_intern("to_json");
+ if ( rb_hash_aref(state, rb_str_new2("processing_key")) != Qtrue && rb_respond_to(self, sym_to_json) ) {
+ VALUE json_opts = rb_hash_aref(state, rb_str_new2("json_opts"));
+ VALUE str = rb_funcall(self, sym_to_json, 1, json_opts);
+
+ gen_number(rb_yajl_gen, str);
+ } else {
+ gen_string_to_s(rb_yajl_gen, self);
+ }
+
+ return Qnil;
+}
+
+void Init_encoder() {
+ mFFI_Yajl = rb_define_module("FFI_Yajl");
+ mEncoder2 = rb_define_class_under(mFFI_Yajl, "Encoder", rb_cObject);
+ cEncodeError = rb_define_class_under(mFFI_Yajl, "EncodeError", rb_eStandardError);
+ mExt = rb_define_module_under(mFFI_Yajl, "Ext");
+ mEncoder = rb_define_module_under(mExt, "Encoder");
+ cYajl_Gen = rb_define_class_under(mEncoder, "YajlGen", rb_cObject);
+ rb_define_method(mEncoder, "do_yajl_encode", mEncoder_do_yajl_encode, 3);
+
+ /* use rb_const_get instead of rb_define_class so that we don't get superclass mismatches */
+ ID sym_Date = rb_intern("Date");
+ cDate = rb_const_get(rb_cObject, sym_Date);
+ ID sym_Time = rb_intern("Time");
+ cTime = rb_const_get(rb_cObject, sym_Time);
+ ID sym_DateTime = rb_intern("DateTime");
+ cDateTime = rb_const_get(rb_cObject, sym_DateTime);
+ ID sym_StringIO = rb_intern("StringIO");
+ cStringIO = rb_const_get(rb_cObject, sym_StringIO);
+
+ rb_define_method(rb_cHash, "ffi_yajl", rb_cHash_ffi_yajl, 2);
+ rb_define_method(rb_cArray, "ffi_yajl", rb_cArray_ffi_yajl, 2);
+ rb_define_method(rb_cNilClass, "ffi_yajl", rb_cNilClass_ffi_yajl, 2);
+ rb_define_method(rb_cTrueClass, "ffi_yajl", rb_cTrueClass_ffi_yajl, 2);
+ rb_define_method(rb_cFalseClass, "ffi_yajl", rb_cFalseClass_ffi_yajl, 2);
+#ifdef rb_cFixnum /* ruby < 2.4 */
+ rb_define_method(rb_cFixnum, "ffi_yajl", rb_cFixnum_ffi_yajl, 2);
+ rb_define_method(rb_cBignum, "ffi_yajl", rb_cBignum_ffi_yajl, 2);
+#else
+ rb_define_method(rb_cInteger, "ffi_yajl", rb_cFixnum_ffi_yajl, 2);
+#endif
+ rb_define_method(rb_cFloat, "ffi_yajl", rb_cFloat_ffi_yajl, 2);
+ rb_define_method(rb_cString, "ffi_yajl", rb_cString_ffi_yajl, 2);
+ rb_define_method(rb_cSymbol, "ffi_yajl", rb_cSymbol_ffi_yajl, 2);
+ rb_define_method(cDate, "ffi_yajl", rb_cDate_ffi_yajl, 2);
+ rb_define_method(cTime, "ffi_yajl", rb_cTime_ffi_yajl, 2);
+ rb_define_method(cDateTime, "ffi_yajl", rb_cDateTime_ffi_yajl, 2);
+ rb_define_method(cStringIO, "ffi_yajl", rb_cStringIO_ffi_yajl, 2);
+ rb_define_method(rb_cObject, "ffi_yajl", rb_cObject_ffi_yajl, 2);
+}
diff --git a/ext/encoder/extconf.rb b/ext/encoder/extconf.rb
new file mode 100644
index 0000000..8f7d14b
--- /dev/null
+++ b/ext/encoder/extconf.rb
@@ -0,0 +1,65 @@
+# rubocop:disable Style/GlobalVars
+require "mkmf"
+require "rubygems"
+require "libyajl2"
+
+RbConfig::MAKEFILE_CONFIG["CC"] = ENV["CC"] if ENV["CC"]
+
+# pick up the vendored libyajl2 out of the libyajl2 gem
+$CFLAGS = " -I#{Libyajl2.include_path} #{$CFLAGS}"
+$LDFLAGS = " -L#{Libyajl2.opt_path} #{$LDFLAGS}"
+
+# remove "-Wl,--no-undefined" flag if existent to allow for loading with dlopen
+$LDFLAGS.slice!("-Wl,--no-undefined")
+
+puts $CFLAGS
+puts $LDFLAGS
+
+# except if you're doing an unoptimized gcc install we're going to help you out a bit
+if RbConfig::MAKEFILE_CONFIG["CC"] =~ /gcc|clang/
+ $CFLAGS << " -O3" unless $CFLAGS[/-O\d/]
+ # how many people realize that -Wall is a compiler-specific flag???
+ # apparently not many based on reading lots of shitty extconf.rb's out there
+ $CFLAGS << " -Wall"
+end
+
+def windows?
+ !!(RUBY_PLATFORM =~ /mswin|mingw|cygwin|windows/)
+end
+
+if windows?
+ # include our libyajldll.a definitions on windows in the libyajl2 gem
+ $libs = "#{$libs} -lyajldll"
+end
+
+# NOTE: find_library has the side effect of adding -lyajl to the flags which we are deliberately
+# avoiding doing with the libyajl2-gem (allowing it to be lazily loaded with dlopen)
+if !windows? && !find_header("yajl/yajl_tree.h")
+ puts "libyajl2 headers not found in libyajl2-gem, searching for system libraries..."
+
+ HEADER_DIRS = [
+ "/opt/local/include", # MacPorts
+ "/usr/local/include", # /usr/local
+ RbConfig::CONFIG["includedir"], # Ruby
+ "/usr/include", # (default)
+ ].freeze
+
+ LIB_DIRS = [
+ "/opt/local/lib", # MacPorts
+ "/usr/local/lib", # /usr/local + Homebrew
+ RbConfig::CONFIG["libdir"], # Ruby
+ "/usr/lib", # (default)
+ ].freeze
+
+ # add --with-yajl-dir, --with-yajl-include, --with-yajl-lib
+ dir_config("yajl", HEADER_DIRS, LIB_DIRS)
+
+ # here we use find_library in order to deliberately link with -lyajl as a useful side-effect
+ unless find_header("yajl/yajl_tree.h") && find_library("yajl", "yajl_complete_parse")
+ abort "libyajl2 is missing. please install libyajl2"
+ end
+end
+
+dir_config "encoder"
+
+create_makefile "ffi_yajl/ext/encoder"
diff --git a/ext/parser/extconf.rb b/ext/parser/extconf.rb
new file mode 100644
index 0000000..5611934
--- /dev/null
+++ b/ext/parser/extconf.rb
@@ -0,0 +1,65 @@
+# rubocop:disable Style/GlobalVars
+require "mkmf"
+require "rubygems"
+require "libyajl2"
+
+RbConfig::MAKEFILE_CONFIG["CC"] = ENV["CC"] if ENV["CC"]
+
+# pick up the vendored libyajl2 out of the libyajl2 gem
+$CFLAGS = "-I#{Libyajl2.include_path} #{$CFLAGS}"
+$LDFLAGS = "-L#{Libyajl2.opt_path} #{$LDFLAGS}"
+
+# remove "-Wl,--no-undefined" flag if existent to allow for loading with dlopen
+$LDFLAGS.slice!("-Wl,--no-undefined")
+
+puts $CFLAGS
+puts $LDFLAGS
+
+# except if you're doing an unoptimized gcc install we're going to help you out a bit
+if RbConfig::MAKEFILE_CONFIG["CC"] =~ /gcc|clang/
+ $CFLAGS << " -O3" unless $CFLAGS[/-O\d/]
+ # how many people realize that -Wall is a compiler-specific flag???
+ # apparently not many based on reading lots of shitty extconf.rb's out there
+ $CFLAGS << " -Wall"
+end
+
+def windows?
+ !!(RUBY_PLATFORM =~ /mswin|mingw|cygwin|windows/)
+end
+
+if windows?
+ # include our libyajldll.a definitions on windows in the libyajl2 gem
+ $libs = "#{$libs} -lyajldll"
+end
+
+# NOTE: find_library has the side effect of adding -lyajl to the flags which we are deliberately
+# avoiding doing with the libyajl2-gem (allowing it to be lazily loaded with dlopen)
+if !windows? && !find_header("yajl/yajl_tree.h")
+ puts "libyajl2 headers not found in libyajl2-gem, searching for system libraries..."
+
+ HEADER_DIRS = [
+ "/opt/local/include", # MacPorts
+ "/usr/local/include", # /usr/local
+ RbConfig::CONFIG["includedir"], # Ruby
+ "/usr/include", # (default)
+ ].freeze
+
+ LIB_DIRS = [
+ "/opt/local/lib", # MacPorts
+ "/usr/local/lib", # /usr/local + Homebrew
+ RbConfig::CONFIG["libdir"], # Ruby
+ "/usr/lib", # (default)
+ ].freeze
+
+ # add --with-yajl-dir, --with-yajl-include, --with-yajl-lib
+ dir_config("yajl", HEADER_DIRS, LIB_DIRS)
+
+ # here we use find_library in order to deliberately link with -lyajl as a useful side-effect
+ unless find_header("yajl/yajl_tree.h") && find_library("yajl", "yajl_complete_parse")
+ abort "libyajl2 is missing. please install libyajl2"
+ end
+end
+
+dir_config "parser"
+
+create_makefile "ffi_yajl/ext/parser"
diff --git a/ext/parser/parser.c b/ext/parser/parser.c
new file mode 100644
index 0000000..c0b8eab
--- /dev/null
+++ b/ext/parser/parser.c
@@ -0,0 +1,240 @@
+#include <ruby.h>
+#include <yajl/yajl_parse.h>
+
+#ifdef HAVE_RUBY_ENCODING_H
+#include <ruby/encoding.h>
+static rb_encoding *utf8Encoding;
+#endif
+
+static VALUE mFFI_Yajl, mExt, mParser, cParseError;
+
+typedef struct {
+ VALUE self;
+ int symbolizeKeys;
+ int uniqueKeyChecking;
+} CTX;
+
+void set_value(CTX *ctx, VALUE val) {
+ VALUE stack = rb_ivar_get(ctx->self, rb_intern("stack"));
+ VALUE key = rb_ivar_get(ctx->self, rb_intern("key"));
+ long len = RARRAY_LEN(stack);
+ VALUE last = rb_ary_entry(stack, len-1);
+ switch (TYPE(last)) {
+ case T_ARRAY:
+ rb_ary_push(last, val);
+ break;
+ case T_HASH:
+ if ( ctx->uniqueKeyChecking ) {
+ ID sym_has_key = rb_intern("has_key?");
+ if ( rb_funcall(last, sym_has_key, 1, key) == Qtrue ) {
+ rb_raise(cParseError, "repeated key: %s", RSTRING_PTR(key));
+ }
+ }
+ rb_hash_aset(last, key, val);
+ break;
+ default:
+ rb_ary_push(stack, val);
+ break;
+ }
+}
+
+void set_key(CTX *ctx, VALUE key) {
+ rb_ivar_set(ctx->self, rb_intern("key"), key);
+}
+
+void start_object(CTX *ctx, VALUE obj) {
+ VALUE key_stack = rb_ivar_get(ctx->self, rb_intern("key_stack"));
+ VALUE key = rb_ivar_get(ctx->self, rb_intern("key"));
+ VALUE stack = rb_ivar_get(ctx->self, rb_intern("stack"));
+
+ rb_ary_push(key_stack, key);
+ rb_ary_push(stack, obj);
+}
+
+void end_object(CTX *ctx) {
+ VALUE key_stack = rb_ivar_get(ctx->self, rb_intern("key_stack"));
+ VALUE stack = rb_ivar_get(ctx->self, rb_intern("stack"));
+ rb_ivar_set(ctx->self, rb_intern("key"), rb_ary_pop(key_stack));
+ if ( RARRAY_LEN(stack) > 1 ) {
+ set_value(ctx, rb_ary_pop(stack));
+ }
+}
+
+int null_callback(void *ctx) {
+ set_value(ctx, Qnil);
+ return 1;
+}
+
+int boolean_callback(void *ctx, int boolean) {
+ set_value(ctx, boolean ? Qtrue : Qfalse);
+ return 1;
+}
+
+int integer_callback(void *ctx, long long intVal) {
+ set_value(ctx, LONG2NUM(intVal));
+ return 1;
+}
+
+int double_callback(void *ctx, double doubleVal) {
+ set_value(ctx, rb_float_new(doubleVal));
+ return 1;
+}
+
+int number_callback(void *ctx, const char *numberVal, size_t numberLen) {
+ char *buf = (char *)malloc(numberLen+1);
+ buf[numberLen] = 0;
+ memcpy(buf, numberVal, numberLen);
+ if (memchr(buf, '.', numberLen) ||
+ memchr(buf, 'e', numberLen) ||
+ memchr(buf, 'E', numberLen)) {
+ set_value(ctx, rb_float_new(strtod(buf, NULL)));
+ } else {
+ set_value(ctx, rb_cstr2inum(buf, 10));
+ }
+ free(buf);
+ return 1;
+}
+
+int string_callback(void *ctx, const unsigned char *stringVal, size_t stringLen) {
+ VALUE str = rb_str_new((const char *)stringVal, stringLen);
+#ifdef HAVE_RUBY_ENCODING_H
+ rb_encoding *default_internal_enc = rb_default_internal_encoding();
+ rb_enc_associate(str, utf8Encoding);
+ if (default_internal_enc) {
+ str = rb_str_export_to_enc(str, default_internal_enc);
+ }
+#endif
+ set_value(ctx,str);
+ return 1;
+}
+
+int start_map_callback(void *ctx) {
+ start_object(ctx,rb_hash_new());
+ return 1;
+}
+
+int map_key_callback(void *ctx, const unsigned char *stringVal, size_t stringLen) {
+ VALUE key;
+#ifdef HAVE_RUBY_ENCODING_H
+ rb_encoding *default_internal_enc;
+#endif
+
+ if ( ((CTX *)ctx)->symbolizeKeys ) {
+#ifdef HAVE_RUBY_ENCODING_H
+ ID id = rb_intern3((const char *)stringVal, stringLen, utf8Encoding);
+ key = ID2SYM(id);
+#else
+ VALUE str = rb_str_new((const char *)stringVal, stringLen);
+ key = rb_str_intern(str);
+#endif
+ } else {
+ key = rb_str_new((const char *)stringVal, stringLen);
+#ifdef HAVE_RUBY_ENCODING_H
+ default_internal_enc = rb_default_internal_encoding();
+ rb_enc_associate(key, utf8Encoding);
+ if (default_internal_enc) {
+ key = rb_str_export_to_enc(key, default_internal_enc);
+ }
+#endif
+ }
+ set_key(ctx, key);
+ return 1;
+}
+
+int end_map_callback(void *ctx) {
+ end_object(ctx);
+ return 1;
+}
+
+int start_array_callback(void *ctx) {
+ start_object(ctx,rb_ary_new());
+ return 1;
+}
+
+int end_array_callback(void *ctx) {
+ end_object(ctx);
+ return 1;
+}
+
+static yajl_callbacks callbacks = {
+ null_callback,
+ boolean_callback,
+ integer_callback,
+ double_callback,
+ number_callback,
+ string_callback,
+ start_map_callback,
+ map_key_callback,
+ end_map_callback,
+ start_array_callback,
+ end_array_callback,
+};
+
+int get_opts_key(VALUE self, const char *key) {
+ VALUE opts = rb_iv_get(self, "@opts");
+ if (TYPE(opts) != T_HASH) {
+ rb_raise(rb_eTypeError, "opts is not a valid hash");
+ }
+ return rb_hash_aref(opts, ID2SYM(rb_intern(key))) == Qtrue;
+}
+
+static VALUE mParser_do_yajl_parse(VALUE self, VALUE str, VALUE yajl_opts) {
+ yajl_handle hand;
+ yajl_status stat;
+ unsigned char *err;
+ volatile CTX ctx;
+
+ rb_ivar_set(self, rb_intern("key"), Qnil);
+ rb_ivar_set(self, rb_intern("stack"), rb_ary_new());
+ rb_ivar_set(self, rb_intern("key_stack"), rb_ary_new());
+
+ ctx.self = self;
+ ctx.symbolizeKeys = get_opts_key(self, "symbolize_keys");
+ ctx.uniqueKeyChecking = get_opts_key(self, "unique_key_checking");
+
+ hand = yajl_alloc(&callbacks, NULL, (void *)&ctx);
+
+ if (rb_hash_aref(yajl_opts, ID2SYM(rb_intern("yajl_allow_comments"))) == Qtrue) {
+ yajl_config(hand, yajl_allow_comments, 1);
+ }
+ if (rb_hash_aref(yajl_opts, ID2SYM(rb_intern("yajl_dont_validate_strings"))) == Qtrue) {
+ yajl_config(hand, yajl_dont_validate_strings, 1);
+ }
+ if (rb_hash_aref(yajl_opts, ID2SYM(rb_intern("yajl_allow_trailing_garbage"))) == Qtrue) {
+ yajl_config(hand, yajl_allow_trailing_garbage, 1);
+ }
+ if (rb_hash_aref(yajl_opts, ID2SYM(rb_intern("yajl_allow_multiple_values"))) == Qtrue) {
+ yajl_config(hand, yajl_allow_multiple_values, 1);
+ }
+ if (rb_hash_aref(yajl_opts, ID2SYM(rb_intern("yajl_allow_partial_values"))) == Qtrue) {
+ yajl_config(hand, yajl_allow_partial_values, 1);
+ }
+
+ if ((stat = yajl_parse(hand, (unsigned char *)RSTRING_PTR(str), RSTRING_LEN(str))) != yajl_status_ok) {
+ err = yajl_get_error(hand, 1, (unsigned char *)RSTRING_PTR(str), RSTRING_LEN(str));
+ goto raise;
+ }
+ if ((stat = yajl_complete_parse(hand)) != yajl_status_ok) {
+ err = yajl_get_error(hand, 1, (unsigned char *)RSTRING_PTR(str), RSTRING_LEN(str));
+ goto raise;
+ }
+ yajl_free(hand);
+ return rb_ary_pop(rb_ivar_get(self, rb_intern("stack")));
+
+raise:
+ if (hand) {
+ yajl_free(hand);
+ }
+ rb_raise(cParseError, "%s", err);
+}
+
+void Init_parser() {
+ mFFI_Yajl = rb_define_module("FFI_Yajl");
+ cParseError = rb_define_class_under(mFFI_Yajl, "ParseError", rb_eStandardError);
+ mExt = rb_define_module_under(mFFI_Yajl, "Ext");
+ mParser = rb_define_module_under(mExt, "Parser");
+ rb_define_method(mParser, "do_yajl_parse", mParser_do_yajl_parse, 2);
+#ifdef HAVE_RUBY_ENCODING_H
+ utf8Encoding = rb_utf8_encoding();
+#endif
+}