Bug 22803 - Make header conform to implementation
[swfdec.git] / swfdec / swfdec_buffer.c
blob3112f475b9d4cf31250f16758f0e89b86c93fcf6
1 /* Swfdec
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
22 #ifndef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
26 #include <swfdec_buffer.h>
27 #include <liboil/liboil.h>
28 #include <glib.h>
29 #include <string.h>
30 #include <swfdec_debug.h>
31 #include <swfdec_internal.h> /* for buffer_queue_pull_text */
33 /*** gtk-doc ***/
35 /**
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 ***/
48 /**
49 * 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.
63 /**
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.
72 GType
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
79 ("SwfdecBuffer",
80 (GBoxedCopyFunc) swfdec_buffer_ref,
81 (GBoxedFreeFunc) swfdec_buffer_unref);
83 return type_swfdec_buffer;
86 /**
87 * swfdec_buffer_new:
88 * @size: amount of bytes to allocate
90 * Creates a new buffer and allocates new memory of @size bytes to be used with
91 * the buffer.
93 * Returns: a new #SwfdecBuffer with buffer->data pointing to new data
94 **/
95 SwfdecBuffer *
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
111 SwfdecBuffer *
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
127 SwfdecBuffer *
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:
137 * @data: static data
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
157 SwfdecBuffer *
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;
165 buffer->data = data;
166 buffer->length = size;
167 buffer->free = free_func;
168 buffer->priv = priv;
170 return buffer;
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
180 * to by @buffer.
182 * Returns: a new #SwfdecBuffer managing the indicated region.
184 SwfdecBuffer *
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)));
199 return subbuffer;
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
211 * by @buffer.
213 SwfdecBuffer *
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;
221 return buffer;
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
235 SwfdecBuffer *
236 swfdec_buffer_new_from_file (const char *filename, GError **error)
238 GMappedFile *file;
239 char *data;
240 gsize length;
242 g_return_val_if_fail (filename != NULL, NULL);
244 file = g_mapped_file_new (filename, FALSE, NULL);
245 if (file != 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))
252 return NULL;
254 return swfdec_buffer_new_for_data ((guint8 *) data, length);
258 * swfdec_buffer_ref:
259 * @buffer: a #SwfdecBuffer
261 * increases the reference count of @buffer by one.
263 * Returns: The passed in @buffer.
265 SwfdecBuffer *
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);
271 buffer->ref_count++;
272 return buffer;
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.
282 void
283 swfdec_buffer_unref (SwfdecBuffer * buffer)
285 g_return_if_fail (buffer != NULL);
286 g_return_if_fail (buffer->ref_count > 0);
288 buffer->ref_count--;
289 if (buffer->ref_count == 0) {
290 if (buffer->free)
291 buffer->free (buffer->priv, buffer->data);
292 g_slice_free (SwfdecBuffer, buffer);
296 /*** SwfdecBufferQueue ***/
299 * 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
304 * size.
307 GType
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.
328 SwfdecBufferQueue *
329 swfdec_buffer_queue_new (void)
331 SwfdecBufferQueue *buffer_queue;
333 buffer_queue = g_new0 (SwfdecBufferQueue, 1);
334 buffer_queue->ref_count = 1;
335 return buffer_queue;
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.
346 gsize
347 swfdec_buffer_queue_get_depth (SwfdecBufferQueue * queue)
349 g_return_val_if_fail (queue != NULL, 0);
351 return queue->depth;
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.
363 gsize
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.
378 void
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);
395 } else {
396 queue->first_buffer->data = swfdec_buffer_new_subbuffer (buffer,
397 n_bytes, buffer->length - n_bytes);
398 n_bytes = 0;
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.
413 void
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;
422 queue->depth = 0;
423 queue->offset = 0;
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.
435 void
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);
443 return;
445 queue->last_buffer = g_slist_append (queue->last_buffer, buffer);
446 if (queue->first_buffer == NULL) {
447 queue->first_buffer = queue->last_buffer;
448 } else {
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.
465 SwfdecBuffer *
466 swfdec_buffer_queue_peek (SwfdecBufferQueue * queue, gsize length)
468 GSList *g;
469 SwfdecBuffer *newbuffer;
470 SwfdecBuffer *buffer;
472 g_return_val_if_fail (queue != NULL, NULL);
474 if (queue->depth < length)
475 return NULL;
477 SWFDEC_LOG ("peeking %zu, %zu available", length, queue->depth);
479 /* need to special case here, because the queue may be empty */
480 if (length == 0)
481 return swfdec_buffer_new (0);
483 g = queue->first_buffer;
484 buffer = g->data;
485 if (buffer->length >= length) {
486 newbuffer = swfdec_buffer_new_subbuffer (buffer, 0, length);
487 } else {
488 gsize amount, offset;
489 newbuffer = swfdec_buffer_new (length);
490 offset = 0;
491 while (offset < length) {
492 buffer = g->data;
493 amount = MIN (length - offset, buffer->length);
494 oil_copy_u8 (newbuffer->data + offset, buffer->data, amount);
495 offset += amount;
496 g = g->next;
500 return newbuffer;
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
511 * is returned.
513 * Returns: a new #SwfdecBuffer or %NULL
515 SwfdecBuffer *
516 swfdec_buffer_queue_pull (SwfdecBufferQueue * queue, gsize length)
518 SwfdecBuffer *ret;
520 g_return_val_if_fail (queue != NULL, NULL);
522 ret = swfdec_buffer_queue_peek (queue, length);
523 if (ret == NULL)
524 return NULL;
526 swfdec_buffer_queue_flush (queue, length);
527 return ret;
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.
541 SwfdecBuffer *
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)
549 return 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.
567 SwfdecBuffer *
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);
575 if (buffer)
576 swfdec_buffer_queue_flush (queue, buffer->length);
578 return buffer;
582 * swfdec_buffer_queue_ref:
583 * @queue: a #SwfdecBufferQueue
585 * increases the reference count of @queue by one.
587 * Returns: The passed in @queue.
589 SwfdecBufferQueue *
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);
595 queue->ref_count++;
596 return queue;
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.
607 void
608 swfdec_buffer_queue_unref (SwfdecBufferQueue * queue)
610 g_return_if_fail (queue != NULL);
611 g_return_if_fail (queue->ref_count > 0);
613 queue->ref_count--;
614 if (queue->ref_count == 0) {
615 swfdec_buffer_queue_clear (queue);
616 g_free (queue);
620 typedef struct {
621 const char *name;
622 guint length;
623 guchar data[4];
624 } ByteOrderMark;
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} }
633 char *
634 swfdec_buffer_queue_pull_text (SwfdecBufferQueue *queue, guint version)
636 SwfdecBuffer *buffer;
637 char *text;
638 guint size, i, j;
640 size = swfdec_buffer_queue_get_depth (queue);
641 if (size == 0) {
642 SWFDEC_LOG ("empty loader, returning empty string");
643 return g_strdup ("");
646 buffer = swfdec_buffer_queue_pull (queue, size);
647 g_assert (buffer);
649 if (version > 5) {
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)
653 continue;
655 for (j = 0; j < boms[i].length; j++) {
656 if (buffer->data[j] != boms[i].data[j])
657 break;
659 if (j == boms[i].length)
660 break;
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");
667 text = NULL;
668 } else {
669 text =
670 g_strndup ((char *)buffer->data + boms[i].length,
671 size - boms[i].length);
673 } else {
674 text = g_convert ((char *)buffer->data + boms[i].length,
675 size - boms[i].length, "UTF-8", boms[i].name, NULL, NULL, NULL);
676 if (text == NULL)
677 SWFDEC_ERROR ("downloaded data is not valid %s", boms[i].name);
679 } else {
680 text = g_convert ((char *)buffer->data, size, "UTF-8", "LATIN1", NULL,
681 NULL, NULL);
682 if (text == NULL)
683 SWFDEC_ERROR ("downloaded data is not valid LATIN1");
686 swfdec_buffer_unref (buffer);
688 return text;