brianmario-yajl-ruby 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +55 -0
- data/Rakefile +15 -0
- data/VERSION.yml +4 -0
- data/ext/extconf.rb +6 -0
- data/ext/yajl.c +150 -0
- metadata +57 -0
data/README.rdoc
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
= YAJL C Bindings for Ruby (work in progress)
|
2
|
+
|
3
|
+
This gem (although not in gem form just yet) is a C binding to the excellent YAJL JSON parsing library.
|
4
|
+
|
5
|
+
You can read more info at the projects website http://lloydforge.org/projects/yajl or check out it's codes at http://github.com/lloyd/yajl.
|
6
|
+
|
7
|
+
== Example of use
|
8
|
+
|
9
|
+
File IO
|
10
|
+
|
11
|
+
json_contents = File.new('test.json', 'r')
|
12
|
+
hash = Yajl::Native.parse(json)
|
13
|
+
|
14
|
+
OR StringIO
|
15
|
+
|
16
|
+
json_contents = StringIO.new
|
17
|
+
hash = Yajl::Native.parse(json)
|
18
|
+
|
19
|
+
OR maybe STDIN
|
20
|
+
|
21
|
+
cat someJsonFile.json | ruby -ryajl -e "puts Yajl::Native.parse(STDIN).inspect"
|
22
|
+
|
23
|
+
There are a lot more possibilities, some of which I'm going to write other gems/plugins for.
|
24
|
+
|
25
|
+
Some ideas are parsing logs in JSON format, an ActiveSupport patch, Rack middleware, use with ohai, JSON API clients, etc...
|
26
|
+
|
27
|
+
== How to install
|
28
|
+
|
29
|
+
First, Yajl uses CMake to build itself (yes, the author realizes this isn't the norm for open source and is willing and ready to accept patches, fork away kids!) so you'll need to grab it first from http://www.cmake.org.
|
30
|
+
|
31
|
+
After you've got that, grab the latest version of Yajl itself from the Githubs at http://github.com/lloyd/yajl.
|
32
|
+
|
33
|
+
After you have that installed, you should be able to install it like any other gem hosted here like so:
|
34
|
+
|
35
|
+
(more instructions here: http://gems.github.com)
|
36
|
+
|
37
|
+
sudo gem install brianmario-yajl-ruby
|
38
|
+
|
39
|
+
== Benchmarks
|
40
|
+
|
41
|
+
I'll update this readme with some actual data soon.
|
42
|
+
|
43
|
+
After I finished implementation - this library performs close to the same as the current JSON.parse (C gem) does on small/medium files.
|
44
|
+
|
45
|
+
But on larger files, and higher amounts of iteration, this library was around 2x faster than JSON.parse.
|
46
|
+
|
47
|
+
The main benefit of this library is in it's memory usage.
|
48
|
+
|
49
|
+
Since it's able to parse the stream in chunks, it's memory requirements are very, very low.
|
50
|
+
|
51
|
+
Again, I'll post some actual data on this; but in my testing here's what my ruby executable was using to parse a 10MB JSON file 100 times:
|
52
|
+
|
53
|
+
JSON.parse ~60MB
|
54
|
+
|
55
|
+
Yajl::Native.parse ~30MB
|
data/Rakefile
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
begin
|
2
|
+
require 'jeweler'
|
3
|
+
Jeweler::Tasks.new do |gem|
|
4
|
+
gem.name = "yajl-ruby"
|
5
|
+
gem.summary = "Ruby C bindings to the excellent Yajl JSON stream-based parser library."
|
6
|
+
gem.email = "seniorlopez@gmail.com"
|
7
|
+
gem.homepage = "http://github.com/brianmario/yajl-ruby"
|
8
|
+
gem.description = "Ruby C bindings to the excellent Yajl JSON stream-based parser library."
|
9
|
+
gem.authors = ["Brian Lopez"]
|
10
|
+
gem.files.include %w(lib/jeweler/templates/.document lib/jeweler/templates/.gitignore)
|
11
|
+
# gem.rubyforge_project = "yajl-ruby"
|
12
|
+
end
|
13
|
+
rescue LoadError
|
14
|
+
puts "Jeweler, or one of its dependencies, is not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
|
15
|
+
end
|
data/VERSION.yml
ADDED
data/ext/extconf.rb
ADDED
data/ext/yajl.c
ADDED
@@ -0,0 +1,150 @@
|
|
1
|
+
#include <yajl/yajl_parse.h>
|
2
|
+
#include <yajl/yajl_gen.h>
|
3
|
+
#include <ruby.h>
|
4
|
+
|
5
|
+
void set_static_value(void * ctx, VALUE val) {
|
6
|
+
VALUE len = RARRAY((VALUE)ctx)->len;
|
7
|
+
|
8
|
+
if (len > 0) {
|
9
|
+
VALUE lastEntry = rb_ary_entry((VALUE)ctx, len-1);
|
10
|
+
VALUE hash;
|
11
|
+
switch (TYPE(lastEntry)) {
|
12
|
+
case T_ARRAY:
|
13
|
+
rb_ary_push(lastEntry, val);
|
14
|
+
if (TYPE(val) == T_HASH || TYPE(val) == T_ARRAY) {
|
15
|
+
rb_ary_push((VALUE)ctx, val);
|
16
|
+
}
|
17
|
+
break;
|
18
|
+
case T_HASH:
|
19
|
+
rb_hash_aset(lastEntry, val, Qnil);
|
20
|
+
rb_ary_push((VALUE)ctx, val);
|
21
|
+
break;
|
22
|
+
case T_STRING:
|
23
|
+
hash = rb_ary_entry((VALUE)ctx, len-2);
|
24
|
+
if (TYPE(hash) == T_HASH) {
|
25
|
+
rb_hash_aset(hash, lastEntry, val);
|
26
|
+
rb_ary_pop((VALUE)ctx);
|
27
|
+
if (TYPE(val) == T_HASH) {
|
28
|
+
rb_ary_push((VALUE)ctx, val);
|
29
|
+
}
|
30
|
+
}
|
31
|
+
break;
|
32
|
+
}
|
33
|
+
} else {
|
34
|
+
rb_ary_push((VALUE)ctx, val);
|
35
|
+
}
|
36
|
+
}
|
37
|
+
|
38
|
+
static int found_null(void * ctx) {
|
39
|
+
set_static_value(ctx, Qnil);
|
40
|
+
return 1;
|
41
|
+
}
|
42
|
+
|
43
|
+
static int found_boolean(void * ctx, int boolean) {
|
44
|
+
set_static_value(ctx, boolean ? Qtrue : Qfalse);
|
45
|
+
return 1;
|
46
|
+
}
|
47
|
+
|
48
|
+
static int found_integer(void * ctx, long integerVal) {
|
49
|
+
set_static_value(ctx, LONG2FIX(integerVal));
|
50
|
+
return 1;
|
51
|
+
}
|
52
|
+
|
53
|
+
static int found_double(void * ctx, double doubleVal) {
|
54
|
+
set_static_value(ctx, rb_float_new(doubleVal));
|
55
|
+
return 1;
|
56
|
+
}
|
57
|
+
|
58
|
+
static int found_string(void * ctx, const unsigned char * stringVal, unsigned int stringLen) {
|
59
|
+
set_static_value(ctx, rb_str_new((char *)stringVal, stringLen));
|
60
|
+
return 1;
|
61
|
+
}
|
62
|
+
|
63
|
+
static int found_hash_key(void * ctx, const unsigned char * stringVal, unsigned int stringLen) {
|
64
|
+
set_static_value(ctx, rb_str_new((char *)stringVal, stringLen));
|
65
|
+
return 1;
|
66
|
+
}
|
67
|
+
|
68
|
+
static int found_start_hash(void * ctx) {
|
69
|
+
set_static_value(ctx, rb_hash_new());
|
70
|
+
return 1;
|
71
|
+
}
|
72
|
+
|
73
|
+
static int found_end_hash(void * ctx) {
|
74
|
+
if (RARRAY((VALUE)ctx)->len > 1) {
|
75
|
+
rb_ary_pop((VALUE)ctx);
|
76
|
+
}
|
77
|
+
return 1;
|
78
|
+
}
|
79
|
+
|
80
|
+
static int found_start_array(void * ctx) {
|
81
|
+
set_static_value(ctx, rb_ary_new());
|
82
|
+
return 1;
|
83
|
+
}
|
84
|
+
|
85
|
+
static int found_end_array(void * ctx) {
|
86
|
+
if (RARRAY((VALUE)ctx)->len > 1) {
|
87
|
+
rb_ary_pop((VALUE)ctx);
|
88
|
+
}
|
89
|
+
return 1;
|
90
|
+
}
|
91
|
+
|
92
|
+
static yajl_callbacks callbacks = {
|
93
|
+
found_null,
|
94
|
+
found_boolean,
|
95
|
+
found_integer,
|
96
|
+
found_double,
|
97
|
+
NULL,
|
98
|
+
found_string,
|
99
|
+
found_start_hash,
|
100
|
+
found_hash_key,
|
101
|
+
found_end_hash,
|
102
|
+
found_start_array,
|
103
|
+
found_end_array
|
104
|
+
};
|
105
|
+
|
106
|
+
ID intern_io_read, intern_eof;
|
107
|
+
yajl_parser_config cfg = {1, 1};
|
108
|
+
|
109
|
+
static VALUE t_parse(VALUE self, VALUE io) {
|
110
|
+
yajl_handle hand;
|
111
|
+
yajl_status stat;
|
112
|
+
int bufferSize = 8192;
|
113
|
+
intern_io_read = rb_intern("read");
|
114
|
+
intern_eof = rb_intern("eof?");
|
115
|
+
VALUE ctx = rb_ary_new();
|
116
|
+
|
117
|
+
// allocate our parser
|
118
|
+
hand = yajl_alloc(&callbacks, &cfg, NULL, (void *)ctx);
|
119
|
+
VALUE parsed = rb_str_new("", 0);
|
120
|
+
VALUE rbufsize = INT2FIX(bufferSize);
|
121
|
+
|
122
|
+
// now parse from the IO
|
123
|
+
while (rb_funcall(io, intern_eof, 0) == Qfalse) {
|
124
|
+
rb_funcall(io, intern_io_read, 2, rbufsize, parsed);
|
125
|
+
|
126
|
+
stat = yajl_parse(hand, (const unsigned char *)RSTRING(parsed)->ptr, RSTRING(parsed)->len);
|
127
|
+
|
128
|
+
if (stat != yajl_status_ok && stat != yajl_status_insufficient_data) {
|
129
|
+
unsigned char * str = yajl_get_error(hand, 1, (const unsigned char *)RSTRING_PTR(parsed), RSTRING_LEN(parsed));
|
130
|
+
fprintf(stderr, (const char *) str);
|
131
|
+
yajl_free_error(hand, str);
|
132
|
+
break;
|
133
|
+
}
|
134
|
+
}
|
135
|
+
|
136
|
+
// parse any remaining buffered data
|
137
|
+
stat = yajl_parse_complete(hand);
|
138
|
+
yajl_free(hand);
|
139
|
+
|
140
|
+
return rb_ary_pop(ctx);
|
141
|
+
}
|
142
|
+
|
143
|
+
VALUE mYajl;
|
144
|
+
VALUE mNative;
|
145
|
+
|
146
|
+
void Init_yajl() {
|
147
|
+
mYajl = rb_define_module("Yajl");
|
148
|
+
mNative = rb_define_module_under(mYajl, "Native");
|
149
|
+
rb_define_module_function(mNative, "parse", t_parse, 1);
|
150
|
+
}
|
metadata
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: brianmario-yajl-ruby
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Brian Lopez
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-04-22 00:00:00 -07:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description: ""
|
17
|
+
email: seniorlopez@gmail.com
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions:
|
21
|
+
- ext/extconf.rb
|
22
|
+
extra_rdoc_files: []
|
23
|
+
|
24
|
+
files:
|
25
|
+
- README.rdoc
|
26
|
+
- Rakefile
|
27
|
+
- VERSION.yml
|
28
|
+
- ext/extconf.rb
|
29
|
+
- ext/yajl.c
|
30
|
+
has_rdoc: true
|
31
|
+
homepage: http://github.com/brianmario/yajl-ruby
|
32
|
+
post_install_message:
|
33
|
+
rdoc_options:
|
34
|
+
- --charset=UTF-8
|
35
|
+
require_paths:
|
36
|
+
- ext
|
37
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - ">="
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: "0"
|
42
|
+
version:
|
43
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: "0"
|
48
|
+
version:
|
49
|
+
requirements: []
|
50
|
+
|
51
|
+
rubyforge_project:
|
52
|
+
rubygems_version: 1.2.0
|
53
|
+
signing_key:
|
54
|
+
specification_version: 3
|
55
|
+
summary: Ruby C bindings to the excellent Yajl JSON stream-based parser library.
|
56
|
+
test_files: []
|
57
|
+
|