File Coverage

srl_buffer.h
Criterion Covered Total %
statement 61 63 96.8
branch 9 12 75.0
condition n/a
subroutine n/a
pod n/a
total 70 75 93.3


line stmt bran cond sub pod time code
1             #ifndef SRL_BUFFER_H_
2             #define SRL_BUFFER_H_
3              
4             #include "assert.h"
5              
6             #include "srl_inline.h"
7             #include "srl_common.h"
8             #include "srl_buffer_types.h"
9              
10             /* The static's below plus the ifndef sort of make this header only
11             * usable in one place per compilation unit. Drop "static" when necessary.
12             * For now, potentially smaller code wins. */
13              
14             /* buffer operations */
15             #define BUF_POS_OFS(buf) (((buf)->pos) - ((buf)->start))
16             #define BUF_SPACE(buf) (((buf)->end) - ((buf)->pos))
17             #define BUF_SIZE(buf) (((buf)->end) - ((buf)->start))
18             #define BUF_NEED_GROW(buf, minlen) ((size_t)BUF_SPACE(buf) <= minlen)
19             #define BUF_NEED_GROW_TOTAL(buf, minlen) ((size_t)BUF_SIZE(buf) <= minlen)
20             #define BUF_NOT_DONE(buf) ((buf)->pos < (buf)->end)
21             #define BUF_DONE(buf) ((buf)->pos >= (buf)->end)
22              
23             /* body-position/size related operations */
24             #define BODY_POS_OFS(buf) (((buf)->pos) - ((buf)->body_pos))
25              
26             /* these are mostly for right between (de)serializing the header and the body */
27             #define SRL_SET_BODY_POS(buf, pos_ptr) ((buf)->body_pos = pos_ptr)
28             #define SRL_UPDATE_BODY_POS(buf, protocol_version) \
29             STMT_START { \
30             if (expect_false((protocol_version) == 1)) { \
31             SRL_SET_BODY_POS((buf), (buf)->start); \
32             } else { \
33             SRL_SET_BODY_POS((buf), (buf)->pos-1); \
34             } \
35             } STMT_END
36              
37             /* Internal debugging macros, used only in DEBUG mode */
38             #ifndef NDEBUG
39             #define DEBUG_ASSERT_BUF_SPACE(buf, len) STMT_START { \
40             if((BUF_SPACE(buf) < (ptrdiff_t)(len))) { \
41             warn("failed assertion check - pos: %ld [%p %p %p] %ld < %ld", \
42             (long)BUF_POS_OFS(buf), (buf)->start, \
43             (buf)->pos, (buf)->end, \
44             (long)BUF_SPACE(buf),(long)(len)); \
45             } \
46             assert(BUF_SPACE(buf) >= (ptrdiff_t)(len)); \
47             } STMT_END
48             #else
49             #define DEBUG_ASSERT_BUF_SPACE(buf, len) ((void)0)
50             #endif
51              
52             #ifndef NDEBUG
53             #define DEBUG_ASSERT_BUF_SANE(buf) STMT_START { \
54             if(!(((buf)->start <= (buf)->pos) && ((buf)->pos <= (buf)->end))){ \
55             warn("failed sanity assertion check - pos: %ld [%p %p %p] %ld", \
56             (long)BUF_POS_OFS(buf), (buf)->start, \
57             (buf)->pos, (buf)->end, (long)BUF_SPACE(buf)); \
58             } \
59             assert(((buf)->start <= (buf)->pos) && ((buf)->pos <= (buf)->end)); \
60             } STMT_END
61             #else
62             #define DEBUG_ASSERT_BUF_SANE(buf) \
63             assert(((buf)->start <= (buf)->pos) && ((buf)->pos <= (buf)->end))
64             #endif
65              
66             /* Allocate a virgin buffer (but not the buffer struct) */
67             SRL_STATIC_INLINE int
68 443397           srl_buf_init_buffer(pTHX_ srl_buffer_t *buf, const STRLEN init_size)
69             {
70 443397           Newx(buf->start, init_size, srl_buffer_char);
71 443397 50         if (expect_false( buf->start == NULL ))
72 0           return 1;
73 443397           buf->end = buf->start + init_size - 1;
74 443397           buf->pos = buf->start;
75 443397           buf->body_pos = buf->start; /* SRL_SET_BODY_POS(enc, enc->buf.start) equiv */
76 443397           return 0;
77             }
78              
79             /* Free a buffer (but not the buffer struct) */
80             SRL_STATIC_INLINE void
81 443397           srl_buf_free_buffer(pTHX_ srl_buffer_t *buf)
82             {
83 443397           Safefree(buf->start);
84 443397           }
85              
86             /* Copy one buffer to another (shallowly!) */
87             SRL_STATIC_INLINE void
88 198750           srl_buf_copy_buffer(pTHX_ srl_buffer_t *src, srl_buffer_t *dest)
89             {
90 198750           Copy(src, dest, 1, srl_buffer_t);
91 198750           }
92              
93             /* Swap two buffers */
94             SRL_STATIC_INLINE void
95 983421           srl_buf_swap_buffer(pTHX_ srl_buffer_t *buf1, srl_buffer_t *buf2)
96             {
97             srl_buffer_t tmp;
98 983421           Copy(buf1, &tmp, 1, srl_buffer_t);
99 983421           Copy(buf2, buf1, 1, srl_buffer_t);
100 983421           Copy(&tmp, buf2, 1, srl_buffer_t);
101 983421           }
102              
103             /* old_size + (new_size / 4) */
104             #define OVERALLOC_FUNC(cur_size,req_size) (cur_size + (req_size >> 2))
105              
106             SRL_STATIC_INLINE void
107 142629           srl_buf_grow_nocheck(pTHX_ srl_buffer_t *buf, const size_t minlen)
108             {
109 142629           const size_t pos_ofs= BUF_POS_OFS(buf); /* have to store the offset of pos */
110 142629           const size_t body_ofs= buf->body_pos - buf->start; /* have to store the offset of the body */
111             #ifdef MEMDEBUG
112             const size_t new_size = minlen;
113             #else
114 142629           const size_t cur_size = BUF_SIZE(buf);
115 142629           const size_t tmp_size = OVERALLOC_FUNC(cur_size, minlen);
116 142629           const size_t new_size = (minlen > tmp_size) ? minlen : tmp_size;
117             #endif
118              
119             DEBUG_ASSERT_BUF_SANE(buf);
120             /* assert that Renew means GROWING the buffer */
121             assert(buf->start + new_size > buf->end);
122              
123 142629           Renew(buf->start, new_size, srl_buffer_char);
124 142629 50         if (buf->start == NULL)
125 0           croak("Out of memory!");
126             if (0) warn("Renew(%ld)", new_size);
127              
128 142629           buf->end = (srl_buffer_char*) (buf->start + new_size);
129 142629           buf->pos = buf->start + pos_ofs;
130 142629           SRL_SET_BODY_POS(buf, buf->start + body_ofs);
131              
132             DEBUG_ASSERT_BUF_SANE(buf);
133             assert(buf->end - buf->start > (ptrdiff_t)0);
134             assert(buf->pos - buf->start >= (ptrdiff_t)0);
135             /* The following is checking against -1 because SRL_UPDATE_BODY_POS
136             * will actually set the body_pos to pos-1, where pos can be 0.
137             * This works out fine in the end, but is admittedly a bit shady.
138             * FIXME */
139             assert(buf->body_pos - buf->start >= (ptrdiff_t)-1);
140 142629           }
141              
142             #define BUF_SIZE_ASSERT(buf, minlen) \
143             STMT_START { \
144             DEBUG_ASSERT_BUF_SANE(buf); \
145             if (BUF_NEED_GROW(buf, minlen)) \
146             srl_buf_grow_nocheck(aTHX_ (buf), (BUF_SIZE(buf) + minlen)); \
147             DEBUG_ASSERT_BUF_SANE(buf); \
148             } STMT_END
149              
150             #define BUF_SIZE_ASSERT_TOTAL(buf, minlen) \
151             STMT_START { \
152             DEBUG_ASSERT_BUF_SANE(buf); \
153             if (BUF_NEED_GROW_TOTAL(buf, minlen)) \
154             srl_buf_grow_nocheck(aTHX_ (buf), (minlen)); \
155             DEBUG_ASSERT_BUF_SANE(buf); \
156             } STMT_END
157              
158             SRL_STATIC_INLINE void
159             srl_buf_cat_str_int(pTHX_ srl_buffer_t *buf, const char *str, size_t len)
160             {
161             BUF_SIZE_ASSERT(buf, len);
162             Copy(str, buf->pos, len, char);
163             buf->pos += len;
164             DEBUG_ASSERT_BUF_SANE(buf);
165             }
166             #define srl_buf_cat_str(buf, str, len) srl_buf_cat_str_int(aTHX_ buf, str, len)
167             /* see perl.git:handy.h STR_WITH_LEN macro for explanation of the below code */
168             #define srl_buf_cat_str_s(buf, str) srl_buf_cat_str(buf, ("" str ""), sizeof(str)-1)
169              
170             SRL_STATIC_INLINE void
171 1189144           srl_buf_cat_str_nocheck_int(pTHX_ srl_buffer_t *buf, const char *str, size_t len)
172             {
173             DEBUG_ASSERT_BUF_SANE(buf);
174             DEBUG_ASSERT_BUF_SPACE(buf, len);
175 1189144           Copy(str, buf->pos, len, char);
176 1189144           buf->pos += len;
177             DEBUG_ASSERT_BUF_SANE(buf);
178 1189144           }
179             #define srl_buf_cat_str_nocheck(buf, str, len) srl_buf_cat_str_nocheck_int(aTHX_ buf, str, len)
180             /* see perl.git:handy.h STR_WITH_LEN macro for explanation of the below code */
181             #define srl_buf_cat_str_s_nocheck(buf, str) srl_buf_cat_str_nocheck(buf, ("" str ""), sizeof(str)-1)
182              
183             SRL_STATIC_INLINE void
184 2820549           srl_buf_cat_char_int(pTHX_ srl_buffer_t *buf, const char c)
185             {
186             DEBUG_ASSERT_BUF_SANE(buf);
187 2820549 50         BUF_SIZE_ASSERT(buf, 1);
188             DEBUG_ASSERT_BUF_SPACE(buf, 1);
189 2820549           *buf->pos++ = c;
190             DEBUG_ASSERT_BUF_SANE(buf);
191 2820549           }
192             #define srl_buf_cat_char(buf, c) srl_buf_cat_char_int(aTHX_ buf, c)
193              
194             SRL_STATIC_INLINE void
195 5113084           srl_buf_cat_char_nocheck_int(pTHX_ srl_buffer_t *buf, const char c)
196             {
197             DEBUG_ASSERT_BUF_SANE(buf);
198             DEBUG_ASSERT_BUF_SPACE(buf, 1);
199 5113084           *buf->pos++ = c;
200             DEBUG_ASSERT_BUF_SANE(buf);
201 5113084           }
202             #define srl_buf_cat_char_nocheck(buf, c) srl_buf_cat_char_nocheck_int(aTHX_ buf, c)
203              
204             /*
205             * This implements "varint" and "zigzag varint" types as used in protobufs, etc.
206             *
207             * varint is a variable length encoding of unsigned integers, where the low
208             * 7 bits of the input value are encoded into each byte of output, with the high bit
209             * used as a flag to indicate there is another byte worth of bits to be read.
210             *
211             * zigzag is a way of encoding signed integers as an unsigned integer in such a way
212             * that positive and negative numbers are interleaved, so that z0=0, z1=-1, z2=1,
213             * z3=-2, z4=2, etc. When the zigzag form is represented as a varint, the result is
214             * that both negative and positive number take space proportional to their distance
215             * from zero.
216             *
217             * see: https://developers.google.com/protocol-buffers/docs/encoding#types
218             *
219             */
220             #define srl_varint_size(x) ( \
221             z <= (1UL << 7) ? 1 : \
222             z <= (1UL << 14) ? 2 : \
223             z <= (1UL << 21) ? 3 : \
224             z <= (1UL << 28) ? 4 : \
225             z <= (1UL << 35) ? 5 : \
226             z <= (1UL << 42) ? 6 : \
227             z <= (1UL << 49) ? 7 : \
228             z <= (1UL << 56) ? 8 : \
229             z <= (1UL << 63) ? 9 : \
230             10 )
231              
232              
233             SRL_STATIC_INLINE void
234 3571971           srl_buf_cat_varint_raw_nocheck(pTHX_ srl_buffer_t *buf, UV value) {
235             DEBUG_ASSERT_BUF_SANE(buf);
236             DEBUG_ASSERT_BUF_SPACE(buf, SRL_MAX_VARINT_LENGTH);
237 8585668 100         while (value >= 0x80) { /* while we are larger than 7 bits long */
238 5013697           *buf->pos++ = (value & 0x7f) | 0x80; /* write out the least significant 7 bits, set the high bit */
239 5013697           value >>= 7; /* shift off the 7 least significant bits */
240             }
241 3571971           *buf->pos++ = (U8)value; /* encode the last 7 bits without the high bit being set */
242             DEBUG_ASSERT_BUF_SANE(buf);
243 3571971           }
244              
245             SRL_STATIC_INLINE UV
246 194506           srl_zigzag_iv(IV value) {
247 194506           return (UV)((value << 1) ^ (value >> (sizeof(IV) * 8 - 1)));
248             }
249              
250             SRL_STATIC_INLINE void
251             srl_buf_cat_zigzag_raw_nocheck(pTHX_ srl_buffer_t *buf, const IV value) {
252             srl_buf_cat_varint_raw_nocheck(aTHX_ buf, srl_zigzag_iv(value));
253             }
254              
255             SRL_STATIC_INLINE void
256 3571971           srl_buf_cat_varint_nocheck(pTHX_ srl_buffer_t *buf, const char tag, UV value) {
257             DEBUG_ASSERT_BUF_SPACE(buf, 1);
258 3571971 100         if (expect_true( tag ))
259 2851032           *buf->pos++ = tag;
260 3571971           srl_buf_cat_varint_raw_nocheck(aTHX_ buf, value);
261 3571971           }
262              
263             SRL_STATIC_INLINE void
264             srl_buf_cat_zigzag_nocheck(pTHX_ srl_buffer_t *buf, const char tag, const IV value) {
265             srl_buf_cat_varint_nocheck(aTHX_ buf, tag, srl_zigzag_iv(value));
266             }
267              
268             SRL_STATIC_INLINE void
269 1575862           srl_buf_cat_varint(pTHX_ srl_buffer_t *buf, const char tag, const UV value) {
270             /* this implements "varint" from google protocol buffers */
271 1575862 100         BUF_SIZE_ASSERT(buf, SRL_MAX_VARINT_LENGTH + 1); /* always allocate space for the tag, overalloc is harmless */
272 1575862           srl_buf_cat_varint_nocheck(aTHX_ buf, tag, value);
273 1575862           }
274              
275             SRL_STATIC_INLINE void
276 194506           srl_buf_cat_zigzag(pTHX_ srl_buffer_t *buf, const char tag, const IV value) {
277 194506           srl_buf_cat_varint(aTHX_ buf, tag, srl_zigzag_iv(value));
278 194506           }
279              
280             #endif