1 /* A dynamic memory buffer that expands as you write.
2  */
3 
4 /*
5 
6     Copyright (C) 1991-2003 The National Gallery
7 
8     This library is free software; you can redistribute it and/or
9     modify it under the terms of the GNU Lesser General Public
10     License as published by the Free Software Foundation; either
11     version 2.1 of the License, or (at your option) any later version.
12 
13     This library is distributed in the hope that it will be useful,
14     but WITHOUT ANY WARRANTY; without even the implied warranty of
15     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16     Lesser General Public License for more details.
17 
18     You should have received a copy of the GNU Lesser General Public
19     License along with this library; if not, write to the Free Software
20     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21     02110-1301  USA
22 
23  */
24 
25 /*
26 
27     These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
28 
29  */
30 
31 /*
32 #define DEBUG
33  */
34 
35 #ifdef HAVE_CONFIG_H
36 #include <config.h>
37 #endif /*HAVE_CONFIG_H*/
38 #include <vips/intl.h>
39 
40 #include <string.h>
41 
42 #include <vips/vips.h>
43 
44 /**
45  * vips_dbuf_init:
46  * @dbuf: the buffer
47  *
48  * Initialize @dbuf.
49  */
50 void
vips_dbuf_init(VipsDbuf * dbuf)51 vips_dbuf_init( VipsDbuf *dbuf )
52 {
53 	dbuf->data = NULL;
54 	dbuf->allocated_size = 0;
55 	dbuf->data_size = 0;
56 	dbuf->write_point = 0;
57 }
58 
59 /**
60  * vips_dbuf_minimum_size:
61  * @dbuf: the buffer
62  * @size: the minimum size
63  *
64  * Make sure @dbuf is at least @size bytes.
65  *
66  * Returns: %FALSE on out of memory, %TRUE otherwise.
67  */
68 gboolean
vips_dbuf_minimum_size(VipsDbuf * dbuf,size_t size)69 vips_dbuf_minimum_size( VipsDbuf *dbuf, size_t size )
70 {
71 	if( size > dbuf->allocated_size ) {
72 		const size_t new_allocated_size = 3 * (16 + size) / 2;
73 
74 		unsigned char *new_data;
75 
76 		if( !(new_data =
77 			g_try_realloc( dbuf->data, new_allocated_size )) ) {
78 			vips_error( "VipsDbuf", "%s", _( "out of memory" ) );
79 			return( FALSE );
80 		}
81 
82 		dbuf->data = new_data;
83 		dbuf->allocated_size = new_allocated_size;
84 	}
85 
86 	return( TRUE );
87 }
88 
89 /**
90  * vips_dbuf_allocate:
91  * @dbuf: the buffer
92  * @size: the size to allocate
93  *
94  * Make sure @dbuf has at least @size bytes available after the write point.
95  *
96  * Returns: %FALSE on out of memory, %TRUE otherwise.
97  */
98 gboolean
vips_dbuf_allocate(VipsDbuf * dbuf,size_t size)99 vips_dbuf_allocate( VipsDbuf *dbuf, size_t size )
100 {
101 	return( vips_dbuf_minimum_size( dbuf, dbuf->write_point + size ) );
102 }
103 
104 /**
105  * vips_dbuf_read:
106  * @dbuf: the buffer
107  * @data: read to this area
108  * @size: read up to this many bytes
109  *
110  * Up to @size bytes are read from the buffer and copied to @data. The number
111  * of bytes transferred is returned.
112  *
113  * Returns: the number of bytes transferred.
114  */
115 size_t
vips_dbuf_read(VipsDbuf * dbuf,unsigned char * data,size_t size)116 vips_dbuf_read( VipsDbuf *dbuf, unsigned char *data, size_t size )
117 {
118 	const size_t available = dbuf->data_size - dbuf->write_point;
119 	const size_t copied = VIPS_MIN( size, available );
120 
121 	memcpy( data, dbuf->data + dbuf->write_point, copied );
122 	dbuf->write_point += copied;
123 
124 	return( copied );
125 }
126 
127 /**
128  * vips_dbuf_get_write:
129  * @dbuf: the buffer
130  * @size: (allow-none): optionally return length in bytes here
131  *
132  * Return a pointer to an area you can write to, return length of area in
133  * @size. Use vips_dbuf_allocate() before this call to set a minimum amount of
134  * space to have available.
135  *
136  * The write point moves to just beyond the returned block. Use
137  * vips_dbuf_seek() to move it back again.
138  *
139  * Returns: (transfer none): start of write area.
140  */
141 unsigned char *
vips_dbuf_get_write(VipsDbuf * dbuf,size_t * size)142 vips_dbuf_get_write( VipsDbuf *dbuf, size_t *size )
143 {
144 	unsigned char *write = dbuf->data + dbuf->write_point;
145 	const size_t available = dbuf->allocated_size - dbuf->write_point;
146 
147 	memset( write, 0, available );
148 	dbuf->write_point = dbuf->allocated_size;
149 	dbuf->data_size = dbuf->allocated_size;
150 
151 	if( size )
152 		*size = available;
153 
154 	return( write );
155 }
156 
157 /**
158  * vips_dbuf_write:
159  * @dbuf: the buffer
160  * @data: the data to write to the buffer
161  * @size: the size of the len to write
162  *
163  * Append @size bytes from @data. @dbuf expands if necessary.
164  *
165  * Returns: %FALSE on out of memory, %TRUE otherwise.
166  */
167 gboolean
vips_dbuf_write(VipsDbuf * dbuf,const unsigned char * data,size_t size)168 vips_dbuf_write( VipsDbuf *dbuf, const unsigned char *data, size_t size )
169 {
170 	if( !vips_dbuf_allocate( dbuf, size ) )
171 		return( FALSE );
172 
173 	memcpy( dbuf->data + dbuf->write_point, data, size );
174 	dbuf->write_point += size;
175 	dbuf->data_size = VIPS_MAX( dbuf->data_size, dbuf->write_point );
176 
177 	return( TRUE );
178 }
179 
180 /**
181  * vips_dbuf_writef:
182  * @dbuf: the buffer
183  * @fmt: <function>printf()</function>-style format string
184  * @...: arguments to format string
185  *
186  * Format the string and write to @dbuf.
187  *
188  * Returns: %FALSE on out of memory, %TRUE otherwise.
189  */
190 gboolean
vips_dbuf_writef(VipsDbuf * dbuf,const char * fmt,...)191 vips_dbuf_writef( VipsDbuf *dbuf, const char *fmt, ... )
192 {
193 	va_list ap;
194 	char *line;
195 
196         va_start( ap, fmt );
197 	line = g_strdup_vprintf( fmt, ap );
198         va_end( ap );
199 
200 	if( vips_dbuf_write( dbuf, (unsigned char *) line, strlen( line ) ) ) {
201 		g_free( line );
202 		return( FALSE );
203 	}
204 	g_free( line );
205 
206 	return( TRUE );
207 }
208 
209 /**
210  * vips_dbuf_write_amp:
211  * @dbuf: the buffer
212  * @str: string to write
213  *
214  * Write @str to @dbuf, but escape stuff that xml hates in text. Our
215  * argument string is utf-8.
216  *
217  * XML rules:
218  *
219  * - We must escape &<>
220  * - Don't escape \n, \t, \r
221  * - Do escape the other ASCII codes.
222  *
223  * Returns: %FALSE on out of memory, %TRUE otherwise.
224  */
225 gboolean
vips_dbuf_write_amp(VipsDbuf * dbuf,const char * str)226 vips_dbuf_write_amp( VipsDbuf *dbuf, const char *str )
227 {
228 	const char *p;
229 
230 	for( p = str; *p; p++ )
231 		if( *p < 32 &&
232 			*p != '\n' &&
233 			*p != '\t' &&
234 			*p != '\r' ) {
235 			/* You'd think we could output "&#x02%x;", but xml
236 			 * 1.0 parsers barf on that. xml 1.1 allows this, but
237 			 * there are almost no parsers.
238 			 *
239 			 * U+2400 onwards are unicode glyphs for the ASCII
240 			 * control characters, so we can use them -- thanks
241 			 * electroly.
242 			 */
243 			if( !vips_dbuf_writef( dbuf, "&#x%04x;", 0x2400 + *p ) )
244 			       return( FALSE );
245 		}
246 		else if( *p == '<' ) {
247 			if( !vips_dbuf_write( dbuf, (guchar *) "&lt;", 4 ) )
248 				return( FALSE );
249 		}
250 		else if( *p == '>' ) {
251 			if( !vips_dbuf_write( dbuf, (guchar *) "&gt;", 4 ) )
252 				return( FALSE );
253 		}
254 		else if( *p == '&' ) {
255 			if( !vips_dbuf_write( dbuf, (guchar *) "&amp;", 5 ) )
256 				return( FALSE );
257 		}
258 		else  {
259 			if( !vips_dbuf_write( dbuf, (guchar *) p, 1 ) )
260 				return( FALSE );
261 		}
262 
263 	return( TRUE );
264 }
265 
266 /**
267  * vips_dbuf_reset:
268  * @dbuf: the buffer
269  *
270  * Reset the buffer to empty. No memory is freed, just the data size and
271  * write point are reset.
272  */
273 void
vips_dbuf_reset(VipsDbuf * dbuf)274 vips_dbuf_reset( VipsDbuf *dbuf )
275 {
276 	dbuf->write_point = 0;
277 	dbuf->data_size = 0;
278 }
279 
280 /**
281  * vips_dbuf_destroy:
282  * @dbuf: the buffer
283  *
284  * Destroy @dbuf. This frees any allocated memory.
285  */
286 void
vips_dbuf_destroy(VipsDbuf * dbuf)287 vips_dbuf_destroy( VipsDbuf *dbuf )
288 {
289 	vips_dbuf_reset( dbuf );
290 
291 	VIPS_FREE( dbuf->data );
292 	dbuf->allocated_size = 0;
293 }
294 
295 /**
296  * vips_dbuf_seek:
297  * @dbuf: the buffer
298  * @offset: how to move the write point
299  * @whence: from start, from end, from current
300  *
301  * Move the write point. @whence can be %SEEK_SET, %SEEK_CUR, %SEEK_END, with
302  * the usual meaning.
303  */
304 gboolean
vips_dbuf_seek(VipsDbuf * dbuf,off_t offset,int whence)305 vips_dbuf_seek( VipsDbuf *dbuf, off_t offset, int whence )
306 {
307 	off_t new_write_point;
308 
309 	switch( whence ) {
310 	case SEEK_SET:
311 		new_write_point = offset;
312 		break;
313 
314 	case SEEK_END:
315 		new_write_point = dbuf->data_size + offset;
316 		break;
317 
318 	case SEEK_CUR:
319 		new_write_point = dbuf->write_point + offset;
320 		break;
321 
322 	default:
323 		g_assert( 0 );
324 		new_write_point = dbuf->write_point;
325 		break;
326 	}
327 
328 	if( new_write_point < 0 ) {
329 		vips_error( "VipsDbuf", "%s", "negative seek" );
330 		return( FALSE );
331 	}
332 
333 	/* Possibly need to grow the buffer
334 	 */
335 	if( !vips_dbuf_minimum_size( dbuf, new_write_point ) )
336 		return( FALSE );
337 	dbuf->write_point = new_write_point;
338 	if( dbuf->data_size < dbuf->write_point ) {
339 		memset( dbuf->data + dbuf->data_size, 0,
340 			dbuf->write_point - dbuf->data_size );
341 		dbuf->data_size = dbuf->write_point;
342 	}
343 
344 	return( TRUE );
345 }
346 
347 /**
348  * vips_dbuf_truncate:
349  * @dbuf: the buffer
350  *
351  * Truncate the data so that it ends at the write point. No memory is freed.
352  */
353 void
vips_dbuf_truncate(VipsDbuf * dbuf)354 vips_dbuf_truncate( VipsDbuf *dbuf )
355 {
356 	dbuf->data_size = dbuf->write_point;
357 }
358 
359 /**
360  * vips_dbuf_tell:
361  * @dbuf: the buffer
362  *
363  * Returns: the current write point
364  */
365 off_t
vips_dbuf_tell(VipsDbuf * dbuf)366 vips_dbuf_tell( VipsDbuf *dbuf )
367 {
368 	return( dbuf->write_point );
369 }
370 
371 /**
372  * vips_dbuf_null_terminate:
373  * @dbuf: the buffer
374  *
375  * Make sure the byte after the last data byte is `\0`. This extra byte is not
376  * included in the data size and the write point is not moved.
377  *
378  * This makes it safe to treat the dbuf contents as a C string.
379  *
380  * Returns: %FALSE on out of memory, %TRUE otherwise.
381  */
382 static gboolean
vips_dbuf_null_terminate(VipsDbuf * dbuf)383 vips_dbuf_null_terminate( VipsDbuf *dbuf )
384 {
385 	if( !vips_dbuf_minimum_size( dbuf, dbuf->data_size + 1 ) )
386 		return( FALSE );
387 
388 	dbuf->data[dbuf->data_size] = 0;
389 
390 	return( TRUE );
391 }
392 
393 /**
394  * vips_dbuf_steal:
395  * @dbuf: the buffer
396  * @size: (allow-none): optionally return length in bytes here
397  *
398  * Destroy a buffer, but rather than freeing memory, a pointer is returned.
399  * This must be freed with g_free().
400  *
401  * A `\0` is appended, but not included in the character count. This is so the
402  * pointer can be safely treated as a C string.
403  *
404  * Returns: (transfer full): The pointer held by @dbuf.
405  */
406 unsigned char *
vips_dbuf_steal(VipsDbuf * dbuf,size_t * size)407 vips_dbuf_steal( VipsDbuf *dbuf, size_t *size )
408 {
409 	unsigned char *data;
410 
411 	vips_dbuf_null_terminate( dbuf );
412 
413 	data = dbuf->data;
414 
415 	if( size )
416 		*size = dbuf->data_size;
417 
418 	dbuf->data = NULL;
419 	vips_dbuf_destroy( dbuf );
420 
421 	return( data );
422 }
423 
424 /**
425  * vips_dbuf_string:
426  * @dbuf: the buffer
427  * @size: (allow-none): optionally return length in bytes here
428  *
429  * Return a pointer to @dbuf's internal data.
430  *
431  * A `\0` is appended, but not included in the character count. This is so the
432  * pointer can be safely treated as a C string.
433  *
434  * Returns: (transfer none): The pointer held by @dbuf.
435  */
436 unsigned char *
vips_dbuf_string(VipsDbuf * dbuf,size_t * size)437 vips_dbuf_string( VipsDbuf *dbuf, size_t *size )
438 {
439 	vips_dbuf_null_terminate( dbuf );
440 
441 	if( size )
442 		*size = dbuf->data_size;
443 
444 	return( dbuf->data );
445 }
446 
447 
448