zephyr: Remove unused defines and headers.
[pidgin-git.git] / libpurple / circularbuffer.c
blob00d9095a1c6993eed8caa6332cd647f9426d2a10
1 /* Purple is the legal property of its developers, whose names are too numerous
2 * to list here. Please refer to the COPYRIGHT file distributed with this
3 * source distribution.
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
19 #include "internal.h"
20 #include "glibcompat.h"
22 #include "circularbuffer.h"
24 #define DEFAULT_BUF_SIZE 256
26 /******************************************************************************
27 * Structs
28 *****************************************************************************/
29 typedef struct {
30 /** A pointer to the starting address of our chunk of memory. */
31 gchar *buffer;
33 /** The incremental amount to increase this buffer by when
34 * the buffer is not big enough to hold incoming data, in bytes. */
35 gsize growsize;
37 /** The length of this buffer, in bytes. */
38 gsize buflen;
40 /** The number of bytes of this buffer that contain unread data. */
41 gsize bufused;
43 /** A pointer to the next byte where new incoming data is
44 * buffered to. */
45 gchar *input;
47 /** A pointer to the next byte of buffered data that should be
48 * read by the consumer. */
49 gchar *output;
50 } PurpleCircularBufferPrivate;
52 /******************************************************************************
53 * Enums
54 *****************************************************************************/
55 enum {
56 PROP_ZERO,
57 PROP_GROW_SIZE,
58 PROP_BUFFER_USED,
59 PROP_INPUT,
60 PROP_OUTPUT,
61 PROP_LAST,
64 /******************************************************************************
65 * Globals
66 *****************************************************************************/
67 static GParamSpec *properties[PROP_LAST];
69 G_DEFINE_TYPE_WITH_PRIVATE(PurpleCircularBuffer, purple_circular_buffer,
70 G_TYPE_OBJECT);
72 /******************************************************************************
73 * Circular Buffer Implementation
74 *****************************************************************************/
75 static void
76 purple_circular_buffer_real_grow(PurpleCircularBuffer *buffer, gsize len) {
77 PurpleCircularBufferPrivate *priv = NULL;
78 gsize in_offset = 0, out_offset = 0;
79 gsize start_buflen;
80 GObject *obj;
82 priv = purple_circular_buffer_get_instance_private(buffer);
84 start_buflen = priv->buflen;
86 while((priv->buflen - priv->bufused) < len)
87 priv->buflen += priv->growsize;
89 if(priv->input != NULL) {
90 in_offset = priv->input - priv->buffer;
91 out_offset = priv->output - priv->buffer;
94 priv->buffer = g_realloc(priv->buffer, priv->buflen);
96 /* adjust the fill and remove pointer locations */
97 if(priv->input == NULL) {
98 priv->input = priv->output = priv->buffer;
99 } else {
100 priv->input = priv->buffer + in_offset;
101 priv->output = priv->buffer + out_offset;
104 /* If the fill pointer is wrapped to before the remove
105 * pointer, we need to shift the data */
106 if (in_offset < out_offset
107 || (in_offset == out_offset && priv->bufused > 0))
109 gsize shift_n = MIN(priv->buflen - start_buflen, in_offset);
110 memcpy(priv->buffer + start_buflen, priv->buffer, shift_n);
112 /* If we couldn't fit the wrapped read buffer at the end */
113 if (shift_n < in_offset) {
114 memmove(priv->buffer, priv->buffer + shift_n, in_offset - shift_n);
115 priv->input = priv->buffer + (in_offset - shift_n);
116 } else {
117 priv->input = priv->buffer + start_buflen + in_offset;
121 obj = G_OBJECT(buffer);
122 g_object_freeze_notify(obj);
123 g_object_notify_by_pspec(obj, properties[PROP_INPUT]);
124 g_object_notify_by_pspec(obj, properties[PROP_OUTPUT]);
125 g_object_thaw_notify(obj);
128 static void
129 purple_circular_buffer_real_append(PurpleCircularBuffer *buffer,
130 gconstpointer src, gsize len)
132 PurpleCircularBufferPrivate *priv = NULL;
133 gsize len_stored;
134 GObject *obj;
136 priv = purple_circular_buffer_get_instance_private(buffer);
138 /* Grow the buffer, if necessary */
139 if((priv->buflen - priv->bufused) < len)
140 purple_circular_buffer_grow(buffer, len);
142 /* If there is not enough room to copy all of src before hitting
143 * the end of the buffer then we will need to do two copies.
144 * One copy from input to the end of the buffer, and the
145 * second copy from the start of the buffer to the end of src. */
146 if(priv->input >= priv->output)
147 len_stored = MIN(len, priv->buflen - (priv->input - priv->buffer));
148 else
149 len_stored = len;
151 if(len_stored > 0)
152 memcpy(priv->input, src, len_stored);
154 if(len_stored < len) {
155 memcpy(priv->buffer, (char*)src + len_stored, len - len_stored);
156 priv->input = priv->buffer + (len - len_stored);
157 } else {
158 priv->input += len_stored;
161 priv->bufused += len;
163 obj = G_OBJECT(buffer);
164 g_object_freeze_notify(obj);
165 g_object_notify_by_pspec(obj, properties[PROP_BUFFER_USED]);
166 g_object_notify_by_pspec(obj, properties[PROP_INPUT]);
167 g_object_thaw_notify(obj);
170 static gsize
171 purple_circular_buffer_real_max_read_size(PurpleCircularBuffer *buffer) {
172 PurpleCircularBufferPrivate *priv = NULL;
173 gsize max_read;
175 priv = purple_circular_buffer_get_instance_private(buffer);
177 if(priv->bufused == 0)
178 max_read = 0;
179 else if((priv->output - priv->input) >= 0)
180 max_read = priv->buflen - (priv->output - priv->buffer);
181 else
182 max_read = priv->input - priv->output;
184 return max_read;
187 static gboolean
188 purple_circular_buffer_real_mark_read(PurpleCircularBuffer *buffer,
189 gsize len)
191 PurpleCircularBufferPrivate *priv = NULL;
192 GObject *obj;
194 g_return_val_if_fail(purple_circular_buffer_get_max_read(buffer) >= len, FALSE);
196 priv = purple_circular_buffer_get_instance_private(buffer);
198 priv->output += len;
199 priv->bufused -= len;
201 /* wrap to the start if we're at the end */
202 if ((gsize)(priv->output - priv->buffer) == priv->buflen)
203 priv->output = priv->buffer;
205 obj = G_OBJECT(buffer);
206 g_object_freeze_notify(obj);
207 g_object_notify_by_pspec(obj, properties[PROP_BUFFER_USED]);
208 g_object_notify_by_pspec(obj, properties[PROP_OUTPUT]);
209 g_object_thaw_notify(obj);
211 return TRUE;
214 /******************************************************************************
215 * Private API
216 *****************************************************************************/
217 static void
218 purple_circular_buffer_set_grow_size(PurpleCircularBuffer *buffer,
219 gsize grow_size)
221 PurpleCircularBufferPrivate *priv =
222 purple_circular_buffer_get_instance_private(buffer);
224 priv->growsize = (grow_size != 0) ? grow_size : DEFAULT_BUF_SIZE;
226 g_object_notify_by_pspec(G_OBJECT(buffer), properties[PROP_GROW_SIZE]);
229 static const gchar *
230 purple_circular_buffer_get_input(PurpleCircularBuffer *buffer) {
231 PurpleCircularBufferPrivate *priv = NULL;
233 g_return_val_if_fail(PURPLE_IS_CIRCULAR_BUFFER(buffer), NULL);
235 priv = purple_circular_buffer_get_instance_private(buffer);
237 return priv->input;
240 /******************************************************************************
241 * Object Stuff
242 *****************************************************************************/
243 static void
244 purple_circular_buffer_init(PurpleCircularBuffer *buffer)
248 static void
249 purple_circular_buffer_finalize(GObject *obj) {
250 PurpleCircularBufferPrivate *priv =
251 purple_circular_buffer_get_instance_private(
252 PURPLE_CIRCULAR_BUFFER(obj));
254 g_free(priv->buffer);
256 G_OBJECT_CLASS(purple_circular_buffer_parent_class)->finalize(obj);
259 static void
260 purple_circular_buffer_get_property(GObject *obj, guint param_id,
261 GValue *value, GParamSpec *pspec)
263 PurpleCircularBuffer *buffer = PURPLE_CIRCULAR_BUFFER(obj);
265 switch(param_id) {
266 case PROP_GROW_SIZE:
267 g_value_set_ulong(value,
268 purple_circular_buffer_get_grow_size(buffer));
269 break;
270 case PROP_BUFFER_USED:
271 g_value_set_ulong(value,
272 purple_circular_buffer_get_used(buffer));
273 break;
274 case PROP_INPUT:
275 g_value_set_pointer(value,
276 (void*) purple_circular_buffer_get_input(buffer));
277 break;
278 case PROP_OUTPUT:
279 g_value_set_pointer(value,
280 (void*) purple_circular_buffer_get_output(buffer));
281 break;
282 default:
283 G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
284 break;
288 static void
289 purple_circular_buffer_set_property(GObject *obj, guint param_id,
290 const GValue *value, GParamSpec *pspec)
292 PurpleCircularBuffer *buffer = PURPLE_CIRCULAR_BUFFER(obj);
294 switch(param_id) {
295 case PROP_GROW_SIZE:
296 purple_circular_buffer_set_grow_size(buffer,
297 g_value_get_ulong(value));
298 break;
299 default:
300 G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
301 break;
305 static void
306 purple_circular_buffer_class_init(PurpleCircularBufferClass *klass) {
307 GObjectClass *obj_class = G_OBJECT_CLASS(klass);
308 PurpleCircularBufferClass *buffer_class = PURPLE_CIRCULAR_BUFFER_CLASS(klass);
310 obj_class->finalize = purple_circular_buffer_finalize;
311 obj_class->get_property = purple_circular_buffer_get_property;
312 obj_class->set_property = purple_circular_buffer_set_property;
314 buffer_class->grow = purple_circular_buffer_real_grow;
315 buffer_class->append = purple_circular_buffer_real_append;
316 buffer_class->max_read_size = purple_circular_buffer_real_max_read_size;
317 buffer_class->mark_read = purple_circular_buffer_real_mark_read;
319 /* using a ulong for the gsize properties since there is no
320 * g_param_spec_size, and the ulong should always work. --gk 3/21/11
322 properties[PROP_GROW_SIZE] = g_param_spec_ulong("grow-size", "grow-size",
323 "The grow size of the buffer",
324 0, G_MAXSIZE, 0,
325 G_PARAM_READWRITE | G_PARAM_CONSTRUCT |
326 G_PARAM_STATIC_STRINGS);
328 properties[PROP_BUFFER_USED] = g_param_spec_ulong("buffer-used",
329 "buffer-used",
330 "The amount of the buffer used",
331 0, G_MAXSIZE, 0,
332 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
334 properties[PROP_INPUT] = g_param_spec_pointer("input", "input",
335 "The input pointer of the buffer",
336 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
338 properties[PROP_OUTPUT] = g_param_spec_pointer("output", "output",
339 "The output pointer of the buffer",
340 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
342 g_object_class_install_properties(obj_class, PROP_LAST, properties);
345 /******************************************************************************
346 * API
347 *****************************************************************************/
348 PurpleCircularBuffer *
349 purple_circular_buffer_new(gsize growsize) {
350 return g_object_new(PURPLE_TYPE_CIRCULAR_BUFFER,
351 "grow-size", growsize ? growsize : DEFAULT_BUF_SIZE,
352 NULL);
355 void
356 purple_circular_buffer_grow(PurpleCircularBuffer *buffer, gsize len) {
357 PurpleCircularBufferClass *klass = NULL;
359 g_return_if_fail(PURPLE_IS_CIRCULAR_BUFFER(buffer));
361 klass = PURPLE_CIRCULAR_BUFFER_GET_CLASS(buffer);
362 if(klass && klass->grow)
363 klass->grow(buffer, len);
366 void
367 purple_circular_buffer_append(PurpleCircularBuffer *buffer, gconstpointer src,
368 gsize len)
370 PurpleCircularBufferClass *klass = NULL;
372 g_return_if_fail(PURPLE_IS_CIRCULAR_BUFFER(buffer));
373 g_return_if_fail(src != NULL);
375 klass = PURPLE_CIRCULAR_BUFFER_GET_CLASS(buffer);
376 if(klass && klass->append)
377 klass->append(buffer, src, len);
380 gsize
381 purple_circular_buffer_get_max_read(PurpleCircularBuffer *buffer) {
382 PurpleCircularBufferClass *klass = NULL;
384 g_return_val_if_fail(PURPLE_IS_CIRCULAR_BUFFER(buffer), 0);
386 klass = PURPLE_CIRCULAR_BUFFER_GET_CLASS(buffer);
387 if(klass && klass->max_read_size)
388 return klass->max_read_size(buffer);
390 return 0;
393 gboolean
394 purple_circular_buffer_mark_read(PurpleCircularBuffer *buffer, gsize len) {
395 PurpleCircularBufferClass *klass = NULL;
397 g_return_val_if_fail(PURPLE_IS_CIRCULAR_BUFFER(buffer), FALSE);
399 klass = PURPLE_CIRCULAR_BUFFER_GET_CLASS(buffer);
400 if(klass && klass->mark_read)
401 return klass->mark_read(buffer, len);
403 return FALSE;
406 gsize
407 purple_circular_buffer_get_grow_size(PurpleCircularBuffer *buffer) {
409 PurpleCircularBufferPrivate *priv = NULL;
411 g_return_val_if_fail(PURPLE_IS_CIRCULAR_BUFFER(buffer), 0);
413 priv = purple_circular_buffer_get_instance_private(buffer);
415 return priv->growsize;
418 gsize
419 purple_circular_buffer_get_used(PurpleCircularBuffer *buffer) {
420 PurpleCircularBufferPrivate *priv = NULL;
422 g_return_val_if_fail(PURPLE_IS_CIRCULAR_BUFFER(buffer), 0);
424 priv = purple_circular_buffer_get_instance_private(buffer);
426 return priv->bufused;
429 const gchar *
430 purple_circular_buffer_get_output(PurpleCircularBuffer *buffer) {
431 PurpleCircularBufferPrivate *priv = NULL;
433 g_return_val_if_fail(PURPLE_IS_CIRCULAR_BUFFER(buffer), NULL);
435 priv = purple_circular_buffer_get_instance_private(buffer);
437 return priv->output;
440 void
441 purple_circular_buffer_reset(PurpleCircularBuffer *buffer) {
442 PurpleCircularBufferPrivate *priv = NULL;
443 GObject *obj;
445 g_return_if_fail(PURPLE_IS_CIRCULAR_BUFFER(buffer));
447 priv = purple_circular_buffer_get_instance_private(buffer);
449 priv->input = priv->buffer;
450 priv->output = priv->buffer;
452 obj = G_OBJECT(buffer);
453 g_object_freeze_notify(obj);
454 g_object_notify_by_pspec(obj, properties[PROP_INPUT]);
455 g_object_notify_by_pspec(obj, properties[PROP_OUTPUT]);
456 g_object_thaw_notify(obj);