1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to you under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * https://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
14 * implied. See the License for the specific language governing
15 * permissions and limitations under the License.
16 */
17
18 #include <stddef.h>
19 #include <stdlib.h>
20 #include <string.h>
21
22 #include "avro_private.h"
23 #include "avro/data.h"
24 #include "avro/allocation.h"
25 #include "avro/errors.h"
26
27 #ifndef AVRO_STRING_DEBUG
28 #define AVRO_STRING_DEBUG 0
29 #endif
30
31 #if AVRO_STRING_DEBUG
32 #include <stdio.h>
33 #define DEBUG(...) \
34 do { \
35 fprintf(stderr, __VA_ARGS__); \
36 fprintf(stderr, "\n"); \
37 } while (0)
38 #else
39 #define DEBUG(...) /* don't print messages */
40 #endif
41
42
43 /*
44 * A resizable wrapped buffer implementation. This implementation makes
45 * actual copies in its copy method; if we wanted a zero-copy solution
46 * here, then we'd have to keep track of all copies of the buffer, so
47 * that we can update pointers whenever the buffer is resized (since
48 * this might change the location of the memory region).
49 */
50
51 struct avro_wrapped_resizable {
52 size_t buf_size;
53 };
54
55 #define avro_wrapped_resizable_size(sz) \
56 (sizeof(struct avro_wrapped_resizable) + (sz))
57
58 static void
avro_wrapped_resizable_free(avro_wrapped_buffer_t * self)59 avro_wrapped_resizable_free(avro_wrapped_buffer_t *self)
60 {
61 DEBUG("--- Freeing resizable <%p:%" PRIsz "> (%p)", self->buf, self->size, self->user_data);
62 struct avro_wrapped_resizable *resizable = (struct avro_wrapped_resizable *) self->user_data;
63 avro_free(resizable, avro_wrapped_resizable_size(resizable->buf_size));
64 }
65
66 static int
avro_wrapped_resizable_resize(avro_wrapped_buffer_t * self,size_t desired)67 avro_wrapped_resizable_resize(avro_wrapped_buffer_t *self, size_t desired)
68 {
69 struct avro_wrapped_resizable *resizable = (struct avro_wrapped_resizable *) self->user_data;
70
71 /*
72 * If we've already allocated enough memory for the desired
73 * size, there's nothing to do.
74 */
75
76 if (resizable->buf_size >= desired) {
77 return 0;
78 }
79
80 size_t new_buf_size = resizable->buf_size * 2;
81 if (desired > new_buf_size) {
82 new_buf_size = desired;
83 }
84
85 DEBUG("--- Resizing <%p:%" PRIsz "> (%p) -> %" PRIsz,
86 self->buf, self->buf_size, self->user_data, new_buf_size);
87
88 struct avro_wrapped_resizable *new_resizable =
89 (struct avro_wrapped_resizable *) avro_realloc(resizable,
90 avro_wrapped_resizable_size(resizable->buf_size),
91 avro_wrapped_resizable_size(new_buf_size));
92 if (new_resizable == NULL) {
93 return ENOMEM;
94 }
95 DEBUG("--- New buffer <%p:%" PRIsz ">", new_buf, new_buf_size);
96
97 new_resizable->buf_size = new_buf_size;
98
99 char *old_buf = (char *) resizable;
100 char *new_buf = (char *) new_resizable;
101
102 ptrdiff_t offset = (char *) self->buf - old_buf;
103 DEBUG("--- Old data pointer is %p", self->buf);
104 self->buf = new_buf + offset;
105 self->user_data = new_resizable;
106 DEBUG("--- New data pointer is %p", self->buf);
107 return 0;
108 }
109
110 static int
avro_wrapped_resizable_new(avro_wrapped_buffer_t * dest,size_t buf_size)111 avro_wrapped_resizable_new(avro_wrapped_buffer_t *dest, size_t buf_size)
112 {
113 size_t allocated_size = avro_wrapped_resizable_size(buf_size);
114 struct avro_wrapped_resizable *resizable =
115 (struct avro_wrapped_resizable *) avro_malloc(allocated_size);
116 if (resizable == NULL) {
117 return ENOMEM;
118 }
119
120 resizable->buf_size = buf_size;
121
122 dest->buf = ((char *) resizable) + sizeof(struct avro_wrapped_resizable);
123 DEBUG("--- Creating resizable <%p:%" PRIsz "> (%p)", dest->buf, buf_size, resizable);
124 dest->size = buf_size;
125 dest->user_data = resizable;
126 dest->free = avro_wrapped_resizable_free;
127 dest->copy = NULL;
128 dest->slice = NULL;
129 return 0;
130 }
131
132 #define is_resizable(buf) \
133 ((buf).free == avro_wrapped_resizable_free)
134
135
136
137 void
avro_raw_string_init(avro_raw_string_t * str)138 avro_raw_string_init(avro_raw_string_t *str)
139 {
140 memset(str, 0, sizeof(avro_raw_string_t));
141 }
142
143
144 void
avro_raw_string_clear(avro_raw_string_t * str)145 avro_raw_string_clear(avro_raw_string_t *str)
146 {
147 /*
148 * If the string's buffer is one that we control, then we don't
149 * free it; that lets us reuse the storage on the next call to
150 * avro_raw_string_set[_length].
151 */
152
153 if (is_resizable(str->wrapped)) {
154 DEBUG("--- Clearing resizable buffer");
155 str->wrapped.size = 0;
156 } else {
157 DEBUG("--- Freeing wrapped buffer");
158 avro_wrapped_buffer_free(&str->wrapped);
159 avro_raw_string_init(str);
160 }
161 }
162
163
164 void
avro_raw_string_done(avro_raw_string_t * str)165 avro_raw_string_done(avro_raw_string_t *str)
166 {
167 avro_wrapped_buffer_free(&str->wrapped);
168 avro_raw_string_init(str);
169 }
170
171
172 /**
173 * Makes sure that the string's buffer is one that we allocated
174 * ourselves, and that the buffer is big enough to hold a string of the
175 * given length.
176 */
177
178 static int
avro_raw_string_ensure_buf(avro_raw_string_t * str,size_t length)179 avro_raw_string_ensure_buf(avro_raw_string_t *str, size_t length)
180 {
181 int rval;
182
183 DEBUG("--- Ensuring resizable buffer of size %" PRIsz, length);
184 if (is_resizable(str->wrapped)) {
185 /*
186 * If we've already got a resizable buffer, just have it
187 * resize itself.
188 */
189
190 return avro_wrapped_resizable_resize(&str->wrapped, length);
191 } else {
192 /*
193 * Stash a copy of the old wrapped buffer, and then
194 * create a new resizable buffer to store our content
195 * in.
196 */
197
198 avro_wrapped_buffer_t orig = str->wrapped;
199 check(rval, avro_wrapped_resizable_new(&str->wrapped, length));
200
201 /*
202 * If there was any content in the old wrapped buffer,
203 * copy it into the new resizable one.
204 */
205
206 if (orig.size > 0) {
207 size_t to_copy =
208 (orig.size < length)? orig.size: length;
209 memcpy((void *) str->wrapped.buf, orig.buf, to_copy);
210 }
211 avro_wrapped_buffer_free(&orig);
212
213 return 0;
214 }
215 }
216
217
218 void
avro_raw_string_set_length(avro_raw_string_t * str,const void * src,size_t length)219 avro_raw_string_set_length(avro_raw_string_t *str,
220 const void *src, size_t length)
221 {
222 avro_raw_string_ensure_buf(str, length+1);
223 memcpy((void *) str->wrapped.buf, src, length);
224 ((char *) str->wrapped.buf)[length] = '\0';
225 str->wrapped.size = length;
226 }
227
228
avro_raw_string_append_length(avro_raw_string_t * str,const void * src,size_t length)229 void avro_raw_string_append_length(avro_raw_string_t *str,
230 const void *src,
231 size_t length)
232 {
233 if (avro_raw_string_length(str) == 0) {
234 return avro_raw_string_set_length(str, src, length);
235 }
236
237 avro_raw_string_ensure_buf(str, str->wrapped.size + length);
238 memcpy((char *) str->wrapped.buf + str->wrapped.size, src, length);
239 str->wrapped.size += length;
240 }
241
242
243 void
avro_raw_string_set(avro_raw_string_t * str,const char * src)244 avro_raw_string_set(avro_raw_string_t *str, const char *src)
245 {
246 size_t length = strlen(src);
247 avro_raw_string_ensure_buf(str, length+1);
248 memcpy((void *) str->wrapped.buf, src, length+1);
249 str->wrapped.size = length+1;
250 }
251
252
253 void
avro_raw_string_append(avro_raw_string_t * str,const char * src)254 avro_raw_string_append(avro_raw_string_t *str, const char *src)
255 {
256 if (avro_raw_string_length(str) == 0) {
257 return avro_raw_string_set(str, src);
258 }
259
260 /* Assume that str->wrapped.size includes a NUL terminator */
261 size_t length = strlen(src);
262 avro_raw_string_ensure_buf(str, str->wrapped.size + length);
263 memcpy((char *) str->wrapped.buf + str->wrapped.size - 1, src, length+1);
264 str->wrapped.size += length;
265 }
266
267
268 void
avro_raw_string_give(avro_raw_string_t * str,avro_wrapped_buffer_t * src)269 avro_raw_string_give(avro_raw_string_t *str,
270 avro_wrapped_buffer_t *src)
271 {
272 DEBUG("--- Giving control of <%p:%" PRIsz "> (%p) to string",
273 src->buf, src->size, src);
274 avro_wrapped_buffer_free(&str->wrapped);
275 avro_wrapped_buffer_move(&str->wrapped, src);
276 }
277
278 int
avro_raw_string_grab(const avro_raw_string_t * str,avro_wrapped_buffer_t * dest)279 avro_raw_string_grab(const avro_raw_string_t *str,
280 avro_wrapped_buffer_t *dest)
281 {
282 return avro_wrapped_buffer_copy(dest, &str->wrapped, 0, str->wrapped.size);
283 }
284
285
286 int
avro_raw_string_equals(const avro_raw_string_t * str1,const avro_raw_string_t * str2)287 avro_raw_string_equals(const avro_raw_string_t *str1,
288 const avro_raw_string_t *str2)
289 {
290 if (str1 == str2) {
291 return 1;
292 }
293
294 if (!str1 || !str2) {
295 return 0;
296 }
297
298 if (str1->wrapped.size != str2->wrapped.size) {
299 return 0;
300 }
301
302 return (memcmp(str1->wrapped.buf, str2->wrapped.buf,
303 str1->wrapped.size) == 0);
304 }
305