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.
- checksums.yaml +7 -0
- data/LICENSE +22 -0
- data/README.md +118 -0
- data/bin/ffi-yajl-bench +36 -0
- data/ext/ffi_yajl/ext/dlopen/dlopen.c +40 -0
- data/ext/ffi_yajl/ext/dlopen/extconf.rb +16 -0
- data/ext/ffi_yajl/ext/dlopen/yajl.sym +18 -0
- data/ext/ffi_yajl/ext/encoder/encoder.c +395 -0
- data/ext/ffi_yajl/ext/encoder/extconf.rb +86 -0
- data/ext/ffi_yajl/ext/parser/extconf.rb +86 -0
- data/ext/ffi_yajl/ext/parser/parser.c +240 -0
- data/lib/ffi_yajl/benchmark/MIT-LICENSE +20 -0
- data/lib/ffi_yajl/benchmark/encode.rb +82 -0
- data/lib/ffi_yajl/benchmark/encode_json_and_marshal.rb +42 -0
- data/lib/ffi_yajl/benchmark/encode_json_and_yaml.rb +47 -0
- data/lib/ffi_yajl/benchmark/encode_profile.rb +34 -0
- data/lib/ffi_yajl/benchmark/http.rb +28 -0
- data/lib/ffi_yajl/benchmark/parse.rb +93 -0
- data/lib/ffi_yajl/benchmark/parse_json_and_marshal.rb +50 -0
- data/lib/ffi_yajl/benchmark/parse_json_and_yaml.rb +55 -0
- data/lib/ffi_yajl/benchmark/parse_profile.rb +33 -0
- data/lib/ffi_yajl/benchmark/parse_profile_ruby_prof.rb +34 -0
- data/lib/ffi_yajl/benchmark/parse_stream.rb +54 -0
- data/lib/ffi_yajl/benchmark/subjects/item.json +1 -0
- data/lib/ffi_yajl/benchmark/subjects/ohai.json +1216 -0
- data/lib/ffi_yajl/benchmark/subjects/ohai.marshal_dump +0 -0
- data/lib/ffi_yajl/benchmark/subjects/ohai.yml +975 -0
- data/lib/ffi_yajl/benchmark/subjects/twitter_search.json +1 -0
- data/lib/ffi_yajl/benchmark/subjects/twitter_stream.json +430 -0
- data/lib/ffi_yajl/benchmark/subjects/unicode.json +1 -0
- data/lib/ffi_yajl/benchmark.rb +27 -0
- data/lib/ffi_yajl/encoder.rb +94 -0
- data/lib/ffi_yajl/ext/.keep +0 -0
- data/lib/ffi_yajl/ext.rb +49 -0
- data/lib/ffi_yajl/ffi/encoder.rb +262 -0
- data/lib/ffi_yajl/ffi/parser.rb +163 -0
- data/lib/ffi_yajl/ffi.rb +153 -0
- data/lib/ffi_yajl/map_library_name.rb +109 -0
- data/lib/ffi_yajl/parser.rb +91 -0
- data/lib/ffi_yajl/platform.rb +29 -0
- data/lib/ffi_yajl/version.rb +25 -0
- data/lib/ffi_yajl.rb +50 -0
- 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
|
+
[](https://buildkite.com/chef-oss/chef-ffi-yajl-master-verify) [](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.
|
data/bin/ffi-yajl-bench
ADDED
|
@@ -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
|
+
}
|