summaryrefslogtreecommitdiff
path: root/gpr/source/lib/vc5_common/bitstream.c
blob: cef4312132e71b28c7fba40c9a2ce96aaa7f5cc5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
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
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
/*! @file bitstream.c
 *
 *  @brief Implementation of a bitstream for reading bits from a byte stream.
 *
 *  @version 1.0.0
 *
 *  (C) Copyright 2018 GoPro Inc (http://gopro.com/).
 *
 *  Licensed under either:
 *  - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0
 *  - MIT license, http://opensource.org/licenses/MIT
 *  at your option.
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

#include "common.h"

/*!
	@brief Return a mask with the specified number of bits set to one
 */
BITWORD BitMask(int n)
{
    if (n < bit_word_count)
    {
        BITWORD mask = 0;
        if (n > 0) {
            mask = ((1 << n) - 1);
        }
        return mask;
    }
    return BIT_WORD_MAX;
}

/*!
	@brief Initialize a bitstream data structure
 
	This routine is the constructor for the bitstream data type.
 
	The sample offset stack is used to mark the offset to a position
	in the bitstream for computing the size field of sample chunks.
 */
CODEC_ERROR InitBitstream(BITSTREAM *bitstream)
{
    if (bitstream != NULL)
    {
        bitstream->error = BITSTREAM_ERROR_OKAY;
        bitstream->stream = NULL;
        bitstream->buffer = 0;
        bitstream->count = 0;
        
#if VC5_ENABLED_PART(VC5_PART_SECTIONS)
        // Initialize the stack of sample offsets
        memset(bitstream->sample_offset_stack, 0, sizeof(bitstream->sample_offset_stack));
        bitstream->sample_offset_count = 0;
#endif

        return CODEC_ERROR_OKAY;
    }
    
    return CODEC_ERROR_NULLPTR;
}

/*!
	@brief Attach a bitstream to a byte stream.
 
	It is permitted for the byte stream to be NULL, in which case the
	bitstream will not be able to replenish its internal buffer, but
	the consequences are undefined.
 */
CODEC_ERROR AttachBitstream(struct _bitstream *bitstream, struct _stream *stream)
{
    assert(bitstream != NULL);
    bitstream->stream = stream;
    return CODEC_ERROR_OKAY;
}

/*!
	@brief Detach a bitstream from a byte stream.
 
	Any resources allocated by the bitstream are released without deallocating
	the bitstream data structure itself.  The byte stream associated with the
	bitstream is not closed by this routine.  The byte stream must be closed,
	if and when appropriate, by the caller.
 */
CODEC_ERROR ReleaseBitstream(BITSTREAM *bitstream)
{
    //TODO: What cleanup needs to be performed?
    (void) bitstream;
    return CODEC_ERROR_OKAY;
}

/*!
	@brief Return the specified number of bits from the bitstream
 */
BITWORD GetBits(BITSTREAM *stream, BITCOUNT count)
{
    // Return zero if the request cannot be satisfied
    BITWORD bits = 0;
    
    // Check that the number of requested bits is valid
    //assert(0 <= count && count <= bit_word_count);
    assert(count <= bit_word_count);
    
    // Check that the unused portion of the bit buffer is empty
    assert((stream->buffer & BitMask(bit_word_count - stream->count)) == 0);
    
    if (count == 0) goto finish;
    
    // Are there enough bits in the buffer to satisfy the request?
    if (count <= stream->count)
    {
        // Right align the requested number of bits in the bit buffer
        bits = (stream->buffer >> (bit_word_count - count));
        
        // Reduce the number of bits in the bit buffer
        stream->buffer <<= count;
        stream->count = (stream->count - count);
    }
    else
    {
        BITCOUNT low_bit_count;
        
        // Use the remaining bits in the bit buffer
        assert(stream->count > 0 || stream->buffer == 0);
        bits = (stream->buffer >> (bit_word_count - count));
        
        // Compute the number of bits to be used from the next word
        low_bit_count = count - stream->count;
        stream->count = 0;
        assert(low_bit_count > 0);
        
        // Fill the bit buffer
        assert(stream->count == 0);
        GetBuffer(stream);
        assert(stream->count >= low_bit_count);
        
        // Use the new bits in the bit buffer
        bits |= (stream->buffer >> (bit_word_count - low_bit_count));
        
        // Reduce the number of bits in the bit buffer
        if (low_bit_count < bit_word_count) {
            stream->buffer <<= low_bit_count;
        }
        else {
            stream->buffer = 0;
        }
        assert(low_bit_count <= stream->count);
        stream->count = (stream->count - low_bit_count);
    }
    
finish:
    // The bit count should never be negative or larger than the size of the bit buffer
    //assert(0 <= stream->count && stream->count <= bit_word_count);
    assert(stream->count <= bit_word_count);
    
    // The unused bits in the bit buffer should all be zero
    assert((stream->buffer & BitMask(bit_word_count - stream->count)) == 0);
    
    // The unused bits in the result should all be zero
    assert((bits & ~BitMask(count)) == 0);
    
    return bits;
}

