1 /********************************************************************
3 * THIS FILE IS PART OF THE Ogg Reference Library SOURCE CODE. *
4 * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS *
5 * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE *
6 * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. *
8 * THE Ogg Reference Library SOURCE CODE IS (C) COPYRIGHT 1994-2004 *
9 * by the Xiph.Org Foundation http://www.xiph.org/ *
11 ********************************************************************
13 function: centralized fragment buffer management
16 ********************************************************************/
18 #ifdef OGGBUFFER_DEBUG
23 #include "ogginternal.h"
25 /* basic, centralized Ogg memory management based on linked lists of
26 references to refcounted memory buffers. References and buffers
27 are both recycled. Buffers are passed around and consumed in
30 /* management is here; actual production and consumption of data is
31 found in the rest of the libogg code */
33 ogg2_buffer_state
*ogg2_buffer_create(void){
34 ogg2_buffer_state
*bs
=_ogg_calloc(1,sizeof(*bs
));
35 ogg2_mutex_init(&bs
->mutex
);
39 /* destruction is 'lazy'; there may be memory references outstanding,
40 and yanking the buffer state out from underneath would be
41 antisocial. Dealloc what is currently unused and have
42 _release_one watch for the stragglers to come in. When they do,
43 finish destruction. */
45 /* call the helper while holding lock */
46 static void _ogg2_buffer_destroy(ogg2_buffer_state
*bs
){
52 #ifdef OGGBUFFER_DEBUG
53 fprintf(stderr
,"\nZero-copy pool %p lazy destroy: %d buffers outstanding.\n",
56 fprintf(stderr
,"Finishing memory cleanup of %p.\n",bs
);
59 bt
=bs
->unused_buffers
;
60 rt
=bs
->unused_references
;
63 ogg2_mutex_unlock(&bs
->mutex
);
64 ogg2_mutex_clear(&bs
->mutex
);
68 ogg2_mutex_unlock(&bs
->mutex
);
73 if(b
->data
)_ogg_free(b
->data
);
82 bs
->unused_references
=0;
86 void ogg2_buffer_destroy(ogg2_buffer_state
*bs
){
87 ogg2_mutex_lock(&bs
->mutex
);
89 _ogg2_buffer_destroy(bs
);
92 static ogg2_buffer
*_fetch_buffer(ogg2_buffer_state
*bs
,long bytes
){
94 ogg2_mutex_lock(&bs
->mutex
);
97 /* do we have an unused buffer sitting in the pool? */
98 if(bs
->unused_buffers
){
99 ob
=bs
->unused_buffers
;
100 bs
->unused_buffers
=ob
->ptr
.next
;
101 ogg2_mutex_unlock(&bs
->mutex
);
103 /* if the unused buffer is too small, grow it */
105 ob
->data
=_ogg_realloc(ob
->data
,bytes
);
109 /* allocate a new buffer */
110 ogg2_mutex_unlock(&bs
->mutex
);
111 ob
=_ogg_malloc(sizeof(*ob
));
112 ob
->data
=_ogg_malloc(bytes
<OGG2PACK_MINCHUNKSIZE
?
113 OGG2PACK_MINCHUNKSIZE
:bytes
);
122 static ogg2_reference
*_fetch_ref(ogg2_buffer_state
*bs
){
124 ogg2_mutex_lock(&bs
->mutex
);
127 /* do we have an unused reference sitting in the pool? */
128 if(bs
->unused_references
){
129 or=bs
->unused_references
;
130 bs
->unused_references
=or->next
;
131 ogg2_mutex_unlock(&bs
->mutex
);
133 /* allocate a new reference */
134 ogg2_mutex_unlock(&bs
->mutex
);
135 or=_ogg_malloc(sizeof(*or));
141 #ifdef OGGBUFFER_DEBUG
147 /* fetch a reference pointing to a fresh, initially continguous buffer
148 of at least [bytes] length */
149 ogg2_reference
*ogg2_buffer_alloc(ogg2_buffer_state
*bs
,long bytes
){
150 ogg2_buffer
*ob
=_fetch_buffer(bs
,bytes
);
151 ogg2_reference
*or=_fetch_ref(bs
);
156 /* enlarge the data buffer in the current link */
157 void ogg2_buffer_realloc(ogg2_reference
*or,long bytes
){
158 ogg2_buffer
*ob
=or->buffer
;
160 /* if the unused buffer is too small, grow it */
162 ob
->data
=_ogg_realloc(ob
->data
,bytes
);
167 /* duplicate a reference (pointing to the same actual buffer memory)
168 and increment buffer refcount. If the desired segment begins out
169 of range, NULL is returned; if the desired segment is simply zero
170 length, a zero length ref is returned. Partial range overlap
171 returns the overlap of the ranges */
172 ogg2_reference
*ogg2_buffer_sub(ogg2_reference
*or,long begin
,long length
){
173 ogg2_reference
*ret
=0,*head
=0;
175 /* walk past any preceeding fragments we don't want */
176 while(or && begin
>=or->length
){
177 #ifdef OGGBUFFER_DEBUG
179 fprintf(stderr
,"\nERROR: Using reference marked as usused.\n");
187 /* duplicate the reference chain; increment refcounts */
189 ogg2_reference
*temp
=_fetch_ref(or->buffer
->ptr
.owner
);
196 #ifdef OGGBUFFER_DEBUG
198 fprintf(stderr
,"\nERROR: Using reference marked as usused.\n");
203 head
->buffer
=or->buffer
;
205 head
->begin
=or->begin
+begin
;
207 if(head
->length
>or->length
-begin
)
208 head
->length
=or->length
-begin
;
211 length
-=head
->length
;
215 ogg2_buffer_mark(ret
);
219 ogg2_reference
*ogg2_buffer_dup(ogg2_reference
*or){
220 ogg2_reference
*ret
=0,*head
=0;
222 /* duplicate the reference chain; increment refcounts */
224 ogg2_reference
*temp
=_fetch_ref(or->buffer
->ptr
.owner
);
231 #ifdef OGGBUFFER_DEBUG
233 fprintf(stderr
,"\nERROR: Using reference marked as usused.\n");
238 head
->buffer
=or->buffer
;
239 head
->begin
=or->begin
;
240 head
->length
=or->length
;
244 ogg2_buffer_mark(ret
);
248 static void _ogg2_buffer_mark_one(ogg2_reference
*or){
249 ogg2_buffer_state
*bs
=or->buffer
->ptr
.owner
;
250 ogg2_mutex_lock(&bs
->mutex
); /* lock now in case someone is mixing
253 #ifdef OGGBUFFER_DEBUG
254 if(or->buffer
->refcount
==0){
255 fprintf(stderr
,"WARNING: marking buffer fragment with refcount of zero!\n");
259 fprintf(stderr
,"\nERROR: Using reference marked as usused.\n");
264 or->buffer
->refcount
++;
265 ogg2_mutex_unlock(&bs
->mutex
);
268 /* split a reference into two references; 'return' is a reference to
269 the buffer preceeding pos and 'head'/'tail' are the buffer past the
270 split. If pos is at or past the end of the passed in segment,
271 'head/tail' are NULL */
272 ogg2_reference
*ogg2_buffer_split(ogg2_reference
**tail
,
273 ogg2_reference
**head
,long pos
){
275 /* walk past any preceeding fragments to one of:
276 a) the exact boundary that seps two fragments
277 b) the fragment that needs split somewhere in the middle */
278 ogg2_reference
*ret
=*tail
;
279 ogg2_reference
*or=*tail
;
281 while(or && pos
>or->length
){
282 #ifdef OGGBUFFER_DEBUG
284 fprintf(stderr
,"\nERROR: Using reference marked as usused.\n");
299 /* exact split, or off the end? */
308 /* off or at the end */
314 /* split within a fragment */
316 long beginB
=or->begin
+pos
;
317 long lengthB
=or->length
-pos
;
319 /* make a new reference to tail the second piece */
320 *tail
=_fetch_ref(or->buffer
->ptr
.owner
);
322 (*tail
)->buffer
=or->buffer
;
323 (*tail
)->begin
=beginB
;
324 (*tail
)->length
=lengthB
;
325 (*tail
)->next
=or->next
;
326 _ogg2_buffer_mark_one(*tail
);
327 if(head
&& or==*head
)*head
=*tail
;
329 /* update the first piece */
338 /* add a new fragment link to the end of a chain; return ptr to the new link */
339 ogg2_reference
*ogg2_buffer_extend(ogg2_reference
*or,long bytes
){
341 #ifdef OGGBUFFER_DEBUG
343 fprintf(stderr
,"\nERROR: Using reference marked as usused.\n");
349 #ifdef OGGBUFFER_DEBUG
351 fprintf(stderr
,"\nERROR: Using reference marked as usused.\n");
356 or->next
=ogg2_buffer_alloc(or->buffer
->ptr
.owner
,bytes
);
362 /* increase the refcount of the buffers to which the reference points */
363 void ogg2_buffer_mark(ogg2_reference
*or){
365 _ogg2_buffer_mark_one(or);
370 void ogg2_buffer_release_one(ogg2_reference
*or){
371 ogg2_buffer
*ob
=or->buffer
;
372 ogg2_buffer_state
*bs
=ob
->ptr
.owner
;
373 ogg2_mutex_lock(&bs
->mutex
);
375 #ifdef OGGBUFFER_DEBUG
377 ogg2_mutex_unlock(&bs
->mutex
);
378 fprintf(stderr
,"WARNING: releasing buffer fragment with refcount of zero!\n");
382 ogg2_mutex_unlock(&bs
->mutex
);
383 fprintf(stderr
,"WARNING: releasing previously released reference!\n");
391 bs
->outstanding
--; /* for the returned buffer */
392 ob
->ptr
.next
=bs
->unused_buffers
;
393 bs
->unused_buffers
=ob
;
396 bs
->outstanding
--; /* for the returned reference */
397 or->next
=bs
->unused_references
;
398 bs
->unused_references
=or;
399 ogg2_mutex_unlock(&bs
->mutex
);
401 _ogg2_buffer_destroy(bs
); /* lazy cleanup (if needed) */
405 /* release the references, decrease the refcounts of buffers to which
406 they point, release any buffers with a refcount that drops to zero */
407 void ogg2_buffer_release(ogg2_reference
*or){
409 ogg2_reference
*next
=or->next
;
410 ogg2_buffer_release_one(or);
415 ogg2_reference
*ogg2_buffer_pretruncate(ogg2_reference
*or,long pos
){
416 /* release preceeding fragments we don't want */
417 while(or && pos
>=or->length
){
418 ogg2_reference
*next
=or->next
;
420 ogg2_buffer_release_one(or);
424 #ifdef OGGBUFFER_DEBUG
426 fprintf(stderr
,"\nERROR: Using reference marked as usused.\n");
436 void ogg2_buffer_posttruncate(ogg2_reference
*or,long pos
){
437 /* walk to the point where we want to begin truncate */
438 while(or && pos
>or->length
){
439 #ifdef OGGBUFFER_DEBUG
441 fprintf(stderr
,"\nERROR: Using reference marked as usused.\n");
449 /* release or->next and beyond */
450 ogg2_buffer_release(or->next
);
452 /* update length fencepost */
457 ogg2_reference
*ogg2_buffer_walk(ogg2_reference
*or){
460 #ifdef OGGBUFFER_DEBUG
462 fprintf(stderr
,"\nERROR: Using reference marked as usused.\n");
468 #ifdef OGGBUFFER_DEBUG
470 fprintf(stderr
,"\nERROR: Using reference marked as usused.\n");
477 /* *head is appended to the front end (head) of *tail; both continue to
478 be valid pointers, with *tail at the tail and *head at the head */
479 ogg2_reference
*ogg2_buffer_cat(ogg2_reference
*tail
, ogg2_reference
*head
){
480 if(!tail
)return head
;
483 #ifdef OGGBUFFER_DEBUG
485 fprintf(stderr
,"\nERROR: Using reference marked as usused.\n");
491 #ifdef OGGBUFFER_DEBUG
493 fprintf(stderr
,"\nERROR: Using reference marked as usused.\n");
498 return ogg2_buffer_walk(head
);
501 long ogg2_buffer_length(ogg2_reference
*or){
504 #ifdef OGGBUFFER_DEBUG
506 fprintf(stderr
,"\nERROR: Using reference marked as usused.\n");
516 void ogg2_buffer_outstanding(ogg2_buffer_state
*bs
){
517 #ifdef OGGBUFFER_DEBUG
518 fprintf(stderr
,"Zero-copy pool %p: %d buffers outstanding.\n",