prefs: Convert Status/Idle page to glade.
[pidgin-git.git] / libpurple / circularbuffer.c
blob651ceefc3521407284f5608cc5948821b855dc90
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 #define PURPLE_CIRCULAR_BUFFER_GET_PRIVATE(obj) \
27 (G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_CIRCULAR_BUFFER, PurpleCircularBufferPrivate))
29 /******************************************************************************
30 * Structs
31 *****************************************************************************/
32 typedef struct {
33 /** A pointer to the starting address of our chunk of memory. */
34 gchar *buffer;
36 /** The incremental amount to increase this buffer by when
37 * the buffer is not big enough to hold incoming data, in bytes. */
38 gsize growsize;
40 /** The length of this buffer, in bytes. */
41 gsize buflen;
43 /** The number of bytes of this buffer that contain unread data. */
44 gsize bufused;
46 /** A pointer to the next byte where new incoming data is
47 * buffered to. */
48 gchar *input;
50 /** A pointer to the next byte of buffered data that should be
51 * read by the consumer. */
52 gchar *output;
53 } PurpleCircularBufferPrivate;
55 /******************************************************************************
56 * Enums
57 *****************************************************************************/
58 enum {
59 PROP_ZERO,
60 PROP_GROW_SIZE,
61 PROP_BUFFER_USED,
62 PROP_INPUT,
63 PROP_OUTPUT,
64 PROP_LAST,
67 /******************************************************************************
68 * Globals
69 *****************************************************************************/
70 static GObjectClass *parent_class = NULL;
71 static GParamSpec *properties[PROP_LAST];
73 /******************************************************************************
74 * Circular Buffer Implementation
75 *****************************************************************************/
76 static void
77 purple_circular_buffer_real_grow(PurpleCircularBuffer *buffer, gsize len) {
78 PurpleCircularBufferPrivate *priv = NULL;
79 gsize in_offset = 0, out_offset = 0;
80 gsize start_buflen;
81 GObject *obj;
83 priv = PURPLE_CIRCULAR_BUFFER_GET_PRIVATE(buffer);
85 start_buflen = priv->buflen;
87 while((priv->buflen - priv->bufused) < len)
88 priv->buflen += priv->growsize;
90 if(priv->input != NULL) {
91 in_offset = priv->input - priv->buffer;
92 out_offset = priv->output - priv->buffer;
95 priv->buffer = g_realloc(priv->buffer, priv->buflen);
97 /* adjust the fill and remove pointer locations */
98 if(priv->input == NULL) {
99 priv->input = priv->output = priv->buffer;
100 } else {
101 priv->input = priv->buffer + in_offset;
102 priv->output = priv->buffer + out_offset;
105 /* If the fill pointer is wrapped to before the remove
106 * pointer, we need to shift the data */
107 if (in_offset < out_offset
108 || (in_offset == out_offset && priv->bufused > 0))
110 gsize shift_n = MIN(priv->buflen - start_buflen, in_offset);
111 memcpy(priv->buffer + start_buflen, priv->buffer, shift_n);
113 /* If we couldn't fit the wrapped read buffer at the end */
114 if (shift_n < in_offset) {
115 memmove(priv->buffer, priv->buffer + shift_n, in_offset - shift_n);
116 priv->input = priv->buffer + (in_offset - shift_n);
117 } else {
118 priv->input = priv->buffer + start_buflen + in_offset;
122 obj = G_OBJECT(buffer);
123 g_object_freeze_notify(obj);
124 g_object_notify_by_pspec(obj, properties[PROP_INPUT]);
125 g_object_notify_by_pspec(obj, properties[PROP_OUTPUT]);
126 g_object_thaw_notify(obj);
129 static void
130 purple_circular_buffer_real_append(PurpleCircularBuffer *buffer,
131 gconstpointer src, gsize len)
133 PurpleCircularBufferPrivate *priv = NULL;
134 gsize len_stored;
135 GObject *obj;
137 priv = PURPLE_CIRCULAR_BUFFER_GET_PRIVATE(buffer);
139 /* Grow the buffer, if necessary */
140 if((priv->buflen - priv->bufused) < len)
141 purple_circular_buffer_grow(buffer, len);
143 /* If there is not enough room to copy all of src before hitting
144 * the end of the buffer then we will need to do two copies.
145 * One copy from input to the end of the buffer, and the
146 * second copy from the start of the buffer to the end of src. */
147 if(priv->input >= priv->output)
148 len_stored = MIN(len, priv->buflen - (priv->input - priv->buffer));
149 else
150 len_stored = len;
152 if(len_stored > 0)
153 memcpy(priv->input, src, len_stored);
155 if(len_stored < len) {
156 memcpy(priv->buffer, (char*)src + len_stored, len - len_stored);
157 priv->input = priv->buffer + (len - len_stored);
158 } else {
159 priv->input += len_stored;
162 priv->bufused += len;
164 obj = G_OBJECT(buffer);
165 g_object_freeze_notify(obj);
166 g_object_notify_by_pspec(obj, properties[PROP_BUFFER_USED]);
167 g_object_notify_by_pspec(obj, properties[PROP_INPUT]);
168 g_object_thaw_notify(obj);
171 static gsize
172 purple_circular_buffer_real_max_read_size(const PurpleCircularBuffer *buffer) {
173 PurpleCircularBufferPrivate *priv = NULL;
174 gsize max_read;
176 priv = PURPLE_CIRCULAR_BUFFER_GET_PRIVATE(buffer);
178 if(priv->bufused == 0)
179 max_read = 0;
180 else if((priv->output - priv->input) >= 0)
181 max_read = priv->buflen - (priv->output - priv->buffer);
182 else
183 max_read = priv->input - priv->output;
185 return max_read;
188 static gboolean
189 purple_circular_buffer_real_mark_read(PurpleCircularBuffer *buffer,
190 gsize len)
192 PurpleCircularBufferPrivate *priv = NULL;
193 GObject *obj;
195 g_return_val_if_fail(purple_circular_buffer_get_max_read(buffer) >= len, FALSE);
197 priv = PURPLE_CIRCULAR_BUFFER_GET_PRIVATE(buffer);
199 priv->output += len;
200 priv->bufused -= len;
202 /* wrap to the start if we're at the end */
203 if ((gsize)(priv->output - priv->buffer) == priv->buflen)
204 priv->output = priv->buffer;
206 obj = G_OBJECT(buffer);
207 g_object_freeze_notify(obj);
208 g_object_notify_by_pspec(obj, properties[PROP_BUFFER_USED]);
209 g_object_notify_by_pspec(obj, properties[PROP_OUTPUT]);
210 g_object_thaw_notify(obj);
212 return TRUE;
215 /******************************************************************************
216 * Private API
217 *****************************************************************************/
218 static void
219 purple_circular_buffer_set_grow_size(PurpleCircularBuffer *buffer,
220 gsize grow_size)
222 PurpleCircularBufferPrivate *priv =
223 PURPLE_CIRCULAR_BUFFER_GET_PRIVATE(buffer);
225 priv->growsize = (grow_size != 0) ? grow_size : DEFAULT_BUF_SIZE;
227 g_object_notify_by_pspec(G_OBJECT(buffer), properties[PROP_GROW_SIZE]);
230 static const gchar *
231 purple_circular_buffer_get_input(const PurpleCircularBuffer *buffer) {
232 PurpleCircularBufferPrivate *priv = NULL;
234 g_return_val_if_fail(PURPLE_IS_CIRCULAR_BUFFER(buffer), NULL);
236 priv = PURPLE_CIRCULAR_BUFFER_GET_PRIVATE(buffer);
238 return priv->input;
241 /******************************************************************************
242 * Object Stuff
243 *****************************************************************************/
244 static void
245 purple_circular_buffer_finalize(GObject *obj) {
246 PurpleCircularBufferPrivate *priv =
247 PURPLE_CIRCULAR_BUFFER_GET_PRIVATE(obj);
249 g_free(priv->buffer);
251 G_OBJECT_CLASS(parent_class)->finalize(obj);
254 static void
255 purple_circular_buffer_get_property(GObject *obj, guint param_id,
256 GValue *value, GParamSpec *pspec)
258 PurpleCircularBuffer *buffer = PURPLE_CIRCULAR_BUFFER(obj);
260 switch(param_id) {
261 case PROP_GROW_SIZE:
262 g_value_set_ulong(value,
263 purple_circular_buffer_get_grow_size(buffer));
264 break;
265 case PROP_BUFFER_USED:
266 g_value_set_ulong(value,
267 purple_circular_buffer_get_used(buffer));
268 break;
269 case PROP_INPUT:
270 g_value_set_pointer(value,
271 (void*) purple_circular_buffer_get_input(buffer));
272 break;
273 case PROP_OUTPUT:
274 g_value_set_pointer(value,
275 (void*) purple_circular_buffer_get_output(buffer));
276 break;
277 default:
278 G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
279 break;
283 static void
284 purple_circular_buffer_set_property(GObject *obj, guint param_id,
285 const GValue *value, GParamSpec *pspec)
287 PurpleCircularBuffer *buffer = PURPLE_CIRCULAR_BUFFER(obj);
289 switch(param_id) {
290 case PROP_GROW_SIZE:
291 purple_circular_buffer_set_grow_size(buffer,
292 g_value_get_ulong(value));
293 break;
294 default:
295 G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
296 break;
300 static void
301 purple_circular_buffer_class_init(PurpleCircularBufferClass *klass) {
302 GObjectClass *obj_class = G_OBJECT_CLASS(klass);
303 PurpleCircularBufferClass *buffer_class = PURPLE_CIRCULAR_BUFFER_CLASS(klass);
305 parent_class = g_type_class_peek_parent(klass);
307 g_type_class_add_private(klass, sizeof(PurpleCircularBufferPrivate));
309 obj_class->finalize = purple_circular_buffer_finalize;
310 obj_class->get_property = purple_circular_buffer_get_property;
311 obj_class->set_property = purple_circular_buffer_set_property;
313 buffer_class->grow = purple_circular_buffer_real_grow;
314 buffer_class->append = purple_circular_buffer_real_append;
315 buffer_class->max_read_size = purple_circular_buffer_real_max_read_size;
316 buffer_class->mark_read = purple_circular_buffer_real_mark_read;
318 /* using a ulong for the gsize properties since there is no
319 * g_param_spec_size, and the ulong should always work. --gk 3/21/11
321 properties[PROP_GROW_SIZE] = g_param_spec_ulong("grow-size", "grow-size",
322 "The grow size of the buffer",
323 0, G_MAXSIZE, 0,
324 G_PARAM_READWRITE | G_PARAM_CONSTRUCT |
325 G_PARAM_STATIC_STRINGS);
327 properties[PROP_BUFFER_USED] = g_param_spec_ulong("buffer-used",
328 "buffer-used",
329 "The amount of the buffer used",
330 0, G_MAXSIZE, 0,
331 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
333 properties[PROP_INPUT] = g_param_spec_pointer("input", "input",
334 "The input pointer of the buffer",
335 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
337 properties[PROP_OUTPUT] = g_param_spec_pointer("output", "output",
338 "The output pointer of the buffer",
339 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
341 g_object_class_install_properties(obj_class, PROP_LAST, properties);
344 /******************************************************************************
345 * API
346 *****************************************************************************/
347 GType
348 purple_circular_buffer_get_type(void) {
349 static GType type = 0;
351 if(G_UNLIKELY(type == 0)) {
352 static const GTypeInfo info = {
353 .class_size = sizeof(PurpleCircularBufferClass),
354 .class_init = (GClassInitFunc)purple_circular_buffer_class_init,
355 .instance_size = sizeof(PurpleCircularBuffer),
358 type = g_type_register_static(G_TYPE_OBJECT,
359 "PurpleCircularBuffer",
360 &info, 0);
363 return type;
366 PurpleCircularBuffer *
367 purple_circular_buffer_new(gsize growsize) {
368 return g_object_new(PURPLE_TYPE_CIRCULAR_BUFFER,
369 "grow-size", growsize ? growsize : DEFAULT_BUF_SIZE,
370 NULL);
373 void
374 purple_circular_buffer_grow(PurpleCircularBuffer *buffer, gsize len) {
375 PurpleCircularBufferClass *klass = NULL;
377 g_return_if_fail(PURPLE_IS_CIRCULAR_BUFFER(buffer));
379 klass = PURPLE_CIRCULAR_BUFFER_GET_CLASS(buffer);
380 if(klass && klass->grow)
381 klass->grow(buffer, len);
384 void
385 purple_circular_buffer_append(PurpleCircularBuffer *buffer, gconstpointer src,
386 gsize len)
388 PurpleCircularBufferClass *klass = NULL;
390 g_return_if_fail(PURPLE_IS_CIRCULAR_BUFFER(buffer));
391 g_return_if_fail(src != NULL);
393 klass = PURPLE_CIRCULAR_BUFFER_GET_CLASS(buffer);
394 if(klass && klass->append)
395 klass->append(buffer, src, len);
398 gsize
399 purple_circular_buffer_get_max_read(const PurpleCircularBuffer *buffer) {
400 PurpleCircularBufferClass *klass = NULL;
402 g_return_val_if_fail(PURPLE_IS_CIRCULAR_BUFFER(buffer), 0);
404 klass = PURPLE_CIRCULAR_BUFFER_GET_CLASS(buffer);
405 if(klass && klass->max_read_size)
406 return klass->max_read_size(buffer);
408 return 0;
411 gboolean
412 purple_circular_buffer_mark_read(PurpleCircularBuffer *buffer, gsize len) {
413 PurpleCircularBufferClass *klass = NULL;
415 g_return_val_if_fail(PURPLE_IS_CIRCULAR_BUFFER(buffer), FALSE);
417 klass = PURPLE_CIRCULAR_BUFFER_GET_CLASS(buffer);
418 if(klass && klass->mark_read)
419 return klass->mark_read(buffer, len);
421 return FALSE;
424 gsize
425 purple_circular_buffer_get_grow_size(const PurpleCircularBuffer *buffer) {
427 PurpleCircularBufferPrivate *priv = NULL;
429 g_return_val_if_fail(PURPLE_IS_CIRCULAR_BUFFER(buffer), 0);
431 priv = PURPLE_CIRCULAR_BUFFER_GET_PRIVATE(buffer);
433 return priv->growsize;
436 gsize
437 purple_circular_buffer_get_used(const PurpleCircularBuffer *buffer) {
438 PurpleCircularBufferPrivate *priv = NULL;
440 g_return_val_if_fail(PURPLE_IS_CIRCULAR_BUFFER(buffer), 0);
442 priv = PURPLE_CIRCULAR_BUFFER_GET_PRIVATE(buffer);
444 return priv->bufused;
447 const gchar *
448 purple_circular_buffer_get_output(const PurpleCircularBuffer *buffer) {
449 PurpleCircularBufferPrivate *priv = NULL;
451 g_return_val_if_fail(PURPLE_IS_CIRCULAR_BUFFER(buffer), NULL);
453 priv = PURPLE_CIRCULAR_BUFFER_GET_PRIVATE(buffer);
455 return priv->output;
458 void
459 purple_circular_buffer_reset(PurpleCircularBuffer *buffer) {
460 PurpleCircularBufferPrivate *priv = NULL;
461 GObject *obj;
463 g_return_if_fail(PURPLE_IS_CIRCULAR_BUFFER(buffer));
465 priv = PURPLE_CIRCULAR_BUFFER_GET_PRIVATE(buffer);
467 priv->input = priv->buffer;
468 priv->output = priv->buffer;
470 obj = G_OBJECT(buffer);
471 g_object_freeze_notify(obj);
472 g_object_notify_by_pspec(obj, properties[PROP_INPUT]);
473 g_object_notify_by_pspec(obj, properties[PROP_OUTPUT]);
474 g_object_thaw_notify(obj);