// Copyright (c) 2011 Peter Ohler. All rights reserved. // Licensed under the MIT License. See LICENSE file in the project root for license details. #include #include #include #include #include #ifdef NEEDS_UIO #if NEEDS_UIO #include #endif #endif #include #include #include "mem.h" #include "oj.h" #include "reader.h" #include "ruby.h" #define BUF_PAD 4 static VALUE rescue_cb(VALUE rdr, VALUE err); static VALUE io_cb(VALUE rdr); static VALUE partial_io_cb(VALUE rdr); static int read_from_io(Reader reader); static int read_from_fd(Reader reader); static int read_from_io_partial(Reader reader); // static int read_from_str(Reader reader); void oj_reader_init(Reader reader, VALUE io, int fd, bool to_s) { VALUE io_class = rb_obj_class(io); VALUE stat; VALUE ftype; reader->head = reader->base; *((char *)reader->head) = '\0'; reader->end = reader->head + sizeof(reader->base) - BUF_PAD; reader->tail = reader->head; reader->read_end = reader->head; reader->pro = 0; reader->str = 0; reader->pos = 0; reader->line = 1; reader->col = 0; reader->free_head = 0; if (0 != fd) { reader->read_func = read_from_fd; reader->fd = fd; } else if (rb_cString == io_class) { reader->read_func = 0; reader->in_str = StringValuePtr(io); reader->head = (char *)reader->in_str; reader->tail = reader->head; reader->read_end = reader->head + RSTRING_LEN(io); } else if (oj_stringio_class == io_class) { VALUE s = rb_funcall2(io, oj_string_id, 0, 0); reader->read_func = 0; reader->in_str = StringValuePtr(s); reader->head = (char *)reader->in_str; reader->tail = reader->head; reader->read_end = reader->head + RSTRING_LEN(s); } else if (rb_cFile == io_class && Qnil != (stat = rb_funcall(io, oj_stat_id, 0)) && Qnil != (ftype = rb_funcall(stat, oj_ftype_id, 0)) && 0 == strcmp("file", StringValuePtr(ftype)) && 0 == FIX2INT(rb_funcall(io, oj_pos_id, 0))) { reader->read_func = read_from_fd; reader->fd = FIX2INT(rb_funcall(io, oj_fileno_id, 0)); } else if (rb_respond_to(io, oj_readpartial_id)) { reader->read_func = read_from_io_partial; reader->io = io; } else if (rb_respond_to(io, oj_read_id)) { reader->read_func = read_from_io; reader->io = io; } else if (to_s) { volatile VALUE rstr = oj_safe_string_convert(io); reader->read_func = 0; reader->in_str = StringValuePtr(rstr); reader->head = (char *)reader->in_str; reader->tail = reader->head; reader->read_end = reader->head + RSTRING_LEN(rstr); } else { rb_raise(rb_eArgError, "parser io argument must be a String or respond to readpartial() or read().\n"); } } int oj_reader_read(Reader reader) { int err; size_t shift = 0; if (0 == reader->read_func) { return -1; } // if there is not much room to read into, shift or realloc a larger buffer. if (reader->head < reader->tail && 4096 > reader->end - reader->tail) { if (0 == reader->pro) { shift = reader->tail - reader->head; } else { shift = reader->pro - reader->head - 1; // leave one character so we can backup one } if (0 >= shift) { /* no space left so allocate more */ const char *old = reader->head; size_t size = reader->end - reader->head + BUF_PAD; if (reader->head == reader->base) { reader->head = OJ_R_ALLOC_N(char, size * 2); memcpy((char *)reader->head, old, size); } else { OJ_R_REALLOC_N(reader->head, char, size * 2); } reader->free_head = 1; reader->end = reader->head + size * 2 - BUF_PAD; reader->tail = reader->head + (reader->tail - old); reader->read_end = reader->head + (reader->read_end - old); if (0 != reader->pro) { reader->pro = reader->head + (reader->pro - old); } if (0 != reader->str) { reader->str = reader->head + (reader->str - old); } } else { memmove((char *)reader->head, reader->head + shift, reader->read_end - (reader->head + shift)); reader->tail -= shift; reader->read_end -= shift; if (0 != reader->pro) { reader->pro -= shift; } if (0 != reader->str) { reader->str -= shift; } } } err = reader->read_func(reader); *(char *)reader->read_end = '\0'; return err; } static VALUE rescue_cb(VALUE rbuf, VALUE err) { VALUE clas = rb_obj_class(err); if (rb_eTypeError != clas && rb_eEOFError != clas) { Reader reader = (Reader)rbuf; rb_raise(clas, "at line %d, column %d\n", reader->line, reader->col); } return Qfalse; } static VALUE partial_io_cb(VALUE rbuf) { Reader reader = (Reader)rbuf; VALUE args[1]; VALUE rstr; char *str; size_t cnt; args[0] = ULONG2NUM(reader->end - reader->tail); rstr = rb_funcall2(reader->io, oj_readpartial_id, 1, args); if (Qnil == rstr) { return Qfalse; } str = StringValuePtr(rstr); cnt = RSTRING_LEN(rstr); strcpy(reader->tail, str); reader->read_end = reader->tail + cnt; return Qtrue; } static VALUE io_cb(VALUE rbuf) { Reader reader = (Reader)rbuf; VALUE args[1]; VALUE rstr; char *str; size_t cnt; args[0] = ULONG2NUM(reader->end - reader->tail); rstr = rb_funcall2(reader->io, oj_read_id, 1, args); if (Qnil == rstr) { return Qfalse; } str = StringValuePtr(rstr); cnt = RSTRING_LEN(rstr); strcpy(reader->tail, str); reader->read_end = reader->tail + cnt; return Qtrue; } static int read_from_io_partial(Reader reader) { return (Qfalse == rb_rescue(partial_io_cb, (VALUE)reader, rescue_cb, (VALUE)reader)); } static int read_from_io(Reader reader) { return (Qfalse == rb_rescue(io_cb, (VALUE)reader, rescue_cb, (VALUE)reader)); } static int read_from_fd(Reader reader) { ssize_t cnt; size_t max = reader->end - reader->tail; cnt = read(reader->fd, reader->tail, max); if (cnt <= 0) { return -1; } else if (0 != cnt) { reader->read_end = reader->tail + cnt; } return 0; } // This is only called when the end of the string is reached so just return -1. /* static int read_from_str(Reader reader) { return -1; } */