blob: 87bc756afbcb338b0cbbabdfe5386e2ea6c1b2b7 [file] [log] [blame]
#ifndef PUFFS_GIF_H
#define PUFFS_GIF_H
// Code generated by puffs-gen-c. DO NOT EDIT.
#ifndef PUFFS_BASE_HEADER_H
#define PUFFS_BASE_HEADER_H
// Use of this source code is governed by a BSD-style license that can be found
// in the LICENSE file.
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
// Puffs requires a word size of at least 32 bits because it assumes that
// converting a u32 to usize will never overflow. For example, the size of a
// decoded image is often represented, explicitly or implicitly in an image
// file, as a u32, and it is convenient to compare that to a buffer size.
//
// Similarly, the word size is at most 64 bits because it assumes that
// converting a usize to u64 will never overflow.
#if __WORDSIZE < 32
#error "Puffs requires a word size of at least 32 bits"
#elif __WORDSIZE > 64
#error "Puffs requires a word size of at most 64 bits"
#endif
// PUFFS_VERSION is the major.minor version number as a uint32. The major
// number is the high 16 bits. The minor number is the low 16 bits.
//
// The intention is to bump the version number at least on every API / ABI
// backwards incompatible change.
//
// For now, the API and ABI are simply unstable and can change at any time.
//
// TODO: don't hard code this in base-header.h.
#define PUFFS_VERSION (0x00001)
// puffs_base_buf1 is a 1-dimensional buffer (a pointer and length) plus
// additional indexes into that buffer.
//
// A value with all fields NULL or zero is a valid, empty buffer.
typedef struct {
uint8_t* ptr; // Pointer.
size_t len; // Length.
size_t wi; // Write index. Invariant: wi <= len.
size_t ri; // Read index. Invariant: ri <= wi.
bool closed; // No further writes are expected.
} puffs_base_buf1;
// puffs_base_limit1 provides a limited view of a 1-dimensional byte stream:
// its first N bytes. That N can be greater than a buffer's current read or
// write capacity. N decreases naturally over time as bytes are read from or
// written to the stream.
//
// A value with all fields NULL or zero is a valid, unlimited view.
typedef struct puffs_base_limit1 {
uint64_t* ptr_to_len; // Pointer to N.
struct puffs_base_limit1* next; // Linked list of limits.
} puffs_base_limit1;
typedef struct {
puffs_base_buf1* buf;
puffs_base_limit1 limit;
} puffs_base_reader1;
typedef struct {
puffs_base_buf1* buf;
puffs_base_limit1 limit;
} puffs_base_writer1;
#endif // PUFFS_BASE_HEADER_H
#ifdef __cplusplus
extern "C" {
#endif
// ---------------- Status Codes
// Status codes are non-positive integers.
//
// The least significant bit indicates a non-recoverable status code: an error.
typedef enum {
puffs_gif_status_ok = 0,
puffs_gif_error_bad_version = -2 + 1,
puffs_gif_error_bad_receiver = -4 + 1,
puffs_gif_error_bad_argument = -6 + 1,
puffs_gif_error_constructor_not_called = -8 + 1,
puffs_gif_error_unexpected_eof = -10 + 1,
puffs_gif_status_short_read = -12,
puffs_gif_status_short_write = -14,
puffs_gif_error_closed_for_writes = -16 + 1,
puffs_gif_error_bad_gif_block = -256 + 1,
puffs_gif_error_bad_gif_extension_label = -258 + 1,
puffs_gif_error_bad_gif_header = -260 + 1,
puffs_gif_error_bad_lzw_literal_width = -262 + 1,
puffs_gif_error_todo_unsupported_local_color_table = -264 + 1,
puffs_gif_error_lzw_code_is_out_of_range = -266 + 1,
puffs_gif_error_lzw_prefix_chain_is_cyclical = -268 + 1,
} puffs_gif_status;
bool puffs_gif_status_is_error(puffs_gif_status s);
const char* puffs_gif_status_string(puffs_gif_status s);
// ---------------- Structs
typedef struct {
// Do not access the private_impl's fields directly. There is no API/ABI
// compatibility or safety guarantee if you do so. Instead, use the
// puffs_gif_lzw_decoder_etc functions.
//
// In C++, these fields would be "private", but C does not support that.
//
// It is a struct, not a struct*, so that it can be stack allocated.
struct {
puffs_gif_status status;
uint32_t magic;
uint32_t f_literal_width;
uint8_t f_stack[4096];
uint8_t f_suffixes[4096];
uint16_t f_prefixes[4096];
struct {
uint32_t coro_state;
uint32_t v_clear_code;
uint32_t v_end_code;
uint32_t v_save_code;
uint32_t v_prev_code;
uint32_t v_width;
uint32_t v_bits;
uint32_t v_n_bits;
uint32_t v_code;
uint32_t v_s;
uint32_t v_c;
} c_decode[1];
} private_impl;
} puffs_gif_lzw_decoder;
typedef struct {
// Do not access the private_impl's fields directly. There is no API/ABI
// compatibility or safety guarantee if you do so. Instead, use the
// puffs_gif_decoder_etc functions.
//
// In C++, these fields would be "private", but C does not support that.
//
// It is a struct, not a struct*, so that it can be stack allocated.
struct {
puffs_gif_status status;
uint32_t magic;
uint32_t f_width;
uint32_t f_height;
uint8_t f_background_color_index;
uint8_t f_gct[768];
puffs_gif_lzw_decoder f_lzw;
struct {
uint32_t coro_state;
uint8_t v_c;
} c_decode[1];
struct {
uint32_t coro_state;
uint8_t v_c[6];
uint32_t v_i;
} c_decode_header[1];
struct {
uint32_t coro_state;
uint8_t v_c[7];
uint32_t v_i;
uint32_t v_gct_size;
} c_decode_lsd[1];
struct {
uint32_t coro_state;
uint8_t v_label;
uint8_t v_block_size;
} c_decode_extension[1];
struct {
uint32_t coro_state;
uint8_t v_c[9];
uint32_t v_i;
bool v_interlace;
uint8_t v_lw;
uint8_t v_block_size;
uint64_t l_lzw_src;
puffs_base_reader1 v_lzw_src;
} c_decode_id[1];
} private_impl;
} puffs_gif_decoder;
// ---------------- Public Constructor and Destructor Prototypes
// puffs_gif_lzw_decoder_constructor is a constructor function.
//
// It should be called before any other puffs_gif_lzw_decoder_* function.
//
// Pass PUFFS_VERSION and 0 for puffs_version and for_internal_use_only.
void puffs_gif_lzw_decoder_constructor(puffs_gif_lzw_decoder* self,
uint32_t puffs_version,
uint32_t for_internal_use_only);
void puffs_gif_lzw_decoder_destructor(puffs_gif_lzw_decoder* self);
// puffs_gif_decoder_constructor is a constructor function.
//
// It should be called before any other puffs_gif_decoder_* function.
//
// Pass PUFFS_VERSION and 0 for puffs_version and for_internal_use_only.
void puffs_gif_decoder_constructor(puffs_gif_decoder* self,
uint32_t puffs_version,
uint32_t for_internal_use_only);
void puffs_gif_decoder_destructor(puffs_gif_decoder* self);
// ---------------- Public Function Prototypes
puffs_gif_status puffs_gif_decoder_decode(puffs_gif_decoder* self,
puffs_base_writer1 a_dst,
puffs_base_reader1 a_src);
void puffs_gif_lzw_decoder_set_literal_width(puffs_gif_lzw_decoder* self,
uint32_t a_lw);
puffs_gif_status puffs_gif_lzw_decoder_decode(puffs_gif_lzw_decoder* self,
puffs_base_writer1 a_dst,
puffs_base_reader1 a_src);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // PUFFS_GIF_H
// C HEADER ENDS HERE.
#ifndef PUFFS_BASE_IMPL_H
#define PUFFS_BASE_IMPL_H
// Use of this source code is governed by a BSD-style license that can be found
// in the LICENSE file.
// Use switch cases for coroutine state, similar to the technique in
// https://www.chiark.greenend.org.uk/~sgtatham/coroutines.html
//
// We use a trivial macro instead of an explicit assignment and case statement
// so that clang-format doesn't get confused by the unusual "case"s.
#define PUFFS_COROUTINE_STATE(n) \
coro_state = n; \
case n:
#define PUFFS_LOW_BITS(x, n) ((x) & ((1 << (n)) - 1))
#if defined(__clang__) || defined(__GNUC__)
#define PUFFS_LIKELY(expr) (__builtin_expect((expr), 1))
#define PUFFS_UNLIKELY(expr) (__builtin_expect((expr), 0))
#else
#define PUFFS_LIKELY(expr) (expr)
#define PUFFS_UNLIKELY(expr) (expr)
#endif
#endif // PUFFS_BASE_IMPL_H
// ---------------- Status Codes Implementations
bool puffs_gif_status_is_error(puffs_gif_status s) {
return s & 1;
}
const char* puffs_gif_status_strings[16] = {
"gif: ok",
"gif: bad version",
"gif: bad receiver",
"gif: bad argument",
"gif: constructor not called",
"gif: unexpected EOF",
"gif: short read",
"gif: short write",
"gif: closed for writes",
"gif: bad GIF block",
"gif: bad GIF extension label",
"gif: bad GIF header",
"gif: bad LZW literal width",
"gif: TODO: unsupported Local Color Table",
"gif: LZW code is out of range",
"gif: LZW prefix chain is cyclical",
};
const char* puffs_gif_status_string(puffs_gif_status s) {
s = -(s >> 1);
if (0 <= s) {
if (s < 9) {
return puffs_gif_status_strings[s];
}
s -= 119;
if ((9 <= s) && (s < 16)) {
return puffs_gif_status_strings[s];
}
}
return "gif: unknown status";
}
// ---------------- Private Constructor and Destructor Prototypes
// ---------------- Private Function Prototypes
puffs_gif_status puffs_gif_decoder_decode_header(puffs_gif_decoder* self,
puffs_base_reader1 a_src);
puffs_gif_status puffs_gif_decoder_decode_lsd(puffs_gif_decoder* self,
puffs_base_reader1 a_src);
puffs_gif_status puffs_gif_decoder_decode_extension(puffs_gif_decoder* self,
puffs_base_reader1 a_src);
puffs_gif_status puffs_gif_decoder_decode_id(puffs_gif_decoder* self,
puffs_base_writer1 a_dst,
puffs_base_reader1 a_src);
// ---------------- Constructor and Destructor Implementations
// PUFFS_MAGIC is a magic number to check that constructors are called. It's
// not foolproof, given C doesn't automatically zero memory before use, but it
// should catch 99.99% of cases.
//
// Its (non-zero) value is arbitrary, based on md5sum("puffs").
#define PUFFS_MAGIC (0xCB3699CCU)
// PUFFS_ALREADY_ZEROED is passed from a container struct's constructor to a
// containee struct's constructor when the container has already zeroed the
// containee's memory.
//
// Its (non-zero) value is arbitrary, based on md5sum("zeroed").
#define PUFFS_ALREADY_ZEROED (0x68602EF1U)
void puffs_gif_lzw_decoder_constructor(puffs_gif_lzw_decoder* self,
uint32_t puffs_version,
uint32_t for_internal_use_only) {
if (!self) {
return;
}
if (puffs_version != PUFFS_VERSION) {
self->private_impl.status = puffs_gif_error_bad_version;
return;
}
if (for_internal_use_only != PUFFS_ALREADY_ZEROED) {
memset(self, 0, sizeof(*self));
}
self->private_impl.magic = PUFFS_MAGIC;
self->private_impl.f_literal_width = 8;
}
void puffs_gif_lzw_decoder_destructor(puffs_gif_lzw_decoder* self) {
if (!self) {
return;
}
}
void puffs_gif_decoder_constructor(puffs_gif_decoder* self,
uint32_t puffs_version,
uint32_t for_internal_use_only) {
if (!self) {
return;
}
if (puffs_version != PUFFS_VERSION) {
self->private_impl.status = puffs_gif_error_bad_version;
return;
}
if (for_internal_use_only != PUFFS_ALREADY_ZEROED) {
memset(self, 0, sizeof(*self));
}
self->private_impl.magic = PUFFS_MAGIC;
puffs_gif_lzw_decoder_constructor(&self->private_impl.f_lzw, PUFFS_VERSION,
PUFFS_ALREADY_ZEROED);
}
void puffs_gif_decoder_destructor(puffs_gif_decoder* self) {
if (!self) {
return;
}
puffs_gif_lzw_decoder_destructor(&self->private_impl.f_lzw);
}
// ---------------- Function Implementations
puffs_gif_status puffs_gif_decoder_decode(puffs_gif_decoder* self,
puffs_base_writer1 a_dst,
puffs_base_reader1 a_src) {
if (!self) {
return puffs_gif_error_bad_receiver;
}
puffs_gif_status status = self->private_impl.status;
if (status & 1) {
return status;
}
if (self->private_impl.magic != PUFFS_MAGIC) {
status = puffs_gif_error_constructor_not_called;
goto exit;
}
uint8_t v_c;
uint8_t* b_rptr_src = NULL;
uint8_t* b_rend_src = NULL;
if (a_src.buf) {
b_rptr_src = a_src.buf->ptr + a_src.buf->ri;
size_t len = a_src.buf->wi - a_src.buf->ri;
puffs_base_limit1* lim;
for (lim = &a_src.limit; lim; lim = lim->next) {
if (lim->ptr_to_len && (len > *lim->ptr_to_len)) {
len = *lim->ptr_to_len;
}
}
b_rend_src = b_rptr_src + len;
}
uint32_t coro_state = self->private_impl.c_decode[0].coro_state;
if (coro_state) {
v_c = self->private_impl.c_decode[0].v_c;
}
switch (coro_state) {
PUFFS_COROUTINE_STATE(0);
PUFFS_COROUTINE_STATE(1);
if (a_src.buf) {
size_t n = b_rptr_src - (a_src.buf->ptr + a_src.buf->ri);
a_src.buf->ri += n;
puffs_base_limit1* lim;
for (lim = &a_src.limit; lim; lim = lim->next) {
if (lim->ptr_to_len) {
*lim->ptr_to_len -= n;
}
}
}
status = puffs_gif_decoder_decode_header(self, a_src);
if (a_src.buf) {
b_rptr_src = a_src.buf->ptr + a_src.buf->ri;
size_t len = a_src.buf->wi - a_src.buf->ri;
puffs_base_limit1* lim;
for (lim = &a_src.limit; lim; lim = lim->next) {
if (lim->ptr_to_len && (len > *lim->ptr_to_len)) {
len = *lim->ptr_to_len;
}
}
b_rend_src = b_rptr_src + len;
}
if (status) {
goto suspend;
}
PUFFS_COROUTINE_STATE(2);
if (a_src.buf) {
size_t n = b_rptr_src - (a_src.buf->ptr + a_src.buf->ri);
a_src.buf->ri += n;
puffs_base_limit1* lim;
for (lim = &a_src.limit; lim; lim = lim->next) {
if (lim->ptr_to_len) {
*lim->ptr_to_len -= n;
}
}
}
status = puffs_gif_decoder_decode_lsd(self, a_src);
if (a_src.buf) {
b_rptr_src = a_src.buf->ptr + a_src.buf->ri;
size_t len = a_src.buf->wi - a_src.buf->ri;
puffs_base_limit1* lim;
for (lim = &a_src.limit; lim; lim = lim->next) {
if (lim->ptr_to_len && (len > *lim->ptr_to_len)) {
len = *lim->ptr_to_len;
}
}
b_rend_src = b_rptr_src + len;
}
if (status) {
goto suspend;
}
while (true) {
PUFFS_COROUTINE_STATE(3);
if (PUFFS_UNLIKELY(b_rptr_src == b_rend_src)) {
goto short_read_src;
}
uint8_t t_0 = *b_rptr_src++;
v_c = t_0;
if (v_c == 33) {
PUFFS_COROUTINE_STATE(4);
if (a_src.buf) {
size_t n = b_rptr_src - (a_src.buf->ptr + a_src.buf->ri);
a_src.buf->ri += n;
puffs_base_limit1* lim;
for (lim = &a_src.limit; lim; lim = lim->next) {
if (lim->ptr_to_len) {
*lim->ptr_to_len -= n;
}
}
}
status = puffs_gif_decoder_decode_extension(self, a_src);
if (a_src.buf) {
b_rptr_src = a_src.buf->ptr + a_src.buf->ri;
size_t len = a_src.buf->wi - a_src.buf->ri;
puffs_base_limit1* lim;
for (lim = &a_src.limit; lim; lim = lim->next) {
if (lim->ptr_to_len && (len > *lim->ptr_to_len)) {
len = *lim->ptr_to_len;
}
}
b_rend_src = b_rptr_src + len;
}
if (status) {
goto suspend;
}
} else if (v_c == 44) {
PUFFS_COROUTINE_STATE(5);
if (a_src.buf) {
size_t n = b_rptr_src - (a_src.buf->ptr + a_src.buf->ri);
a_src.buf->ri += n;
puffs_base_limit1* lim;
for (lim = &a_src.limit; lim; lim = lim->next) {
if (lim->ptr_to_len) {
*lim->ptr_to_len -= n;
}
}
}
status = puffs_gif_decoder_decode_id(self, a_dst, a_src);
if (a_src.buf) {
b_rptr_src = a_src.buf->ptr + a_src.buf->ri;
size_t len = a_src.buf->wi - a_src.buf->ri;
puffs_base_limit1* lim;
for (lim = &a_src.limit; lim; lim = lim->next) {
if (lim->ptr_to_len && (len > *lim->ptr_to_len)) {
len = *lim->ptr_to_len;
}
}
b_rend_src = b_rptr_src + len;
}
if (status) {
goto suspend;
}
} else if (v_c == 59) {
status = puffs_gif_status_ok;
goto suspend;
} else {
status = puffs_gif_error_bad_gif_block;
goto suspend;
}
}
coro_state = 0;
}
goto suspend;
suspend:
self->private_impl.c_decode[0].coro_state = coro_state;
self->private_impl.c_decode[0].v_c = v_c;
if (a_src.buf) {
size_t n = b_rptr_src - (a_src.buf->ptr + a_src.buf->ri);
a_src.buf->ri += n;
puffs_base_limit1* lim;
for (lim = &a_src.limit; lim; lim = lim->next) {
if (lim->ptr_to_len) {
*lim->ptr_to_len -= n;
}
}
}
goto exit;
exit:
self->private_impl.status = status;
return status;
short_read_src:
status = ((a_src.buf->closed) && (a_src.buf->ri == a_src.buf->wi))
? puffs_gif_error_unexpected_eof
: puffs_gif_status_short_read;
goto suspend;
}
puffs_gif_status puffs_gif_decoder_decode_header(puffs_gif_decoder* self,
puffs_base_reader1 a_src) {
puffs_gif_status status = self->private_impl.status;
uint8_t v_c[6];
uint32_t v_i;
uint8_t* b_rptr_src = NULL;
uint8_t* b_rend_src = NULL;
if (a_src.buf) {
b_rptr_src = a_src.buf->ptr + a_src.buf->ri;
size_t len = a_src.buf->wi - a_src.buf->ri;
puffs_base_limit1* lim;
for (lim = &a_src.limit; lim; lim = lim->next) {
if (lim->ptr_to_len && (len > *lim->ptr_to_len)) {
len = *lim->ptr_to_len;
}
}
b_rend_src = b_rptr_src + len;
}
uint32_t coro_state = self->private_impl.c_decode_header[0].coro_state;
if (coro_state) {
memcpy(v_c, self->private_impl.c_decode_header[0].v_c, 6);
v_i = self->private_impl.c_decode_header[0].v_i;
}
switch (coro_state) {
PUFFS_COROUTINE_STATE(0);
{
size_t i;
for (i = 0; i < 6; i++) {
v_c[i] = 0;
}
};
v_i = 0;
while (v_i < 6) {
PUFFS_COROUTINE_STATE(1);
if (PUFFS_UNLIKELY(b_rptr_src == b_rend_src)) {
goto short_read_src;
}
uint8_t t_0 = *b_rptr_src++;
v_c[v_i] = t_0;
v_i += 1;
}
if ((v_c[0] != 71) || (v_c[1] != 73) || (v_c[2] != 70) || (v_c[3] != 56) ||
((v_c[4] != 55) && (v_c[4] != 57)) || (v_c[5] != 97)) {
return puffs_gif_error_bad_gif_header;
}
coro_state = 0;
}
goto suspend;
suspend:
self->private_impl.c_decode_header[0].coro_state = coro_state;
memcpy(self->private_impl.c_decode_header[0].v_c, v_c, 6);
self->private_impl.c_decode_header[0].v_i = v_i;
if (a_src.buf) {
size_t n = b_rptr_src - (a_src.buf->ptr + a_src.buf->ri);
a_src.buf->ri += n;
puffs_base_limit1* lim;
for (lim = &a_src.limit; lim; lim = lim->next) {
if (lim->ptr_to_len) {
*lim->ptr_to_len -= n;
}
}
}
goto exit;
exit:
return status;
short_read_src:
status = ((a_src.buf->closed) && (a_src.buf->ri == a_src.buf->wi))
? puffs_gif_error_unexpected_eof
: puffs_gif_status_short_read;
return status;
}
puffs_gif_status puffs_gif_decoder_decode_lsd(puffs_gif_decoder* self,
puffs_base_reader1 a_src) {
puffs_gif_status status = self->private_impl.status;
uint8_t v_c[7];
uint32_t v_i;
uint32_t v_gct_size;
uint8_t* b_rptr_src = NULL;
uint8_t* b_rend_src = NULL;
if (a_src.buf) {
b_rptr_src = a_src.buf->ptr + a_src.buf->ri;
size_t len = a_src.buf->wi - a_src.buf->ri;
puffs_base_limit1* lim;
for (lim = &a_src.limit; lim; lim = lim->next) {
if (lim->ptr_to_len && (len > *lim->ptr_to_len)) {
len = *lim->ptr_to_len;
}
}
b_rend_src = b_rptr_src + len;
}
uint32_t coro_state = self->private_impl.c_decode_lsd[0].coro_state;
if (coro_state) {
memcpy(v_c, self->private_impl.c_decode_lsd[0].v_c, 7);
v_i = self->private_impl.c_decode_lsd[0].v_i;
v_gct_size = self->private_impl.c_decode_lsd[0].v_gct_size;
}
switch (coro_state) {
PUFFS_COROUTINE_STATE(0);
{
size_t i;
for (i = 0; i < 7; i++) {
v_c[i] = 0;
}
};
v_i = 0;
while (v_i < 7) {
PUFFS_COROUTINE_STATE(1);
if (PUFFS_UNLIKELY(b_rptr_src == b_rend_src)) {
goto short_read_src;
}
uint8_t t_0 = *b_rptr_src++;
v_c[v_i] = t_0;
v_i += 1;
}
self->private_impl.f_width =
(((uint32_t)(v_c[0])) | (((uint32_t)(v_c[1])) << 8));
self->private_impl.f_height =
(((uint32_t)(v_c[2])) | (((uint32_t)(v_c[3])) << 8));
self->private_impl.f_background_color_index = v_c[5];
if ((v_c[4] & 128) != 0) {
v_gct_size = (((uint32_t)(1)) << (1 + (v_c[4] & 7)));
v_i = 0;
while (v_i < v_gct_size) {
PUFFS_COROUTINE_STATE(2);
if (PUFFS_UNLIKELY(b_rptr_src == b_rend_src)) {
goto short_read_src;
}
uint8_t t_1 = *b_rptr_src++;
self->private_impl.f_gct[(3 * v_i) + 0] = t_1;
PUFFS_COROUTINE_STATE(3);
if (PUFFS_UNLIKELY(b_rptr_src == b_rend_src)) {
goto short_read_src;
}
uint8_t t_2 = *b_rptr_src++;
self->private_impl.f_gct[(3 * v_i) + 1] = t_2;
PUFFS_COROUTINE_STATE(4);
if (PUFFS_UNLIKELY(b_rptr_src == b_rend_src)) {
goto short_read_src;
}
uint8_t t_3 = *b_rptr_src++;
self->private_impl.f_gct[(3 * v_i) + 2] = t_3;
v_i += 1;
}
}
coro_state = 0;
}
goto suspend;
suspend:
self->private_impl.c_decode_lsd[0].coro_state = coro_state;
memcpy(self->private_impl.c_decode_lsd[0].v_c, v_c, 7);
self->private_impl.c_decode_lsd[0].v_i = v_i;
self->private_impl.c_decode_lsd[0].v_gct_size = v_gct_size;
if (a_src.buf) {
size_t n = b_rptr_src - (a_src.buf->ptr + a_src.buf->ri);
a_src.buf->ri += n;
puffs_base_limit1* lim;
for (lim = &a_src.limit; lim; lim = lim->next) {
if (lim->ptr_to_len) {
*lim->ptr_to_len -= n;
}
}
}
goto exit;
exit:
return status;
short_read_src:
status = ((a_src.buf->closed) && (a_src.buf->ri == a_src.buf->wi))
? puffs_gif_error_unexpected_eof
: puffs_gif_status_short_read;
return status;
}
puffs_gif_status puffs_gif_decoder_decode_extension(puffs_gif_decoder* self,
puffs_base_reader1 a_src) {
puffs_gif_status status = self->private_impl.status;
uint8_t v_label;
uint8_t v_block_size;
uint8_t* b_rptr_src = NULL;
uint8_t* b_rend_src = NULL;
if (a_src.buf) {
b_rptr_src = a_src.buf->ptr + a_src.buf->ri;
size_t len = a_src.buf->wi - a_src.buf->ri;
puffs_base_limit1* lim;
for (lim = &a_src.limit; lim; lim = lim->next) {
if (lim->ptr_to_len && (len > *lim->ptr_to_len)) {
len = *lim->ptr_to_len;
}
}
b_rend_src = b_rptr_src + len;
}
uint32_t coro_state = self->private_impl.c_decode_extension[0].coro_state;
if (coro_state) {
v_label = self->private_impl.c_decode_extension[0].v_label;
v_block_size = self->private_impl.c_decode_extension[0].v_block_size;
}
switch (coro_state) {
PUFFS_COROUTINE_STATE(0);
PUFFS_COROUTINE_STATE(1);
if (PUFFS_UNLIKELY(b_rptr_src == b_rend_src)) {
goto short_read_src;
}
uint8_t t_0 = *b_rptr_src++;
v_label = t_0;
if (v_label == 1) {
} else if (v_label == 249) {
} else if (v_label == 254) {
} else if (v_label == 255) {
} else {
return puffs_gif_error_bad_gif_extension_label;
}
while (true) {
PUFFS_COROUTINE_STATE(2);
if (PUFFS_UNLIKELY(b_rptr_src == b_rend_src)) {
goto short_read_src;
}
uint8_t t_1 = *b_rptr_src++;
v_block_size = t_1;
if (v_block_size == 0) {
goto label_0_break;
}
PUFFS_COROUTINE_STATE(3);
size_t t_2 = ((uint32_t)(v_block_size));
if (t_2 > b_rend_src - b_rptr_src) {
t_2 -= b_rend_src - b_rptr_src;
a_src.buf->ri = a_src.buf->wi;
status = a_src.buf->closed ? puffs_gif_error_unexpected_eof
: puffs_gif_status_short_read;
return status;
}
b_rptr_src += t_2;
}
label_0_break:;
coro_state = 0;
}
goto suspend;
suspend:
self->private_impl.c_decode_extension[0].coro_state = coro_state;
self->private_impl.c_decode_extension[0].v_label = v_label;
self->private_impl.c_decode_extension[0].v_block_size = v_block_size;
if (a_src.buf) {
size_t n = b_rptr_src - (a_src.buf->ptr + a_src.buf->ri);
a_src.buf->ri += n;
puffs_base_limit1* lim;
for (lim = &a_src.limit; lim; lim = lim->next) {
if (lim->ptr_to_len) {
*lim->ptr_to_len -= n;
}
}
}
goto exit;
exit:
return status;
short_read_src:
status = ((a_src.buf->closed) && (a_src.buf->ri == a_src.buf->wi))
? puffs_gif_error_unexpected_eof
: puffs_gif_status_short_read;
return status;
}
puffs_gif_status puffs_gif_decoder_decode_id(puffs_gif_decoder* self,
puffs_base_writer1 a_dst,
puffs_base_reader1 a_src) {
puffs_gif_status status = self->private_impl.status;
uint8_t v_c[9];
uint32_t v_i;
bool v_interlace;
uint8_t v_lw;
uint8_t v_block_size;
uint64_t l_lzw_src;
puffs_base_reader1 v_lzw_src;
uint8_t* b_rptr_src = NULL;
uint8_t* b_rend_src = NULL;
if (a_src.buf) {
b_rptr_src = a_src.buf->ptr + a_src.buf->ri;
size_t len = a_src.buf->wi - a_src.buf->ri;
puffs_base_limit1* lim;
for (lim = &a_src.limit; lim; lim = lim->next) {
if (lim->ptr_to_len && (len > *lim->ptr_to_len)) {
len = *lim->ptr_to_len;
}
}
b_rend_src = b_rptr_src + len;
}
uint32_t coro_state = self->private_impl.c_decode_id[0].coro_state;
if (coro_state) {
memcpy(v_c, self->private_impl.c_decode_id[0].v_c, 9);
v_i = self->private_impl.c_decode_id[0].v_i;
v_interlace = self->private_impl.c_decode_id[0].v_interlace;
v_lw = self->private_impl.c_decode_id[0].v_lw;
v_block_size = self->private_impl.c_decode_id[0].v_block_size;
l_lzw_src = self->private_impl.c_decode_id[0].l_lzw_src;
v_lzw_src = self->private_impl.c_decode_id[0].v_lzw_src;
}
switch (coro_state) {
PUFFS_COROUTINE_STATE(0);
{
size_t i;
for (i = 0; i < 9; i++) {
v_c[i] = 0;
}
};
v_i = 0;
while (v_i < 9) {
PUFFS_COROUTINE_STATE(1);
if (PUFFS_UNLIKELY(b_rptr_src == b_rend_src)) {
goto short_read_src;
}
uint8_t t_0 = *b_rptr_src++;
v_c[v_i] = t_0;
v_i += 1;
}
v_interlace = ((v_c[8] & 64) != 0);
if (v_interlace) {
}
if ((v_c[8] & 128) != 0) {
return puffs_gif_error_todo_unsupported_local_color_table;
}
PUFFS_COROUTINE_STATE(2);
if (PUFFS_UNLIKELY(b_rptr_src == b_rend_src)) {
goto short_read_src;
}
uint8_t t_1 = *b_rptr_src++;
v_lw = t_1;
if ((v_lw < 2) || (8 < v_lw)) {
return puffs_gif_error_bad_lzw_literal_width;
}
puffs_gif_lzw_decoder_set_literal_width(&self->private_impl.f_lzw,
((uint32_t)(v_lw)));
while (true) {
PUFFS_COROUTINE_STATE(3);
if (PUFFS_UNLIKELY(b_rptr_src == b_rend_src)) {
goto short_read_src;
}
uint8_t t_2 = *b_rptr_src++;
v_block_size = t_2;
if (v_block_size == 0) {
goto label_0_break;
}
l_lzw_src = ((uint64_t)(v_block_size));
v_lzw_src = (puffs_base_reader1){
.buf = a_src.buf,
.limit = (puffs_base_limit1){
.ptr_to_len = &l_lzw_src, .next = &a_src.limit,
}};
PUFFS_COROUTINE_STATE(4);
if (a_src.buf) {
size_t n = b_rptr_src - (a_src.buf->ptr + a_src.buf->ri);
a_src.buf->ri += n;
puffs_base_limit1* lim;
for (lim = &a_src.limit; lim; lim = lim->next) {
if (lim->ptr_to_len) {
*lim->ptr_to_len -= n;
}
}
}
status = puffs_gif_lzw_decoder_decode(&self->private_impl.f_lzw, a_dst,
v_lzw_src);
if (a_src.buf) {
b_rptr_src = a_src.buf->ptr + a_src.buf->ri;
size_t len = a_src.buf->wi - a_src.buf->ri;
puffs_base_limit1* lim;
for (lim = &a_src.limit; lim; lim = lim->next) {
if (lim->ptr_to_len && (len > *lim->ptr_to_len)) {
len = *lim->ptr_to_len;
}
}
b_rend_src = b_rptr_src + len;
}
if (status & 1) {
return status;
}
}
label_0_break:;
coro_state = 0;
}
goto suspend;
suspend:
self->private_impl.c_decode_id[0].coro_state = coro_state;
memcpy(self->private_impl.c_decode_id[0].v_c, v_c, 9);
self->private_impl.c_decode_id[0].v_i = v_i;
self->private_impl.c_decode_id[0].v_interlace = v_interlace;
self->private_impl.c_decode_id[0].v_lw = v_lw;
self->private_impl.c_decode_id[0].v_block_size = v_block_size;
self->private_impl.c_decode_id[0].l_lzw_src = l_lzw_src;
self->private_impl.c_decode_id[0].v_lzw_src = v_lzw_src;
if (a_src.buf) {
size_t n = b_rptr_src - (a_src.buf->ptr + a_src.buf->ri);
a_src.buf->ri += n;
puffs_base_limit1* lim;
for (lim = &a_src.limit; lim; lim = lim->next) {
if (lim->ptr_to_len) {
*lim->ptr_to_len -= n;
}
}
}
goto exit;
exit:
return status;
short_read_src:
status = ((a_src.buf->closed) && (a_src.buf->ri == a_src.buf->wi))
? puffs_gif_error_unexpected_eof
: puffs_gif_status_short_read;
return status;
}
void puffs_gif_lzw_decoder_set_literal_width(puffs_gif_lzw_decoder* self,
uint32_t a_lw) {
if (!self) {
return;
}
if (self->private_impl.status & 1) {
return;
}
if (self->private_impl.magic != PUFFS_MAGIC) {
self->private_impl.status = puffs_gif_error_constructor_not_called;
return;
}
if (a_lw < 2 || a_lw > 8) {
self->private_impl.status = puffs_gif_error_bad_argument;
return;
}
self->private_impl.f_literal_width = a_lw;
}
puffs_gif_status puffs_gif_lzw_decoder_decode(puffs_gif_lzw_decoder* self,
puffs_base_writer1 a_dst,
puffs_base_reader1 a_src) {
if (!self) {
return puffs_gif_error_bad_receiver;
}
puffs_gif_status status = self->private_impl.status;
if (status & 1) {
return status;
}
if (self->private_impl.magic != PUFFS_MAGIC) {
status = puffs_gif_error_constructor_not_called;
goto exit;
}
uint32_t v_clear_code;
uint32_t v_end_code;
uint32_t v_save_code;
uint32_t v_prev_code;
uint32_t v_width;
uint32_t v_bits;
uint32_t v_n_bits;
uint32_t v_code;
uint32_t v_s;
uint32_t v_c;
uint8_t* b_wptr_dst = NULL;
uint8_t* b_wend_dst = NULL;
if (a_dst.buf) {
b_wptr_dst = a_dst.buf->ptr + a_dst.buf->wi;
size_t len = a_dst.buf->len - a_dst.buf->wi;
puffs_base_limit1* lim;
for (lim = &a_dst.limit; lim; lim = lim->next) {
if (lim->ptr_to_len && (len > *lim->ptr_to_len)) {
len = *lim->ptr_to_len;
}
}
b_wend_dst = b_wptr_dst + len;
}
uint8_t* b_rptr_src = NULL;
uint8_t* b_rend_src = NULL;
if (a_src.buf) {
b_rptr_src = a_src.buf->ptr + a_src.buf->ri;
size_t len = a_src.buf->wi - a_src.buf->ri;
puffs_base_limit1* lim;
for (lim = &a_src.limit; lim; lim = lim->next) {
if (lim->ptr_to_len && (len > *lim->ptr_to_len)) {
len = *lim->ptr_to_len;
}
}
b_rend_src = b_rptr_src + len;
}
uint32_t coro_state = self->private_impl.c_decode[0].coro_state;
if (coro_state) {
v_clear_code = self->private_impl.c_decode[0].v_clear_code;
v_end_code = self->private_impl.c_decode[0].v_end_code;
v_save_code = self->private_impl.c_decode[0].v_save_code;
v_prev_code = self->private_impl.c_decode[0].v_prev_code;
v_width = self->private_impl.c_decode[0].v_width;
v_bits = self->private_impl.c_decode[0].v_bits;
v_n_bits = self->private_impl.c_decode[0].v_n_bits;
v_code = self->private_impl.c_decode[0].v_code;
v_s = self->private_impl.c_decode[0].v_s;
v_c = self->private_impl.c_decode[0].v_c;
}
switch (coro_state) {
PUFFS_COROUTINE_STATE(0);
v_clear_code = (((uint32_t)(1)) << self->private_impl.f_literal_width);
v_end_code = (v_clear_code + 1);
v_save_code = v_end_code;
v_prev_code = 0;
v_width = (self->private_impl.f_literal_width + 1);
v_bits = 0;
v_n_bits = 0;
label_0_continue:;
while (true) {
while (v_n_bits < v_width) {
PUFFS_COROUTINE_STATE(1);
if (PUFFS_UNLIKELY(b_rptr_src == b_rend_src)) {
goto short_read_src;
}
uint8_t t_0 = *b_rptr_src++;
v_bits |= (((uint32_t)(t_0)) << v_n_bits);
v_n_bits += 8;
}
v_code = PUFFS_LOW_BITS(v_bits, v_width);
v_bits >>= v_width;
v_n_bits -= v_width;
if (v_code < v_clear_code) {
PUFFS_COROUTINE_STATE(2);
if (b_wptr_dst == b_wend_dst) {
status = puffs_gif_status_short_write;
goto suspend;
}
*b_wptr_dst++ = ((uint8_t)(v_code));
if (v_save_code <= 4095) {
self->private_impl.f_suffixes[v_save_code] = ((uint8_t)(v_code));
self->private_impl.f_prefixes[v_save_code] =
((uint16_t)(v_prev_code));
}
} else if (v_code == v_clear_code) {
v_save_code = v_end_code;
v_prev_code = 0;
v_width = (self->private_impl.f_literal_width + 1);
goto label_0_continue;
} else if (v_code == v_end_code) {
status = puffs_gif_status_ok;
goto suspend;
} else if (v_code <= v_save_code) {
v_s = 4095;
v_c = v_code;
if (v_code == v_save_code) {
v_s -= 1;
v_c = v_prev_code;
}
while (v_c >= v_clear_code) {
self->private_impl.f_stack[v_s] = self->private_impl.f_suffixes[v_c];
if (v_s == 0) {
status = puffs_gif_error_lzw_prefix_chain_is_cyclical;
goto suspend;
}
v_s -= 1;
v_c = ((uint32_t)(self->private_impl.f_prefixes[v_c]));
}
self->private_impl.f_stack[v_s] = ((uint8_t)(v_c));
if (v_code == v_save_code) {
self->private_impl.f_stack[4095] = ((uint8_t)(v_c));
}
PUFFS_COROUTINE_STATE(3);
if (a_dst.buf->closed) {
status = puffs_gif_error_closed_for_writes;
goto suspend;
}
if ((b_wend_dst - b_wptr_dst) <
(sizeof(self->private_impl.f_stack) - v_s)) {
status = puffs_gif_status_short_write;
goto suspend;
}
memmove(b_wptr_dst, self->private_impl.f_stack + v_s,
sizeof(self->private_impl.f_stack) - v_s);
b_wptr_dst += sizeof(self->private_impl.f_stack) - v_s;
if (v_save_code <= 4095) {
self->private_impl.f_suffixes[v_save_code] = ((uint8_t)(v_c));
self->private_impl.f_prefixes[v_save_code] =
((uint16_t)(v_prev_code));
}
} else {
status = puffs_gif_error_lzw_code_is_out_of_range;
goto suspend;
}
if (v_save_code <= 4095) {
v_save_code += 1;
if ((v_save_code == (((uint32_t)(1)) << v_width)) && (v_width < 12)) {
v_width += 1;
}
}
v_prev_code = v_code;
}
coro_state = 0;
}
goto suspend;
suspend:
self->private_impl.c_decode[0].coro_state = coro_state;
self->private_impl.c_decode[0].v_clear_code = v_clear_code;
self->private_impl.c_decode[0].v_end_code = v_end_code;
self->private_impl.c_decode[0].v_save_code = v_save_code;
self->private_impl.c_decode[0].v_prev_code = v_prev_code;
self->private_impl.c_decode[0].v_width = v_width;
self->private_impl.c_decode[0].v_bits = v_bits;
self->private_impl.c_decode[0].v_n_bits = v_n_bits;
self->private_impl.c_decode[0].v_code = v_code;
self->private_impl.c_decode[0].v_s = v_s;
self->private_impl.c_decode[0].v_c = v_c;
if (a_dst.buf) {
size_t n = b_wptr_dst - (a_dst.buf->ptr + a_dst.buf->wi);
a_dst.buf->wi += n;
puffs_base_limit1* lim;
for (lim = &a_dst.limit; lim; lim = lim->next) {
if (lim->ptr_to_len) {
*lim->ptr_to_len -= n;
}
}
}
if (a_src.buf) {
size_t n = b_rptr_src - (a_src.buf->ptr + a_src.buf->ri);
a_src.buf->ri += n;
puffs_base_limit1* lim;
for (lim = &a_src.limit; lim; lim = lim->next) {
if (lim->ptr_to_len) {
*lim->ptr_to_len -= n;
}
}
}
goto exit;
exit:
self->private_impl.status = status;
return status;
short_read_src:
status = ((a_src.buf->closed) && (a_src.buf->ri == a_src.buf->wi))
? puffs_gif_error_unexpected_eof
: puffs_gif_status_short_read;
goto suspend;
}