1*65d1f550SNick Terrell /* SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause */
2e0c1b49fSNick Terrell /* ******************************************************************
3e0c1b49fSNick Terrell * bitstream
4e0c1b49fSNick Terrell * Part of FSE library
5*65d1f550SNick Terrell * Copyright (c) Meta Platforms, Inc. and affiliates.
6e0c1b49fSNick Terrell *
7e0c1b49fSNick Terrell * You can contact the author at :
8e0c1b49fSNick Terrell * - Source repository : https://github.com/Cyan4973/FiniteStateEntropy
9e0c1b49fSNick Terrell *
10e0c1b49fSNick Terrell * This source code is licensed under both the BSD-style license (found in the
11e0c1b49fSNick Terrell * LICENSE file in the root directory of this source tree) and the GPLv2 (found
12e0c1b49fSNick Terrell * in the COPYING file in the root directory of this source tree).
13e0c1b49fSNick Terrell * You may select, at your option, one of the above-listed licenses.
14e0c1b49fSNick Terrell ****************************************************************** */
15e0c1b49fSNick Terrell #ifndef BITSTREAM_H_MODULE
16e0c1b49fSNick Terrell #define BITSTREAM_H_MODULE
17e0c1b49fSNick Terrell
18e0c1b49fSNick Terrell /*
19e0c1b49fSNick Terrell * This API consists of small unitary functions, which must be inlined for best performance.
20e0c1b49fSNick Terrell * Since link-time-optimization is not available for all compilers,
21e0c1b49fSNick Terrell * these functions are defined into a .h to be included.
22e0c1b49fSNick Terrell */
23e0c1b49fSNick Terrell
24e0c1b49fSNick Terrell /*-****************************************
25e0c1b49fSNick Terrell * Dependencies
26e0c1b49fSNick Terrell ******************************************/
27e0c1b49fSNick Terrell #include "mem.h" /* unaligned access routines */
28e0c1b49fSNick Terrell #include "compiler.h" /* UNLIKELY() */
29e0c1b49fSNick Terrell #include "debug.h" /* assert(), DEBUGLOG(), RAWLOG() */
30e0c1b49fSNick Terrell #include "error_private.h" /* error codes and messages */
31*65d1f550SNick Terrell #include "bits.h" /* ZSTD_highbit32 */
32e0c1b49fSNick Terrell
33e0c1b49fSNick Terrell /*=========================================
34e0c1b49fSNick Terrell * Target specific
35e0c1b49fSNick Terrell =========================================*/
36e0c1b49fSNick Terrell
37e0c1b49fSNick Terrell #define STREAM_ACCUMULATOR_MIN_32 25
38e0c1b49fSNick Terrell #define STREAM_ACCUMULATOR_MIN_64 57
39e0c1b49fSNick Terrell #define STREAM_ACCUMULATOR_MIN ((U32)(MEM_32bits() ? STREAM_ACCUMULATOR_MIN_32 : STREAM_ACCUMULATOR_MIN_64))
40e0c1b49fSNick Terrell
41e0c1b49fSNick Terrell
42e0c1b49fSNick Terrell /*-******************************************
43e0c1b49fSNick Terrell * bitStream encoding API (write forward)
44e0c1b49fSNick Terrell ********************************************/
45*65d1f550SNick Terrell typedef size_t BitContainerType;
46e0c1b49fSNick Terrell /* bitStream can mix input from multiple sources.
47e0c1b49fSNick Terrell * A critical property of these streams is that they encode and decode in **reverse** direction.
48e0c1b49fSNick Terrell * So the first bit sequence you add will be the last to be read, like a LIFO stack.
49e0c1b49fSNick Terrell */
50e0c1b49fSNick Terrell typedef struct {
51*65d1f550SNick Terrell BitContainerType bitContainer;
52e0c1b49fSNick Terrell unsigned bitPos;
53e0c1b49fSNick Terrell char* startPtr;
54e0c1b49fSNick Terrell char* ptr;
55e0c1b49fSNick Terrell char* endPtr;
56e0c1b49fSNick Terrell } BIT_CStream_t;
57e0c1b49fSNick Terrell
58e0c1b49fSNick Terrell MEM_STATIC size_t BIT_initCStream(BIT_CStream_t* bitC, void* dstBuffer, size_t dstCapacity);
59*65d1f550SNick Terrell MEM_STATIC void BIT_addBits(BIT_CStream_t* bitC, BitContainerType value, unsigned nbBits);
60e0c1b49fSNick Terrell MEM_STATIC void BIT_flushBits(BIT_CStream_t* bitC);
61e0c1b49fSNick Terrell MEM_STATIC size_t BIT_closeCStream(BIT_CStream_t* bitC);
62e0c1b49fSNick Terrell
63e0c1b49fSNick Terrell /* Start with initCStream, providing the size of buffer to write into.
64e0c1b49fSNick Terrell * bitStream will never write outside of this buffer.
65e0c1b49fSNick Terrell * `dstCapacity` must be >= sizeof(bitD->bitContainer), otherwise @return will be an error code.
66e0c1b49fSNick Terrell *
67e0c1b49fSNick Terrell * bits are first added to a local register.
68*65d1f550SNick Terrell * Local register is BitContainerType, 64-bits on 64-bits systems, or 32-bits on 32-bits systems.
69e0c1b49fSNick Terrell * Writing data into memory is an explicit operation, performed by the flushBits function.
70e0c1b49fSNick Terrell * Hence keep track how many bits are potentially stored into local register to avoid register overflow.
71e0c1b49fSNick Terrell * After a flushBits, a maximum of 7 bits might still be stored into local register.
72e0c1b49fSNick Terrell *
73e0c1b49fSNick Terrell * Avoid storing elements of more than 24 bits if you want compatibility with 32-bits bitstream readers.
74e0c1b49fSNick Terrell *
75e0c1b49fSNick Terrell * Last operation is to close the bitStream.
76e0c1b49fSNick Terrell * The function returns the final size of CStream in bytes.
77e0c1b49fSNick Terrell * If data couldn't fit into `dstBuffer`, it will return a 0 ( == not storable)
78e0c1b49fSNick Terrell */
79e0c1b49fSNick Terrell
80e0c1b49fSNick Terrell
81e0c1b49fSNick Terrell /*-********************************************
82e0c1b49fSNick Terrell * bitStream decoding API (read backward)
83e0c1b49fSNick Terrell **********************************************/
84e0c1b49fSNick Terrell typedef struct {
85*65d1f550SNick Terrell BitContainerType bitContainer;
86e0c1b49fSNick Terrell unsigned bitsConsumed;
87e0c1b49fSNick Terrell const char* ptr;
88e0c1b49fSNick Terrell const char* start;
89e0c1b49fSNick Terrell const char* limitPtr;
90e0c1b49fSNick Terrell } BIT_DStream_t;
91e0c1b49fSNick Terrell
92*65d1f550SNick Terrell typedef enum { BIT_DStream_unfinished = 0, /* fully refilled */
93*65d1f550SNick Terrell BIT_DStream_endOfBuffer = 1, /* still some bits left in bitstream */
94*65d1f550SNick Terrell BIT_DStream_completed = 2, /* bitstream entirely consumed, bit-exact */
95*65d1f550SNick Terrell BIT_DStream_overflow = 3 /* user requested more bits than present in bitstream */
96*65d1f550SNick Terrell } BIT_DStream_status; /* result of BIT_reloadDStream() */
97e0c1b49fSNick Terrell
98e0c1b49fSNick Terrell MEM_STATIC size_t BIT_initDStream(BIT_DStream_t* bitD, const void* srcBuffer, size_t srcSize);
99*65d1f550SNick Terrell MEM_STATIC BitContainerType BIT_readBits(BIT_DStream_t* bitD, unsigned nbBits);
100e0c1b49fSNick Terrell MEM_STATIC BIT_DStream_status BIT_reloadDStream(BIT_DStream_t* bitD);
101e0c1b49fSNick Terrell MEM_STATIC unsigned BIT_endOfDStream(const BIT_DStream_t* bitD);
102e0c1b49fSNick Terrell
103e0c1b49fSNick Terrell
104e0c1b49fSNick Terrell /* Start by invoking BIT_initDStream().
105e0c1b49fSNick Terrell * A chunk of the bitStream is then stored into a local register.
106*65d1f550SNick Terrell * Local register size is 64-bits on 64-bits systems, 32-bits on 32-bits systems (BitContainerType).
107e0c1b49fSNick Terrell * You can then retrieve bitFields stored into the local register, **in reverse order**.
108e0c1b49fSNick Terrell * Local register is explicitly reloaded from memory by the BIT_reloadDStream() method.
109e0c1b49fSNick Terrell * A reload guarantee a minimum of ((8*sizeof(bitD->bitContainer))-7) bits when its result is BIT_DStream_unfinished.
110e0c1b49fSNick Terrell * Otherwise, it can be less than that, so proceed accordingly.
111e0c1b49fSNick Terrell * Checking if DStream has reached its end can be performed with BIT_endOfDStream().
112e0c1b49fSNick Terrell */
113e0c1b49fSNick Terrell
114e0c1b49fSNick Terrell
115e0c1b49fSNick Terrell /*-****************************************
116e0c1b49fSNick Terrell * unsafe API
117e0c1b49fSNick Terrell ******************************************/
118*65d1f550SNick Terrell MEM_STATIC void BIT_addBitsFast(BIT_CStream_t* bitC, BitContainerType value, unsigned nbBits);
119e0c1b49fSNick Terrell /* faster, but works only if value is "clean", meaning all high bits above nbBits are 0 */
120e0c1b49fSNick Terrell
121e0c1b49fSNick Terrell MEM_STATIC void BIT_flushBitsFast(BIT_CStream_t* bitC);
122e0c1b49fSNick Terrell /* unsafe version; does not check buffer overflow */
123e0c1b49fSNick Terrell
124e0c1b49fSNick Terrell MEM_STATIC size_t BIT_readBitsFast(BIT_DStream_t* bitD, unsigned nbBits);
125e0c1b49fSNick Terrell /* faster, but works only if nbBits >= 1 */
126e0c1b49fSNick Terrell
127e0c1b49fSNick Terrell /*===== Local Constants =====*/
128e0c1b49fSNick Terrell static const unsigned BIT_mask[] = {
129e0c1b49fSNick Terrell 0, 1, 3, 7, 0xF, 0x1F,
130e0c1b49fSNick Terrell 0x3F, 0x7F, 0xFF, 0x1FF, 0x3FF, 0x7FF,
131e0c1b49fSNick Terrell 0xFFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF, 0x1FFFF,
132e0c1b49fSNick Terrell 0x3FFFF, 0x7FFFF, 0xFFFFF, 0x1FFFFF, 0x3FFFFF, 0x7FFFFF,
133e0c1b49fSNick Terrell 0xFFFFFF, 0x1FFFFFF, 0x3FFFFFF, 0x7FFFFFF, 0xFFFFFFF, 0x1FFFFFFF,
134e0c1b49fSNick Terrell 0x3FFFFFFF, 0x7FFFFFFF}; /* up to 31 bits */
135e0c1b49fSNick Terrell #define BIT_MASK_SIZE (sizeof(BIT_mask) / sizeof(BIT_mask[0]))
136e0c1b49fSNick Terrell
137e0c1b49fSNick Terrell /*-**************************************************************
138e0c1b49fSNick Terrell * bitStream encoding
139e0c1b49fSNick Terrell ****************************************************************/
140e0c1b49fSNick Terrell /*! BIT_initCStream() :
141e0c1b49fSNick Terrell * `dstCapacity` must be > sizeof(size_t)
142e0c1b49fSNick Terrell * @return : 0 if success,
143e0c1b49fSNick Terrell * otherwise an error code (can be tested using ERR_isError()) */
BIT_initCStream(BIT_CStream_t * bitC,void * startPtr,size_t dstCapacity)144e0c1b49fSNick Terrell MEM_STATIC size_t BIT_initCStream(BIT_CStream_t* bitC,
145e0c1b49fSNick Terrell void* startPtr, size_t dstCapacity)
146e0c1b49fSNick Terrell {
147e0c1b49fSNick Terrell bitC->bitContainer = 0;
148e0c1b49fSNick Terrell bitC->bitPos = 0;
149e0c1b49fSNick Terrell bitC->startPtr = (char*)startPtr;
150e0c1b49fSNick Terrell bitC->ptr = bitC->startPtr;
151e0c1b49fSNick Terrell bitC->endPtr = bitC->startPtr + dstCapacity - sizeof(bitC->bitContainer);
152e0c1b49fSNick Terrell if (dstCapacity <= sizeof(bitC->bitContainer)) return ERROR(dstSize_tooSmall);
153e0c1b49fSNick Terrell return 0;
154e0c1b49fSNick Terrell }
155e0c1b49fSNick Terrell
BIT_getLowerBits(BitContainerType bitContainer,U32 const nbBits)156*65d1f550SNick Terrell FORCE_INLINE_TEMPLATE BitContainerType BIT_getLowerBits(BitContainerType bitContainer, U32 const nbBits)
157*65d1f550SNick Terrell {
158*65d1f550SNick Terrell assert(nbBits < BIT_MASK_SIZE);
159*65d1f550SNick Terrell return bitContainer & BIT_mask[nbBits];
160*65d1f550SNick Terrell }
161*65d1f550SNick Terrell
162e0c1b49fSNick Terrell /*! BIT_addBits() :
163e0c1b49fSNick Terrell * can add up to 31 bits into `bitC`.
164e0c1b49fSNick Terrell * Note : does not check for register overflow ! */
BIT_addBits(BIT_CStream_t * bitC,BitContainerType value,unsigned nbBits)165e0c1b49fSNick Terrell MEM_STATIC void BIT_addBits(BIT_CStream_t* bitC,
166*65d1f550SNick Terrell BitContainerType value, unsigned nbBits)
167e0c1b49fSNick Terrell {
168e0c1b49fSNick Terrell DEBUG_STATIC_ASSERT(BIT_MASK_SIZE == 32);
169e0c1b49fSNick Terrell assert(nbBits < BIT_MASK_SIZE);
170e0c1b49fSNick Terrell assert(nbBits + bitC->bitPos < sizeof(bitC->bitContainer) * 8);
171*65d1f550SNick Terrell bitC->bitContainer |= BIT_getLowerBits(value, nbBits) << bitC->bitPos;
172e0c1b49fSNick Terrell bitC->bitPos += nbBits;
173e0c1b49fSNick Terrell }
174e0c1b49fSNick Terrell
175e0c1b49fSNick Terrell /*! BIT_addBitsFast() :
176e0c1b49fSNick Terrell * works only if `value` is _clean_,
177e0c1b49fSNick Terrell * meaning all high bits above nbBits are 0 */
BIT_addBitsFast(BIT_CStream_t * bitC,BitContainerType value,unsigned nbBits)178e0c1b49fSNick Terrell MEM_STATIC void BIT_addBitsFast(BIT_CStream_t* bitC,
179*65d1f550SNick Terrell BitContainerType value, unsigned nbBits)
180e0c1b49fSNick Terrell {
181e0c1b49fSNick Terrell assert((value>>nbBits) == 0);
182e0c1b49fSNick Terrell assert(nbBits + bitC->bitPos < sizeof(bitC->bitContainer) * 8);
183e0c1b49fSNick Terrell bitC->bitContainer |= value << bitC->bitPos;
184e0c1b49fSNick Terrell bitC->bitPos += nbBits;
185e0c1b49fSNick Terrell }
186e0c1b49fSNick Terrell
187e0c1b49fSNick Terrell /*! BIT_flushBitsFast() :
188e0c1b49fSNick Terrell * assumption : bitContainer has not overflowed
189e0c1b49fSNick Terrell * unsafe version; does not check buffer overflow */
BIT_flushBitsFast(BIT_CStream_t * bitC)190e0c1b49fSNick Terrell MEM_STATIC void BIT_flushBitsFast(BIT_CStream_t* bitC)
191e0c1b49fSNick Terrell {
192e0c1b49fSNick Terrell size_t const nbBytes = bitC->bitPos >> 3;
193e0c1b49fSNick Terrell assert(bitC->bitPos < sizeof(bitC->bitContainer) * 8);
194e0c1b49fSNick Terrell assert(bitC->ptr <= bitC->endPtr);
195e0c1b49fSNick Terrell MEM_writeLEST(bitC->ptr, bitC->bitContainer);
196e0c1b49fSNick Terrell bitC->ptr += nbBytes;
197e0c1b49fSNick Terrell bitC->bitPos &= 7;
198e0c1b49fSNick Terrell bitC->bitContainer >>= nbBytes*8;
199e0c1b49fSNick Terrell }
200e0c1b49fSNick Terrell
201e0c1b49fSNick Terrell /*! BIT_flushBits() :
202e0c1b49fSNick Terrell * assumption : bitContainer has not overflowed
203e0c1b49fSNick Terrell * safe version; check for buffer overflow, and prevents it.
204e0c1b49fSNick Terrell * note : does not signal buffer overflow.
205e0c1b49fSNick Terrell * overflow will be revealed later on using BIT_closeCStream() */
BIT_flushBits(BIT_CStream_t * bitC)206e0c1b49fSNick Terrell MEM_STATIC void BIT_flushBits(BIT_CStream_t* bitC)
207e0c1b49fSNick Terrell {
208e0c1b49fSNick Terrell size_t const nbBytes = bitC->bitPos >> 3;
209e0c1b49fSNick Terrell assert(bitC->bitPos < sizeof(bitC->bitContainer) * 8);
210e0c1b49fSNick Terrell assert(bitC->ptr <= bitC->endPtr);
211e0c1b49fSNick Terrell MEM_writeLEST(bitC->ptr, bitC->bitContainer);
212e0c1b49fSNick Terrell bitC->ptr += nbBytes;
213e0c1b49fSNick Terrell if (bitC->ptr > bitC->endPtr) bitC->ptr = bitC->endPtr;
214e0c1b49fSNick Terrell bitC->bitPos &= 7;
215e0c1b49fSNick Terrell bitC->bitContainer >>= nbBytes*8;
216e0c1b49fSNick Terrell }
217e0c1b49fSNick Terrell
218e0c1b49fSNick Terrell /*! BIT_closeCStream() :
219e0c1b49fSNick Terrell * @return : size of CStream, in bytes,
220e0c1b49fSNick Terrell * or 0 if it could not fit into dstBuffer */
BIT_closeCStream(BIT_CStream_t * bitC)221e0c1b49fSNick Terrell MEM_STATIC size_t BIT_closeCStream(BIT_CStream_t* bitC)
222e0c1b49fSNick Terrell {
223e0c1b49fSNick Terrell BIT_addBitsFast(bitC, 1, 1); /* endMark */
224e0c1b49fSNick Terrell BIT_flushBits(bitC);
225e0c1b49fSNick Terrell if (bitC->ptr >= bitC->endPtr) return 0; /* overflow detected */
226*65d1f550SNick Terrell return (size_t)(bitC->ptr - bitC->startPtr) + (bitC->bitPos > 0);
227e0c1b49fSNick Terrell }
228e0c1b49fSNick Terrell
229e0c1b49fSNick Terrell
230e0c1b49fSNick Terrell /*-********************************************************
231e0c1b49fSNick Terrell * bitStream decoding
232e0c1b49fSNick Terrell **********************************************************/
233e0c1b49fSNick Terrell /*! BIT_initDStream() :
234e0c1b49fSNick Terrell * Initialize a BIT_DStream_t.
235e0c1b49fSNick Terrell * `bitD` : a pointer to an already allocated BIT_DStream_t structure.
236e0c1b49fSNick Terrell * `srcSize` must be the *exact* size of the bitStream, in bytes.
237e0c1b49fSNick Terrell * @return : size of stream (== srcSize), or an errorCode if a problem is detected
238e0c1b49fSNick Terrell */
BIT_initDStream(BIT_DStream_t * bitD,const void * srcBuffer,size_t srcSize)239e0c1b49fSNick Terrell MEM_STATIC size_t BIT_initDStream(BIT_DStream_t* bitD, const void* srcBuffer, size_t srcSize)
240e0c1b49fSNick Terrell {
241e0c1b49fSNick Terrell if (srcSize < 1) { ZSTD_memset(bitD, 0, sizeof(*bitD)); return ERROR(srcSize_wrong); }
242e0c1b49fSNick Terrell
243e0c1b49fSNick Terrell bitD->start = (const char*)srcBuffer;
244e0c1b49fSNick Terrell bitD->limitPtr = bitD->start + sizeof(bitD->bitContainer);
245e0c1b49fSNick Terrell
246e0c1b49fSNick Terrell if (srcSize >= sizeof(bitD->bitContainer)) { /* normal case */
247e0c1b49fSNick Terrell bitD->ptr = (const char*)srcBuffer + srcSize - sizeof(bitD->bitContainer);
248e0c1b49fSNick Terrell bitD->bitContainer = MEM_readLEST(bitD->ptr);
249e0c1b49fSNick Terrell { BYTE const lastByte = ((const BYTE*)srcBuffer)[srcSize-1];
250*65d1f550SNick Terrell bitD->bitsConsumed = lastByte ? 8 - ZSTD_highbit32(lastByte) : 0; /* ensures bitsConsumed is always set */
251e0c1b49fSNick Terrell if (lastByte == 0) return ERROR(GENERIC); /* endMark not present */ }
252e0c1b49fSNick Terrell } else {
253e0c1b49fSNick Terrell bitD->ptr = bitD->start;
254e0c1b49fSNick Terrell bitD->bitContainer = *(const BYTE*)(bitD->start);
255e0c1b49fSNick Terrell switch(srcSize)
256e0c1b49fSNick Terrell {
257*65d1f550SNick Terrell case 7: bitD->bitContainer += (BitContainerType)(((const BYTE*)(srcBuffer))[6]) << (sizeof(bitD->bitContainer)*8 - 16);
258e0c1b49fSNick Terrell ZSTD_FALLTHROUGH;
259e0c1b49fSNick Terrell
260*65d1f550SNick Terrell case 6: bitD->bitContainer += (BitContainerType)(((const BYTE*)(srcBuffer))[5]) << (sizeof(bitD->bitContainer)*8 - 24);
261e0c1b49fSNick Terrell ZSTD_FALLTHROUGH;
262e0c1b49fSNick Terrell
263*65d1f550SNick Terrell case 5: bitD->bitContainer += (BitContainerType)(((const BYTE*)(srcBuffer))[4]) << (sizeof(bitD->bitContainer)*8 - 32);
264e0c1b49fSNick Terrell ZSTD_FALLTHROUGH;
265e0c1b49fSNick Terrell
266*65d1f550SNick Terrell case 4: bitD->bitContainer += (BitContainerType)(((const BYTE*)(srcBuffer))[3]) << 24;
267e0c1b49fSNick Terrell ZSTD_FALLTHROUGH;
268e0c1b49fSNick Terrell
269*65d1f550SNick Terrell case 3: bitD->bitContainer += (BitContainerType)(((const BYTE*)(srcBuffer))[2]) << 16;
270e0c1b49fSNick Terrell ZSTD_FALLTHROUGH;
271e0c1b49fSNick Terrell
272*65d1f550SNick Terrell case 2: bitD->bitContainer += (BitContainerType)(((const BYTE*)(srcBuffer))[1]) << 8;
273e0c1b49fSNick Terrell ZSTD_FALLTHROUGH;
274e0c1b49fSNick Terrell
275e0c1b49fSNick Terrell default: break;
276e0c1b49fSNick Terrell }
277e0c1b49fSNick Terrell { BYTE const lastByte = ((const BYTE*)srcBuffer)[srcSize-1];
278*65d1f550SNick Terrell bitD->bitsConsumed = lastByte ? 8 - ZSTD_highbit32(lastByte) : 0;
279e0c1b49fSNick Terrell if (lastByte == 0) return ERROR(corruption_detected); /* endMark not present */
280e0c1b49fSNick Terrell }
281e0c1b49fSNick Terrell bitD->bitsConsumed += (U32)(sizeof(bitD->bitContainer) - srcSize)*8;
282e0c1b49fSNick Terrell }
283e0c1b49fSNick Terrell
284e0c1b49fSNick Terrell return srcSize;
285e0c1b49fSNick Terrell }
286e0c1b49fSNick Terrell
BIT_getUpperBits(BitContainerType bitContainer,U32 const start)287*65d1f550SNick Terrell FORCE_INLINE_TEMPLATE BitContainerType BIT_getUpperBits(BitContainerType bitContainer, U32 const start)
288e0c1b49fSNick Terrell {
289e0c1b49fSNick Terrell return bitContainer >> start;
290e0c1b49fSNick Terrell }
291e0c1b49fSNick Terrell
BIT_getMiddleBits(BitContainerType bitContainer,U32 const start,U32 const nbBits)292*65d1f550SNick Terrell FORCE_INLINE_TEMPLATE BitContainerType BIT_getMiddleBits(BitContainerType bitContainer, U32 const start, U32 const nbBits)
293e0c1b49fSNick Terrell {
294e0c1b49fSNick Terrell U32 const regMask = sizeof(bitContainer)*8 - 1;
295e0c1b49fSNick Terrell /* if start > regMask, bitstream is corrupted, and result is undefined */
296e0c1b49fSNick Terrell assert(nbBits < BIT_MASK_SIZE);
2972aa14b1aSNick Terrell /* x86 transform & ((1 << nbBits) - 1) to bzhi instruction, it is better
2982aa14b1aSNick Terrell * than accessing memory. When bmi2 instruction is not present, we consider
2992aa14b1aSNick Terrell * such cpus old (pre-Haswell, 2013) and their performance is not of that
3002aa14b1aSNick Terrell * importance.
3012aa14b1aSNick Terrell */
302*65d1f550SNick Terrell #if defined(__x86_64__) || defined(_M_X64)
3032aa14b1aSNick Terrell return (bitContainer >> (start & regMask)) & ((((U64)1) << nbBits) - 1);
3042aa14b1aSNick Terrell #else
305e0c1b49fSNick Terrell return (bitContainer >> (start & regMask)) & BIT_mask[nbBits];
3062aa14b1aSNick Terrell #endif
307e0c1b49fSNick Terrell }
308e0c1b49fSNick Terrell
309e0c1b49fSNick Terrell /*! BIT_lookBits() :
310e0c1b49fSNick Terrell * Provides next n bits from local register.
311e0c1b49fSNick Terrell * local register is not modified.
312e0c1b49fSNick Terrell * On 32-bits, maxNbBits==24.
313e0c1b49fSNick Terrell * On 64-bits, maxNbBits==56.
314e0c1b49fSNick Terrell * @return : value extracted */
BIT_lookBits(const BIT_DStream_t * bitD,U32 nbBits)315*65d1f550SNick Terrell FORCE_INLINE_TEMPLATE BitContainerType BIT_lookBits(const BIT_DStream_t* bitD, U32 nbBits)
316e0c1b49fSNick Terrell {
317e0c1b49fSNick Terrell /* arbitrate between double-shift and shift+mask */
318e0c1b49fSNick Terrell #if 1
319e0c1b49fSNick Terrell /* if bitD->bitsConsumed + nbBits > sizeof(bitD->bitContainer)*8,
320e0c1b49fSNick Terrell * bitstream is likely corrupted, and result is undefined */
321e0c1b49fSNick Terrell return BIT_getMiddleBits(bitD->bitContainer, (sizeof(bitD->bitContainer)*8) - bitD->bitsConsumed - nbBits, nbBits);
322e0c1b49fSNick Terrell #else
323e0c1b49fSNick Terrell /* this code path is slower on my os-x laptop */
324e0c1b49fSNick Terrell U32 const regMask = sizeof(bitD->bitContainer)*8 - 1;
325e0c1b49fSNick Terrell return ((bitD->bitContainer << (bitD->bitsConsumed & regMask)) >> 1) >> ((regMask-nbBits) & regMask);
326e0c1b49fSNick Terrell #endif
327e0c1b49fSNick Terrell }
328e0c1b49fSNick Terrell
329e0c1b49fSNick Terrell /*! BIT_lookBitsFast() :
330e0c1b49fSNick Terrell * unsafe version; only works if nbBits >= 1 */
BIT_lookBitsFast(const BIT_DStream_t * bitD,U32 nbBits)331*65d1f550SNick Terrell MEM_STATIC BitContainerType BIT_lookBitsFast(const BIT_DStream_t* bitD, U32 nbBits)
332e0c1b49fSNick Terrell {
333e0c1b49fSNick Terrell U32 const regMask = sizeof(bitD->bitContainer)*8 - 1;
334e0c1b49fSNick Terrell assert(nbBits >= 1);
335e0c1b49fSNick Terrell return (bitD->bitContainer << (bitD->bitsConsumed & regMask)) >> (((regMask+1)-nbBits) & regMask);
336e0c1b49fSNick Terrell }
337e0c1b49fSNick Terrell
BIT_skipBits(BIT_DStream_t * bitD,U32 nbBits)338*65d1f550SNick Terrell FORCE_INLINE_TEMPLATE void BIT_skipBits(BIT_DStream_t* bitD, U32 nbBits)
339e0c1b49fSNick Terrell {
340e0c1b49fSNick Terrell bitD->bitsConsumed += nbBits;
341e0c1b49fSNick Terrell }
342e0c1b49fSNick Terrell
343e0c1b49fSNick Terrell /*! BIT_readBits() :
344e0c1b49fSNick Terrell * Read (consume) next n bits from local register and update.
345e0c1b49fSNick Terrell * Pay attention to not read more than nbBits contained into local register.
346e0c1b49fSNick Terrell * @return : extracted value. */
BIT_readBits(BIT_DStream_t * bitD,unsigned nbBits)347*65d1f550SNick Terrell FORCE_INLINE_TEMPLATE BitContainerType BIT_readBits(BIT_DStream_t* bitD, unsigned nbBits)
348e0c1b49fSNick Terrell {
349*65d1f550SNick Terrell BitContainerType const value = BIT_lookBits(bitD, nbBits);
350e0c1b49fSNick Terrell BIT_skipBits(bitD, nbBits);
351e0c1b49fSNick Terrell return value;
352e0c1b49fSNick Terrell }
353e0c1b49fSNick Terrell
354e0c1b49fSNick Terrell /*! BIT_readBitsFast() :
355*65d1f550SNick Terrell * unsafe version; only works if nbBits >= 1 */
BIT_readBitsFast(BIT_DStream_t * bitD,unsigned nbBits)356*65d1f550SNick Terrell MEM_STATIC BitContainerType BIT_readBitsFast(BIT_DStream_t* bitD, unsigned nbBits)
357e0c1b49fSNick Terrell {
358*65d1f550SNick Terrell BitContainerType const value = BIT_lookBitsFast(bitD, nbBits);
359e0c1b49fSNick Terrell assert(nbBits >= 1);
360e0c1b49fSNick Terrell BIT_skipBits(bitD, nbBits);
361e0c1b49fSNick Terrell return value;
362e0c1b49fSNick Terrell }
363e0c1b49fSNick Terrell
364*65d1f550SNick Terrell /*! BIT_reloadDStream_internal() :
365*65d1f550SNick Terrell * Simple variant of BIT_reloadDStream(), with two conditions:
366*65d1f550SNick Terrell * 1. bitstream is valid : bitsConsumed <= sizeof(bitD->bitContainer)*8
367*65d1f550SNick Terrell * 2. look window is valid after shifted down : bitD->ptr >= bitD->start
368*65d1f550SNick Terrell */
BIT_reloadDStream_internal(BIT_DStream_t * bitD)369*65d1f550SNick Terrell MEM_STATIC BIT_DStream_status BIT_reloadDStream_internal(BIT_DStream_t* bitD)
370*65d1f550SNick Terrell {
371*65d1f550SNick Terrell assert(bitD->bitsConsumed <= sizeof(bitD->bitContainer)*8);
372*65d1f550SNick Terrell bitD->ptr -= bitD->bitsConsumed >> 3;
373*65d1f550SNick Terrell assert(bitD->ptr >= bitD->start);
374*65d1f550SNick Terrell bitD->bitsConsumed &= 7;
375*65d1f550SNick Terrell bitD->bitContainer = MEM_readLEST(bitD->ptr);
376*65d1f550SNick Terrell return BIT_DStream_unfinished;
377*65d1f550SNick Terrell }
378*65d1f550SNick Terrell
379e0c1b49fSNick Terrell /*! BIT_reloadDStreamFast() :
380e0c1b49fSNick Terrell * Similar to BIT_reloadDStream(), but with two differences:
381e0c1b49fSNick Terrell * 1. bitsConsumed <= sizeof(bitD->bitContainer)*8 must hold!
382e0c1b49fSNick Terrell * 2. Returns BIT_DStream_overflow when bitD->ptr < bitD->limitPtr, at this
383e0c1b49fSNick Terrell * point you must use BIT_reloadDStream() to reload.
384e0c1b49fSNick Terrell */
BIT_reloadDStreamFast(BIT_DStream_t * bitD)385e0c1b49fSNick Terrell MEM_STATIC BIT_DStream_status BIT_reloadDStreamFast(BIT_DStream_t* bitD)
386e0c1b49fSNick Terrell {
387e0c1b49fSNick Terrell if (UNLIKELY(bitD->ptr < bitD->limitPtr))
388e0c1b49fSNick Terrell return BIT_DStream_overflow;
389*65d1f550SNick Terrell return BIT_reloadDStream_internal(bitD);
390e0c1b49fSNick Terrell }
391e0c1b49fSNick Terrell
392e0c1b49fSNick Terrell /*! BIT_reloadDStream() :
393e0c1b49fSNick Terrell * Refill `bitD` from buffer previously set in BIT_initDStream() .
394*65d1f550SNick Terrell * This function is safe, it guarantees it will not never beyond src buffer.
395e0c1b49fSNick Terrell * @return : status of `BIT_DStream_t` internal register.
396e0c1b49fSNick Terrell * when status == BIT_DStream_unfinished, internal register is filled with at least 25 or 57 bits */
BIT_reloadDStream(BIT_DStream_t * bitD)397*65d1f550SNick Terrell FORCE_INLINE_TEMPLATE BIT_DStream_status BIT_reloadDStream(BIT_DStream_t* bitD)
398e0c1b49fSNick Terrell {
399*65d1f550SNick Terrell /* note : once in overflow mode, a bitstream remains in this mode until it's reset */
400*65d1f550SNick Terrell if (UNLIKELY(bitD->bitsConsumed > (sizeof(bitD->bitContainer)*8))) {
401*65d1f550SNick Terrell static const BitContainerType zeroFilled = 0;
402*65d1f550SNick Terrell bitD->ptr = (const char*)&zeroFilled; /* aliasing is allowed for char */
403*65d1f550SNick Terrell /* overflow detected, erroneous scenario or end of stream: no update */
404e0c1b49fSNick Terrell return BIT_DStream_overflow;
405*65d1f550SNick Terrell }
406*65d1f550SNick Terrell
407*65d1f550SNick Terrell assert(bitD->ptr >= bitD->start);
408e0c1b49fSNick Terrell
409e0c1b49fSNick Terrell if (bitD->ptr >= bitD->limitPtr) {
410*65d1f550SNick Terrell return BIT_reloadDStream_internal(bitD);
411e0c1b49fSNick Terrell }
412e0c1b49fSNick Terrell if (bitD->ptr == bitD->start) {
413*65d1f550SNick Terrell /* reached end of bitStream => no update */
414e0c1b49fSNick Terrell if (bitD->bitsConsumed < sizeof(bitD->bitContainer)*8) return BIT_DStream_endOfBuffer;
415e0c1b49fSNick Terrell return BIT_DStream_completed;
416e0c1b49fSNick Terrell }
417*65d1f550SNick Terrell /* start < ptr < limitPtr => cautious update */
418e0c1b49fSNick Terrell { U32 nbBytes = bitD->bitsConsumed >> 3;
419e0c1b49fSNick Terrell BIT_DStream_status result = BIT_DStream_unfinished;
420e0c1b49fSNick Terrell if (bitD->ptr - nbBytes < bitD->start) {
421e0c1b49fSNick Terrell nbBytes = (U32)(bitD->ptr - bitD->start); /* ptr > start */
422e0c1b49fSNick Terrell result = BIT_DStream_endOfBuffer;
423e0c1b49fSNick Terrell }
424e0c1b49fSNick Terrell bitD->ptr -= nbBytes;
425e0c1b49fSNick Terrell bitD->bitsConsumed -= nbBytes*8;
426e0c1b49fSNick Terrell bitD->bitContainer = MEM_readLEST(bitD->ptr); /* reminder : srcSize > sizeof(bitD->bitContainer), otherwise bitD->ptr == bitD->start */
427e0c1b49fSNick Terrell return result;
428e0c1b49fSNick Terrell }
429e0c1b49fSNick Terrell }
430e0c1b49fSNick Terrell
431e0c1b49fSNick Terrell /*! BIT_endOfDStream() :
432e0c1b49fSNick Terrell * @return : 1 if DStream has _exactly_ reached its end (all bits consumed).
433e0c1b49fSNick Terrell */
BIT_endOfDStream(const BIT_DStream_t * DStream)434e0c1b49fSNick Terrell MEM_STATIC unsigned BIT_endOfDStream(const BIT_DStream_t* DStream)
435e0c1b49fSNick Terrell {
436e0c1b49fSNick Terrell return ((DStream->ptr == DStream->start) && (DStream->bitsConsumed == sizeof(DStream->bitContainer)*8));
437e0c1b49fSNick Terrell }
438e0c1b49fSNick Terrell
439e0c1b49fSNick Terrell #endif /* BITSTREAM_H_MODULE */
440