vlc_bits.h 7.12 KB
Newer Older
1
/*****************************************************************************
Pere Orga's avatar
Pere Orga committed
2
 * vlc_bits.h : Bit handling helpers
3
 *****************************************************************************
4
 * Copyright (C) 2001, 2002, 2003, 2006, 2015 VLC authors and VideoLAN
5
 * $Id$
6 7
 *
 * Authors: Laurent Aimar <fenrir@via.ecp.fr>
8 9
 *          Gildas Bazin <gbazin at videolan dot org>
 *          Rafaël Carré <funman at videolan dot org>
10
 *
Jean-Baptiste Kempf's avatar
LGPL  
Jean-Baptiste Kempf committed
11 12 13
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
14 15 16 17
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Jean-Baptiste Kempf's avatar
LGPL  
Jean-Baptiste Kempf committed
18 19
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
20
 *
Jean-Baptiste Kempf's avatar
LGPL  
Jean-Baptiste Kempf committed
21 22 23
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24
 *****************************************************************************/
25 26
#ifndef VLC_BITS_H
#define VLC_BITS_H 1
27

28
#include <vlc_common.h>
29

30 31 32 33 34
/**
 * \file
 * This file defines functions, structures for handling streams of bits in vlc
 */

35 36 37 38 39 40 41 42 43 44
typedef struct bs_s bs_t;

typedef struct
{
    /* forward read modifier (p_start, p_end, p_fwpriv, count, pos, remain) */
    size_t (*pf_byte_forward)(bs_t *, size_t);
    size_t (*pf_byte_pos)(const bs_t *);
    size_t (*pf_byte_remain)(const bs_t *);
} bs_byte_callbacks_t;

45 46 47
typedef struct bs_s
{
    uint8_t *p_start;
48
    uint8_t *p;         /* points to currently read/written byte */
49 50
    uint8_t *p_end;

51
    uint8_t  i_left;    /* i_count number of available bits */
52
    bool     b_read_only;
François Cartegnie's avatar
François Cartegnie committed
53

54 55
    bs_byte_callbacks_t cb;
    void    *p_priv;
56
} bs_t;
57

58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92
static size_t bs_impl_bytes_forward( bs_t *s, size_t i_count )
{
    if( s->p == NULL )
    {
        s->p = s->p_start;
        return 1;
    }

    if( s->p >= s->p_end )
        return 0;

    if( (size_t) (s->p_end - s->p) < i_count )
        i_count = s->p_end - s->p;
    s->p += i_count;
    return i_count;
}

static size_t bs_impl_bytes_remain( const bs_t *s )
{
    if( s->p )
        return s->p < s->p_end ? s->p_end - s->p - 1: 0;
    else
        return s->p_end - s->p_start;
}

static size_t bs_impl_bytes_pos( const bs_t *s )
{
    if( s->p )
        return s->p < s->p_end ? s->p - s->p_start + 1 : s->p - s->p_start;
    else
        return 0;
}

static inline void bs_init_custom( bs_t *s, const void *p_data, size_t i_data,
                                   const bs_byte_callbacks_t *cb, void *priv )
93
{
94
    s->p_start = (uint8_t *)p_data;
95
    s->p       = NULL;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
96
    s->p_end   = s->p_start + i_data;
97 98 99 100
    s->i_left  = 0;
    s->b_read_only = true;
    s->p_priv = priv;
    s->cb = *cb;
101 102 103 104
}

static inline void bs_init( bs_t *s, const void *p_data, size_t i_data )
{
105 106 107 108 109 110
    bs_byte_callbacks_t cb = {
        bs_impl_bytes_forward,
        bs_impl_bytes_pos,
        bs_impl_bytes_remain,
    };
    bs_init_custom( s, p_data, i_data, &cb, NULL );
111
}
112

113
static inline void bs_write_init( bs_t *s, void *p_data, size_t i_data )
114
{
115 116
    bs_init( s, (const void *) p_data, i_data );
    s->b_read_only = false;
117
}
118

119
static inline int bs_refill( bs_t *s )
120
{
121 122 123 124 125 126 127 128 129 130 131 132 133 134
    if( s->i_left == 0 )
    {
       if( s->cb.pf_byte_forward( s, 1 ) != 1 )
           return -1;

       if( s->p < s->p_end )
            s->i_left = 8;
    }
    return s->i_left > 0 ? 0 : 1;
}

static inline bool bs_eof( bs_t *s )
{
    return bs_refill( s ) != 0;
135 136
}

137
static inline size_t bs_pos( const bs_t *s )
138
{
139
    return 8 * s->cb.pf_byte_pos( s ) - s->i_left;
140
}
141

142 143 144 145
static inline size_t bs_remain( const bs_t *s )
{
    return 8 * s->cb.pf_byte_remain( s ) + s->i_left;
}
François Cartegnie's avatar
François Cartegnie committed
146

