Recognizes if input is ogg or not.
[xiph.git] / ogg2 / src / buffer.c
blob7d141ed2b781305844839ac7192294e383937d3c
1 /********************************************************************
2 * *
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. *
7 * *
8 * THE Ogg Reference Library SOURCE CODE IS (C) COPYRIGHT 1994-2004 *
9 * by the Xiph.Org Foundation http://www.xiph.org/ *
10 * *
11 ********************************************************************
13 function: centralized fragment buffer management
14 last mod: $Id$
16 ********************************************************************/
18 #ifdef OGGBUFFER_DEBUG
19 #include <stdio.h>
20 #endif
22 #include <stdlib.h>
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
28 reference form. */
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);
36 return bs;
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){
47 ogg2_buffer *bt;
48 ogg2_reference *rt;
50 if(bs->shutdown){
52 #ifdef OGGBUFFER_DEBUG
53 fprintf(stderr,"\nZero-copy pool %p lazy destroy: %d buffers outstanding.\n",
54 bs,bs->outstanding);
55 if(!bs->outstanding)
56 fprintf(stderr,"Finishing memory cleanup of %p.\n",bs);
57 #endif
59 bt=bs->unused_buffers;
60 rt=bs->unused_references;
62 if(!bs->outstanding){
63 ogg2_mutex_unlock(&bs->mutex);
64 ogg2_mutex_clear(&bs->mutex);
65 _ogg_free(bs);
66 return;
67 }else
68 ogg2_mutex_unlock(&bs->mutex);
70 while(bt){
71 ogg2_buffer *b=bt;
72 bt=b->ptr.next;
73 if(b->data)_ogg_free(b->data);
74 _ogg_free(b);
76 bs->unused_buffers=0;
77 while(rt){
78 ogg2_reference *r=rt;
79 rt=r->next;
80 _ogg_free(r);
82 bs->unused_references=0;
86 void ogg2_buffer_destroy(ogg2_buffer_state *bs){
87 ogg2_mutex_lock(&bs->mutex);
88 bs->shutdown=1;
89 _ogg2_buffer_destroy(bs);
92 static ogg2_buffer *_fetch_buffer(ogg2_buffer_state *bs,long bytes){
93 ogg2_buffer *ob;
94 ogg2_mutex_lock(&bs->mutex);
95 bs->outstanding++;
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 */
104 if(ob->size<bytes){
105 ob->data=_ogg_realloc(ob->data,bytes);
106 ob->size=bytes;
108 }else{
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);
114 ob->size=bytes;
117 ob->refcount=1;
118 ob->ptr.owner=bs;
119 return ob;
122 static ogg2_reference *_fetch_ref(ogg2_buffer_state *bs){
123 ogg2_reference *or;
124 ogg2_mutex_lock(&bs->mutex);
125 bs->outstanding++;
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);
132 }else{
133 /* allocate a new reference */
134 ogg2_mutex_unlock(&bs->mutex);
135 or=_ogg_malloc(sizeof(*or));
138 or->begin=0;
139 or->length=0;
140 or->next=0;
141 #ifdef OGGBUFFER_DEBUG
142 or->used=1;
143 #endif
144 return or;
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);
152 or->buffer=ob;
153 return or;
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 */
161 if(ob->size<bytes){
162 ob->data=_ogg_realloc(ob->data,bytes);
163 ob->size=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
178 if(or->used==0){
179 fprintf(stderr,"\nERROR: Using reference marked as usused.\n");
180 exit(1);
182 #endif
183 begin-=or->length;
184 or=or->next;
187 /* duplicate the reference chain; increment refcounts */
188 while(or && length){
189 ogg2_reference *temp=_fetch_ref(or->buffer->ptr.owner);
190 if(head)
191 head->next=temp;
192 else
193 ret=temp;
194 head=temp;
196 #ifdef OGGBUFFER_DEBUG
197 if(or->used==0){
198 fprintf(stderr,"\nERROR: Using reference marked as usused.\n");
199 exit(1);
201 #endif
203 head->buffer=or->buffer;
205 head->begin=or->begin+begin;
206 head->length=length;
207 if(head->length>or->length-begin)
208 head->length=or->length-begin;
210 begin=0;
211 length-=head->length;
212 or=or->next;
215 ogg2_buffer_mark(ret);
216 return ret;
219 ogg2_reference *ogg2_buffer_dup(ogg2_reference *or){
220 ogg2_reference *ret=0,*head=0;
222 /* duplicate the reference chain; increment refcounts */
223 while(or){
224 ogg2_reference *temp=_fetch_ref(or->buffer->ptr.owner);
225 if(head)
226 head->next=temp;
227 else
228 ret=temp;
229 head=temp;
231 #ifdef OGGBUFFER_DEBUG
232 if(or->used==0){
233 fprintf(stderr,"\nERROR: Using reference marked as usused.\n");
234 exit(1);
236 #endif
238 head->buffer=or->buffer;
239 head->begin=or->begin;
240 head->length=or->length;
241 or=or->next;
244 ogg2_buffer_mark(ret);
245 return 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
251 pools */
253 #ifdef OGGBUFFER_DEBUG
254 if(or->buffer->refcount==0){
255 fprintf(stderr,"WARNING: marking buffer fragment with refcount of zero!\n");
256 exit(1);
258 if(or->used==0){
259 fprintf(stderr,"\nERROR: Using reference marked as usused.\n");
260 exit(1);
262 #endif
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
283 if(or->used==0){
284 fprintf(stderr,"\nERROR: Using reference marked as usused.\n");
285 exit(1);
287 #endif
288 pos-=or->length;
289 or=or->next;
292 if(!or || pos==0){
294 return 0;
296 }else{
298 if(pos>=or->length){
299 /* exact split, or off the end? */
300 if(or->next){
302 /* a split */
303 *tail=or->next;
304 or->next=0;
306 }else{
308 /* off or at the end */
309 *tail=*head=0;
312 }else{
314 /* split within a fragment */
315 long lengthA=pos;
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 */
330 or->next=0;
331 or->length=lengthA;
335 return ret;
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){
340 if(or){
341 #ifdef OGGBUFFER_DEBUG
342 if(or->used==0){
343 fprintf(stderr,"\nERROR: Using reference marked as usused.\n");
344 exit(1);
346 #endif
347 while(or->next){
348 or=or->next;
349 #ifdef OGGBUFFER_DEBUG
350 if(or->used==0){
351 fprintf(stderr,"\nERROR: Using reference marked as usused.\n");
352 exit(1);
354 #endif
356 or->next=ogg2_buffer_alloc(or->buffer->ptr.owner,bytes);
357 return(or->next);
359 return 0;
362 /* increase the refcount of the buffers to which the reference points */
363 void ogg2_buffer_mark(ogg2_reference *or){
364 while(or){
365 _ogg2_buffer_mark_one(or);
366 or=or->next;
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
376 if(ob->refcount==0){
377 ogg2_mutex_unlock(&bs->mutex);
378 fprintf(stderr,"WARNING: releasing buffer fragment with refcount of zero!\n");
379 exit(1);
381 if(or->used==0){
382 ogg2_mutex_unlock(&bs->mutex);
383 fprintf(stderr,"WARNING: releasing previously released reference!\n");
384 exit(1);
386 or->used=0;
387 #endif
389 ob->refcount--;
390 if(ob->refcount==0){
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){
408 while(or){
409 ogg2_reference *next=or->next;
410 ogg2_buffer_release_one(or);
411 or=next;
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;
419 pos-=or->length;
420 ogg2_buffer_release_one(or);
421 or=next;
423 if (or) {
424 #ifdef OGGBUFFER_DEBUG
425 if(or->used==0){
426 fprintf(stderr,"\nERROR: Using reference marked as usused.\n");
427 exit(1);
429 #endif
430 or->begin+=pos;
431 or->length-=pos;
433 return or;
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
440 if(or->used==0){
441 fprintf(stderr,"\nERROR: Using reference marked as usused.\n");
442 exit(1);
444 #endif
445 pos-=or->length;
446 or=or->next;
448 if(or){
449 /* release or->next and beyond */
450 ogg2_buffer_release(or->next);
451 or->next=0;
452 /* update length fencepost */
453 or->length=pos;
457 ogg2_reference *ogg2_buffer_walk(ogg2_reference *or){
458 if(!or)return NULL;
459 while(or->next){
460 #ifdef OGGBUFFER_DEBUG
461 if(or->used==0){
462 fprintf(stderr,"\nERROR: Using reference marked as usused.\n");
463 exit(1);
465 #endif
466 or=or->next;
468 #ifdef OGGBUFFER_DEBUG
469 if(or->used==0){
470 fprintf(stderr,"\nERROR: Using reference marked as usused.\n");
471 exit(1);
473 #endif
474 return(or);
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;
482 while(tail->next){
483 #ifdef OGGBUFFER_DEBUG
484 if(tail->used==0){
485 fprintf(stderr,"\nERROR: Using reference marked as usused.\n");
486 exit(1);
488 #endif
489 tail=tail->next;
491 #ifdef OGGBUFFER_DEBUG
492 if(tail->used==0){
493 fprintf(stderr,"\nERROR: Using reference marked as usused.\n");
494 exit(1);
496 #endif
497 tail->next=head;
498 return ogg2_buffer_walk(head);
501 long ogg2_buffer_length(ogg2_reference *or){
502 int count=0;
503 while(or){
504 #ifdef OGGBUFFER_DEBUG
505 if(or->used==0){
506 fprintf(stderr,"\nERROR: Using reference marked as usused.\n");
507 exit(1);
509 #endif
510 count+=or->length;
511 or=or->next;
513 return count;
516 void ogg2_buffer_outstanding(ogg2_buffer_state *bs){
517 #ifdef OGGBUFFER_DEBUG
518 fprintf(stderr,"Zero-copy pool %p: %d buffers outstanding.\n",
519 bs,bs->outstanding);
520 #endif