1 /* GdkPixbuf library
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>
5  *
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 of the License, or (at your option) any later version.
10  *
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.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #include "config.h"
21 
22 #include "gdk-pixbuf-buffer-queue-private.h"
23 
24 #include <string.h>
25 
26 struct _GdkPixbufBufferQueue
27 {
28   GSList *	first_buffer;		/* pointer to first buffer */
29   GSList *	last_buffer;		/* pointer to last buffer (for fast appending) */
30   gsize		size;			/* amount of bytes in the queue */
31   gsize		offset;			/* amount of data already flushed out of the queue */
32 
33   int		ref_count;
34 };
35 
36 /**
37  * GdkPixbufBufferQueue:
38  *
39  * A #GdkPixbufBufferQueue is a queue of continuous buffers that allows reading
40  * its data in chunks of pre-defined sizes. It is used to transform a data
41  * stream that was provided by buffers of random sizes to buffers of the right
42  * size.
43  */
44 
45 /**
46  * gdk_pixbuf_buffer_queue_new:
47  *
48  * Creates a new empty buffer queue.
49  *
50  * Returns: a new buffer queue. Use gdk_pixbuf_buffer_queue_unref () to free it.
51  **/
52 GdkPixbufBufferQueue *
gdk_pixbuf_buffer_queue_new(void)53 gdk_pixbuf_buffer_queue_new (void)
54 {
55   GdkPixbufBufferQueue *buffer_queue;
56 
57   buffer_queue = g_new0 (GdkPixbufBufferQueue, 1);
58   buffer_queue->ref_count = 1;
59 
60   return buffer_queue;
61 }
62 
63 /**
64  * gdk_pixbuf_buffer_queue_get_size:
65  * @queue: a #GdkPixbufBufferQueue
66  *
67  * Returns the number of bytes currently in @queue.
68  *
69  * Returns: amount of bytes in @queue.
70  **/
71 gsize
gdk_pixbuf_buffer_queue_get_size(GdkPixbufBufferQueue * queue)72 gdk_pixbuf_buffer_queue_get_size (GdkPixbufBufferQueue *queue)
73 {
74   g_return_val_if_fail (queue != NULL, 0);
75 
76   return queue->size;
77 }
78 
79 /**
80  * gdk_pixbuf_buffer_queue_get_offset:
81  * @queue: a #GdkPixbufBufferQueue
82  *
83  * Queries the amount of bytes that has already been pulled out of
84  * @queue using functions like gdk_pixbuf_buffer_queue_pull().
85  *
86  * Returns: Number of bytes that were already pulled from this queue.
87  **/
88 gsize
gdk_pixbuf_buffer_queue_get_offset(GdkPixbufBufferQueue * queue)89 gdk_pixbuf_buffer_queue_get_offset (GdkPixbufBufferQueue * queue)
90 {
91   g_return_val_if_fail (queue != NULL, 0);
92 
93   return queue->offset;
94 }
95 
96 /**
97  * gdk_pixbuf_buffer_queue_flush:
98  * @queue: a #GdkPixbufBufferQueue
99  * @n_bytes: amount of bytes to flush from the queue
100  *
101  * Removes the first @n_bytes bytes from the queue.
102  */
103 void
gdk_pixbuf_buffer_queue_flush(GdkPixbufBufferQueue * queue,gsize n_bytes)104 gdk_pixbuf_buffer_queue_flush (GdkPixbufBufferQueue *queue, gsize n_bytes)
105 {
106   g_return_if_fail (queue != NULL);
107   g_return_if_fail (n_bytes <= queue->size);
108 
109   queue->size -= n_bytes;
110   queue->offset += n_bytes;
111 
112   while (n_bytes > 0)
113     {
114       GBytes *bytes;
115       gsize size;
116 
117       bytes = queue->first_buffer->data;
118       size = g_bytes_get_size (bytes);
119 
120       if (size <= n_bytes)
121         {
122           n_bytes -= size;
123           queue->first_buffer = g_slist_remove (queue->first_buffer, bytes);
124         }
125       else
126         {
127           queue->first_buffer->data = g_bytes_new_from_bytes (bytes,
128 	                                                      n_bytes,
129                                                               size - n_bytes);
130           n_bytes = 0;
131         }
132       g_bytes_unref (bytes);
133     }
134 
135   if (queue->first_buffer == NULL)
136     queue->last_buffer = NULL;
137 }
138 
139 /**
140  * gdk_pixbuf_buffer_queue_clear:
141  * @queue: a #GdkPixbufBufferQueue
142  *
143  * Resets @queue into to initial state. All buffers it contains will be
144  * released and the offset will be reset to 0.
145  **/
146 void
gdk_pixbuf_buffer_queue_clear(GdkPixbufBufferQueue * queue)147 gdk_pixbuf_buffer_queue_clear (GdkPixbufBufferQueue *queue)
148 {
149   g_return_if_fail (queue != NULL);
150 
151   g_slist_free_full (queue->first_buffer, (GDestroyNotify) g_bytes_unref);
152   queue->first_buffer = NULL;
153   queue->last_buffer = NULL;
154   queue->size = 0;
155   queue->offset = 0;
156 }
157 
158 /**
159  * gdk_pixbuf_buffer_queue_push:
160  * @queue: a #GdkPixbufBufferQueue
161  * @bytes: #GBytes to append to @queue
162  *
163  * Appends the given @bytes to the buffers already in @queue. This function
164  * will take ownership of the given @buffer. Use g_bytes_ref () before
165  * calling this function to keep a reference.
166  **/
167 void
gdk_pixbuf_buffer_queue_push(GdkPixbufBufferQueue * queue,GBytes * bytes)168 gdk_pixbuf_buffer_queue_push (GdkPixbufBufferQueue *queue,
169                               GBytes               *bytes)
170 {
171   gsize size;
172 
173   g_return_if_fail (queue != NULL);
174   g_return_if_fail (bytes != NULL);
175 
176   size = g_bytes_get_size (bytes);
177   if (size == 0)
178     {
179       g_bytes_unref (bytes);
180       return;
181     }
182 
183   queue->last_buffer = g_slist_append (queue->last_buffer, bytes);
184   if (queue->first_buffer == NULL)
185     queue->first_buffer = queue->last_buffer;
186   else
187     queue->last_buffer = queue->last_buffer->next;
188 
189   queue->size += size;
190 }
191 
192 /**
193  * gdk_pixbuf_buffer_queue_peek:
194  * @queue: a #GdkPixbufBufferQueue to read from
195  * @length: amount of bytes to peek
196  *
197  * Creates a new buffer with the first @length bytes from @queue, but unlike
198  * gdk_pixbuf_buffer_queue_pull(), does not remove them from @queue.
199  *
200  * Returns: NULL if the requested amount of data wasn't available or a new
201  *          #GBytes. Use g_bytes_unref() after use.
202  **/
203 GBytes *
gdk_pixbuf_buffer_queue_peek(GdkPixbufBufferQueue * queue,gsize length)204 gdk_pixbuf_buffer_queue_peek (GdkPixbufBufferQueue *queue,
205                               gsize                 length)
206 {
207   GSList *g;
208   GBytes *result, *bytes;
209 
210   g_return_val_if_fail (queue != NULL, NULL);
211 
212   if (queue->size < length)
213     return NULL;
214 
215   /* need to special case here, because the queue may be empty */
216   if (length == 0)
217     return g_bytes_new (NULL, 0);
218 
219   g = queue->first_buffer;
220   bytes = g->data;
221   if (g_bytes_get_size (bytes) == length)
222     {
223       result = g_bytes_ref (bytes);
224     }
225   else if (g_bytes_get_size (bytes) > length)
226     {
227       result = g_bytes_new_from_bytes (bytes, 0, length);
228     }
229   else
230     {
231       guchar *data;
232       gsize amount, offset;
233 
234       data = g_malloc (length);
235 
236       for (offset = 0; offset < length; offset += amount)
237         {
238           bytes = g->data;
239           amount = MIN (length - offset, g_bytes_get_size (bytes));
240           memcpy (data + offset, g_bytes_get_data (bytes, NULL), amount);
241           g = g->next;
242         }
243 
244       result = g_bytes_new_take (data, length);
245     }
246 
247   return result;
248 }
249 
250 /**
251  * gdk_pixbuf_buffer_queue_pull:
252  * @queue: a #GdkPixbufBufferQueue
253  * @length: amount of bytes to pull
254  *
255  * If enough data is still available in @queue, the first @length bytes are
256  * put into a new buffer and that buffer is returned. The @length bytes are
257  * removed from the head of the queue. If not enough data is available, %NULL
258  * is returned.
259  *
260  * Returns: a new #GBytes or %NULL
261  **/
262 GBytes *
gdk_pixbuf_buffer_queue_pull(GdkPixbufBufferQueue * queue,gsize length)263 gdk_pixbuf_buffer_queue_pull (GdkPixbufBufferQueue * queue, gsize length)
264 {
265   GBytes *result;
266 
267   g_return_val_if_fail (queue != NULL, NULL);
268 
269   result = gdk_pixbuf_buffer_queue_peek (queue, length);
270   if (result == NULL)
271     return NULL;
272 
273   gdk_pixbuf_buffer_queue_flush (queue, length);
274 
275   return result;
276 }
277 
278 /**
279  * gdk_pixbuf_buffer_queue_peek_buffer:
280  * @queue: a #GdkPixbufBufferQueue
281  *
282  * Gets the first buffer out of @queue and returns it. This function is
283  * equivalent to calling gdk_pixbuf_buffer_queue_peek() with the size of the
284  * first buffer in it.
285  *
286  * Returns: The first buffer in @queue or %NULL if @queue is empty. Use
287  *          g_bytes_unref() after use.
288  **/
289 GBytes *
gdk_pixbuf_buffer_queue_peek_buffer(GdkPixbufBufferQueue * queue)290 gdk_pixbuf_buffer_queue_peek_buffer (GdkPixbufBufferQueue * queue)
291 {
292   GBytes *bytes;
293 
294   g_return_val_if_fail (queue != NULL, NULL);
295 
296   if (queue->first_buffer == NULL)
297     return NULL;
298 
299   bytes = queue->first_buffer->data;
300 
301   return g_bytes_ref (bytes);
302 }
303 
304 /**
305  * gdk_pixbuf_buffer_queue_pull_buffer:
306  * @queue: a #GdkPixbufBufferQueue
307  *
308  * Pulls the first buffer out of @queue and returns it. This function is
309  * equivalent to calling gdk_pixbuf_buffer_queue_pull() with the size of the
310  * first buffer in it.
311  *
312  * Returns: The first buffer in @queue or %NULL if @queue is empty.
313  **/
314 GBytes *
gdk_pixbuf_buffer_queue_pull_buffer(GdkPixbufBufferQueue * queue)315 gdk_pixbuf_buffer_queue_pull_buffer (GdkPixbufBufferQueue *queue)
316 {
317   GBytes *bytes;
318 
319   g_return_val_if_fail (queue != NULL, NULL);
320 
321   bytes = gdk_pixbuf_buffer_queue_peek_buffer (queue);
322   if (bytes)
323     gdk_pixbuf_buffer_queue_flush (queue, g_bytes_get_size (bytes));
324 
325   return bytes;
326 }
327 
328 /**
329  * gdk_pixbuf_buffer_queue_ref:
330  * @queue: a #GdkPixbufBufferQueue
331  *
332  * increases the reference count of @queue by one.
333  *
334  * Returns: The passed in @queue.
335  **/
336 GdkPixbufBufferQueue *
gdk_pixbuf_buffer_queue_ref(GdkPixbufBufferQueue * queue)337 gdk_pixbuf_buffer_queue_ref (GdkPixbufBufferQueue * queue)
338 {
339   g_return_val_if_fail (queue != NULL, NULL);
340   g_return_val_if_fail (queue->ref_count > 0, NULL);
341 
342   queue->ref_count++;
343   return queue;
344 }
345 
346 /**
347  * gdk_pixbuf_buffer_queue_unref:
348  * @queue: a #GdkPixbufBufferQueue
349  *
350  * Decreases the reference count of @queue by one. If no reference
351  * to this buffer exists anymore, the buffer and the memory
352  * it manages are freed.
353  **/
354 void
gdk_pixbuf_buffer_queue_unref(GdkPixbufBufferQueue * queue)355 gdk_pixbuf_buffer_queue_unref (GdkPixbufBufferQueue * queue)
356 {
357   g_return_if_fail (queue != NULL);
358   g_return_if_fail (queue->ref_count > 0);
359 
360   queue->ref_count--;
361   if (queue->ref_count > 0)
362     return;
363 
364   gdk_pixbuf_buffer_queue_clear (queue);
365   g_free (queue);
366 }
367 
368