mirror of
https://github.com/GrammaticalFramework/gf-core.git
synced 2026-04-18 17:29:32 -06:00
382 lines
7.0 KiB
C
382 lines
7.0 KiB
C
#include <gu/in.h>
|
|
#include <gu/bits.h>
|
|
#include <math.h>
|
|
|
|
GU_DEFINE_TYPE(GuEOF, abstract, _);
|
|
|
|
|
|
static bool
|
|
gu_in_is_buffering(GuIn* in)
|
|
{
|
|
return (in->buf_end != NULL);
|
|
}
|
|
|
|
static void
|
|
gu_in_end_buffering(GuIn* in, GuExn* err)
|
|
{
|
|
if (!gu_in_is_buffering(in)) {
|
|
return;
|
|
}
|
|
if (in->stream->end_buffer) {
|
|
size_t len = ((ptrdiff_t) in->buf_size) + in->buf_curr;
|
|
in->stream->end_buffer(in->stream, len, err);
|
|
}
|
|
in->buf_curr = 0;
|
|
in->buf_size = 0;
|
|
in->buf_end = NULL;
|
|
}
|
|
|
|
static bool
|
|
gu_in_begin_buffering(GuIn* in, GuExn* err)
|
|
{
|
|
if (gu_in_is_buffering(in)) {
|
|
if (in->buf_curr < 0) {
|
|
return true;
|
|
} else {
|
|
gu_in_end_buffering(in, err);
|
|
if (!gu_ok(err)) return false;
|
|
}
|
|
}
|
|
if (!in->stream->begin_buffer) {
|
|
return false;
|
|
}
|
|
size_t sz = 0;
|
|
const uint8_t* new_buf =
|
|
in->stream->begin_buffer(in->stream, &sz, err);
|
|
if (new_buf) {
|
|
in->buf_end = &new_buf[sz];
|
|
in->buf_curr = -(ptrdiff_t) sz;
|
|
in->buf_size = sz;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static size_t
|
|
gu_in_input(GuIn* in, uint8_t* dst, size_t sz, GuExn* err)
|
|
{
|
|
if (sz == 0) {
|
|
return 0;
|
|
}
|
|
gu_in_end_buffering(in, err);
|
|
if (!gu_ok(err)) {
|
|
return 0;
|
|
}
|
|
GuInStream* stream = in->stream;
|
|
if (stream->input) {
|
|
return stream->input(stream, dst, sz, err);
|
|
}
|
|
gu_raise(err, GuEOF);
|
|
return 0;
|
|
}
|
|
|
|
size_t
|
|
gu_in_some(GuIn* in, uint8_t* dst, size_t sz, GuExn* err)
|
|
{
|
|
gu_require(sz <= PTRDIFF_MAX);
|
|
if (!gu_in_begin_buffering(in, err)) {
|
|
if (!gu_ok(err)) return 0;
|
|
return gu_in_input(in, dst, sz, err);
|
|
}
|
|
size_t real_sz = GU_MIN(sz, (size_t)(-in->buf_curr));
|
|
memcpy(dst, &in->buf_end[in->buf_curr], real_sz);
|
|
in->buf_curr += real_sz;
|
|
return real_sz;
|
|
}
|
|
|
|
void
|
|
gu_in_bytes_(GuIn* in, uint8_t* dst, size_t sz, GuExn* err)
|
|
{
|
|
size_t avail_sz = GU_MIN(sz, (size_t)(-in->buf_curr));
|
|
memcpy(dst, &in->buf_end[in->buf_curr], avail_sz);
|
|
in->buf_curr += avail_sz;
|
|
if (avail_sz < sz) {
|
|
gu_in_input(in, &dst[avail_sz], sz - avail_sz, err);
|
|
}
|
|
}
|
|
|
|
const uint8_t*
|
|
gu_in_begin_span(GuIn* in, size_t *sz_out, GuExn* err)
|
|
{
|
|
if (!gu_in_begin_buffering(in, err)) {
|
|
return NULL;
|
|
}
|
|
*sz_out = (size_t) -in->buf_curr;
|
|
return &in->buf_end[in->buf_curr];
|
|
}
|
|
|
|
void
|
|
gu_in_end_span(GuIn* in, size_t consumed)
|
|
{
|
|
gu_require(consumed <= (size_t) -in->buf_curr);
|
|
in->buf_curr += (ptrdiff_t) consumed;
|
|
}
|
|
|
|
uint8_t
|
|
gu_in_u8_(GuIn* in, GuExn* err)
|
|
{
|
|
if (gu_in_begin_buffering(in, err) && in->buf_curr < 0) {
|
|
return in->buf_end[in->buf_curr++];
|
|
}
|
|
uint8_t u = 0;
|
|
size_t r = gu_in_input(in, &u, 1, err);
|
|
if (r < 1) {
|
|
gu_raise(err, GuEOF);
|
|
return 0;
|
|
}
|
|
return u;
|
|
}
|
|
|
|
static uint64_t
|
|
gu_in_be(GuIn* in, GuExn* err, int n)
|
|
{
|
|
uint8_t buf[8];
|
|
gu_in_bytes(in, buf, n, err);
|
|
uint64_t u = 0;
|
|
for (int i = 0; i < n; i++) {
|
|
u = u << 8 | buf[i];
|
|
}
|
|
return u;
|
|
}
|
|
|
|
static uint64_t
|
|
gu_in_le(GuIn* in, GuExn* err, int n)
|
|
{
|
|
uint8_t buf[8];
|
|
gu_in_bytes(in, buf, n, err);
|
|
uint64_t u = 0;
|
|
for (int i = 0; i < n; i++) {
|
|
u = u << 8 | buf[i];
|
|
}
|
|
return u;
|
|
}
|
|
|
|
int8_t
|
|
gu_in_s8(GuIn* in, GuExn* err)
|
|
{
|
|
return gu_decode_2c8(gu_in_u8(in, err), err);
|
|
}
|
|
|
|
|
|
uint16_t
|
|
gu_in_u16le(GuIn* in, GuExn* err)
|
|
{
|
|
return gu_in_le(in, err, 2);
|
|
}
|
|
|
|
int16_t
|
|
gu_in_s16le(GuIn* in, GuExn* err)
|
|
{
|
|
return gu_decode_2c16(gu_in_u16le(in, err), err);
|
|
}
|
|
|
|
uint16_t
|
|
gu_in_u16be(GuIn* in, GuExn* err)
|
|
{
|
|
return gu_in_be(in, err, 2);
|
|
}
|
|
|
|
int16_t
|
|
gu_in_s16be(GuIn* in, GuExn* err)
|
|
{
|
|
return gu_decode_2c16(gu_in_u16be(in, err), err);
|
|
}
|
|
|
|
|
|
uint32_t
|
|
gu_in_u32le(GuIn* in, GuExn* err)
|
|
{
|
|
return gu_in_le(in, err, 4);
|
|
}
|
|
|
|
int32_t
|
|
gu_in_s32le(GuIn* in, GuExn* err)
|
|
{
|
|
return gu_decode_2c32(gu_in_u32le(in, err), err);
|
|
}
|
|
|
|
uint32_t
|
|
gu_in_u32be(GuIn* in, GuExn* err)
|
|
{
|
|
return gu_in_be(in, err, 4);
|
|
}
|
|
|
|
int32_t
|
|
gu_in_s32be(GuIn* in, GuExn* err)
|
|
{
|
|
return gu_decode_2c32(gu_in_u32be(in, err), err);
|
|
}
|
|
|
|
|
|
uint64_t
|
|
gu_in_u64le(GuIn* in, GuExn* err)
|
|
{
|
|
return gu_in_le(in, err, 8);
|
|
}
|
|
|
|
int64_t
|
|
gu_in_s64le(GuIn* in, GuExn* err)
|
|
{
|
|
return gu_decode_2c64(gu_in_u64le(in, err), err);
|
|
}
|
|
|
|
uint64_t
|
|
gu_in_u64be(GuIn* in, GuExn* err)
|
|
{
|
|
return gu_in_be(in, err, 8);
|
|
}
|
|
|
|
int64_t
|
|
gu_in_s64be(GuIn* in, GuExn* err)
|
|
{
|
|
return gu_decode_2c64(gu_in_u64be(in, err), err);
|
|
}
|
|
|
|
double
|
|
gu_in_f64le(GuIn* in, GuExn* err)
|
|
{
|
|
return gu_decode_double(gu_in_u64le(in, err));
|
|
}
|
|
|
|
double
|
|
gu_in_f64be(GuIn* in, GuExn* err)
|
|
{
|
|
return gu_decode_double(gu_in_u64le(in, err));
|
|
}
|
|
|
|
|
|
static void
|
|
gu_in_fini(GuFinalizer* fin)
|
|
{
|
|
GuIn* in = gu_container(fin, GuIn, fini);
|
|
GuPool* pool = gu_local_pool();
|
|
GuExn* err = gu_exn(NULL, type, pool);
|
|
gu_in_end_buffering(in, err);
|
|
gu_pool_free(pool);
|
|
}
|
|
|
|
GuIn*
|
|
gu_new_in(GuInStream* stream, GuPool* pool)
|
|
{
|
|
gu_require(stream != NULL);
|
|
|
|
GuIn* in = gu_new(GuIn, pool);
|
|
in->buf_end = NULL;
|
|
in->buf_curr = 0;
|
|
in->buf_size = 0;
|
|
in->stream = stream;
|
|
in->fini.fn = gu_in_fini;
|
|
return in;
|
|
}
|
|
|
|
|
|
typedef struct GuProxyInStream GuProxyInStream;
|
|
|
|
enum {
|
|
GU_BUFFERED_IN_BUF_SIZE = 4096
|
|
};
|
|
|
|
typedef struct GuBufferedInStream GuBufferedInStream;
|
|
|
|
struct GuBufferedInStream {
|
|
GuInStream stream;
|
|
size_t alloc;
|
|
size_t have;
|
|
size_t curr;
|
|
GuIn* in;
|
|
uint8_t buf[];
|
|
};
|
|
|
|
static const uint8_t*
|
|
gu_buffered_in_begin_buffer(GuInStream* self, size_t* sz_out, GuExn* err)
|
|
{
|
|
GuBufferedInStream* bis =
|
|
gu_container(self, GuBufferedInStream, stream);
|
|
if (bis->curr == bis->have) {
|
|
bis->curr = 0;
|
|
bis->have = gu_in_some(bis->in, bis->buf, bis->alloc, err);
|
|
if (!gu_ok(err)) return NULL;
|
|
}
|
|
*sz_out = bis->have - bis->curr;
|
|
return &bis->buf[bis->curr];
|
|
}
|
|
|
|
static void
|
|
gu_buffered_in_end_buffer(GuInStream* self, size_t consumed, GuExn* err)
|
|
{
|
|
GuBufferedInStream* bis =
|
|
gu_container(self, GuBufferedInStream, stream);
|
|
gu_require(consumed < bis->have - bis->curr);
|
|
bis->curr += consumed;
|
|
}
|
|
|
|
static size_t
|
|
gu_buffered_in_input(GuInStream* self, uint8_t* dst, size_t sz, GuExn* err)
|
|
{
|
|
GuBufferedInStream* bis =
|
|
gu_container(self, GuBufferedInStream, stream);
|
|
return gu_in_some(bis->in, dst, sz, err);
|
|
}
|
|
|
|
GuIn*
|
|
gu_buffered_in(GuIn* in, size_t buf_sz, GuPool* pool)
|
|
{
|
|
GuBufferedInStream* bis = gu_new_flex(pool, GuBufferedInStream,
|
|
buf, buf_sz);
|
|
bis->stream = (GuInStream) {
|
|
.begin_buffer = gu_buffered_in_begin_buffer,
|
|
.end_buffer = gu_buffered_in_end_buffer,
|
|
.input = gu_buffered_in_input
|
|
};
|
|
bis->alloc = buf_sz;
|
|
bis->have = bis->curr = 0;
|
|
bis->in = in;
|
|
return gu_new_in(&bis->stream, pool);
|
|
}
|
|
|
|
typedef struct GuDataIn GuDataIn;
|
|
|
|
struct GuDataIn {
|
|
GuInStream stream;
|
|
const uint8_t* data;
|
|
size_t sz;
|
|
};
|
|
|
|
static const uint8_t*
|
|
gu_data_in_begin_buffer(GuInStream* self, size_t* sz_out, GuExn* err)
|
|
{
|
|
(void) err;
|
|
GuDataIn* di = gu_container(self, GuDataIn, stream);
|
|
const uint8_t* buf = di->data;
|
|
if (buf) {
|
|
*sz_out = di->sz;
|
|
di->data = NULL;
|
|
di->sz = 0;
|
|
}
|
|
return buf;
|
|
}
|
|
|
|
GuIn*
|
|
gu_data_in(const uint8_t* data, size_t sz, GuPool* pool)
|
|
{
|
|
GuDataIn* di = gu_new(GuDataIn, pool);
|
|
di->stream.begin_buffer = gu_data_in_begin_buffer;
|
|
di->stream.end_buffer = NULL;
|
|
di->stream.input = NULL;
|
|
di->data = data;
|
|
di->sz = sz;
|
|
return gu_new_in(&di->stream, pool);
|
|
}
|
|
|
|
extern inline uint8_t
|
|
gu_in_u8(GuIn* restrict in, GuExn* err);
|
|
|
|
extern inline void
|
|
gu_in_bytes(GuIn* in, uint8_t* buf, size_t sz, GuExn* err);
|
|
|
|
extern inline int
|
|
gu_in_peek_u8(GuIn* restrict in);
|
|
|
|
extern inline void
|
|
gu_in_consume(GuIn* restrict in, size_t sz);
|