2 * Copyright (C) 2003-2006 David Schleef <ds@schleef.org>
3 * 2005-2006 Eric Anholt <eric@anholt.net>
4 * 2006-2007 Benjamin Otte <otte@gnome.org>
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301 USA
26 #include <swfdec_buffer.h>
27 #include <liboil/liboil.h>
30 #include <swfdec_debug.h>
31 #include <swfdec_internal.h> /* for buffer_queue_pull_text */
36 * SECTION:SwfdecBuffer
37 * @title: SwfdecBuffer
38 * @short_description: memory region handling
40 * This section describes how memory is to be handled when interacting with the
41 * Swfdec library. Memory regions are refcounted and passed using a
42 * #SwfdecBuffer. If large memory segments need to be handled that may span
43 * multiple buffers, Swfdec uses a #SwfdecBufferQueue.
46 /*** SwfdecBuffer ***/
50 * @data: the data. read-only
51 * @length: number of bytes in @data. read-only
53 * To allow for easy sharing of memory regions, #SwfdecBuffer was created.
54 * Every buffer refers to a memory region and its size and takes care of
55 * freeing that region when the buffer is no longer needed. They are
56 * reference countedto make it easy to refer to the same region from various
57 * independant parts of your code. Buffers also support some advanced
58 * functionalities like extracting parts of the buffer using
59 * swfdec_buffer_new_subbuffer() or using mmapped files with
60 * swfdec_buffer_new_from_file() without the need for a different API.
64 * SwfdecBufferFreeFunc:
65 * @priv: The private data registered for passing to this function
66 * @data: The data to free
68 * This is the function prototype for the function that is called for freeing
69 * the memory pointed to by a buffer. See swfdec_buffer_new() for an example.
73 swfdec_buffer_get_type (void)
75 static GType type_swfdec_buffer
= 0;
77 if (!type_swfdec_buffer
)
78 type_swfdec_buffer
= g_boxed_type_register_static
80 (GBoxedCopyFunc
) swfdec_buffer_ref
,
81 (GBoxedFreeFunc
) swfdec_buffer_unref
);
83 return type_swfdec_buffer
;
88 * @size: amount of bytes to allocate
90 * Creates a new buffer and allocates new memory of @size bytes to be used with
93 * Returns: a new #SwfdecBuffer with buffer->data pointing to new data
96 swfdec_buffer_new (gsize size
)
98 unsigned char *data
= g_malloc (size
);
99 return swfdec_buffer_new_full (data
, size
, (SwfdecBufferFreeFunc
) g_free
, data
);
103 * swfdec_buffer_new0:
104 * @size: amount of bytes to allocate
106 * Createsa new buffer just like swfdec_buffer_new(), but ensures
107 * that the returned data gets initialized to be 0.
109 * Returns: a new #SwfdecBuffer with buffer->data pointing to new data
112 swfdec_buffer_new0 (gsize size
)
114 unsigned char *data
= g_malloc0 (size
);
115 return swfdec_buffer_new_full (data
, size
, (SwfdecBufferFreeFunc
) g_free
, data
);
119 * swfdec_buffer_new_for_data:
120 * @data: memory region allocated with g_malloc()
121 * @size: size of @data in bytes
123 * Takes ownership of @data and creates a new buffer managing it.
125 * Returns: a new #SwfdecBuffer pointing to @data
128 swfdec_buffer_new_for_data (guchar
*data
, gsize size
)
130 /* This is not a macro because a macro would evaluate the data pointer twice
131 * and people like doing swfdec_buffer_new_for_data (g_malloc (10), 10);
133 return swfdec_buffer_new_full (data
, size
, (SwfdecBufferFreeFunc
) g_free
, data
);
136 * swfdec_buffer_new_static:
138 * @size: size of @data in bytes
140 * Creates a buffer for static data.
142 * Returns: a new #SwfdecBuffer pointing to @data
145 * swfdec_buffer_new_full:
146 * @data: memory region to reference
147 * @size: size of the provided memory region
148 * @free_func: function to call for freeing the @data
149 * @priv: private data to bass to @free_func
151 * Creates a new #SwfdecBuffer for managing @data. The provided @free_func
152 * will be called when the returned buffer is not referenced anymore, the
153 * provided data needs to stay valid until that point.
155 * Returns: a new #SwfdecBuffer pointing to @data
158 swfdec_buffer_new_full (unsigned char *data
, gsize size
,
159 SwfdecBufferFreeFunc free_func
, gpointer priv
)
161 SwfdecBuffer
*buffer
;
163 buffer
= g_slice_new (SwfdecBuffer
);
164 buffer
->ref_count
= 1;
166 buffer
->length
= size
;
167 buffer
->free
= free_func
;
174 * swfdec_buffer_new_subbuffer:
175 * @buffer: #SwfdecBuffer managing the region of memory
176 * @offset: starting offset into data
177 * @length: amount of bytes to manage
179 * Creates a #SwfdecBuffer for managing a partial section of the memory pointed
182 * Returns: a new #SwfdecBuffer managing the indicated region.
185 swfdec_buffer_new_subbuffer (SwfdecBuffer
*buffer
, gsize offset
, gsize length
)
187 SwfdecBuffer
*subbuffer
;
189 g_return_val_if_fail (buffer
!= NULL
, NULL
);
190 g_return_val_if_fail (offset
+ length
<= buffer
->length
, NULL
);
192 if (offset
== 0 && length
== buffer
->length
)
193 return swfdec_buffer_ref (buffer
);
195 subbuffer
= swfdec_buffer_new_full (buffer
->data
+ offset
, length
,
196 (SwfdecBufferFreeFunc
) swfdec_buffer_unref
,
197 swfdec_buffer_ref (swfdec_buffer_get_super (buffer
)));
203 * swfdec_buffer_get_super:
204 * @buffer: a #SwfdecBuffer
206 * Returns the largest buffer that contains the memory pointed to by @buffer.
207 * This will either be the passed @buffer itself, or if the buffer was created
208 * via swfdec_buffer_new_subbuffer(), the buffer used for that.
210 * Returns: The largest @buffer available that contains the memory pointed to
214 swfdec_buffer_get_super (SwfdecBuffer
*buffer
)
216 g_return_val_if_fail (buffer
!= NULL
, NULL
);
218 if (buffer
->free
== (SwfdecBufferFreeFunc
) swfdec_buffer_unref
)
219 buffer
= buffer
->priv
;
225 * swfdec_buffer_new_from_file:
226 * @filename: file to read, in filename encoding
227 * @error: return location for a #GError or %NULL
229 * Creates a buffer containing the contents of the given file. If loading the
230 * file fails, %NULL is returned and @error is set. The error can be
231 * any of the errors that are valid for g_file_get_contents().
233 * Returns: a new #SwfdecBuffer or %NULL on failure
236 swfdec_buffer_new_from_file (const char *filename
, GError
**error
)
242 g_return_val_if_fail (filename
!= NULL
, NULL
);
244 file
= g_mapped_file_new (filename
, FALSE
, NULL
);
246 return swfdec_buffer_new_full ((guchar
*) g_mapped_file_get_contents (file
),
247 g_mapped_file_get_length (file
),
248 (SwfdecBufferFreeFunc
) g_mapped_file_free
, file
);
251 if (!g_file_get_contents (filename
, &data
, &length
, error
))
254 return swfdec_buffer_new_for_data ((guint8
*) data
, length
);
259 * @buffer: a #SwfdecBuffer
261 * increases the reference count of @buffer by one.
263 * Returns: The passed in @buffer.
266 swfdec_buffer_ref (SwfdecBuffer
* buffer
)
268 g_return_val_if_fail (buffer
!= NULL
, NULL
);
269 g_return_val_if_fail (buffer
->ref_count
> 0, NULL
);
276 * swfdec_buffer_unref:
277 * @buffer: a #SwfdecBuffer
279 * Decreases the reference count of @buffer by one. If no reference to this
280 * buffer exists anymore, the buffer and the memory it manages are freed.
283 swfdec_buffer_unref (SwfdecBuffer
* buffer
)
285 g_return_if_fail (buffer
!= NULL
);
286 g_return_if_fail (buffer
->ref_count
> 0);
289 if (buffer
->ref_count
== 0) {
291 buffer
->free (buffer
->priv
, buffer
->data
);
292 g_slice_free (SwfdecBuffer
, buffer
);
296 /*** SwfdecBufferQueue ***/
301 * A #SwfdecBufferQueue is a queue of continuous buffers that allows reading
302 * its data in chunks of pre-defined sizes. It is used to transform a data
303 * stream that was provided by buffers of random sizes to buffers of the right
308 swfdec_buffer_queue_get_type (void)
310 static GType type_swfdec_buffer_queue
= 0;
312 if (!type_swfdec_buffer_queue
)
313 type_swfdec_buffer_queue
= g_boxed_type_register_static
314 ("SwfdecBufferQueue",
315 (GBoxedCopyFunc
) swfdec_buffer_queue_ref
,
316 (GBoxedFreeFunc
) swfdec_buffer_queue_unref
);
318 return type_swfdec_buffer_queue
;
322 * swfdec_buffer_queue_new:
324 * Creates a new empty buffer queue.
326 * Returns: a new buffer queue. Use swfdec_buffer_queue_unref () to free it.
329 swfdec_buffer_queue_new (void)
331 SwfdecBufferQueue
*buffer_queue
;
333 buffer_queue
= g_new0 (SwfdecBufferQueue
, 1);
334 buffer_queue
->ref_count
= 1;
339 * swfdec_buffer_queue_get_depth:
340 * @queue: a #SwfdecBufferQueue
342 * Returns the number of bytes currently in @queue.
344 * Returns: amount of bytes in @queue.
347 swfdec_buffer_queue_get_depth (SwfdecBufferQueue
* queue
)
349 g_return_val_if_fail (queue
!= NULL
, 0);
355 * swfdec_buffer_queue_get_offset:
356 * @queue: a #SwfdecBufferQueue
358 * Queries the amount of bytes that has already been pulled out of
359 * @queue using functions like swfdec_buffer_queue_pull().
361 * Returns: Number of bytes that were already pulled from this queue.
364 swfdec_buffer_queue_get_offset (SwfdecBufferQueue
* queue
)
366 g_return_val_if_fail (queue
!= NULL
, 0);
368 return queue
->offset
;
372 * swfdec_buffer_queue_flush:
373 * @queue: a #SwfdecBufferQueue
374 * @n_bytes: amount of bytes to flush from the queue
376 * Removes the first @n_bytes bytes from the queue.
379 swfdec_buffer_queue_flush (SwfdecBufferQueue
*queue
, gsize n_bytes
)
381 g_return_if_fail (queue
!= NULL
);
382 g_return_if_fail (n_bytes
<= queue
->depth
);
384 queue
->depth
-= n_bytes
;
385 queue
->offset
+= n_bytes
;
387 SWFDEC_LOG ("flushing %zu bytes (%zu left)", n_bytes
, queue
->depth
);
389 while (n_bytes
> 0) {
390 SwfdecBuffer
*buffer
= queue
->first_buffer
->data
;
392 if (buffer
->length
<= n_bytes
) {
393 n_bytes
-= buffer
->length
;
394 queue
->first_buffer
= g_slist_remove (queue
->first_buffer
, buffer
);
396 queue
->first_buffer
->data
= swfdec_buffer_new_subbuffer (buffer
,
397 n_bytes
, buffer
->length
- n_bytes
);
400 swfdec_buffer_unref (buffer
);
402 if (queue
->first_buffer
== NULL
)
403 queue
->last_buffer
= NULL
;
407 * swfdec_buffer_queue_clear:
408 * @queue: a #SwfdecBufferQueue
410 * Resets @queue into to initial state. All buffers it contains will be
411 * released and the offset will be reset to 0.
414 swfdec_buffer_queue_clear (SwfdecBufferQueue
*queue
)
416 g_return_if_fail (queue
!= NULL
);
418 g_slist_foreach (queue
->first_buffer
, (GFunc
) swfdec_buffer_unref
, NULL
);
419 g_slist_free (queue
->first_buffer
);
420 queue
->first_buffer
= NULL
;
421 queue
->last_buffer
= NULL
;
427 * swfdec_buffer_queue_push:
428 * @queue: a #SwfdecBufferQueue
429 * @buffer: #SwfdecBuffer to append to @queue
431 * Appends the given @buffer to the buffers already in @queue. This function
432 * will take ownership of the given @buffer. Use swfdec_buffer_ref () before
433 * calling this function to keep a reference.
436 swfdec_buffer_queue_push (SwfdecBufferQueue
* queue
, SwfdecBuffer
* buffer
)
438 g_return_if_fail (queue
!= NULL
);
439 g_return_if_fail (buffer
!= NULL
);
441 if (buffer
->length
== 0) {
442 swfdec_buffer_unref (buffer
);
445 queue
->last_buffer
= g_slist_append (queue
->last_buffer
, buffer
);
446 if (queue
->first_buffer
== NULL
) {
447 queue
->first_buffer
= queue
->last_buffer
;
449 queue
->last_buffer
= queue
->last_buffer
->next
;
451 queue
->depth
+= buffer
->length
;
455 * swfdec_buffer_queue_peek:
456 * @queue: a #SwfdecBufferQueue to read from
457 * @length: amount of bytes to peek
459 * Creates a new buffer with the first @length bytes from @queue, but unlike
460 * swfdec_buffer_queue_pull(), does not remove them from @queue.
462 * Returns: NULL if the requested amount of data wasn't available or a new
463 * readonly #SwfdecBuffer. Use swfdec_buffer_unref() after use.
466 swfdec_buffer_queue_peek (SwfdecBufferQueue
* queue
, gsize length
)
469 SwfdecBuffer
*newbuffer
;
470 SwfdecBuffer
*buffer
;
472 g_return_val_if_fail (queue
!= NULL
, NULL
);
474 if (queue
->depth
< length
)
477 SWFDEC_LOG ("peeking %zu, %zu available", length
, queue
->depth
);
479 /* need to special case here, because the queue may be empty */
481 return swfdec_buffer_new (0);
483 g
= queue
->first_buffer
;
485 if (buffer
->length
>= length
) {
486 newbuffer
= swfdec_buffer_new_subbuffer (buffer
, 0, length
);
488 gsize amount
, offset
;
489 newbuffer
= swfdec_buffer_new (length
);
491 while (offset
< length
) {
493 amount
= MIN (length
- offset
, buffer
->length
);
494 oil_copy_u8 (newbuffer
->data
+ offset
, buffer
->data
, amount
);
504 * swfdec_buffer_queue_pull:
505 * @queue: a #SwfdecBufferQueue
506 * @length: amount of bytes to pull
508 * If enough data is still available in @queue, the first @length bytes are
509 * put into a new buffer and that buffer is returned. The @length bytes are
510 * removed from the head of the queue. If not enough data is available, %NULL
513 * Returns: a new #SwfdecBuffer or %NULL
516 swfdec_buffer_queue_pull (SwfdecBufferQueue
* queue
, gsize length
)
520 g_return_val_if_fail (queue
!= NULL
, NULL
);
522 ret
= swfdec_buffer_queue_peek (queue
, length
);
526 swfdec_buffer_queue_flush (queue
, length
);
531 * swfdec_buffer_queue_peek_buffer:
532 * @queue: a #SwfdecBufferQueue
534 * Gets the first buffer out of @queue and returns it. This function is
535 * equivalent to calling swfdec_buffer_queue_peek() with the size of the
536 * first buffer in it.
538 * Returns: The first buffer in @queue or %NULL if @queue is empty. Use
539 * swfdec_buffer_unref() after use.
542 swfdec_buffer_queue_peek_buffer (SwfdecBufferQueue
* queue
)
544 SwfdecBuffer
*buffer
;
546 g_return_val_if_fail (queue
!= NULL
, NULL
);
548 if (queue
->first_buffer
== NULL
)
551 buffer
= queue
->first_buffer
->data
;
552 SWFDEC_LOG ("peeking one buffer: %zu bytes, %zu available", buffer
->length
, queue
->depth
);
554 return swfdec_buffer_ref (buffer
);
558 * swfdec_buffer_queue_pull_buffer:
559 * @queue: a #SwfdecBufferQueue
561 * Pulls the first buffer out of @queue and returns it. This function is
562 * equivalent to calling swfdec_buffer_queue_pull() with the size of the
563 * first buffer in it.
565 * Returns: The first buffer in @queue or %NULL if @queue is empty.
568 swfdec_buffer_queue_pull_buffer (SwfdecBufferQueue
*queue
)
570 SwfdecBuffer
*buffer
;
572 g_return_val_if_fail (queue
!= NULL
, NULL
);
574 buffer
= swfdec_buffer_queue_peek_buffer (queue
);
576 swfdec_buffer_queue_flush (queue
, buffer
->length
);
582 * swfdec_buffer_queue_ref:
583 * @queue: a #SwfdecBufferQueue
585 * increases the reference count of @queue by one.
587 * Returns: The passed in @queue.
590 swfdec_buffer_queue_ref (SwfdecBufferQueue
* queue
)
592 g_return_val_if_fail (queue
!= NULL
, NULL
);
593 g_return_val_if_fail (queue
->ref_count
> 0, NULL
);
600 * swfdec_buffer_queue_unref:
601 * @queue: a #SwfdecBufferQueue
603 * Decreases the reference count of @queue by one. If no reference
604 * to this buffer exists anymore, the buffer and the memory
605 * it manages are freed.
608 swfdec_buffer_queue_unref (SwfdecBufferQueue
* queue
)
610 g_return_if_fail (queue
!= NULL
);
611 g_return_if_fail (queue
->ref_count
> 0);
614 if (queue
->ref_count
== 0) {
615 swfdec_buffer_queue_clear (queue
);
626 static ByteOrderMark boms
[] = {
627 { "UTF-8", 3, {0xEF, 0xBB, 0xBF, 0} },
628 { "UTF-16BE", 2, {0xFE, 0xFF, 0, 0} },
629 { "UTF-16LE", 2, {0xFF, 0xFE, 0, 0} },
630 { "UTF-8", 0, {0, 0, 0, 0} }
634 swfdec_buffer_queue_pull_text (SwfdecBufferQueue
*queue
, guint version
)
636 SwfdecBuffer
*buffer
;
640 size
= swfdec_buffer_queue_get_depth (queue
);
642 SWFDEC_LOG ("empty loader, returning empty string");
643 return g_strdup ("");
646 buffer
= swfdec_buffer_queue_pull (queue
, size
);
650 for (i
= 0; boms
[i
].length
> 0; i
++) {
651 // FIXME: test what happens if we have BOM and nothing else
652 if (size
< boms
[i
].length
)
655 for (j
= 0; j
< boms
[i
].length
; j
++) {
656 if (buffer
->data
[j
] != boms
[i
].data
[j
])
659 if (j
== boms
[i
].length
)
663 if (!strcmp (boms
[i
].name
, "UTF-8")) {
664 if (!g_utf8_validate ((char *)buffer
->data
+ boms
[i
].length
,
665 size
- boms
[i
].length
, NULL
)) {
666 SWFDEC_ERROR ("downloaded data is not valid UTF-8");
670 g_strndup ((char *)buffer
->data
+ boms
[i
].length
,
671 size
- boms
[i
].length
);
674 text
= g_convert ((char *)buffer
->data
+ boms
[i
].length
,
675 size
- boms
[i
].length
, "UTF-8", boms
[i
].name
, NULL
, NULL
, NULL
);
677 SWFDEC_ERROR ("downloaded data is not valid %s", boms
[i
].name
);
680 text
= g_convert ((char *)buffer
->data
, size
, "UTF-8", "LATIN1", NULL
,
683 SWFDEC_ERROR ("downloaded data is not valid LATIN1");
686 swfdec_buffer_unref (buffer
);