Added ez-string. master
authorNot Zed <notzed@gmail.com>
Tue, 23 Jul 2024 09:28:51 +0000 (18:58 +0930)
committerNot Zed <notzed@gmail.com>
Tue, 23 Jul 2024 10:21:48 +0000 (19:51 +0930)
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.

16 files changed:
Makefile
ez-blob-io.c [deleted file]
ez-blob-io.h [deleted file]
ez-blob-print.c
ez-blob-tagz.c
ez-blob-xdrn.c
ez-blob-xdrn.h
ez-http.c
ez-http.h
ez-list.h
ez-node.h
ez-set.h
ez-string.c [new file with mode: 0644]
ez-string.h [new file with mode: 0644]
test-blob.c
test-http.c

index 66a86a8..334eac6 100644 (file)
--- 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 (file)
index ce26327..0000000
+++ /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
-   <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
diff --git a/ez-blob-io.h b/ez-blob-io.h
deleted file mode 100644 (file)
index 1c01620..0000000
+++ /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
-   <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
index 4757453..83a2f15 100644 (file)
@@ -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;
index 80b4bc9..b0acdb8 100644 (file)
@@ -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;i<dlen;i++) {
                const ez_blob_desc *d = &desc[i+1];
@@ -227,17 +227,17 @@ static void tagz_encode_struct(struct ez_blobio *io, int id, const ez_blob_desc
                        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:
@@ -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<<sc));
+                                       ez_string_take(io, fcount * (1<<sc));
                                } else {
-                                       blobio_take(io, (1<<sc));
+                                       ez_string_take(io, (1<<sc));
                                }
                                break;
                        case EZT_STRUCT:
@@ -555,8 +555,8 @@ static void tagz_decode_fields(struct ez_blobio *io, const ez_blob_desc *desc, v
        }
 }
 
-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;
@@ -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<<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);
@@ -674,27 +671,27 @@ static void tagz_dump_fields(struct ez_blobio *io, int depth) {
                                        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:
@@ -713,7 +710,7 @@ static void tagz_dump_fields(struct ez_blobio *io, int depth) {
                        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);
 }
 
@@ -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);
 }
index 4c6d144..3da274e 100644 (file)
@@ -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;i<dlen;i++) {
                const ez_blob_desc *d = &desc[i+1];
                const void *v = (p + d->bd_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;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;
        }
@@ -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;i<dlen;i++) {
                const ez_blob_desc *d = &desc[i+1];
                void *v = (p + d->bd_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;j<count;j++) {
                                void *node = ez_blob_alloc(d->bd_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);
 
index 0cfa77c..fe3e860 100644 (file)
@@ -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);
index b804f27..3978752 100644 (file)
--- a/ez-http.c
+++ b/ez-http.c
   <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));
@@ -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;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");
@@ -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);
index d2417fb..92a4634 100644 (file)
--- 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.
  */
index d3494eb..d6a9fa9 100644 (file)
--- 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
index 9664290..32a5de1 100644 (file)
--- 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 <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);
 }
 
 /**
@@ -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;
 };
index b9be90f..36ff86e 100644 (file)
--- 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 (file)
index 0000000..572f017
--- /dev/null
@@ -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
+  <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;
+       }
+}
diff --git a/ez-string.h b/ez-string.h
new file mode 100644 (file)
index 0000000..88f5d56
--- /dev/null
@@ -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
+  <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
index e1a7588..fb83f98 100644 (file)
@@ -14,8 +14,6 @@
 #include "ez-blob-xdrn.h"
 #include "ez-blob-basic.h"
 
-#include "ez-blob-io.h"
-
 /*
   TODO: some bad data tests.
 
index ec830de..4cf9679 100644 (file)
 
-#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;
 }