/*!
	@brief Read more bits and append to an existing word of bits
 
	Read the specified number of bits from the bitstream and append
	the new bits to the right end of the word supplied as an argument.
	This is a convenience routine that is used to accumulate bits that
	may match a codeword.
 */
BITWORD AddBits(BITSTREAM *bitstream, BITWORD bits, BITCOUNT count)
{
    BITWORD new_bits = GetBits(bitstream, count);
    assert((new_bits & ~BitMask(count)) == 0);
    
    bits = (bits << count) | new_bits;
    
    return bits;
}

/*!
	@brief Write a longword (32 bits) to the stream
 */
CODEC_ERROR PutLong(BITSTREAM *stream, BITWORD longword)
{
    return PutBits(stream, longword, bit_word_count);
}

/*!
	@brief Write the specified number of bits to the bitstream
 */
CODEC_ERROR PutBits(BITSTREAM *stream, BITWORD bits, BITCOUNT count)
{
    BITCOUNT unused_bit_count;
    
    if (count == 0) {
        return CODEC_ERROR_OKAY;
    }
    
    // Check that the unused portion of the input bits is empty
    assert((bits & (BitMask(bit_word_count - count) << count)) == 0);
    
    // Check that the number of input bits is valid
    //assert(0 <= count && count <= bit_word_count);
    assert(count <= bit_word_count);
    
    // Check that the unused portion of the bit buffer is empty
    unused_bit_count = bit_word_count - stream->count;
    assert((stream->buffer & BitMask(unused_bit_count)) == 0);
    
    // Is there room in the bit buffer for the new bits?
    if (count <= unused_bit_count)
    {
        // Fill the remaining space in the bit buffer
        stream->buffer |= (bits << (unused_bit_count - count));
        
        // Reduce the number of unused bits in the bit buffer
        stream->count += count;
    }
    else
    {
        // Any room in the bit buffer?
        if (unused_bit_count > 0)
        {
            // Use the number of input bits that will fit in the bit buffer
            stream->buffer |= (bits >> (count - unused_bit_count));
            
            // Reduce the number of input bits by the amount used
            count -= unused_bit_count;
            //assert(count >= 0);
        }
        
        // Write the bit buffer to the byte stream
        PutWord(stream->stream, stream->buffer );
        
        // Insert the remaining input bits into the bit buffer
        stream->buffer = (bits << (bit_word_count - count));
        
        // Increment the number of bits in the bit buffer
        stream->count = count;
    }
    
    return CODEC_ERROR_OKAY;
}

/*!
	@brief Fill the internal bitstream buffer by reading a byte stream
 
	@todo Need to modify this routine to return an error code if it cannot
	read from the byte stream associated with the bitstream.
 */
CODEC_ERROR GetBuffer(BITSTREAM *bitstream)
{
    // Need to signal an underflow error?
    if (! (bitstream != NULL && bitstream->stream != NULL)) {
        assert(0);
        
        if( bitstream->error == BITSTREAM_ERROR_UNDERFLOW )
            return CODEC_ERROR_OUTOFMEMORY;
    }
    
    // The bit buffer should be empty
    assert(bitstream->count == 0);
    
    // Fill the bit buffer with a word from the byte stream
    bitstream->buffer = Swap32(GetWord(bitstream->stream));
    bitstream->count = bit_word_count;
    
    return CODEC_ERROR_OKAY;
}

/*!
	@brief Write the internal bitstream buffer to a byte stream
 
	@todo Need to modify this routine to return an error code if it cannot
	write to the byte stream associated with the bitstream.
 */
