]> git.sesse.net Git - fjl/blob - bitsource.c
7a4ea4a0ca3c553e276be095932061313e833441
[fjl] / bitsource.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <stdbool.h>
4 #include <string.h>
5
6 #include "bitsource.h"
7
8 #define MARKER_CHAR 0xff
9 #define STUFF_MARKER 0x00
10
11 void init_bit_source(struct bit_source* source, input_func_t* input_func,
12                      unsigned padding_bytes, void* userdata)
13 {
14         memset(source, 0, sizeof(*source));
15         source->bytes = (uint8_t*)malloc(BYTERESERVOIR_SIZE);
16         source->byte_read_ptr = source->bytes;
17         source->input_func = input_func;
18         source->padding_bytes_available = padding_bytes;
19         source->userdata = userdata;
20 }
21
22 void possibly_refill_slow_path(struct bit_source* source, unsigned num_bits)
23 {
24         // First, move out the data we already read.
25         assert(source->bytes_available <= BYTERESERVOIR_SIZE);
26         assert(source->byte_read_ptr >= source->bytes);
27         memmove(source->bytes, source->byte_read_ptr, source->bytes_available);
28         source->byte_read_ptr = source->bytes;
29
30         // Then, make sure there's stuff in the byte reservoir if we can.
31         // Read data from the source until we have enough to satisfy the request.
32         while (source->bits_available + 8 * source->bytes_available < num_bits) {
33                 const size_t bytes_to_read = BYTERESERVOIR_SIZE - source->bytes_available;
34                 const ssize_t bytes_read =
35                         (*source->input_func)(source->userdata,
36                                               source->bytes + source->bytes_available,
37                                               bytes_to_read);
38                 assert(bytes_read <= (ssize_t)bytes_to_read);
39                 assert(bytes_read >= (ssize_t)-1);
40
41                 // TODO: We need better error handling here. setjmp()/longjmp()
42                 // should hopefully do the trick, but we need to take care for
43                 // suspension.
44                 if (bytes_read == (ssize_t)-1) {
45                         fprintf(stderr, "Input function returned error\n");
46                         exit(1);
47                 }
48                 if (bytes_read == 0 && source->padding_bytes_available > 0) {
49                         unsigned padding_to_add = source->padding_bytes_available;
50                         if (padding_to_add > bytes_to_read) {
51                                 padding_to_add = bytes_to_read;
52                         }
53                         memset(source->bytes + source->bytes_available, 0, padding_to_add);
54                         source->padding_bytes_available -= padding_to_add;
55                         source->bytes_available += padding_to_add;
56                 } else if (bytes_read == 0) {
57                         fprintf(stderr, "Premature EOF\n");
58                         exit(1);
59                 } else {
60                         source->bytes_available += bytes_read;
61                 }
62         }
63
64         // Fill the bit reservoir one by one byte until we have enough.
65         while (source->bits_available < num_bits) {
66                 assert(source->bytes_available > 0);
67                 assert(source->bits_available + 8 <= BITRESERVOIR_SIZE);
68                 uint8_t byte = *(source->byte_read_ptr);
69                 ++source->byte_read_ptr;
70                 --source->bytes_available;
71                 source->bits |= ((bitreservoir_t)byte << (BITRESERVOIR_SIZE - source->bits_available - 8));
72                 source->bits_available += 8;
73         }
74 }