From: Not Zed Date: Tue, 23 Jul 2024 09:28:51 +0000 (+0930) Subject: Added ez-string. X-Git-Url: https://code.zedzone.au/cvs?a=commitdiff_plain;ds=inline;p=libeze Added ez-string. Removed ez-blob-io and changed everything to use ez-string. Made test-http standalone. Expanded the link node fields in string and pair nodes. Rename ez_string_node to ez_name to closer match ez_pair etc. --- diff --git a/Makefile b/Makefile index 66a86a8..334eac6 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ CPPFLAGS=-I. -#CFLAGS=-O2 -g -fPIC -Wall -mtune=native -CFLAGS=-O0 -g -fPIC -Wall +#CFLAGS=-O2 -fPIC -Wall -mtune=native +CFLAGS=-Og -g -fPIC -Wall test_LDLIBS=-lpthread -lrt ARFLAGS=rvsUc VERSION=2.1.99 @@ -12,7 +12,6 @@ SRCS= \ ez-blob.c \ ez-blob.c \ ez-blob-basic.c \ - ez-blob-io.c \ ez-blob-print.c \ ez-blob-dump.c \ ez-blob-tagz.c \ @@ -21,6 +20,7 @@ SRCS= \ ez-http.c \ ez-port.c \ ez-set.c \ + ez-string.c \ ez-tree.c HEADERS = \ @@ -28,7 +28,6 @@ HEADERS = \ ez-bitset.h \ ez-blob.h \ ez-blob-basic.h \ - ez-blobio.h \ ez-blob-tagz.h \ ez-blob-xdrn.h \ ez-elf.h \ @@ -37,9 +36,18 @@ HEADERS = \ ez-node.h \ ez-port.h \ ez-set.h \ + ez-string.h \ ez-tree.h -test_SRCS=test-bitset.c test-blob.c test-port.c test-set.c test-tree.c test-list.c test-http.c +test_SRCS= \ + test-bitset.c \ + test-blob.c \ + test-http.c \ + test-list.c \ + test-port.c \ + test-set.c \ + test-string.c \ + test-tree.c TESTS=$(test_SRCS:.c=) @@ -54,7 +62,7 @@ tests: $(TESTS) check: tests for test in $(TESTS) ; do \ echo $$test ; \ - ./$$test || exit 1; \ + valgrind --error-exitcode=1 ./$$test || exit 1; \ done test-%: test-%.o @@ -62,11 +70,14 @@ test-%: test-%.o test-array: libeze.a(ez-array.o) test-bitset: libeze.a(ez-bitset.o) -test-blob: libeze.a(ez-blob.o) libeze.a(ez-blob-print.o) libeze.a(ez-blob-io.o) \ - libeze.a(ez-blob-xdrn.o) libeze.a(ez-blob-tagz.o) libeze.a(ez-blob-dump.o) +test-blob: libeze.a(ez-blob.o) libeze.a(ez-blob-print.o) \ + libeze.a(ez-blob-xdrn.o) libeze.a(ez-blob-tagz.o) libeze.a(ez-blob-dump.o) \ + libeze.a(ez-blob-basic.o) libeze.a(ez-string.o) test-port: libeze.a(ez-port.o) test-set: libeze.a(ez-set.o) test-tree: libeze.a(ez-tree.o) +test-string: libeze.a(ez-string.o) +test-http: libeze.a(ez-string.o) libeze.a(ez-http.o) dist: libeze-$(VERSION).tar.gz diff --git a/ez-blob-io.c b/ez-blob-io.c deleted file mode 100644 index ce26327..0000000 --- a/ez-blob-io.c +++ /dev/null @@ -1,184 +0,0 @@ -/* ez-blob-io.c: Common i/o utilities for serialisers - - Copyright (C) 2019 Michael Zucchi - - This program is free software: you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public License - as published by the Free Software Foundation, either version 3 of - the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this program. If not, see - . -*/ - -#include -#include -#include -#include -#include - -#include "ez-blob.h" -#include "ez-blob-io.h" - -//#define ABORT() abort() -#define ABORT() - -void *blobio_reserve(struct ez_blobio * __restrict io, size_t len) { - size_t size = io->size; - size_t to = io->index + len; - void *v; - - if (io->error) - return NULL; - - switch (io->mode) { - case BLOBIO_WRITE_ALLOC: - if (to > size) { - void *alloc; - - do { - size = size ? size * 2 : 256; - } while (to > size); - - io->size = size; - alloc = realloc(io->data, size); - - if (!alloc) { - io->error = ENOMEM; - printf("realloc failed\n"); - ABORT(); - return NULL; - } - - io->data = alloc; - } - - v = io->index + io->data; - io->index = to; - return v; - - case BLOBIO_WRITE_FIXED: - if (to > size) { - io->error = EOVERFLOW; - ABORT(); - return NULL; - } - v = io->index + io->data; - io->index = to; - return v; - - case BLOBIO_WRITE_SIZE: - io->index = to; - return NULL; - default: - io->error = EINVAL; - return NULL; - } -} - -void *blobio_take(struct ez_blobio * __restrict io, size_t len) { - size_t to = io->index + len; - - if (to <= io->size) { - void *v = io->data + io->index; - io->index = to; - return v; - } else { - ABORT(); - io->index = io->size; - io->error = EOVERFLOW; - return NULL; - } -} - -void blobio_write_align(struct ez_blobio *io, unsigned int step) { - int skip = (step - io->index) & (step-1); - uint8_t *v; - - if (skip && (v = blobio_reserve(io, skip))) - memset(v, 0, skip); -} - -void blobio_read_align(struct ez_blobio *io, unsigned int step) { - int skip = (step - io->index) & (step-1); - - blobio_take(io, skip); -} - -#ifndef BLOBIO_INLINE - void blobio_write(struct ez_blobio *io, const void *data, size_t len) { - void *v = blobio_reserve(io, len); - if (v) memcpy(v, data, len); -} - -void blobio_writeb(struct ez_blobio *io, uint8_t val) { - uint8_t *v = blobio_reserve(io, 1); - if (v) *v = val; -} - -void blobio_write8(struct ez_blobio *io, uint8_t val) { - uint8_t *v = blobio_reserve(io, 1); - if (v) *v = val; -} - -void blobio_write16(struct ez_blobio *io, uint16_t val) { - uint16_t *v = blobio_reserve(io, 2); - if (v) *v = val; -} - -void blobio_write32(struct ez_blobio *io, uint32_t val) { - uint32_t *v = blobio_reserve(io, 4); - if (v) *v = val; -} - -void blobio_write64(struct ez_blobio *io, uint64_t val) { - uint64_t *v = blobio_reserve(io, 8); - if (v) *v = val; -} - -void blobio_writef(struct ez_blobio *io, float val) { - float *v = blobio_reserve(io, 4); - if (v) *v = val; -} - -void blobio_writed(struct ez_blobio *io, double val) { - double *v = blobio_reserve(io, 8); - if (v) *v = val; -} - -uint8_t blobio_readi8(struct ez_blobio *io) { - uint8_t *v = blobio_take(io, 1); - return v ? *v : 0; -} - -uint16_t blobio_readi16(struct ez_blobio *io) { - uint16_t *v = blobio_take(io, 2); - return v ? *v : 0; -} - -uint32_t blobio_readi32(struct ez_blobio *io) { - uint32_t *v = blobio_take(io, 4); - return v ? *v : 0; -} - -uint64_t blobio_readi64(struct ez_blobio *io) { - uint64_t *v = blobio_take(io, 8); - return v ? *v : 0; -} - -float blobio_readf(struct ez_blobio *io) { - float *v = blobio_take(io, 4); - return v ? *v : 0.0f; -} - -double blobio_readd(struct ez_blobio *io) { - double *v = blobio_take(io, 8); - return v ? *v : 0.0; -} -#endif diff --git a/ez-blob-io.h b/ez-blob-io.h deleted file mode 100644 index 1c01620..0000000 --- a/ez-blob-io.h +++ /dev/null @@ -1,196 +0,0 @@ -/* ez-blob-io.h: Common i/o utilities for serialisers - - Copyright (C) 2019 Michael Zucchi - - This program is free software: you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public License - as published by the Free Software Foundation, either version 3 of - the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this program. If not, see - . -*/ - -// Grow output as necessary -#define BLOBIO_WRITE_ALLOC 0 -// Writing to pre-existing buffer -#define BLOBIO_WRITE_FIXED 1 -// Calculating size only -#define BLOBIO_WRITE_SIZE 2 -// Read mode -#define BLOBIO_READ 3 - -struct ez_blobio { - size_t index; - uint8_t *data; - - // for read: upper limit - // for write: allocation size - size_t size; - // total allocation size - size_t alloc; - - // on read, non-zero means invalid - int error; - // io mode. - int mode; -}; - -/** - * Reserve write-space. - * - * For BLOBIO_WRITE_ALLOC mode this will fail if any allocation fails. - * - * For BLOBIO_WRITE_FIXED this will fail if there is a buffer - * overflow. - * - * For BLOBIO_WRITE_SIZE this will never fail. - * - * @param io - * @param len length required - * @return base of memory, or NULL on error. - */ -void *blobio_reserve(struct ez_blobio *io, size_t len); - -/** - * Take read-space. - * - * For BLOBIO_READ this will fail if there is a buffer underflow. - * - * @param io - * @param len Length required. - * @return base of memory or NULL on error. - */ -void *blobio_take(struct ez_blobio *io, size_t len); - -/** - * Align write output. - * - * Data is padded with 0 bytes. - * - * @param io - * @param step must be power of 2. - */ -void blobio_write_align(struct ez_blobio *io, unsigned int step); - -/** - * Align read input. - * - * @param io - * @param step must be power of 2. - */ -void blobio_read_align(struct ez_blobio *io, unsigned int step); - -/** - * Dump binary data in human-readable hex+ascii format. - * @todo rename ez_blob_dump(prefix, ez_blob)? - */ -void blobio_dumphex(const char *data, size_t size, const char *prefix); - -/* - The read and write functions read or write the type and silently - perform no-operation on error. The error can be checked via - io.error being non-zero. - - @todo I don't think there's any point in making these inline if it just - calls an external function anyway. - */ - -#define BLOBIO_INLINE -#ifdef BLOBIO_INLINE - -static __inline__ void blobio_write(struct ez_blobio *io, const void *data, size_t len) { - void *v = blobio_reserve(io, len); - if (v) memcpy(v, data, len); -} - -static __inline__ void blobio_writeb(struct ez_blobio *io, uint8_t val) { - uint8_t *v = blobio_reserve(io, 1); - if (v) *v = val; -} - -static __inline__ void blobio_write8(struct ez_blobio *io, uint8_t val) { - uint8_t *v = blobio_reserve(io, 1); - if (v) *v = val; -} - -static __inline__ void blobio_write16(struct ez_blobio *io, uint16_t val) { - uint16_t *v = blobio_reserve(io, 2); - if (v) *v = val; -} - -static __inline__ void blobio_write32(struct ez_blobio *io, uint32_t val) { - uint32_t *v = blobio_reserve(io, 4); - if (v) *v = val; -} - -static __inline__ void blobio_write64(struct ez_blobio *io, uint64_t val) { - uint64_t *v = blobio_reserve(io, 8); - if (v) *v = val; -} - -static __inline__ void blobio_writef(struct ez_blobio *io, float val) { - float *v = blobio_reserve(io, 4); - if (v) *v = val; -} - -static __inline__ void blobio_writed(struct ez_blobio *io, double val) { - double *v = blobio_reserve(io, 8); - if (v) *v = val; -} - -static __inline__ uint8_t blobio_readi8(struct ez_blobio *io) { - uint8_t *v = blobio_take(io, 1); - return v ? *v : 0; -} - -static __inline__ uint16_t blobio_readi16(struct ez_blobio *io) { - uint16_t *v = blobio_take(io, 2); - return v ? *v : 0; -} - -static __inline__ uint32_t blobio_readi32(struct ez_blobio *io) { - uint32_t *v = blobio_take(io, 4); - return v ? *v : 0; -} - -static __inline__ uint64_t blobio_readi64(struct ez_blobio *io) { - uint64_t *v = blobio_take(io, 8); - return v ? *v : 0; -} - -static __inline__ float blobio_readf(struct ez_blobio *io) { - float *v = blobio_take(io, 4); - return v ? *v : 0.0f; -} - -static __inline__ double blobio_readd(struct ez_blobio *io) { - double *v = blobio_take(io, 8); - return v ? *v : 0.0; -} - -#else - -void blobio_write(struct ez_blobio *io, const void *data, size_t len); -void blobio_writeb(struct ez_blobio *io, uint8_t val); -void blobio_write8(struct ez_blobio *io, uint8_t val); -void blobio_write16(struct ez_blobio *io, uint16_t val); -void blobio_write32(struct ez_blobio *io, uint32_t val); -void blobio_write64(struct ez_blobio *io, uint64_t val); -void blobio_writef(struct ez_blobio *io, float val); -void blobio_writed(struct ez_blobio *io, double val); - -uint8_t blobio_readi8(struct ez_blobio *io); -uint16_t blobio_readi16(struct ez_blobio *io); -uint32_t blobio_readi32(struct ez_blobio *io); -uint64_t blobio_readi64(struct ez_blobio *io); -float blobio_readf(struct ez_blobio *io); -double blobio_readd(struct ez_blobio *io); - -#endif diff --git a/ez-blob-print.c b/ez-blob-print.c index 4757453..83a2f15 100644 --- a/ez-blob-print.c +++ b/ez-blob-print.c @@ -27,8 +27,6 @@ #include "ez-blob.h" #include "ez-list.h" -#include "ez-blob-io.h" - void ez_blob_print(const ez_blob_desc *d, const void *a, int depth) { char x[depth+1]; int len = d->bd_length; diff --git a/ez-blob-tagz.c b/ez-blob-tagz.c index 80b4bc9..b0acdb8 100644 --- a/ez-blob-tagz.c +++ b/ez-blob-tagz.c @@ -115,7 +115,7 @@ final note: #include "ez-blob.h" #include "ez-blob-tagz.h" -#include "ez-blob-io.h" +#include "ez-string.h" //#define D(x) do { x; fflush(stdout); } while (0) #define D(x) @@ -145,19 +145,19 @@ final note: #define EZT_END 0xff // well, end-struct -static void blobio_writei(struct ez_blobio *io, int sc, uint64_t val) { +static void tagz_putux(struct ez_string *io, int sc, uint64_t val) { switch (sc) { case 0: - blobio_write8(io, val); + ez_string_putu8(io, val); break; case 1: - blobio_write16(io, val); + ez_string_putu16(io, val); break; case 2: - blobio_write32(io, val); + ez_string_putu32(io, val); break; case 3: - blobio_write64(io, val); + ez_string_putu64(io, val); break; } } @@ -184,25 +184,25 @@ static int tag_code(uint32_t size) { } // tag single item, tt=11 and cc=tag -static void blobio_writet(struct ez_blobio *io, uint8_t type, uint32_t tag) { +static void tagz_putut(struct ez_string *io, uint8_t type, uint32_t tag) { int tc = tag_code(tag); - blobio_writeb(io, type | EZT_NOCOUNT | (tc << EZT_COUNTSHIFT)); - blobio_writei(io, tc, tag); + ez_string_putu8(io, type | EZT_NOCOUNT | (tc << EZT_COUNTSHIFT)); + tagz_putux(io, tc, tag); } // tag counted item, tt=tag, cc=count -static void blobio_writetc(struct ez_blobio *io, uint8_t type, uint32_t tag, uint64_t count) { +static void tagz_pututc(struct ez_string *io, uint8_t type, uint32_t tag, uint64_t count) { int tc = tag_code(tag); int cc = size_code(count); - blobio_writeb(io, type | (tc << EZT_TAGSHIFT) | (cc << EZT_COUNTSHIFT)); - blobio_writei(io, tc, tag); - blobio_writei(io, cc, count); + ez_string_putu8(io, type | (tc << EZT_TAGSHIFT) | (cc << EZT_COUNTSHIFT)); + tagz_putux(io, tc, tag); + tagz_putux(io, cc, count); } -static void tagz_encode_struct(struct ez_blobio *io, int id, const ez_blob_desc *desc, const void *p) { - blobio_writet(io, EZT_STRUCT, id); +static void tagz_encode_struct(struct ez_string *io, int id, const ez_blob_desc *desc, const void *p) { + tagz_putut(io, EZT_STRUCT, id); for (int i=0,dlen=desc->bd_length;ibd_id); - blobio_writei(io, sc, val); + tagz_putut(io, EZT_INT8 | (sc << EZT_DATASHIFT), d->bd_id); + tagz_putux(io, sc, val); } break; case EZ_BLOB_SINGLE | EZ_BLOB_FLOAT32: - blobio_writet(io, EZT_FLOAT32, d->bd_id); - blobio_writef(io, *(const float *)v); + tagz_putut(io, EZT_FLOAT32, d->bd_id); + ez_string_putf32(io, *(const float *)v); break; case EZ_BLOB_SINGLE | EZ_BLOB_FLOAT64: - blobio_writet(io, EZT_FLOAT64, d->bd_id); - blobio_writed(io, *(const double *)v); + tagz_putut(io, EZT_FLOAT64, d->bd_id); + ez_string_putf64(io, *(const double *)v); break; case EZ_BLOB_VECTOR | EZ_BLOB_INT8: @@ -252,8 +252,8 @@ static void tagz_encode_struct(struct ez_blobio *io, int id, const ez_blob_desc int tc = (d->bd_type & EZ_BLOB_TYPE) << 4; ptr = ((ez_blob *)v)->eb_data; - blobio_writetc(io, tc, d->bd_id, count); - blobio_write(io, ptr, count << sc); + tagz_pututc(io, tc, d->bd_id, count); + ez_string_put(io, ptr, count << sc); } break; @@ -262,8 +262,8 @@ static void tagz_encode_struct(struct ez_blobio *io, int id, const ez_blob_desc v = *(const void **)v; if (v) { count = strlen(v); - blobio_writetc(io, EZT_INT8, d->bd_id, count); - blobio_write(io, v, count); + tagz_pututc(io, EZT_INT8, d->bd_id, count); + ez_string_put(io, v, count); } break; @@ -280,7 +280,7 @@ static void tagz_encode_struct(struct ez_blobio *io, int id, const ez_blob_desc if (v) { count = ez_list_size((ez_list *)v); if (count) { - blobio_writetc(io, EZT_STRUCT, d->bd_id, count); + tagz_pututc(io, EZT_STRUCT, d->bd_id, count); for (ez_node *w = ez_list_head((ez_list *)v), *n = ez_node_succ(w); n; w = n, n=ez_node_succ(n)) { // which id to use? @@ -296,39 +296,39 @@ static void tagz_encode_struct(struct ez_blobio *io, int id, const ez_blob_desc } - blobio_writeb(io, EZT_END); + ez_string_putu8(io, EZT_END); } size_t ez_tagz_size(const ez_blob_desc *d, const void *p) { - struct ez_blobio io = { - .mode = BLOBIO_WRITE_SIZE +#if 0 + struct ez_string io = { + .mode = EZ_STRING_PUTU_SIZE }; tagz_encode_struct(&io, d->bd_id, d, p); return io.index; +#endif + abort(); + return 0; } int ez_tagz_encode_raw(const ez_blob_desc *d, const void *p, ez_blob *blob) { - struct ez_blobio io = { - .data = blob->eb_data, - .size = blob->eb_size, - .mode = BLOBIO_WRITE_FIXED - }; + struct ez_string io; + ez_string_init_fixed(&io, blob->eb_data, blob->eb_size); tagz_encode_struct(&io, d->bd_id, d, p); return io.error; } int ez_tagz_encode(const ez_blob_desc *d, const void *p, ez_blob *blob) { - struct ez_blobio io = { - .mode = BLOBIO_WRITE_ALLOC - }; + struct ez_string io; + ez_string_init_grow(&io); tagz_encode_struct(&io, d->bd_id, d, p); if (!io.error) { - blob->eb_size = io.index; + blob->eb_size = ez_string_grow_size(&io); blob->eb_data = io.data; return 0; } @@ -340,38 +340,38 @@ int ez_tagz_encode(const ez_blob_desc *d, const void *p, ez_blob *blob) { return io.error; } -static int blobio_readb(struct ez_blobio *io) { - uint8_t *v = blobio_take(io, 1); +static int tagz_getb(struct ez_string *io) { + uint8_t *v = ez_string_take(io, 1); return v ? *v : EZT_END; } -static uint64_t blobio_readi(struct ez_blobio *io, int sc) { +static uint64_t tagz_getux(struct ez_string *io, int sc) { switch (sc) { case 0: - return blobio_readi8(io); + return ez_string_getu8(io); case 1: - return blobio_readi16(io); + return ez_string_getu16(io); case 2: - return blobio_readi32(io); + return ez_string_getu32(io); case 3: - return blobio_readi64(io); + return ez_string_getu64(io); } return 0; } -static void tagz_decode_struct(struct ez_blobio *io, const ez_blob_desc *desc, void *p); -static void tagz_decode_fields(struct ez_blobio *io, const ez_blob_desc *desc, void *p); +static void tagz_decode_struct(struct ez_string *io, const ez_blob_desc *desc, void *p); +static void tagz_decode_fields(struct ez_string *io, const ez_blob_desc *desc, void *p); static unsigned int tagz_blob_type(int h) { return (h >> 4) & EZ_BLOB_TYPE; } -static void tagz_decode_fields(struct ez_blobio *io, const ez_blob_desc *desc, void *p) { +static void tagz_decode_fields(struct ez_string *io, const ez_blob_desc *desc, void *p) { uint8_t h; const ez_blob_desc *dscan = desc ? desc + 1 : desc; const ez_blob_desc *dend = desc ? desc + desc->bd_length + 1 : desc; - while ((h = blobio_readb(io)) != EZT_END) { + while ((h = tagz_getb(io)) != EZT_END) { uint32_t ftag; uint64_t fcount; int sc = (h >> EZT_DATASHIFT) & 3; @@ -380,8 +380,8 @@ static void tagz_decode_fields(struct ez_blobio *io, const ez_blob_desc *desc, v int use_count = tc != 3; tc = tc != 3 ? tc : cc; - ftag = blobio_readi(io, tc); - fcount = use_count ? blobio_readi(io, cc) : 1; + ftag = tagz_getux(io, tc); + fcount = use_count ? tagz_getux(io, cc) : 1; // This forces the tags to be in order // It ensures each field can only be visited once @@ -415,7 +415,7 @@ static void tagz_decode_fields(struct ez_blobio *io, const ez_blob_desc *desc, v case EZ_BLOB_VECTOR | EZ_BLOB_FLOAT32: case EZ_BLOB_VECTOR | EZ_BLOB_FLOAT64: { size_t size = fcount << (d->bd_type & EZ_BLOB_SIZE); - void *src = blobio_take(io, size); + void *src = ez_string_take(io, size); if (src) { void *mem = malloc(size); @@ -436,7 +436,7 @@ static void tagz_decode_fields(struct ez_blobio *io, const ez_blob_desc *desc, v /* C string */ case EZ_BLOB_CSTRING | EZ_BLOB_INT8: { size_t size = fcount; - void *src = blobio_take(io, size); + void *src = ez_string_take(io, size); if (src) { char *mem = malloc(size+1); @@ -489,22 +489,22 @@ static void tagz_decode_fields(struct ez_blobio *io, const ez_blob_desc *desc, v switch (d->bd_type & (EZ_BLOB_STORAGE | EZ_BLOB_TYPE)) { case EZ_BLOB_SINGLE | EZ_BLOB_INT8: - *(uint8_t *)v = blobio_readi(io, sc); + *(uint8_t *)v = tagz_getux(io, sc); break; case EZ_BLOB_SINGLE | EZ_BLOB_INT16: - *(uint16_t *)v = blobio_readi(io, sc); + *(uint16_t *)v = tagz_getux(io, sc); break; case EZ_BLOB_SINGLE | EZ_BLOB_INT32: - *(uint32_t *)v = blobio_readi(io, sc); + *(uint32_t *)v = tagz_getux(io, sc); break; case EZ_BLOB_SINGLE | EZ_BLOB_INT64: - *(uint64_t *)v = blobio_readi(io, sc); + *(uint64_t *)v = tagz_getux(io, sc); break; case EZ_BLOB_SINGLE | EZ_BLOB_FLOAT32: - *(float *)v = blobio_readf(io); + *(float *)v = ez_string_getf32(io); break; case EZ_BLOB_SINGLE | EZ_BLOB_FLOAT64: - *(double *)v = blobio_readd(io); + *(double *)v = ez_string_getf64(io); break; case EZ_BLOB_STRUCT | EZ_BLOB_SINGLEP: @@ -533,9 +533,9 @@ static void tagz_decode_fields(struct ez_blobio *io, const ez_blob_desc *desc, v case EZT_FLOAT32: case EZT_FLOAT64: if (use_count) { - blobio_take(io, fcount * (1<> EZT_TAGSHIFT) & 3; int cc = (h >> EZT_COUNTSHIFT) & 3; @@ -569,19 +569,16 @@ static void tagz_decode_struct(struct ez_blobio *io, const ez_blob_desc *desc, v } // unused, but could check against desc->bd_id - stag = blobio_readi(io, cc); + stag = tagz_getux(io, cc); stag = stag; tagz_decode_fields(io, desc, p); } int ez_tagz_decode_raw(const ez_blob_desc *desc, const ez_blob *blob, void *p) { - struct ez_blobio io = { - .size = blob->eb_size, - .data = blob->eb_data, - .mode = BLOBIO_READ - }; + struct ez_string io; + ez_string_init_fixed(&io, blob->eb_data, blob->eb_size); ez_blob_init(desc, p); tagz_decode_struct(&io, desc, p); @@ -606,11 +603,11 @@ void *ez_tagz_decode(const ez_blob_desc *desc, const ez_blob *blob) { /* ********************************************************************** */ // TODO: move to separate .o // TODO: copy fail behaviour of decode() -static void tagz_dump_struct(struct ez_blobio *io, int depth); -static void tagz_dump_fields(struct ez_blobio *io, int depth); +static void tagz_dump_struct(struct ez_string *io, int depth); +static void tagz_dump_fields(struct ez_string *io, int depth); -static void tagz_dump_struct(struct ez_blobio *io, int depth) { - uint8_t h = blobio_readb(io); +static void tagz_dump_struct(struct ez_string *io, int depth) { + uint8_t h = tagz_getb(io); uint32_t stag; int tc = (h >> EZT_TAGSHIFT) & 3; int cc = (h >> EZT_COUNTSHIFT) & 3; @@ -633,14 +630,14 @@ static void tagz_dump_struct(struct ez_blobio *io, int depth) { return; } - stag = blobio_readi(io, cc); + stag = tagz_getux(io, cc); printf("%s[%02x] %d = {\n", s, h, stag); tagz_dump_fields(io, depth+4); } -static void tagz_dump_fields(struct ez_blobio *io, int depth) { +static void tagz_dump_fields(struct ez_string *io, int depth) { int h; char s[depth+1]; @@ -648,7 +645,7 @@ static void tagz_dump_fields(struct ez_blobio *io, int depth) { s[depth] = 0; // read fields - while ((h = blobio_readb(io)) != EZT_END) { + while ((h = tagz_getb(io)) != EZT_END) { uint32_t ftag; uint64_t fcount; int sc = (h >> EZT_DATASHIFT) & 3; @@ -657,8 +654,8 @@ static void tagz_dump_fields(struct ez_blobio *io, int depth) { int use_count = tc != 3; tc = tc != 3 ? tc : cc; - ftag = blobio_readi(io, tc); - fcount = use_count ? blobio_readi(io, cc) : 1; + ftag = tagz_getux(io, tc); + fcount = use_count ? tagz_getux(io, cc) : 1; switch (h & EZT_TYPE) { case EZT_INT8: @@ -666,7 +663,7 @@ static void tagz_dump_fields(struct ez_blobio *io, int depth) { case EZT_INT32: case EZT_INT64: if (use_count) { - void *v = blobio_take(io, fcount * (1<index-1); + printf("%s[%02x] END $%04lx\n", s, h, (io->pos - io->data) - 1); printf("%s}\n", s); } @@ -721,11 +718,8 @@ static void tagz_dump_fields(struct ez_blobio *io, int depth) { * ezt format is self-describing so can be dumped directly. */ void ez_tagz_dump(ez_blob *blob) { - struct ez_blobio io = { - .data = blob->eb_data, - .size = blob->eb_size, - .mode = BLOBIO_READ - }; + struct ez_string io; + ez_string_init_fixed(&io, blob->eb_data, blob->eb_size); tagz_dump_struct(&io, 4); } diff --git a/ez-blob-xdrn.c b/ez-blob-xdrn.c index 4c6d144..3da274e 100644 --- a/ez-blob-xdrn.c +++ b/ez-blob-xdrn.c @@ -55,7 +55,7 @@ #include "ez-blob.h" #include "ez-blob-xdrn.h" -#include "ez-blob-io.h" +#include "ez-string.h" static size_t roundup(size_t v) __attribute__ ((always_inline)); static __inline__ size_t roundup(size_t v) { @@ -67,23 +67,25 @@ static __inline__ size_t roundup(size_t v) { Array but with natural sized elements, followed by padding. */ -static void xdrio_writev(struct ez_blobio *io, const ez_blob *data, size_t elshift) { +static size_t xdrio_sizev(const ez_blob *data, size_t elshift) { + size_t size = (data->eb_size << elshift) * data->eb_size; + + return (4 + size + 3) & ~3; +} + +static void xdrio_writev(struct ez_string *io, const ez_blob *data, size_t elshift) { size_t size = data->eb_size << elshift; int32_t count = data->eb_size; - void *v; - blobio_write32(io, count); - v = blobio_reserve(io, size); - if (v) { - memcpy(v, data->eb_data, size); - blobio_write_align(io, 4); - } + ez_string_putu32(io, count); + ez_string_put(io, data->eb_data, size); + ez_string_put_align(io, 4); } -static void xdrio_readv(struct ez_blobio *io, ez_blob *data, size_t elshift) { - int32_t count = blobio_readi32(io); +static void xdrio_readv(struct ez_string *io, ez_blob *data, size_t elshift) { + int32_t count = ez_string_getu32(io); size_t size = count << elshift; - void *src = blobio_take(io, size); + void *src = ez_string_take(io, size); if (src) { void *mem = malloc(size); @@ -92,38 +94,30 @@ static void xdrio_readv(struct ez_blobio *io, ez_blob *data, size_t elshift) { memcpy(mem, src, size); data->eb_data = mem; data->eb_size = count; - blobio_read_align(io, 4); + ez_string_get_align(io, 4); } else { io->error = ENOMEM; } } } -static void xdrio_writes(struct ez_blobio *io, const char *v) { - if (v) { - int32_t size = strlen(v); -#if 0 - int32_t total = (size + 4 + 3) & ~3; - void *v = blobio_reserve(io, total); - - if (v) { - *((uint32_t *)v) = size; - memcpy(v+4, v, size); - memset(v+4+size, 0, total-size-4); - } -#else - blobio_write32(io, size); - blobio_write(io, v, size); - blobio_write_align(io, 4); -#endif - } else { - io->error = EINVAL; - } +static size_t xdrio_sizes(const char *v) { + size_t size = strlen(v); + + return (4 + size + 3) & ~3; +} + +static void xdrio_writes(struct ez_string *io, const char *v) { + int32_t size = strlen(v); + + ez_string_putu32(io, size); + ez_string_put(io, v, size); + ez_string_put_align(io, 4); } -static void xdrio_reads(struct ez_blobio *io, char **v) { - uint32_t size = blobio_readi32(io); - void *src = blobio_take(io, size); +static void xdrio_reads(struct ez_string *io, char **v) { + uint32_t size = ez_string_getu32(io); + void *src = ez_string_take(io, size); if (src) { char *mem = malloc(size+1); @@ -132,14 +126,14 @@ static void xdrio_reads(struct ez_blobio *io, char **v) { memcpy(mem, src, size); mem[size] = 0; *v = mem; - blobio_read_align(io, 4); + ez_string_get_align(io, 4); } else { io->error = ENOMEM; } } } -static void xdrn_encode_raw(struct ez_blobio *io, const ez_blob_desc *desc, const void *p) { +static void xdrn_encode_raw(struct ez_string *io, const ez_blob_desc *desc, const void *p) { for (int i=0,dlen=desc->bd_length;ibd_offset); @@ -149,22 +143,22 @@ static void xdrn_encode_raw(struct ez_blobio *io, const ez_blob_desc *desc, cons * Primitive x 1 */ case EZ_BLOB_SINGLE | EZ_BLOB_INT8: - blobio_write32(io, *(uint8_t *)v); + ez_string_putu32(io, *(uint8_t *)v); break; case EZ_BLOB_SINGLE | EZ_BLOB_INT16: - blobio_write32(io, *(uint16_t *)v); + ez_string_putu32(io, *(uint16_t *)v); break; case EZ_BLOB_SINGLE | EZ_BLOB_INT32: - blobio_write32(io, *(uint32_t *)v); + ez_string_putu32(io, *(uint32_t *)v); break; case EZ_BLOB_SINGLE | EZ_BLOB_INT64: - blobio_write64(io, *(uint64_t *)v); + ez_string_putu64(io, *(uint64_t *)v); break; case EZ_BLOB_SINGLE | EZ_BLOB_FLOAT32: - blobio_writef(io, *(float *)v); + ez_string_putf32(io, *(float *)v); break; case EZ_BLOB_SINGLE | EZ_BLOB_FLOAT64: - blobio_writed(io, *(double *)v); + ez_string_putf64(io, *(double *)v); break; /* @@ -184,10 +178,10 @@ static void xdrn_encode_raw(struct ez_blobio *io, const ez_blob_desc *desc, cons */ case EZ_BLOB_CSTRING | EZ_BLOB_INT8 | EZ_BLOB_ISNULLABLE: if (!*(const char **)v) { - blobio_write32(io, 0); + ez_string_putu32(io, 0); break; } - blobio_write32(io, 1); + ez_string_putu32(io, 1); // falls through case EZ_BLOB_CSTRING | EZ_BLOB_INT8: xdrio_writes(io, *(const char **)v); @@ -198,10 +192,10 @@ static void xdrn_encode_raw(struct ez_blobio *io, const ez_blob_desc *desc, cons */ case EZ_BLOB_STRUCT | EZ_BLOB_SINGLEP | EZ_BLOB_ISNULLABLE: if (!*(const void **)v) { - blobio_write32(io, 0); + ez_string_putu32(io, 0); break; } - blobio_write32(io, 1); + ez_string_putu32(io, 1); // falls through case EZ_BLOB_STRUCT | EZ_BLOB_SINGLEP: v = *(const void **)v; @@ -219,10 +213,10 @@ static void xdrn_encode_raw(struct ez_blobio *io, const ez_blob_desc *desc, cons */ case EZ_BLOB_STRUCT | EZ_BLOB_LISTP | EZ_BLOB_ISNULLABLE: if (!*(const void **)v) { - blobio_write32(io, 0); + ez_string_putu32(io, 0); break; } - blobio_write32(io, 1); + ez_string_putu32(io, 1); // falls through case EZ_BLOB_STRUCT | EZ_BLOB_LISTP: v = *(const void **)v; @@ -232,7 +226,7 @@ static void xdrn_encode_raw(struct ez_blobio *io, const ez_blob_desc *desc, cons } // falls through case EZ_BLOB_STRUCT | EZ_BLOB_LIST: - blobio_write32(io, ez_list_size((ez_list *)v)); + ez_string_putu32(io, ez_list_size((ez_list *)v)); for (ez_node *w = ez_list_head((ez_list *)v), *n = ez_node_succ(w); n; w = n, n=ez_node_succ(n)) { xdrn_encode_raw(io, d->bd_table, w); } @@ -244,36 +238,116 @@ static void xdrn_encode_raw(struct ez_blobio *io, const ez_blob_desc *desc, cons } } -size_t ez_xdrn_size(const ez_blob_desc *d, const void *p) { - struct ez_blobio io = { - .mode = BLOBIO_WRITE_SIZE - }; - xdrn_encode_raw(&io, d, p); +ssize_t ez_xdrn_size(const ez_blob_desc *desc, const void *p) { + size_t size = 0; + + for (int i=0,dlen=desc->bd_length;ibd_offset); + + switch (d->bd_type) { + /* + * Primitive x 1 + */ + case EZ_BLOB_SINGLE | EZ_BLOB_INT8: + case EZ_BLOB_SINGLE | EZ_BLOB_INT16: + case EZ_BLOB_SINGLE | EZ_BLOB_INT32: + case EZ_BLOB_SINGLE | EZ_BLOB_FLOAT32: + size += 4; + break; + case EZ_BLOB_SINGLE | EZ_BLOB_INT64: + case EZ_BLOB_SINGLE | EZ_BLOB_FLOAT64: + size += 8; + + /* + * Primitive x array + */ + case EZ_BLOB_VECTOR | EZ_BLOB_INT8: + case EZ_BLOB_VECTOR | EZ_BLOB_INT16: + case EZ_BLOB_VECTOR | EZ_BLOB_INT32: + case EZ_BLOB_VECTOR | EZ_BLOB_INT64: + case EZ_BLOB_VECTOR | EZ_BLOB_FLOAT32: + case EZ_BLOB_VECTOR | EZ_BLOB_FLOAT64: + size += xdrio_sizev((const ez_blob *)v, (d->bd_type & EZ_BLOB_SIZE)); + break; + + /* + * C string byte + */ + case EZ_BLOB_CSTRING | EZ_BLOB_INT8 | EZ_BLOB_ISNULLABLE: + size += 4; + if (!*(const char **)v) + break; + // falls through + case EZ_BLOB_CSTRING | EZ_BLOB_INT8: + if (!*(const char **)v) + goto einval; + size += xdrio_sizes(*(const char **)v); + break; + + /* + * Structure x 1 + */ + case EZ_BLOB_STRUCT | EZ_BLOB_SINGLEP | EZ_BLOB_ISNULLABLE: + size += 4; + // falls through + case EZ_BLOB_STRUCT | EZ_BLOB_SINGLEP: + v = *(const void **)v; + if (!v) + goto einval; + // falls through + case EZ_BLOB_STRUCT | EZ_BLOB_SINGLE: + size += ez_xdrn_size(d->bd_table, v); + break; + + /* + * Structure x list + */ + case EZ_BLOB_STRUCT | EZ_BLOB_LISTP | EZ_BLOB_ISNULLABLE: + size += 4; + if (!*(const void **)v) + break; + // falls through + case EZ_BLOB_STRUCT | EZ_BLOB_LISTP: + v = *(const void **)v; + if (!v) + goto einval; + // falls through + case EZ_BLOB_STRUCT | EZ_BLOB_LIST: + size += 4; + for (ez_node *w = ez_list_head((ez_list *)v), *n = ez_node_succ(w); n; w = n, n=ez_node_succ(n)) + size += ez_xdrn_size(d->bd_table, w); + break; + default: + errno = ENOTSUP; + return -1; + } + } + + return size; +einval: + errno = EINVAL; - return io.index; + return -1; } int ez_xdrn_encode_raw(const ez_blob_desc *d, const void *p, ez_blob *blob) { - struct ez_blobio io = { - .data = blob->eb_data, - .size = blob->eb_size, - .mode = BLOBIO_WRITE_FIXED - }; + struct ez_string io; + ez_string_init_fixed(&io, blob->eb_data, blob->eb_size); xdrn_encode_raw(&io, d, p); return io.error; } int ez_xdrn_encode(const ez_blob_desc *d, const void *p, ez_blob *blob) { - struct ez_blobio io = { - .mode = BLOBIO_WRITE_ALLOC - }; + struct ez_string io; + ez_string_init_grow(&io); xdrn_encode_raw(&io, d, p); if (!io.error) { - blob->eb_size = io.index; + blob->eb_size = ez_string_grow_size(&io); blob->eb_data = io.data; return 0; } @@ -285,7 +359,7 @@ int ez_xdrn_encode(const ez_blob_desc *d, const void *p, ez_blob *blob) { return io.error; } -static void xdrn_decode_raw(struct ez_blobio *io, const ez_blob_desc *desc, void *p) { +static void xdrn_decode_raw(struct ez_string *io, const ez_blob_desc *desc, void *p) { for (int i=0,dlen=desc->bd_length;ibd_offset); @@ -295,22 +369,22 @@ static void xdrn_decode_raw(struct ez_blobio *io, const ez_blob_desc *desc, void * Primitive x 1 */ case EZ_BLOB_SINGLE | EZ_BLOB_INT8: - *(uint8_t *)v = blobio_readi32(io); + *(uint8_t *)v = ez_string_getu32(io); break; case EZ_BLOB_SINGLE | EZ_BLOB_INT16: - *(uint16_t *)v = blobio_readi32(io); + *(uint16_t *)v = ez_string_getu32(io); break; case EZ_BLOB_SINGLE | EZ_BLOB_INT32: - *(uint32_t *)v = blobio_readi32(io); + *(uint32_t *)v = ez_string_getu32(io); break; case EZ_BLOB_SINGLE | EZ_BLOB_INT64: - *(uint64_t *)v = blobio_readi64(io); + *(uint64_t *)v = ez_string_getu64(io); break; case EZ_BLOB_SINGLE | EZ_BLOB_FLOAT32: - *(float *)v = blobio_readf(io); + *(float *)v = ez_string_getf32(io); break; case EZ_BLOB_SINGLE | EZ_BLOB_FLOAT64: - *(double *)v = blobio_readd(io); + *(double *)v = ez_string_getf64(io); break; /* @@ -329,7 +403,7 @@ static void xdrn_decode_raw(struct ez_blobio *io, const ez_blob_desc *desc, void * C string byte */ case EZ_BLOB_CSTRING | EZ_BLOB_INT8 | EZ_BLOB_ISNULLABLE: - if (blobio_readi32(io) == 0) + if (ez_string_getu32(io) == 0) break; // falls through case EZ_BLOB_CSTRING | EZ_BLOB_INT8: @@ -340,7 +414,7 @@ static void xdrn_decode_raw(struct ez_blobio *io, const ez_blob_desc *desc, void * Structure x 1 */ case EZ_BLOB_STRUCT | EZ_BLOB_SINGLEP | EZ_BLOB_ISNULLABLE: - if (blobio_readi32(io) == 0) + if (ez_string_getu32(io) == 0) break; // falls through case EZ_BLOB_STRUCT | EZ_BLOB_SINGLEP: @@ -359,7 +433,7 @@ static void xdrn_decode_raw(struct ez_blobio *io, const ez_blob_desc *desc, void * Structure x list */ case EZ_BLOB_STRUCT | EZ_BLOB_LISTP | EZ_BLOB_ISNULLABLE: - if (blobio_readi32(io) == 0) + if (ez_string_getu32(io) == 0) break; // falls through case EZ_BLOB_STRUCT | EZ_BLOB_LISTP: @@ -372,7 +446,7 @@ static void xdrn_decode_raw(struct ez_blobio *io, const ez_blob_desc *desc, void ez_list_init((ez_list *)v); // falls through case EZ_BLOB_STRUCT | EZ_BLOB_LIST: { - uint32_t count = blobio_readi32(io); + uint32_t count = ez_string_getu32(io); for (int j=0;jbd_table); @@ -395,12 +469,9 @@ static void xdrn_decode_raw(struct ez_blobio *io, const ez_blob_desc *desc, void } int ez_xdrn_decode_raw(const ez_blob_desc *desc, const ez_blob *blob, void *p) { - struct ez_blobio io = { - .size = blob->eb_size, - .data = blob->eb_data, - .mode = BLOBIO_READ - }; + struct ez_string io; + ez_string_init_fixed(&io, blob->eb_data, blob->eb_size); ez_blob_init(desc, p); xdrn_decode_raw(&io, desc, p); diff --git a/ez-blob-xdrn.h b/ez-blob-xdrn.h index 0cfa77c..fe3e860 100644 --- a/ez-blob-xdrn.h +++ b/ez-blob-xdrn.h @@ -20,7 +20,7 @@ #ifndef _EZ_BLOB_XDRN_H #define _EZ_BLOB_XDRN_H -size_t ez_xdrn_size(const ez_blob_desc *d, const void *p); +ssize_t ez_xdrn_size(const ez_blob_desc *d, const void *p); int ez_xdrn_encode_raw(const ez_blob_desc *d, const void *p, ez_blob *blob); int ez_xdrn_encode(const ez_blob_desc *d, const void *p, ez_blob *blob); diff --git a/ez-http.c b/ez-http.c index b804f27..3978752 100644 --- a/ez-http.c +++ b/ez-http.c @@ -17,42 +17,33 @@ . */ -#define _GNU_SOURCE - #include #include +#include #include #include #include #include -#include #include #include #include +#ifdef HAVE_SIGNAL #include +#endif #include -#include "ez-blob-io.h" -#include "ez-list.h" -#include "ez-tree.h" +#include -#define obstack_chunk_alloc malloc -#define obstack_chunk_free free -#include #include #include + #define D(x) +#include "ez-list.h" +#include "ez-string.h" #include "ez-http.h" -static void blobio_init_read(struct ez_blobio *io, const void *data, size_t size) { - io->index = 0; - io->data = (void *)data; - io->size = size; - io->alloc = 0; - io->error = 0; - io->mode = BLOBIO_READ; -} +#define HTTP_BUFFER_SIZE 4096 static void init_request(struct ez_httprequest *r, struct ez_httpconnection *c) { memset(r, 0, sizeof(*r)); @@ -71,7 +62,8 @@ static void init_response(struct ez_httpresponse *r, struct ez_httpconnection *c void httpresponse_set_response(struct ez_httpresponse *r, int code, const char *msg) { r->code = code; - r->message = obstack_copy(&r->http.conn->os, msg, strlen(msg)+1); + ez_string_print(&r->http.conn->os, msg); + r->message = ez_string_chunk_end0(&r->http.conn->os); } /* @@ -97,25 +89,26 @@ enum { STATE_CONTENT, }; -static int parse_http(struct ez_http *r, struct ez_blobio * __restrict io) { - struct obstack *os = &r->conn->os; +static int parse_http(struct ez_http *r, struct ez_string * __restrict io) { + struct ez_string *os = &r->conn->os; while (r->state != STATE_CONTENT) { - size_t size = io->size - io->index; - const uint8_t *s = memchr(io->data + io->index, '\n', size); + size_t size = io->end - io->pos; + const uint8_t *s = memchr(io->pos, '\n', size); + if (s) { - size_t len = s - (io->data + io->index); + size_t len = s - (uint8_t *)io->pos; if (len > 0 && s[-1] == '\r') - obstack_grow0(os, blobio_take(io, len + 1), len - 1); + ez_string_put(os, ez_string_take(io, len + 1), len - 1); else if (len == 0 && r->state == STATE_CR) - obstack_grow0(os, blobio_take(io, 1), 0); + ez_string_take(io, 1); else return -1; - len = obstack_object_size(os) - 1; + len = ez_string_chunk_size(os); if (len) { - char *value = obstack_finish(os); + char *value = ez_string_chunk_end0(os); if (!value) return -1; @@ -123,7 +116,7 @@ static int parse_http(struct ez_http *r, struct ez_blobio * __restrict io) { if (!r->status) { r->status = value; } else { - struct ez_pair *h = obstack_alloc(os, sizeof(*h)); + struct ez_pair *h = ez_string_chunk_alloc(os, sizeof(*h)); if (!h) return -1; @@ -141,35 +134,28 @@ static int parse_http(struct ez_http *r, struct ez_blobio * __restrict io) { } r->state = STATE_HEADER; } else { - // loses a byte, ignore - obstack_finish(os); - r->state = STATE_CONTENT; - // new 'read-all-in' version - r->data.index = 0; - r->data.data = obstack_alloc(os, r->content_length); + // allocate room for all content + // TODO: have it previously allocated/limited + ez_string_init_fixed(&r->data, ez_string_chunk_alloc(os, r->content_length), r->content_length); if (!r->data.data) return -1; - r->data.size = r->content_length; - r->data.alloc = r->content_length; - r->data.mode = BLOBIO_READ; // swallow all we have up to limit - size_t size = io->size - io->index; - size_t max = r->data.size - r->data.index; + size_t size = io->end - io->pos; + size_t max = r->data.end - r->data.pos; size_t len = size > max ? max : size; - memcpy(r->data.data + r->data.index, blobio_take(io, len), len); - r->data.index += len; + ez_string_put(&r->data, ez_string_take(io, len), len); } } else { - if (io->size > 0 && io->index < io->size && io->data[io->size-1] == '\r') { + if (size > 0 && io->end[-1] == '\r') { r->state = STATE_CR; - obstack_grow(os, blobio_take(io, size), size-1); + ez_string_put(os, ez_string_take(io, size), size-1); } else { r->state = STATE_HEADER; - obstack_grow(os, blobio_take(io, size), size); + ez_string_put(os, ez_string_take(io, size), size); } return 1; } @@ -184,7 +170,7 @@ static int nybble(int c) { return c - '0'; else if (c <= 'F') return c - 'A' + 10; - else + else // this is out of spec return c - 'a' + 10; } @@ -208,45 +194,48 @@ static int tohex(int nybble) { return nybble < 10 ? nybble + '0' : nybble + 'a' - 10; } -// see tables.c -#define UCHAR 0x01 -#define HEX 0x02 -const uint8_t type[256] = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, - 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x00, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -}; +/* -static __inline__ int is_uchar(char c) { - return (type[(unsigned)c] & UCHAR) != 0; + ALPHA = %x41-5A / %x61-7A ; A-Z / a-z + DIGIT = %x30-39 ; 0-9 + HEXDIG = DIGIT / "A" / "B" / "C" / "D" / "E" / "F" + + pchar = unreserved / pct-encoded / sub-delims / ":" / "@" + + query = *( pchar / "/" / "?" ) + fragment = *( pchar / "/" / "?" ) + + pct-encoded = "%" HEXDIG HEXDIG + + unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" + reserved = gen-delims / sub-delims + gen-delims = ":" / "/" / "?" / "#" / "[" / "]" / "@" + sub-delims = "!" / "$" / "&" / "'" / "(" / ")" + / "*" / "+" / "," / ";" / "=" + */ + +// 1 bit per character code 00-7f +const uint32_t pchar_tab[] = { 0x00000000, 0x2fff7fd2, 0x87ffffff, 0x47fffffe }; + +static __inline__ int is_pchar(unsigned char c) { + return c >= 32 + && c <= 128 + && pchar_tab[c >> 5] & (1 << (c & 31)); } -static void url_encode(struct obstack *os, const char *s) { +// This is only for url parameters, pchar +static void url_encode(struct ez_string *os, const char *s) { char c; - // TODO: utf8? + while ((c = *s++)) { - if (is_uchar(c)) - obstack_1grow(os, c); - else if (c == ' ') - obstack_1grow(os, '+'); - else { - obstack_1grow(os, '%'); - obstack_1grow(os, tohex((c>>4)&0x0f)); - obstack_1grow(os, tohex(c&0x0f)); + if (c == ' ') + c = '+'; + else if (is_pchar(c) || c == '&') { + ez_string_putc(os, '%'); + ez_string_putc(os, tohex((c >> 4) & 0x0f)); + c = tohex(c & 0x0f); } + ez_string_putc(os, c); } } @@ -284,17 +273,16 @@ static int parse_request_status(struct ez_httprequest *r) { } else return -1; - url_decode(url); - r->method = r->http.status; r->url = url; r->version = version; + //printf("request '%s' '%s' '%s'\n", r->method, r->url, r->version); // parse the url parameters if there are any // this overwwirtes r->url[contents] char *s = strchr(url, '?'), *t, *u; if (s) { - struct obstack *os = &r->http.conn->os; + struct ez_string *os = &r->http.conn->os; *s++ = 0; do { @@ -307,7 +295,7 @@ static int parse_request_status(struct ez_httprequest *r) { } url_decode(s); - struct ez_pair *h = obstack_alloc(os, sizeof(*h)); + struct ez_pair *h = ez_string_chunk_alloc(os, sizeof(*h)); h->name = s; h->value = u; ez_list_addtail(&r->params, h); @@ -317,95 +305,108 @@ static int parse_request_status(struct ez_httprequest *r) { return 0; } +// should it take a blob? // read all data, data may be null (== noop) -int httpconnection_read(struct ez_httpconnection *s, struct ez_blobio *data) { - if (data) { - ssize_t len = data->size - data->index; - - while (len > 0 - && (len = read(s->fd, data->data + data->index, len)) >= 0) { - printf("read:\n"); - fwrite(data->data + data->index, 1, len, stdout); - printf("\n--\n"); - data->index += len; - len = data->size - data->index; - } - // reset index for reading - data->index = 0; - return len; - } else - return 0; +int httpconnection_read(struct ez_httpconnection *s, struct ez_string *data) { + size_t left = data->end - data->pos; + ssize_t len = 0; + + D(printf("reading: %zd\n", left)); + + // TODO: this should probably be using stream functions + + while (left > 0 + && (len = read(s->fd, data->pos, left)) >= 0) { + + D(printf("read2:\n")); + D(fwrite(data->pos, 1, len, stdout)); + D(printf("\n--\n")); + + data->pos += len; + left -= len; + } + // reset index for reading + data->pos = data->data; + return len < 0 ? len : 0; } -// write all data, data may be null (== noop) +// write all data +// update pos? // return 0 on success -int httpconnection_write(struct ez_httpconnection *s, struct ez_blobio *data) { - if (data) { - ssize_t len = data->size - data->index; - - while (len > 0 - && (len = write(s->fd, data->data + data->index, len)) >= 0) { - //printf("send:\n"); - //fwrite(data->data + data->index, 1, len, stdout); - //printf("\n--\n"); - data->index += len; - len = data->size - data->index; - } - return len; - } else - return 0; +int httpconnection_write(struct ez_httpconnection *s, struct ez_string *data) { + char *pos = data->pos; + size_t left = data->end - pos; + ssize_t len = 0; + + while (left > 0 + && (len = write(s->fd, pos, left)) >= 0) { + + D(printf("send:\n")); + D(fwrite(pos, 1, len, stdout)); + D(printf("\n--\n")); + + pos += len; + left -= len; + } + + return len < 0 ? len : 0; } // read headers and all data into memory -static int read_http(struct ez_http *http) { +static int read_http(struct ez_http * __restrict http) { struct ez_httpconnection *s = http->conn; int res; do { - ssize_t len = read(s->fd, s->io.data, s->io.alloc); + ssize_t len = read(s->fd, s->io.data, HTTP_BUFFER_SIZE); if (len <= 0) break; - s->io.index = 0; - s->io.size = len; + s->io.pos = s->io.data; + s->io.end = s->io.data + len; - //printf("read:\n"); - //fwrite(s->io.data, 1, len, stdout); - //printf("\n--\n"); + D(printf("read:\n")); + D(fwrite(s->io.data, 1, len, stdout)); + D(printf("\n--\n")); res = parse_http(http, &s->io); if (res == 2) return httpconnection_read(s, &http->data); } while (res > 0); + return -1; } // Format and write headers static int write_headers(struct ez_http *http) { - struct obstack *os = &http->conn->os; - struct ez_blobio msg = { 0 }; - int res; + struct ez_string *os = &http->conn->os; + char *data; + size_t size; + int res = 0; - http->content_length = http->data.size - http->data.index; + //http->content_length = blobio_size(&http->data); - for (struct ez_pair *w = ez_list_head(&http->headers), *n = ez_node_succ(w);n;w=n,n = ez_node_succ(w)) - obstack_printf(os, "%s:%s\r\n", w->name, w->value); - obstack_printf(os, "Content-Length:%zd\r\n\r\n", http->content_length); + for (struct ez_pair *w = ez_list_head(&http->headers), *n = ez_node_succ(w);n;w=n,n = ez_node_succ(w)) { + ez_string_printf(os, "%s:%s\r\n", w->name, w->value); + } - msg.size = obstack_object_size(os); - msg.data = obstack_finish(os); - if (msg.data) { - res = httpconnection_write(http->conn, &msg); - obstack_free(os, msg.data); - } else { - const char *oom = "HTTP/1.0 500 Out of Memory"; + ez_string_printf(os, "Content-Length:%d\r\n\r\n", (int)http->content_length); - msg.size = strlen(oom); - msg.data = (void *)oom; + data = ez_string_chunk_end(os, &size); + if (data) { + struct ez_string m; - httpconnection_write(http->conn, &msg); + ez_string_init_fixed(&m, data, size); + httpconnection_write(http->conn, &m); + // FIXME: free or truncate os? does it matter? + //blobio_free(os, msg.eb_data); + } else { + struct ez_string m; + const char *oom = "HTTP/1.0 500 Out of Memory"; + ez_string_init_fixed(&m, (char *)oom, strlen(oom)); + httpconnection_write(http->conn, &m); res = -1; } @@ -414,13 +415,17 @@ static int write_headers(struct ez_http *http) { // Send a response static int httpresponse_run(struct ez_httpresponse *r) { - struct obstack *os = &r->http.conn->os; + struct ez_string *os = &r->http.conn->os; int res; - obstack_printf(os, "HTTP/1.0 %d %s\r\n", r->code, r->message); + ez_string_printf(os, "HTTP/1.0 %d %s\r\n", r->code, r->message); + res = write_headers(&r->http); - if (res == 0) + if (res == 0) { res = httpconnection_write(r->http.conn, &r->http.data); + } else { + D(printf("write failed: %d\n", res)); + } return res; } @@ -428,7 +433,7 @@ static int httpresponse_run(struct ez_httpresponse *r) { int httpserver_init(struct ez_httpserver *s, int port) { memset(s, 0, sizeof(*s)); - ez_tree_init(&s->handlers, ez_string_node_cmp); + ez_list_init(&s->handlers); memset(&s->addr, 0, sizeof(s->addr)); s->addr.sin_family = AF_INET; @@ -443,40 +448,38 @@ void httpserver_free(struct ez_httpserver *s) { } int httpconnection_init(struct ez_httpconnection *conn, struct ez_httpserver *s) { - memset(conn, 0, sizeof(*conn)); - - conn->io.alloc = 4096; - conn->io.data = malloc(conn->io.alloc); - conn->io.mode = BLOBIO_READ; + char *data = malloc(HTTP_BUFFER_SIZE); - obstack_init(&conn->os); - conn->empty = obstack_alloc(&conn->os, 0); + memset(conn, 0, sizeof(*conn)); + ez_string_init_fixed(&conn->io, data, HTTP_BUFFER_SIZE); + ez_string_init_chunk(&conn->os); return 0; } +// reset? void httpconnection_clear(struct ez_httpconnection *conn) { // close fd? - obstack_free(&conn->os, conn->empty); - conn->empty = obstack_alloc(&conn->os, 0); + ez_string_chunk_reset(&conn->os); } void httpconnection_free(struct ez_httpconnection *conn) { - obstack_free(&conn->os, NULL); + // FIXME: stream free + ez_string_chunk_reset(&conn->os); free(conn->io.data); memset(conn, 0, sizeof(*conn)); } void httpserver_addhandlers(struct ez_httpserver *s, struct ez_httphandler *list, int count) { for (int i=0;ihandlers, &list[i]); + ez_list_addtail(&s->handlers, &list[i]); } int httpserver_run(struct ez_httpserver *s) { int res; - +#ifdef HAVE_SIGNAL signal(SIGPIPE, SIG_IGN); - +#endif s->fd = socket(AF_INET, SOCK_STREAM, 0); if (s->fd < 0) { perror("ERROR opening socket"); @@ -513,17 +516,6 @@ int httpserver_run(struct ez_httpserver *s) { setsockopt(conn.fd, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout, sizeof(timeout)); } - char ipstr[INET6_ADDRSTRLEN]; - - // deal with both IPv4 and IPv6: - if (conn.addr.sin_family == AF_INET) { - struct sockaddr_in *s = (struct sockaddr_in *)&conn.addr; - inet_ntop(AF_INET, &s->sin_addr, ipstr, sizeof(ipstr)); - } else { // AF_INET6 - struct sockaddr_in6 *s = (struct sockaddr_in6 *)&conn.addr; - inet_ntop(AF_INET6, &s->sin6_addr, ipstr, sizeof(ipstr)); - } - // Default response init_response(&rep, &conn); rep.code = 500; @@ -534,27 +526,19 @@ int httpserver_run(struct ez_httpserver *s) { res = read_http(&req.http); if (res == 0) { - if (s->debug > 0) - printf("%-15s \"%s\"\n", ipstr, req.http.status); res = parse_request_status(&req); if (res == 0) { - const struct ez_httphandler key = { .path = req.url }; - ez_tree_scan scan; - struct ez_httphandler *handler = ez_tree_scan_init_key(&s->handlers, &scan, EZ_LINK_RIGHT, EZ_LINK_LEFT, &key); - - if (handler && handler->mode == EZ_PATH_EXACT && strcmp(req.url, handler->path) != 0) - handler = NULL; - - if (handler && handler->mode == EZ_PATH_PREFIX && strncmp(req.url, handler->path, strlen(handler->path)) != 0) - handler = NULL; - - if (handler) { - if (s->debug > 0) - printf("%s %s\n", req.method, req.url); - - quit = handler->fn(&req, &rep); - } else - httpresponse_set_response(&rep, 404, "Missing"); + for (struct ez_httphandler *w = ez_list_head(&s->handlers), *n = ez_node_succ(w); + n; + w = n, n = ez_node_succ(w)) { + if ((w->mode == EZ_PATH_EXACT && strcmp(req.url, w->path) == 0) + || (w->mode == EZ_PATH_PREFIX && strncmp(req.url, w->path, strlen(w->path)) == 0)) { + quit = w->fn(&req, &rep); + goto done; + } + } + httpresponse_set_response(&rep, 404, "Missing"); + done: } else httpresponse_set_response(&rep, 400, "Invalid"); } else @@ -569,8 +553,9 @@ int httpserver_run(struct ez_httpserver *s) { httpconnection_free(&conn); res = 0; fail: +#ifdef HAVE_SIGNAL signal(SIGPIPE, SIG_DFL); - +#endif close(s->fd); s->fd = -1; @@ -594,6 +579,12 @@ int httpclient_init(struct ez_httpclient *cc, const char *host, int port) { return 0; } +void httpclient_clear(struct ez_httpclient *cc) { + httpconnection_clear(&cc->conn); + init_request(&cc->request, &cc->conn); + init_response(&cc->response, &cc->conn); +} + void httpclient_free(struct ez_httpclient *cc) { httpconnection_free(&cc->conn); } @@ -608,8 +599,25 @@ void http_addheaders(struct ez_http *http, struct ez_pair *list, int count) { ez_list_addtail(&http->headers, &list[i]); } -void http_set_data(struct ez_http *http, const char *data, size_t size) { - blobio_init_read(&http->data, data, size); +#if 0 +void http_addheader(struct ez_http *http, const char *name, const char *value) { + struct ez_string *os = &http->conn->os; + struct ez_pair *h = ez_string_chunk_alloc(os, sizeof(*h)); + + ez_string_print(os, name); + h->name = ez_string_chunk_end0(os); + ez_string_print(os, value); + h->value = ez_string_chunk_end0(os); + + ez_list_addtail(&http->headers, h); +} +#endif + +// FIXME: needs to bre replaced with (potentially) streaming output +// Note that this doesn't copy data +void http_set_data(struct ez_http *http, char *data, size_t size) { + ez_string_init_fixed(&http->data, data, size); + http->content_length = size; } // simple send/receive in-memory version @@ -620,7 +628,7 @@ int httpclient_run(struct ez_httpclient *cc, const char *method, const char *url struct ez_httpconnection *c = &cc->conn; struct ez_httprequest *req = &cc->request; struct ez_httpresponse *rep = &cc->response; - struct obstack *os = &c->os; + struct ez_string *os = &c->os; c->fd = socket(AF_INET, SOCK_STREAM, 0); res = connect(c->fd, (struct sockaddr *)&c->addr, sizeof(c->addr)); @@ -635,30 +643,33 @@ int httpclient_run(struct ez_httpclient *cc, const char *method, const char *url } // from request status line, method, url - req->method = obstack_copy(os, method, strlen(method)+1); - req->url = obstack_copy(os, url, strlen(url)+1); + ez_string_print(os, method); + req->method = ez_string_chunk_end0(os); + ez_string_print(os, url); + req->url = ez_string_chunk_end0(os); - obstack_printf(os, "%s %s", method, url); - char next = '?'; + ez_string_print(os, method); + ez_string_putc(os, ' '); + ez_string_print(os, url); // add encoded parameters + char next = '?'; for (struct ez_pair *w = ez_list_head(&req->params), *n = ez_node_succ(w);n;w=n,n = ez_node_succ(w)) { - obstack_1grow(os, next); + ez_string_putc(os, next); url_encode(os, w->name); if (w->value) { - obstack_1grow(os, '='); + ez_string_putc(os, '='); url_encode(os, w->value); } next = '&'; } // and version - obstack_printf(os, " HTTP/1.0\r\n"); + ez_string_print(os, " HTTP/1.0\r\n"); // write headers and data res = write_headers(&req->http); if (res == 0) { res = httpconnection_write(c, &req->http.data); - if (res == 0) { // fully read response init_response(rep, req->http.conn); diff --git a/ez-http.h b/ez-http.h index d2417fb..92a4634 100644 --- a/ez-http.h +++ b/ez-http.h @@ -43,7 +43,7 @@ struct ez_http { char *status; // raw status line before decoding struct ez_list headers; // struct ez_pair headers size_t content_length; - struct ez_blobio data; + struct ez_string data; int state; // internal decode state }; @@ -75,11 +75,11 @@ struct ez_httpresponse { * HTTP connection, used for both client and server endpoints. */ struct ez_httpconnection { - struct obstack os; // Use for building responses and other per-request objects + struct ez_string os; // Use for building responses and other per-request objects void *empty; struct sockaddr_in addr; // remote address of connection - struct ez_blobio io; // used for buffering i/o for headers + struct ez_string io; // used for buffering i/o for headers int fd; }; @@ -92,7 +92,7 @@ void http_addheaders(struct ez_http *h, struct ez_pair *list, int count); /** * Set the payload for a request or response. */ -void http_set_data(struct ez_http *h, const char *data, size_t size); +void http_set_data(struct ez_http *http, char *data, size_t size); /** * Add url parameters for request. @@ -120,7 +120,7 @@ void httpresponse_set_response(struct ez_httpresponse *r, int code, const char * * HTTP server listening socket and state. */ struct ez_httpserver { - ez_tree handlers; // tree of struct ez_httphandler + ez_list handlers; // tree of struct ez_httphandler int debug; struct sockaddr_in addr; @@ -174,6 +174,11 @@ int httpclient_init(struct ez_httpclient *cc, const char *host, int port); */ int httpclient_run(struct ez_httpclient *cc, const char *method, const char *url); +/** + * Prepare for another request. + */ +void httpclient_clear(struct ez_httpclient *cc); + /** * Free all httpclient resources. */ diff --git a/ez-list.h b/ez-list.h index d3494eb..d6a9fa9 100644 --- a/ez-list.h +++ b/ez-list.h @@ -263,4 +263,14 @@ static int ez_list_empty(struct ez_list *l) __attribute__ ((always_inline)); static void *ez_list_head(struct ez_list *l) __attribute__ ((always_inline)); static void *ez_list_tail(struct ez_list *l) __attribute__ ((always_inline)); +static __inline__ void *ez_list_find_name(struct ez_list * __restrict l, const char *name) { + for (struct ez_name *w = (struct ez_name *)l->head, *n = w->succ; + n; + w = n, n = n->succ) + if (strcmp(w->name, name) == 0) + return w; + + return NULL; +} + #endif diff --git a/ez-node.h b/ez-node.h index 9664290..32a5de1 100644 --- a/ez-node.h +++ b/ez-node.h @@ -129,22 +129,23 @@ static unsigned int ez_node_hash(void *node) __attribute__((always_inline)); /** * Extended node - a named node. */ -typedef struct ez_string_node ez_string_node; +typedef struct ez_name ez_name; -struct ez_string_node { - struct ez_node ln; +struct ez_name { + struct ez_name *succ; + struct ez_name *pred; char *name; }; #include // see also ez-set.h ez_string_node_hash() -static __inline__ int ez_string_node_equals(const void *a, const void *b) { - return strcmp(((struct ez_string_node *)a)->name, ((struct ez_string_node *)b)->name) == 0; +static __inline__ int ez_name_equals(const void *a, const void *b) { + return strcmp(((struct ez_name *)a)->name, ((struct ez_name *)b)->name) == 0; } -static __inline__ int ez_string_node_cmp(const void *a, const void *b) { - return strcmp(((struct ez_string_node *)a)->name, ((struct ez_string_node *)b)->name); +static __inline__ int ez_name_cmp(const void *a, const void *b) { + return strcmp(((struct ez_name *)a)->name, ((struct ez_name *)b)->name); } /** @@ -155,7 +156,8 @@ static __inline__ int ez_string_node_cmp(const void *a, const void *b) { typedef struct ez_pair ez_pair; struct ez_pair { - struct ez_node ln; + struct ez_pair *succ; + struct ez_pair *pred; char *name; char *value; }; diff --git a/ez-set.h b/ez-set.h index b9be90f..36ff86e 100644 --- a/ez-set.h +++ b/ez-set.h @@ -240,8 +240,8 @@ static void *ez_set_scan_get(ez_set_scan *scan) __attribute__((always_inline)); /** * Extended node - string node for hash set. */ -static __inline__ unsigned int ez_string_node_hash(void *n) { - return ez_hash_string(((struct ez_string_node *)n)->name); +static __inline__ unsigned int ez_name_hash(void *n) { + return ez_hash_string(((struct ez_name *)n)->name); } #endif diff --git a/ez-string.c b/ez-string.c new file mode 100644 index 0000000..572f017 --- /dev/null +++ b/ez-string.c @@ -0,0 +1,542 @@ +/* ez-string.c: String builder/allocator/iterator. + + Copyright (C) 2024 Michael Zucchi + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see + . +*/ + +#include +#include +#include +#include +#include + +#include + +#include "ez-list.h" +#include "ez-string.h" + +#define D(x) + +/* + Fixed + Accesses an existing buffer for reading or writing. Addresses are persistant. + + Grow + A buffer is allocated and re-allocated as necessary for writing. Addresses + are not persistant. Can be used for reading. + + Chunk + A series of buffers are allocated as necessary. Addresses are persistant. + Can only be used in write-only mode. + + */ + +static void ez_string_init(struct ez_string * __restrict s, const struct ez_string_class *class) { + s->class = (struct ez_string_class *)class; + s->pos = NULL; + s->end = NULL; + s->data = NULL; + s->error = 0; + ez_list_init(&s->hunks); +} + +/* ********************************************************************** */ +/* fixed size input/output */ + +static void *fixed_reserve(struct ez_string * __restrict s, size_t size) { + // write out + // error? flush? + return NULL; +} + +static void *fixed_take(struct ez_string * __restrict s, size_t size) { + // error? fill? + return NULL; +} + +static void fixed_truncate(struct ez_string * __restrict s, void *p) { +} + +const struct ez_string_class fixed_class = { + fixed_reserve, + fixed_take, + fixed_truncate, +}; + +void ez_string_init_fixed(struct ez_string * __restrict s, char *data, size_t size) { + ez_string_init(s, &fixed_class); + s->pos = s->data = data; + s->end = data + size; +} + +/* ********************************************************************** */ +/* grow mode - a single buffer is grown as necessary */ + +static void *grow_reserve(struct ez_string * __restrict s, size_t size) { + size_t pos = s->data ? s->pos - s->data : 0; + size_t new = s->data ? s->end - s->data : EZ_STRING_HUNK_SIZE; + char *data; + + while (new - pos < size) + new = new + (new >> 1); + + data = realloc(s->data, sizeof(struct ez_hunk) + new); + if (data) { + s->data = data; + s->end = data + new; + s->pos = data + pos + size; + + return data + pos; + } else { + s->error = errno; + return NULL; + } +} + +static void *grow_take(struct ez_string * __restrict s, size_t size) { + // Error + return NULL; +} + +static void grow_truncate(struct ez_string * __restrict s, void *p) { + if ((char *)p < s->end && (char *)p >= s->data) { + s->pos = p; + } else { + // error? + } +} + +const struct ez_string_class grow_class = { + grow_reserve, + grow_take, + grow_truncate, +}; + +void ez_string_init_grow(struct ez_string * __restrict s) { + ez_string_init(s, &grow_class); +} + +/* ********************************************************************** */ +/* basically an obstack - growable in steps and addresses static */ + +static void *chunk_reserve(struct ez_string * __restrict s, size_t size) { + size_t new = size <= EZ_STRING_HUNK_THRESHOLD ? EZ_STRING_HUNK_SIZE : size; + size_t keep = 0; + struct ez_hunk *h, *t; + + t = ez_list_tail(&s->hunks); + if (t->ln.pred) { + keep = s->pos - t->pos; + new += keep; + while (new < size) + new = new + (new >> 1); + if (t->pos == t->data) { + //printf("chunk grow %zd -> %zd\n", keep, new); + ez_node_remove(t); + h = realloc(t, sizeof(struct ez_hunk) + new); + if (!h) { + ez_list_addtail(&s->hunks, t); + s->error = ENOMEM; + return NULL; + } + goto init; + } + + //printf("chunk save %zd\n", t->pos - t->data); + } + + //printf("chunk append %zd + keep=%zd + new=%zd\n", new, keep, size); + + h = malloc(sizeof(struct ez_hunk) + new + keep); + if (!h) { + s->error = ENOMEM; + return NULL; + } + + if (keep) + memcpy(h->data, t->pos, keep); + +init: + ez_list_addtail(&s->hunks, h); + + h->pos = h->data; + s->pos = h->data + size + keep; + s->data = h->data; + s->end = h->end = h->data + new; + + return h->data + keep; +} + +static void *chunk_take(struct ez_string * __restrict s, size_t size) { + // error? fill? + return NULL; +} + +static void chunk_truncate(struct ez_string * __restrict s, void *p) { +} + +const struct ez_string_class chunk_class = { + chunk_reserve, + chunk_take, + chunk_truncate, +}; + +void ez_string_init_chunk(struct ez_string * __restrict s) { + ez_string_init(s, &chunk_class); +} + +/* free all the hunks, should it leave one behind? */ +void ez_string_chunk_reset(struct ez_string * __restrict s) { + D(printf("%p: reset\n", s)); + for (struct ez_hunk *w = ez_list_head(&s->hunks), *n = ez_node_succ(w);n;w=n,n = ez_node_succ(w)) { + D(printf("%p: free hunk %p - %p\n", w, w->data, w->end)); + free(w); + } + ez_string_init_chunk(s); +} + +size_t ez_string_chunk_size(struct ez_string * __restrict s) { + struct ez_hunk *t = ez_list_tail(&s->hunks); + + return t == (struct ez_hunk *)&s->hunks ? 0 : s->pos - t->pos; +} + +void *ez_string_chunk_end(struct ez_string * __restrict s, size_t *size) { + struct ez_hunk *h = ez_list_tail(&s->hunks); + + //if (h->ln.pred) { + if (h != (struct ez_hunk *)(&s->hunks)) { + void *mem = h->pos; + + *size = s->pos - h->pos; + h->pos = s->pos; + + return mem; + } else + return NULL; +} + +void *ez_string_chunk_end0(struct ez_string * __restrict s) { + size_t size; + + ez_string_putc(s, 0); + + return ez_string_chunk_end(s, &size); +} + +void *ez_string_chunk_alloc(struct ez_string * __restrict s, size_t size) { + size_t sizep; + + ez_string_reserve(s, size); + + return ez_string_chunk_end(s, &sizep); +} + +// output + +#ifndef EZ_STRING_INLINE +void *ez_string_reserve(struct ez_string * __restrict s, size_t size) { + char *pos = s->pos; + char *new = pos + size; + + if (new < pos) { + s->error = EINVAL; + return NULL; + } + + if (new <= s->end) { + s->pos = new; + return pos; + } else { + return s->class->reserve(s, size); + } +} +#endif + +void *ez_string_put(struct ez_string * __restrict s, const void *src, size_t size) { + void *dst = ez_string_reserve(s, size); + + if (dst) + memcpy(dst, src, size); + + return dst; +} + +void *ez_string_putc(struct ez_string * __restrict s, char c) { + char *pos = s->pos; + if (pos < s->end) + *s->pos++ = c; + else { + pos = s->class->reserve(s, 1); + if (pos) + *pos = c; + } + return pos; +} + +int ez_string_print(struct ez_string * __restrict s, const char *src) { + char *dst = ez_string_put(s, src, strlen(src)); + + return dst != NULL ? 0 : -1; +} + +static int inline bitflagged(int c) { + return (1 << (c - ' ')); +} + +static int inline isflagged(int x, int c) { + return (x & (1 << (c - ' '))) != 0; +} + +static const char hex[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; +typedef struct udiv_t { + uint32_t quot, rem; +} udiv_t; + +#define HAVE_DIVMOD + +#if defined(HAVE_DIVMOD) +udiv_t udiv10(uint32_t num) { + return (udiv_t){ num / 10, num % 10 }; +} +#elif defined(HAVE_MULH) +// ESP32C3 has mulh, this is a bit faster +udiv_t udiv10(uint32_t num) { + uint32_t q = (uint64_t)num * 0x19999999 >> 32; + uint32_t r = num - q * 10; + + if (r >= 10) { + r -= 10; + q += 1; + } + + return (udiv_t){ q, r }; +} +#else +// ESP8266 doesn't have mulh +udiv_t udiv10(uint32_t num) { + uint16_t ah = num >> 16; + uint16_t al = num; + uint16_t bh = 0x1999; + uint16_t bl = 0x9999; + + uint32_t c = ah * bh; + uint32_t d = ah * bl; + uint32_t e = al * bh; + uint32_t f = al * bl; + + uint32_t t = d + e + (f >> 16); + + uint32_t q = c + (t >> 16); + uint32_t r = num - q * 10; + + if (r >= 10) { + r -= 10; + q += 1; + } + + return (udiv_t){ q, r }; +} +#endif + +//#pragma GCC diagnostic push +//#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" + +/* + what interface for this? +*/ +int ez_string_printf(struct ez_string * __restrict iob, const char *fmt, ...) { + va_list ap; + char c; + + va_start(ap, fmt); + while ((c = *fmt++)) { + if (c != '%') { + fchar: + ez_string_putc(iob, c); + continue; + } + c = *fmt++; + if (c == '%') + goto fchar; + + const char *s = ""; + int slen = 0; + int width = 0; + int flag = 0; + uint32_t u; + int32_t d; +#ifdef EZ_STRING_FORMAT64 + uint64_t U; + int64_t D; +#endif + char pad = ' '; + int lpad = 0, rpad = 0; + char work[32]; + char *w = work + sizeof(work); + char sign = '+'; + + while (c >= ' ' && c <= '0') { + flag |= bitflagged(c); + c = *fmt++; + } + while (c >= '0' && c <= '9') { + width = width * 10 + (c - '0'); + c = *fmt++; + } + + switch (c) { + case 0: + // truncated % sequence + goto done; + case 'd': + d = va_arg(ap, int32_t); + if (d < 0) { + sign = '-'; + flag |= bitflagged('+'); + u = -d; + } else { + u = d; + } + goto fdec; + case 'u': + u = va_arg(ap, uint32_t); + fdec: + do { + udiv_t r = udiv10(u); + *--w = hex[r.rem]; + u = r.quot; + } while (u > 0); + + if (isflagged(flag, '0') & !isflagged(flag, '-')) { + pad = '0'; + if (isflagged(flag, '+')) { + width -= 1; + ez_string_putc(iob, sign); + } + } else { + if (isflagged(flag, '+')) + *--w = sign; + } + + s = w; + slen = work + sizeof(work) - w; + break; + case 'x': + u = va_arg(ap, uint32_t); + do { + *--w = hex[u & 0xf]; + u >>= 4; + } while (u > 0); + + if (isflagged(flag, '0')) + pad = '0'; + + s = w; + slen = work + sizeof(work) - w; + break; +#ifdef EZ_STRING_FORMAT64 + case 'X': // 64-bit versions? + U = va_arg(ap, uint64_t); + u = U >> 32; + do { + *--w = hex[u & 0xf]; + u >>= 4; + } while (u > 0); + u = U; + goto fhex; + case 'D': + D = va_arg(ap, int64_t); + if (D < 0) { + sign = '-'; + U = -D; + } else { + U = D; + } + goto fdec64; + case 'U': + U = va_arg(ap, uint64_t); + fdec64: + while (U >> 16) { + *--w = (U % 10) + '0'; + U / = 10; + } + u = U; + goto fdec; +#endif + case 's': + s = va_arg(ap, const char *); + slen = strlen(s); + break; + } + + if (width > slen) { + if (isflagged(flag, '-')) + rpad = width - slen; + else + lpad = width - slen; + } + while (lpad--) + ez_string_putc(iob, pad); + ez_string_put(iob, s, slen); + while (rpad--) + ez_string_putc(iob, pad); + } +done: + va_end(ap); + + return 0; +} +//#pragma GCC diagnostic pop + + +// input +#ifndef EZ_STRING_INLINE +void *ez_string_take(struct ez_string * __restrict s, size_t size) { + char *pos = s->pos; + char *new = pos + size; + + if (new < pos) { + s->error = EINVAL; + return NULL; + } + + if (new <= s->end) { + s->pos = new; + return pos; + } else { + return s->class->take(s, size); + } +} +#endif + +int ez_string_get(struct ez_string * __restrict s, char *dst, size_t len) { + size_t max = s->end - s->pos; + + if (max < len) + len = max; + memcpy(dst, ez_string_take(s, len), len); + return len; +} + +int ez_string_getc(struct ez_string * __restrict s) { + if (s->pos < s->end) { + return *s->pos++; + } else { + char *p = s->class->take(s, 1); + return p ? *(unsigned char *)p : -1; + } +} diff --git a/ez-string.h b/ez-string.h new file mode 100644 index 0000000..88f5d56 --- /dev/null +++ b/ez-string.h @@ -0,0 +1,186 @@ +/* ez-string.h: String builder/allocator/iterator. + + Copyright (C) 2024 Michael Zucchi + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see + . +*/ + +#ifndef EZ_STRING_H +#define EZ_STRING_H + +//#define EZ_STRING_INLINE +#define EZ_UNALIGNED_ACCESS + +struct ez_string; + +struct ez_hunk { + struct ez_node ln; + char *pos; + char *end; + char data[]; +}; + +//#define EZ_STRING_HUNK_SIZE (4096-4*sizeof(void*)) +#define EZ_STRING_HUNK_SIZE (256-4*sizeof(void*)) +#define EZ_STRING_HUNK_THRESHOLD (EZ_STRING_HUNK_SIZE*3/2) + +struct ez_string_class { + // Reserve for writing/flush + void *(*reserve)(struct ez_string * __restrict, size_t); + // Reserve for reading/fill + void *(*take)(struct ez_string * __restrict, size_t); + // free back till address + void (*truncate)(struct ez_string * __restrict, void *p); +}; + +// A memory buffer, but could also work as an i/o buffer +struct ez_string { + struct ez_string_class *class; + char *pos; /* current output pos */ + char *end; + char *data; + int error; /* internal error, not sure about this mechanism */ + ez_list hunks; +}; + +void *ez_string_put(struct ez_string * __restrict s, const void *src, size_t size); +void *ez_string_putc(struct ez_string * __restrict s, char c); +int ez_string_print(struct ez_string * __restrict s, const char *src); +int ez_string_printf(struct ez_string * __restrict s, const char *fmt, ...); + +int ez_string_get(struct ez_string * __restrict s, char *dst, size_t len); +int ez_string_getc(struct ez_string * __restrict s); + +void ez_string_init_fixed(struct ez_string * __restrict s, char *data, size_t size); + +void ez_string_init_grow(struct ez_string * __restrict s); + +// TODO: rename hunk/chunk +void ez_string_init_chunk(struct ez_string * __restrict s); +void ez_string_chunk_reset(struct ez_string * __restrict s); +size_t ez_string_chunk_size(struct ez_string * __restrict s); +void *ez_string_chunk_end(struct ez_string * __restrict s, size_t *size); +void *ez_string_chunk_end0(struct ez_string * __restrict s); + +void *ez_string_chunk_alloc(struct ez_string * __restrict s, size_t size); + +#ifndef EZ_STRING_INLINE + +void *ez_string_reserve(struct ez_string * __restrict s, size_t size); +void *ez_string_take(struct ez_string * __restrict s, size_t size); + +#else + +static inline void *ez_string_reserve(struct ez_string * __restrict s, size_t size) { + char *pos = s->pos; + char *new = pos + size; + + // maybe let it crash + if (new < pos) { + s->error = EINVAL; + return NULL; + } + + if (new <= s->end) { + s->pos = new; + return pos; + } else { + return s->class->reserve(s, size); + } +} + +static inline void *ez_string_take(struct ez_string * __restrict s, size_t size) { + char *pos = s->pos; + char *new = pos + size; + + if (new < pos) { + s->error = EINVAL; + return NULL; + } + + if (new <= s->end) { + s->pos = new; + return pos; + } else { + return s->class->take(s, size); + } +} + +#endif + +static inline size_t ez_string_left(struct ez_string * __restrict s) { + return s->end - s->pos; +} + +static inline void ez_string_grow_reset(struct ez_string * __restrict s) { + s->pos = s->data; +} + +static inline size_t ez_string_grow_size(struct ez_string * __restrict s) { + return s->pos - s->data; +} + +static inline void ez_string_put_align(struct ez_string * __restrict s, unsigned int a) { + intptr_t l = a - ((intptr_t)s->pos & (a - 1)); + void *p = ez_string_reserve(s, l); + if (p) memset(p, 0, l); +} + +static inline void ez_string_get_align(struct ez_string * __restrict s, unsigned int a) { + intptr_t l = a - ((intptr_t)s->pos & (a - 1)); + + ez_string_take(s, l); +} + +/* binary i/o */ + +#ifdef EZ_UNALIGNED_ACCESS +#define EZ_STRING_ACCESS(name, type) \ +static inline void ez_string_put ## name \ +(struct ez_string * __restrict s, type v) { \ + type *p = ez_string_reserve(s, sizeof(v)); \ + if (p) *p = v; \ +} \ + \ +static inline type ez_string_get ## name \ +(struct ez_string * __restrict s) { \ + type *p = ez_string_take(s, sizeof(type)); \ + return p ? *p : 0; \ +} +#else +#define EZ_STRING_ACCESS(name, type) \ +static inline void ez_string_put ## name \ +(struct ez_string * __restrict s, type v) { \ + type *p = ez_string_reserve(s, sizeof(v)); \ + if (p) memcpy(p, &v, sizeof(v)); \ +} \ + \ +static inline type ez_string_get ## name \ +(struct ez_string * __restrict s) { \ + type *p = ez_string_take(s, sizeof(type)); \ + type v = 0; \ + if (p) memcpy(&v, p, sizeof(v)); \ + return v; \ +} +#endif + +EZ_STRING_ACCESS(u8, uint8_t) +EZ_STRING_ACCESS(u16, uint16_t) +EZ_STRING_ACCESS(u32, uint64_t) +EZ_STRING_ACCESS(u64, uint64_t) +EZ_STRING_ACCESS(f32, float) +EZ_STRING_ACCESS(f64, double) + +#endif diff --git a/test-blob.c b/test-blob.c index e1a7588..fb83f98 100644 --- a/test-blob.c +++ b/test-blob.c @@ -14,8 +14,6 @@ #include "ez-blob-xdrn.h" #include "ez-blob-basic.h" -#include "ez-blob-io.h" - /* TODO: some bad data tests. diff --git a/test-http.c b/test-http.c index ec830de..4cf9679 100644 --- a/test-http.c +++ b/test-http.c @@ -1,89 +1,163 @@ -#define _GNU_SOURCE - +#include #include #include +#include #include +#include +#include +#include +#include -#include -#include "ez-blob-io.h" #include "ez-list.h" -#include "ez-set.h" -#define obstack_chunk_alloc malloc -#define obstack_chunk_free free -#include -#include -#include #define D(x) -#include "ez-tree.h" +#include "ez-string.h" #include "ez-http.h" +static sem_t server_sem; + // copy to use const static struct ez_pair ct_text_html = { .name = "Content-Type", .value = "text/html;charset=utf-8" }; +#if 0 const static struct ez_pair ct_text_xml = { .name = "Content-Type", .value = "text/xml" }; +#endif void demo_client(void) { - const char *cmd = "-3501dB"; struct ez_httpclient c; - struct ez_pair h0 = ct_text_xml; + struct ez_pair *h; + + printf("client: GET /\n"); + httpclient_init(&c, "localhost", 8081); + httpclient_run(&c, "GET", "/"); + printf("client response: %s\n", c.response.http.status); + for (struct ez_pair *w = ez_list_head(&c.response.http.headers), *n = ez_node_succ(w);n;w=n,n = ez_node_succ(w)) + printf(" %s:%s\n", w->name, w->value); - httpclient_init(&c, "amplifier", 80); + assert(c.response.code == 301); + assert((h = ez_list_find_name(&c.response.http.headers, "Location"))); + printf("client: Location='%s'\n", h->value); + assert(strcmp(h->value, "/index.html") == 0); + httpclient_clear(&c); + printf("client: GET /index.html\n"); + httpclient_run(&c, "GET", "/index.html"); + printf("client response: %s\n", c.response.http.status); + for (struct ez_pair *w = ez_list_head(&c.response.http.headers), *n = ez_node_succ(w);n;w=n,n = ez_node_succ(w)) + printf(" %s:%s\n", w->name, w->value); + + assert(c.response.code == 200); + // TODO: content too + httpclient_clear(&c); + + printf("client: GET /quit\n"); + httpclient_run(&c, "GET", "/quit"); + printf("client response: %s\n", c.response.http.status); + for (struct ez_pair *w = ez_list_head(&c.response.http.headers), *n = ez_node_succ(w);n;w=n,n = ez_node_succ(w)) + printf(" %s:%s\n", w->name, w->value); + + assert(c.response.code == 200); + + httpclient_free(&c); + +#if 0 http_addheaders(&c.request.http, &h0, 1); http_set_data(&c.request.http, (char *)cmd, strlen(cmd)); - httpclient_run(&c, "POST", "/YamahaRemoteControl/ctrl"); - printf("response: %s\n", c.response.http.status); for (struct ez_pair *w = ez_list_head(&c.response.http.headers), *n = ez_node_succ(w);n;w=n,n = ez_node_succ(w)) printf("%s:%s\n", w->name, w->value); printf("payload:\n"); - fwrite(c.response.http.data.data + c.response.http.data.index, 1, c.response.http.data.size - c.response.http.data.index, stdout); + fwrite(c.response.http.data.data, 1, c.response.http.data.end - c.response.http.data.data, stdout); printf("\n--\n"); - - httpclient_free(&c); +#endif } -static int handle_quit(struct ez_httprequest *r, struct ez_httpresponse *rep) { +static int handle_quit(struct ez_httprequest *req, struct ez_httpresponse *rep) { httpresponse_set_response(rep, 200, "Byte"); return 1; } static int handle_root(struct ez_httprequest *req, struct ez_httpresponse *rep) { - static struct ez_pair h0 = ct_text_html; + struct ez_string *os = &rep->http.conn->os; + struct ez_pair *h; + + httpresponse_set_response(rep, 301, "Moved Permanently"); + + h = ez_string_chunk_alloc(os, sizeof(*h)); + h->name = "Location"; + h->value = "/index.html"; + ez_list_addtail(&rep->http.headers, h); + + return 0; +} + +static int handle_index(struct ez_httprequest *req, struct ez_httpresponse *rep) { + struct ez_string *os = &rep->http.conn->os; + struct ez_pair *h; const char *msg = "

