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