]> git.sesse.net Git - fjl/blob - bytesource.c
Add DQT parsing.
[fjl] / bytesource.c
1 #include <stdbool.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <assert.h>
5
6 #include "choice.h"
7 #include "bytesource.h"
8
9 #define MARKER_CHAR 0xff
10 #define STUFF_MARKER 0x00
11
12 void init_byte_source(struct byte_source* source, raw_input_func_t* input_func, void* userdata)
13 {
14         // TODO: should this size be a different constant?
15         memset(source, 0, sizeof(*source));
16         source->bytes = (uint8_t*)malloc(BYTESOURCE_CHUNK_SIZE);
17         source->input_func = input_func;
18         source->userdata = userdata;
19 }
20
21 uint8_t byte_source_read_marker(struct byte_source* src)
22 {
23         // Refill until we have at least two bytes or EOF.
24         while (src->bytes_available < 2) {
25                 const unsigned bytes_to_read = 2 - src->bytes_available;
26                 const ssize_t bytes_read =
27                         (*src->input_func)(src->userdata,
28                                            src->bytes + src->bytes_available,
29                                            bytes_to_read);
30                 assert(bytes_read >= -1);
31                 assert(bytes_read <= (ssize_t)bytes_to_read);
32                 
33                 if (bytes_read == -1 || bytes_read == 0) {
34                         return 0x00;
35                 }
36
37                 src->bytes_available += bytes_read;
38         }
39
40         assert(src->bytes_available >= 2);
41         if (src->bytes[0] != MARKER_CHAR || src->bytes[1] == STUFF_MARKER) {
42                 return 0x00;
43         }
44
45         uint8_t ret = src->bytes[1];
46         memmove(src->bytes, src->bytes + 2, src->bytes_available - 2);
47         src->bytes_available -= 2;
48         
49         return ret;
50 }
51
52 ssize_t byte_source_input_func(void* source, uint8_t* buf, size_t len)
53 {
54         struct byte_source* src = (struct byte_source*)source;
55
56         // If there's no data in the buffer (or only a partial marker), we have
57         // to read in more from our upstream src.
58         while (src->bytes_available == 0 ||
59                (src->bytes_available == 1 && src->bytes[0] == MARKER_CHAR)) {
60                 const unsigned space_left = BYTESOURCE_CHUNK_SIZE - src->bytes_available;
61                 const unsigned missing_data = len - src->bytes_available;
62                 const size_t bytes_to_read = (missing_data > space_left ? space_left : missing_data);
63                 assert(bytes_to_read <= BYTESOURCE_CHUNK_SIZE);
64                 const ssize_t bytes_read =
65                         (*src->input_func)(src->userdata,
66                                            src->bytes + src->bytes_available,
67                                            bytes_to_read);
68                 assert(bytes_read >= -1);
69                 assert(bytes_read <= (ssize_t)bytes_to_read);
70                 
71                 if (bytes_read == -1) {
72                         return -1;
73                 } else if (bytes_read == 0) {
74                         if (src->bytes_available == 1) {
75                                 // EOF in the middle of a marker => read error
76                                 return -1;
77                         } else {
78                                 assert(src->bytes_available == 0);
79                                 return 0;
80                         }
81                 }
82
83                 src->bytes_available += bytes_read;
84         }
85
86         // Now unstuff as much as we can. First of all, if there's a 0xFF at the
87         // end of the buffer, we don't include it this time; the unstuff function
88         // will only give us an error since it can't decide if it's a marker or
89         // a stuffed 0xFF.
90         unsigned bytes_to_unstuff = src->bytes_available;
91         bool end_marker = false;
92         assert(bytes_to_unstuff > 0);
93         if (src->bytes[bytes_to_unstuff - 1] == 0xff) {
94                 --bytes_to_unstuff;
95                 end_marker = true;
96         }
97
98         // Fast path: No markers in the data, we don't have more data than we
99         // need. We can basically just return it.
100         if (bytes_to_unstuff <= len) {
101                 int unstuffed_bytes = (*unstuff_choice)(buf, src->bytes, bytes_to_unstuff);
102                 assert(unstuffed_bytes != 0);
103                 if (unstuffed_bytes > 0) {
104                         // Fast path: No markers in the data. We can basically just
105                         // return it.
106                         if (end_marker) {
107                                 src->bytes_available = 1;
108                                 src->bytes[0] = 0xff;
109                         } else {
110                                 src->bytes_available = 0;
111                         }
112                         return unstuffed_bytes;
113                 }
114         }
115
116         // Slow path: There was a marker in the data. Unstuff manually until
117         // we hit the marker, then return that.
118         unsigned bytes_read;
119         unsigned bytes_written = 0;
120         for (bytes_read = 0; bytes_read < bytes_to_unstuff && bytes_written < len; ++bytes_read) {
121                 if (src->bytes[bytes_read] != MARKER_CHAR) {
122                         buf[bytes_written++] = src->bytes[bytes_read];
123                         continue;
124                 }
125
126                 assert(bytes_read < src->bytes_available);
127                 if (src->bytes[bytes_read + 1] == STUFF_MARKER) {
128                         src->bytes[bytes_written++] = MARKER_CHAR;
129
130                         // Skip the stuff byte.
131                         ++bytes_read;
132                 } else {
133                         // OK, this is our marker.
134                         break;
135                 }
136         }
137
138         memmove(src->bytes, src->bytes + bytes_read, src->bytes_available - bytes_read);
139         src->bytes_available -= bytes_read;
140         return bytes_written;
141 }