LGPL
[vlc.git] / include / vlc_block_helper.h
1 /*****************************************************************************
2  * vlc_block_helper.h: Helper functions for data blocks management.
3  *****************************************************************************
4  * Copyright (C) 2003 VLC authors and VideoLAN
5  * $Id$
6  *
7  * Authors: Gildas Bazin <gbazin@netcourrier.com>
8  *
9  * This program is free software; you can redistribute it and/or modify it
10  * under the terms of the GNU Lesser General Public License as published by
11  * the Free Software Foundation; either version 2.1 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public License
20  * along with this program; if not, write to the Free Software Foundation,
21  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 #ifndef VLC_BLOCK_HELPER_H
25 #define VLC_BLOCK_HELPER_H 1
26
27 #include <vlc_block.h>
28
29 typedef struct block_bytestream_t
30 {
31     block_t *p_chain;  /**< byte stream head block */
32     block_t *p_block;  /**< byte stream read pointer block */
33     size_t   i_offset; /**< byte stream read pointer offset within block */
34     /* TODO? add tail pointer for faster push? */
35 } block_bytestream_t;
36
37 /*****************************************************************************
38  * block_bytestream_t management
39  *****************************************************************************/
40 static inline void block_BytestreamInit( block_bytestream_t *p_bytestream )
41 {
42     p_bytestream->p_chain = p_bytestream->p_block = NULL;
43     p_bytestream->i_offset = 0;
44 }
45
46 static inline void block_BytestreamRelease( block_bytestream_t *p_bytestream )
47 {
48     for( block_t *block = p_bytestream->p_chain; block != NULL; )
49     {
50         block_t *p_next = block->p_next;
51
52         block_Release( block );
53         block = p_next;
54     }
55 }
56
57 /**
58  * It flush all data (read and unread) from a block_bytestream_t.
59  */
60 static inline void block_BytestreamEmpty( block_bytestream_t *p_bytestream )
61 {
62     block_BytestreamRelease( p_bytestream );
63     block_BytestreamInit( p_bytestream );
64 }
65
66 /**
67  * It flushes all already read data from a block_bytestream_t.
68  */
69 static inline void block_BytestreamFlush( block_bytestream_t *p_bytestream )
70 {
71     block_t *block = p_bytestream->p_chain;
72
73     while( block != p_bytestream->p_block )
74     {
75         block_t *p_next = block->p_next;
76
77         block_Release( block );
78         block = p_next;
79     }
80
81     while( block != NULL && block->i_buffer == p_bytestream->i_offset )
82     {
83         block_t *p_next = block->p_next;
84
85         block_Release( block );
86         block = p_next;
87         p_bytestream->i_offset = 0;
88     }
89
90     p_bytestream->p_chain = p_bytestream->p_block = block;
91 }
92
93 static inline void block_BytestreamPush( block_bytestream_t *p_bytestream,
94                                          block_t *p_block )
95 {
96     block_ChainAppend( &p_bytestream->p_chain, p_block );
97     if( !p_bytestream->p_block ) p_bytestream->p_block = p_block;
98 }
99
100 VLC_USED
101 static inline block_t *block_BytestreamPop( block_bytestream_t *p_bytestream )
102 {
103     block_t *p_block;
104
105     block_BytestreamFlush( p_bytestream );
106
107     p_block = p_bytestream->p_block;
108     if( p_block == NULL )
109     {
110         return NULL;
111     }
112     else if( !p_block->p_next )
113     {
114         p_block->p_buffer += p_bytestream->i_offset;
115         p_block->i_buffer -= p_bytestream->i_offset;
116         p_bytestream->i_offset = 0;
117         p_bytestream->p_chain = p_bytestream->p_block = NULL;
118         return p_block;
119     }
120
121     while( p_block->p_next && p_block->p_next->p_next )
122         p_block = p_block->p_next;
123
124     {
125         block_t *p_block_old = p_block;
126         p_block = p_block->p_next;
127         p_block_old->p_next = NULL;
128     }
129
130     return p_block;
131 }
132
133 static inline int block_SkipByte( block_bytestream_t *p_bytestream )
134 {
135     /* Most common case first */
136     if( p_bytestream->p_block->i_buffer - p_bytestream->i_offset )
137     {
138         p_bytestream->i_offset++;
139         return VLC_SUCCESS;
140     }
141     else
142     {
143         block_t *p_block;
144
145         /* Less common case which is also slower */
146         for( p_block = p_bytestream->p_block->p_next;
147              p_block != NULL; p_block = p_block->p_next )
148         {
149             if( p_block->i_buffer )
150             {
151                 p_bytestream->i_offset = 1;
152                 p_bytestream->p_block = p_block;
153                 return VLC_SUCCESS;
154             }
155         }
156     }
157
158     /* Not enough data, bail out */
159     return VLC_EGENERIC;
160 }
161
162 static inline int block_PeekByte( block_bytestream_t *p_bytestream,
163                                   uint8_t *p_data )
164 {
165     /* Most common case first */
166     if( p_bytestream->p_block->i_buffer - p_bytestream->i_offset )
167     {
168         *p_data = p_bytestream->p_block->p_buffer[p_bytestream->i_offset];
169         return VLC_SUCCESS;
170     }
171     else
172     {
173         block_t *p_block;
174
175         /* Less common case which is also slower */
176         for( p_block = p_bytestream->p_block->p_next;
177              p_block != NULL; p_block = p_block->p_next )
178         {
179             if( p_block->i_buffer )
180             {
181                 *p_data = p_block->p_buffer[0];
182                 return VLC_SUCCESS;
183             }
184         }
185     }
186
187     /* Not enough data, bail out */
188     return VLC_EGENERIC;
189 }
190
191 static inline int block_GetByte( block_bytestream_t *p_bytestream,
192                                  uint8_t *p_data )
193 {
194     /* Most common case first */
195     if( p_bytestream->p_block->i_buffer - p_bytestream->i_offset )
196     {
197         *p_data = p_bytestream->p_block->p_buffer[p_bytestream->i_offset];
198         p_bytestream->i_offset++;
199         return VLC_SUCCESS;
200     }
201     else
202     {
203         block_t *p_block;
204
205         /* Less common case which is also slower */
206         for( p_block = p_bytestream->p_block->p_next;
207              p_block != NULL; p_block = p_block->p_next )
208         {
209             if( p_block->i_buffer )
210             {
211                 *p_data = p_block->p_buffer[0];
212                 p_bytestream->i_offset = 1;
213                 p_bytestream->p_block = p_block;
214                 return VLC_SUCCESS;
215             }
216         }
217     }
218
219     /* Not enough data, bail out */
220     return VLC_EGENERIC;
221 }
222
223 static inline int block_WaitBytes( block_bytestream_t *p_bytestream,
224                                    size_t i_data )
225 {
226     block_t *p_block;
227     size_t i_offset, i_copy, i_size;
228
229     /* Check we have that much data */
230     i_offset = p_bytestream->i_offset;
231     i_size = i_data;
232     i_copy = 0;
233     for( p_block = p_bytestream->p_block;
234          p_block != NULL; p_block = p_block->p_next )
235     {
236         i_copy = __MIN( i_size, p_block->i_buffer - i_offset );
237         i_size -= i_copy;
238         i_offset = 0;
239
240         if( !i_size ) break;
241     }
242
243     if( i_size )
244     {
245         /* Not enough data, bail out */
246         return VLC_EGENERIC;
247     }
248     return VLC_SUCCESS;
249 }
250
251 static inline int block_SkipBytes( block_bytestream_t *p_bytestream,
252                                    size_t i_data )
253 {
254     block_t *p_block;
255     size_t i_offset, i_copy;
256
257     /* Check we have that much data */
258     i_offset = p_bytestream->i_offset;
259     i_copy = 0;
260     for( p_block = p_bytestream->p_block;
261          p_block != NULL; p_block = p_block->p_next )
262     {
263         i_copy = __MIN( i_data, p_block->i_buffer - i_offset );
264         i_data -= i_copy;
265
266         if( !i_data ) break;
267
268         i_offset = 0;
269     }
270
271     if( i_data )
272     {
273         /* Not enough data, bail out */
274         return VLC_EGENERIC;
275     }
276
277     p_bytestream->p_block = p_block;
278     p_bytestream->i_offset = i_offset + i_copy;
279     return VLC_SUCCESS;
280 }
281
282 static inline int block_PeekBytes( block_bytestream_t *p_bytestream,
283                                    uint8_t *p_data, size_t i_data )
284 {
285     block_t *p_block;
286     size_t i_offset, i_copy, i_size;
287
288     /* Check we have that much data */
289     i_offset = p_bytestream->i_offset;
290     i_size = i_data;
291     i_copy = 0;
292     for( p_block = p_bytestream->p_block;
293          p_block != NULL; p_block = p_block->p_next )
294     {
295         i_copy = __MIN( i_size, p_block->i_buffer - i_offset );
296         i_size -= i_copy;
297         i_offset = 0;
298
299         if( !i_size ) break;
300     }
301
302     if( i_size )
303     {
304         /* Not enough data, bail out */
305         return VLC_EGENERIC;
306     }
307
308     /* Copy the data */
309     i_offset = p_bytestream->i_offset;
310     i_size = i_data;
311     i_copy = 0;
312     for( p_block = p_bytestream->p_block;
313          p_block != NULL; p_block = p_block->p_next )
314     {
315         i_copy = __MIN( i_size, p_block->i_buffer - i_offset );
316         i_size -= i_copy;
317
318         if( i_copy )
319         {
320             memcpy( p_data, p_block->p_buffer + i_offset, i_copy );
321             p_data += i_copy;
322         }
323
324         i_offset = 0;
325
326         if( !i_size ) break;
327     }
328
329     return VLC_SUCCESS;
330 }
331
332 static inline int block_GetBytes( block_bytestream_t *p_bytestream,
333                                   uint8_t *p_data, size_t i_data )
334 {
335     block_t *p_block;
336     size_t i_offset, i_copy, i_size;
337
338     /* Check we have that much data */
339     i_offset = p_bytestream->i_offset;
340     i_size = i_data;
341     i_copy = 0;
342     for( p_block = p_bytestream->p_block;
343          p_block != NULL; p_block = p_block->p_next )
344     {
345         i_copy = __MIN( i_size, p_block->i_buffer - i_offset );
346         i_size -= i_copy;
347         i_offset = 0;
348
349         if( !i_size ) break;
350     }
351
352     if( i_size )
353     {
354         /* Not enough data, bail out */
355         return VLC_EGENERIC;
356     }
357
358     /* Copy the data */
359     i_offset = p_bytestream->i_offset;
360     i_size = i_data;
361     i_copy = 0;
362     for( p_block = p_bytestream->p_block;
363          p_block != NULL; p_block = p_block->p_next )
364     {
365         i_copy = __MIN( i_size, p_block->i_buffer - i_offset );
366         i_size -= i_copy;
367
368         if( i_copy )
369         {
370             memcpy( p_data, p_block->p_buffer + i_offset, i_copy );
371             p_data += i_copy;
372         }
373
374         if( !i_size ) break;
375
376         i_offset = 0;
377     }
378
379     /* No buffer given, just skip the data */
380     p_bytestream->p_block = p_block;
381     p_bytestream->i_offset = i_offset + i_copy;
382
383     return VLC_SUCCESS;
384 }
385
386 static inline int block_PeekOffsetBytes( block_bytestream_t *p_bytestream,
387     size_t i_peek_offset, uint8_t *p_data, size_t i_data )
388 {
389     block_t *p_block;
390     size_t i_offset, i_copy, i_size;
391
392     /* Check we have that much data */
393     i_offset = p_bytestream->i_offset;
394     i_size = i_data + i_peek_offset;
395     i_copy = 0;
396     for( p_block = p_bytestream->p_block;
397          p_block != NULL; p_block = p_block->p_next )
398     {
399         i_copy = __MIN( i_size, p_block->i_buffer - i_offset );
400         i_size -= i_copy;
401         i_offset = 0;
402
403         if( !i_size ) break;
404     }
405
406     if( i_size )
407     {
408         /* Not enough data, bail out */
409         return VLC_EGENERIC;
410     }
411
412     /* Find the right place */
413     i_offset = p_bytestream->i_offset;
414     i_size = i_peek_offset;
415     i_copy = 0;
416     for( p_block = p_bytestream->p_block;
417          p_block != NULL; p_block = p_block->p_next )
418     {
419         i_copy = __MIN( i_size, p_block->i_buffer - i_offset );
420         i_size -= i_copy;
421
422         if( !i_size ) break;
423
424         i_offset = 0;
425     }
426
427     /* Copy the data */
428     i_offset += i_copy;
429     i_size = i_data;
430     i_copy = 0;
431     for( ; p_block != NULL; p_block = p_block->p_next )
432     {
433         i_copy = __MIN( i_size, p_block->i_buffer - i_offset );
434         i_size -= i_copy;
435
436         if( i_copy )
437         {
438             memcpy( p_data, p_block->p_buffer + i_offset, i_copy );
439             p_data += i_copy;
440         }
441
442         i_offset = 0;
443
444         if( !i_size ) break;
445     }
446
447     return VLC_SUCCESS;
448 }
449
450 static inline int block_FindStartcodeFromOffset(
451     block_bytestream_t *p_bytestream, size_t *pi_offset,
452     const uint8_t *p_startcode, int i_startcode_length )
453 {
454     block_t *p_block, *p_block_backup = 0;
455     int i_size = 0;
456     size_t i_offset, i_offset_backup = 0;
457     int i_caller_offset_backup = 0, i_match;
458
459     /* Find the right place */
460     i_size = *pi_offset + p_bytestream->i_offset;
461     for( p_block = p_bytestream->p_block;
462          p_block != NULL; p_block = p_block->p_next )
463     {
464         i_size -= p_block->i_buffer;
465         if( i_size < 0 ) break;
466     }
467
468     if( i_size >= 0 )
469     {
470         /* Not enough data, bail out */
471         return VLC_EGENERIC;
472     }
473
474     /* Begin the search.
475      * We first look for an occurrence of the 1st startcode byte and
476      * if found, we do a more thorough check. */
477     i_size += p_block->i_buffer;
478     *pi_offset -= i_size;
479     i_match = 0;
480     for( ; p_block != NULL; p_block = p_block->p_next )
481     {
482         for( i_offset = i_size; i_offset < p_block->i_buffer; i_offset++ )
483         {
484             if( p_block->p_buffer[i_offset] == p_startcode[i_match] )
485             {
486                 if( !i_match )
487                 {
488                     p_block_backup = p_block;
489                     i_offset_backup = i_offset;
490                     i_caller_offset_backup = *pi_offset;
491                 }
492
493                 if( i_match + 1 == i_startcode_length )
494                 {
495                     /* We have it */
496                     *pi_offset += i_offset - i_match;
497                     return VLC_SUCCESS;
498                 }
499
500                 i_match++;
501             }
502             else if ( i_match )
503             {
504                 /* False positive */
505                 p_block = p_block_backup;
506                 i_offset = i_offset_backup;
507                 *pi_offset = i_caller_offset_backup;
508                 i_match = 0;
509             }
510
511         }
512         i_size = 0;
513         *pi_offset += i_offset;
514     }
515
516     *pi_offset -= i_match;
517     return VLC_EGENERIC;
518 }
519
520 #endif /* VLC_BLOCK_HELPER_H */