Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 6 additions & 101 deletions ext/json_scanner/json_scanner.c
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
#include "json_scanner.h"
#include "scan_ctx.h"
#include "options.h"
#include "selector.h"

VALUE rb_mJsonScanner;
VALUE rb_cJsonScannerSelector;
VALUE rb_cJsonScannerOptions;
VALUE rb_eJsonScannerParseError;
#define BYTES_CONSUMED "bytes_consumed"
ID rb_iv_bytes_consumed;
#define SCAN_KWARGS_SIZE 9
ID scan_kwargs_table[SCAN_KWARGS_SIZE];

#include "options.h"

VALUE null_sym;
VALUE boolean_sym;
Expand All @@ -20,89 +17,6 @@ VALUE array_sym;

VALUE any_key_sym;

enum matcher_type
{
MATCHER_KEY,
MATCHER_INDEX,
MATCHER_ANY_KEY,
MATCHER_INDEX_RANGE,
// MATCHER_KEYS_LIST,
// MATCHER_KEY_REGEX,
};

enum path_type
{
PATH_KEY,
PATH_INDEX,
};

typedef struct
{
const char *val;
size_t len;
} hashkey_t;

typedef struct
{
const char *val;
size_t len;
int owned;
} path_key_t;

typedef struct
{
long start;
long end;
} range_t;

typedef struct
{
enum matcher_type type;
union
{
hashkey_t key;
long index;
range_t range;
} value;
} path_matcher_elem_t;

typedef struct
{
enum path_type type;
union
{
path_key_t key;
long index;
} value;
} path_elem_t;

typedef struct
{
path_matcher_elem_t *elems;
int len;
int matched_depth;
} paths_t;

typedef struct
{
int with_path;
int symbolize_path_keys;
int paths_len;
paths_t *paths;
int current_path_len;
int max_path_len;
path_elem_t *current_path;
// Easier to use a Ruby array for result than convert later
// must be supplied by the caller and RB_GC_GUARD-ed if it isn't on the stack
VALUE points_list;
VALUE roots_info_list;
// by depth
size_t *starts;
yajl_handle handle;
size_t yajl_bytes_consumed;
const unsigned char *json_text;
size_t json_text_len;
} scan_ctx;

static inline size_t scan_ctx_get_bytes_consumed(scan_ctx *ctx)
{
Expand Down Expand Up @@ -323,6 +237,7 @@ static void scan_ctx_init(scan_ctx *ctx, VALUE path_ary)
// Assign ctx->paths early so ruby_xfree(ctx->paths) will free the arena
// if a Ruby exception happens during the population loop below
ctx->paths = paths;
ctx->paths_arena_size = arena_size;
ctx->paths_len = 0;
ctx->current_path = NULL;
ctx->starts = NULL;
Expand Down Expand Up @@ -740,9 +655,6 @@ static int scan_on_end_array(void *ctx)
return true;
}

#include "selector.h"


static yajl_callbacks scan_callbacks = {
scan_on_null,
scan_on_boolean,
Expand Down Expand Up @@ -860,6 +772,8 @@ static VALUE scan(int argc, VALUE *argv, VALUE self)
{
free_ctx = false;
TypedData_Get_Struct(path_ary, scan_ctx, &selector_type, ctx);
if (!ctx->paths)
rb_raise(rb_eRuntimeError, "selector is not initialized");
}
else
{
Expand Down Expand Up @@ -955,13 +869,4 @@ Init_json_scanner(void)
string_sym = rb_id2sym(rb_intern("string"));
object_sym = rb_id2sym(rb_intern("object"));
array_sym = rb_id2sym(rb_intern("array"));
scan_kwargs_table[0] = rb_intern("with_path");
scan_kwargs_table[1] = rb_intern("verbose_error");
scan_kwargs_table[2] = rb_intern("allow_comments");
scan_kwargs_table[3] = rb_intern("dont_validate_strings");
scan_kwargs_table[4] = rb_intern("allow_trailing_garbage");
scan_kwargs_table[5] = rb_intern("allow_multiple_values");
scan_kwargs_table[6] = rb_intern("allow_partial_values");
scan_kwargs_table[7] = rb_intern("symbolize_path_keys");
scan_kwargs_table[8] = rb_intern("with_roots_info");
}
125 changes: 88 additions & 37 deletions ext/json_scanner/options.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,47 +13,47 @@ typedef struct
int symbolize_path_keys;
int with_roots_info;
} scan_options;

