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 /******************************************************************************
28 *****************************************************************************/
30 /** A pointer to the starting address of our chunk of memory. */
33 /** The incremental amount to increase this buffer by when
34 * the buffer is not big enough to hold incoming data, in bytes. */
37 /** The length of this buffer, in bytes. */
40 /** The number of bytes of this buffer that contain unread data. */
43 /** A pointer to the next byte where new incoming data is
47 /** A pointer to the next byte of buffered data that should be
48 * read by the consumer. */
50 } PurpleCircularBufferPrivate
;
52 /******************************************************************************
54 *****************************************************************************/
64 /******************************************************************************
66 *****************************************************************************/
67 static GParamSpec
*properties
[PROP_LAST
];
69 G_DEFINE_TYPE_WITH_PRIVATE(PurpleCircularBuffer
, purple_circular_buffer
,
72 /******************************************************************************
73 * Circular Buffer Implementation
74 *****************************************************************************/
76 purple_circular_buffer_real_grow(PurpleCircularBuffer
*buffer
, gsize len
) {
77 PurpleCircularBufferPrivate
*priv
= NULL
;
78 gsize in_offset
= 0, out_offset
= 0;
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
;
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
);
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
);
129 purple_circular_buffer_real_append(PurpleCircularBuffer
*buffer
,
130 gconstpointer src
, gsize len
)
132 PurpleCircularBufferPrivate
*priv
= NULL
;
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
));
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
);
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
);
171 purple_circular_buffer_real_max_read_size(PurpleCircularBuffer
*buffer
) {
172 PurpleCircularBufferPrivate
*priv
= NULL
;
175 priv
= purple_circular_buffer_get_instance_private(buffer
);
177 if(priv
->bufused
== 0)
179 else if((priv
->output
- priv
->input
) >= 0)
180 max_read
= priv
->buflen
- (priv
->output
- priv
->buffer
);
182 max_read
= priv
->input
- priv
->output
;
188 purple_circular_buffer_real_mark_read(PurpleCircularBuffer
*buffer
,
191 PurpleCircularBufferPrivate
*priv
= NULL
;
194 g_return_val_if_fail(purple_circular_buffer_get_max_read(buffer
) >= len
, FALSE
);
196 priv
= purple_circular_buffer_get_instance_private(buffer
);
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
);
214 /******************************************************************************
216 *****************************************************************************/
218 purple_circular_buffer_set_grow_size(PurpleCircularBuffer
*buffer
,
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
]);
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
);
240 /******************************************************************************
242 *****************************************************************************/
244 purple_circular_buffer_init(PurpleCircularBuffer
*buffer
)
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
);
260 purple_circular_buffer_get_property(GObject
*obj
, guint param_id
,
261 GValue
*value
, GParamSpec
*pspec
)
263 PurpleCircularBuffer
*buffer
= PURPLE_CIRCULAR_BUFFER(obj
);
267 g_value_set_ulong(value
,
268 purple_circular_buffer_get_grow_size(buffer
));
270 case PROP_BUFFER_USED
:
271 g_value_set_ulong(value
,
272 purple_circular_buffer_get_used(buffer
));
275 g_value_set_pointer(value
,
276 (void*) purple_circular_buffer_get_input(buffer
));
279 g_value_set_pointer(value
,
280 (void*) purple_circular_buffer_get_output(buffer
));
283 G_OBJECT_WARN_INVALID_PROPERTY_ID(obj
, param_id
, pspec
);
289 purple_circular_buffer_set_property(GObject
*obj
, guint param_id
,
290 const GValue
*value
, GParamSpec
*pspec
)
292 PurpleCircularBuffer
*buffer
= PURPLE_CIRCULAR_BUFFER(obj
);
296 purple_circular_buffer_set_grow_size(buffer
,
297 g_value_get_ulong(value
));
300 G_OBJECT_WARN_INVALID_PROPERTY_ID(obj
, param_id
, pspec
);
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",
325 G_PARAM_READWRITE
| G_PARAM_CONSTRUCT
|
326 G_PARAM_STATIC_STRINGS
);
328 properties
[PROP_BUFFER_USED
] = g_param_spec_ulong("buffer-used",
330 "The amount of the buffer used",
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 /******************************************************************************
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
,
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
);
367 purple_circular_buffer_append(PurpleCircularBuffer
*buffer
, gconstpointer src
,
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
);
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
);
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
);
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
;
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
;
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
);
441 purple_circular_buffer_reset(PurpleCircularBuffer
*buffer
) {
442 PurpleCircularBufferPrivate
*priv
= NULL
;
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
);