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
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
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 /******************************************************************************
31 *****************************************************************************/
33 /** A pointer to the starting address of our chunk of memory. */
36 /** The incremental amount to increase this buffer by when
37 * the buffer is not big enough to hold incoming data, in bytes. */
40 /** The length of this buffer, in bytes. */
43 /** The number of bytes of this buffer that contain unread data. */
46 /** A pointer to the next byte where new incoming data is
50 /** A pointer to the next byte of buffered data that should be
51 * read by the consumer. */
53 } PurpleCircularBufferPrivate
;
55 /******************************************************************************
57 *****************************************************************************/
67 /******************************************************************************
69 *****************************************************************************/
70 static GObjectClass
*parent_class
= NULL
;
71 static GParamSpec
*properties
[PROP_LAST
];
73 /******************************************************************************
74 * Circular Buffer Implementation
75 *****************************************************************************/
77 purple_circular_buffer_real_grow(PurpleCircularBuffer
*buffer
, gsize len
) {
78 PurpleCircularBufferPrivate
*priv
= NULL
;
79 gsize in_offset
= 0, out_offset
= 0;
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
;
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
);
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
);
130 purple_circular_buffer_real_append(PurpleCircularBuffer
*buffer
,
131 gconstpointer src
, gsize len
)
133 PurpleCircularBufferPrivate
*priv
= NULL
;
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
));
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
);
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
);
172 purple_circular_buffer_real_max_read_size(const PurpleCircularBuffer
*buffer
) {
173 PurpleCircularBufferPrivate
*priv
= NULL
;
176 priv
= PURPLE_CIRCULAR_BUFFER_GET_PRIVATE(buffer
);
178 if(priv
->bufused
== 0)
180 else if((priv
->output
- priv
->input
) >= 0)
181 max_read
= priv
->buflen
- (priv
->output
- priv
->buffer
);
183 max_read
= priv
->input
- priv
->output
;
189 purple_circular_buffer_real_mark_read(PurpleCircularBuffer
*buffer
,
192 PurpleCircularBufferPrivate
*priv
= NULL
;
195 g_return_val_if_fail(purple_circular_buffer_get_max_read(buffer
) >= len
, FALSE
);
197 priv
= PURPLE_CIRCULAR_BUFFER_GET_PRIVATE(buffer
);
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
);
215 /******************************************************************************
217 *****************************************************************************/
219 purple_circular_buffer_set_grow_size(PurpleCircularBuffer
*buffer
,
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
]);
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
);
241 /******************************************************************************
243 *****************************************************************************/
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
);
255 purple_circular_buffer_get_property(GObject
*obj
, guint param_id
,
256 GValue
*value
, GParamSpec
*pspec
)
258 PurpleCircularBuffer
*buffer
= PURPLE_CIRCULAR_BUFFER(obj
);
262 g_value_set_ulong(value
,
263 purple_circular_buffer_get_grow_size(buffer
));
265 case PROP_BUFFER_USED
:
266 g_value_set_ulong(value
,
267 purple_circular_buffer_get_used(buffer
));
270 g_value_set_pointer(value
,
271 (void*) purple_circular_buffer_get_input(buffer
));
274 g_value_set_pointer(value
,
275 (void*) purple_circular_buffer_get_output(buffer
));
278 G_OBJECT_WARN_INVALID_PROPERTY_ID(obj
, param_id
, pspec
);
284 purple_circular_buffer_set_property(GObject
*obj
, guint param_id
,
285 const GValue
*value
, GParamSpec
*pspec
)
287 PurpleCircularBuffer
*buffer
= PURPLE_CIRCULAR_BUFFER(obj
);
291 purple_circular_buffer_set_grow_size(buffer
,
292 g_value_get_ulong(value
));
295 G_OBJECT_WARN_INVALID_PROPERTY_ID(obj
, param_id
, pspec
);
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",
324 G_PARAM_READWRITE
| G_PARAM_CONSTRUCT
|
325 G_PARAM_STATIC_STRINGS
);
327 properties
[PROP_BUFFER_USED
] = g_param_spec_ulong("buffer-used",
329 "The amount of the buffer used",
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 /******************************************************************************
346 *****************************************************************************/
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",
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
,
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
);
385 purple_circular_buffer_append(PurpleCircularBuffer
*buffer
, gconstpointer src
,
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
);
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
);
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
);
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
;
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
;
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
);
459 purple_circular_buffer_reset(PurpleCircularBuffer
*buffer
) {
460 PurpleCircularBufferPrivate
*priv
= NULL
;
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
);