macosx/library: add basic information panel
[vlc.git] / include / vlc_block_helper.h
1 /*****************************************************************************
2  * vlc_block_helper.h: Helper functions for data blocks management.
3  *****************************************************************************
4  * Copyright (C) 2003-2017 VLC authors and VideoLAN
5  *
6  * Authors: Gildas Bazin <gbazin@netcourrier.com>
7  *
8  * This program is free software; you can redistribute it and/or modify it
9  * under the terms of the GNU Lesser General Public License as published by
10  * the Free Software Foundation; either version 2.1 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License
19  * along with this program; if not, write to the Free Software Foundation,
20  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
21  *****************************************************************************/
22
23 #ifndef VLC_BLOCK_HELPER_H
24 #define VLC_BLOCK_HELPER_H 1
25
26 #include <vlc_block.h>
27
28 typedef struct block_bytestream_t
29 {
30     block_t *p_chain;  /**< byte stream head block */
31     block_t **pp_last; /**< tail ppointer for appends */
32     block_t *p_block;  /**< byte stream read pointer block */
33     size_t   i_block_offset; /**< byte stream read pointer offset within block */
34     size_t   i_base_offset; /**< block base offset (previous blocks total size) */
35     size_t   i_total;  /**< total bytes over all linked blocks */
36 } block_bytestream_t;
37
38 /*****************************************************************************
39  * block_bytestream_t management
40  *****************************************************************************/
41 static inline void block_BytestreamInit( block_bytestream_t *p_bytestream )
42 {
43     p_bytestream->p_chain = p_bytestream->p_block = NULL;
44     p_bytestream->pp_last = &p_bytestream->p_chain;
45     p_bytestream->i_block_offset = 0;
46     p_bytestream->i_base_offset = 0;
47     p_bytestream->i_total = 0;
48 }
49
50 static inline void block_BytestreamRelease( block_bytestream_t *p_bytestream )
51 {
52     block_ChainRelease( p_bytestream->p_chain );
53 }
54
55 /**
56  * It flush all data (read and unread) from a block_bytestream_t.
57  */
58 static inline void block_BytestreamEmpty( block_bytestream_t *p_bytestream )
59 {
60     block_BytestreamRelease( p_bytestream );
61     block_BytestreamInit( p_bytestream );
62 }
63
64 /**
65  * It flushes all already read data from a block_bytestream_t.
66  */
67 static inline void block_BytestreamFlush( block_bytestream_t *p_bytestream )
68 {
69     block_t *block = p_bytestream->p_chain;
70
71     while( block != p_bytestream->p_block )
72     {
73         block_t *p_next = block->p_next;
74
75         p_bytestream->i_total -= block->i_buffer;
76         p_bytestream->i_base_offset -= block->i_buffer;
77         block_Release( block );
78         block = p_next;
79     }
80
81     while( block != NULL && block->i_buffer == p_bytestream->i_block_offset )
82     {
83         block_t *p_next = block->p_next;
84
85         p_bytestream->i_total -= block->i_buffer;
86         block_Release( block );
87         block = p_next;
88         p_bytestream->i_block_offset = 0;
89     }
90
91     p_bytestream->p_chain = p_bytestream->p_block = block;
92     if( p_bytestream->p_chain == NULL )
93         p_bytestream->pp_last = &p_bytestream->p_chain;
94 }
95
96 static inline void block_BytestreamPush( block_bytestream_t *p_bytestream,
97                                          block_t *p_block )
98 {
99     block_ChainLastAppend( &p_bytestream->pp_last, p_block );
100     if( !p_bytestream->p_block ) p_bytestream->p_block = p_block;
101     for( ; p_block; p_block = p_block->p_next )
102         p_bytestream->i_total += p_block->i_buffer;
103 }
104
105 static inline size_t block_BytestreamRemaining( const block_bytestream_t *p_bytestream )
106 {
107     return ( p_bytestream->i_total > p_bytestream->i_base_offset + p_bytestream->i_block_offset ) ?
108              p_bytestream->i_total - p_bytestream->i_base_offset - p_bytestream->i_block_offset : 0;
109 }
110
111 VLC_USED
112 static inline block_t *block_BytestreamPop( block_bytestream_t *p_bytestream )
113 {
114     block_t *p_block;
115
116     block_BytestreamFlush( p_bytestream );
117
118     p_block = p_bytestream->p_block;
119     if( unlikely( p_block == NULL ) )
120     {
121         return NULL;
122     }
123     else if( !p_block->p_next )
124     {
125         p_block->p_buffer += p_bytestream->i_block_offset;
126         p_block->i_buffer -= p_bytestream->i_block_offset;
127         p_bytestream->i_block_offset = 0;
128         p_bytestream->i_total = 0;
129         p_bytestream->p_chain = p_bytestream->p_block = NULL;
130         p_bytestream->pp_last = &p_bytestream->p_chain;
131         return p_block;
132     }
133
134     while( p_block->p_next && p_block->p_next->p_next )
135         p_block = p_block->p_next;
136
137     block_t *p_block_old = p_block;
138     p_block = p_block->p_next;
139     p_block_old->p_next = NULL;
140     p_bytestream->pp_last = &p_block_old->p_next;
141     if( p_block )
142         p_bytestream->i_total -= p_block->i_buffer;
143
144     return p_block;
145 }
146
147 static inline int block_WaitBytes( block_bytestream_t *p_bytestream,
148                                    size_t i_data )
149 {
150     if( block_BytestreamRemaining( p_bytestream ) >= i_data )
151         return VLC_SUCCESS;
152     return VLC_EGENERIC;
153 }
154
155 static inline int block_PeekBytes( block_bytestream_t *p_bytestream,
156                                    uint8_t *p_data, size_t i_data )
157 {
158     if( block_BytestreamRemaining( p_bytestream ) < i_data )
159         return VLC_EGENERIC;
160
161     /* Copy the data */
162     size_t i_offset = p_bytestream->i_block_offset;
163     size_t i_size = i_data;
164     for( block_t *p_block = p_bytestream->p_block;
165          p_block != NULL; p_block = p_block->p_next )
166     {
167         size_t i_copy = __MIN( i_size, p_block->i_buffer - i_offset );
168         i_size -= i_copy;
169
170         if( i_copy )
171         {
172             memcpy( p_data, p_block->p_buffer + i_offset, i_copy );
173             p_data += i_copy;
174         }
175
176         i_offset = 0;
177
178         if( !i_size ) break;
179     }
180
181     return VLC_SUCCESS;
182 }
183
184 static inline int block_GetBytes( block_bytestream_t *p_bytestream,
185                                   uint8_t *p_data, size_t i_data )
186 {
187     if( block_BytestreamRemaining( p_bytestream ) < i_data )
188         return VLC_EGENERIC;
189
190     /* Copy the data */
191     size_t i_offset = p_bytestream->i_block_offset;
192     size_t i_size = i_data;
193     size_t i_copy = 0;
194     block_t *p_block;
195     for( p_block = p_bytestream->p_block;
196          p_block != NULL; p_block = p_block->p_next )
197     {
198         i_copy = __MIN( i_size, p_block->i_buffer - i_offset );
199         i_size -= i_copy;
200
201         if( i_copy && p_data != NULL )
202         {
203             memcpy( p_data, p_block->p_buffer + i_offset, i_copy );
204             p_data += i_copy;
205         }
206
207         if( i_size == 0 )
208             break;
209
210         p_bytestream->i_base_offset += p_block->i_buffer;
211         i_offset = 0;
212     }
213
214     p_bytestream->p_block = p_block;
215     p_bytestream->i_block_offset = i_offset + i_copy;
216
217     return VLC_SUCCESS;
218 }
219
220 static inline int block_SkipBytes( block_bytestream_t *p_bytestream,
221                                    size_t i_data )
222 {
223     return block_GetBytes( p_bytestream, NULL, i_data );
224 }
225
226 static inline int block_SkipByte( block_bytestream_t *p_bytestream )
227 {
228     return block_GetBytes( p_bytestream, NULL, 1 );
229 }
230
231 static inline int block_PeekOffsetBytes( block_bytestream_t *p_bytestream,
232     size_t i_peek_offset, uint8_t *p_data, size_t i_data )
233 {
234     const size_t i_remain = block_BytestreamRemaining( p_bytestream );
235     if( i_remain < i_data + i_peek_offset )
236         return VLC_EGENERIC;
237
238     /* Find the right place */
239     size_t i_offset = p_bytestream->i_block_offset;
240     size_t i_size = i_peek_offset;
241     size_t i_copy = 0;
242     block_t *p_block;
243     for( p_block = p_bytestream->p_block;
244          p_block != NULL; p_block = p_block->p_next )
245     {
246         i_copy = __MIN( i_size, p_block->i_buffer - i_offset );
247         i_size -= i_copy;
248
249         if( !i_size ) break;
250
251         i_offset = 0;
252     }
253
254     /* Copy the data */
255     i_offset += i_copy;
256     i_size = i_data;
257     for( ; p_block != NULL; p_block = p_block->p_next )
258     {
259         i_copy = __MIN( i_size, p_block->i_buffer - i_offset );
260         i_size -= i_copy;
261
262         if( i_copy )
263         {
264             memcpy( p_data, p_block->p_buffer + i_offset, i_copy );
265             p_data += i_copy;
266         }
267
268         i_offset = 0;
269
270         if( !i_size ) break;
271     }
272
273     return VLC_SUCCESS;
274 }
275
276 typedef const uint8_t * (*block_startcode_helper_t)( const uint8_t *, const uint8_t * );
277 typedef bool (*block_startcode_matcher_t)( uint8_t, size_t, const uint8_t * );
278
279 static inline int block_FindStartcodeFromOffset(
280     block_bytestream_t *p_bytestream, size_t *pi_offset,
281     const uint8_t *p_startcode, int i_startcode_length,
282     block_startcode_helper_t p_startcode_helper,
283     block_startcode_matcher_t p_startcode_matcher )
284 {
285     block_t *p_block, *p_block_backup = 0;
286     ssize_t i_size = 0;
287     size_t i_offset, i_offset_backup = 0;
288     int i_caller_offset_backup = 0, i_match;
289
290     /* Find the right place */
291     i_size = *pi_offset + p_bytestream->i_block_offset;
292     for( p_block = p_bytestream->p_block;
293          p_block != NULL; p_block = p_block->p_next )
294     {
295         i_size -= p_block->i_buffer;
296         if( i_size < 0 ) break;
297     }
298
299     if( unlikely( i_size >= 0 ) )
300     {
301         /* Not enough data, bail out */
302         return VLC_EGENERIC;
303     }
304
305     /* Begin the search.
306      * We first look for an occurrence of the 1st startcode byte and
307      * if found, we do a more thorough check. */
308     i_size += p_block->i_buffer;
309     *pi_offset -= i_size;
310     i_match = 0;
311     for( ; p_block != NULL; p_block = p_block->p_next )
312     {
313         for( i_offset = i_size; i_offset < p_block->i_buffer; i_offset++ )
314         {
315             /* Use optimized helper when possible */
316             if( p_startcode_helper && !i_match &&
317                (p_block->i_buffer - i_offset) > ((size_t)i_startcode_length - 1) )
318             {
319                 const uint8_t *p_res = p_startcode_helper( &p_block->p_buffer[i_offset],
320                                                            &p_block->p_buffer[p_block->i_buffer] );
321                 if( p_res )
322                 {
323                     *pi_offset += i_offset + (p_res - &p_block->p_buffer[i_offset]);
324                     return VLC_SUCCESS;
325                 }
326                 /* Then parsing boundary with legacy code */
327                 i_offset = p_block->i_buffer - (i_startcode_length - 1);
328             }
329
330             bool b_matched = ( p_startcode_matcher )
331                            ? p_startcode_matcher( p_block->p_buffer[i_offset], i_match, p_startcode )
332                            : p_block->p_buffer[i_offset] == p_startcode[i_match];
333             if( b_matched )
334             {
335                 if( i_match == 0 )
336                 {
337                     p_block_backup = p_block;
338                     i_offset_backup = i_offset;
339                     i_caller_offset_backup = *pi_offset;
340                 }
341
342                 if( i_match + 1 == i_startcode_length )
343                 {
344                     /* We have it */
345                     *pi_offset += i_offset - i_match;
346                     return VLC_SUCCESS;
347                 }
348
349                 i_match++;
350             }
351             else if ( i_match > 0 )
352             {
353                 /* False positive */
354                 p_block = p_block_backup;
355                 i_offset = i_offset_backup;
356                 *pi_offset = i_caller_offset_backup;
357                 i_match = 0;
358             }
359
360         }
361         i_size = 0;
362         *pi_offset += i_offset;
363     }
364
365     *pi_offset -= i_match;
366     return VLC_EGENERIC;
367 }
368
369 #endif /* VLC_BLOCK_HELPER_H */