fast-xml 1.0.0

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: d5c3255eaacfe69128a428f67ab670d680e05bf2
4
+ data.tar.gz: 65b1e5898db7b64bd1b58ec0b23fedb1c3692a1c
5
+ SHA512:
6
+ metadata.gz: 8a290a9676f388f342c63e2fdef2d2445f2db91df0d58b13cb64515f019c6595ce18cfef7466c2b932de527dca1230ef690280ec1ef7598ce6a25e9038a8d727
7
+ data.tar.gz: b0c95264bf47245e3d53e28844cee4fffe468b365098a45881f0869be4c01f9533ba9420527fc24c7ef8359303a3ee0779efae06d1a18073bde7336fc104a684
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2017 Yuriy Ustushenko
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,164 @@
1
+ # FastXML
2
+
3
+ Fast Ruby Hash to XML and XML to Ruby Hash converter written in pure C.
4
+
5
+ [![Build Status](https://secure.travis-ci.org/yoreek/fastxml.png?branch=master)](http://travis-ci.org/yoreek/fastxml)
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem 'fastxml'
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install fast-xml
22
+
23
+ ## Release Notes
24
+
25
+ See [CHANGELOG.md](CHANGELOG.md)
26
+
27
+ ## Usage
28
+
29
+ ```ruby
30
+ require 'fastxml'
31
+
32
+ # convert hash to XML
33
+ FastXML.hash2xml({ tag1: { tag2: 'content' } }, indent: 2)
34
+ # =>
35
+ # <?xml version="1.0" encoding="utf-8"?>
36
+ # <root>
37
+ # <tag1>
38
+ # <tag2>content</tag2>
39
+ # </tag1>
40
+ # </root>
41
+
42
+ # use enumerator
43
+ FastXML.hash2xml({ enumerator: 3.times }, indent: 2)
44
+ # =>
45
+ # <?xml version="1.0" encoding="utf-8"?>
46
+ # <root>
47
+ # <enumerator>0</enumerator>
48
+ # <enumerator>1</enumerator>
49
+ # <enumerator>2</enumerator>
50
+ # </root>
51
+
52
+ # output to file handle
53
+ # fh = StringIO.new
54
+ FastXML.hash2xml({ enumerator: 3.times }, indent: 2, output: fh)
55
+ fh.string
56
+ # =>
57
+ # <?xml version="1.0" encoding="utf-8"?>
58
+ # <root>
59
+ # <enumerator>0</enumerator>
60
+ # <enumerator>1</enumerator>
61
+ # <enumerator>2</enumerator>
62
+ # </root>
63
+
64
+ ```
65
+
66
+ ## Options
67
+
68
+ The following options are available to pass to FastXML.hash2xml(hash, options = {}).
69
+
70
+ * **:root** => 'root'
71
+ * Root node name.
72
+
73
+ * **:version** => '1.0'
74
+ * XML document version
75
+
76
+ * **:encoding** => 'utf-8'
77
+ * XML input/output encoding
78
+
79
+ * **:indent** => 0
80
+ * if indent great than "0", XML output should be indented according to
81
+ its hierarchic structure. This value determines the number of
82
+ spaces.
83
+
84
+ * if indent is "0", XML output will all be on one line.
85
+
86
+ * **:output** => nil
87
+ * XML output method
88
+
89
+ * if output is nil, XML document dumped into string.
90
+
91
+ * if output is filehandle, XML document writes directly to a filehandle or a
92
+ stream.
93
+
94
+ * **:canonical** => false
95
+ * if canonical is "true", converter will be write hashes sorted by key.
96
+
97
+ * if canonical is "false", order of the element will be pseudo-randomly.
98
+
99
+ * **:use_attr** => false
100
+ * if use_attr is "true", converter will be use the attributes.
101
+
102
+ * if use_attr is "fale", converter will be use tags only.
103
+
104
+ * **:content** => nil
105
+ * if defined that the key name for the text content(used only if
106
+ use_attr = true).
107
+
108
+ * **:force_array** => nil
109
+ * This option is similar to "ForceArray" from [XMl::Simple module]:
110
+ (https://metacpan.org/pod/XML::Simple#ForceArray-1-in---important).
111
+
112
+ * **:force_content** => nil
113
+ * This option is similar to "ForceContent" from [XMl::Simple module]:
114
+ (https://metacpan.org/pod/XML::Simple#ForceContent-1-in---seldom-used).
115
+
116
+ * **:merge_text** => false
117
+ * Setting this option to "true" will cause merge adjacent text nodes.
118
+
119
+ * **:xml_decl** => true
120
+ * if xml_decl is "true", output will start with the XML declaration
121
+ '<?xml version="1.0" encoding="utf-8"?>'.
122
+
123
+ * if xml_decl is "false", XML declaration will not be output.
124
+
125
+ * **:trim** => false
126
+ * Trim leading and trailing whitespace from text nodes.
127
+
128
+ * **:utf8** => true
129
+ * Turn on utf8 flag for strings if enabled.
130
+
131
+ * **:max_depth** => 1024
132
+ * Maximum recursion depth.
133
+
134
+ * **:buf_size** => 4096
135
+ * Buffer size for reading end encoding data.
136
+
137
+ * **:keep_root** => false
138
+ * Keep root element.
139
+
140
+ ## Configuration
141
+ ```
142
+ FastXML.configure do |config|
143
+ config.trim = true
144
+ end
145
+ ```
146
+
147
+ ## Benchmarks
148
+
149
+ Performance benchmark in comparison with some popular gems:
150
+
151
+ ```
152
+ Converting Hash to XML:
153
+ user system total real
154
+ activesupport(rexml) 11.020000 0.000000 11.020000 ( 11.058084)
155
+ activesupport(libxml) 10.690000 0.000000 10.690000 ( 10.731521)
156
+ activesupport(nokogiri) 10.730000 0.010000 10.740000 ( 10.769866)
157
+ xmlsimple 1.470000 0.000000 1.470000 ( 1.477457)
158
+ fastxml 0.010000 0.000000 0.010000 ( 0.018434)
159
+ ```
160
+
161
+ ## License
162
+
163
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
164
+
@@ -0,0 +1,17 @@
1
+ require 'mkmf'
2
+
3
+ extension_name = 'fastxml'
4
+ dir_config(extension_name)
5
+
6
+ puts ">>>>> Creating Makefile for #{RUBY_DESCRIPTION} <<<<<"
7
+
8
+ OPTS = Hash[
9
+ ARGV.map { |a| a =~ /^--with-(.+)(?:=(.*))?$/ ? [$1.to_sym, $2 || true] : nil }.compact
10
+ ].freeze
11
+
12
+ if OPTS[:debug]
13
+ $CPPFLAGS += ' -g -Wall -Werror -Wextra -pedantic -std=c99 -DWITH_DEBUG -O0'
14
+ $CPPFLAGS += ' -DWITH_TRACE' if OPTS[:trace]
15
+ end
16
+
17
+ create_makefile(extension_name)
@@ -0,0 +1,67 @@
1
+ #include "fastxml.h"
2
+
3
+ #include "xh_config.h"
4
+ #include "xh_core.h"
5
+
6
+ void Init_fastxml();
7
+
8
+ VALUE xh_module;
9
+ VALUE xh_parse_error_class;
10
+ ID xh_id_next;
11
+
12
+ typedef struct {
13
+ int argc;
14
+ VALUE *argv;
15
+ xh_h2x_ctx_t *ctx;
16
+ } xh_h2x_arg_t;
17
+
18
+ static VALUE
19
+ hash2xml_exec(VALUE a) {
20
+ xh_h2x_arg_t *arg = (xh_h2x_arg_t *) a;
21
+
22
+ xh_h2x_init_ctx(arg->ctx, arg->argc, arg->argv);
23
+
24
+ return xh_h2x(arg->ctx);
25
+ }
26
+
27
+ static VALUE
28
+ hash2xml(int argc, VALUE *argv, VALUE self) {
29
+ xh_h2x_ctx_t ctx;
30
+ VALUE result;
31
+ int state;
32
+ xh_h2x_arg_t arg;
33
+
34
+ arg.argc = argc;
35
+ arg.argv = argv;
36
+ arg.ctx = &ctx;
37
+
38
+ result = rb_protect(hash2xml_exec, (VALUE) &arg, &state);
39
+
40
+ if (state) {
41
+ xh_h2x_destroy_ctx(&ctx);
42
+ rb_exc_raise(rb_errinfo());
43
+ }
44
+
45
+ if (ctx.opts.output != Qnil) result = Qnil;
46
+
47
+ xh_h2x_destroy_ctx(&ctx);
48
+
49
+ return result;
50
+ }
51
+
52
+ static VALUE
53
+ xml2hash(int argc, VALUE *argv, VALUE self) {
54
+ VALUE obj = Qnil;
55
+
56
+ return obj;
57
+ }
58
+
59
+ void Init_fastxml(void) {
60
+ xh_module = rb_define_module("FastXML");
61
+ xh_parse_error_class = rb_const_get_at(xh_module, rb_intern("ParseError"));
62
+
63
+ rb_define_module_function(xh_module, "hash2xml", hash2xml, -1);
64
+ rb_define_module_function(xh_module, "xml2hash", xml2hash, -1);
65
+
66
+ xh_id_next = rb_intern("next");
67
+ }
@@ -0,0 +1,14 @@
1
+ #ifndef __FASTXML_H__
2
+ #define __FASTXML_H__
3
+
4
+ #if defined(__cplusplus)
5
+ extern "C" {
6
+ #endif
7
+
8
+ #include "ruby.h"
9
+
10
+ #if defined(__cplusplus)
11
+ } /* extern "C" { */
12
+ #endif
13
+
14
+ #endif /* __FASTXML_H__ */
data/ext/fastxml/xh.c ADDED
@@ -0,0 +1,338 @@
1
+ #include "xh_config.h"
2
+ #include "xh_core.h"
3
+
4
+ xh_bool_t
5
+ xh_init_opts(xh_opts_t *opts)
6
+ {
7
+ xh_char_t method[XH_PARAM_LEN];
8
+
9
+ XH_PARAM_READ_INIT
10
+
11
+ /* native options */
12
+ XH_PARAM_READ_STRING (opts->root, "@root");
13
+ XH_PARAM_READ_STRING (opts->version, "@version");
14
+ XH_PARAM_READ_STRING (opts->encoding, "@encoding");
15
+ XH_PARAM_READ_INT (opts->indent, "@indent");
16
+ XH_PARAM_READ_BOOL (opts->canonical, "@canonical");
17
+ XH_PARAM_READ_STRING (opts->content, "@content");
18
+ XH_PARAM_READ_BOOL (opts->utf8, "@utf8");
19
+ XH_PARAM_READ_BOOL (opts->xml_decl, "@xml_decl");
20
+ XH_PARAM_READ_BOOL (opts->keep_root, "@keep_root");
21
+ #ifdef XH_HAVE_DOM
22
+ XH_PARAM_READ_BOOL (opts->doc, "@doc");
23
+ #endif
24
+ XH_PARAM_READ_INT (opts->max_depth, "@max_depth");
25
+ XH_PARAM_READ_INT (opts->buf_size, "@buf_size");
26
+ XH_PARAM_READ_PATTERN(opts->force_array, "@force_array");
27
+ XH_PARAM_READ_BOOL (opts->force_content, "@force_content");
28
+ XH_PARAM_READ_BOOL (opts->merge_text, "@merge_text");
29
+
30
+ /* XML::Hash::LX options */
31
+ XH_PARAM_READ_STRING (opts->attr, "@attr");
32
+ opts->attr_len = xh_strlen(opts->attr);
33
+ XH_PARAM_READ_STRING (opts->text, "@text");
34
+ XH_PARAM_READ_BOOL (opts->trim, "@trim");
35
+ XH_PARAM_READ_STRING (opts->cdata, "@cdata");
36
+ XH_PARAM_READ_STRING (opts->comm, "@comm");
37
+
38
+ /* method */
39
+ XH_PARAM_READ_STRING (method, "@method");
40
+ XH_PARAM_READ_BOOL (opts->use_attr, "@use_attr");
41
+ if (xh_strcmp(method, XH_CHAR_CAST "LX") == 0) {
42
+ opts->method = XH_METHOD_LX;
43
+ }
44
+ else {
45
+ opts->method = XH_METHOD_NATIVE;
46
+ }
47
+
48
+ /* output, NULL - to string */
49
+ XH_PARAM_READ_REF (opts->output, "@output");
50
+
51
+ return TRUE;
52
+ }
53
+
54
+ xh_opts_t *
55
+ xh_create_opts(void)
56
+ {
57
+ xh_opts_t *opts;
58
+
59
+ if ((opts = malloc(sizeof(xh_opts_t))) == NULL) {
60
+ return NULL;
61
+ }
62
+ memset(opts, 0, sizeof(xh_opts_t));
63
+
64
+ if (! xh_init_opts(opts)) {
65
+ xh_destroy_opts(opts);
66
+ return NULL;
67
+ }
68
+
69
+ return opts;
70
+ }
71
+
72
+ void
73
+ xh_destroy_opts(xh_opts_t *opts)
74
+ {
75
+ /* nothing */
76
+ }
77
+
78
+ void
79
+ xh_copy_opts(xh_opts_t *dst, xh_opts_t *src)
80
+ {
81
+ memcpy(dst, src, sizeof(xh_opts_t));
82
+ }
83
+
84
+ static int
85
+ xh_parse_arg(VALUE key, VALUE value, VALUE ctx)
86
+ {
87
+ xh_opts_t *opts = (xh_opts_t *) ctx;
88
+ xh_char_t *keyptr, *valueptr;
89
+ size_t keylen, valuelen;
90
+
91
+ if (SYMBOL_P(key)) {
92
+ key = rb_sym2str(key);
93
+ }
94
+
95
+ keyptr = XH_CHAR_CAST RSTRING_PTR(key);
96
+ keylen = RSTRING_LEN(key);
97
+
98
+ switch (keylen) {
99
+ case 2:
100
+ if (xh_str_equal2(keyptr, 'c', 'b')) {
101
+ VALUE v = rb_inspect(value);
102
+ rb_warn("cb: %s\n", StringValueCStr(v));
103
+ opts->cb = xh_param_assign_cb(value);
104
+ break;
105
+ }
106
+ goto error;
107
+ #ifdef XH_HAVE_DOM
108
+ case 3:
109
+ if (xh_str_equal3(keyptr, 'd', 'o', 'c')) {
110
+ opts->doc = xh_param_assign_bool(value);
111
+ break;
112
+ }
113
+ goto error;
114
+ #endif
115
+ case 4:
116
+ if (xh_str_equal4(keyptr, 'a', 't', 't', 'r')) {
117
+ xh_param_assign_string(opts->attr, value);
118
+ if (opts->attr[0] == '\0') {
119
+ opts->attr_len = 0;
120
+ }
121
+ else {
122
+ opts->attr_len = xh_strlen(opts->attr);
123
+ }
124
+ break;
125
+ }
126
+ if (xh_str_equal4(keyptr, 'c', 'o', 'm', 'm')) {
127
+ xh_param_assign_string(opts->comm, value);
128
+ break;
129
+ }
130
+ if (xh_str_equal4(keyptr, 'r', 'o', 'o', 't')) {
131
+ xh_param_assign_string(opts->root, value);
132
+ break;
133
+ }
134
+ if (xh_str_equal4(keyptr, 't', 'r', 'i', 'm')) {
135
+ opts->trim = xh_param_assign_bool(value);
136
+ break;
137
+ }
138
+ if (xh_str_equal4(keyptr, 't', 'e', 'x', 't')) {
139
+ xh_param_assign_string(opts->text, value);
140
+ break;
141
+ }
142
+ if (xh_str_equal4(keyptr, 'u', 't', 'f', '8')) {
143
+ opts->utf8 = xh_param_assign_bool(value);
144
+ break;
145
+ }
146
+ goto error;
147
+ case 5:
148
+ if (xh_str_equal5(keyptr, 'c', 'd', 'a', 't', 'a')) {
149
+ xh_param_assign_string(opts->cdata, value);
150
+ break;
151
+ }
152
+ goto error;
153
+ case 6:
154
+ if (xh_str_equal6(keyptr, 'i', 'n', 'd', 'e', 'n', 't')) {
155
+ xh_param_assign_int(keyptr, &opts->indent, value);
156
+ break;
157
+ }
158
+ if (xh_str_equal6(keyptr, 'm', 'e', 't', 'h', 'o', 'd')) {
159
+ if (value == Qnil) {
160
+ rb_raise(rb_eArgError, "Parameter '%s' is undefined", StringValueCStr(key));
161
+ }
162
+ valueptr = XH_CHAR_CAST RSTRING_PTR(value);
163
+ valuelen = RSTRING_LEN(value);
164
+ switch (valuelen) {
165
+ case 6:
166
+ if (xh_str_equal6(valueptr, 'N', 'A', 'T', 'I', 'V', 'E')) {
167
+ opts->method = XH_METHOD_NATIVE;
168
+ break;
169
+ }
170
+ goto error_value;
171
+ case 2:
172
+ if (valueptr[0] == 'L' && valueptr[1] == 'X') {
173
+ opts->method = XH_METHOD_LX;
174
+ break;
175
+ }
176
+ goto error_value;
177
+ default:
178
+ goto error_value;
179
+ }
180
+ break;
181
+ }
182
+ if (xh_str_equal6(keyptr, 'o', 'u', 't', 'p', 'u', 't')) {
183
+ if ( RTEST(value) ) {
184
+ opts->output = value;
185
+ }
186
+ else {
187
+ opts->output = Qnil;
188
+ }
189
+ break;
190
+ }
191
+ if (xh_str_equal6(keyptr, 'f', 'i', 'l', 't', 'e', 'r')) {
192
+ xh_param_assign_filter(&opts->filter, value);
193
+ break;
194
+ }
195
+ goto error;
196
+ case 7:
197
+ if (xh_str_equal7(keyptr, 'c', 'o', 'n', 't', 'e', 'n', 't')) {
198
+ xh_param_assign_string(opts->content, value);
199
+ break;
200
+ }
201
+ if (xh_str_equal7(keyptr, 'v', 'e', 'r', 's', 'i', 'o', 'n')) {
202
+ xh_param_assign_string(opts->version, value);
203
+ break;
204
+ }
205
+ goto error;
206
+ case 8:
207
+ if (xh_str_equal8(keyptr, 'e', 'n', 'c', 'o', 'd', 'i', 'n', 'g')) {
208
+ xh_param_assign_string(opts->encoding, value);
209
+ break;
210
+ }
211
+ if (xh_str_equal8(keyptr, 'u', 's', 'e', '_', 'a', 't', 't', 'r')) {
212
+ opts->use_attr = xh_param_assign_bool(value);
213
+ break;
214
+ }
215
+ if (xh_str_equal8(keyptr, 'x', 'm', 'l', '_', 'd', 'e', 'c', 'l')) {
216
+ opts->xml_decl = xh_param_assign_bool(value);
217
+ break;
218
+ }
219
+ if (xh_str_equal8(keyptr, 'b', 'u', 'f', '_', 's', 'i', 'z', 'e')) {
220
+ xh_param_assign_int(keyptr, &opts->buf_size, value);
221
+ break;
222
+ }
223
+ goto error;
224
+ case 9:
225
+ if (xh_str_equal9(keyptr, 'c', 'a', 'n', 'o', 'n', 'i', 'c', 'a', 'l')) {
226
+ opts->canonical = xh_param_assign_bool(value);
227
+ break;
228
+ }
229
+ if (xh_str_equal9(keyptr, 'm', 'a', 'x', '_', 'd', 'e', 'p', 't', 'h')) {
230
+ xh_param_assign_int(keyptr, &opts->max_depth, value);
231
+ break;
232
+ }
233
+ if (xh_str_equal9(keyptr, 'k', 'e', 'e', 'p', '_', 'r', 'o', 'o', 't')) {
234
+ opts->keep_root = xh_param_assign_bool(value);
235
+ break;
236
+ }
237
+ goto error;
238
+ case 10:
239
+ if (xh_str_equal10(keyptr, 'm', 'e', 'r', 'g', 'e', '_', 't', 'e', 'x', 't')) {
240
+ opts->merge_text = xh_param_assign_bool(value);
241
+ break;
242
+ }
243
+ case 11:
244
+ if (xh_str_equal11(keyptr, 'f', 'o', 'r', 'c', 'e', '_', 'a', 'r', 'r', 'a', 'y')) {
245
+ xh_param_assign_pattern(&opts->force_array, value);
246
+ break;
247
+ }
248
+ case 13:
249
+ if (xh_str_equal13(keyptr, 'f', 'o', 'r', 'c', 'e', '_', 'c', 'o', 'n', 't', 'e', 'n', 't')) {
250
+ opts->force_content = xh_param_assign_bool(value);
251
+ break;
252
+ }
253
+ default:
254
+ goto error;
255
+ }
256
+
257
+ return ST_CONTINUE;
258
+
259
+ error_value:
260
+ rb_raise(rb_eArgError, "Invalid parameter value for '%s': %s", StringValueCStr(key), StringValueCStr(value));
261
+
262
+ error:
263
+ rb_raise(rb_eArgError, "Invalid parameter '%s'", StringValueCStr(key));
264
+ }
265
+
266
+ void
267
+ xh_parse_args(xh_opts_t *opts, xh_int_t *nparam, xh_int_t argc, VALUE *argv)
268
+ {
269
+ VALUE hash;
270
+
271
+ if (*nparam >= argc)
272
+ return;
273
+
274
+ hash = argv[*nparam];
275
+ if (rb_cHash != rb_obj_class(hash))
276
+ rb_raise(rb_eArgError, "Parameter is not a hash");
277
+
278
+ (*nparam)++;
279
+
280
+ rb_hash_foreach(hash, xh_parse_arg, (VALUE) opts);
281
+ }
282
+
283
+ void *
284
+ xh_get_obj_param(xh_int_t *nparam, xh_int_t argc, VALUE *argv, const char *class)
285
+ {
286
+ void *obj = NULL;
287
+ /*
288
+ SV *param;
289
+
290
+ if (*nparam >= items)
291
+ rb_raise(rb_eArgError, "Invalid parameters");
292
+
293
+ param = ST(*nparam);
294
+ if ( sv_derived_from(param, class) ) {
295
+ if ( sv_isobject(param) ) {
296
+ // reference to object
297
+ IV tmp = SvIV((SV *) SvRV(param));
298
+ obj = INT2PTR(xh_opts_t *, tmp);
299
+ }
300
+ (*nparam)++;
301
+ }
302
+ */
303
+ return obj;
304
+ }
305
+
306
+ VALUE
307
+ xh_get_hash_param(xh_int_t *nparam, xh_int_t argc, VALUE *argv)
308
+ {
309
+ VALUE param;
310
+
311
+ if (*nparam >= argc)
312
+ rb_raise(rb_eArgError, "Invalid parameters");
313
+
314
+ param = argv[*nparam];
315
+ if (rb_cHash != rb_obj_class(param))
316
+ rb_raise(rb_eArgError, "Parameter is not a hash");
317
+
318
+ (*nparam)++;
319
+
320
+ return param;
321
+ }
322
+
323
+ void
324
+ xh_merge_opts(xh_opts_t *ctx_opts, xh_opts_t *opts, xh_int_t *nparam, xh_int_t argc, VALUE *argv)
325
+ {
326
+ if (opts == NULL) {
327
+ /* read global options */
328
+ xh_init_opts(ctx_opts);
329
+ }
330
+ else {
331
+ /* copy options from object */
332
+ xh_copy_opts(ctx_opts, opts);
333
+ }
334
+ if (*nparam < argc) {
335
+ xh_parse_args(ctx_opts, nparam, argc, argv);
336
+ }
337
+ }
338
+