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