| /* | |
| * MessagePack for C zero-copy buffer implementation | |
| * | |
| * Copyright (C) 2008-2009 FURUHASHI Sadayuki | |
| * | |
| * Distributed under the Boost Software License, Version 1.0. | |
| * (See accompanying file LICENSE_1_0.txt or copy at | |
| * http://www.boost.org/LICENSE_1_0.txt) | |
| */ | |
| #include "msgpack/vrefbuffer.h" | |
| #include <stdlib.h> | |
| #include <string.h> | |
| #define MSGPACK_PACKER_MAX_BUFFER_SIZE 9 | |
| struct msgpack_vrefbuffer_chunk { | |
| struct msgpack_vrefbuffer_chunk* next; | |
| /* data ... */ | |
| }; | |
| bool msgpack_vrefbuffer_init(msgpack_vrefbuffer* vbuf, | |
| size_t ref_size, size_t chunk_size) | |
| { | |
| size_t nfirst; | |
| struct iovec* array; | |
| msgpack_vrefbuffer_chunk* chunk; | |
| vbuf->chunk_size = chunk_size; | |
| vbuf->ref_size = | |
| ref_size > MSGPACK_PACKER_MAX_BUFFER_SIZE + 1 ? | |
| ref_size : MSGPACK_PACKER_MAX_BUFFER_SIZE + 1 ; | |
| nfirst = (sizeof(struct iovec) < 72/2) ? | |
| 72 / sizeof(struct iovec) : 8; | |
| array = (struct iovec*)malloc( | |
| sizeof(struct iovec) * nfirst); | |
| if(array == NULL) { | |
| return false; | |
| } | |
| vbuf->tail = array; | |
| vbuf->end = array + nfirst; | |
| vbuf->array = array; | |
| chunk = (msgpack_vrefbuffer_chunk*)malloc( | |
| sizeof(msgpack_vrefbuffer_chunk) + chunk_size); | |
| if(chunk == NULL) { | |
| free(array); | |
| return false; | |
| } | |
| else { | |
| msgpack_vrefbuffer_inner_buffer* const ib = &vbuf->inner_buffer; | |
| ib->free = chunk_size; | |
| ib->ptr = ((char*)chunk) + sizeof(msgpack_vrefbuffer_chunk); | |
| ib->head = chunk; | |
| chunk->next = NULL; | |
| return true; | |
| } | |
| } | |
| void msgpack_vrefbuffer_destroy(msgpack_vrefbuffer* vbuf) | |
| { | |
| msgpack_vrefbuffer_chunk* c = vbuf->inner_buffer.head; | |
| while(true) { | |
| msgpack_vrefbuffer_chunk* n = c->next; | |
| free(c); | |
| if(n != NULL) { | |
| c = n; | |
| } else { | |
| break; | |
| } | |
| } | |
| free(vbuf->array); | |
| } | |
| void msgpack_vrefbuffer_clear(msgpack_vrefbuffer* vbuf) | |
| { | |
| msgpack_vrefbuffer_chunk* c = vbuf->inner_buffer.head->next; | |
| msgpack_vrefbuffer_chunk* n; | |
| while(c != NULL) { | |
| n = c->next; | |
| free(c); | |
| c = n; | |
| } | |
| { | |
| msgpack_vrefbuffer_inner_buffer* const ib = &vbuf->inner_buffer; | |
| msgpack_vrefbuffer_chunk* chunk = ib->head; | |
| chunk->next = NULL; | |
| ib->free = vbuf->chunk_size; | |
| ib->ptr = ((char*)chunk) + sizeof(msgpack_vrefbuffer_chunk); | |
| vbuf->tail = vbuf->array; | |
| } | |
| } | |
| int msgpack_vrefbuffer_append_ref(msgpack_vrefbuffer* vbuf, | |
| const char* buf, size_t len) | |
| { | |
| if(vbuf->tail == vbuf->end) { | |
| const size_t nused = (size_t)(vbuf->tail - vbuf->array); | |
| const size_t nnext = nused * 2; | |
| struct iovec* nvec = (struct iovec*)realloc( | |
| vbuf->array, sizeof(struct iovec)*nnext); | |
| if(nvec == NULL) { | |
| return -1; | |
| } | |
| vbuf->array = nvec; | |
| vbuf->end = nvec + nnext; | |
| vbuf->tail = nvec + nused; | |
| } | |
| vbuf->tail->iov_base = (char*)buf; | |
| vbuf->tail->iov_len = len; | |
| ++vbuf->tail; | |
| return 0; | |
| } | |
| int msgpack_vrefbuffer_append_copy(msgpack_vrefbuffer* vbuf, | |
| const char* buf, size_t len) | |
| { | |
| msgpack_vrefbuffer_inner_buffer* const ib = &vbuf->inner_buffer; | |
| char* m; | |
| if(ib->free < len) { | |
| msgpack_vrefbuffer_chunk* chunk; | |
| size_t sz = vbuf->chunk_size; | |
| if(sz < len) { | |
| sz = len; | |
| } | |
| chunk = (msgpack_vrefbuffer_chunk*)malloc( | |
| sizeof(msgpack_vrefbuffer_chunk) + sz); | |
| if(chunk == NULL) { | |
| return -1; | |
| } | |
| chunk->next = ib->head; | |
| ib->head = chunk; | |
| ib->free = sz; | |
| ib->ptr = ((char*)chunk) + sizeof(msgpack_vrefbuffer_chunk); | |
| } | |
| m = ib->ptr; | |
| memcpy(m, buf, len); | |
| ib->free -= len; | |
| ib->ptr += len; | |
| if(vbuf->tail != vbuf->array && m == | |
| (const char*)((vbuf->tail-1)->iov_base) + (vbuf->tail-1)->iov_len) { | |
| (vbuf->tail-1)->iov_len += len; | |
| return 0; | |
| } else { | |
| return msgpack_vrefbuffer_append_ref(vbuf, m, len); | |
| } | |
| } | |
| int msgpack_vrefbuffer_migrate(msgpack_vrefbuffer* vbuf, msgpack_vrefbuffer* to) | |
| { | |
| size_t sz = vbuf->chunk_size; | |
| msgpack_vrefbuffer_chunk* empty = (msgpack_vrefbuffer_chunk*)malloc( | |
| sizeof(msgpack_vrefbuffer_chunk) + sz); | |
| if(empty == NULL) { | |
| return -1; | |
| } | |
| empty->next = NULL; | |
| { | |
| const size_t nused = (size_t)(vbuf->tail - vbuf->array); | |
| if(to->tail + nused < vbuf->end) { | |
| struct iovec* nvec; | |
| const size_t tosize = (size_t)(to->tail - to->array); | |
| const size_t reqsize = nused + tosize; | |
| size_t nnext = (size_t)(to->end - to->array) * 2; | |
| while(nnext < reqsize) { | |
| size_t tmp_nnext = nnext * 2; | |
| if (tmp_nnext <= nnext) { | |
| nnext = reqsize; | |
| break; | |
| } | |
| nnext = tmp_nnext; | |
| } | |
| nvec = (struct iovec*)realloc( | |
| to->array, sizeof(struct iovec)*nnext); | |
| if(nvec == NULL) { | |
| free(empty); | |
| return -1; | |
| } | |
| to->array = nvec; | |
| to->end = nvec + nnext; | |
| to->tail = nvec + tosize; | |
| } | |
| memcpy(to->tail, vbuf->array, sizeof(struct iovec)*nused); | |
| to->tail += nused; | |
| vbuf->tail = vbuf->array; | |
| { | |
| msgpack_vrefbuffer_inner_buffer* const ib = &vbuf->inner_buffer; | |
| msgpack_vrefbuffer_inner_buffer* const toib = &to->inner_buffer; | |
| msgpack_vrefbuffer_chunk* last = ib->head; | |
| while(last->next != NULL) { | |
| last = last->next; | |
| } | |
| last->next = toib->head; | |
| toib->head = ib->head; | |
| if(toib->free < ib->free) { | |
| toib->free = ib->free; | |
| toib->ptr = ib->ptr; | |
| } | |
| ib->head = empty; | |
| ib->free = sz; | |
| ib->ptr = ((char*)empty) + sizeof(msgpack_vrefbuffer_chunk); | |
| } | |
| } | |
| return 0; | |
| } |