#define SCAN_OPTION_VALUE_MASK 1
#define SCAN_OPTION_SET_MASK (1 << 1)
#define SCAN_OPTION(options, field) ((options)->field & SCAN_OPTION_VALUE_MASK)
#define SCAN_OPTION_IS_SET(options, field) ((options)->field & SCAN_OPTION_SET_MASK)
#define SCAN_OPTION_SET(options, field, value) ((options)->field = ((value) & SCAN_OPTION_VALUE_MASK) | SCAN_OPTION_SET_MASK)
#define SCAN_OPTION_FALSE(options, field) \
(!SCAN_OPTION(options, field) && ((options)->field & SCAN_OPTION_SET_MASK))
#define OPTIONS_FIELDS(X) \
X(with_path) \
X(verbose_error) \
X(allow_comments) \
X(dont_validate_strings) \
X(allow_trailing_garbage) \
X(allow_multiple_values) \
X(allow_partial_values) \
X(symbolize_path_keys) \
X(with_roots_info)

static VALUE rb_cJsonScannerOptions;
enum
{
#define SCAN_KWARG_ENUM(field) SCAN_KWARG_##field,
OPTIONS_FIELDS(SCAN_KWARG_ENUM)
#undef SCAN_KWARG_ENUM
SCAN_KWARGS_SIZE,
};
static ID scan_kwargs_table[SCAN_KWARGS_SIZE];

