ffi-yajl 2.7.5-universal-mingw-ucrt

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +22 -0
  3. data/README.md +118 -0
  4. data/bin/ffi-yajl-bench +36 -0
  5. data/ext/ffi_yajl/ext/dlopen/dlopen.c +40 -0
  6. data/ext/ffi_yajl/ext/dlopen/extconf.rb +16 -0
  7. data/ext/ffi_yajl/ext/dlopen/yajl.sym +18 -0
  8. data/ext/ffi_yajl/ext/encoder/encoder.c +395 -0
  9. data/ext/ffi_yajl/ext/encoder/extconf.rb +86 -0
  10. data/ext/ffi_yajl/ext/parser/extconf.rb +86 -0
  11. data/ext/ffi_yajl/ext/parser/parser.c +240 -0
  12. data/lib/ffi_yajl/benchmark/MIT-LICENSE +20 -0
  13. data/lib/ffi_yajl/benchmark/encode.rb +82 -0
  14. data/lib/ffi_yajl/benchmark/encode_json_and_marshal.rb +42 -0
  15. data/lib/ffi_yajl/benchmark/encode_json_and_yaml.rb +47 -0
  16. data/lib/ffi_yajl/benchmark/encode_profile.rb +34 -0
  17. data/lib/ffi_yajl/benchmark/http.rb +28 -0
  18. data/lib/ffi_yajl/benchmark/parse.rb +93 -0
  19. data/lib/ffi_yajl/benchmark/parse_json_and_marshal.rb +50 -0
  20. data/lib/ffi_yajl/benchmark/parse_json_and_yaml.rb +55 -0
  21. data/lib/ffi_yajl/benchmark/parse_profile.rb +33 -0
  22. data/lib/ffi_yajl/benchmark/parse_profile_ruby_prof.rb +34 -0
  23. data/lib/ffi_yajl/benchmark/parse_stream.rb +54 -0
  24. data/lib/ffi_yajl/benchmark/subjects/item.json +1 -0
  25. data/lib/ffi_yajl/benchmark/subjects/ohai.json +1216 -0
  26. data/lib/ffi_yajl/benchmark/subjects/ohai.marshal_dump +0 -0
  27. data/lib/ffi_yajl/benchmark/subjects/ohai.yml +975 -0
  28. data/lib/ffi_yajl/benchmark/subjects/twitter_search.json +1 -0
  29. data/lib/ffi_yajl/benchmark/subjects/twitter_stream.json +430 -0
  30. data/lib/ffi_yajl/benchmark/subjects/unicode.json +1 -0
  31. data/lib/ffi_yajl/benchmark.rb +27 -0
  32. data/lib/ffi_yajl/encoder.rb +94 -0
  33. data/lib/ffi_yajl/ext/.keep +0 -0
  34. data/lib/ffi_yajl/ext.rb +49 -0
  35. data/lib/ffi_yajl/ffi/encoder.rb +262 -0
  36. data/lib/ffi_yajl/ffi/parser.rb +163 -0
  37. data/lib/ffi_yajl/ffi.rb +153 -0
  38. data/lib/ffi_yajl/map_library_name.rb +109 -0
  39. data/lib/ffi_yajl/parser.rb +91 -0
  40. data/lib/ffi_yajl/platform.rb +29 -0
  41. data/lib/ffi_yajl/version.rb +25 -0
  42. data/lib/ffi_yajl.rb +50 -0
  43. metadata +128 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 61e4e54489f7ea4835b58105e6cb7b7d759da4a77e2ea72eb90e2064c3027345
