#define MARKER_CHAR 0xff
#define STUFF_MARKER 0x00
-void init_bit_source(struct bit_source* source, input_func_t* input_func, void* userdata)
+void init_bit_source(struct bit_source* source, input_func_t* input_func,
+ unsigned padding_bytes, void* userdata)
{
memset(source, 0, sizeof(*source));
source->bytes = (uint8_t*)malloc(BYTERESERVOIR_SIZE);
source->byte_read_ptr = source->bytes;
source->input_func = input_func;
+ source->padding_bytes_available = padding_bytes;
source->userdata = userdata;
}
fprintf(stderr, "Input function returned error\n");
exit(1);
}
- if (bytes_read == 0) {
+ if (bytes_read == 0 && source->padding_bytes_available > 0) {
+ unsigned padding_to_add = source->padding_bytes_available;
+ if (padding_to_add > bytes_to_read) {
+ padding_to_add = bytes_to_read;
+ }
+ memset(source->bytes + source->bytes_available, 0, padding_to_add);
+ source->padding_bytes_available -= padding_to_add;
+ source->bytes_available += padding_to_add;
+ } else if (bytes_read == 0) {
fprintf(stderr, "Premature EOF\n");
exit(1);
+ } else {
+ source->bytes_available += bytes_read;
}
-
- source->bytes_available += bytes_read;
}
// Fill the bit reservoir one by one byte until we have enough.
uint8_t* byte_read_ptr;
unsigned bytes_available;
+ // Some clients will purposedly read a bit ahead of the stream, causing
+ // problems at EOF. Thus, the client is allowed to request that we pad
+ // the end stream with a few bytes after the source reports EOF.
+ int padding_bytes_available;
+
// Data source.
input_func_t* input_func;
void* userdata;
};
-void init_bit_source(struct bit_source* source, input_func_t* input_func, void* userdata);
+void init_bit_source(struct bit_source* source, input_func_t* input_func,
+ unsigned padding_bytes, void* userdata);
// Internal function. Do not use.
void possibly_refill_slow_path(struct bit_source* source, unsigned num_bits);
ud.bytes_left = sizeof(bytes);
struct bit_source source;
- init_bit_source(&source, custom_read, &ud);
+ init_bit_source(&source, custom_read, 0, &ud);
for (unsigned i = 0; i < sizeof(bytes) * 8 / 6; ++i) {
possibly_refill(&source, 6);
ud.bytes_left = sizeof(bytes);
struct bit_source source;
- init_bit_source(&source, custom_read_slow, &ud);
+ init_bit_source(&source, custom_read_slow, 0, &ud);
for (unsigned i = 0; i < sizeof(bytes) * 8 / 6; ++i) {
possibly_refill(&source, 6);
ud.bytes_left = sizeof(bytes);
struct bit_source source;
- init_bit_source(&source, custom_read, &ud);
+ init_bit_source(&source, custom_read, 0, &ud);
{
possibly_refill(&source, 4);
assert(ret == 0xf);
}
+ assert(ud.bytes_left == 0);
+}
+
+// Tests that padding bytes are properly added at the end, even crossing reads.
+void test_padding_reads()
+{
+ uint8_t bytes[] = { 0x01, 0x02 };
+
+ struct custom_read_userdata ud;
+ ud.bytes = bytes;
+ ud.bytes_left = sizeof(bytes);
+
+ struct bit_source source;
+ init_bit_source(&source, custom_read, 2, &ud);
+
+ {
+ possibly_refill(&source, 8);
+ unsigned ret = read_bits(&source, 8);
+ assert(ret == 0x01);
+ }
+ {
+ possibly_refill(&source, 12);
+ unsigned ret = read_bits(&source, 12);
+ assert(ret == 0x020);
+ }
+
assert(ud.bytes_left == 0);
}
printf("test_variable_size()\n");
test_variable_size();
+ printf("test_padding_reads()\n");
+ test_padding_reads();
+
printf("All tests pass.\n");
return 0;
}
static inline unsigned read_huffman_symbol(const struct huffman_table* table,
struct bit_source* source)
{
- // FIXME: We can read past the end of the stream here in some edge
- // cases. We need to define some guarantees in the layers above.
possibly_refill(source, DEHUF_TABLE_BITS);
+ assert(source->bits_available >= DEHUF_TABLE_BITS);
unsigned lookup = peek_bits(source, DEHUF_TABLE_BITS);
int code = table->lookup_table_codes[lookup];
int length = table->lookup_table_length[lookup];
if (code == DEHUF_SLOW_PATH) {
return read_huffman_symbol_slow_path(table, source);
}
-
+
read_bits(source, length);
return code;
}
// Test that we can decode a simple bit stream.
// Note that since we end on a long code, we won't crash into
-// the end-of-stream problems we currently have.
+// the end-of-stream problems we have without padding.
void test_decoding()
{
huffman_tables_t tables;
ud.bytes_left = sizeof(bytes);
struct bit_source source;
- init_bit_source(&source, custom_read, &ud);
+ init_bit_source(&source, custom_read, 0, &ud);
struct huffman_table* tbl = &tables[DC_CLASS][0];
for (unsigned i = 0; i < 12; ++i) {
assert(source.bits_available == 0);
}
+// Test that we can decode a bit stream that ends on a short code,
+// if we've got padding.
+void test_padded_decoding()
+{
+ huffman_tables_t tables;
+ read_example_tables(&tables);
+
+ // Our stream looks like this:
+ //
+ // 0 1 2 3 4
+ // 00 010 011 100 101
+ uint8_t bytes[] = {
+ 0x13, 0x94
+ };
+
+ struct custom_read_userdata ud;
+ ud.bytes = bytes;
+ ud.bytes_left = sizeof(bytes);
+
+ struct bit_source source;
+ init_bit_source(&source, custom_read, 2, &ud);
+
+ struct huffman_table* tbl = &tables[DC_CLASS][0];
+ for (unsigned i = 0; i < 5; ++i) {
+ unsigned symbol = read_huffman_symbol(tbl, &source);
+ assert(symbol == i);
+ }
+}
+
int main(void)
{
printf("test_table_gen()\n");
printf("test_decoding()\n");
test_decoding();
+ printf("test_padded_decoding()\n");
+ test_padded_decoding();
+
printf("All tests pass.\n");
return 0;
}
}
struct bit_source bits;
- init_bit_source(&bits, byte_source_input_func, source);
+ init_bit_source(&bits, byte_source_input_func, 8, source);
unsigned mcu_x = 0, mcu_y = 0;