1 /*------------------------------------------------------------------------------
2 *
3 * Copyright (c) 2011-2021, EURid vzw. All rights reserved.
4 * The YADIFA TM software product is provided under the BSD 3-clause license:
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * * Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * * Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * * Neither the name of EURid nor the names of its contributors may be
16 * used to endorse or promote products derived from this software
17 * without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
23 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 *
31 *------------------------------------------------------------------------------
32 *
33 */
34
35 /** @defgroup streaming Streams
36 * @ingroup dnscore
37 * @brief
38 *
39 *
40 *
41 * @{
42 *
43 *----------------------------------------------------------------------------*/
44 #include "dnscore/dnscore-config.h"
45 #include <stdio.h>
46 #include <stdlib.h>
47
48 #include "dnscore/bytearray_output_stream.h"
49 #include "dnscore/zalloc.h"
50
51 #define BYTE_ARRAY_OUTPUT_STREAM_TAG 0x534f4142 /* BAOS */
52 #define BYTE_ARRAY_OUTPUT_STREAM_DATA_TAG 0x41544144534f4142 /* BAOSDATA */
53 #define BYTE_ARRAY_OUTPUT_STREAM_BUFF_TAG 0x46465542534f4142 /* BAOSBUFF */
54
55 #define BYTEARRAY_STARTSIZE 1024
56
57 #define BAOSZDUP_TAG 0x5055445a534f4142
58 #define BAOSDUP_TAG 0x505544534f4142
59
60 typedef struct bytearray_output_stream_data bytearray_output_stream_data;
61
62 /**
63 * @NOTE: if this changes, take care that bytearray_output_stream_context in the header file has at least the SAME SIZE
64 */
65
66 struct bytearray_output_stream_data
67 {
68 u8* buffer;
69 u32 buffer_size;
70 u32 buffer_offset;
71 u8 flags;
72 };
73
74 static ya_result
bytearray_output_stream_write(output_stream * stream,const u8 * buffer,u32 len)75 bytearray_output_stream_write(output_stream* stream, const u8* buffer, u32 len)
76 {
77 if(len == 0)
78 {
79 return 0;
80 }
81
82 bytearray_output_stream_data* data = (bytearray_output_stream_data*)stream->data;
83
84 u32 remaining = data->buffer_size - data->buffer_offset;
85
86 if(len > remaining)
87 {
88 /* Either we can resize, either we have to trunk */
89
90 if((data->flags & BYTEARRAY_DYNAMIC) != 0)
91 {
92 u8* newbuffer;
93 u32 newsize = data->buffer_size;
94
95 do
96 {
97 newsize = newsize * 2;
98 }
99 while(newsize < data->buffer_size + len);
100
101 MALLOC_OR_DIE(u8*, newbuffer, newsize, BYTE_ARRAY_OUTPUT_STREAM_BUFF_TAG);
102 MEMCOPY(newbuffer, data->buffer, data->buffer_offset);
103
104 if((data->flags & BYTEARRAY_OWNED) != 0)
105 {
106 free(data->buffer);
107 }
108
109 data->buffer = newbuffer;
110 data->buffer_size = newsize;
111
112 data->flags |= BYTEARRAY_OWNED;
113 }
114 else
115 {
116 len = remaining;
117 }
118 }
119
120 MEMCOPY(&data->buffer[data->buffer_offset], buffer, len);
121 data->buffer_offset += len;
122
123 return len;
124 }
125
126 static ya_result
bytearray_output_stream_flush(output_stream * stream)127 bytearray_output_stream_flush(output_stream* stream)
128 {
129 (void)stream;
130
131 return SUCCESS;
132 }
133
134 static void
bytearray_output_stream_close(output_stream * stream)135 bytearray_output_stream_close(output_stream* stream)
136 {
137 bytearray_output_stream_data* data = (bytearray_output_stream_data*)stream->data;
138
139 if((data->flags & BYTEARRAY_OWNED) != 0)
140 {
141 #if DEBUG
142 memset(data->buffer, 0xe5, data->buffer_size);
143 #endif
144 free(data->buffer);
145 }
146
147 if((data->flags & BYTEARRAY_ZALLOC_CONTEXT) != 0)
148 {
149 ZFREE(data,bytearray_output_stream_data);
150 }
151
152 output_stream_set_void(stream);
153 }
154
155 static const output_stream_vtbl bytearray_output_stream_vtbl =
156 {
157 bytearray_output_stream_write,
158 bytearray_output_stream_flush,
159 bytearray_output_stream_close,
160 "bytearray_output_stream",
161 };
162
163 void
bytearray_output_stream_init_ex_static(output_stream * out_stream,u8 * array,u32 size,u8 flags,bytearray_output_stream_context * ctx)164 bytearray_output_stream_init_ex_static(output_stream* out_stream, u8* array,u32 size, u8 flags, bytearray_output_stream_context *ctx)
165 {
166 bytearray_output_stream_data *data = (bytearray_output_stream_data*)ctx;
167
168 if(array == NULL)
169 {
170 flags |= BYTEARRAY_OWNED;
171
172 if(size == 0)
173 {
174 flags |= BYTEARRAY_DYNAMIC;
175
176 size = BYTEARRAY_STARTSIZE;
177 }
178
179 MALLOC_OR_DIE(u8*, array, size, BYTE_ARRAY_OUTPUT_STREAM_BUFF_TAG);
180 }
181
182 data->buffer = array;
183 data->buffer_size = size;
184 data->buffer_offset = 0;
185 data->flags = flags;
186
187 out_stream->data = data;
188 out_stream->vtbl = &bytearray_output_stream_vtbl;
189 }
190
191 void
bytearray_output_stream_init_ex(output_stream * out_stream,u8 * array,u32 size,u8 flags)192 bytearray_output_stream_init_ex(output_stream* out_stream, u8* array, u32 size, u8 flags)
193 {
194 bytearray_output_stream_data* data;
195
196 ZALLOC_OBJECT_OR_DIE( data, bytearray_output_stream_data, BYTE_ARRAY_OUTPUT_STREAM_DATA_TAG);
197
198 flags |= BYTEARRAY_ZALLOC_CONTEXT;
199
200 bytearray_output_stream_init_ex_static(out_stream, array, size, flags, (bytearray_output_stream_context*)data);
201 }
202
203 void
bytearray_output_stream_init(output_stream * out_stream,u8 * array,u32 size)204 bytearray_output_stream_init(output_stream* out_stream, u8* array, u32 size)
205 {
206 bytearray_output_stream_init_ex(out_stream, array, size, 0);
207 }
208
209 void
bytearray_output_stream_reset(output_stream * stream)210 bytearray_output_stream_reset(output_stream* stream)
211 {
212 bytearray_output_stream_data* data = (bytearray_output_stream_data*)stream->data;
213 data->buffer_offset = 0;
214 }
215
216 ya_result
bytearray_output_stream_ensure(output_stream * stream,u32 size)217 bytearray_output_stream_ensure(output_stream* stream, u32 size)
218 {
219 bytearray_output_stream_data* data = (bytearray_output_stream_data*)stream->data;
220
221 if(data->buffer_size < size)
222 {
223 /* Either we can resize, either we have to trunk */
224
225 size = (size + 7) & ~7;
226
227 if((data->flags & BYTEARRAY_DYNAMIC) != 0)
228 {
229 u8* newbuffer;
230
231 MALLOC_OR_DIE(u8*, newbuffer, size, BYTE_ARRAY_OUTPUT_STREAM_BUFF_TAG);
232 MEMCOPY(newbuffer, data->buffer, data->buffer_offset);
233
234 if((data->flags & BYTEARRAY_OWNED) != 0)
235 {
236 free(data->buffer);
237 }
238
239 data->buffer = newbuffer;
240 data->buffer_size = size;
241
242 data->flags |= BYTEARRAY_OWNED;
243 }
244 else
245 {
246 return ERROR; // not dynamic
247 }
248 }
249
250 return SUCCESS;
251 }
252
253 u32
bytearray_output_stream_size(output_stream * stream)254 bytearray_output_stream_size(output_stream* stream)
255 {
256 bytearray_output_stream_data* data = (bytearray_output_stream_data*)stream->data;
257 return data->buffer_offset;
258 }
259
260 u8*
bytearray_output_stream_buffer(output_stream * stream)261 bytearray_output_stream_buffer(output_stream* stream)
262 {
263 bytearray_output_stream_data* data = (bytearray_output_stream_data*)stream->data;
264
265 return data->buffer;
266 }
267
268 u8*
bytearray_output_stream_detach(output_stream * stream)269 bytearray_output_stream_detach(output_stream* stream)
270 {
271 bytearray_output_stream_data* data = (bytearray_output_stream_data*)stream->data;
272
273 data->flags &= ~BYTEARRAY_OWNED;
274
275 return data->buffer;
276 }
277
278 void
bytearray_output_stream_set(output_stream * stream,u8 * buffer,u32 buffer_size,bool owned)279 bytearray_output_stream_set(output_stream* stream, u8 *buffer, u32 buffer_size, bool owned)
280 {
281 bytearray_output_stream_data* data = (bytearray_output_stream_data*)stream->data;
282 if((data->buffer != buffer) && ((data->flags & BYTEARRAY_OWNED) != 0))
283 {
284 free(data->buffer);
285 }
286 data->buffer = buffer;
287 data->buffer_offset = buffer_size;
288 data->buffer_size = buffer_size;
289 data->flags = (data->flags & BYTEARRAY_ZALLOC_CONTEXT) | ((owned)?BYTEARRAY_OWNED:0);
290 }
291
292 /**
293
294 * @param out_stream
295 * @param by
296 * @return the actual rewind_count
297 */
298
299 u32
bytearray_output_stream_rewind(output_stream * out_stream,u32 rewind_count)300 bytearray_output_stream_rewind(output_stream* out_stream, u32 rewind_count)
301 {
302 bytearray_output_stream_data* data = (bytearray_output_stream_data*)out_stream->data;
303 if(rewind_count < data->buffer_offset)
304 {
305 data->buffer_offset -= rewind_count;
306 }
307 else
308 {
309 rewind_count = data->buffer_offset;
310 data->buffer_offset = 0;
311 }
312
313 return rewind_count;
314 }
315
316 u8*
bytearray_output_stream_zdup(output_stream * out_stream)317 bytearray_output_stream_zdup(output_stream* out_stream)
318 {
319 bytearray_output_stream_data* data = (bytearray_output_stream_data*)out_stream->data;
320 u8 *ret;
321 u32 n = MAX(data->buffer_offset, 1); // because allocating 0 bytes can be an hassle
322 ZALLOC_OBJECT_ARRAY_OR_DIE(ret, u8, n, BAOSZDUP_TAG);
323 memcpy(ret, data->buffer, n);
324
325 return ret;
326 }
327
328 u8*
bytearray_output_stream_dup(output_stream * out_stream)329 bytearray_output_stream_dup(output_stream* out_stream)
330 {
331 bytearray_output_stream_data* data = (bytearray_output_stream_data*)out_stream->data;
332 u8 *ret;
333 u32 n = MAX(data->buffer_offset, 1); // because allocating 0 bytes can be an hassle
334 MALLOC_OR_DIE(u8*, ret, n, BAOSDUP_TAG);
335 memcpy(ret, data->buffer, n);
336
337 return ret;
338 }
339
340 /** @} */
341