It Works!

Quit | Player"; httpresponse_set_response(rep, 200, "Ok"); - ez_list_addtail(&rep->http.headers, &h0); - http_set_data(&rep->http, msg, strlen(msg)); + + h = ez_string_chunk_alloc(os, sizeof(*h)); + *h = ct_text_html; + ez_list_addtail(&rep->http.headers, h); + + http_set_data(&rep->http, (char *)msg, strlen(msg)); return 0; } static struct ez_httphandler handler_list[] = { - { .path = "/index.html", .fn = handle_root }, + { .path = "/", .fn = handle_root }, + { .path = "/index.html", .fn = handle_index }, { .path = "/quit", .fn = handle_quit }, }; -void demo_server(void) { +static void *demo_server(void *d) { struct ez_httpserver serv; httpserver_init(&serv, 8081); - httpserver_addhandlers(&serv, handler_list, 2); + httpserver_addhandlers(&serv, handler_list, 3); + + sem_post(&server_sem); + httpserver_run(&serv); httpserver_free(&serv); + + sem_post(&server_sem); + + return NULL; } int main(int argc, char **argv) { + pthread_t sid; + + sem_init(&server_sem, 0, 0); + pthread_create(&sid, NULL, demo_server, NULL); + sem_wait(&server_sem); + + sleep(1); demo_client(); - //demo_server(); + + int waitcount = 0; + while (sem_trywait(&server_sem) != 0) { + printf("Waiting for server to quit\n"); + sleep(1); + waitcount++; + if (waitcount == 2) { + printf("Server didn't quit\n"); + abort(); + } + } + + pthread_join(sid, NULL); return 0; }