CODEC_ERROR PutBuffer(BITSTREAM *bitstream)
{
    //TODO: Need to signal an overflow error
    assert(bitstream != NULL && bitstream->stream != NULL);
    
    // The bit buffer should be full
    assert(bitstream->count == bit_word_count);
    
    // Write the bit buffer to the byte stream
    PutWord(bitstream->stream, bitstream->buffer );
    
    // Empty the bit buffer
    bitstream->buffer = 0;
    bitstream->count = 0;
    
    return CODEC_ERROR_OKAY;
}

/*!
	@brief Convert a bitstream error code to a codec error codec
 
	The bitstream and byte stream modules use a separate enumeration
	for error codes since these modules are used in other applications.
	The bistream error code is embedded in a range of codec error codes
	that are reserved for bitstream errors.
 */
CODEC_ERROR CodecErrorBitstream(BITSTREAM_ERROR error)
{
    uint32_t codec_error = CODEC_ERROR_BITSTREAM;
    codec_error |= (uint32_t)error;
    return (CODEC_ERROR)codec_error;
}

/*!
	@brief Convert a bitstream error code into a codec error code
 
	The bitstream and byte stream modules might be used in other
	applications and have their own errors codes.  This routine
	embeds a bitstream error code into a codec error code.  The
	bitstream error code is carried in the low bits of the codec
	error code.
 
	@todo Eliminate separate error codes for bitstreams?
 */
CODEC_ERROR BitstreamCodecError(BITSTREAM *bitstream)
{
    assert(bitstream != NULL);
    if (! (bitstream != NULL)) {
        return CODEC_ERROR_NULLPTR;
    }
    
    return CodecErrorBitstream(bitstream->error);
}

/*!
 @brief Read a block of bytes from the bitstream
 */
CODEC_ERROR GetByteArray(BITSTREAM *bitstream, uint8_t *array, size_t size)
{
    size_t i;
    
    for (i = 0; i < size; i++)
    {
        array[i] = GetBits(bitstream, 8);
    }
    
    return CODEC_ERROR_OKAY;
}

/*!
 @brief Write a block of bytes into the bitstream
 */
CODEC_ERROR PutByteArray(BITSTREAM *bitstream, const uint8_t *array, size_t size)
{
    size_t i;
    
    for (i = 0; i < size; i++)
    {
        PutBits(bitstream, array[i], 8);
    }
    
    return CODEC_ERROR_OKAY;
}

/*!
	@brief Write any bits in the buffer to the byte stream
 */
CODEC_ERROR FlushBitstream(BITSTREAM *bitstream)
{
    // Any bits remaining in the bit buffer?
    if (bitstream->count > 0)
    {
        // Write the bit buffer to the output stream
        PutBuffer(bitstream);
    }
    
    // Indicate that the bitstream buffer is empty
    bitstream->count = 0;
    bitstream->buffer = 0;
    
    // Flush the output stream
    FlushStream(bitstream->stream);
    
    return CODEC_ERROR_OKAY;
}

/*!
	@brief Get the current position in the byte stream
 
	The current position in the byte stream associated with the
	bitstream is returned.  The intent is to allow the bitstream
	(and the associated byte stream) to be restored to the saved
	position.
 
	@todo Need to record the state of the bitstream buffer and
	bit count so that the entire bitstream state can be restored.
 */
size_t GetBitstreamPosition(BITSTREAM *bitstream)
{
    if (bitstream->count == bit_word_count) {
        PutBuffer(bitstream);
    }
    
    // The bit buffer must be empty
    assert(bitstream->count == 0);
    
    // The bit buffer must be empty
    return (bitstream->stream->byte_count);
}

/*!
	@brief Rewind the bitstream
 
	This routine rewinds the bitstream to the beginning of the byte stream
	that has been attached to the bitstream.  The byte stream is also reset.
 
	If the byte stream could not be reset, then the internal bitstream state
	is not reset.
 */
CODEC_ERROR RewindBitstream(BITSTREAM *bitstream)
{
    assert(bitstream != NULL);
    if (! (bitstream != NULL)) {
        return CODEC_ERROR_NULLPTR;
    }
    
    if (bitstream->stream != NULL) {
        CODEC_ERROR error = RewindStream(bitstream->stream);
        if (error != CODEC_ERROR_OKAY) {
            return error;
        }
    }
    
    // Reset the bitstream internal state
    bitstream->buffer = 0;
    bitstream->count = 0;
    bitstream->error = BITSTREAM_ERROR_OKAY;
    
    return CODEC_ERROR_OKAY;
}