| 25 |
25 |
| 26 #include "circbuffer.h" |
26 #include "circbuffer.h" |
| 27 |
27 |
| 28 #define DEFAULT_BUF_SIZE 256 |
28 #define DEFAULT_BUF_SIZE 256 |
| 29 |
29 |
| 30 PurpleCircBuffer * |
30 #define PURPLE_CIRCULAR_BUFFER_GET_PRIVATE(obj) \ |
| 31 purple_circ_buffer_new(gsize growsize) { |
31 (G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_CIRCULAR_BUFFER, PurpleCircularBufferPrivate)) |
| 32 PurpleCircBuffer *buf = g_new0(PurpleCircBuffer, 1); |
32 |
| 33 buf->growsize = growsize ? growsize : DEFAULT_BUF_SIZE; |
33 /****************************************************************************** |
| 34 return buf; |
34 * Structs |
| 35 } |
35 *****************************************************************************/ |
| 36 |
36 typedef struct { |
| 37 void purple_circ_buffer_destroy(PurpleCircBuffer *buf) { |
37 gchar *buffer; |
| 38 g_return_if_fail(buf != NULL); |
38 gsize growsize; |
| 39 |
39 gsize buflen; |
| 40 g_free(buf->buffer); |
40 gsize bufused; |
| 41 g_free(buf); |
41 gchar *input; |
| 42 } |
42 gchar *output; |
| 43 |
43 } PurpleCircularBufferPrivate; |
| 44 static void grow_circ_buffer(PurpleCircBuffer *buf, gsize len) { |
44 |
| 45 int in_offset = 0, out_offset = 0; |
45 /****************************************************************************** |
| 46 int start_buflen; |
46 * Enums |
| 47 |
47 *****************************************************************************/ |
| 48 g_return_if_fail(buf != NULL); |
48 enum { |
| 49 |
49 PROP_ZERO, |
| 50 start_buflen = buf->buflen; |
50 PROP_GROW_SIZE, |
| 51 |
51 PROP_BUFFER_USED, |
| 52 while ((buf->buflen - buf->bufused) < len) |
52 PROP_INPUT, |
| 53 buf->buflen += buf->growsize; |
53 PROP_OUTPUT, |
| 54 |
54 PROP_LAST, |
| 55 if (buf->inptr != NULL) { |
55 }; |
| 56 in_offset = buf->inptr - buf->buffer; |
56 |
| 57 out_offset = buf->outptr - buf->buffer; |
57 /****************************************************************************** |
| 58 } |
58 * Globals |
| 59 buf->buffer = g_realloc(buf->buffer, buf->buflen); |
59 *****************************************************************************/ |
| |
60 static GObjectClass *parent_class = NULL; |
| |
61 |
| |
62 /****************************************************************************** |
| |
63 * Circular Buffer Implementation |
| |
64 *****************************************************************************/ |
| |
65 static void |
| |
66 purple_circular_buffer_real_grow(PurpleCircularBuffer *buffer, gsize len) { |
| |
67 PurpleCircularBufferPrivate *priv = NULL; |
| |
68 gint in_offset = 0, out_offset = 0; |
| |
69 gint start_buflen; |
| |
70 |
| |
71 priv = PURPLE_CIRCULAR_BUFFER_GET_PRIVATE(buffer); |
| |
72 |
| |
73 start_buflen = priv->buflen; |
| |
74 |
| |
75 while((priv->buflen - priv->bufused) < len) |
| |
76 priv->buflen += priv->growsize; |
| |
77 |
| |
78 if(priv->input != NULL) { |
| |
79 in_offset = priv->input - priv->buffer; |
| |
80 out_offset = priv->output - priv->buffer; |
| |
81 } |
| |
82 |
| |
83 priv->buffer = g_realloc(priv->buffer, priv->buflen); |
| 60 |
84 |
| 61 /* adjust the fill and remove pointer locations */ |
85 /* adjust the fill and remove pointer locations */ |
| 62 if (buf->inptr == NULL) { |
86 if(priv->input == NULL) { |
| 63 buf->inptr = buf->outptr = buf->buffer; |
87 priv->input = priv->output = priv->buffer; |
| 64 } else { |
88 } else { |
| 65 buf->inptr = buf->buffer + in_offset; |
89 priv->input = priv->buffer + in_offset; |
| 66 buf->outptr = buf->buffer + out_offset; |
90 priv->output = priv->buffer + out_offset; |
| 67 } |
91 } |
| 68 |
92 |
| 69 /* If the fill pointer is wrapped to before the remove |
93 /* If the fill pointer is wrapped to before the remove |
| 70 * pointer, we need to shift the data */ |
94 * pointer, we need to shift the data */ |
| 71 if (in_offset < out_offset |
95 if(in_offset < out_offset |
| 72 || (in_offset == out_offset && buf->bufused > 0)) { |
96 || (in_offset == out_offset && priv->bufused > 0)) |
| 73 int shift_n = MIN(buf->buflen - start_buflen, |
97 { |
| 74 in_offset); |
98 gint shift_n = MIN(priv->buflen - start_buflen, in_offset); |
| 75 memcpy(buf->buffer + start_buflen, buf->buffer, |
99 memcpy(priv->buffer + start_buflen, priv->buffer, shift_n); |
| 76 shift_n); |
100 |
| 77 |
101 /* If we couldn't fit the wrapped read buffer at the end */ |
| 78 /* If we couldn't fit the wrapped read buffer |
|
| 79 * at the end */ |
|
| 80 if (shift_n < in_offset) { |
102 if (shift_n < in_offset) { |
| 81 memmove(buf->buffer, |
103 memmove(priv->buffer, priv->buffer + shift_n, in_offset - shift_n); |
| 82 buf->buffer + shift_n, |
104 priv->input = priv->buffer + (in_offset - shift_n); |
| 83 in_offset - shift_n); |
|
| 84 buf->inptr = buf->buffer + |
|
| 85 (in_offset - shift_n); |
|
| 86 } else { |
105 } else { |
| 87 buf->inptr = buf->buffer + |
106 priv->input = priv->buffer + start_buflen + in_offset; |
| 88 start_buflen + in_offset; |
|
| 89 } |
107 } |
| 90 } |
108 } |
| 91 } |
109 } |
| 92 |
110 |
| 93 void purple_circ_buffer_append(PurpleCircBuffer *buf, gconstpointer src, gsize len) { |
111 static void |
| 94 |
112 purple_circular_buffer_real_append(PurpleCircularBuffer *buffer, |
| 95 int len_stored; |
113 gconstpointer src, gsize len) |
| 96 |
114 { |
| 97 g_return_if_fail(buf != NULL); |
115 PurpleCircularBufferPrivate *priv = NULL; |
| |
116 gint len_stored; |
| |
117 |
| |
118 priv = PURPLE_CIRCULAR_BUFFER_GET_PRIVATE(buffer); |
| 98 |
119 |
| 99 /* Grow the buffer, if necessary */ |
120 /* Grow the buffer, if necessary */ |
| 100 if ((buf->buflen - buf->bufused) < len) |
121 if((priv->buflen - priv->bufused) < len) |
| 101 grow_circ_buffer(buf, len); |
122 purple_circular_buffer_grow(buffer, len); |
| 102 |
123 |
| 103 /* If there is not enough room to copy all of src before hitting |
124 /* If there is not enough room to copy all of src before hitting |
| 104 * the end of the buffer then we will need to do two copies. |
125 * the end of the buffer then we will need to do two copies. |
| 105 * One copy from inptr to the end of the buffer, and the |
126 * One copy from input to the end of the buffer, and the |
| 106 * second copy from the start of the buffer to the end of src. */ |
127 * second copy from the start of the buffer to the end of src. */ |
| 107 if (buf->inptr >= buf->outptr) |
128 if(priv->input >= priv->output) |
| 108 len_stored = MIN(len, buf->buflen |
129 len_stored = MIN(len, priv->buflen - (priv->input - priv->buffer)); |
| 109 - (buf->inptr - buf->buffer)); |
|
| 110 else |
130 else |
| 111 len_stored = len; |
131 len_stored = len; |
| 112 |
132 |
| 113 if (len_stored > 0) |
133 if(len_stored > 0) |
| 114 memcpy(buf->inptr, src, len_stored); |
134 memcpy(priv->input, src, len_stored); |
| 115 |
135 |
| 116 if (len_stored < len) { |
136 if(len_stored < len) { |
| 117 memcpy(buf->buffer, (char*)src + len_stored, len - len_stored); |
137 memcpy(priv->buffer, (char*)src + len_stored, len - len_stored); |
| 118 buf->inptr = buf->buffer + (len - len_stored); |
138 priv->input = priv->buffer + (len - len_stored); |
| 119 } else { |
139 } else { |
| 120 buf->inptr += len_stored; |
140 priv->input += len_stored; |
| 121 } |
141 } |
| 122 |
142 |
| 123 buf->bufused += len; |
143 priv->bufused += len; |
| 124 } |
144 } |
| 125 |
145 |
| 126 gsize purple_circ_buffer_get_max_read(const PurpleCircBuffer *buf) { |
146 static gsize |
| |
147 purple_circular_buffer_real_max_read_size(const PurpleCircularBuffer *buffer) { |
| |
148 PurpleCircularBufferPrivate *priv = NULL; |
| 127 gsize max_read; |
149 gsize max_read; |
| 128 |
150 |
| 129 g_return_val_if_fail(buf != NULL, 0); |
151 priv = PURPLE_CIRCULAR_BUFFER_GET_PRIVATE(buffer); |
| 130 |
152 |
| 131 if (buf->bufused == 0) |
153 if(priv->bufused == 0) |
| 132 max_read = 0; |
154 max_read = 0; |
| 133 else if ((buf->outptr - buf->inptr) >= 0) |
155 else if((priv->output - priv->input) >= 0) |
| 134 max_read = buf->buflen - (buf->outptr - buf->buffer); |
156 max_read = priv->buflen - (priv->output - priv->buffer); |
| 135 else |
157 else |
| 136 max_read = buf->inptr - buf->outptr; |
158 max_read = priv->input - priv->output; |
| 137 |
159 |
| 138 return max_read; |
160 return max_read; |
| 139 } |
161 } |
| 140 |
162 |
| 141 gboolean purple_circ_buffer_mark_read(PurpleCircBuffer *buf, gsize len) { |
163 static gboolean |
| 142 g_return_val_if_fail(buf != NULL, FALSE); |
164 purple_circular_buffer_real_mark_read(PurpleCircularBuffer *buffer, |
| 143 g_return_val_if_fail(purple_circ_buffer_get_max_read(buf) >= len, FALSE); |
165 gsize len) |
| 144 |
166 { |
| 145 buf->outptr += len; |
167 PurpleCircularBufferPrivate *priv = NULL; |
| 146 buf->bufused -= len; |
168 |
| |
169 g_return_val_if_fail(purple_circular_buffer_get_max_read(buffer) >= len, FALSE); |
| |
170 |
| |
171 priv = PURPLE_CIRCULAR_BUFFER_GET_PRIVATE(buffer); |
| |
172 |
| |
173 priv->output += len; |
| |
174 priv->bufused -= len; |
| |
175 |
| 147 /* wrap to the start if we're at the end */ |
176 /* wrap to the start if we're at the end */ |
| 148 if ((buf->outptr - buf->buffer) == buf->buflen) |
177 if((priv->output - priv->buffer) == priv->buflen) |
| 149 buf->outptr = buf->buffer; |
178 priv->output = priv->buffer; |
| 150 |
179 |
| 151 return TRUE; |
180 return TRUE; |
| 152 } |
181 } |
| 153 |
182 |
| |
183 /****************************************************************************** |
| |
184 * Private API |
| |
185 *****************************************************************************/ |
| |
186 static void |
| |
187 purple_circular_buffer_set_grow_size(PurpleCircularBuffer *buffer, |
| |
188 gsize grow_size) |
| |
189 { |
| |
190 PurpleCircularBufferPrivate *priv = |
| |
191 PURPLE_CIRCULAR_BUFFER_GET_PRIVATE(buffer); |
| |
192 |
| |
193 priv->growsize = (grow_size != 0) ? grow_size : DEFAULT_BUF_SIZE; |
| |
194 |
| |
195 g_object_notify(G_OBJECT(buffer), "grow-size"); |
| |
196 } |
| |
197 |
| |
198 /****************************************************************************** |
| |
199 * Object Stuff |
| |
200 *****************************************************************************/ |
| |
201 static void |
| |
202 purple_circular_buffer_finalize(GObject *obj) { |
| |
203 PurpleCircularBufferPrivate *priv = |
| |
204 PURPLE_CIRCULAR_BUFFER_GET_PRIVATE(obj); |
| |
205 |
| |
206 g_free(priv->buffer); |
| |
207 |
| |
208 G_OBJECT_CLASS(parent_class)->finalize(obj); |
| |
209 } |
| |
210 |
| |
211 static void |
| |
212 purple_circular_buffer_get_property(GObject *obj, guint param_id, |
| |
213 GValue *value, GParamSpec *pspec) |
| |
214 { |
| |
215 PurpleCircularBuffer *buffer = PURPLE_CIRCULAR_BUFFER(obj); |
| |
216 |
| |
217 switch(param_id) { |
| |
218 case PROP_GROW_SIZE: |
| |
219 g_value_set_ulong(value, |
| |
220 purple_circular_buffer_get_grow_size(buffer)); |
| |
221 break; |
| |
222 case PROP_BUFFER_USED: |
| |
223 g_value_set_ulong(value, |
| |
224 purple_circular_buffer_get_used(buffer)); |
| |
225 break; |
| |
226 case PROP_INPUT: |
| |
227 g_value_set_pointer(value, |
| |
228 (void*) purple_circular_buffer_get_input(buffer)); |
| |
229 break; |
| |
230 case PROP_OUTPUT: |
| |
231 g_value_set_pointer(value, |
| |
232 (void*) purple_circular_buffer_get_output(buffer)); |
| |
233 break; |
| |
234 default: |
| |
235 G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec); |
| |
236 break; |
| |
237 } |
| |
238 } |
| |
239 |
| |
240 static void |
| |
241 purple_circular_buffer_set_property(GObject *obj, guint param_id, |
| |
242 const GValue *value, GParamSpec *pspec) |
| |
243 { |
| |
244 PurpleCircularBuffer *buffer = PURPLE_CIRCULAR_BUFFER(obj); |
| |
245 |
| |
246 switch(param_id) { |
| |
247 case PROP_GROW_SIZE: |
| |
248 purple_circular_buffer_set_grow_size(buffer, |
| |
249 g_value_get_uint(value)); |
| |
250 break; |
| |
251 default: |
| |
252 G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec); |
| |
253 break; |
| |
254 } |
| |
255 } |
| |
256 |
| |
257 static void |
| |
258 purple_circular_buffer_class_init(PurpleCircularBufferClass *klass) { |
| |
259 GObjectClass *obj_class = G_OBJECT_CLASS(klass); |
| |
260 PurpleCircularBufferClass *buffer_class = PURPLE_CIRCULAR_BUFFER_CLASS(klass); |
| |
261 |
| |
262 parent_class = g_type_class_peek_parent(klass); |
| |
263 |
| |
264 g_type_class_add_private(klass, sizeof(PurpleCircularBufferPrivate)); |
| |
265 |
| |
266 obj_class->finalize = purple_circular_buffer_finalize; |
| |
267 obj_class->get_property = purple_circular_buffer_get_property; |
| |
268 obj_class->set_property = purple_circular_buffer_set_property; |
| |
269 |
| |
270 buffer_class->grow = purple_circular_buffer_real_grow; |
| |
271 buffer_class->append = purple_circular_buffer_real_append; |
| |
272 buffer_class->max_read_size = purple_circular_buffer_real_max_read_size; |
| |
273 buffer_class->mark_read = purple_circular_buffer_real_mark_read; |
| |
274 |
| |
275 /* using a ulong for the gsize properties since there is no |
| |
276 * g_param_spec_size, and the ulong should always work. --gk 3/21/11 |
| |
277 */ |
| |
278 g_object_class_install_property(obj_class, PROP_GROW_SIZE, |
| |
279 g_param_spec_ulong("grow-size", "grow-size", |
| |
280 "The grow size of the buffer", |
| |
281 0, G_MAXSIZE, 0, |
| |
282 G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); |
| |
283 |
| |
284 g_object_class_install_property(obj_class, PROP_BUFFER_USED, |
| |
285 g_param_spec_ulong("buffer-used", "buffer-used", |
| |
286 "The amount of the buffer used", |
| |
287 0, G_MAXSIZE, 0, |
| |
288 G_PARAM_READABLE)); |
| |
289 |
| |
290 g_object_class_install_property(obj_class, PROP_INPUT, |
| |
291 g_param_spec_pointer("input", "input", |
| |
292 "The input pointer of the buffer", |
| |
293 G_PARAM_READABLE)); |
| |
294 |
| |
295 g_object_class_install_property(obj_class, PROP_OUTPUT, |
| |
296 g_param_spec_pointer("output", "output", |
| |
297 "The output pointer of the buffer", |
| |
298 G_PARAM_READABLE)); |
| |
299 } |
| |
300 |
| |
301 /****************************************************************************** |
| |
302 * API |
| |
303 *****************************************************************************/ |
| |
304 GType |
| |
305 purple_circular_buffer_get_type(void) { |
| |
306 static GType type = 0; |
| |
307 |
| |
308 if(G_UNLIKELY(type == 0)) { |
| |
309 static const GTypeInfo info = { |
| |
310 .class_size = sizeof(PurpleCircularBufferClass), |
| |
311 .class_init = (GClassInitFunc)purple_circular_buffer_class_init, |
| |
312 .instance_size = sizeof(PurpleCircularBuffer), |
| |
313 }; |
| |
314 |
| |
315 type = g_type_register_static(G_TYPE_OBJECT, |
| |
316 "PurpleCircularBuffer", |
| |
317 &info, 0); |
| |
318 } |
| |
319 |
| |
320 return type; |
| |
321 } |
| |
322 |
| |
323 PurpleCircularBuffer * |
| |
324 purple_circular_buffer_new(gsize growsize) { |
| |
325 return g_object_new(PURPLE_TYPE_CIRCULAR_BUFFER, |
| |
326 "grow-size", growsize ? growsize : DEFAULT_BUF_SIZE, |
| |
327 NULL); |
| |
328 } |
| |
329 |
| |
330 void |
| |
331 purple_circular_buffer_grow(PurpleCircularBuffer *buffer, gsize len) { |
| |
332 PurpleCircularBufferClass *klass = NULL; |
| |
333 |
| |
334 g_return_if_fail(PURPLE_IS_CIRCULAR_BUFFER(buffer)); |
| |
335 |
| |
336 klass = PURPLE_CIRCULAR_BUFFER_GET_CLASS(buffer); |
| |
337 if(klass && klass->grow) |
| |
338 klass->grow(buffer, len); |
| |
339 } |
| |
340 |
| |
341 void |
| |
342 purple_circular_buffer_append(PurpleCircularBuffer *buffer, gconstpointer src, |
| |
343 gsize len) |
| |
344 { |
| |
345 PurpleCircularBufferClass *klass = NULL; |
| |
346 |
| |
347 g_return_if_fail(PURPLE_IS_CIRCULAR_BUFFER(buffer)); |
| |
348 g_return_if_fail(src != NULL); |
| |
349 |
| |
350 klass = PURPLE_CIRCULAR_BUFFER_GET_CLASS(buffer); |
| |
351 if(klass && klass->append) |
| |
352 klass->append(buffer, src, len); |
| |
353 } |
| |
354 |
| |
355 gsize |
| |
356 purple_circular_buffer_get_max_read(const PurpleCircularBuffer *buffer) { |
| |
357 PurpleCircularBufferClass *klass = NULL; |
| |
358 |
| |
359 g_return_val_if_fail(PURPLE_IS_CIRCULAR_BUFFER(buffer), 0); |
| |
360 |
| |
361 klass = PURPLE_CIRCULAR_BUFFER_GET_CLASS(buffer); |
| |
362 if(klass && klass->max_read_size) |
| |
363 return klass->max_read_size(buffer); |
| |
364 |
| |
365 return 0; |
| |
366 } |
| |
367 |
| |
368 gboolean |
| |
369 purple_circular_buffer_mark_read(PurpleCircularBuffer *buffer, gsize len) { |
| |
370 PurpleCircularBufferClass *klass = NULL; |
| |
371 |
| |
372 g_return_val_if_fail(PURPLE_IS_CIRCULAR_BUFFER(buffer), FALSE); |
| |
373 |
| |
374 klass = PURPLE_CIRCULAR_BUFFER_CLASS(buffer); |
| |
375 if(klass && klass->mark_read) |
| |
376 return klass->mark_read(buffer, len); |
| |
377 |
| |
378 return FALSE; |
| |
379 } |
| |
380 |
| |
381 gsize |
| |
382 purple_circular_buffer_get_grow_size(const PurpleCircularBuffer *buffer) { |
| |
383 |
| |
384 PurpleCircularBufferPrivate *priv = NULL; |
| |
385 |
| |
386 g_return_val_if_fail(PURPLE_IS_CIRCULAR_BUFFER(buffer), 0); |
| |
387 |
| |
388 priv = PURPLE_CIRCULAR_BUFFER_GET_PRIVATE(buffer); |
| |
389 |
| |
390 return priv->growsize; |
| |
391 } |
| |
392 |
| |
393 gsize |
| |
394 purple_circular_buffer_get_used(const PurpleCircularBuffer *buffer) { |
| |
395 PurpleCircularBufferPrivate *priv = NULL; |
| |
396 |
| |
397 g_return_val_if_fail(PURPLE_IS_CIRCULAR_BUFFER(buffer), 0); |
| |
398 |
| |
399 priv = PURPLE_CIRCULAR_BUFFER_GET_PRIVATE(buffer); |
| |
400 |
| |
401 return priv->bufused; |
| |
402 } |
| |
403 |
| |
404 const gchar * |
| |
405 purple_circular_buffer_get_input(const PurpleCircularBuffer *buffer) { |
| |
406 PurpleCircularBufferPrivate *priv = NULL; |
| |
407 |
| |
408 g_return_val_if_fail(PURPLE_IS_CIRCULAR_BUFFER(buffer), NULL); |
| |
409 |
| |
410 priv = PURPLE_CIRCULAR_BUFFER_GET_PRIVATE(buffer); |
| |
411 |
| |
412 return priv->input; |
| |
413 } |
| |
414 |
| |
415 const gchar * |
| |
416 purple_circular_buffer_get_output(const PurpleCircularBuffer *buffer) { |
| |
417 PurpleCircularBufferPrivate *priv = NULL; |
| |
418 |
| |
419 g_return_val_if_fail(PURPLE_IS_CIRCULAR_BUFFER(buffer), NULL); |
| |
420 |
| |
421 priv = PURPLE_CIRCULAR_BUFFER_GET_PRIVATE(buffer); |
| |
422 |
| |
423 return priv->output; |
| |
424 } |
| |
425 |
| |
426 void |
| |
427 purple_circular_buffer_reset(PurpleCircularBuffer *buffer) { |
| |
428 PurpleCircularBufferPrivate *priv = NULL; |
| |
429 GObject *obj = NULL; |
| |
430 |
| |
431 g_return_if_fail(PURPLE_IS_CIRCULAR_BUFFER(buffer)); |
| |
432 |
| |
433 priv = PURPLE_CIRCULAR_BUFFER_GET_PRIVATE(buffer); |
| |
434 |
| |
435 priv->input = priv->buffer; |
| |
436 priv->output = priv->buffer; |
| |
437 |
| |
438 obj = G_OBJECT(buffer); |
| |
439 g_object_freeze_notify(obj); |
| |
440 g_object_notify(obj, "input"); |
| |
441 g_object_notify(obj, "output"); |
| |
442 g_object_thaw_notify(obj); |
| |
443 } |
| |
444 |