|
| 1 | +#include "bitstream.h" |
| 2 | + |
| 3 | +#include <limits.h> |
| 4 | +#include "simplicity_assert.h" |
| 5 | + |
| 6 | +/* Closes a bitstream by consuming all remaining bits. |
| 7 | + * Returns 'SIMPLICITY_ERR_BITSTREAM_TRAILING_BYTES' if CHAR_BIT or more bits remain in the stream. |
| 8 | + * Otherwise, returns 'SIMPLICITY_ERR_BITSTREAM_ILLEGAL_PADDING' if any remaining bits are non-zero. |
| 9 | + * Otherwise returns 'SIMPLICITY_NO_ERROR'. |
| 10 | + * |
| 11 | + * Precondition: NULL != stream |
| 12 | + */ |
| 13 | +simplicity_err simplicity_closeBitstream(bitstream* stream) { |
| 14 | + if (1 < stream->len) return SIMPLICITY_ERR_BITSTREAM_TRAILING_BYTES; /* If there is more than one byte remaining. */ |
| 15 | + if (1 == stream->len) { |
| 16 | + if (0 == stream->offset) return SIMPLICITY_ERR_BITSTREAM_TRAILING_BYTES; /* If there is one byte remaining */ |
| 17 | + if (0 != (*stream->arr & (UCHAR_MAX >> stream->offset))) { /* If any of the unconsumed bits are non-zero */ |
| 18 | + return SIMPLICITY_ERR_BITSTREAM_ILLEGAL_PADDING; |
| 19 | + } |
| 20 | + } |
| 21 | + /* Otherwise there are either 0 bits remaining or there are between 1 and CHAR_BITS-1 bits remaining and they are all zero. */ |
| 22 | + *stream = (bitstream){0}; |
| 23 | + return SIMPLICITY_NO_ERROR; |
| 24 | +} |
| 25 | + |
| 26 | +/* Fetches up to 31 bits from 'stream' as the 'n' least significant bits of return value. |
| 27 | + * The 'n' bits are set from the MSB to the LSB. |
| 28 | + * Returns 'SIMPLICITY_ERR_BITSTREAM_EOF' if not enough bits are available. |
| 29 | + * |
| 30 | + * Precondition: 0 <= n < 32 |
| 31 | + * NULL != stream |
| 32 | + */ |
| 33 | +int32_t simplicity_readNBits(int n, bitstream* stream) { |
| 34 | + simplicity_assert(0 <= n && n < 32); |
| 35 | + |
| 36 | + uint32_t result = 0; |
| 37 | + while (CHAR_BIT <= stream->offset + n) { |
| 38 | + if (!stream->len) return SIMPLICITY_ERR_BITSTREAM_EOF; |
| 39 | + n -= CHAR_BIT - stream->offset; |
| 40 | + result |= (uint32_t)(*stream->arr & (UCHAR_MAX >> stream->offset)) << n; |
| 41 | + stream->arr++; stream->len--; stream->offset = 0; |
| 42 | + } |
| 43 | + /* stream->offset + n < CHAR_BIT */ |
| 44 | + if (n) { |
| 45 | + if (!stream->len) return SIMPLICITY_ERR_BITSTREAM_EOF; |
| 46 | + stream->offset += (unsigned char)n; |
| 47 | + result |= (*stream->arr >> (CHAR_BIT - stream->offset)) & ((UCHAR_MAX >> (CHAR_BIT - n))); |
| 48 | + } |
| 49 | + return (int32_t)result; |
| 50 | +} |
| 51 | +/* Decode an encoded bitstring up to length 1. |
| 52 | + * If successful returns the length of the bitstring and 'result' contains the decoded bits. |
| 53 | + * The decoded bitstring is stored in the LSBs of 'result', with the LSB being the last bit decoded. |
| 54 | + * Any remaining bits in 'result' are reset to 0. |
| 55 | + * If the decoded bitstring would be too long 'SIMPLICITY_ERR_DATA_OUT_OF_RANGE' is returned ('result' may be modified). |
| 56 | + * If more bits are needed than available in the 'stream', 'SIMPLICITY_ERR_BITSTRING_EOF' is returned ('result' may be modified). |
| 57 | + * If an I/O error occurs when reading from the 'stream', 'SIMPLICITY_ERR_BISTRING_ERROR' is returned ('result' may be modified). |
| 58 | + * |
| 59 | + * Precondition: NULL != result |
| 60 | + * NULL != stream |
| 61 | + */ |
| 62 | +static int32_t decodeUpto1Bit(int32_t* result, bitstream* stream) { |
| 63 | + *result = read1Bit(stream); |
| 64 | + if (*result <= 0) return *result; |
| 65 | + |
| 66 | + *result = read1Bit(stream); |
| 67 | + if (*result < 0) return *result; |
| 68 | + if (0 != *result) return SIMPLICITY_ERR_DATA_OUT_OF_RANGE; |
| 69 | + |
| 70 | + *result = read1Bit(stream); |
| 71 | + if (*result < 0) return *result; |
| 72 | + return 1; |
| 73 | +} |
| 74 | + |
| 75 | +/* Decode an encoded number between 1 and 3 inclusive. |
| 76 | + * When successful returns the decoded result. |
| 77 | + * If the decoded value would be too large, 'SIMPLICITY_ERR_DATA_OUT_OF_RANGE' is returned. |
| 78 | + * If more bits are needed than available in the 'stream', 'SIMPLICITY_ERR_BITSTRING_EOF' is returned. |
| 79 | + * If an I/O error occurs when reading from the 'stream', 'SIMPLICITY_ERR_BISTRING_ERROR' is returned. |
| 80 | + * |
| 81 | + * Precondition: NULL != stream |
| 82 | + */ |
| 83 | +static int32_t decodeUpto3(bitstream* stream) { |
| 84 | + int32_t result; |
| 85 | + int32_t len = decodeUpto1Bit(&result, stream); |
| 86 | + if (len < 0) return len; |
| 87 | + result |= 1 << len; |
| 88 | + return result; |
| 89 | +} |
| 90 | + |
| 91 | +/* Decode an encoded bitstring up to length 3. |
| 92 | + * If successful returns the length of the bitstring and 'result' contains the decoded bits. |
| 93 | + * The decoded bitstring is stored in the LSBs of 'result', with the LSB being the last bit decoded. |
| 94 | + * Any remaining bits in 'result' are reset to 0. |
| 95 | + * If the decoded bitstring would be too long 'SIMPLICITY_ERR_DATA_OUT_OF_RANGE' is returned ('result' may be modified). |
| 96 | + * If more bits are needed than available in the 'stream', 'SIMPLICITY_ERR_BITSTRING_EOF' is returned ('result' may be modified). |
| 97 | + * If an I/O error occurs when reading from the 'stream', 'SIMPLICITY_ERR_BISTRING_ERROR' is returned ('result' may be modified). |
| 98 | + * |
| 99 | + * Precondition: NULL != result |
| 100 | + * NULL != stream |
| 101 | + */ |
| 102 | +static int32_t decodeUpto3Bits(int32_t* result, bitstream* stream) { |
| 103 | + int32_t bit = read1Bit(stream); |
| 104 | + if (bit < 0) return bit; |
| 105 | + |
| 106 | + *result = 0; |
| 107 | + if (0 == bit) { |
| 108 | + return 0; |
| 109 | + } else { |
| 110 | + int32_t n = decodeUpto3(stream); |
| 111 | + if (0 <= n) { |
| 112 | + *result = simplicity_readNBits(n, stream); |
| 113 | + if (*result < 0) return *result; |
| 114 | + } |
| 115 | + return n; |
| 116 | + } |
| 117 | +} |
| 118 | + |
| 119 | +/* Decode an encoded number between 1 and 15 inclusive. |
| 120 | + * When successful returns the decoded result. |
| 121 | + * If the decoded value would be too large, 'SIMPLICITY_ERR_DATA_OUT_OF_RANGE' is returned. |
| 122 | + * If more bits are needed than available in the 'stream', 'SIMPLICITY_ERR_BITSTRING_EOF' is returned. |
| 123 | + * If an I/O error occurs when reading from the 'stream', 'SIMPLICITY_ERR_BISTRING_ERROR' is returned. |
| 124 | + * |
| 125 | + * Precondition: NULL != stream |
| 126 | + */ |
| 127 | +static int32_t decodeUpto15(bitstream* stream) { |
| 128 | + int32_t result; |
| 129 | + int32_t len = decodeUpto3Bits(&result, stream); |
| 130 | + if (len < 0) return len; |
| 131 | + result |= 1 << len; |
| 132 | + return result; |
| 133 | +} |
| 134 | + |
| 135 | +/* Decode an encoded bitstring up to length 15. |
| 136 | + * If successful returns the length of the bitstring and 'result' contains the decoded bits. |
| 137 | + * The decoded bitstring is stored in the LSBs of 'result', with the LSB being the last bit decoded. |
| 138 | + * Any remaining bits in 'result' are reset to 0. |
| 139 | + * If the decoded bitstring would be too long 'SIMPLICITY_ERR_DATA_OUT_OF_RANGE' is returned ('result' may be modified). |
| 140 | + * If more bits are needed than available in the 'stream', 'SIMPLICITY_ERR_BITSTRING_EOF' is returned ('result' may be modified). |
| 141 | + * If an I/O error occurs when reading from the 'stream', 'SIMPLICITY_ERR_BISTRING_ERROR' is returned ('result' may be modified). |
| 142 | + * |
| 143 | + * Precondition: NULL != result |
| 144 | + * NULL != stream |
| 145 | + */ |
| 146 | +static int32_t decodeUpto15Bits(int32_t* result, bitstream* stream) { |
| 147 | + int32_t bit = read1Bit(stream); |
| 148 | + if (bit < 0) return bit; |
| 149 | + |
| 150 | + *result = 0; |
| 151 | + if (0 == bit) { |
| 152 | + return 0; |
| 153 | + } else { |
| 154 | + int32_t n = decodeUpto15(stream); |
| 155 | + if (0 <= n) { |
| 156 | + *result = simplicity_readNBits(n, stream); |
| 157 | + if (*result < 0) return *result; |
| 158 | + } |
| 159 | + return n; |
| 160 | + } |
| 161 | +} |
| 162 | + |
| 163 | +/* Decode an encoded number between 1 and 65535 inclusive. |
| 164 | + * When successful returns the decoded result. |
| 165 | + * If the decoded value would be too large, 'SIMPLICITY_ERR_DATA_OUT_OF_RANGE' is returned. |
| 166 | + * If more bits are needed than available in the 'stream', 'SIMPLICITY_ERR_BITSTRING_EOF' is returned. |
| 167 | + * If an I/O error occurs when reading from the 'stream', 'SIMPLICITY_ERR_BISTRING_ERROR' is returned. |
| 168 | + * |
| 169 | + * Precondition: NULL != stream |
| 170 | + */ |
| 171 | +static int32_t decodeUpto65535(bitstream* stream) { |
| 172 | + int32_t result; |
| 173 | + int32_t len = decodeUpto15Bits(&result, stream); |
| 174 | + if (len < 0) return len; |
| 175 | + result |= 1 << len; |
| 176 | + return result; |
| 177 | +} |
| 178 | + |
| 179 | +/* Decode an encoded number between 1 and 2^31 - 1 inclusive. |
| 180 | + * When successful returns the decoded result. |
| 181 | + * If the decoded value would be too large, 'SIMPLICITY_ERR_DATA_OUT_OF_RANGE' is returned. |
| 182 | + * If more bits are needed than available in the 'stream', 'SIMPLICITY_ERR_BITSTRING_EOF' is returned. |
| 183 | + * If an I/O error occurs when reading from the 'stream', 'SIMPLICITY_ERR_BISTRING_ERROR' is returned. |
| 184 | + * |
| 185 | + * Precondition: NULL != stream |
| 186 | + */ |
| 187 | +int32_t simplicity_decodeUptoMaxInt(bitstream* stream) { |
| 188 | + int32_t bit = read1Bit(stream); |
| 189 | + if (bit < 0) return bit; |
| 190 | + if (0 == bit) { |
| 191 | + return 1; |
| 192 | + } else { |
| 193 | + int32_t n = decodeUpto65535(stream); |
| 194 | + if (n < 0) return n; |
| 195 | + if (30 < n) return SIMPLICITY_ERR_DATA_OUT_OF_RANGE; |
| 196 | + { |
| 197 | + int32_t result = simplicity_readNBits(n, stream); |
| 198 | + if (result < 0) return result; |
| 199 | + return ((1 << n) | result); |
| 200 | + } |
| 201 | + } |
| 202 | +} |
| 203 | + |
| 204 | +/* Fills a 'bitstring' containing 'n' bits from 'stream'. |
| 205 | + * Returns 'SIMPLICITY_ERR_BITSTREAM_EOF' if not enough bits are available. |
| 206 | + * If successful, '*result' is set to a bitstring with 'n' bits read from 'stream' and 'SIMPLICITY_NO_ERROR' is returned. |
| 207 | + * |
| 208 | + * If an error is returned '*result' might be modified. |
| 209 | + * |
| 210 | + * Precondition: NULL != result |
| 211 | + * n <= 2^31 |
| 212 | + * NULL != stream |
| 213 | + */ |
| 214 | +simplicity_err simplicity_readBitstring(bitstring* result, size_t n, bitstream* stream) { |
| 215 | + static_assert(0x80000000u + 2*(CHAR_BIT - 1) <= SIZE_MAX, "size_t needs to be at least 32-bits"); |
| 216 | + simplicity_assert(n <= 0x80000000u); |
| 217 | + size_t total_offset = n + stream->offset; |
| 218 | + /* |= stream->len * CHAR_BIT < total_offset iff stream->len < (total_offset + (CHAR_BIT - 1)) / CHAR_BIT */ |
| 219 | + if (stream->len < (total_offset + (CHAR_BIT - 1)) / CHAR_BIT) return SIMPLICITY_ERR_BITSTREAM_EOF; |
| 220 | + /* total_offset <= stream->len * CHAR_BIT */ |
| 221 | + *result = (bitstring) |
| 222 | + { .arr = stream->arr |
| 223 | + , .offset = stream->offset |
| 224 | + , .len = n |
| 225 | + }; |
| 226 | + { |
| 227 | + size_t delta = total_offset / CHAR_BIT; |
| 228 | + stream->arr += delta; stream->len -= delta; |
| 229 | + stream->offset = total_offset % CHAR_BIT; |
| 230 | + /* Note that if 0 == stream->len then 0 == stream->offset. */ |
| 231 | + } |
| 232 | + return SIMPLICITY_NO_ERROR; |
| 233 | +} |
0 commit comments