X-Git-Url: https://git.sesse.net/?p=fjl;a=blobdiff_plain;f=bytesource.c;fp=bytesource.c;h=25cea227e399cce151228489642004e6ad49fe7d;hp=0000000000000000000000000000000000000000;hb=30860bda5fd2474a3b45b05f6b89dcf7230a75a8;hpb=618548d1f2e076a3da21368e708cf887dcbd20d2 diff --git a/bytesource.c b/bytesource.c new file mode 100644 index 0000000..25cea22 --- /dev/null +++ b/bytesource.c @@ -0,0 +1,122 @@ +#include +#include +#include +#include +#include + +#include "choice.h" +#include "bytesource.h" + +#define MARKER_CHAR 0xff +#define STUFF_MARKER 0x00 + +void init_byte_source(struct byte_source* source, raw_input_func_t* input_func, void* userdata) +{ + // TODO: should this size be a different constant? + memset(source, 0, sizeof(*source)); + source->bytes = (uint8_t*)malloc(BYTESOURCE_CHUNK_SIZE); + source->input_func = input_func; + source->userdata = userdata; +} + +uint8_t byte_source_read_marker(struct byte_source* source) +{ + assert(source->bytes_available >= 2); + assert(source->bytes[0] == MARKER_CHAR); + assert(source->bytes[1] != STUFF_MARKER); + + uint8_t ret = source->bytes[1]; + + memmove(source->bytes, source->bytes + 2, source->bytes_available - 2); + source->bytes_available -= 2; + + return ret; +} + +ssize_t byte_source_input_func(void* source, uint8_t* buf, size_t len) +{ + struct byte_source* src = (struct byte_source*)source; + + // If there's no data in the buffer (or only a partial marker), we have + // to read in more from our upstream src. + while (src->bytes_available == 0 || + (src->bytes_available == 1 && src->bytes[0] == MARKER_CHAR)) { + const unsigned space_left = BYTESOURCE_CHUNK_SIZE - src->bytes_available; + const size_t bytes_to_read = (len > space_left ? space_left : len); + assert(bytes_to_read <= BYTESOURCE_CHUNK_SIZE); + const ssize_t bytes_read = + (*src->input_func)(src->userdata, + src->bytes + src->bytes_available, + bytes_to_read); + assert(bytes_read >= -1); + assert(bytes_read <= bytes_to_read); + + if (bytes_read == -1) { + return -1; + } else if (bytes_read == 0) { + if (src->bytes_available == 1) { + // EOF in the middle of a marker => read error + return -1; + } else { + assert(src->bytes_available == 0); + return 0; + } + } + + src->bytes_available += bytes_read; + } + + // Now unstuff as much as we can. First of all, if there's a 0xFF at the + // end of the buffer, we don't include it this time; the unstuff function + // will only give us an error since it can't decide if it's a marker or + // a stuffed 0xFF. + unsigned bytes_to_unstuff = src->bytes_available; + bool end_marker = false; + assert(bytes_to_unstuff > 0); + if (src->bytes[bytes_to_unstuff - 1] == 0xff) { + --bytes_to_unstuff; + end_marker = true; + } + + int unstuffed_bytes = (*unstuff_choice)(buf, src->bytes, bytes_to_unstuff); + assert(unstuffed_bytes != 0); + if (unstuffed_bytes > 0) { + // Fast path: No markers in the data. We can basically just + // return it. + if (end_marker) { + src->bytes_available = 1; + src->bytes[0] = 0xff; + } else { + src->bytes_available = 0; + src->bytes[0] = 0; + } + return unstuffed_bytes; + } + + // Slow path: There was a marker in the data. Unstuff manually until + // we hit the marker, then return that. + assert(unstuffed_bytes == -1); + unsigned bytes_read; + unsigned bytes_written = 0; + for (bytes_read = 0; bytes_read < src->bytes_available; ++bytes_read) { + buf[bytes_written++] = src->bytes[bytes_read]; + if (src->bytes[bytes_read] != MARKER_CHAR) { + continue; + } + + assert(bytes_read < src->bytes_available); + if (src->bytes[bytes_read + 1] == STUFF_MARKER) { + // Skip the stuff byte. + ++bytes_read; + continue; + } else { + // OK, this is our marker. + break; + } + } + + memmove(src->bytes, src->bytes + bytes_read, src->bytes_available - bytes_read); + src->bytes_available -= bytes_read; + assert(bytes_written >= 1); + return bytes_written - 1; +}