void mpack_writer_init_error(mpack_writer_t* writer, mpack_error_t error) { mpack_writer_clear(writer); writer->error = error; mpack_log("===========================\n"); mpack_log("initializing writer in error state %i\n", (int)error); }
static void mpack_reader_skip_using_fill(mpack_reader_t* reader, size_t count) { mpack_assert(reader->fill != NULL, "missing fill function!"); mpack_assert(reader->left == 0, "there are bytes left in the buffer!"); mpack_assert(reader->error == mpack_ok, "should not have called this in an error state (%i)", reader->error); mpack_log("skip using fill for %i bytes\n", (int)count); // fill and discard multiples of the buffer size while (count > reader->size) { mpack_log("filling and discarding buffer of %i bytes\n", (int)reader->size); if (mpack_fill(reader, reader->buffer, reader->size) < reader->size) { mpack_reader_flag_error(reader, mpack_error_io); return; } count -= reader->size; } // fill the buffer as much as possible reader->pos = 0; reader->left = mpack_fill(reader, reader->buffer, reader->size); if (reader->left < count) { mpack_reader_flag_error(reader, mpack_error_io); return; } mpack_log("filled %i bytes into buffer; discarding %i bytes\n", (int)reader->left, (int)count); reader->pos += count; reader->left -= count; }
void mpack_reader_init_error(mpack_reader_t* reader, mpack_error_t error) { mpack_memset(reader, 0, sizeof(*reader)); reader->error = error; mpack_log("===========================\n"); mpack_log("initializing reader error state %i\n", (int)error); }
static void mpack_growable_writer_flush(mpack_writer_t* writer, const char* data, size_t count) { // This is an intrusive flush function which modifies the writer's buffer // in response to a flush instead of emptying it in order to add more // capacity for data. This removes the need to copy data from a fixed buffer // into a growable one, improving performance. // // There are three ways flush can be called: // - flushing the buffer during writing (used is zero, count is all data, data is buffer) // - flushing extra data during writing (used is all flushed data, count is extra data, data is not buffer) // - flushing during teardown (used and count are both all flushed data, data is buffer) // // In the first two cases, we grow the buffer by at least double, enough // to ensure that new data will fit. We ignore the teardown flush. if (data == writer->buffer) { // teardown, do nothing if (writer->used == count) return; // otherwise leave the data in the buffer and just grow writer->used = count; count = 0; } mpack_log("flush size %i used %i data %p buffer %p\n", (int)count, (int)writer->used, data, writer->buffer); mpack_assert(data == writer->buffer || writer->used + count > writer->size, "extra flush for %i but there is %i space left in the buffer! (%i/%i)", (int)count, (int)writer->size - (int)writer->used, (int)writer->used, (int)writer->size); // grow to fit the data // TODO: this really needs to correctly test for overflow size_t new_size = writer->size * 2; while (new_size < writer->used + count) new_size *= 2; mpack_log("flush growing buffer size from %i to %i\n", (int)writer->size, (int)new_size); // grow the buffer char* new_buffer = (char*)mpack_realloc(writer->buffer, writer->used, new_size); if (new_buffer == NULL) { mpack_writer_flag_error(writer, mpack_error_memory); return; } writer->buffer = new_buffer; writer->size = new_size; // append the extra data if (count > 0) { mpack_memcpy(writer->buffer + writer->used, data, count); writer->used += count; } mpack_log("new buffer %p, used %i\n", new_buffer, (int)writer->used); }
void mpack_writer_init(mpack_writer_t* writer, char* buffer, size_t size) { mpack_assert(buffer != NULL, "cannot initialize writer with empty buffer"); mpack_writer_clear(writer); writer->buffer = buffer; writer->size = size; #if MPACK_WRITE_TRACKING mpack_writer_flag_if_error(writer, mpack_track_init(&writer->track)); #endif mpack_log("===========================\n"); mpack_log("initializing writer with buffer size %i\n", (int)size); }
void mpack_reader_init(mpack_reader_t* reader, char* buffer, size_t size, size_t count) { mpack_assert(buffer != NULL, "buffer is NULL"); mpack_memset(reader, 0, sizeof(*reader)); reader->buffer = buffer; reader->size = size; reader->left = count; #if MPACK_READ_TRACKING mpack_reader_flag_if_error(reader, mpack_track_init(&reader->track)); #endif mpack_log("===========================\n"); mpack_log("initializing reader with buffer size %i\n", (int)size); }
// Ensures there are at least count bytes free in the buffer. This // will flag an error if the flush function fails to make enough // room in the buffer. static bool mpack_writer_ensure(mpack_writer_t* writer, size_t count) { mpack_assert(count != 0, "cannot ensure zero bytes!"); mpack_assert(count <= MPACK_WRITER_MINIMUM_BUFFER_SIZE, "cannot ensure %i bytes, this is more than the minimum buffer size %i!", (int)count, (int)MPACK_WRITER_MINIMUM_BUFFER_SIZE); mpack_assert(count > mpack_writer_buffer_left(writer), "request to ensure %i bytes but there are already %i left in the buffer!", (int)count, (int)mpack_writer_buffer_left(writer)); mpack_log("ensuring %i bytes, %i left\n", (int)count, (int)mpack_writer_buffer_left(writer)); if (mpack_writer_error(writer) != mpack_ok) return false; if (writer->flush == NULL) { mpack_writer_flag_error(writer, mpack_error_too_big); return false; } mpack_writer_flush_unchecked(writer); if (mpack_writer_error(writer) != mpack_ok) return false; if (mpack_writer_buffer_left(writer) >= count) return true; mpack_writer_flag_error(writer, mpack_error_io); return false; }
mpack_error_t mpack_track_pop(mpack_track_t* track, mpack_type_t type) { mpack_assert(track->elements, "null track elements!"); mpack_log("track popping %s\n", mpack_type_to_string(type)); if (track->count == 0) { mpack_break("attempting to close a %s but nothing was opened!", mpack_type_to_string(type)); return mpack_error_bug; } mpack_track_element_t* element = &track->elements[track->count - 1]; if (element->type != type) { mpack_break("attempting to close a %s but the open element is a %s!", mpack_type_to_string(type), mpack_type_to_string(element->type)); return mpack_error_bug; } if (element->left != 0) { mpack_break("attempting to close a %s but there are %" PRIu64 " %s left", mpack_type_to_string(type), element->left, (type == mpack_type_map || type == mpack_type_array) ? "elements" : "bytes"); return mpack_error_bug; } --track->count; return mpack_ok; }
void mpack_writer_flag_error(mpack_writer_t* writer, mpack_error_t error) { mpack_log("writer %p setting error %i: %s\n", writer, (int)error, mpack_error_to_string(error)); if (writer->error == mpack_ok) { writer->error = error; if (writer->error_fn) writer->error_fn(writer, writer->error); } }
void mpack_skip_bytes(mpack_reader_t* reader, size_t count) { if (mpack_reader_error(reader) != mpack_ok) return; mpack_log("skip requested for %i bytes\n", (int)count); mpack_reader_track_bytes(reader, count); // check if we have enough in the buffer already if (reader->left >= count) { mpack_log("skipping %i bytes still in buffer\n", (int)count); reader->left -= count; reader->pos += count; return; } // we'll need at least a fill function to skip more data. if there's // no fill function, the buffer should contain an entire MessagePack // object, so we raise mpack_error_invalid instead of mpack_error_io // on truncated data. (see mpack_read_native_big()) if (reader->fill == NULL) { mpack_log("reader has no fill function!\n"); mpack_reader_flag_error(reader, mpack_error_invalid); return; } // discard whatever's left in the buffer mpack_log("discarding %i bytes still in buffer\n", (int)reader->left); count -= reader->left; reader->pos += reader->left; reader->left = 0; #if !MPACK_OPTIMIZE_FOR_SIZE // use the skip function if we've got one, and if we're trying // to skip a lot of data. if we only need to skip some tiny // fraction of the buffer size, it's probably better to just // fill the buffer and skip from it instead of trying to seek. if (reader->skip && count > reader->size / 16) { mpack_log("calling skip function for %i bytes\n", (int)count); reader->skip(reader, count); return; } #endif mpack_reader_skip_using_fill(reader, count); }
void mpack_reader_flag_error(mpack_reader_t* reader, mpack_error_t error) { mpack_log("reader %p setting error %i: %s\n", reader, (int)error, mpack_error_to_string(error)); if (reader->error == mpack_ok) { reader->error = error; reader->left = 0; if (reader->error_fn) reader->error_fn(reader, error); } }
static void mpack_file_reader_skip(mpack_reader_t* reader, size_t count) { if (mpack_reader_error(reader) != mpack_ok) return; FILE* file = (FILE*)reader->context; // We call ftell() to test whether the stream is seekable // without causing a file error. if (ftell(file) >= 0) { mpack_log("seeking forward %i bytes\n", (int)count); if (fseek(file, (long int)count, SEEK_CUR) == 0) return; mpack_log("fseek() didn't return zero!\n"); if (ferror(file)) { mpack_reader_flag_error(reader, mpack_error_io); return; } } // If the stream is not seekable, fall back to the fill function. mpack_reader_skip_using_fill(reader, count); }
void mpack_reader_init_data(mpack_reader_t* reader, const char* data, size_t count) { mpack_assert(data != NULL, "data is NULL"); mpack_memset(reader, 0, sizeof(*reader)); reader->left = count; // unfortunately we have to cast away the const to store the buffer, // but we won't be modifying it because there's no fill function. // the buffer size is left at 0 to ensure no fill function can be // set or used (see mpack_reader_set_fill().) #ifdef __cplusplus reader->buffer = const_cast<char*>(data); #else reader->buffer = (char*)data; #endif #if MPACK_READ_TRACKING mpack_reader_flag_if_error(reader, mpack_track_init(&reader->track)); #endif mpack_log("===========================\n"); mpack_log("initializing reader with data size %i\n", (int)count); }
mpack_tag_t mpack_peek_tag(mpack_reader_t* reader) { mpack_log("peeking tag\n"); // make sure we can peek a tag if (mpack_reader_error(reader) != mpack_ok) return mpack_tag_nil(); if (mpack_reader_track_peek_element(reader) != mpack_ok) return mpack_tag_nil(); mpack_tag_t tag; mpack_memset(&tag, 0, sizeof(tag)); if (mpack_parse_tag(reader, &tag) == 0) return mpack_tag_nil(); return tag; }
mpack_tag_t mpack_read_tag(mpack_reader_t* reader) { mpack_log("reading tag\n"); // make sure we can read a tag if (mpack_reader_error(reader) != mpack_ok) return mpack_tag_nil(); if (mpack_reader_track_element(reader) != mpack_ok) return mpack_tag_nil(); mpack_tag_t tag; mpack_memset(&tag, 0, sizeof(tag)); size_t count = mpack_parse_tag(reader, &tag); if (count == 0) return mpack_tag_nil(); #if MPACK_READ_TRACKING mpack_error_t track_error = mpack_ok; switch (tag.type) { case mpack_type_map: case mpack_type_array: track_error = mpack_track_push(&reader->track, tag.type, tag.v.l); break; case mpack_type_str: case mpack_type_bin: case mpack_type_ext: track_error = mpack_track_push(&reader->track, tag.type, tag.v.n); break; default: break; } if (track_error != mpack_ok) { mpack_reader_flag_error(reader, track_error); return mpack_tag_nil(); } #endif // the tag is guaranteed to have been read out of // the buffer, so we advance past it reader->pos += count; reader->left -= count; return tag; }
mpack_error_t mpack_track_push(mpack_track_t* track, mpack_type_t type, uint64_t count) { mpack_assert(track->elements, "null track elements!"); mpack_log("track pushing %s count %i\n", mpack_type_to_string(type), (int)count); // maps have twice the number of elements (key/value pairs) if (type == mpack_type_map) count *= 2; // grow if needed if (track->count == track->capacity) { mpack_error_t error = mpack_track_grow(track); if (error != mpack_ok) return error; } // insert new track track->elements[track->count].type = type; track->elements[track->count].left = count; ++track->count; return mpack_ok; }
// Writes encoded bytes to the buffer when we already know the data // does not fit in the buffer (i.e. it straddles the edge of the // buffer.) If there is a flush function, it is guaranteed to be // called; otherwise mpack_error_too_big is raised. static void mpack_write_native_straddle(mpack_writer_t* writer, const char* p, size_t count) { mpack_assert(count == 0 || p != NULL, "data pointer for %i bytes is NULL", (int)count); if (mpack_writer_error(writer) != mpack_ok) return; mpack_log("big write for %i bytes from %p, %i space left in buffer\n", (int)count, p, (int)(writer->size - writer->used)); mpack_assert(count > writer->size - writer->used, "big write requested for %i bytes, but there is %i available " "space in buffer. should have called mpack_write_native() instead", (int)count, (int)(writer->size - writer->used)); // we'll need a flush function if (!writer->flush) { mpack_writer_flag_error(writer, mpack_error_too_big); return; } // flush the buffer mpack_writer_flush_unchecked(writer); if (mpack_writer_error(writer) != mpack_ok) return; // note that an intrusive flush function (such as mpack_growable_writer_flush()) // may have changed size and/or reset used to a non-zero value. we treat both as // though they may have changed, and there may still be data in the buffer. // flush the extra data directly if it doesn't fit in the buffer if (count > writer->size - writer->used) { writer->flush(writer, p, count); if (mpack_writer_error(writer) != mpack_ok) return; } else { mpack_memcpy(writer->buffer + writer->used, p, count); writer->used += count; } }
// Reads count bytes into p. Used when there are not enough bytes // left in the buffer to satisfy a read. void mpack_read_native_big(mpack_reader_t* reader, char* p, size_t count) { mpack_assert(count == 0 || p != NULL, "data pointer for %i bytes is NULL", (int)count); if (mpack_reader_error(reader) != mpack_ok) { mpack_memset(p, 0, count); return; } mpack_log("big read for %i bytes into %p, %i left in buffer, buffer size %i\n", (int)count, p, (int)reader->left, (int)reader->size); if (count <= reader->left) { mpack_assert(0, "big read requested for %i bytes, but there are %i bytes " "left in buffer. call mpack_read_native() instead", (int)count, (int)reader->left); mpack_reader_flag_error(reader, mpack_error_bug); mpack_memset(p, 0, count); return; } // we'll need a fill function to get more data. if there's no // fill function, the buffer should contain an entire MessagePack // object, so we raise mpack_error_invalid instead of mpack_error_io // on truncated data. if (reader->fill == NULL) { mpack_reader_flag_error(reader, mpack_error_invalid); mpack_memset(p, 0, count); return; } if (reader->size == 0) { // somewhat debatable what error should be returned here. when // initializing a reader with an in-memory buffer it's not // necessarily a bug if the data is blank; it might just have // been truncated to zero. for this reason we return the same // error as if the data was truncated. mpack_reader_flag_error(reader, mpack_error_io); mpack_memset(p, 0, count); return; } // flush what's left of the buffer if (reader->left > 0) { mpack_log("flushing %i bytes remaining in buffer\n", (int)reader->left); mpack_memcpy(p, reader->buffer + reader->pos, reader->left); count -= reader->left; p += reader->left; reader->pos += reader->left; reader->left = 0; } // we read only in multiples of the buffer size. read the middle portion, if any size_t middle = count - (count % reader->size); if (middle > 0) { mpack_log("reading %i bytes in middle\n", (int)middle); if (mpack_fill(reader, p, middle) < middle) { mpack_reader_flag_error(reader, mpack_error_io); mpack_memset(p, 0, count); return; } count -= middle; p += middle; if (count == 0) return; } // fill the buffer reader->pos = 0; reader->left = mpack_fill(reader, reader->buffer, reader->size); mpack_log("filled %i bytes into buffer\n", (int)reader->left); if (reader->left < count) { mpack_reader_flag_error(reader, mpack_error_io); mpack_memset(p, 0, count); return; } // serve the remainder mpack_log("serving %i remaining bytes from %p to %p\n", (int)count, reader->buffer+reader->pos,p); mpack_memcpy(p, reader->buffer + reader->pos, count); reader->pos += count; reader->left -= count; }