yaji 0.2.3-x86-mingw32
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +7 -0
- data/.travis.yml +4 -0
- data/Gemfile +4 -0
- data/HISTORY.markdown +67 -0
- data/LICENSE +201 -0
- data/README.markdown +148 -0
- data/Rakefile +4 -0
- data/ext/yaji/api/yajl_common.h +89 -0
- data/ext/yaji/api/yajl_gen.h +159 -0
- data/ext/yaji/api/yajl_parse.h +193 -0
- data/ext/yaji/api/yajl_version.h +23 -0
- data/ext/yaji/extconf.rb +23 -0
- data/ext/yaji/parser_ext.c +442 -0
- data/ext/yaji/parser_ext.h +94 -0
- data/ext/yaji/yajl.c +159 -0
- data/ext/yaji/yajl_alloc.c +65 -0
- data/ext/yaji/yajl_alloc.h +51 -0
- data/ext/yaji/yajl_buf.c +119 -0
- data/ext/yaji/yajl_buf.h +80 -0
- data/ext/yaji/yajl_bytestack.h +85 -0
- data/ext/yaji/yajl_encode.c +195 -0
- data/ext/yaji/yajl_encode.h +53 -0
- data/ext/yaji/yajl_gen.c +347 -0
- data/ext/yaji/yajl_lex.c +737 -0
- data/ext/yaji/yajl_lex.h +142 -0
- data/ext/yaji/yajl_parser.c +448 -0
- data/ext/yaji/yajl_parser.h +84 -0
- data/ext/yaji/yajl_version.c +7 -0
- data/lib/yaji.rb +24 -0
- data/lib/yaji/version.rb +22 -0
- data/tasks/compile.rake +45 -0
- data/tasks/test.rake +7 -0
- data/tasks/util.rake +4 -0
- data/test/fixture.json +47 -0
- data/test/test_parser.rb +275 -0
- data/yaji.gemspec +28 -0
- metadata +171 -0
@@ -0,0 +1,159 @@
|
|
1
|
+
/*
|
2
|
+
* Copyright 2010, Lloyd Hilaiel.
|
3
|
+
*
|
4
|
+
* Redistribution and use in source and binary forms, with or without
|
5
|
+
* modification, are permitted provided that the following conditions are
|
6
|
+
* met:
|
7
|
+
*
|
8
|
+
* 1. Redistributions of source code must retain the above copyright
|
9
|
+
* notice, this list of conditions and the following disclaimer.
|
10
|
+
*
|
11
|
+
* 2. Redistributions in binary form must reproduce the above copyright
|
12
|
+
* notice, this list of conditions and the following disclaimer in
|
13
|
+
* the documentation and/or other materials provided with the
|
14
|
+
* distribution.
|
15
|
+
*
|
16
|
+
* 3. Neither the name of Lloyd Hilaiel nor the names of its
|
17
|
+
* contributors may be used to endorse or promote products derived
|
18
|
+
* from this software without specific prior written permission.
|
19
|
+
*
|
20
|
+
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
21
|
+
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
22
|
+
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
23
|
+
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
|
24
|
+
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
25
|
+
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
26
|
+
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
27
|
+
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
28
|
+
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
29
|
+
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
30
|
+
* POSSIBILITY OF SUCH DAMAGE.
|
31
|
+
*/
|
32
|
+
|
33
|
+
/**
|
34
|
+
* \file yajl_gen.h
|
35
|
+
* Interface to YAJL's JSON generation facilities.
|
36
|
+
*/
|
37
|
+
|
38
|
+
#include "api/yajl_common.h"
|
39
|
+
|
40
|
+
#ifndef __YAJL_GEN_H__
|
41
|
+
#define __YAJL_GEN_H__
|
42
|
+
|
43
|
+
#ifdef __cplusplus
|
44
|
+
extern "C" {
|
45
|
+
#endif
|
46
|
+
/** generator status codes */
|
47
|
+
typedef enum {
|
48
|
+
/** no error */
|
49
|
+
yajl_gen_status_ok = 0,
|
50
|
+
/** at a point where a map key is generated, a function other than
|
51
|
+
* yajl_gen_string was called */
|
52
|
+
yajl_gen_keys_must_be_strings,
|
53
|
+
/** YAJL's maximum generation depth was exceeded. see
|
54
|
+
* YAJL_MAX_DEPTH */
|
55
|
+
yajl_max_depth_exceeded,
|
56
|
+
/** A generator function (yajl_gen_XXX) was called while in an error
|
57
|
+
* state */
|
58
|
+
yajl_gen_in_error_state,
|
59
|
+
/** A complete JSON document has been generated */
|
60
|
+
yajl_gen_generation_complete,
|
61
|
+
/** yajl_gen_double was passed an invalid floating point value
|
62
|
+
* (infinity or NaN). */
|
63
|
+
yajl_gen_invalid_number,
|
64
|
+
/** A print callback was passed in, so there is no internal
|
65
|
+
* buffer to get from */
|
66
|
+
yajl_gen_no_buf
|
67
|
+
} yajl_gen_status;
|
68
|
+
|
69
|
+
/** an opaque handle to a generator */
|
70
|
+
typedef struct yajl_gen_t * yajl_gen;
|
71
|
+
|
72
|
+
/** a callback used for "printing" the results. */
|
73
|
+
typedef void (*yajl_print_t)(void * ctx,
|
74
|
+
const char * str,
|
75
|
+
unsigned int len);
|
76
|
+
|
77
|
+
/** configuration structure for the generator */
|
78
|
+
typedef struct {
|
79
|
+
/** generate indented (beautiful) output */
|
80
|
+
unsigned int beautify;
|
81
|
+
/** an opportunity to define an indent string. such as \\t or
|
82
|
+
* some number of spaces. default is four spaces ' '. This
|
83
|
+
* member is only relevant when beautify is true */
|
84
|
+
const char * indentString;
|
85
|
+
} yajl_gen_config;
|
86
|
+
|
87
|
+
/** allocate a generator handle
|
88
|
+
* \param config a pointer to a structure containing parameters which
|
89
|
+
* configure the behavior of the json generator
|
90
|
+
* \param allocFuncs an optional pointer to a structure which allows
|
91
|
+
* the client to overide the memory allocation
|
92
|
+
* used by yajl. May be NULL, in which case
|
93
|
+
* malloc/free/realloc will be used.
|
94
|
+
*
|
95
|
+
* \returns an allocated handle on success, NULL on failure (bad params)
|
96
|
+
*/
|
97
|
+
YAJL_API yajl_gen yajl_gen_alloc(const yajl_gen_config * config,
|
98
|
+
const yajl_alloc_funcs * allocFuncs);
|
99
|
+
|
100
|
+
/** allocate a generator handle that will print to the specified
|
101
|
+
* callback rather than storing the results in an internal buffer.
|
102
|
+
* \param callback a pointer to a printer function. May be NULL
|
103
|
+
* in which case, the results will be store in an
|
104
|
+
* internal buffer.
|
105
|
+
* \param config a pointer to a structure containing parameters
|
106
|
+
* which configure the behavior of the json
|
107
|
+
* generator.
|
108
|
+
* \param allocFuncs an optional pointer to a structure which allows
|
109
|
+
* the client to overide the memory allocation
|
110
|
+
* used by yajl. May be NULL, in which case
|
111
|
+
* malloc/free/realloc will be used.
|
112
|
+
* \param ctx a context pointer that will be passed to the
|
113
|
+
* printer callback.
|
114
|
+
*
|
115
|
+
* \returns an allocated handle on success, NULL on failure (bad params)
|
116
|
+
*/
|
117
|
+
YAJL_API yajl_gen yajl_gen_alloc2(const yajl_print_t callback,
|
118
|
+
const yajl_gen_config * config,
|
119
|
+
const yajl_alloc_funcs * allocFuncs,
|
120
|
+
void * ctx);
|
121
|
+
|
122
|
+
/** free a generator handle */
|
123
|
+
YAJL_API void yajl_gen_free(yajl_gen handle);
|
124
|
+
|
125
|
+
YAJL_API yajl_gen_status yajl_gen_integer(yajl_gen hand, long int number);
|
126
|
+
/** generate a floating point number. number may not be infinity or
|
127
|
+
* NaN, as these have no representation in JSON. In these cases the
|
128
|
+
* generator will return 'yajl_gen_invalid_number' */
|
129
|
+
YAJL_API yajl_gen_status yajl_gen_double(yajl_gen hand, double number);
|
130
|
+
YAJL_API yajl_gen_status yajl_gen_number(yajl_gen hand,
|
131
|
+
const char * num,
|
132
|
+
unsigned int len);
|
133
|
+
YAJL_API yajl_gen_status yajl_gen_string(yajl_gen hand,
|
134
|
+
const unsigned char * str,
|
135
|
+
unsigned int len);
|
136
|
+
YAJL_API yajl_gen_status yajl_gen_null(yajl_gen hand);
|
137
|
+
YAJL_API yajl_gen_status yajl_gen_bool(yajl_gen hand, int boolean);
|
138
|
+
YAJL_API yajl_gen_status yajl_gen_map_open(yajl_gen hand);
|
139
|
+
YAJL_API yajl_gen_status yajl_gen_map_close(yajl_gen hand);
|
140
|
+
YAJL_API yajl_gen_status yajl_gen_array_open(yajl_gen hand);
|
141
|
+
YAJL_API yajl_gen_status yajl_gen_array_close(yajl_gen hand);
|
142
|
+
|
143
|
+
/** access the null terminated generator buffer. If incrementally
|
144
|
+
* outputing JSON, one should call yajl_gen_clear to clear the
|
145
|
+
* buffer. This allows stream generation. */
|
146
|
+
YAJL_API yajl_gen_status yajl_gen_get_buf(yajl_gen hand,
|
147
|
+
const unsigned char ** buf,
|
148
|
+
unsigned int * len);
|
149
|
+
|
150
|
+
/** clear yajl's output buffer, but maintain all internal generation
|
151
|
+
* state. This function will not "reset" the generator state, and is
|
152
|
+
* intended to enable incremental JSON outputing. */
|
153
|
+
YAJL_API void yajl_gen_clear(yajl_gen hand);
|
154
|
+
|
155
|
+
#ifdef __cplusplus
|
156
|
+
}
|
157
|
+
#endif
|
158
|
+
|
159
|
+
#endif
|
@@ -0,0 +1,193 @@
|
|
1
|
+
/*
|
2
|
+
* Copyright 2010, Lloyd Hilaiel.
|
3
|
+
*
|
4
|
+
* Redistribution and use in source and binary forms, with or without
|
5
|
+
* modification, are permitted provided that the following conditions are
|
6
|
+
* met:
|
7
|
+
*
|
8
|
+
* 1. Redistributions of source code must retain the above copyright
|
9
|
+
* notice, this list of conditions and the following disclaimer.
|
10
|
+
*
|
11
|
+
* 2. Redistributions in binary form must reproduce the above copyright
|
12
|
+
* notice, this list of conditions and the following disclaimer in
|
13
|
+
* the documentation and/or other materials provided with the
|
14
|
+
* distribution.
|
15
|
+
*
|
16
|
+
* 3. Neither the name of Lloyd Hilaiel nor the names of its
|
17
|
+
* contributors may be used to endorse or promote products derived
|
18
|
+
* from this software without specific prior written permission.
|
19
|
+
*
|
20
|
+
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
21
|
+
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
22
|
+
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
23
|
+
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
|
24
|
+
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
25
|
+
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
26
|
+
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
27
|
+
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
28
|
+
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
29
|
+
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
30
|
+
* POSSIBILITY OF SUCH DAMAGE.
|
31
|
+
*/
|
32
|
+
|
33
|
+
/**
|
34
|
+
* \file yajl_parse.h
|
35
|
+
* Interface to YAJL's JSON parsing facilities.
|
36
|
+
*/
|
37
|
+
|
38
|
+
#include "api/yajl_common.h"
|
39
|
+
|
40
|
+
#ifndef __YAJL_PARSE_H__
|
41
|
+
#define __YAJL_PARSE_H__
|
42
|
+
|
43
|
+
#ifdef __cplusplus
|
44
|
+
extern "C" {
|
45
|
+
#endif
|
46
|
+
/** error codes returned from this interface */
|
47
|
+
typedef enum {
|
48
|
+
/** no error was encountered */
|
49
|
+
yajl_status_ok,
|
50
|
+
/** a client callback returned zero, stopping the parse */
|
51
|
+
yajl_status_client_canceled,
|
52
|
+
/** The parse cannot yet complete because more json input text
|
53
|
+
* is required, call yajl_parse with the next buffer of input text.
|
54
|
+
* (pertinent only when stream parsing) */
|
55
|
+
yajl_status_insufficient_data,
|
56
|
+
/** An error occured during the parse. Call yajl_get_error for
|
57
|
+
* more information about the encountered error */
|
58
|
+
yajl_status_error
|
59
|
+
} yajl_status;
|
60
|
+
|
61
|
+
/** attain a human readable, english, string for an error */
|
62
|
+
YAJL_API const char * yajl_status_to_string(yajl_status code);
|
63
|
+
|
64
|
+
/** an opaque handle to a parser */
|
65
|
+
typedef struct yajl_handle_t * yajl_handle;
|
66
|
+
|
67
|
+
/** yajl is an event driven parser. this means as json elements are
|
68
|
+
* parsed, you are called back to do something with the data. The
|
69
|
+
* functions in this table indicate the various events for which
|
70
|
+
* you will be called back. Each callback accepts a "context"
|
71
|
+
* pointer, this is a void * that is passed into the yajl_parse
|
72
|
+
* function which the client code may use to pass around context.
|
73
|
+
*
|
74
|
+
* All callbacks return an integer. If non-zero, the parse will
|
75
|
+
* continue. If zero, the parse will be canceled and
|
76
|
+
* yajl_status_client_canceled will be returned from the parse.
|
77
|
+
*
|
78
|
+
* Note about handling of numbers:
|
79
|
+
* yajl will only convert numbers that can be represented in a double
|
80
|
+
* or a long int. All other numbers will be passed to the client
|
81
|
+
* in string form using the yajl_number callback. Furthermore, if
|
82
|
+
* yajl_number is not NULL, it will always be used to return numbers,
|
83
|
+
* that is yajl_integer and yajl_double will be ignored. If
|
84
|
+
* yajl_number is NULL but one of yajl_integer or yajl_double are
|
85
|
+
* defined, parsing of a number larger than is representable
|
86
|
+
* in a double or long int will result in a parse error.
|
87
|
+
*/
|
88
|
+
typedef struct {
|
89
|
+
int (* yajl_null)(void * ctx);
|
90
|
+
int (* yajl_boolean)(void * ctx, int boolVal);
|
91
|
+
int (* yajl_integer)(void * ctx, long integerVal);
|
92
|
+
int (* yajl_double)(void * ctx, double doubleVal);
|
93
|
+
/** A callback which passes the string representation of the number
|
94
|
+
* back to the client. Will be used for all numbers when present */
|
95
|
+
int (* yajl_number)(void * ctx, const char * numberVal,
|
96
|
+
unsigned int numberLen);
|
97
|
+
|
98
|
+
/** strings are returned as pointers into the JSON text when,
|
99
|
+
* possible, as a result, they are _not_ null padded */
|
100
|
+
int (* yajl_string)(void * ctx, const unsigned char * stringVal,
|
101
|
+
unsigned int stringLen);
|
102
|
+
|
103
|
+
int (* yajl_start_map)(void * ctx);
|
104
|
+
int (* yajl_map_key)(void * ctx, const unsigned char * key,
|
105
|
+
unsigned int stringLen);
|
106
|
+
int (* yajl_end_map)(void * ctx);
|
107
|
+
|
108
|
+
int (* yajl_start_array)(void * ctx);
|
109
|
+
int (* yajl_end_array)(void * ctx);
|
110
|
+
} yajl_callbacks;
|
111
|
+
|
112
|
+
/** configuration structure for the generator */
|
113
|
+
typedef struct {
|
114
|
+
/** if nonzero, javascript style comments will be allowed in
|
115
|
+
* the json input, both slash star and slash slash */
|
116
|
+
unsigned int allowComments;
|
117
|
+
/** if nonzero, invalid UTF8 strings will cause a parse
|
118
|
+
* error */
|
119
|
+
unsigned int checkUTF8;
|
120
|
+
} yajl_parser_config;
|
121
|
+
|
122
|
+
/** allocate a parser handle
|
123
|
+
* \param callbacks a yajl callbacks structure specifying the
|
124
|
+
* functions to call when different JSON entities
|
125
|
+
* are encountered in the input text. May be NULL,
|
126
|
+
* which is only useful for validation.
|
127
|
+
* \param config configuration parameters for the parse.
|
128
|
+
* \param ctx a context pointer that will be passed to callbacks.
|
129
|
+
*/
|
130
|
+
YAJL_API yajl_handle yajl_alloc(const yajl_callbacks * callbacks,
|
131
|
+
const yajl_parser_config * config,
|
132
|
+
const yajl_alloc_funcs * allocFuncs,
|
133
|
+
void * ctx);
|
134
|
+
|
135
|
+
/** free a parser handle */
|
136
|
+
YAJL_API void yajl_free(yajl_handle handle);
|
137
|
+
|
138
|
+
/** Parse some json!
|
139
|
+
* \param hand - a handle to the json parser allocated with yajl_alloc
|
140
|
+
* \param jsonText - a pointer to the UTF8 json text to be parsed
|
141
|
+
* \param jsonTextLength - the length, in bytes, of input text
|
142
|
+
*/
|
143
|
+
YAJL_API yajl_status yajl_parse(yajl_handle hand,
|
144
|
+
const unsigned char * jsonText,
|
145
|
+
unsigned int jsonTextLength);
|
146
|
+
|
147
|
+
/** Parse any remaining buffered json.
|
148
|
+
* Since yajl is a stream-based parser, without an explicit end of
|
149
|
+
* input, yajl sometimes can't decide if content at the end of the
|
150
|
+
* stream is valid or not. For example, if "1" has been fed in,
|
151
|
+
* yajl can't know whether another digit is next or some character
|
152
|
+
* that would terminate the integer token.
|
153
|
+
*
|
154
|
+
* \param hand - a handle to the json parser allocated with yajl_alloc
|
155
|
+
*/
|
156
|
+
YAJL_API yajl_status yajl_parse_complete(yajl_handle hand);
|
157
|
+
|
158
|
+
/** get an error string describing the state of the
|
159
|
+
* parse.
|
160
|
+
*
|
161
|
+
* If verbose is non-zero, the message will include the JSON
|
162
|
+
* text where the error occured, along with an arrow pointing to
|
163
|
+
* the specific char.
|
164
|
+
*
|
165
|
+
* \returns A dynamically allocated string will be returned which should
|
166
|
+
* be freed with yajl_free_error
|
167
|
+
*/
|
168
|
+
YAJL_API unsigned char * yajl_get_error(yajl_handle hand, int verbose,
|
169
|
+
const unsigned char * jsonText,
|
170
|
+
unsigned int jsonTextLength);
|
171
|
+
|
172
|
+
/**
|
173
|
+
* get the amount of data consumed from the last chunk passed to YAJL.
|
174
|
+
*
|
175
|
+
* In the case of a successful parse this can help you understand if
|
176
|
+
* the entire buffer was consumed (which will allow you to handle
|
177
|
+
* "junk at end of input".
|
178
|
+
*
|
179
|
+
* In the event an error is encountered during parsing, this function
|
180
|
+
* affords the client a way to get the offset into the most recent
|
181
|
+
* chunk where the error occured. 0 will be returned if no error
|
182
|
+
* was encountered.
|
183
|
+
*/
|
184
|
+
YAJL_API unsigned int yajl_get_bytes_consumed(yajl_handle hand);
|
185
|
+
|
186
|
+
/** free an error returned from yajl_get_error */
|
187
|
+
YAJL_API void yajl_free_error(yajl_handle hand, unsigned char * str);
|
188
|
+
|
189
|
+
#ifdef __cplusplus
|
190
|
+
}
|
191
|
+
#endif
|
192
|
+
|
193
|
+
#endif
|
@@ -0,0 +1,23 @@
|
|
1
|
+
#ifndef YAJL_VERSION_H_
|
2
|
+
#define YAJL_VERSION_H_
|
3
|
+
|
4
|
+
#include "api/yajl_common.h"
|
5
|
+
|
6
|
+
#define YAJL_MAJOR 1
|
7
|
+
#define YAJL_MINOR 0
|
8
|
+
#define YAJL_MICRO 12
|
9
|
+
|
10
|
+
#define YAJL_VERSION ((YAJL_MAJOR * 10000) + (YAJL_MINOR * 100) + YAJL_MICRO)
|
11
|
+
|
12
|
+
#ifdef __cplusplus
|
13
|
+
extern "C" {
|
14
|
+
#endif
|
15
|
+
|
16
|
+
extern int YAJL_API yajl_version(void);
|
17
|
+
|
18
|
+
#ifdef __cplusplus
|
19
|
+
}
|
20
|
+
#endif
|
21
|
+
|
22
|
+
#endif /* YAJL_VERSION_H_ */
|
23
|
+
|
data/ext/yaji/extconf.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
ENV['RC_ARCHS'] = '' if RUBY_PLATFORM =~ /darwin/
|
3
|
+
|
4
|
+
require 'mkmf'
|
5
|
+
require 'rbconfig'
|
6
|
+
|
7
|
+
def define(macro, value = nil)
|
8
|
+
$defs.push("-D #{[macro.upcase, value].compact.join('=')}")
|
9
|
+
end
|
10
|
+
|
11
|
+
$CFLAGS << " #{ENV["CFLAGS"]}"
|
12
|
+
$LDFLAGS << " #{ENV["LDFLAGS"]}"
|
13
|
+
$LIBS << " #{ENV["LIBS"]}"
|
14
|
+
|
15
|
+
$CFLAGS << ' -std=c99 -Wall -funroll-loops -Wextra '
|
16
|
+
$CFLAGS << ' -O0 -ggdb3 -pedantic ' if ENV['DEBUG']
|
17
|
+
|
18
|
+
# have_library('yajl', 'yajl_parse', 'yajl/yajl_parse.h')
|
19
|
+
|
20
|
+
define("READ_BUFSIZE", "8192")
|
21
|
+
|
22
|
+
create_header("yaji_config.h")
|
23
|
+
create_makefile("parser_ext")
|
@@ -0,0 +1,442 @@
|
|
1
|
+
/*
|
2
|
+
* Author:: Couchbase <info@couchbase.com>
|
3
|
+
* Copyright:: 2011 Couchbase, Inc.
|
4
|
+
* License:: Apache License, Version 2.0
|
5
|
+
*
|
6
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
* you may not use this file except in compliance with the License.
|
8
|
+
* You may obtain a copy of the License at
|
9
|
+
*
|
10
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
*
|
12
|
+
* Unless required by applicable law or agreed to in writing, software
|
13
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
* See the License for the specific language governing permissions and
|
16
|
+
* limitations under the License.
|
17
|
+
*/
|
18
|
+
|
19
|
+
#include "parser_ext.h"
|
20
|
+
|
21
|
+
#define STATUS_CONTINUE 1
|
22
|
+
|
23
|
+
#define RB_P(OBJ) \
|
24
|
+
rb_funcall(rb_stderr, rb_intern("print"), 1, rb_funcall(OBJ, rb_intern("object_id"), 0)); \
|
25
|
+
rb_funcall(rb_stderr, rb_intern("print"), 1, rb_str_new2(" ")); \
|
26
|
+
rb_funcall(rb_stderr, rb_intern("print"), 1, rb_funcall(OBJ, rb_intern("class"), 0)); \
|
27
|
+
rb_funcall(rb_stderr, rb_intern("print"), 1, rb_str_new2(" ")); \
|
28
|
+
rb_funcall(rb_stderr, rb_intern("puts"), 1, rb_funcall(OBJ, rb_intern("inspect"), 0));
|
29
|
+
|
30
|
+
#define RERAISE_PARSER_ERROR(parser) \
|
31
|
+
{ \
|
32
|
+
unsigned char* emsg = yajl_get_error(parser->handle, 1, \
|
33
|
+
(const unsigned char*)RSTRING_PTR(p->chunk), \
|
34
|
+
RSTRING_LEN(p->chunk)); \
|
35
|
+
VALUE errobj = rb_exc_new2(c_parse_error, (const char*) emsg); \
|
36
|
+
yajl_free_error(parser->handle, emsg); \
|
37
|
+
rb_exc_raise(errobj); \
|
38
|
+
}
|
39
|
+
|
40
|
+
static int yaji_null(void *ctx)
|
41
|
+
{
|
42
|
+
yaji_parser* p = (yaji_parser*) DATA_PTR(ctx);
|
43
|
+
VALUE rv = rb_ary_new3(3, p->path_str, sym_null, Qnil);
|
44
|
+
rb_ary_push(p->events, rv);
|
45
|
+
return STATUS_CONTINUE;
|
46
|
+
}
|
47
|
+
|
48
|
+
static int yaji_boolean(void *ctx, int val)
|
49
|
+
{
|
50
|
+
yaji_parser* p = (yaji_parser*) DATA_PTR(ctx);
|
51
|
+
VALUE rv = rb_ary_new3(3, p->path_str, sym_boolean, val ? Qtrue : Qfalse);
|
52
|
+
rb_ary_push(p->events, rv);
|
53
|
+
return STATUS_CONTINUE;
|
54
|
+
}
|
55
|
+
|
56
|
+
static int yaji_number(void *ctx, const char *val, unsigned int len)
|
57
|
+
{
|
58
|
+
yaji_parser* p = (yaji_parser*) DATA_PTR(ctx);
|
59
|
+
char buf[len+1];
|
60
|
+
buf[len] = 0;
|
61
|
+
memcpy(buf, val, len);
|
62
|
+
VALUE rv;
|
63
|
+
|
64
|
+
if (memchr(buf, '.', len) || memchr(buf, 'e', len) || memchr(buf, 'E', len)) {
|
65
|
+
rv = rb_ary_new3(3, p->path_str, sym_number, rb_float_new(strtod(buf, NULL)));
|
66
|
+
} else {
|
67
|
+
rv = rb_ary_new3(3, p->path_str, sym_number, rb_cstr2inum(buf, 10));
|
68
|
+
}
|
69
|
+
rb_ary_push(p->events, rv);
|
70
|
+
return STATUS_CONTINUE;
|
71
|
+
}
|
72
|
+
|
73
|
+
#ifdef HAVE_RUBY_ENCODING_H
|
74
|
+
#define YAJI_TO_STR(val, len, str) \
|
75
|
+
str = rb_str_new((const char *)val, len); \
|
76
|
+
rb_encoding *default_internal_enc = rb_default_internal_encoding(); \
|
77
|
+
rb_enc_associate(str, utf8_encoding); \
|
78
|
+
if (default_internal_enc) { \
|
79
|
+
str = rb_str_export_to_enc(str, default_internal_enc); \
|
80
|
+
}
|
81
|
+
#else
|
82
|
+
#define YAJI_TO_STR(val, len, str) \
|
83
|
+
str = rb_str_new((const char *)val, len);
|
84
|
+
#endif
|
85
|
+
|
86
|
+
static int yaji_string(void *ctx, const unsigned char *val, unsigned int len)
|
87
|
+
{
|
88
|
+
yaji_parser* p = (yaji_parser*) DATA_PTR(ctx);
|
89
|
+
VALUE str, rv;
|
90
|
+
YAJI_TO_STR((const char *)val, len, str);
|
91
|
+
rv = rb_ary_new3(3, p->path_str, sym_string, str);
|
92
|
+
rb_ary_push(p->events, rv);
|
93
|
+
return STATUS_CONTINUE;
|
94
|
+
}
|
95
|
+
|
96
|
+
static int yaji_hash_key(void *ctx, const unsigned char *val, unsigned int len)
|
97
|
+
{
|
98
|
+
yaji_parser* p = (yaji_parser*) DATA_PTR(ctx);
|
99
|
+
VALUE key, rv;
|
100
|
+
YAJI_TO_STR((const char *)val, len, key);
|
101
|
+
key = p->symbolize_keys ? ID2SYM(rb_to_id(key)) : key;
|
102
|
+
if (p->key_in_use) {
|
103
|
+
rb_ary_pop(p->path);
|
104
|
+
} else {
|
105
|
+
p->key_in_use = 1;
|
106
|
+
}
|
107
|
+
p->path_str = rb_ary_join(p->path, rb_str_new2("/"));
|
108
|
+
rb_str_freeze(p->path_str);
|
109
|
+
rv = rb_ary_new3(3, p->path_str, sym_hash_key, key);
|
110
|
+
rb_ary_push(p->events, rv);
|
111
|
+
rb_ary_push(p->path, key);
|
112
|
+
p->path_str = rb_ary_join(p->path, rb_str_new2("/"));
|
113
|
+
rb_str_freeze(p->path_str);
|
114
|
+
return STATUS_CONTINUE;
|
115
|
+
}
|
116
|
+
|
117
|
+
static int yaji_start_hash(void *ctx)
|
118
|
+
{
|
119
|
+
yaji_parser* p = (yaji_parser*) DATA_PTR(ctx);
|
120
|
+
p->key_in_use = 0;
|
121
|
+
VALUE rv = rb_ary_new3(3, p->path_str, sym_start_hash, Qnil);
|
122
|
+
rb_ary_push(p->events, rv);
|
123
|
+
return STATUS_CONTINUE;
|
124
|
+
}
|
125
|
+
|
126
|
+
static int yaji_end_hash(void *ctx)
|
127
|
+
{
|
128
|
+
yaji_parser* p = (yaji_parser*) DATA_PTR(ctx);
|
129
|
+
rb_ary_pop(p->path);
|
130
|
+
p->path_str = rb_ary_join(p->path, rb_str_new2("/"));
|
131
|
+
VALUE rv = rb_ary_new3(3, p->path_str, sym_end_hash, Qnil);
|
132
|
+
rb_ary_push(p->events, rv);
|
133
|
+
return STATUS_CONTINUE;
|
134
|
+
}
|
135
|
+
|
136
|
+
static int yaji_start_array(void *ctx)
|
137
|
+
{
|
138
|
+
yaji_parser* p = (yaji_parser*) DATA_PTR(ctx);
|
139
|
+
VALUE rv = rb_ary_new3(3, p->path_str, sym_start_array, Qnil);
|
140
|
+
rb_ary_push(p->path, rb_str_new2(""));
|
141
|
+
p->path_str = rb_ary_join(p->path, rb_str_new2("/"));
|
142
|
+
rb_str_freeze(p->path_str);
|
143
|
+
rb_ary_push(p->events, rv);
|
144
|
+
return STATUS_CONTINUE;
|
145
|
+
}
|
146
|
+
|
147
|
+
static int yaji_end_array(void *ctx)
|
148
|
+
{
|
149
|
+
yaji_parser* p = (yaji_parser*) DATA_PTR(ctx);
|
150
|
+
|
151
|
+
rb_ary_pop(p->path);
|
152
|
+
p->path_str = rb_ary_join(p->path, rb_str_new2("/"));
|
153
|
+
|
154
|
+
VALUE rv = rb_ary_new3(3, p->path_str, sym_end_array, Qnil);
|
155
|
+
rb_ary_push(p->events, rv);
|
156
|
+
return STATUS_CONTINUE;
|
157
|
+
}
|
158
|
+
|
159
|
+
static VALUE rb_yaji_parser_parse_chunk(VALUE chunk, VALUE self)
|
160
|
+
{
|
161
|
+
yajl_status rc;
|
162
|
+
yaji_parser* p = (yaji_parser*) DATA_PTR(self);
|
163
|
+
const char* buf;
|
164
|
+
unsigned int len;
|
165
|
+
int i;
|
166
|
+
|
167
|
+
if (NIL_P(chunk) || (len = RSTRING_LEN(chunk)) == 0) {
|
168
|
+
return INT2FIX(0);
|
169
|
+
}
|
170
|
+
buf = RSTRING_PTR(chunk);
|
171
|
+
p->events = rb_ary_new();
|
172
|
+
p->chunk = chunk;
|
173
|
+
rc = yajl_parse(p->handle, (const unsigned char*)buf, len);
|
174
|
+
if (rc == yajl_status_error) {
|
175
|
+
RERAISE_PARSER_ERROR(p);
|
176
|
+
}
|
177
|
+
for (i=0; i<RARRAY_LEN(p->events); i++) {
|
178
|
+
rb_funcall(p->parser_cb, id_call, 1, RARRAY_PTR(p->events)[i]);
|
179
|
+
}
|
180
|
+
return rb_funcall(chunk, id_bytesize, 0, NULL);
|
181
|
+
}
|
182
|
+
|
183
|
+
static VALUE rb_yaji_parser_new(int argc, VALUE *argv, VALUE klass)
|
184
|
+
{
|
185
|
+
yaji_parser* p;
|
186
|
+
VALUE opts, obj;
|
187
|
+
|
188
|
+
obj = Data_Make_Struct(klass, yaji_parser, rb_yaji_parser_mark, rb_yaji_parser_free, p);
|
189
|
+
p->handle = NULL;
|
190
|
+
p->config.allowComments = 1;
|
191
|
+
p->config.checkUTF8 = 1;
|
192
|
+
p->symbolize_keys = 0;
|
193
|
+
p->rbufsize = Qnil;
|
194
|
+
p->input = Qnil;
|
195
|
+
p->parser_cb = Qnil;
|
196
|
+
|
197
|
+
rb_scan_args(argc, argv, "11", &p->input, &opts);
|
198
|
+
if (TYPE(p->input) == T_STRING) {
|
199
|
+
p->input = rb_class_new_instance(1, &p->input, c_stringio);
|
200
|
+
} else if (rb_respond_to(p->input, id_perform) && rb_respond_to(p->input, id_on_body)) {
|
201
|
+
rb_block_call(p->input, id_on_body, 0, NULL, rb_yaji_parser_parse_chunk, obj);
|
202
|
+
} else if (!rb_respond_to(p->input, id_read)) {
|
203
|
+
rb_raise(c_parse_error, "input must be a String or IO or "
|
204
|
+
"something responding to #perform and #on_body e.g. Curl::Easy");
|
205
|
+
}
|
206
|
+
if (!NIL_P(opts)) {
|
207
|
+
Check_Type(opts, T_HASH);
|
208
|
+
if (rb_hash_aref(opts, sym_allow_comments) == Qfalse) {
|
209
|
+
p->config.allowComments = 0;
|
210
|
+
}
|
211
|
+
if (rb_hash_aref(opts, sym_check_utf8) == Qfalse) {
|
212
|
+
p->config.checkUTF8 = 0;
|
213
|
+
}
|
214
|
+
if (rb_hash_aref(opts, sym_symbolize_keys) == Qtrue) {
|
215
|
+
p->symbolize_keys = 1;
|
216
|
+
}
|
217
|
+
p->rbufsize = rb_hash_aref(opts, sym_read_buffer_size);
|
218
|
+
}
|
219
|
+
if (NIL_P(p->rbufsize)) {
|
220
|
+
p->rbufsize = INT2FIX(READ_BUFSIZE);
|
221
|
+
} else {
|
222
|
+
Check_Type(p->rbufsize, T_FIXNUM);
|
223
|
+
}
|
224
|
+
p->handle = yajl_alloc(&yaji_callbacks, &p->config, NULL, (void *)obj);
|
225
|
+
rb_obj_call_init(obj, 0, 0);
|
226
|
+
return obj;
|
227
|
+
}
|
228
|
+
|
229
|
+
static VALUE rb_yaji_parser_init(int argc, VALUE *argv, VALUE self)
|
230
|
+
{
|
231
|
+
return self;
|
232
|
+
}
|
233
|
+
|
234
|
+
|
235
|
+
static VALUE rb_yaji_parser_parse(int argc, VALUE* argv, VALUE self)
|
236
|
+
{
|
237
|
+
yajl_status rc;
|
238
|
+
yaji_parser* p = (yaji_parser*) DATA_PTR(self);
|
239
|
+
int i;
|
240
|
+
|
241
|
+
rb_scan_args(argc, argv, "00&", &p->parser_cb);
|
242
|
+
RETURN_ENUMERATOR(self, argc, argv);
|
243
|
+
|
244
|
+
p->path = rb_ary_new();
|
245
|
+
rb_ary_push(p->path, rb_str_new("", 0));
|
246
|
+
p->path_str = rb_str_new("", 0);
|
247
|
+
p->chunk = Qnil;
|
248
|
+
|
249
|
+
if (rb_respond_to(p->input, id_perform)) {
|
250
|
+
rb_funcall(p->input, id_perform, 0);
|
251
|
+
} else {
|
252
|
+
p->chunk = rb_str_new(NULL, 0);
|
253
|
+
while (rb_funcall(p->input, id_read, 2, p->rbufsize, p->chunk) != Qnil) {
|
254
|
+
rb_yaji_parser_parse_chunk(p->chunk, self);
|
255
|
+
}
|
256
|
+
}
|
257
|
+
|
258
|
+
p->events = rb_ary_new();
|
259
|
+
rc = yajl_parse_complete(p->handle);
|
260
|
+
|
261
|
+
if (rc == yajl_status_error ||
|
262
|
+
(rc == yajl_status_insufficient_data && p->chunk != Qnil &&
|
263
|
+
RSTRING_LEN(rb_funcall(p->chunk, id_strip, 0)) != 0)) {
|
264
|
+
RERAISE_PARSER_ERROR(p);
|
265
|
+
}
|
266
|
+
for (i=0; i<RARRAY_LEN(p->events); i++) {
|
267
|
+
rb_funcall(p->parser_cb, id_call, 1, RARRAY_PTR(p->events)[i]);
|
268
|
+
}
|
269
|
+
|
270
|
+
return Qnil;
|
271
|
+
}
|
272
|
+
|
273
|
+
static int rb_yaji_str_start_with(VALUE str, VALUE query)
|
274
|
+
{
|
275
|
+
int i;
|
276
|
+
const char *ptr = RSTRING_PTR(str);
|
277
|
+
int len = RSTRING_LEN(str);
|
278
|
+
VALUE entry;
|
279
|
+
|
280
|
+
switch(TYPE(query)) {
|
281
|
+
case T_STRING:
|
282
|
+
return RSTRING_LEN(query) <= len && memcmp(RSTRING_PTR(query), ptr, RSTRING_LEN(query)) == 0;
|
283
|
+
break;
|
284
|
+
case T_ARRAY:
|
285
|
+
for (i=0; i<RARRAY_LEN(query); i++) {
|
286
|
+
entry = RARRAY_PTR(query)[i];
|
287
|
+
if (RSTRING_LEN(entry) <= len && memcmp(RSTRING_PTR(entry), ptr, RSTRING_LEN(entry)) == 0) {
|
288
|
+
return 1;
|
289
|
+
}
|
290
|
+
}
|
291
|
+
break;
|
292
|
+
}
|
293
|
+
return 0;
|
294
|
+
}
|
295
|
+
|
296
|
+
static VALUE rb_yaji_each_iter(VALUE chunk, VALUE* params_p)
|
297
|
+
{
|
298
|
+
VALUE* params = (VALUE*)params_p;
|
299
|
+
VALUE path = rb_ary_shift(chunk);
|
300
|
+
VALUE event = rb_ary_shift(chunk);
|
301
|
+
VALUE value = rb_ary_shift(chunk);
|
302
|
+
VALUE proc = params[0];
|
303
|
+
VALUE stack = params[1];
|
304
|
+
VALUE query = params[2];
|
305
|
+
VALUE with_path = params[3];
|
306
|
+
VALUE last_entry, object, container, key, hash;
|
307
|
+
|
308
|
+
if (NIL_P(query) || rb_yaji_str_start_with(path, query)) {
|
309
|
+
if (event == sym_hash_key) {
|
310
|
+
rb_ary_push(stack, value);
|
311
|
+
} else if (event == sym_start_hash || event == sym_start_array) {
|
312
|
+
container = (event == sym_start_hash) ? rb_hash_new() : rb_ary_new();
|
313
|
+
last_entry = rb_ary_entry(stack, -1);
|
314
|
+
switch(TYPE(last_entry)) {
|
315
|
+
case T_STRING:
|
316
|
+
key = rb_ary_pop(stack);
|
317
|
+
hash = rb_ary_entry(stack, -1);
|
318
|
+
rb_hash_aset(hash, key, container);
|
319
|
+
break;
|
320
|
+
case T_ARRAY:
|
321
|
+
rb_ary_push(last_entry, container);
|
322
|
+
}
|
323
|
+
rb_ary_push(stack, container);
|
324
|
+
} else if (event == sym_end_hash || event == sym_end_array) {
|
325
|
+
object = rb_ary_pop(stack);
|
326
|
+
if (RARRAY_LEN(stack) == 0) {
|
327
|
+
if (with_path == Qnil || with_path == Qfalse) {
|
328
|
+
rb_funcall(proc, id_call, 1, object);
|
329
|
+
} else {
|
330
|
+
rb_funcall(proc, id_call, 1, rb_ary_new3(2, path, object));
|
331
|
+
}
|
332
|
+
}
|
333
|
+
} else {
|
334
|
+
last_entry = rb_ary_entry(stack, -1);
|
335
|
+
switch(TYPE(last_entry)) {
|
336
|
+
case T_STRING:
|
337
|
+
key = rb_ary_pop(stack);
|
338
|
+
hash = rb_ary_entry(stack, -1);
|
339
|
+
rb_hash_aset(hash, key, value);
|
340
|
+
break;
|
341
|
+
case T_ARRAY:
|
342
|
+
rb_ary_push(last_entry, value);
|
343
|
+
break;
|
344
|
+
case T_NIL:
|
345
|
+
if (with_path == Qnil || with_path == Qfalse) {
|
346
|
+
rb_funcall(proc, id_call, 1, value);
|
347
|
+
} else {
|
348
|
+
rb_funcall(proc, id_call, 1, rb_ary_new3(2, path, value));
|
349
|
+
}
|
350
|
+
break;
|
351
|
+
}
|
352
|
+
}
|
353
|
+
}
|
354
|
+
return Qnil;
|
355
|
+
}
|
356
|
+
|
357
|
+
static VALUE rb_yaji_parser_each(int argc, VALUE* argv, VALUE self)
|
358
|
+
{
|
359
|
+
VALUE query, proc, options, params[4];
|
360
|
+
RETURN_ENUMERATOR(self, argc, argv);
|
361
|
+
rb_scan_args(argc, argv, "02&", &query, &options, &proc);
|
362
|
+
params[0] = proc; // callback
|
363
|
+
params[1] = rb_ary_new(); // stack
|
364
|
+
params[2] = query;
|
365
|
+
if (options != Qnil) {
|
366
|
+
Check_Type(options, T_HASH);
|
367
|
+
params[3] = rb_hash_aref(options, sym_with_path);
|
368
|
+
} else {
|
369
|
+
params[3] = Qnil;
|
370
|
+
}
|
371
|
+
rb_block_call(self, id_parse, 0, NULL, rb_yaji_each_iter, (VALUE)params);
|
372
|
+
return Qnil;
|
373
|
+
}
|
374
|
+
|
375
|
+
static void rb_yaji_parser_free(void *parser)
|
376
|
+
{
|
377
|
+
yaji_parser* p = parser;
|
378
|
+
if (p) {
|
379
|
+
if (p->handle) {
|
380
|
+
yajl_free(p->handle);
|
381
|
+
}
|
382
|
+
free(p);
|
383
|
+
}
|
384
|
+
}
|
385
|
+
|
386
|
+
static void rb_yaji_parser_mark(void *parser)
|
387
|
+
{
|
388
|
+
yaji_parser* p = parser;
|
389
|
+
if (p) {
|
390
|
+
rb_gc_mark(p->input);
|
391
|
+
rb_gc_mark(p->rbufsize);
|
392
|
+
rb_gc_mark(p->events);
|
393
|
+
rb_gc_mark(p->path);
|
394
|
+
rb_gc_mark(p->path_str);
|
395
|
+
rb_gc_mark(p->parser_cb);
|
396
|
+
rb_gc_mark(p->chunk);
|
397
|
+
}
|
398
|
+
}
|
399
|
+
|
400
|
+
/* Ruby Extension initializer */
|
401
|
+
void Init_parser_ext() {
|
402
|
+
m_yaji = rb_define_module("YAJI");
|
403
|
+
|
404
|
+
c_parse_error = rb_define_class_under(m_yaji, "ParseError", rb_eStandardError);
|
405
|
+
|
406
|
+
c_yaji_parser = rb_define_class_under(m_yaji, "Parser", rb_cObject);
|
407
|
+
rb_define_const(c_yaji_parser, "READ_BUFFER_SIZE", INT2FIX(READ_BUFSIZE));
|
408
|
+
rb_define_singleton_method(c_yaji_parser, "new", rb_yaji_parser_new, -1);
|
409
|
+
rb_define_method(c_yaji_parser, "initialize", rb_yaji_parser_init, -1);
|
410
|
+
rb_define_method(c_yaji_parser, "parse", rb_yaji_parser_parse, -1);
|
411
|
+
rb_define_method(c_yaji_parser, "each", rb_yaji_parser_each, -1);
|
412
|
+
|
413
|
+
id_call = rb_intern("call");
|
414
|
+
id_read = rb_intern("read");
|
415
|
+
id_parse = rb_intern("parse");
|
416
|
+
id_strip = rb_intern("strip");
|
417
|
+
id_perform = rb_intern("perform");
|
418
|
+
id_on_body = rb_intern("on_body");
|
419
|
+
id_bytesize = rb_intern("bytesize");
|
420
|
+
|
421
|
+
sym_allow_comments = ID2SYM(rb_intern("allow_comments"));
|
422
|
+
sym_check_utf8 = ID2SYM(rb_intern("check_utf8"));
|
423
|
+
sym_symbolize_keys = ID2SYM(rb_intern("symbolize_keys"));
|
424
|
+
sym_read_buffer_size = ID2SYM(rb_intern("read_buffer_size"));
|
425
|
+
sym_with_path = ID2SYM(rb_intern("with_path"));
|
426
|
+
sym_null = ID2SYM(rb_intern("null"));
|
427
|
+
sym_boolean = ID2SYM(rb_intern("boolean"));
|
428
|
+
sym_number = ID2SYM(rb_intern("number"));
|
429
|
+
sym_string = ID2SYM(rb_intern("string"));
|
430
|
+
sym_hash_key = ID2SYM(rb_intern("hash_key"));
|
431
|
+
sym_start_hash = ID2SYM(rb_intern("start_hash"));
|
432
|
+
sym_end_hash = ID2SYM(rb_intern("end_hash"));
|
433
|
+
sym_start_array = ID2SYM(rb_intern("start_array"));
|
434
|
+
sym_end_array = ID2SYM(rb_intern("end_array"));
|
435
|
+
|
436
|
+
#ifdef HAVE_RUBY_ENCODING_H
|
437
|
+
utf8_encoding = rb_utf8_encoding();
|
438
|
+
#endif
|
439
|
+
|
440
|
+
rb_require("stringio");
|
441
|
+
c_stringio = rb_const_get(rb_cObject, rb_intern("StringIO"));
|
442
|
+
}
|