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