yaji 0.2.3-x86-mingw32
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.
- 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
|
+
}
|