static void scan_options_init(scan_options *options, VALUE kwargs)
{
options->with_path = 0;
options->verbose_error = 0;
options->allow_comments = 0;
options->dont_validate_strings = 0;
options->allow_trailing_garbage = 0;
options->allow_multiple_values = 0;
options->allow_partial_values = 0;
options->symbolize_path_keys = 0;
options->with_roots_info = 0;
memset(options, 0, sizeof(*options));
if (kwargs != Qnil)
{
VALUE kwargs_values[SCAN_KWARGS_SIZE];
rb_get_kwargs(kwargs, scan_kwargs_table, 0, SCAN_KWARGS_SIZE, kwargs_values);
if (kwargs_values[0] != Qundef)
SCAN_OPTION_SET(options, with_path, RTEST(kwargs_values[0]));
if (kwargs_values[1] != Qundef)
SCAN_OPTION_SET(options, verbose_error, RTEST(kwargs_values[1]));
if (kwargs_values[2] != Qundef)
SCAN_OPTION_SET(options, allow_comments, RTEST(kwargs_values[2]));
if (kwargs_values[3] != Qundef)
SCAN_OPTION_SET(options, dont_validate_strings, RTEST(kwargs_values[3]));
if (kwargs_values[4] != Qundef)
SCAN_OPTION_SET(options, allow_trailing_garbage, RTEST(kwargs_values[4]));
if (kwargs_values[5] != Qundef)
SCAN_OPTION_SET(options, allow_multiple_values, RTEST(kwargs_values[5]));
if (kwargs_values[6] != Qundef)
SCAN_OPTION_SET(options, allow_partial_values, RTEST(kwargs_values[6]));
if (kwargs_values[7] != Qundef)
SCAN_OPTION_SET(options, symbolize_path_keys, RTEST(kwargs_values[7]));
if (kwargs_values[8] != Qundef)
SCAN_OPTION_SET(options, with_roots_info, RTEST(kwargs_values[8]));
#define SCAN_OPTION_SET_KWARG(field) \
if (kwargs_values[SCAN_KWARG_##field] != Qundef) \
SCAN_OPTION_SET(options, field, RTEST(kwargs_values[SCAN_KWARG_##field]));
OPTIONS_FIELDS(SCAN_OPTION_SET_KWARG)
#undef SCAN_OPTION_SET_KWARG
}
}

Expand All @@ -68,12 +68,17 @@ static const rb_data_type_t options_type = {
.dfree = RUBY_DEFAULT_FREE,
.dsize = options_size,
},
.flags = RUBY_TYPED_FREE_IMMEDIATELY,
.flags = RUBY_TYPED_FREE_IMMEDIATELY
#ifdef RUBY_TYPED_FROZEN_SHAREABLE
| RUBY_TYPED_FROZEN_SHAREABLE
#endif
,
};

static VALUE options_alloc(VALUE self)
{
scan_options *options;
// TypedData_Make_Struct zeroes options.
return TypedData_Make_Struct(self, scan_options, &options_type, options);
}

Expand All @@ -91,6 +96,50 @@ static VALUE options_m_initialize(int argc, VALUE *argv, VALUE self)
return self;
}

static VALUE options_m_initialize_copy(VALUE self, VALUE other)
{
scan_options *options, *other_options;
rb_call_super(1, &other);
TypedData_Get_Struct(self, scan_options, &options_type, options);
TypedData_Get_Struct(other, scan_options, &options_type, other_options);
*options = *other_options;
return self;
}

#define DEFINE_OPTIONS_ACCESSORS(field) \
static VALUE options_m_##field(VALUE self) \
{ \
scan_options *options; \
TypedData_Get_Struct(self, scan_options, &options_type, options); \
if (!SCAN_OPTION_IS_SET(options, field)) \
return Qnil; \
return SCAN_OPTION(options, field) ? Qtrue : Qfalse; \
} \
static VALUE options_m_##field##_set(VALUE self, VALUE value) \
{ \
scan_options *options; \
rb_check_frozen(self); \
TypedData_Get_Struct(self, scan_options, &options_type, options); \
SCAN_OPTION_SET(options, field, RTEST(value)); \
return value; \
}
OPTIONS_FIELDS(DEFINE_OPTIONS_ACCESSORS)

static VALUE options_m_equal(VALUE self, VALUE other)
{
scan_options *options, *other_options;
if (!rb_obj_is_kind_of(other, rb_cJsonScannerOptions))
return Qfalse;
TypedData_Get_Struct(self, scan_options, &options_type, options);
TypedData_Get_Struct(other, scan_options, &options_type, other_options);
#define OPTIONS_EQUAL(field) \
if (options->field != other_options->field) \
return Qfalse;
OPTIONS_FIELDS(OPTIONS_EQUAL)
#undef OPTIONS_EQUAL
return Qtrue;
}

static VALUE options_m_inspect(VALUE self)
{
VALUE res;
Expand All @@ -100,15 +149,7 @@ static VALUE options_m_inspect(VALUE self)
#define APPEND_OPTION_INSPECT(field) \
if (SCAN_OPTION_IS_SET(options, field)) \
rb_str_catf(res, #field ": %s, ", SCAN_OPTION(options, field) ? "true" : "false");
APPEND_OPTION_INSPECT(with_path)
APPEND_OPTION_INSPECT(verbose_error)
APPEND_OPTION_INSPECT(allow_comments)
APPEND_OPTION_INSPECT(dont_validate_strings)
APPEND_OPTION_INSPECT(allow_trailing_garbage)
APPEND_OPTION_INSPECT(allow_multiple_values)
APPEND_OPTION_INSPECT(allow_partial_values)
APPEND_OPTION_INSPECT(symbolize_path_keys)
APPEND_OPTION_INSPECT(with_roots_info)
OPTIONS_FIELDS(APPEND_OPTION_INSPECT)
#undef APPEND_OPTION_INSPECT
if (RSTRING_END(res)[-1] == ' ')
rb_str_resize(res, RSTRING_LEN(res) - 2);
Expand All @@ -121,7 +162,17 @@ static void init_options(VALUE json_scanner_module)
rb_cJsonScannerOptions = rb_define_class_under(json_scanner_module, "Options", rb_cObject);
rb_define_alloc_func(rb_cJsonScannerOptions, options_alloc);
rb_define_method(rb_cJsonScannerOptions, "initialize", options_m_initialize, -1);
rb_define_method(rb_cJsonScannerOptions, "initialize_copy", options_m_initialize_copy, 1);
#define DEFINE_OPTIONS_ACCESSOR_METHODS(field) \
rb_define_method(rb_cJsonScannerOptions, #field, options_m_##field, 0); \
rb_define_method(rb_cJsonScannerOptions, #field "=", options_m_##field##_set, 1);
OPTIONS_FIELDS(DEFINE_OPTIONS_ACCESSOR_METHODS)
#undef DEFINE_OPTIONS_ACCESSOR_METHODS
rb_define_method(rb_cJsonScannerOptions, "==", options_m_equal, 1);
rb_define_method(rb_cJsonScannerOptions, "inspect", options_m_inspect, 0);
#define INIT_SCAN_KWARG(field) scan_kwargs_table[SCAN_KWARG_##field] = rb_intern(#field);
OPTIONS_FIELDS(INIT_SCAN_KWARG)
#undef INIT_SCAN_KWARG
}

#endif /* JSON_SCANNER_OPTIONS_H */
Loading
Loading