Example #1
0
// internal inplace reader for when it straddles the end of the buffer
static const char* mpack_read_bytes_inplace_big(mpack_reader_t* reader, size_t count) {

    // we should only arrive here from inplace straddle; this should already be checked
    mpack_assert(mpack_reader_error(reader) == mpack_ok, "already in error state? %s",
            mpack_error_to_string(mpack_reader_error(reader)));
    mpack_assert(reader->left < count, "already enough bytes in buffer: %i left, %i count", (int)reader->left, (int)count);

    // 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);
        return NULL;
    }

    // make sure the buffer is big enough to actually fit the data
    if (count > reader->size) {
        mpack_reader_flag_error(reader, mpack_error_too_big);
        return NULL;
    }

    // re-fill as much as possible
    mpack_partial_fill(reader);

    if (reader->left < count) {
        mpack_reader_flag_error(reader, mpack_error_io);
        return NULL;
    }
    reader->pos += count;
    reader->left -= count;
    return reader->buffer;
}
Example #2
0
char* mpack_read_bytes_alloc_impl(mpack_reader_t* reader, size_t count, bool null_terminated) {

    // track the bytes first in case it jumps
    mpack_reader_track_bytes(reader, count);
    if (mpack_reader_error(reader) != mpack_ok)
        return NULL;

    // cannot allocate zero bytes. this is not an error.
    if (count == 0 && null_terminated == false)
        return NULL;

    // allocate data
    char* data = (char*)MPACK_MALLOC(count + (null_terminated ? 1 : 0)); // TODO: can this overflow?
    if (data == NULL) {
        mpack_reader_flag_error(reader, mpack_error_memory);
        return NULL;
    }

    // read with error callback disabled so we don't leak our buffer
    mpack_read_native_noerrorfn(reader, data, count);

    // report flagged errors
    if (mpack_reader_error(reader) != mpack_ok) {
        MPACK_FREE(data);
        if (reader->error_fn)
            reader->error_fn(reader, mpack_reader_error(reader));
        return NULL;
    }

    if (null_terminated)
        data[count] = '\0';
    return data;
}
Example #3
0
size_t mpack_expect_bin_buf(mpack_reader_t* reader, char* buf, size_t bufsize) {
    size_t binsize = mpack_expect_bin(reader);
    if (mpack_reader_error(reader))
        return 0;
    if (binsize > bufsize) {
        mpack_reader_flag_error(reader, mpack_error_too_big);
        return 0;
    }
    mpack_read_bytes(reader, buf, binsize);
    if (mpack_reader_error(reader))
        return 0;
    mpack_done_bin(reader);
    return binsize;
}
Example #4
0
void* mpack_expect_array_alloc_impl(mpack_reader_t* reader, size_t element_size, uint32_t max_count, uint32_t* out_count, bool allow_nil) {
    *out_count = 0;

    uint32_t count;
    bool has_array = true;
    if (allow_nil)
        has_array = mpack_expect_array_max_or_nil(reader, max_count, &count);
    else
        count = mpack_expect_array_max(reader, max_count);
    if (mpack_reader_error(reader))
        return NULL;

    // size 0 is not an error; we return NULL for no elements.
    if (count == 0) {
        // we call mpack_done_array() automatically ONLY if we are using
        // the _or_nil variant. this is the only way to allow nil and empty
        // to work the same way.
        if (allow_nil && has_array)
            mpack_done_array(reader);
        return NULL;
    }

    void* p = MPACK_MALLOC(element_size * count);
    if (p == NULL) {
        mpack_reader_flag_error(reader, mpack_error_memory);
        return NULL;
    }

    *out_count = count;
    return p;
}
Example #5
0
// Converts MessagePack bin/ext bytes to JSON base64 string
static bool base64(mpack_reader_t* reader, yajl_gen gen, options_t* options, uint32_t len, char* output, char* p, bool prefix) {
    if (prefix) {
        memcpy(p, b64_str, strlen(b64_str));
        p += strlen(b64_str);
    }

    base64_encodestate state;
    base64_init_encodestate(&state);

    while (len > 0) {
        char buf[4096];
        uint32_t count = (len < sizeof(buf)) ? len : sizeof(buf);
        len -= count;
        mpack_read_bytes(reader, buf, count);
        if (mpack_reader_error(reader) != mpack_ok) {
            fprintf(stderr, "%s: error reading base64 bytes\n", options->command);
            return false;
        }
        p += base64_encode_block(buf, (int)count, p, &state);
    }
    p += base64_encode_blockend(p, &state);

    bool ret = yajl_gen_string(gen, (const unsigned char*)output, p - output) == yajl_gen_status_ok;
    return ret;
}
Example #6
0
void mpack_read_utf8(mpack_reader_t* reader, char* p, size_t byte_count) {
    mpack_assert(p != NULL, "destination for read of %i bytes is NULL", (int)byte_count);
    mpack_reader_track_str_bytes_all(reader, byte_count);
    mpack_read_native(reader, p, byte_count);

    if (mpack_reader_error(reader) == mpack_ok && !mpack_utf8_check(p, byte_count))
        mpack_reader_flag_error(reader, mpack_error_type);
}
Example #7
0
size_t mpack_expect_str_buf(mpack_reader_t* reader, char* buf, size_t bufsize) {
    size_t length = mpack_expect_str(reader);
    if (mpack_reader_error(reader))
        return 0;

    if (length > bufsize) {
        mpack_reader_flag_error(reader, mpack_error_too_big);
        return 0;
    }

    mpack_read_bytes(reader, buf, length);
    if (mpack_reader_error(reader))
        return 0;

    mpack_done_str(reader);
    return length;
}
Example #8
0
void mpack_read_utf8_cstr(mpack_reader_t* reader, char* buf, size_t buffer_size, size_t byte_count) {
    mpack_read_cstr_unchecked(reader, buf, buffer_size, byte_count);

    // check encoding
    if (mpack_reader_error(reader) == mpack_ok && !mpack_utf8_check_no_null(buf, byte_count)) {
        buf[0] = 0;
        mpack_reader_flag_error(reader, mpack_error_type);
    }
}
Example #9
0
const char* mpack_read_utf8_inplace(mpack_reader_t* reader, size_t count) {
    mpack_reader_track_str_bytes_all(reader, count);
    const char* str = mpack_read_bytes_inplace_notrack(reader, count);

    if (mpack_reader_error(reader) == mpack_ok && !mpack_utf8_check(str, count)) {
        mpack_reader_flag_error(reader, mpack_error_type);
        return NULL;
    }

    return str;
}
Example #10
0
void mpack_discard(mpack_reader_t* reader) {
    mpack_tag_t var = mpack_read_tag(reader);
    if (mpack_reader_error(reader))
        return;
    switch (var.type) {
        case mpack_type_str:
            mpack_skip_bytes(reader, var.v.l);
            mpack_done_str(reader);
            break;
        case mpack_type_bin:
            mpack_skip_bytes(reader, var.v.l);
            mpack_done_bin(reader);
            break;
        case mpack_type_ext:
            mpack_skip_bytes(reader, var.v.l);
            mpack_done_ext(reader);
            break;
        case mpack_type_array: {
            for (; var.v.n > 0; --var.v.n) {
                mpack_discard(reader);
                if (mpack_reader_error(reader))
                    break;
            }
            mpack_done_array(reader);
            break;
        }
        case mpack_type_map: {
            for (; var.v.n > 0; --var.v.n) {
                mpack_discard(reader);
                mpack_discard(reader);
                if (mpack_reader_error(reader))
                    break;
            }
            mpack_done_map(reader);
            break;
        }
        default:
            break;
    }
}
Example #11
0
// read inplace without tracking (since there are different
// tracking modes for different inplace readers)
static const char* mpack_read_bytes_inplace_notrack(mpack_reader_t* reader, size_t count) {
    if (mpack_reader_error(reader) != mpack_ok)
        return NULL;

    // if we have enough bytes already in the buffer, we can return it directly.
    if (reader->left >= count) {
        reader->pos += count;
        reader->left -= count;
        return reader->buffer + reader->pos - count;
    }

    return mpack_read_bytes_inplace_big(reader, count);
}
Example #12
0
size_t mpack_reader_remaining(mpack_reader_t* reader, const char** data) {
    if (mpack_reader_error(reader) != mpack_ok)
        return 0;

    #if MPACK_READ_TRACKING
    if (mpack_reader_flag_if_error(reader, mpack_track_check_empty(&reader->track)) != mpack_ok)
        return 0;
    #endif

    if (data)
        *data = reader->buffer + reader->pos;
    return reader->left;
}
Example #13
0
mpack_error_t mpack_reader_destroy(mpack_reader_t* reader) {

    // clean up tracking, asserting if we're not already in an error state
    #if MPACK_READ_TRACKING
    mpack_reader_flag_if_error(reader, mpack_track_destroy(&reader->track, mpack_reader_error(reader) != mpack_ok));
    #endif

    if (reader->teardown)
        reader->teardown(reader);
    reader->teardown = NULL;

    return reader->error;
}
Example #14
0
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;
}
Example #15
0
static size_t mpack_expect_cstr_unchecked(mpack_reader_t* reader, char* buf, size_t bufsize) {

    // make sure buffer makes sense
    mpack_assert(bufsize >= 1, "buffer size is zero; you must have room for at least a null-terminator");

    // expect a str
    size_t length = mpack_expect_str_buf(reader, buf, bufsize - 1);
    if (mpack_reader_error(reader)) {
        buf[0] = 0;
        return 0;
    }

    buf[length] = 0;
    return length;
}
Example #16
0
// Reads MessagePack string bytes and outputs a JSON string
static bool string(mpack_reader_t* reader, yajl_gen gen, options_t* options, uint32_t len) {
    char* str = (char*)malloc(len);

    mpack_read_bytes(reader, str, len);
    if (mpack_reader_error(reader) != mpack_ok) {
        fprintf(stderr, "%s: error reading string bytes\n", options->command);
        free(str);
        return false;
    }
    mpack_done_str(reader);

    yajl_gen_status status = yajl_gen_string(gen, (const unsigned char*)str, len);
    free(str);
    return status == yajl_gen_status_ok;
}
Example #17
0
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;
}
Example #18
0
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);
}
Example #19
0
static void mpack_read_cstr_unchecked(mpack_reader_t* reader, char* buf, size_t buffer_size, size_t byte_count) {
    mpack_assert(buf != NULL, "destination for read of %i bytes is NULL", (int)byte_count);
    mpack_assert(buffer_size >= 1, "buffer size is zero; you must have room for at least a null-terminator");

    if (mpack_reader_error(reader)) {
        buf[0] = 0;
        return;
    }

    if (byte_count > buffer_size - 1) {
        mpack_reader_flag_error(reader, mpack_error_too_big);
        buf[0] = 0;
        return;
    }

    mpack_reader_track_str_bytes_all(reader, byte_count);
    mpack_read_native(reader, buf, byte_count);
    buf[byte_count] = 0;
}
Example #20
0
void mpack_expect_str_match(mpack_reader_t* reader, const char* str, size_t len) {

    // expect a str the correct length
    if (len > UINT32_MAX)
        mpack_reader_flag_error(reader, mpack_error_type);
    mpack_expect_str_length(reader, (uint32_t)len);
    if (mpack_reader_error(reader))
        return;

    // check each byte
    for (size_t i = 0; i < len; ++i) {
        mpack_reader_track_bytes(reader, 1);
        if (mpack_read_native_u8(reader) != *str++) {
            mpack_reader_flag_error(reader, mpack_error_type);
            return;
        }
    }

    mpack_done_str(reader);
}
Example #21
0
void mpack_print_file(const char* data, size_t len, FILE* file) {
    mpack_assert(data != NULL, "data is NULL");
    mpack_assert(file != NULL, "file is NULL");

    mpack_reader_t reader;
    mpack_reader_init_data(&reader, data, len);

    int depth = 2;
    for (int i = 0; i < depth; ++i)
        fprintf(file, "    ");
    mpack_print_element(&reader, depth, file);
    putc('\n', file);

    size_t remaining = mpack_reader_remaining(&reader, NULL);

    if (mpack_reader_destroy(&reader) != mpack_ok)
        fprintf(file, "<mpack parsing error %s>\n", mpack_error_to_string(mpack_reader_error(&reader)));
    else if (remaining > 0)
        fprintf(file, "<%i extra bytes at end of mpack>\n", (int)remaining);
}
Example #22
0
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);
}
Example #23
0
void test_read_error_handler(mpack_reader_t* reader, mpack_error_t error) {
    TEST_TRUE(test_read_error == mpack_ok, "error handler was called multiple times");
    TEST_TRUE(error != mpack_ok, "error handler was called with mpack_ok");
    TEST_TRUE(mpack_reader_error(reader) == error, "reader error does not match given error");
    test_read_error = error;
}
Example #24
0
void mpack_done_type(mpack_reader_t* reader, mpack_type_t type) {
    if (mpack_reader_error(reader) == mpack_ok)
        mpack_reader_flag_if_error(reader, mpack_track_pop(&reader->track, type));
}
Example #25
0
static bool element(mpack_reader_t* reader, yajl_gen gen, options_t* options, int depth) {
    const mpack_tag_t tag = mpack_read_tag(reader);
    if (mpack_reader_error(reader) != mpack_ok)
        return false;

    if (!options->debug && depth == 0 && (tag.type != mpack_type_map && tag.type != mpack_type_array)) {
        fprintf(stderr, "%s: Top-level object must be a map or array. Try debug viewing mode (-d)\n", options->command);
        return false;
    }

    // TODO check not depth zero
    switch (tag.type) {
        case mpack_type_bool:   return yajl_gen_bool(gen, tag.v.b) == yajl_gen_status_ok;
        case mpack_type_nil:    return yajl_gen_null(gen) == yajl_gen_status_ok;
        case mpack_type_int:    return yajl_gen_integer(gen, tag.v.i) == yajl_gen_status_ok;
        case mpack_type_float:  return yajl_gen_double(gen, tag.v.f) == yajl_gen_status_ok;
        case mpack_type_double: return yajl_gen_double(gen, tag.v.d) == yajl_gen_status_ok;

        case mpack_type_uint:
            if (tag.v.u > (uint64_t)INT64_MAX) {
                char buf[32];
                snprintf(buf, sizeof(buf), "%" PRIu64, tag.v.u);
                return yajl_gen_string(gen, (const unsigned char*)buf, strlen(buf)) == yajl_gen_status_ok;
            }
            return yajl_gen_integer(gen, (int64_t)tag.v.u) == yajl_gen_status_ok;

        case mpack_type_str:
            return string(reader, gen, options, tag.v.l);

        case mpack_type_bin:
            if (options->base64) {
                return base64_bin(reader, gen, options, tag.v.l, options->base64_prefix);
            } else if (options->debug) {
                mpack_skip_bytes(reader, tag.v.l);
                mpack_done_bin(reader);

                // output nothing to allow us to print our debug string
                skip_quotes = true;
                if (yajl_gen_string(gen, (const unsigned char*)"", 0) != yajl_gen_status_ok)
                    return false;
                skip_quotes = false;

                char buf[64];
                snprintf(buf, sizeof(buf), "<bin of size %u>", tag.v.l);
                print(out_file, buf, strlen(buf));
                return true;
            } else {
                fprintf(stderr, "%s: bin unencodable in JSON. Try debug viewing mode (-d)\n", options->command);
                return false;
            }

        case mpack_type_ext:
            if (options->base64) {
                return base64_ext(reader, gen, options, tag.exttype, tag.v.l);
            } else if (options->debug) {
                mpack_skip_bytes(reader, tag.v.l);
                mpack_done_ext(reader);

                // output nothing to allow us to print our debug string
                skip_quotes = true;
                if (yajl_gen_string(gen, (const unsigned char*)"", 0) != yajl_gen_status_ok)
                    return false;
                skip_quotes = false;

                char buf[64];
                snprintf(buf, sizeof(buf), "<ext of type %i size %u>", tag.exttype, tag.v.l);
                print(out_file, buf, strlen(buf));
                return true;
            } else {
                fprintf(stderr, "%s: ext type %i unencodable in JSON. Try debug viewing mode (-d)\n", options->command, tag.exttype);
                return false;
            }

        case mpack_type_array:
            if (yajl_gen_array_open(gen) != yajl_gen_status_ok)
                return false;
            for (size_t i = 0; i < tag.v.l; ++i)
                if (!element(reader, gen, options, depth + 1))
                    return false;
            mpack_done_array(reader);
            return yajl_gen_array_close(gen) == yajl_gen_status_ok;

        case mpack_type_map:
            if (yajl_gen_map_open(gen) != yajl_gen_status_ok)
                return false;
            for (size_t i = 0; i < tag.v.l; ++i) {

                if (options->debug) {
                    element(reader, gen, options, depth + 1);
                } else {
                    uint32_t len = mpack_expect_str(reader);
                    if (mpack_reader_error(reader) != mpack_ok) {
                        fprintf(stderr, "%s: map key is not a string. Try debug viewing mode (-d)\n", options->command);
                        return false;
                    }
                    if (!string(reader, gen, options, len))
                        return false;
                }

                if (!element(reader, gen, options, depth + 1))
                    return false;
            }
            mpack_done_map(reader);
            return yajl_gen_map_close(gen) == yajl_gen_status_ok;
    }

    return true;
}
Example #26
0
static void mpack_print_element(mpack_reader_t* reader, size_t depth, FILE* file) {
    mpack_tag_t val = mpack_read_tag(reader);
    if (mpack_reader_error(reader) != mpack_ok)
        return;
    switch (val.type) {

        case mpack_type_nil:
            fprintf(file, "null");
            break;
        case mpack_type_bool:
            fprintf(file, val.v.b ? "true" : "false");
            break;

        case mpack_type_float:
            fprintf(file, "%f", val.v.f);
            break;
        case mpack_type_double:
            fprintf(file, "%f", val.v.d);
            break;

        case mpack_type_int:
            fprintf(file, "%" PRIi64, val.v.i);
            break;
        case mpack_type_uint:
            fprintf(file, "%" PRIu64, val.v.u);
            break;

        case mpack_type_bin:
            fprintf(file, "<binary data of length %u>", val.v.l);
            mpack_skip_bytes(reader, val.v.l);
            mpack_done_bin(reader);
            break;

        case mpack_type_ext:
            fprintf(file, "<ext data of type %i and length %u>", val.exttype, val.v.l);
            mpack_skip_bytes(reader, val.v.l);
            mpack_done_ext(reader);
            break;

        case mpack_type_str:
            putc('"', file);
            for (size_t i = 0; i < val.v.l; ++i) {
                char c;
                mpack_read_bytes(reader, &c, 1);
                if (mpack_reader_error(reader) != mpack_ok)
                    return;
                switch (c) {
                    case '\n': fprintf(file, "\\n"); break;
                    case '\\': fprintf(file, "\\\\"); break;
                    case '"': fprintf(file, "\\\""); break;
                    default: putc(c, file); break;
                }
            }
            putc('"', file);
            mpack_done_str(reader);
            break;

        case mpack_type_array:
            fprintf(file, "[\n");
            for (size_t i = 0; i < val.v.n; ++i) {
                for (size_t j = 0; j < depth + 1; ++j)
                    fprintf(file, "    ");
                mpack_print_element(reader, depth + 1, file);
                if (mpack_reader_error(reader) != mpack_ok)
                    return;
                if (i != val.v.n - 1)
                    putc(',', file);
                putc('\n', file);
            }
            for (size_t i = 0; i < depth; ++i)
                fprintf(file, "    ");
            putc(']', file);
            mpack_done_array(reader);
            break;

        case mpack_type_map:
            fprintf(file, "{\n");
            for (size_t i = 0; i < val.v.n; ++i) {
                for (size_t j = 0; j < depth + 1; ++j)
                    fprintf(file, "    ");
                mpack_print_element(reader, depth + 1, file);
                if (mpack_reader_error(reader) != mpack_ok)
                    return;
                fprintf(file, ": ");
                mpack_print_element(reader, depth + 1, file);
                if (mpack_reader_error(reader) != mpack_ok)
                    return;
                if (i != val.v.n - 1)
                    putc(',', file);
                putc('\n', file);
            }
            for (size_t i = 0; i < depth; ++i)
                fprintf(file, "    ");
            putc('}', file);
            mpack_done_map(reader);
            break;
    }
}
Example #27
0
// 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;
}