147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168
static inline void bs_skip( bs_t *s, size_t i_count )
{
    if( i_count == 0 )
        return;

    if( bs_refill( s ) )
        return;

    if( i_count > s->i_left )
    {
        i_count -= s->i_left;
        s->i_left = 0;
        if( i_count / 8 )
            s->cb.pf_byte_forward( s, i_count / 8 );
        i_count = i_count % 8;
        if( i_count > 0 && !bs_refill( s ) )
            s->i_left = 8 - i_count;
    }
    else s->i_left -= i_count;
}

static inline uint32_t bs_read( bs_t *s, uint8_t i_count )
169
{
170
     static const uint32_t i_mask[33] =
171 172 173 174 175 176 177 178 179
     {  0x00,
        0x01,      0x03,      0x07,      0x0f,
        0x1f,      0x3f,      0x7f,      0xff,
        0x1ff,     0x3ff,     0x7ff,     0xfff,
        0x1fff,    0x3fff,    0x7fff,    0xffff,
        0x1ffff,   0x3ffff,   0x7ffff,   0xfffff,
        0x1fffff,  0x3fffff,  0x7fffff,  0xffffff,
        0x1ffffff, 0x3ffffff, 0x7ffffff, 0xfffffff,
        0x1fffffff,0x3fffffff,0x7fffffff,0xffffffff};
180
    uint8_t  i_shr, i_drop = 0;
181 182
    uint32_t i_result = 0;

183 184 185 186 187 188
    if( i_count > 32 )
    {
        i_drop = i_count - 32;
        i_count = 32;
    }

189 190
    while( i_count > 0 )
    {
191
        if( bs_refill( s ) )
192 193
            break;

194
        if( s->i_left > i_count )
195
        {
196
            i_shr = s->i_left - i_count;
197 198 199
            /* more in the buffer than requested */
            i_result |= ( *s->p >> i_shr )&i_mask[i_count];
            s->i_left -= i_count;
200
            break;
201 202 203
        }
        else
        {
204
            i_shr = i_count - s->i_left;
205
            /* less in the buffer than requested */
206
           if( i_shr >= 32 )
207 208
               i_result = 0;
           else
209
               i_result |= (*s->p&i_mask[s->i_left]) << i_shr;
210
           i_count  -= s->i_left;
211
           s->i_left = 0;
212 213 214
        }
    }

215
    if( i_drop )
216
        bs_skip( s, i_drop );
217

218 219 220 221 222
    return( i_result );
}

static inline uint32_t bs_read1( bs_t *s )
{
223 224 225 226
    if( bs_refill( s ) )
        return 0;
    s->i_left--;
    return ( *s->p >> s->i_left )&0x01;
227 228
}

229
static inline void bs_write( bs_t *s, uint8_t i_count, uint32_t i_bits )
230
{
231 232 233
    if( s->b_read_only )
        return;

234 235
    while( i_count > 0 )
    {
236
        if( bs_refill( s ) )
237 238 239 240 241
            break;

        i_count--;

        if( ( i_bits >> i_count )&0x01 )
242
        {
243
            *s->p |= 1 << ( s->i_left - 1 );
244
        }
245
        else
246
        {
247
            *s->p &= ~( 1 << ( s->i_left - 1 ) );
248
        }
249 250 251 252
        s->i_left--;
    }
}

François Cartegnie's avatar
François Cartegnie committed
253 254 255 256 257
static inline bool bs_aligned( bs_t *s )
{
    return s->i_left % 8 == 0;
}

258
static inline void bs_align( bs_t *s )
259
{
260 261
    if( s->i_left % 8 )
        s->i_left = 0;
262
}
263

264
static inline void bs_write_align( bs_t *s, uint8_t v )
265
{
266 267
    if( !s->b_read_only && (s->i_left % 8) )
        bs_write( s, s->i_left, v ? 0xFF : 0 );
268
}
269

270 271
#define bs_align_0( s ) bs_write_align( s, 0 )
#define bs_align_1( s ) bs_write_align( s, 1 )
272

273
/* Read unsigned Exp-Golomb code */
274
static inline uint_fast32_t bs_read_ue( bs_t * bs )
275
{
276
    unsigned i = 0;
277

278
    while( bs_read1( bs ) == 0 && bs->p < bs->p_end && i < 31 )
279 280
        i++;

281
    return (1U << i) - 1 + bs_read( bs, i );
282 283 284
}

/* Read signed Exp-Golomb code */
285
static inline int_fast32_t bs_read_se( bs_t *s )
286
{
287
    uint_fast32_t val = bs_read_ue( s );
288

289 290
    return (val & 0x01) ? (int_fast32_t)((val + 1) / 2)
                        : -(int_fast32_t)(val / 2);
291 292
}

François Cartegnie's avatar
François Cartegnie committed
293 294
#undef bs_forward

295
#endif