4
+ data.tar.gz: 3685d7dc11576660418fdec28fa47b0ea0e31c8e1ccc963339d60ca97d9a5370
5
+ SHA512:
6
+ metadata.gz: b28fc263e5acfce39f71c02a3887c4fcee1dde3ffd3560f2501006d386e60e697ec3cbf91d2676e6289aa009ab48c0b165669d4b905c528d5688826d0c8a85ae
7
+ data.tar.gz: e3372cd7d153a9e85a7fce925dddd0718e76c77f1e540c3b62d5e1fafd288e5b88064d239ee2bc1edd351e53e19583e8c6f7e5524ff445b9d66871581e41de01
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Lamont Granquist
2
+ Copyright (c) 2014 Chef Software, Inc.
3
+ Copyright (c) 2008-2011 Brian Lopez - http://github.com/brianmario
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,118 @@
1
+ # FFI YAJL
2
+
3
+ [![Build status](https://badge.buildkite.com/35c43155d637f5201a5030cd4e7d8025573c0042094e00bbb3.svg?branch=master)](https://buildkite.com/chef-oss/chef-ffi-yajl-master-verify) [![Gem Version](https://badge.fury.io/rb/ffi-yajl.svg)](https://badge.fury.io/rb/ffi-yajl)
4
+
5
+ **Umbrella Project**: [Chef Foundation](https://github.com/chef/chef-oss-practices/blob/master/projects/chef-foundation.md)
6
+
7
+ **Project State**: [Active](https://github.com/chef/chef-oss-practices/blob/master/repo-management/repo-states.md#active)
8
+
9
+ **Issues [Response Time Maximum](https://github.com/chef/chef-oss-practices/blob/master/repo-management/repo-states.md)**: 14 days
10
+
11
+ **Pull Request [Response Time Maximum](https://github.com/chef/chef-oss-practices/blob/master/repo-management/repo-states.md)**: 14 days
12
+
13
+
14
+ ffi-yajl is a Ruby adapter for the [yajl](http://lloyd.github.io/yajl/) JSON parser/generator library. ffi-yajl supports multiple Ruby C extension mechanisms, including both MRI native extensions and FFI in order to be compatible with as many Ruby implementations as possible while providing good performance where possible.
15
+
16
+ ## How to Install
17
+
18
+ **Warning** if building through Omnibus, a preinstalled ffi-yajl and libyajl2-gem version not matching the bundled version of the project you're attempting to build (e.g., Chef) may cause:
19
+
20
+ `cannot find -lyajldll: No such file or directory`
21
+
22
+ If your build script ends up up in this state, a short term fix is `gem uninstall -I libyajl2` before the failing `bundle install`
23
+
24
+
25
+ ### Install from the command-line:
26
+
27
+ ```
28
+ gem install ffi-yajl
29
+ ```
30
+
31
+ Or use a Gemfile:
32
+
33
+ ```
34
+ gem 'ffi-yajl'
35
+ ```
36
+
37
+ ## Supported Ruby VMs:
38
+
39
+ - Ruby 2.2+ and compatible rbx or jruby
40
+
41
+ ## Supported Distros:
42
+
43
+ - Ubuntu 10.04 through 14.10
44
+ - Debian 7.x
45
+ - RHEL/CentOS/Oracle 5.x/6.x/7.x
46
+ - Solaris 9/10/11 (gcc, sun compiler untested)
47
+ - AIX 6.x/7.x (gcc or xlc)
48
+ - Windows 2008r2/2012 (and Win2k/2k3 and consumer versions should work)
49
+
50
+ ## Basic Usage
51
+
52
+ Start by requiring it:
53
+
54
+ ```ruby
55
+ require 'ffi_yajl'
56
+ ```
57
+
58
+ You can encode and parse with class objects:
59
+
60
+ ```ruby
61
+ options_hash = {}
62
+ json = FFI_Yajl::Encoder.encode( {"foo"=>["bar","baz"]}, options_hash )
63
+ hash = FFI_Yajl::Parser.parse( json, options_hash )
64
+ ```
65
+
66
+ Or you can be more object oriented:
67
+
68
+ ```ruby
69
+ options_hash = {}
70
+ encoder = FFI_Yajl::Encoder.new( options_hash )
71
+ json = encoder.encode( {"foo"=>["bar","baz"]} )
72
+ parser = FFI_Yajl::Parser.new( options_hash )
73
+ hash = parser.parse( json )
74
+ ```
75
+
76
+ ## Parser Options
77
+
78
+ - `:check_utf8`
79
+ - `:dont_validate_strings`
80
+ - `:symbolize_keys` (default = false): JSON keys are parsed into symbols instead of strings.
81
+ - `:symbolize_names` (default = false): Alias for `:symbolize_keys`.
82
+ - `:allow_trailing_garbage`
83
+ - `:allow_multiple_values`
84
+ - `:allow_partial_values`
85
+ - `:unique_key_checking` (default = false): Will raise an exception if keys are repeated in hashes in the input JSON. Without this, repeated keys will silently replace the previous key.
86
+
87
+ ## Encoder Options
88
+
89
+ - `:pretty` (default = false): Produces more human readable 'pretty' output.
90
+ - `:validate_utf8` (default = true): Will raise an exception when trying to encode strings that are invalid UTF-8\. When set to false this still will produce valid UTF-8 JSON but will replace invalid characters.
91
+
92
+ ## Forcing FFI or C Extension
93
+
94
+ You can set the environment variable `FORCE_FFI_YAJL` to `ext` or `ffi` to control if the C extension or FFI bindings are used.
95
+
96
+ ## Yajl Library Packaging
97
+
98
+ This library prefers to use the embedded yajl 2.x C library packaged in the libyajl2 gem. In order to use the operating system yajl library (which must be yajl 2.x) the environment variable `USE_SYSTEM_LIBYAJL2` can be set before installing or bundling libyajl2\. This will force the libyajl2 gem to skip compiling its embedded library and the ffi-yajl gem will fallback to using the system yajl library.
99
+
100
+ ## No JSON Gem Compatiblity layer
101
+
102
+ This library does not offer a feature to patch `#to_json` methods into all the ruby classes similarly to the JSON gem or yajl-ruby's `yajl/json_gem` compatibility API. The differences in encoding between the JSON gem and the Yajl C library can produce different output on different systems and makes testing brittle. Such a feature will not be included. It was removed in this pull request and could be easily extracted to its own gem (we have no interest in maintaining that gem):
103
+
104
+ <https://github.com/chef/ffi-yajl/pull/47/files>
105
+
106
+ ## Why This Instead of X?
107
+
108
+ yajl is the only JSON library we've found that has error messages that meet our requirements. The stdlib json gem and oj (at the time we started this project) have error messages like "invalid token at byte 1234," which are probably fine for server use, but in [chef](https://github.com/chef/chef) we frequently deal with user-written JSON documents, which means we need a good user experience when encountering malformed JSON.
109
+
110
+ We previously used brianmario's [yajl-ruby](https://github.com/brianmario/yajl-ruby) project, but we wanted to be able to fallback to using FFI bindings to the C code (so we could support non-MRI rubies) and we also needed some bug fixes in yajl2, but the maintainer wasn't able to devote enough time to the project to make these updates in a timeframe that worked for us.
111
+
112
+ ## Thanks
113
+
114
+ This was initially going to be a clean rewrite of an ffi ruby wrapper around yajl2, but as it progressed more and more code was pulled in from brianmario's existing yajl-ruby gem, particularly all the c extension code, lots of specs and the benchmarks. And the process of writing this would have been much more difficult without being able to draw heavily from already solved problems in yajl-ruby.
115
+
116
+ ## License
117
+
118
+ Given that this draws heavily from the yajl-ruby sources, and could be considered a derivative work, the MIT License from that project has been preserved and this source code has deliberately not been dual licensed under Chef's typical Apache License. See the [LICENSE](https://github.com/chef/ffi-yajl/blob/master/LICENSE) file in this project.
@@ -0,0 +1,36 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $LOAD_PATH << File.expand_path(File.join(File.dirname( File.symlink?(__FILE__) ? File.readlink(__FILE__) : __FILE__ ), "../lib"))
4
+
5
+ require "optparse"
6
+ require "ffi_yajl/benchmark"
7
+
8
+ opts = {}
9
+ optparse = OptionParser.new do |o|
10
+ o.banner = "Usage: ffi-yajl-bench"
11
+
12
+ opts[:profile] = false
13
+ o.on( "-p", "--profile", "Run perftools.rb profiling" ) do
14
+ opts[:profile] = true
15
+ end
16
+
17
+ o.on( "-F", "--ffi", "Force using FFI" ) do
18
+ opts[:ffi] = true
19
+ end
20
+
21
+ o.on( "-E", "--ext", "Force using C ext" ) do
22
+ opts[:ext] = true
23
+ end
24
+ end
25
+
26
+ optparse.parse!
27
+
28
+ ENV["FORCE_FFI_YAJL"] = "ffi" if opts[:ffi]
29
+ ENV["FORCE_FFI_YAJL"] = "ext" if opts[:ext]
30
+
31
+ if opts[:profile]
32
+ FFI_Yajl::Benchmark::ParseProfileRubyProf.new.run
33
+ else
34
+ FFI_Yajl::Benchmark::Parse.new.run
35
+ FFI_Yajl::Benchmark::Encode.new.run
36
+ end
@@ -0,0 +1,40 @@
1
+ #include <ruby.h>
2
+
3
+ #if defined(HAVE_DLFCN_H)
4
+ # include <dlfcn.h>
5
+ #ifndef RTLD_LAZY
6
+ #define RTLD_LAZY 0
7
+ #endif
8
+ #ifndef RTLD_GLOBAL
9
+ #define RTLD_GLOBAL 0
10
+ #endif
11
+ #ifndef RTLD_NOW
12
+ #define RTLD_NOW 0
13
+ #endif
14
+ #else
15
+ # if defined(_WIN32)
16
+ # include <windows.h>
17
+ # define dlopen(name,flag) ((void*)LoadLibrary(name))
18
+ # define dlerror() strerror(rb_w32_map_errno(GetLastError()))
19
+ # define dlsym(handle,name) ((void*)GetProcAddress((handle),(name)))
20
+ # define RTLD_LAZY -1
21
+ # define RTLD_NOW -1
22
+ # define RTLD_GLOBAL -1
23
+ # endif
24
+ #endif
25
+
26
+ static VALUE mFFI_Yajl, mDlopen, mExt;
27
+
28
+ static VALUE mDlopen_dlopen(VALUE self, VALUE file) {
29
+ if (dlopen(RSTRING_PTR(file), RTLD_NOW|RTLD_GLOBAL) == NULL) {
30
+ rb_raise(rb_eArgError, "%s", dlerror());
31
+ }
32
+ return Qnil;
33
+ }
34
+
35
+ void Init_dlopen() {
36
+ mFFI_Yajl = rb_define_module("FFI_Yajl");
37
+ mExt = rb_define_module_under(mFFI_Yajl, "Ext");
38
+ mDlopen = rb_define_module_under(mExt, "Dlopen");
39
+ rb_define_method(mDlopen, "dlopen", mDlopen_dlopen, 1);
40
+ }
@@ -0,0 +1,16 @@
1
+ # rubocop:disable Style/GlobalVars
2
+ require "mkmf"
3
+ require "rubygems"
4
+
5
+ RbConfig::MAKEFILE_CONFIG["CC"] = ENV["CC"] if ENV["CC"]
6
+
7
+ puts $CFLAGS
8
+ puts $LDFLAGS
9
+
10
+ have_header("dlfcn.h")
11
+
12
+ have_library("dl", "dlopen")
13
+
14
+ dir_config "dlopen"
15
+
16
+ create_makefile "ffi_yajl/ext/dlopen"
@@ -0,0 +1,18 @@
1
+ _yajl_gen_alloc
2
+ _yajl_gen_array_close
3
+ _yajl_gen_array_open
4
+ _yajl_gen_bool
5
+ _yajl_gen_config
6
+ _yajl_gen_free
7
+ _yajl_gen_get_buf
8
+ _yajl_gen_map_close
9
+ _yajl_gen_map_open
10
+ _yajl_gen_null
11
+ _yajl_gen_number
12
+ _yajl_gen_string
13
+ _yajl_alloc
14
+ _yajl_complete_parse
15
+ _yajl_config
16
+ _yajl_free
17
+ _yajl_get_error
18
+ _yajl_parse
@@ -0,0 +1,395 @@
1
+ #include <ruby.h>
2
+ #include <yajl/yajl_gen.h>
3
+
4
+ static VALUE mFFI_Yajl, mExt, mEncoder, mEncoder2, cEncodeError;
5
+ static VALUE cDate, cTime, cDateTime, cStringIO;
6
+ static VALUE cYajl_Gen;
7
+
8
+ /* FIXME: the json gem does a whole bunch of indirection around monkeypatching... not sure if we need to as well... */
9
+
10
+ static VALUE mEncoder_do_yajl_encode(VALUE self, VALUE obj, VALUE yajl_gen_opts, VALUE json_opts) {
11
+ ID sym_ffi_yajl = rb_intern("ffi_yajl");
12
+ VALUE sym_yajl_gen_beautify = ID2SYM(rb_intern("yajl_gen_beautify"));
13
+ VALUE sym_yajl_gen_validate_utf8 = ID2SYM(rb_intern("yajl_gen_validate_utf8"));
14
+ VALUE sym_yajl_gen_indent_string = ID2SYM(rb_intern("yajl_gen_indent_string"));
15
+ yajl_gen yajl_gen;
16
+ const unsigned char *buf;
17
+ size_t len;
18
+ VALUE state;
19
+ VALUE ret;
20
+ VALUE indent_string;
21
+ VALUE rb_yajl_gen;
22
+
23
+ yajl_gen = yajl_gen_alloc(NULL);
24
+
25
+ if ( rb_hash_aref(yajl_gen_opts, sym_yajl_gen_beautify) == Qtrue ) {
26
+ yajl_gen_config(yajl_gen, yajl_gen_beautify, 1);
27
+ }
28
+ if ( rb_hash_aref(yajl_gen_opts, sym_yajl_gen_validate_utf8) == Qtrue ) {
29
+ yajl_gen_config(yajl_gen, yajl_gen_validate_utf8, 1);
30
+ }
31
+
32
+ indent_string = rb_hash_aref(yajl_gen_opts, sym_yajl_gen_indent_string);
33
+ if (indent_string != Qnil) {
34
+ yajl_gen_config(yajl_gen, yajl_gen_indent_string, RSTRING_PTR(indent_string));
35
+ } else {
36
+ yajl_gen_config(yajl_gen, yajl_gen_indent_string, " ");
37
+ }
38
+
39
+ state = rb_hash_new();
40
+
41
+ rb_hash_aset(state, rb_str_new2("processing_key"), Qfalse);
42
+
43
+ rb_hash_aset(state, rb_str_new2("json_opts"), json_opts);
44
+
45
+ rb_yajl_gen = Data_Wrap_Struct(cYajl_Gen, NULL, NULL, yajl_gen);
46
+
47
+ rb_funcall(obj, sym_ffi_yajl, 2, rb_yajl_gen, state);
48
+
49
+ yajl_gen_get_buf(yajl_gen, &buf, &len);
50
+
51
+ ret = rb_str_new2((char *)buf);
52
+
53
+ yajl_gen_free(yajl_gen);
54
+
55
+ return ret;
56
+ }
57
+
58
+ int rb_cHash_ffi_yajl_callback(VALUE key, VALUE val, VALUE extra) {
59
+ ID sym_ffi_yajl = rb_intern("ffi_yajl");
60
+ VALUE state = rb_hash_aref(extra, rb_str_new2("state"));
61
+ VALUE rb_yajl_gen = rb_hash_aref(extra, rb_str_new2("yajl_gen"));
62
+
63
+ rb_hash_aset(state, rb_str_new2("processing_key"), Qtrue);
64
+ rb_funcall(key, sym_ffi_yajl, 2, rb_yajl_gen, state);
65
+ rb_hash_aset(state, rb_str_new2("processing_key"), Qfalse);
66
+
67
+ rb_funcall(val, sym_ffi_yajl, 2, rb_yajl_gen, state);
68
+
69
+ return 0;
70
+ }
71
+
72
+ #define RB_FUNC0(call) rb_funcall(self, rb_intern(call), 0)
73
+
74
+ /*
75
+ * wrappers around yajl_gen_* functions
76
+ */
77
+
78
+ /* encode a c-string as a yajl string */
79
+ VALUE gen_cstring(VALUE rb_yajl_gen, char *cptr, int len) {
80
+ yajl_gen_status status;
81
+ struct yajl_gen_t *yajl_gen;
82
+ Data_Get_Struct(rb_yajl_gen, struct yajl_gen_t, yajl_gen);
83
+
84
+ if ((status = yajl_gen_string(yajl_gen, (unsigned char *)cptr, len)) != yajl_gen_status_ok) {
85
+ rb_funcall(mEncoder2, rb_intern("raise_error_for_status"), 2, INT2FIX(status), rb_str_new(cptr, len));
86
+ }
87
+
88
+ return Qnil;
89
+ }
90
+
91
+ /* encode a ruby-sring as a yajl string */
92
+ VALUE gen_string(VALUE rb_yajl_gen, VALUE str) {
93
+ char *cptr = RSTRING_PTR(str);
94
+ int len = RSTRING_LEN(str);
95
+
96
+ return gen_cstring(rb_yajl_gen, cptr, len);
97
+ }
98
+
99
+ /* calls #to_s on an object to encode it as a yajl string */
100
+ static VALUE gen_string_to_s(VALUE rb_yajl_gen, VALUE self) {
101
+ return gen_string(rb_yajl_gen, RB_FUNC0("to_s"));
102
+ }
103
+
104
+ /* encode a ruby string as a yajl number (also used to embed already-rendered json from #to_json) */
105
+ VALUE gen_number(VALUE rb_yajl_gen, VALUE str) {
106
+ yajl_gen_status status;
107
+ struct yajl_gen_t *yajl_gen;
108
+ Data_Get_Struct(rb_yajl_gen, struct yajl_gen_t, yajl_gen);
109
+ char *cptr = RSTRING_PTR(str);
110
+ int len = RSTRING_LEN(str);
111
+
112
+ if ((status = yajl_gen_number(yajl_gen, cptr, len)) != yajl_gen_status_ok) {
113
+ rb_funcall(mEncoder2, rb_intern("raise_error_for_status"), 2, INT2FIX(status), str);
114
+ }
115
+
116
+ return Qnil;
117
+ }
118
+
119
+ /* encode hash open */
120
+ VALUE gen_map_open(VALUE rb_yajl_gen) {
121
+ yajl_gen_status status;
122
+ struct yajl_gen_t *yajl_gen;
123
+ Data_Get_Struct(rb_yajl_gen, struct yajl_gen_t, yajl_gen);
124
+
125
+ if ((status = yajl_gen_map_open(yajl_gen)) != yajl_gen_status_ok) {
126
+ rb_funcall(mEncoder2, rb_intern("raise_error_for_status"), 2, INT2FIX(status), rb_str_new2("{"));
127
+ }
128
+
129
+ return Qnil;
130
+ }
131
+
132
+ /* encode a hash close */
133
+ VALUE gen_map_close(VALUE rb_yajl_gen) {
134
+ yajl_gen_status status;
135
+ struct yajl_gen_t *yajl_gen;
136
+ Data_Get_Struct(rb_yajl_gen, struct yajl_gen_t, yajl_gen);
137
+
138
+ if ((status = yajl_gen_map_close(yajl_gen)) != yajl_gen_status_ok) {
139
+ rb_funcall(mEncoder2, rb_intern("raise_error_for_status"), 2, INT2FIX(status), rb_str_new2("}"));
140
+ }
141
+
142
+ return Qnil;
143
+ }
144
+
145
+ /* encode an array open */
146
+ VALUE gen_array_open(VALUE rb_yajl_gen) {
147
+ yajl_gen_status status;
148
+ struct yajl_gen_t *yajl_gen;
149
+ Data_Get_Struct(rb_yajl_gen, struct yajl_gen_t, yajl_gen);
150
+
151
+ if ((status = yajl_gen_array_open(yajl_gen)) != yajl_gen_status_ok) {
152
+ rb_funcall(mEncoder2, rb_intern("raise_error_for_status"), 2, INT2FIX(status), rb_str_new2("["));
153
+ }
154
+
155
+ return Qnil;
156
+ }
157
+
158
+ /* encode an array close */
159
+ VALUE gen_array_close(VALUE rb_yajl_gen) {
160
+ yajl_gen_status status;
161
+ struct yajl_gen_t *yajl_gen;
162
+ Data_Get_Struct(rb_yajl_gen, struct yajl_gen_t, yajl_gen);
163
+
164
+ if ((status = yajl_gen_array_close(yajl_gen)) != yajl_gen_status_ok) {
165
+ rb_funcall(mEncoder2, rb_intern("raise_error_for_status"), 2, INT2FIX(status), rb_str_new2("]"));
166
+ }
167
+
168
+ return Qnil;
169
+ }
170
+
171
+ /* encode a json null */
172
+ VALUE gen_null(VALUE rb_yajl_gen) {
173
+ yajl_gen_status status;
174
+ struct yajl_gen_t *yajl_gen;
175
+ Data_Get_Struct(rb_yajl_gen, struct yajl_gen_t, yajl_gen);
176
+
177
+ if ((status = yajl_gen_null(yajl_gen)) != yajl_gen_status_ok) {
178
+ rb_funcall(mEncoder2, rb_intern("raise_error_for_status"), 2, INT2FIX(status), rb_str_new2("null"));
179
+ }
180
+
181
+ return Qnil;
182
+ }
183
+
184
+ /* encode a true value */
185
+ VALUE gen_true(VALUE rb_yajl_gen) {
186
+ yajl_gen_status status;
187
+ struct yajl_gen_t *yajl_gen;
188
+ Data_Get_Struct(rb_yajl_gen, struct yajl_gen_t, yajl_gen);
189
+
190
+ if ((status = yajl_gen_bool(yajl_gen, 1)) != yajl_gen_status_ok) {
191
+ rb_funcall(mEncoder2, rb_intern("raise_error_for_status"), 2, INT2FIX(status), rb_str_new2("true"));
192
+ }
193
+
194
+ return Qnil;
195
+ }
196
+
197
+ /* encode a false value */
198
+ VALUE gen_false(VALUE rb_yajl_gen) {
199
+ yajl_gen_status status;
200
+ struct yajl_gen_t *yajl_gen;
201
+ Data_Get_Struct(rb_yajl_gen, struct yajl_gen_t, yajl_gen);
202
+
203
+ if ((status = yajl_gen_bool(yajl_gen, 0)) != yajl_gen_status_ok) {
204
+ rb_funcall(mEncoder2, rb_intern("raise_error_for_status"), 2, INT2FIX(status), rb_str_new2("false"));
205
+ }
206
+
207
+ return Qnil;
208
+ }
209
+
210
+ /*
211
+ * <Object>#to_ffi_yajl() method calls
212
+ */
213
+
214
+ static VALUE rb_cHash_ffi_yajl(VALUE self, VALUE rb_yajl_gen, VALUE state) {
215
+ if ( rb_hash_aref(state, rb_str_new2("processing_key")) == Qtrue ) {
216
+ gen_string(rb_yajl_gen, rb_funcall(self, rb_intern("to_s"), 0));
217
+ } else {
218
+
219
+ /* FIXME: i think this got refactored from something else and it is now pointless --
220
+ should just pass rb_yajl_gen directly instead of this 'extra' hash -- i don't
221
+ *think* this indirection is doing anything useful to mark memory against the GC */
222
+
223
+ VALUE extra = rb_hash_new();
224
+
225
+ rb_hash_aset(extra, rb_str_new2("yajl_gen"), rb_yajl_gen);
226
+
227
+ rb_hash_aset(extra, rb_str_new2("state"), state);
228
+
229
+ gen_map_open(rb_yajl_gen);
230
+
231
+ rb_hash_foreach(self, rb_cHash_ffi_yajl_callback, extra);
232
+
233
+ gen_map_close(rb_yajl_gen);
234
+ }
235
+
236
+ return Qnil;
237
+ }
238
+
239
+ static VALUE rb_cArray_ffi_yajl(VALUE self, VALUE rb_yajl_gen, VALUE state) {
240
+ if ( rb_hash_aref(state, rb_str_new2("processing_key")) == Qtrue ) {
241
+ gen_string(rb_yajl_gen, rb_funcall(self, rb_intern("to_s"), 0));
242
+ } else {
243
+ VALUE val;
244
+ long i;
245
+ ID sym_ffi_yajl = rb_intern("ffi_yajl");
246
+
247
+ gen_array_open(rb_yajl_gen);
248
+
249
+ for(i=0; i<RARRAY_LEN(self); i++) {
250
+ val = rb_ary_entry(self, i);
251
+ rb_funcall(val, sym_ffi_yajl, 2, rb_yajl_gen, state);
252
+ }
253
+
254
+ gen_array_close(rb_yajl_gen);
255
+ }
256
+
257
+ return Qnil;
258
+ }
259
+
260
+ static VALUE rb_cNilClass_ffi_yajl(VALUE self, VALUE rb_yajl_gen, VALUE state) {
261
+ if ( rb_hash_aref(state, rb_str_new2("processing_key")) == Qtrue ) {
262
+ gen_cstring(rb_yajl_gen, "", sizeof("")-1);
263
+ } else {
264
+ gen_null(rb_yajl_gen);
265
+ }
266
+
267
+ return Qnil;
268
+ }
269
+
270
+ static VALUE rb_cTrueClass_ffi_yajl(VALUE self, VALUE rb_yajl_gen, VALUE state) {
271
+ if ( rb_hash_aref(state, rb_str_new2("processing_key")) == Qtrue ) {
272
+ gen_cstring(rb_yajl_gen, "true", sizeof("true")-1);
273
+ } else {
274
+ gen_true(rb_yajl_gen);
275
+ }
276
+
277
+ return Qnil;
278
+ }
279
+
280
+ static VALUE rb_cFalseClass_ffi_yajl(VALUE self, VALUE rb_yajl_gen, VALUE state) {
281
+ if ( rb_hash_aref(state, rb_str_new2("processing_key")) == Qtrue ) {
282
+ gen_cstring(rb_yajl_gen, "false", sizeof("false")-1);
283
+ } else {
284
+ gen_false(rb_yajl_gen);
285
+ }
286
+
287
+ return Qnil;
288
+ }
289
+
290
+ static VALUE rb_cFixnum_ffi_yajl(VALUE self, VALUE rb_yajl_gen, VALUE state) {
291
+ VALUE str = rb_funcall(self, rb_intern("to_s"), 0);
292
+ char *cptr = RSTRING_PTR(str);
293
+
294
+ if (memcmp(cptr, "NaN", sizeof("NaN")) == 0 || memcmp(cptr, "Infinity", sizeof("Infinity")) == 0 || memcmp(cptr, "-Infinity", sizeof("-Infinity")) == 0) {
295
+ rb_raise(cEncodeError, "'%s' is an invalid number", cptr);
296
+ }
297
+
298
+ if ( rb_hash_aref(state, rb_str_new2("processing_key")) == Qtrue ) {
299
+ gen_string(rb_yajl_gen, str);
300
+ } else {
301
+ gen_number(rb_yajl_gen, str);
302
+ }
303
+
304
+ return Qnil;
305
+ }
306
+
307
+ static VALUE rb_cBignum_ffi_yajl(VALUE self, VALUE rb_yajl_gen, VALUE state) {
308
+ return rb_cFixnum_ffi_yajl(self, rb_yajl_gen, state);
309
+ }
310
+
311
+ static VALUE rb_cFloat_ffi_yajl(VALUE self, VALUE rb_yajl_gen, VALUE state) {
312
+ return rb_cFixnum_ffi_yajl(self, rb_yajl_gen, state);
313
+ }
314
+
315
+ static VALUE rb_cString_ffi_yajl(VALUE self, VALUE rb_yajl_gen, VALUE state) {
316
+ return gen_string(rb_yajl_gen, self);
317
+ }
318
+
319
+ static VALUE rb_cSymbol_ffi_yajl(VALUE self, VALUE rb_yajl_gen, VALUE state) {
320
+ return gen_string_to_s(rb_yajl_gen, self);
321
+ }
322
+
323
+ static VALUE rb_cDate_ffi_yajl(VALUE self, VALUE rb_yajl_gen, VALUE state) {
324
+ return gen_string_to_s(rb_yajl_gen, self);
325
+ }
326
+
327
+ static VALUE rb_cTime_ffi_yajl(VALUE self, VALUE rb_yajl_gen, VALUE state) {
328
+ ID sym_strftime = rb_intern("strftime");
329
+ VALUE str = rb_funcall(self, sym_strftime, 1, rb_str_new2("%Y-%m-%d %H:%M:%S %z"));
330
+
331
+ return gen_string(rb_yajl_gen, str);
332
+ }
333
+
334
+ static VALUE rb_cStringIO_ffi_yajl(VALUE self, VALUE rb_yajl_gen, VALUE state) {
335
+ return gen_string(rb_yajl_gen, RB_FUNC0("read"));
336
+ }
337
+
338
+ static VALUE rb_cDateTime_ffi_yajl(VALUE self, VALUE rb_yajl_gen, VALUE state) {
339
+ return gen_string_to_s(rb_yajl_gen, self);
340
+ }
341
+
342
+ static VALUE rb_cObject_ffi_yajl(VALUE self, VALUE rb_yajl_gen, VALUE state) {
343
+ ID sym_to_json = rb_intern("to_json");
344
+ if ( rb_hash_aref(state, rb_str_new2("processing_key")) != Qtrue && rb_respond_to(self, sym_to_json) ) {
345
+ VALUE json_opts = rb_hash_aref(state, rb_str_new2("json_opts"));
346
+ VALUE str = rb_funcall(self, sym_to_json, 1, json_opts);
347
+
348
+ gen_number(rb_yajl_gen, str);
349
+ } else {
350
+ gen_string_to_s(rb_yajl_gen, self);
351
+ }
352
+
353
+ return Qnil;
354
+ }
355
+
356
+ void Init_encoder() {
357
+ mFFI_Yajl = rb_define_module("FFI_Yajl");
358
+ mEncoder2 = rb_define_class_under(mFFI_Yajl, "Encoder", rb_cObject);
359
+ cEncodeError = rb_define_class_under(mFFI_Yajl, "EncodeError", rb_eStandardError);
360
+ mExt = rb_define_module_under(mFFI_Yajl, "Ext");
361
+ mEncoder = rb_define_module_under(mExt, "Encoder");
362
+ cYajl_Gen = rb_define_class_under(mEncoder, "YajlGen", rb_cObject);
363
+ rb_undef_alloc_func(cYajl_Gen);
364
+ rb_define_method(mEncoder, "do_yajl_encode", mEncoder_do_yajl_encode, 3);
365
+
366
+ /* use rb_const_get instead of rb_define_class so that we don't get superclass mismatches */
367
+ ID sym_Date = rb_intern("Date");
368
+ cDate = rb_const_get(rb_cObject, sym_Date);
369
+ ID sym_Time = rb_intern("Time");
370
+ cTime = rb_const_get(rb_cObject, sym_Time);
371
+ ID sym_DateTime = rb_intern("DateTime");
372
+ cDateTime = rb_const_get(rb_cObject, sym_DateTime);
373
+ ID sym_StringIO = rb_intern("StringIO");
374
+ cStringIO = rb_const_get(rb_cObject, sym_StringIO);
375
+
376
+ rb_define_method(rb_cHash, "ffi_yajl", rb_cHash_ffi_yajl, 2);
377
+ rb_define_method(rb_cArray, "ffi_yajl", rb_cArray_ffi_yajl, 2);
378
+ rb_define_method(rb_cNilClass, "ffi_yajl", rb_cNilClass_ffi_yajl, 2);
379
+ rb_define_method(rb_cTrueClass, "ffi_yajl", rb_cTrueClass_ffi_yajl, 2);
380
+ rb_define_method(rb_cFalseClass, "ffi_yajl", rb_cFalseClass_ffi_yajl, 2);
381
+ #ifdef rb_cFixnum /* ruby < 2.4 */
382
+ rb_define_method(rb_cFixnum, "ffi_yajl", rb_cFixnum_ffi_yajl, 2);
383
+ rb_define_method(rb_cBignum, "ffi_yajl", rb_cBignum_ffi_yajl, 2);
384
+ #else
385
+ rb_define_method(rb_cInteger, "ffi_yajl", rb_cFixnum_ffi_yajl, 2);
386
+ #endif
387
+ rb_define_method(rb_cFloat, "ffi_yajl", rb_cFloat_ffi_yajl, 2);
388
+ rb_define_method(rb_cString, "ffi_yajl", rb_cString_ffi_yajl, 2);
389
+ rb_define_method(rb_cSymbol, "ffi_yajl", rb_cSymbol_ffi_yajl, 2);
390
+ rb_define_method(cDate, "ffi_yajl", rb_cDate_ffi_yajl, 2);
391
+ rb_define_method(cTime, "ffi_yajl", rb_cTime_ffi_yajl, 2);
392
+ rb_define_method(cDateTime, "ffi_yajl", rb_cDateTime_ffi_yajl, 2);
393
+ rb_define_method(cStringIO, "ffi_yajl", rb_cStringIO_ffi_yajl, 2);
394
+ rb_define_method(rb_cObject, "ffi_yajl", rb_cObject_ffi_yajl, 2);
395
+ }