/* * Document-method: parse * * call-seq: * parse(input, buffer_size=8092) * parse(input, buffer_size=8092) { |obj| ... } * * +input+ can either be a string or an IO to parse JSON from * * +buffer_size+ is the size of chunk that will be parsed off the input (if it's an IO) for each loop of the parsing process. * 8092 is a good balance between the different types of streams (off disk, off a socket, etc...), but this option * is here so the caller can better tune their parsing depending on the type of stream being passed. * A larger read buffer will perform better for files off disk, where as a smaller size may be more efficient for * reading off of a socket directly. * * If a block was passed, it's called when an object has been parsed off the stream. This is especially * usefull when parsing a stream of multiple JSON objects. * * NOTE: you can optionally assign the +on_parse_complete+ callback, and it will be called the same way the optional * block is for this method. */ static VALUE rb_yajl_parser_parse(int argc, VALUE * argv, VALUE self) { yajl_status stat; yajl_parser_wrapper * wrapper; VALUE rbufsize, input, blk; unsigned int len; const char * cptr; GetParser(self, wrapper); /* setup our parameters */ rb_scan_args(argc, argv, "11&", &input, &rbufsize, &blk); if (NIL_P(rbufsize)) { rbufsize = INT2FIX(READ_BUFSIZE); } else { Check_Type(rbufsize, T_FIXNUM); } if (!NIL_P(blk)) { rb_yajl_parser_set_complete_cb(self, blk); } if (TYPE(input) == T_STRING) { cptr = RSTRING_PTR(input); len = RSTRING_LEN(input); yajl_parse_chunk((const unsigned char*)cptr, len, wrapper->parser); } else if (rb_respond_to(input, intern_io_read)) { VALUE parsed = rb_str_new(0, FIX2LONG(rbufsize)); while (rb_funcall(input, intern_io_read, 2, rbufsize, parsed) != Qnil) { cptr = RSTRING_PTR(parsed); len = RSTRING_LEN(parsed); yajl_parse_chunk((const unsigned char*)cptr, len, wrapper->parser); } } else { rb_raise(cParseError, "input must be a string or IO"); } /* parse any remaining buffered data */ stat = yajl_parse_complete(wrapper->parser); if (wrapper->parse_complete_callback != Qnil) { yajl_check_and_fire_callback((void *)self); return Qnil; } // Because we've overridden the yajl_parse_complete state of the parser to allow // us to parse multiple objects out of one stream; we can't use it to determine // whether we've parsed a single object. Instead we'll check whether we've successfully // parsed a single token. if (!RARRAY_LEN(wrapper->builderStack) || wrapper->nestedHashLevel || wrapper->nestedArrayLevel) { rb_raise(cParseError, "unexpected end of JSON string"); } return rb_ary_pop(wrapper->builderStack); }
/* * Document-method: parse * * call-seq: * parse(input, buffer_size=8092) * parse(input, buffer_size=8092) { |obj| ... } * * +input+ can either be a string or an IO to parse JSON from * * +buffer_size+ is the size of chunk that will be parsed off the input (if it's an IO) for each loop of the parsing process. * 8092 is a good balance between the different types of streams (off disk, off a socket, etc...), but this option * is here so the caller can better tune their parsing depending on the type of stream being passed. * A larger read buffer will perform better for files off disk, where as a smaller size may be more efficient for * reading off of a socket directly. * * If a block was passed, it's called when an object has been parsed off the stream. This is especially * usefull when parsing a stream of multiple JSON objects. * * NOTE: you can optionally assign the +on_parse_complete+ callback, and it will be called the same way the optional * block is for this method. */ static VALUE rb_yajl_parser_parse(int argc, VALUE * argv, VALUE self) { yajl_status stat; yajl_parser_wrapper * wrapper; VALUE rbufsize, input, blk; unsigned int len; const char * cptr; GetParser(self, wrapper); /* setup our parameters */ rb_scan_args(argc, argv, "11&", &input, &rbufsize, &blk); if (NIL_P(rbufsize)) { rbufsize = INT2FIX(READ_BUFSIZE); } else { Check_Type(rbufsize, T_FIXNUM); } if (!NIL_P(blk)) { rb_yajl_parser_set_complete_cb(self, blk); } if (TYPE(input) == T_STRING) { cptr = RSTRING_PTR(input); len = RSTRING_LEN(input); yajl_parse_chunk((const unsigned char*)cptr, len, wrapper->parser); } else if (rb_respond_to(input, intern_io_read)) { VALUE parsed = rb_str_new(0, FIX2LONG(rbufsize)); while (rb_funcall(input, intern_io_read, 2, rbufsize, parsed) != Qnil) { cptr = RSTRING_PTR(parsed); len = RSTRING_LEN(parsed); yajl_parse_chunk((const unsigned char*)cptr, len, wrapper->parser); } } else { rb_raise(cParseError, "input must be a string or IO"); } /* parse any remaining buffered data */ stat = yajl_parse_complete(wrapper->parser); if (wrapper->parse_complete_callback != Qnil) { yajl_check_and_fire_callback((void *)self); return Qnil; } return rb_ary_pop(wrapper->builderStack); }
/* * Document-method: parse_chunk * * call-seq: parse_chunk(string_chunk) * * +string_chunk+ can be a partial or full JSON string to push on the parser. * * This method will throw an exception if the +on_parse_complete+ callback hasn't been assigned yet. * The +on_parse_complete+ callback assignment is required so the user can handle objects that have been * parsed off the stream as they're found. */ static VALUE rb_yajl_parser_parse_chunk(VALUE self, VALUE chunk) { yajl_parser_wrapper * wrapper; unsigned int len; GetParser(self, wrapper); if (NIL_P(chunk)) { rb_raise(cParseError, "Can't parse a nil string."); } if (wrapper->parse_complete_callback != Qnil) { const char * cptr = RSTRING_PTR(chunk); len = RSTRING_LEN(chunk); yajl_parse_chunk((const unsigned char*)cptr, len, wrapper->parser); } else { rb_raise(cParseError, "The on_parse_complete callback isn't setup, parsing useless."); } return Qnil; }