Forked from
Hammer / hammer
151 commits behind the upstream repository.
-
Sven M. Hallberg authored
bitreader.c 5.09 KiB
/* Bit-parsing operations for Hammer.
* Copyright (C) 2012 Meredith L. Patterson, Dan "TQ" Hirsch
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation, version 2.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU 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.
*/
#include <stdint.h>
#include <stdio.h>
#include "internal.h"
#include "hammer.h"
#include "test_suite.h"
#define LSB(range) (0:range)
#define MSB(range) (1:range)
#define LDB(range,i) (((i)>>LSB(range))&((1<<(MSB(range)-LSB(range)+1))-1))
int64_t h_read_bits(HInputStream* state, int count, char signed_p) {
// BUG: Does not
int64_t out = 0;
int offset = 0;
int final_shift = 0;
int64_t msb = ((signed_p ? 1LL:0) << (count - 1)); // 0 if unsigned, else 1 << (nbits - 1)
// overflow check...
int bits_left = (state->length - state->index); // well, bytes for now
if (bits_left <= 64) { // Large enough to handle any valid count, but small enough that overflow isn't a problem.
// not in danger of overflowing, so add in bits
// add in number of bits...
bits_left = (bits_left << 3) - state->bit_offset - state->margin;
if (bits_left < count) {
if (state->endianness & BYTE_BIG_ENDIAN)
final_shift = count - bits_left;
else
final_shift = 0;
count = bits_left;
state->overrun = true;
} else
final_shift = 0;
}
if ((state->bit_offset & 0x7) == 0 && (count & 0x7) == 0 && (state->margin == 0)) {
// fast path
if (state->endianness & BYTE_BIG_ENDIAN) {
while (count > 0) {
count -= 8;
out = (out << 8) | state->input[state->index++];
}
} else {
int i;
for (i = 0; count > 0; i += 8) {
count -= 8;
out |= (int64_t)state->input[state->index++] << i;
}
}
} else {
while (count) {
int segment, segment_len;
// Read a segment...
if (state->endianness & BIT_BIG_ENDIAN) {
if (count + state->bit_offset + state->margin >= 8) {
segment_len = 8 - state->bit_offset - state->margin;
segment = (state->input[state->index] >> state->margin) & ((1 << segment_len) - 1);
state->index++;
state->bit_offset = 0;
state->margin = 0;
} else {
segment_len = count;
state->bit_offset += count;
segment = (state->input[state->index] >> (8 - state->bit_offset)) & ((1 << segment_len) - 1);
}
} else { // BIT_LITTLE_ENDIAN
if (count + state->bit_offset + state->margin >= 8) {
segment_len = 8 - state->bit_offset - state->margin;
segment = (state->input[state->index] >> state->bit_offset) & ((1 << segment_len) - 1);
state->index++;
state->bit_offset = 0;
state->margin = 0;
} else {
segment_len = count;
segment = (state->input[state->index] >> state->bit_offset) & ((1 << segment_len) - 1);
state->bit_offset += segment_len;
}
}
// have a valid segment; time to assemble the byte
if (state->endianness & BYTE_BIG_ENDIAN) {
out = out << segment_len | segment;
} else { // BYTE_LITTLE_ENDIAN
out |= (int64_t)segment << offset;
offset += segment_len;
}
count -= segment_len;
}
}
out <<= final_shift;
return (out ^ msb) - msb; // perform sign extension
}
void h_skip_bits(HInputStream* stream, size_t count) {
size_t left;
if (count == 0)
return;
if (stream->overrun)
return;
if (stream->index == stream->length) {
stream->overrun = true;
return;
}
// consume from a partial byte?
left = 8 - stream->bit_offset - stream->margin;
if (count < left) {
stream->bit_offset += count;
return;
}
if (left < 8) {
stream->index += 1;
stream->bit_offset = 0;
stream->margin = 0;
count -= left;
}
assert(stream->bit_offset == 0);
assert(stream->margin == 0);
// consume full bytes
left = stream->length - stream->index;
if (count / 8 <= left) {
stream->index += count / 8;
count = count % 8;
} else {
stream->index = stream->length;
stream->overrun = true;
return;
}
assert(count < 8);
// final partial byte
if (count > 0 && stream->index == stream->length)
stream->overrun = true;
else
stream->bit_offset = count;
}
void h_seek_bits(HInputStream* stream, size_t pos) {
size_t pos_index = pos / 8;
size_t pos_offset = pos % 8;
/* seek within the current byte? */
if (pos_index == stream->index) {
stream->bit_offset = pos_offset;
return;
}
stream->margin = 0;
/* seek past the end? */
if ((pos_index > stream->length) ||
(pos_index == stream->length && pos_offset > 0)) {
stream->index = stream->length;
stream->bit_offset = 0;
stream->overrun = true;
return;
}
stream->index = pos_index;
stream->bit_offset = pos_offset;
stream->margin = 0;
}