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.
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
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 \
ez-http.c \
ez-port.c \
ez-set.c \
+ ez-string.c \
ez-tree.c
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 \
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=)
check: tests
for test in $(TESTS) ; do \
echo $$test ; \
- ./$$test || exit 1; \
+ valgrind --error-exitcode=1 ./$$test || exit 1; \
done
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
+++ /dev/null
-/* 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
- <http://www.gnu.org/licenses/>.
-*/
-
-#include <stdio.h>
-#include <stdint.h>
-#include <string.h>
-#include <stdlib.h>
-#include <errno.h>
-
-#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
+++ /dev/null
-/* 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
- <http://www.gnu.org/licenses/>.
-*/
-
-// 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
#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;
#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)
#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;
}
}
}
// 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;i<dlen;i++) {
const ez_blob_desc *d = &desc[i+1];
if (val) {
int sc = size_code(val);
- blobio_writet(io, EZT_INT8 | (sc << EZT_DATASHIFT), d->bd_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:
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;
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;
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?
}
- 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;
}
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;
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
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);
/* 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);
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:
case EZT_FLOAT32:
case EZT_FLOAT64:
if (use_count) {
- blobio_take(io, fcount * (1<<sc));
+ ez_string_take(io, fcount * (1<<sc));
} else {
- blobio_take(io, (1<<sc));
+ ez_string_take(io, (1<<sc));
}
break;
case EZT_STRUCT:
}
}
-static void tagz_decode_struct(struct ez_blobio *io, const ez_blob_desc *desc, void *p) {
- uint8_t h = blobio_readb(io);
+static void tagz_decode_struct(struct ez_string *io, const ez_blob_desc *desc, void *p) {
+ uint8_t h = tagz_getb(io);
uint32_t stag;
int tc = (h >> EZT_TAGSHIFT) & 3;
int cc = (h >> EZT_COUNTSHIFT) & 3;
}
// 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);
/* ********************************************************************** */
// 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;
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];
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;
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:
case EZT_INT32:
case EZT_INT64:
if (use_count) {
- void *v = blobio_take(io, fcount * (1<<sc));
+ void *v = ez_string_take(io, fcount * (1<<sc));
if (v) {
ez_blob blob = { .eb_size = fcount * (1<<sc), .eb_data = v };
printf("%s[%02x] integer-%-2d %d [%ld] = {\n", s, h, (1<<sc)*8, ftag, fcount);
printf("%s}\n", s);
}
} else {
- printf("%s[%02x] integer-%-2d %d = %02lx\n", s, h, (1<<sc)*8, ftag, blobio_readi(io, sc));
+ printf("%s[%02x] integer-%-2d %d = %02lx\n", s, h, (1<<sc)*8, ftag, tagz_getux(io, sc));
}
break;
case EZT_FLOAT32:
if (use_count) {
printf("%sfloat %d [%ld] = {\n", s, ftag, fcount);
for (int i=0;i<fcount;i++)
- printf(" %f\n", blobio_readf(io));
+ printf(" %f\n", ez_string_getf32(io));
printf("%s}\n", s);
} else {
- printf("%sfloat %d = %f\n", s, ftag, blobio_readf(io));
+ printf("%sfloat %d = %f\n", s, ftag, ez_string_getf32(io));
}
break;
case EZT_FLOAT64:
if (use_count) {
printf("%s%d double[%ld] = {\n", s, ftag, fcount);
for (int i=0;i<fcount;i++)
- printf("%s %f\n", s, blobio_readd(io));
+ printf("%s %f\n", s, ez_string_getf64(io));
printf("%s}\n", s);
} else {
- printf("%s%d double = %f\n", s, ftag, blobio_readd(io));
+ printf("%s%d double = %f\n", s, ftag, ez_string_getf64(io));
}
break;
case EZT_STRUCT:
break;
}
}
- printf("%s[%02x] END $%04lx\n", s, h, io->index-1);
+ printf("%s[%02x] END $%04lx\n", s, h, (io->pos - io->data) - 1);
printf("%s}\n", s);
}
* 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);
}
#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) {
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);
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);
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;i<dlen;i++) {
const ez_blob_desc *d = &desc[i+1];
const void *v = (p + d->bd_offset);
* 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;
/*
*/
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);
*/
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;
*/
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;
}
// 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);
}
}
}
-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;i<dlen;i++) {
+ const ez_blob_desc *d = &desc[i+1];
+ const void *v = (p + d->bd_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;
}
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;i<dlen;i++) {
const ez_blob_desc *d = &desc[i+1];
void *v = (p + d->bd_offset);
* 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;
/*
* 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:
* 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:
* 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:
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;j<count;j++) {
void *node = ez_blob_alloc(d->bd_table);
}
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);
#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);
<http://www.gnu.org/licenses/>.
*/
-#define _GNU_SOURCE
-
#include <stdlib.h>
#include <string.h>
+#include <stddef.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
-#include <arpa/inet.h>
#include <ctype.h>
#include <sys/uio.h>
#include <netdb.h>
+#ifdef HAVE_SIGNAL
#include <signal.h>
+#endif
#include <stdlib.h>
-#include "ez-blob-io.h"
-#include "ez-list.h"
-#include "ez-tree.h"
+#include <stdarg.h>
-#define obstack_chunk_alloc malloc
-#define obstack_chunk_free free
-#include <obstack.h>
#include <stdio.h>
#include <errno.h>
+
#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));
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);
}
/*
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;
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;
}
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;
}
return c - '0';
else if (c <= 'F')
return c - 'A' + 10;
- else
+ else // this is out of spec
return c - 'a' + 10;
}
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);
}
}
} 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 {
}
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);
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;
}
// 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;
}
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;
}
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;i<count;i++)
- ez_tree_put(&s->handlers, &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");
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;
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
httpconnection_free(&conn);
res = 0;
fail:
+#ifdef HAVE_SIGNAL
signal(SIGPIPE, SIG_DFL);
-
+#endif
close(s->fd);
s->fd = -1;
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);
}
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
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));
}
// 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);
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
};
* 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;
};
/**
* 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.
* 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;
*/
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.
*/
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
/**
* 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 <string.h>
// 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);
}
/**
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;
};
/**
* 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
--- /dev/null
+/* 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
+ <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#include <errno.h>
+
+#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;
+ }
+}
--- /dev/null
+/* 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
+ <http://www.gnu.org/licenses/>.
+*/
+
+#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
#include "ez-blob-xdrn.h"
#include "ez-blob-basic.h"
-#include "ez-blob-io.h"
-
/*
TODO: some bad data tests.
-#define _GNU_SOURCE
-
+#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <unistd.h>
#include <netdb.h>
+#include <pthread.h>
+#include <semaphore.h>
+#include <errno.h>
+#include <assert.h>
-#include <stdlib.h>
-#include "ez-blob-io.h"
#include "ez-list.h"
-#include "ez-set.h"
-#define obstack_chunk_alloc malloc
-#define obstack_chunk_free free
-#include <obstack.h>
-#include <stdio.h>
-#include <errno.h>
#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 = "<YAMAHA_AV cmd=\"PUT\"><Main_Zone><Volume><Lvl><Val>-350</Val><Exp>1</Exp><Unit>dB</Unit></Lvl></Volume></Main_Zone></YAMAHA_AV>";
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 = "<h1>It Works!</h1><a href='/quit'>Quit</a> | <a href='/'>Player</a>";
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;
}