Add optional padding data at the end to the bit source (is that the right place?...
authorsgunderson@bigfoot.com <>
Sun, 31 May 2009 18:04:50 +0000 (20:04 +0200)
committersgunderson@bigfoot.com <>
Sun, 31 May 2009 18:04:50 +0000 (20:04 +0200)
bitsource.c
bitsource.h
bitsource_test.c
dehuff.h
dehuff_test.c
driver.c

index 6cc627c..7a4ea4a 100644 (file)
@@ -8,12 +8,14 @@
 #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;
 }
 
@@ -43,12 +45,20 @@ void possibly_refill_slow_path(struct bit_source* source, unsigned num_bits)
                        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.
index bb3a6d2..ae29b4c 100644 (file)
@@ -37,12 +37,18 @@ struct bit_source {
        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);
index 2b5217f..173e499 100644 (file)
@@ -40,7 +40,7 @@ void test_basic_reading()
        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);
@@ -60,7 +60,7 @@ void test_slow_source()
        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);
@@ -80,7 +80,7 @@ void test_variable_size()
        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);
@@ -111,6 +111,32 @@ void test_variable_size()
                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);
 }
 
@@ -125,6 +151,9 @@ int main(void)
        printf("test_variable_size()\n");
        test_variable_size();
        
+       printf("test_padding_reads()\n");
+       test_padding_reads();
+       
        printf("All tests pass.\n");
        return 0;
 }
index 224005f..a6a431a 100644 (file)
--- a/dehuff.h
+++ b/dehuff.h
@@ -51,9 +51,8 @@ unsigned read_huffman_symbol_slow_path(const struct huffman_table* table,
 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];
@@ -61,7 +60,7 @@ static inline unsigned read_huffman_symbol(const struct huffman_table* table,
        if (code == DEHUF_SLOW_PATH) {
                return read_huffman_symbol_slow_path(table, source);
        }
-
+               
        read_bits(source, length);
        return code;
 }
index 0212244..eed0952 100644 (file)
@@ -78,7 +78,7 @@ void test_table_gen()
 
 // 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;
@@ -97,7 +97,7 @@ void test_decoding()
        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) {
@@ -108,6 +108,35 @@ void test_decoding()
        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");
@@ -116,6 +145,9 @@ int main(void)
        printf("test_decoding()\n");
        test_decoding();
        
+       printf("test_padded_decoding()\n");
+       test_padded_decoding();
+       
        printf("All tests pass.\n");
        return 0;
 }
index 06d94d9..aa803d6 100644 (file)
--- a/driver.c
+++ b/driver.c
@@ -147,7 +147,7 @@ void read_scan(struct byte_source* source, struct jpeg_image* image, huffman_tab
        }
 
        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;