1/*
2 * MessagePack for C zero-copy buffer implementation
3 *
4 * Copyright (C) 2008-2009 FURUHASHI Sadayuki
5 *
6 *    Distributed under the Boost Software License, Version 1.0.
7 *    (See accompanying file LICENSE_1_0.txt or copy at
8 *    http://www.boost.org/LICENSE_1_0.txt)
9 */
10#include "msgpack/vrefbuffer.h"
11#include <stdlib.h>
12#include <string.h>
13
14#define MSGPACK_PACKER_MAX_BUFFER_SIZE 9
15
16struct msgpack_vrefbuffer_chunk {
17    struct msgpack_vrefbuffer_chunk* next;
18    /* data ... */
19};
20
21bool msgpack_vrefbuffer_init(msgpack_vrefbuffer* vbuf,
22        size_t ref_size, size_t chunk_size)
23{
24    size_t nfirst;
25    struct iovec* array;
26    msgpack_vrefbuffer_chunk* chunk;
27
28    vbuf->chunk_size = chunk_size;
29    vbuf->ref_size =
30        ref_size > MSGPACK_PACKER_MAX_BUFFER_SIZE + 1 ?
31        ref_size : MSGPACK_PACKER_MAX_BUFFER_SIZE + 1 ;
32
33    nfirst = (sizeof(struct iovec) < 72/2) ?
34            72 / sizeof(struct iovec) : 8;
35
36    array = (struct iovec*)malloc(
37            sizeof(struct iovec) * nfirst);
38    if(array == NULL) {
39        return false;
40    }
41
42    vbuf->tail  = array;
43    vbuf->end   = array + nfirst;
44    vbuf->array = array;
45
46    chunk = (msgpack_vrefbuffer_chunk*)malloc(
47            sizeof(msgpack_vrefbuffer_chunk) + chunk_size);
48    if(chunk == NULL) {
49        free(array);
50        return false;
51    }
52    else {
53        msgpack_vrefbuffer_inner_buffer* const ib = &vbuf->inner_buffer;
54
55        ib->free = chunk_size;
56        ib->ptr  = ((char*)chunk) + sizeof(msgpack_vrefbuffer_chunk);
57        ib->head = chunk;
58        chunk->next = NULL;
59
60        return true;
61    }
62}
63
64void msgpack_vrefbuffer_destroy(msgpack_vrefbuffer* vbuf)
65{
66    msgpack_vrefbuffer_chunk* c = vbuf->inner_buffer.head;
67    while(true) {
68        msgpack_vrefbuffer_chunk* n = c->next;
69        free(c);
70        if(n != NULL) {
71            c = n;
72        } else {
73            break;
74        }
75    }
76    free(vbuf->array);
77}
78
79void msgpack_vrefbuffer_clear(msgpack_vrefbuffer* vbuf)
80{
81    msgpack_vrefbuffer_chunk* c = vbuf->inner_buffer.head->next;
82    msgpack_vrefbuffer_chunk* n;
83    while(c != NULL) {
84        n = c->next;
85        free(c);
86        c = n;
87    }
88
89    {
90        msgpack_vrefbuffer_inner_buffer* const ib = &vbuf->inner_buffer;
91        msgpack_vrefbuffer_chunk* chunk = ib->head;
92        chunk->next = NULL;
93        ib->free = vbuf->chunk_size;
94        ib->ptr  = ((char*)chunk) + sizeof(msgpack_vrefbuffer_chunk);
95
96        vbuf->tail = vbuf->array;
97    }
98}
99
100int msgpack_vrefbuffer_append_ref(msgpack_vrefbuffer* vbuf,
101        const char* buf, size_t len)
102{
103    if(vbuf->tail == vbuf->end) {
104        const size_t nused = (size_t)(vbuf->tail - vbuf->array);
105        const size_t nnext = nused * 2;
106
107        struct iovec* nvec = (struct iovec*)realloc(
108                vbuf->array, sizeof(struct iovec)*nnext);
109        if(nvec == NULL) {
110            return -1;
111        }
112
113        vbuf->array = nvec;
114        vbuf->end   = nvec + nnext;
115        vbuf->tail  = nvec + nused;
116    }
117
118    vbuf->tail->iov_base = (char*)buf;
119    vbuf->tail->iov_len  = len;
120    ++vbuf->tail;
121
122    return 0;
123}
124
125int msgpack_vrefbuffer_append_copy(msgpack_vrefbuffer* vbuf,
126        const char* buf, size_t len)
127{
128    msgpack_vrefbuffer_inner_buffer* const ib = &vbuf->inner_buffer;
129    char* m;
130
131    if(ib->free < len) {
132        msgpack_vrefbuffer_chunk* chunk;
133        size_t sz = vbuf->chunk_size;
134        if(sz < len) {
135            sz = len;
136        }
137
138        chunk = (msgpack_vrefbuffer_chunk*)malloc(
139                sizeof(msgpack_vrefbuffer_chunk) + sz);
140        if(chunk == NULL) {
141            return -1;
142        }
143
144        chunk->next = ib->head;
145        ib->head = chunk;
146        ib->free = sz;
147        ib->ptr  = ((char*)chunk) + sizeof(msgpack_vrefbuffer_chunk);
148    }
149
150    m = ib->ptr;
151    memcpy(m, buf, len);
152    ib->free -= len;
153    ib->ptr  += len;
154
155    if(vbuf->tail != vbuf->array && m ==
156            (const char*)((vbuf->tail-1)->iov_base) + (vbuf->tail-1)->iov_len) {
157        (vbuf->tail-1)->iov_len += len;
158        return 0;
159    } else {
160        return msgpack_vrefbuffer_append_ref(vbuf, m, len);
161    }
162}
163
164int msgpack_vrefbuffer_migrate(msgpack_vrefbuffer* vbuf, msgpack_vrefbuffer* to)
165{
166    size_t sz = vbuf->chunk_size;
167
168    msgpack_vrefbuffer_chunk* empty = (msgpack_vrefbuffer_chunk*)malloc(
169            sizeof(msgpack_vrefbuffer_chunk) + sz);
170    if(empty == NULL) {
171        return -1;
172    }
173
174    empty->next = NULL;
175
176    {
177        const size_t nused = (size_t)(vbuf->tail - vbuf->array);
178        if(to->tail + nused < vbuf->end) {
179            struct iovec* nvec;
180            const size_t tosize = (size_t)(to->tail - to->array);
181            const size_t reqsize = nused + tosize;
182            size_t nnext = (size_t)(to->end - to->array) * 2;
183            while(nnext < reqsize) {
184                size_t tmp_nnext = nnext * 2;
185                if (tmp_nnext <= nnext) {
186                    nnext = reqsize;
187                    break;
188                }
189                nnext = tmp_nnext;
190            }
191
192            nvec = (struct iovec*)realloc(
193                    to->array, sizeof(struct iovec)*nnext);
194            if(nvec == NULL) {
195                free(empty);
196                return -1;
197            }
198
199            to->array = nvec;
200            to->end   = nvec + nnext;
201            to->tail  = nvec + tosize;
202        }
203
204        memcpy(to->tail, vbuf->array, sizeof(struct iovec)*nused);
205
206        to->tail += nused;
207        vbuf->tail = vbuf->array;
208
209        {
210            msgpack_vrefbuffer_inner_buffer* const ib = &vbuf->inner_buffer;
211            msgpack_vrefbuffer_inner_buffer* const toib = &to->inner_buffer;
212
213            msgpack_vrefbuffer_chunk* last = ib->head;
214            while(last->next != NULL) {
215                last = last->next;
216            }
217            last->next = toib->head;
218            toib->head = ib->head;
219
220            if(toib->free < ib->free) {
221                toib->free = ib->free;
222                toib->ptr  = ib->ptr;
223            }
224
225            ib->head = empty;
226            ib->free = sz;
227            ib->ptr  = ((char*)empty) + sizeof(msgpack_vrefbuffer_chunk);
228        }
229    }
230
231    return 0;
232}
233