brianmario-yajl-ruby 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (6) hide show
  1. data/README.rdoc +55 -0
  2. data/Rakefile +15 -0
  3. data/VERSION.yml +4 -0
  4. data/ext/extconf.rb +6 -0
  5. data/ext/yajl.c +150 -0
  6. 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
@@ -0,0 +1,4 @@
1
+ ---
2
+ :patch: 0
3
+ :major: 0
4
+ :minor: 2
data/ext/extconf.rb ADDED
@@ -0,0 +1,6 @@
1
+ require 'mkmf'
2
+ dir_config('yajl')
3
+ have_header('yajl/yajl_parse.h')
4
+ have_header('yajl/yajl_gen.h')
5
+ have_library('yajl')
6
+ create_makefile("yajl")
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
+