From adec5584e13c63662fda18915280ec026063b29d Mon Sep 17 00:00:00 2001 Message-Id: From: Mark Wooding Date: Sat, 9 Mar 2024 23:20:43 +0000 Subject: [PATCH] @@@ man wip Organization: Straylight/Edgeware From: Mark Wooding --- buf/lbuf.3 | 11 +- buf/pkbuf.3 | 13 +- codec/base64.3 | 48 +-- codec/codec.3 | 29 +- codec/url.3 | 57 ++-- hash/t/hash-test.c | 11 +- hash/unihash.3 | 5 +- mem/Makefile.am | 4 + mem/arena.3 | 14 +- mem/growbuf.3 | 98 ++++++ mem/growbuf.h | 137 ++++++++ mem/pool.3 | 15 +- sel/bres.3 | 23 +- sel/conn.3 | 18 +- sel/ident.3 | 39 +-- sel/sel.3 | 49 +-- sel/selbuf.3 | 6 +- sel/selpk.3 | 6 +- sel/sig.3 | 6 +- struct/buf-putf.c | 4 +- struct/buf.3 | 785 ++++++++++++++++++++++++++++++++++++++++++++- struct/buf.c | 56 ++-- struct/buf.h | 41 ++- struct/darray.3 | 7 +- struct/darray.c | 9 +- struct/dstr-putf.c | 4 +- struct/dstr.c | 25 +- struct/hash.3 | 11 +- struct/sym.3 | 8 +- sys/fdflags.3 | 8 +- sys/lock.3 | 7 +- sys/mdup.3 | 35 +- test/t/tvec-test.c | 7 +- test/testrig.3 | 31 +- test/tests.at | 39 +-- test/tvec-core.c | 44 ++- test/tvec-output.c | 20 +- test/tvec-remote.c | 21 +- test/tvec-types.c | 362 +++++++++++++++------ test/tvec.3 | 16 +- test/tvec.h | 75 ++++- trace/trace.3 | 21 +- ui/mdwopt.3 | 41 +-- utils/Makefile.am | 8 +- utils/control.3 | 379 +++++++++++++++------- utils/gprintf.3 | 227 +++++++++++++ utils/gprintf.c | 33 +- utils/gprintf.h | 28 +- utils/linreg.3 | 73 +++++ utils/linreg.h | 2 - utils/maths.3 | 36 +++ 51 files changed, 2380 insertions(+), 672 deletions(-) create mode 100644 mem/growbuf.3 create mode 100644 mem/growbuf.h create mode 100644 utils/gprintf.3 create mode 100644 utils/linreg.3 create mode 100644 utils/maths.3 diff --git a/buf/lbuf.3 b/buf/lbuf.3 index d1663a2..0842cb8 100644 --- a/buf/lbuf.3 +++ b/buf/lbuf.3 @@ -11,18 +11,19 @@ lbuf \- split lines out of asynchronously received blocks .\" @lbuf_destroy .SH "SYNOPSIS" .nf +.ta 2n .B "#include " .B "enum {" -.B "\h'4n'LBUF_CRLF," -.B "\h'4n'LBUF_STRICTCRLF," -.B "\h'4n'..." +.B " LBUF_CRLF," +.B " LBUF_STRICTCRLF," +.B " ..." .B "};" .B "#define LBUF_ENABLE ..." .B "typedef struct {" -.B "\h'4n'unsigned f;" -.B "\h'4n'..." +.B " unsigned f;" +.B " ..." .B "} lbuf;" .B "typedef void lbuf_func(char *" s ", size_t " len ", void *" p ); diff --git a/buf/pkbuf.3 b/buf/pkbuf.3 index 01c4240..50a30fd 100644 --- a/buf/pkbuf.3 +++ b/buf/pkbuf.3 @@ -11,20 +11,21 @@ pkbuf \- split packets out of asynchronously received blocks .\" @pkbuf_destroy .SH "SYNOPSIS" .nf +.ta 2n .B "#include " .B "enum {" -.B "\h'4n'PKBUF_ENABLE = ..." +.B " PKBUF_ENABLE = ..." .B "};" .B "typedef struct {" -.B "\h'4n'unsigned f;" -.B "\h'4n'..." +.B " unsigned f;" +.B " ..." .B "} pkbuf;" -.ds mT \fBtypedef void pkbuf_func( -.B "\*(mToctet *" b ", size_t " sz ", pkbuf *" p , -.BI "\h'\w'\*(mT'u'size_t *" keep ", void *" p ); +.ta \w'\fBtypedef void pkbuf_func('u +.B "typedef void pkbuf_func(octet *" b ", size_t " sz ", pkbuf *" p , +.BI " size_t *" keep ", void *" p ); .BI "void pkbuf_flush(pkbuf *" pk ", octet *" p ", size_t " len ); .BI "void pkbuf_close(pkbuf *" pk ); diff --git a/codec/base64.3 b/codec/base64.3 index 39de50d..9c44d12 100644 --- a/codec/base64.3 +++ b/codec/base64.3 @@ -17,46 +17,52 @@ base64, base32, hex \- obsolete binary encoding functions .B "#include " .B "#include " +.ta 2n .B "typedef struct {" -.B "\h'4n'char *indent;" -.B "\h'4n'unsigned maxline;" -.B "\h'4n'..." +.B " char *indent;" +.B " unsigned maxline;" +.B " ..." .B "} base64_ctx;" +.ta \w'\fBvoid base64_encode('u .BI "void base64_encode(base64_ctx *" ctx , -.BI " const void *" p ", size_t " sz , -.BI " dstr *" d ); +.BI " const void *" p ", size_t " sz , +.BI " dstr *" d ); .BI "void base64_decode(base64_ctx *" ctx , -.BI " const void *" p ", size_t " sz , -.BI " dstr *" d ); +.BI " const void *" p ", size_t " sz , +.BI " dstr *" d ); .BI "void base64_init(base64_ctx *" ctx ); +.ta 2n .B "typedef struct {" -.B "\h'4n'char *indent;" -.B "\h'4n'unsigned maxline;" -.B "\h'4n'..." +.B " char *indent;" +.B " unsigned maxline;" +.B " ..." .B "} base32_ctx;" +.ta \w'\fBvoid base32_encode('u .BI "void base32_encode(base32_ctx *" ctx , -.BI " const void *" p ", size_t " sz , -.BI " dstr *" d ); +.BI " const void *" p ", size_t " sz , +.BI " dstr *" d ); .BI "void base32_decode(base32_ctx *" ctx , -.BI " const void *" p ", size_t " sz , -.BI " dstr *" d ); +.BI " const void *" p ", size_t " sz , +.BI " dstr *" d ); .BI "void base32_init(base32_ctx *" ctx ); +.ta 2n .B "typedef struct {" -.B "\h'4n'char *indent;" -.B "\h'4n'unsigned maxline;" -.B "\h'4n'..." +.B " char *indent;" +.B " unsigned maxline;" +.B " ..." .B "} hex_ctx;" +.ta \w'\fBvoid hex_encode('u .BI "void hex_encode(hex_ctx *" ctx , -.BI " const void *" p ", size_t " sz , -.BI " dstr *" d ); +.BI " const void *" p ", size_t " sz , +.BI " dstr *" d ); .BI "void hex_decode(hex_ctx *" ctx , -.BI " const void *" p ", size_t " sz , -.BI " dstr *" d ); +.BI " const void *" p ", size_t " sz , +.BI " dstr *" d ); .BI "void hex_init(hex_ctx *" ctx ); .fi .SH DESCRIPTION diff --git a/codec/codec.3 b/codec/codec.3 index ee5a447..6f2ccd8 100644 --- a/codec/codec.3 +++ b/codec/codec.3 @@ -29,30 +29,31 @@ codec \- binary encoding and decoding .B "#define CDCF_IGNSPC ..." .B "#define CDCF_IGNJUNK ..." +.ta 2n .B "enum {" -.B "\h'4n'CDCERR_OK = ...," -.B "\h'4n'CDCERR_INVCH = ...," -.B "\h'4n'CDCERR_INVEQPAD = ...," -.B "\h'4n'CDCERR_INVZPAD = ..." +.B " CDCERR_OK = ...," +.B " CDCERR_INVCH = ...," +.B " CDCERR_INVEQPAD = ...," +.B " CDCERR_INVZPAD = ..." .B "};" .B "typedef struct {" -.B "\h'4n'const char *name;" -.ds mT \fBcodec *(*encoder)( -.BI "\h'4n'\*(mTunsigned " flags , -.BI "\h'4n+\w'\*(mT'u'const char *" indent ", unsigned " maxlen ); -.BI "\h'4n'codec *(*decoder)(unsigned " flags ); -.B "\h'4n'...\&" +.B " const char *name;" +.ta 2n +\w'\fBcodec *(*encoder)('u +.BI " codec *(*encoder)(unsigned " flags , +.BI " const char *" indent ", unsigned " maxlen ); +.BI " codec *(*decoder)(unsigned " flags ); +.B " ...\&" .B "} codec_class;" .B "typedef struct {" -.B "\h'4n'const codec_ops *ops;" +.B " const codec_ops *ops;" .B "} codec;" .B "typedef struct {" -.B "\h'4n'const codec_class *c;" -.BI "\h'4n'int (*code)(codec *" c ", const void *" p ", size_t " sz ", dstr *" d ); -.BI "\h'4n'void (*destroy)(codec *" c ); +.B " const codec_class *c;" +.BI " int (*code)(codec *" c ", const void *" p ", size_t " sz ", dstr *" d ); +.BI " void (*destroy)(codec *" c ); .B "} codec_ops;" .B "codec_class null_codec_class;" diff --git a/codec/url.3 b/codec/url.3 index c3b4e98..9e8e880 100644 --- a/codec/url.3 +++ b/codec/url.3 @@ -22,14 +22,15 @@ url \- manipulation of form-urlencoded strings .nf .B "#include " +.ta 2n .B "typedef struct {" -.B "\h'4n'unsigned f;" -.B "\h'4n'..." +.B " unsigned f;" +.B " ..." .B "} url_ectx;" .B "typedef struct {" -.B "\h'4n'unsigned f;" -.B "\h'4n'..." +.B " unsigned f;" +.B " ..." .B "} url_dctx;" .B "#define URLF_STRICT ..." @@ -37,9 +38,9 @@ url \- manipulation of form-urlencoded strings .B "#define URLF_SEMI ..." .BI "void url_initenc(url_ectx *" ctx ); -.ds mT \fBvoid url_enc( -.BI "\*(mTurl_ectx *" ctx ", dstr *" d , -.BI "\h'\w'\*(mT'u'const char *" name ", const char *" value ); +.ta \w'\fBvoid url_enc('u +.BI "void url_enc(url_ectx *" ctx ", dstr *" d , +.BI " const char *" name ", const char *" value ); .BI "void url_initdec(url_dctx *" ctx ", const char *" p ); .BI "int url_dec(url_dctx *" ctx ", dstr *" n ", dstr *" v ); @@ -143,6 +144,7 @@ its use.) The example code below demonstrates converting between a symbol table and a urlencoded representation. The code is untested. .VS +.ta 2n +2n #include #include #include @@ -150,37 +152,36 @@ and a urlencoded representation. The code is untested. #include typedef struct { - sym_base _b; - char *v; + sym_base _b; + char *v; } val; void decode(sym_table *t, const char *p) { - url_dctx c; - dstr n = DSTR_INIT, v = DSTR_INIT; + url_dctx c; + dstr n = DSTR_INIT, v = DSTR_INIT; + val *vv; + unsigned f; - for (url_initdec(&c, p); url_dec(&c, &n, &v); ) { - unsigned f; - val *vv = sym_find(t, n.buf, -1, sizeof(*vv), &f); - if (f) - free(vv->v); - vv->v = xstrdup(v.buf); - DRESET(&n); - DRESET(&v); - } - dstr_destroy(&n); - dstr_destroy(&v); + for (url_initdec(&c, p); url_dec(&c, &n, &v); ) { + vv = sym_find(t, n.buf, -1, sizeof(*vv), &f); + if (f) free(vv->v); + vv->v = xstrdup(v.buf); + DRESET(&n); + DRESET(&v); + } + dstr_destroy(&n); dstr_destroy(&v); } void encode(sym_table *t, dstr *d) { - sym_iter i; - url_ectx c; - val *v; + sym_iter i; + url_ectx c; + val *v; - url_initenc(&c); - for (sym_mkiter(&i, t); (v = sym_next(&i)) != 0; ) - url_enc(&c, d, SYM_NAME(v), v->v); + url_initenc(&c); + for (sym_mkiter(&i, t); (v = sym_next(&i)) != 0; ) + url_enc(&c, d, SYM_NAME(v), v->v); } .VE .SH "SEE ALSO" diff --git a/hash/t/hash-test.c b/hash/t/hash-test.c index 1bb21e9..3c637dc 100644 --- a/hash/t/hash-test.c +++ b/hash/t/hash-test.c @@ -56,9 +56,14 @@ static void test_crc32(const struct tvec_reg *in, struct tvec_reg *out, } } +static void before_hash(struct tvec_state *tv, void *ctx) + { tvec_allocbuffer(&tv->in[RM].v); } + static void bench_crc32(const struct tvec_reg *in, struct tvec_reg *out, void *ctx) { crc32(0, in[RM].v.bytes.p, in[RM].v.bytes.sz); } +static const struct tvec_env crc32_benchenv = + { 0, 0, 0, before_hash }; static void test_unihash(const struct tvec_reg *in, struct tvec_reg *out, void *ctx) @@ -83,9 +88,9 @@ static void bench_unihash(const struct tvec_reg *in, struct tvec_reg *out, { unihash_hash(ctx, 0, in[RM].v.bytes.p, in[RM].v.bytes.sz); } static void setup_unihash(struct tvec_state *tv, const struct tvec_env *env, void *pctx, void *ctx) - { unihash_setkey(ctx, 0); } + { tvec_allocbuffer(&tv->in[RM].v); unihash_setkey(ctx, 0); } static const struct tvec_env unihash_benchenv = - { sizeof(unihash_info), setup_unihash, 0, 0 }; + { sizeof(unihash_info), setup_unihash, 0, before_hash }; static void run_step(struct tvec_state *tv, tvec_testfn *fn, void *ctx) { @@ -124,7 +129,7 @@ static const struct tvec_regdef bench_regs[] = { }; static const struct tvec_benchenv crc32_bench = - { TVEC_BENCHINIT, 1, -1, RM, 0 }; + { TVEC_BENCHINIT, 1, -1, RM, &crc32_benchenv }; static const struct tvec_benchenv unihash_bench = { TVEC_BENCHINIT, 1, -1, RM, &unihash_benchenv }; diff --git a/hash/unihash.3 b/hash/unihash.3 index c6b4c58..72467ac 100644 --- a/hash/unihash.3 +++ b/hash/unihash.3 @@ -51,8 +51,9 @@ unihash \- simple and efficient universal hashing for hashtables .BI "void unihash_setkey(unihash_info *" i ", uint32 " k ); .BI "uint32 UNIHASH_INIT(const unihash_info *" i ); +.ta \w'\fBuint32 unihash_hash('u .BI "uint32 unihash_hash(const unihash_info *" i ", uint32 " a , -.BI " const void *" p ", size_t " sz ); +.BI " const void *" p ", size_t " sz ); .BI "uint32 unihash(const unihash_info *" i ", const void *" p ", size_t " sz ); .BI "uint32 UNIHASH(const unihash_info *" i ", const void *" p ", size_t " sz ); .fi @@ -119,7 +120,7 @@ the data so far. So, to hash multiple chunks of data, do something like uint32 a = UNIHASH_INIT(i); a = unihash_hash(i, a, p_0, sz_0); a = unihash_hash(i, a, p_1, sz_1); -/* ... */ +/* ...\& */ a = unihash_hash(i, a, p_n, sz_n); .VE The macro diff --git a/mem/Makefile.am b/mem/Makefile.am index 7beb771..595fba3 100644 --- a/mem/Makefile.am +++ b/mem/Makefile.am @@ -42,6 +42,10 @@ pkginclude_HEADERS += alloc.h libmem_la_SOURCES += alloc.c LIBMANS += alloc.3 +## Buffer extension. +pkginclude_HEADERS += growbuf.h +LIBMANS += growbuf.3 + ## Slab allocator. pkginclude_HEADERS += sub.h libmem_la_SOURCES += sub.c diff --git a/mem/arena.3 b/mem/arena.3 index 3baf4e6..51c2227 100644 --- a/mem/arena.3 +++ b/mem/arena.3 @@ -15,22 +15,24 @@ arena \- control of memory allocation .nf .B "#include " +.ta 2n .B "typedef struct {" -.B "\h'4n'const struct arena_ops *ops"; +.B " const struct arena_ops *ops"; .B "} arena;" .B "typedef struct {" -.BI "\h'4n'void *(*alloc)(arena *" a ", size_t " sz ); -.BI "\h'4n'void *(*realloc)(arena *" a ", void *" p ", size_t " sz ", size_t " osz ); -.BI "\h'4n'void *(*free)(arena *" a ", void *" p ); -.BI "\h'4n'void *(*purge)(arena *" a ); +.BI " void *(*alloc)(arena *" a ", size_t " sz ); +.BI " void *(*realloc)(arena *" a ", void *" p ", size_t " sz ", size_t " osz ); +.BI " void *(*free)(arena *" a ", void *" p ); +.BI " void *(*purge)(arena *" a ); .B "} arena_ops;" .BI "arena *arena_global;" .BI "arena arena_stdlib;" +.ta \w'\fBvoid *arena_fakerealloc('u .BI "void *arena_fakerealloc(arena *" a ", void *" p , -.BI " size_t " sz ", size_t " osz ); +.BI " size_t " sz ", size_t " osz ); .BI "void *a_alloc(arena *" a ", size_t " sz ); .BI "void *a_realloc(arena *" a ", void *" p ", size_t " sz ", size_t " osz ); diff --git a/mem/growbuf.3 b/mem/growbuf.3 new file mode 100644 index 0000000..c31f27c --- /dev/null +++ b/mem/growbuf.3 @@ -0,0 +1,98 @@ +.\" -*-nroff-*- +.TH growbuf 3 "9 March 2024" "Straylight/Edgeware" "mLib utilities library" +.\" @GROWBUF_SIZE +.\" @GROWBUF_EXTEND +.\" @GROWBUF_REPLACE +. +.SH NAME +growbuf \- extend buffers efficiently +. +.SH SYNOPSIS +.nf +.B "#include " + +.BI "GROWBUF_SIZE(size_t " sz ", size_t " want ", " \c +.BI "size_t " init ", size_t " granule ");" + +.ds mT \fBGROWBUF_EXTEND( +.BI "\*(mTarena *" a ", " type " *" buf ", size_t " sz ", size_t " want "," +.BI "\h'\w'\*(mT'u'size_t " init ", size_t " granule ");" +.ds mT \fBGROWBUF_REPLACE( +.BI "\*(mTarena *" a ", " type " *" buf ", size_t " sz ", size_t " want "," +.BI "\h'\w'\*(mT'u'size_t " init ", size_t " granule ");" +.fi +. +.SH DESCRIPTION +The +.B "" +header file defines macros useful for dynamically resizing buffers. +.PP +The situation envisaged is a buffer, at address +.IR buf , +with space to hold +.I sz +objects each of size +.IR granule ; +but it has become necessary to store +.I want +objects in the buffer. +If the current size +.I sz +is zero, then the buffer pointer +.I buf +may be null; +otherwise, it is assumed to have been allocated from the arena +.IR a . +The size +.I init +is a suggested minimal nonzero size, again counting objects of size +.IR granule . +.PP +The +.B GROWBUF_SIZE +macro merely adjusts +.I sz +so that it is at least as large as +.IR want ; +it is +.I assumed +on entry that +.IR sz "\ <\ " want ; +nothing especially terrible will happen if this is not the case, +but unnecessary work will take place, +and +.I sz +will be left nonzero if it was previously zero, +even if +.IR want "\ =\ 0." +.B GROWBUF_SIZE +arranges to increase buffer sizes in a geometric progression +so as to minimize reallocation and copying overheads \(en +the number of reallocations will be +at most logarithmic in the final size +and each byte is copied an (amortized) constant number of times. +.PP +The +.B GROWBUF_EXTEND +macro will additionally resize the buffer, +preserving its contents, +and adjusting +.I buf +to point to its new location; +if +.I want +\(<= +.I sz +then nothing happens. +The +.B GROWBUF_REPLACE +macro is similar, except that it +.I discards +the existing buffer contents if the buffer needs to be adjusted. +. +.SH "SEE ALSO" +.BR arena (3), +.BR mLib (3). +. +.SH AUTHOR +Mark Wooding, diff --git a/mem/growbuf.h b/mem/growbuf.h new file mode 100644 index 0000000..ec78bcd --- /dev/null +++ b/mem/growbuf.h @@ -0,0 +1,137 @@ +/* -*-c-*- + * + * Grow a buffer if it's too small + * + * (c) 2024 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of the mLib utilities library. + * + * mLib is free software: you can redistribute it and/or modify it under + * the terms of the GNU Library General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * mLib 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 Library General Public + * License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with mLib. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#ifndef MLIB_GROWBUF_H +#define MLIB_GROWBUF_H + +#ifdef __cplusplus + extern "C" { +#endif + +/*----- Header files ------------------------------------------------------*/ + +#include +#include + +#ifndef MLIB_ALLOC_H +# include "alloc.h" +#endif + +/*----- Macros provided ---------------------------------------------------*/ + +/* --- @GROWBUF_LIMIT@ --- * + * + * Arguments: @size_t granule@ = allocation granule + * + * Returns: The largest number %$n$% such that the total size of %$n$% + * objects, each of size @granule@, can be represented in a + * @size_t@. + */ + +#define GROWBUF_LIMIT(granule) (~(size_t)0/(granule)) + +/* --- @GROWBUF_SIZE@ --- * + * + * Arguments: @size_t sz@ = the current size (updated) + * @size_t want@ = the desired minimum size + * @size_t init@ = a suitable initial size + * @size_t granule@ = the allocation granule size + * + * Returns: --- + * + * Use: On entry, @sz@ should be the current capacity of some buffer, + * in terms of objects of size @granule@, and @want@ a needed + * capacity, in the same terms, with @want > sz@; @init@ should + * be some suitable positive initial size, in case the current + * size is zero. The macro updates @sz@ to be some suitable new + * positive size at least as large as @want@. + */ + +#define GROWBUF_SIZE(sz, want, init, granule) do { \ + size_t _sz_ = (sz), _want_ = (want); \ + \ + assert(_want_ < GROWBUF_LIMIT(granule)/2); \ + if (!_sz_) _sz_ = (init); \ + while (_sz_ < _want_) _sz_ *= 2; \ + (sz) = _sz_; \ +} while (0) + +/* --- @GROWBUF_EXTEND@, @GROWBUF_REPLACE@ --- * + * + * Arguments: @arena *a@ = pointer to an arena + * @type *buf@ = pointer to some buffer, possibly null (updated) + * @size_t sz@ = current buffer size (updated) + * @size_t want@ = desired minimum size + * @size_t init@ = a suitable initial size + * @size_t granule@ = the allocation granule size + * + * Returns: --- + * + * Use: On entry, @buf@ should be a pointer to a buffer, allocated + * from the arena @a@, with space for @sz@ objects of size + * @granule@; @buf@ may be null if @sz@ is zero. On exit, @buf@ + * and @sz@ will be updated to refer to a possibly different + * buffer, with space for at least @want@ objects (but certainly + * not smaller than before). + * + * @GROWBUF_EXTEND@ preserves the contents of the buffer; + * @GROWBUF_REPLACE@ discards the existing contents. + */ + +#define GROWBUF_EXTEND(a, buf, sz, want, init, granule) do { \ + size_t _sz0 = (sz), _sz = _sz0, _want = (want), _gr = (granule); \ + void *_p = (buf); \ + arena *_a = (a); \ + \ + if (_sz < _want) { \ + GROWBUF_SIZE(_sz, _want, init, _gr); \ + if (!_p) _p = x_alloc(_a, _sz*_gr); \ + else _p = x_realloc(_a, _p, _sz*_gr, _sz0*_gr); \ + (buf) = _p; (sz) = _sz; \ + } \ +} while (0) + +#define GROWBUF_REPLACE(a, buf, sz, want, init, granule) do { \ + size_t _sz0 = (sz), _sz = _sz0, _want = (want), _gr = (granule); \ + void *_p = (buf); \ + arena *_a = (a); \ + \ + if (_sz < _want) { \ + GROWBUF_SIZE(_sz, want, init, _gr); \ + if (_p) x_free(_a, _p); \ + _p = x_alloc(_a, _sz*_gr); \ + (buf) = _p; (sz) = _sz; \ + } \ +} while (0) + +/*----- That's all, folks -------------------------------------------------*/ + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/mem/pool.3 b/mem/pool.3 index 5acc7b2..38538a8 100644 --- a/mem/pool.3 +++ b/mem/pool.3 @@ -31,30 +31,33 @@ pool \- resource pool management .B "typedef struct { ...\& } pool;" +.ta 2n .B "typedef struct {" -.B "\h'4n'pool_resource *next;" -.BI "\h'4n'void (*destroy)(pool_resource *" r ); +.B " pool_resource *next;" +.BI " void (*destroy)(pool_resource *" r ); .B "} pool_resource;" .B "typedef struct {" -.B "\h'4n'FILE *fp;" -.B "\h'4n'..." +.B " FILE *fp;" +.B " ..." .B "} pool_file;" .BI "void pool_init(pool *" p ", arena *" a ); .BI "pool *pool_create(arena *" a ); .BI "pool *pool_sub(pool *" p ); .BI "void pool_destroy(pool *" p ); +.ta \w'\fBvoid pool_add('u .BI "void pool_add(pool *" p ", pool_resource *" r , -.BI " void (*" dfn ")(pool_resource *" r )); +.BI " void (*" dfn ")(pool_resource *" r )); .BI "void *pool_alloc(pool *" p ", size_t " sz ); .BI "char *pool_strdup(pool *" p ", const char *" s ); .BI "pool_file *pool_fopen(pool *" p ", const char *" file ", const char *" how ); .BI "int pool_fclose(pool_file *" pf ); .BI "subarena *pool_subarena(pool *" p ); +.ta \w'\fBvoid POOL_ADD('u .BI "void POOL_ADD(pool *" p ", pool_resource *" r , -.BI " void (*" dfn ")(pool_resource *" r )); +.BI " void (*" dfn ")(pool_resource *" r )); .fi .SH "DESCRIPTION" .SS "Overview" diff --git a/sel/bres.3 b/sel/bres.3 index a18e24e..b5539b0 100644 --- a/sel/bres.3 +++ b/sel/bres.3 @@ -13,14 +13,14 @@ bres \- background name resolver .B "typedef struct { ...\& } bres_client;" -.ds mT \fBvoid bres_byname( -.BI "\*(mTbres_client *" rc ", const char *" name , -.BI "\h'\w'\*(mT'u'void (*" func ")(struct hostent *" h ", void *" p ), -.BI "\h'\w'\*(mT'u'void *" p ); -.ds mT \fBvoid bres_byaddr( -.BI "\*(mTbres_client *" rc ", struct inaddr " addr , -.BI "\h'\w'\*(mT'u'void (*" func ")(struct hostent *" h ", void *" p ), -.BI "\h'\w'\*(mT'u'void *" p ); +.ta \w'\fBvoid bres_byname('u +.BI "void bres_byname(bres_client *" rc ", const char *" name , +.BI " void (*" func ")(struct hostent *" h ", void *" p ), +.BI " void *" p ); +.ta \w'\fBvoid bres_byaddr('u +.BI "void bres_byaddr(res_client *" rc ", struct inaddr " addr , +.BI " void (*" func ")(struct hostent *" h ", void *" p ), +.BI " void *" p ); .BI "void bres_abort(bres_client *" rc ); .BI "void bres_exec(const char *" file ); .BI "void bres_init(sel_state *" sel ); @@ -50,10 +50,11 @@ Each function is passed the following arguments: Pointer to the client block to initialize and store the resolver job's state. .TP -.BI "struct in_addr " addr "\fR (\fBbres_byaddr\fR)" -.sp -1 +.BR "struct in_addr " \fIaddr "" " (" bres_byaddr ) +.ie t .sp -0.4v +.el .sp -1v .TP -.BI "const char *" name "\fR (\fBbres_byname\fR)" +.BR "const char *" \fIname "" " (" bres_byname ) The IP address or hostname to resolve. .TP .BI "void (*" func ")(struct hostent *" h ", void *" p ) diff --git a/sel/conn.3 b/sel/conn.3 index f115605..1ff936f 100644 --- a/sel/conn.3 +++ b/sel/conn.3 @@ -11,16 +11,16 @@ conn \- selector for nonblocking connections .B "typedef struct { ...\& } conn;" -.ds mT \fBint conn_fd( -.BI "\*(mTconn *" c ", sel_state *" s ", int " fd , -.BI "\h'\w'\*(mT'u'void (*" func ")(int " fd ", void *" p ), -.BI "\h'\w'\*(mT'u'void *" p ); +.ta \w'\fBint conn_fd('u +.BI "int conn_fd(conn *" c ", sel_state *" s ", int " fd , +.BI " void (*" func ")(int " fd ", void *" p ), +.BI " void *" p ); -.ds mT \fBint conn_init( -.BI "\*(mTconn *" c ", sel_state *" s ", int " fd , -.BI "\h'\w'\*(mT'u'struct sockaddr *" dst ", int " dsz , -.BI "\h'\w'\*(mT'u'void (*" func ")(int " fd ", void *" p ), -.BI "\h'\w'\*(mT'u'void *" p ); +.ta \w'\fBint conn_init('u +.BI "int conn_init(conn *" c ", sel_state *" s ", int " fd , +.BI " struct sockaddr *" dst ", int " dsz , +.BI " void (*" func ")(int " fd ", void *" p ), +.BI " void *" p ); .BI "void conn_kill(conn *" c ); .fi diff --git a/sel/ident.3 b/sel/ident.3 index 326d508..aebf903 100644 --- a/sel/ident.3 +++ b/sel/ident.3 @@ -11,32 +11,33 @@ ident \- identd (RFC931) client .B "typedef struct { ...\& } ident_request;" +.ta 2n +2n .B "enum [" -.B "\h'4n'IDENT_USERID = ...," -.B "\h'4n'IDENT_ERROR = ...," -.B "\h'4n'IDENT_BAD = ..." +.B " IDENT_USERID = ...," +.B " IDENT_ERROR = ...," +.B " IDENT_BAD = ..." .B "};" .B "typedef struct {" -.B "\h'4n'unsigned short sport, dport;" -.B "\h'4n'unsigned type;" -.B "\h'4n'union {" -.B "\h'8n'struct { char *os, *user; } userid;" -.B "\h'8n'char *error;" -.B "\h'4n'} u;" +.B " unsigned short sport, dport;" +.B " unsigned type;" +.B " union {" +.B " struct { char *os, *user; } userid;" +.B " char *error;" +.B " } u;" .B "} ident_reply;" .BI "void ident_abort(ident_request *" rq ); -.ds mT \fBvoid ident( -.BI "\*(mTident_request *" rq ", sel_state *" s , -.BI "\h'\w'\*(mT'u'const struct sockaddr_in *" local , -.BI "\h'\w'\*(mT'u'const struct sockaddr_in *" remote , -.BI "\h'\w'\*(mT'u'void (*" func ")(ident_reply *" i ", void *" p ), -.BI "\h'\w'\*(mT'u'void *" p ); -.ds mT \fBvoid ident_socket( -.BI "\*(mTident_request *" rq ", sel_state *" s ", int " sk , -.BI "\h'\w'\*(mT'u'void (*" func ")(ident_reply *" i ", void *" p ), -.BI "\h'\w'\*(mT'u'void *" p ); +.ta \w'\fBvoid ident('u +.BI "void ident(ident_request *" rq ", sel_state *" s , +.BI " const struct sockaddr_in *" local , +.BI " const struct sockaddr_in *" remote , +.BI " void (*" func ")(ident_reply *" i ", void *" p ), +.BI " void *" p ); +.ta \w'\fBvoid ident_socket('u +.BI "void ident_socket(ident_request *" rq ", sel_state *" s ", int " sk , +.BI " void (*" func ")(ident_reply *" i ", void *" p ), +.BI " void *" p ); .fi .SH "DESCRIPTION" The diff --git a/sel/sel.3 b/sel/sel.3 index 021b1c9..e312277 100644 --- a/sel/sel.3 +++ b/sel/sel.3 @@ -17,11 +17,12 @@ sel \- low level interface for waiting for I/O .nf .B "#include " +.ta 2n .B "enum {" -.B "\h'4n'SEL_READ = ...," -.B "\h'4n'SEL_WRITE = ...," -.B "\h'4n'SEL_EXC = ...," -.B "\h'4n'SEL_MODES = ..." +.B " SEL_READ = ...," +.B " SEL_WRITE = ...," +.B " SEL_EXC = ...," +.B " SEL_MODES = ..." .B "};" .B "typedef struct { ...\& } sel_state;" @@ -29,41 +30,41 @@ sel \- low level interface for waiting for I/O .B "typedef struct { ...\& } sel_hook;" .B "typedef struct {" -.B "\h'4n'int fd;" -.B "\h'4n'..." +.B " int fd;" +.B " ..." .B "} sel_file;" .B "typedef struct {" -.B "\h'4n'int maxfd;" -.B "\h'4n'fd_set fd[SEL_MODES];" -.B "\h'4n'struct timeval tv, *tvp;" -.B "\h'4n'struct timeval now;" +.B " int maxfd;" +.B " fd_set fd[SEL_MODES];" +.B " struct timeval tv, *tvp;" +.B " struct timeval now;" .B "} sel_args;" .BI "typedef void (*sel_hookfn)(sel_state *" s ", sel_args *" a ", void *" p ); .BI "void sel_init(sel_state *" s ); -.ds mT \fBvoid sel_initfile( -.BI "\*(mTsel_state *" s ", sel_file *" f , -.BI "\h'\w'\*(mT'u'int " fd ", unsigned " mode , -.BI "\h'\w'\*(mT'u'void (*" func ")(int " fd ", unsigned " mode ", void *" p ), -.BI "\h'\w'\*(mT'u'void *" p ); +.ta \w'\fBvoid sel_initfile('u +.BI "void sel_initfile(sel_state *" s ", sel_file *" f , +.BI " int " fd ", unsigned " mode , +.BI " void (*" func ")(int " fd ", unsigned " mode ", void *" p ), +.BI " void *" p ); .BI "void sel_addfile(sel_file *" f ); .BI "void sel_force(sel_file *" f ); .BI "void sel_rmfile(sel_file *" f ); -.ds mT \fBvoid sel_addtimer( -.BI "\*(mTsel_state *" s ", sel_timer *" t , -.BI "\h'\w'\*(mT'u'struct timeval *" tv , -.BI "\h'\w'\*(mT'u'void (*" func ")(struct timeval *" tv ", void *" p ), -.BI "\h'\w'\*(mT'u'void *" p ); +.ta \w'\fBvoid sel_addtimer('u +.BI "void sel_addtimer(sel_state *" s ", sel_timer *" t , +.BI " struct timeval *" tv , +.BI " void (*" func ")(struct timeval *" tv ", void *" p ), +.BI " void *" p ); .BI "void sel_rmtimer(sel_timer *" t ); -.ds mT \fBvoid sel_addhook( -.BI "\*(mTsel_state *" s ", sel_hook *" h , -.BI "\h'\w'\*(mT'u'sel_hookfn " before ", sel_hookfn " after , -.BI "\h'\w'\*(mT'u'void *" p ); +.ta \w'\fBvoid sel_addhook('u +.BI "void sel_addtimer(sel_state *" s ", sel_hook *" h , +.BI " sel_hookfn " before ", sel_hookfn " after , +.BI " void *" p ); .BI "void sel_rmhook(sel_hook *" h ); .BI "int sel_fdmerge(fd_set *" dest ", fd_set *" fd ", int " maxfd ); diff --git a/sel/selbuf.3 b/sel/selbuf.3 index 032f9f1..5db9a71 100644 --- a/sel/selbuf.3 +++ b/sel/selbuf.3 @@ -16,9 +16,9 @@ selbuf \- line-buffering input selector .BI "void selbuf_enable(selbuf *" b ); .BI "void selbuf_disable(selbuf *" b ); .BI "void selbuf_setsize(selbuf *" b ", size_t " sz ); -.ds mT \fBvoid selbuf_init( -.BI "\*(mTselbuf *" b ", sel_state *" s ", int " fd , -.BI "\h'\w'\*(mT'u'lbuf_func *" func ", void *" p ); +.ta \w'\fBvoid selbuf_init('u +.BI "void selbuf_init(selbuf *" b ", sel_state *" s ", int " fd , +.BI " lbuf_func *" func ", void *" p ); .BI "void selbuf_destroy(selbuf *" b ); .fi .SH DESCRIPTION diff --git a/sel/selpk.3 b/sel/selpk.3 index 952c84f..5a3a0ff 100644 --- a/sel/selpk.3 +++ b/sel/selpk.3 @@ -16,9 +16,9 @@ selpk \- packet-buffering input selector .BI "void selpk_enable(selpk *" pk ); .BI "void selpk_disable(selpk *" pk ); .BI "void selpk_want(selpk *" pk ", size_t " sz ); -.ds mT \fBvoid selpk_init( -.BI "\*(mTselpk *" pk ", sel_state *" s ", int " fd , -.BI "\h'\w'\*(mT'u'pkbuf_func *" func ", void *" p ); +.ta \w'\fBvoid selpk_init('u +.BI "void selpk_init(selpk *" pk ", sel_state *" s ", int " fd , +.BI " pkbuf_func *" func ", void *" p ); .BI "void selpk_destroy(selpk *" b ); .fi .SH DESCRIPTION diff --git a/sel/sig.3 b/sel/sig.3 index 68b58c9..725f859 100644 --- a/sel/sig.3 +++ b/sel/sig.3 @@ -11,9 +11,9 @@ sig \- more controlled signal handling .B "typedef struct { ...\& } sig;" -.ds mT \fBvoid sig_add( -.BI "\*(mTsig *" s ", int " n , -.BI "\h'\w'\*(mT'u'void (*" proc ")(int " n ", void *" p "), void *" p ); +.ta \w'\fBvoid sig_add('u +.BI "void sig_add(sig *" s ", int " n , +.BI " void (*" proc ")(int " n ", void *" p "), void *" p ); .BI "void sig_remove(sig *" s ); .BI "void sig_init(sel_state *" s ); .fi diff --git a/struct/buf-putf.c b/struct/buf-putf.c index 3b4261d..9a4cc54 100644 --- a/struct/buf-putf.c +++ b/struct/buf-putf.c @@ -52,10 +52,10 @@ */ static int putch(void *out, int ch) - { buf *b = out; return (buf_putbyte(b, ch)); } + { buf *b = out; return (buf_putbyte(b, ch) ? -1 : 1); } static int putm(void *out, const char *p, size_t sz) - { buf *b = out; return (buf_put(b, p, sz)); } + { buf *b = out; return (buf_put(b, p, sz) ? -1 : sz); } static int nputf(void *out, size_t maxsz, const char *p, ...) { diff --git a/struct/buf.3 b/struct/buf.3 index 10b52cf..3b3ef7c 100644 --- a/struct/buf.3 +++ b/struct/buf.3 @@ -19,7 +19,9 @@ .. .ie t .ds o \(bu .el .ds o o +. .TH buf 3 "23 September 2005" "Straylight/Edgeware" "mLib utilities library" +. .SH NAME buf \- reading and writing stuff in buffers .\" @BBASE @@ -32,16 +34,64 @@ buf \- reading and writing stuff in buffers .\" @BBAD .\" @BOK .\" @BENSURE + +.\" @DBBASE +.\" @DBLIM +.\" @DBCUR +.\" @DBSZ +.\" @DBLEN +.\" @DBLEFT +.\" @DBSTEP +.\" @DBBAD +.\" @DBOK +.\" @DBENSURE . .\" @buf_init +. +.\" @dbuf_create +.\" @dbuf_reset +.\" @dbuf_destroy +.\" @DBCREATE +.\" @DBRESET +.\" @DBDESTROY +.\" @DBUF_INIT +.\" @DBUF_BUF +. .\" @buf_break .\" @buf_flip .\" @buf_ensure +.\" @buf_tryextend .\" @buf_get .\" @buf_put +.\" @dbuf_break +.\" @dbuf_flip +.\" @dbuf_ensure +.\" @dbuf_tryextend +.\" @dbuf_get +.\" @dbuf_put . .\" @buf_getbyte .\" @buf_putbyte +.\" @dbuf_getbyte +.\" @dbuf_putbyte +. +.\" @buf_getf64 +.\" @buf_getf64l +.\" @buf_getf64b +.\" @buf_putf64 +.\" @buf_putf64l +.\" @buf_putf64b +.\" @dbuf_getf64 +.\" @dbuf_getf64l +.\" @dbuf_getf64b +.\" @dbuf_putf64 +.\" @dbuf_putf64l +.\" @dbuf_putf64b +. +.\" @buf_putstrf +.\" @buf_vputstrf +.\" @dbuf_putstrf +.\" @dbuf_vputstrf . .\" @buf_getu8 .\" @buf_getu16 @@ -53,6 +103,28 @@ buf \- reading and writing stuff in buffers .\" @buf_getu32 .\" @buf_getu32b .\" @buf_getu32l +.\" @buf_getu64 +.\" @buf_getu64b +.\" @buf_getu64l +.\" @buf_getk64 +.\" @buf_getk64b +.\" @buf_getk64l +.\" @dbuf_getu8 +.\" @dbuf_getu16 +.\" @dbuf_getu16b +.\" @dbuf_getu16l +.\" @dbuf_getu24 +.\" @dbuf_getu24b +.\" @dbuf_getu24l +.\" @dbuf_getu32 +.\" @dbuf_getu32b +.\" @dbuf_getu32l +.\" @dbuf_getu64 +.\" @dbuf_getu64b +.\" @dbuf_getu64l +.\" @dbuf_getk64 +.\" @dbuf_getk64b +.\" @dbuf_getk64l . .\" @buf_putu8 .\" @buf_putu16 @@ -64,6 +136,28 @@ buf \- reading and writing stuff in buffers .\" @buf_putu32 .\" @buf_putu32b .\" @buf_putu32l +.\" @buf_putu64 +.\" @buf_putu64b +.\" @buf_putu64l +.\" @buf_putk64 +.\" @buf_putk64b +.\" @buf_putk64l +.\" @dbuf_putu8 +.\" @dbuf_putu16 +.\" @dbuf_putu16b +.\" @dbuf_putu16l +.\" @dbuf_putu24 +.\" @dbuf_putu24b +.\" @dbuf_putu24l +.\" @dbuf_putu32 +.\" @dbuf_putu32b +.\" @dbuf_putu32l +.\" @dbuf_putu64 +.\" @dbuf_putu64b +.\" @dbuf_putu64l +.\" @dbuf_putk64 +.\" @dbuf_putk64b +.\" @dbuf_putk64l . .\" @buf_getbuf8 .\" @buf_getbuf16 @@ -75,7 +169,24 @@ buf \- reading and writing stuff in buffers .\" @buf_getbuf32 .\" @buf_getbuf32b .\" @buf_getbuf32l +.\" @buf_getbuf64 +.\" @buf_getbuf64b +.\" @buf_getbuf64l .\" @buf_getbufz +.\" @dbuf_getbuf8 +.\" @dbuf_getbuf16 +.\" @dbuf_getbuf16b +.\" @dbuf_getbuf16l +.\" @dbuf_getbuf24 +.\" @dbuf_getbuf24b +.\" @dbuf_getbuf24l +.\" @dbuf_getbuf32 +.\" @dbuf_getbuf32b +.\" @dbuf_getbuf32l +.\" @dbuf_getbuf64 +.\" @dbuf_getbuf64b +.\" @dbuf_getbuf64l +.\" @dbuf_getbufz . .\" @buf_putbuf8 .\" @buf_putbuf16 @@ -87,8 +198,26 @@ buf \- reading and writing stuff in buffers .\" @buf_putbuf32 .\" @buf_putbuf32b .\" @buf_putbuf32l +.\" @buf_putbuf64 +.\" @buf_putbuf64b +.\" @buf_putbuf64l .\" @buf_putbufz +.\" @dbuf_putbuf8 +.\" @dbuf_putbuf16 +.\" @dbuf_putbuf16b +.\" @dbuf_putbuf16l +.\" @dbuf_putbuf24 +.\" @dbuf_putbuf24b +.\" @dbuf_putbuf24l +.\" @dbuf_putbuf32 +.\" @dbuf_putbuf32b +.\" @dbuf_putbuf32l +.\" @dbuf_putbuf64 +.\" @dbuf_putbuf64b +.\" @dbuf_putbuf64l +.\" @dbuf_putbufz . +.\" @buf_getmem8 .\" @buf_getmem16 .\" @buf_getmem16b .\" @buf_getmem16l @@ -98,8 +227,24 @@ buf \- reading and writing stuff in buffers .\" @buf_getmem32 .\" @buf_getmem32b .\" @buf_getmem32l -.\" @buf_getmem8 +.\" @buf_getmem64 +.\" @buf_getmem64b +.\" @buf_getmem64l .\" @buf_getmemz +.\" @dbuf_getmem8 +.\" @dbuf_getmem16 +.\" @dbuf_getmem16b +.\" @dbuf_getmem16l +.\" @dbuf_getmem24 +.\" @dbuf_getmem24b +.\" @dbuf_getmem24l +.\" @dbuf_getmem32 +.\" @dbuf_getmem32b +.\" @dbuf_getmem32l +.\" @dbuf_getmem64 +.\" @dbuf_getmem64b +.\" @dbuf_getmem64l +.\" @dbuf_getmemz . .\" @buf_putmem8 .\" @buf_putmem16 @@ -111,7 +256,24 @@ buf \- reading and writing stuff in buffers .\" @buf_putmem32 .\" @buf_putmem32b .\" @buf_putmem32l +.\" @buf_putmem64 +.\" @buf_putmem64b +.\" @buf_putmem64l .\" @buf_putmemz +.\" @dbuf_putmem8 +.\" @dbuf_putmem16 +.\" @dbuf_putmem16b +.\" @dbuf_putmem16l +.\" @dbuf_putmem24 +.\" @dbuf_putmem24b +.\" @dbuf_putmem24l +.\" @dbuf_putmem32 +.\" @dbuf_putmem32b +.\" @dbuf_putmem32l +.\" @dbuf_putmem64 +.\" @dbuf_putmem64b +.\" @dbuf_putmem64l +.\" @dbuf_putmemz . .\" @buf_putstr8 .\" @buf_putstr16 @@ -123,7 +285,24 @@ buf \- reading and writing stuff in buffers .\" @buf_putstr32 .\" @buf_putstr32b .\" @buf_putstr32l +.\" @buf_putstr64 +.\" @buf_putstr64b +.\" @buf_putstr64l .\" @buf_putstrz +.\" @dbuf_putstr8 +.\" @dbuf_putstr16 +.\" @dbuf_putstr16b +.\" @dbuf_putstr16l +.\" @dbuf_putstr24 +.\" @dbuf_putstr24b +.\" @dbuf_putstr24l +.\" @dbuf_putstr32 +.\" @dbuf_putstr32b +.\" @dbuf_putstr32l +.\" @dbuf_putstr64 +.\" @dbuf_putstr64b +.\" @dbuf_putstr64l +.\" @dbuf_putstrz . .\" @buf_getdstr8 .\" @buf_getdstr16 @@ -135,7 +314,24 @@ buf \- reading and writing stuff in buffers .\" @buf_getdstr32 .\" @buf_getdstr32b .\" @buf_getdstr32l +.\" @buf_getdstr64 +.\" @buf_getdstr64b +.\" @buf_getdstr64l .\" @buf_getdstrz +.\" @dbuf_getdstr8 +.\" @dbuf_getdstr16 +.\" @dbuf_getdstr16b +.\" @dbuf_getdstr16l +.\" @dbuf_getdstr24 +.\" @dbuf_getdstr24b +.\" @dbuf_getdstr24l +.\" @dbuf_getdstr32 +.\" @dbuf_getdstr32b +.\" @dbuf_getdstr32l +.\" @dbuf_getdstr64 +.\" @dbuf_getdstr64b +.\" @dbuf_getdstr64l +.\" @dbuf_getdstrz . .\" @buf_putdstr8 .\" @buf_putdstr16 @@ -147,14 +343,154 @@ buf \- reading and writing stuff in buffers .\" @buf_putdstr32 .\" @buf_putdstr32b .\" @buf_putdstr32l +.\" @buf_putdstr64 +.\" @buf_putdstr64b +.\" @buf_putdstr64l .\" @buf_putdstrz +.\" @dbuf_putdstr8 +.\" @dbuf_putdstr16 +.\" @dbuf_putdstr16b +.\" @dbuf_putdstr16l +.\" @dbuf_putdstr24 +.\" @dbuf_putdstr24b +.\" @dbuf_putdstr24l +.\" @dbuf_putdstr32 +.\" @dbuf_putdstr32b +.\" @dbuf_putdstr32l +.\" @dbuf_putdstr64 +.\" @dbuf_putdstr64b +.\" @dbuf_putdstr64l +.\" @dbuf_putdstrz +. +.\" @buf_putstrf8 +.\" @buf_putstrf16 +.\" @buf_putstrf16b +.\" @buf_putstrf16l +.\" @buf_putstrf24 +.\" @buf_putstrf24b +.\" @buf_putstrf24l +.\" @buf_putstrf32 +.\" @buf_putstrf32b +.\" @buf_putstrf32l +.\" @buf_putstrf64 +.\" @buf_putstrf64b +.\" @buf_putstrf64l +.\" @buf_putstrfz +.\" @buf_vputstrf8 +.\" @buf_vputstrf16 +.\" @buf_vputstrf16b +.\" @buf_vputstrf16l +.\" @buf_vputstrf24 +.\" @buf_vputstrf24b +.\" @buf_vputstrf24l +.\" @buf_vputstrf32 +.\" @buf_vputstrf32b +.\" @buf_vputstrf32l +.\" @buf_vputstrf64 +.\" @buf_vputstrf64b +.\" @buf_vputstrf64l +.\" @buf_vputstrfz +.\" @dbuf_putstrf8 +.\" @dbuf_putstrf16 +.\" @dbuf_putstrf16b +.\" @dbuf_putstrf16l +.\" @dbuf_putstrf24 +.\" @dbuf_putstrf24b +.\" @dbuf_putstrf24l +.\" @dbuf_putstrf32 +.\" @dbuf_putstrf32b +.\" @dbuf_putstrf32l +.\" @dbuf_putstrf64 +.\" @dbuf_putstrf64b +.\" @dbuf_putstrf64l +.\" @dbuf_putstrfz +.\" @dbuf_vputstrf8 +.\" @dbuf_vputstrf16 +.\" @dbuf_vputstrf16b +.\" @dbuf_vputstrf16l +.\" @dbuf_vputstrf24 +.\" @dbuf_vputstrf24b +.\" @dbuf_vputstrf24l +.\" @dbuf_vputstrf32 +.\" @dbuf_vputstrf32b +.\" @dbuf_vputstrf32l +.\" @dbuf_vputstrf64 +.\" @dbuf_vputstrf64b +.\" @dbuf_vputstrf64l +.\" @dbuf_vputstrfz +. +.\" @BUF_ENCLOSETAG +.\" @BUF_ENCLOSEITAG +.\" @BUF_ENCLOSEKTAG +.\" @BUF_ENCLOSEZTAG +.\" @BUF_ENCLOSE8 +.\" @BUF_ENCLOSE16 +.\" @BUF_ENCLOSE16_L +.\" @BUF_ENCLOSE16_B +.\" @BUF_ENCLOSE24 +.\" @BUF_ENCLOSE24_L +.\" @BUF_ENCLOSE24_B +.\" @BUF_ENCLOSE32 +.\" @BUF_ENCLOSE32_L +.\" @BUF_ENCLOSE32_B +.\" @BUF_ENCLOSE64 +.\" @BUF_ENCLOSE64_L +.\" @BUF_ENCLOSE64_B +.\" @BUF_ENCLOSEZ +.\" @DBUF_ENCLOSETAG +.\" @DBUF_ENCLOSEITAG +.\" @DBUF_ENCLOSEKTAG +.\" @DBUF_ENCLOSEZTAG +.\" @DBUF_ENCLOSE8 +.\" @DBUF_ENCLOSE16 +.\" @DBUF_ENCLOSE16_L +.\" @DBUF_ENCLOSE16_B +.\" @DBUF_ENCLOSE24 +.\" @DBUF_ENCLOSE24_L +.\" @DBUF_ENCLOSE24_B +.\" @DBUF_ENCLOSE32 +.\" @DBUF_ENCLOSE32_L +.\" @DBUF_ENCLOSE32_B +.\" @DBUF_ENCLOSE64 +.\" @DBUF_ENCLOSE64_L +.\" @DBUF_ENCLOSE64_B +.\" @DBUF_ENCLOSEZ +. .SH SYNOPSIS .nf .B "#include " .B "typedef struct { ...\& } buf;" +.B "typedef struct { ...\& } dbuf;" .BI "void buf_init(buf *" b ", void *" p ", size_t " sz ); +.BI "void dbuf_create(dbuf *" db ); +.BI "void dbuf_reset(dbuf *" db ); +.BI "void dbuf_destroy(dbuf *" db ); +.BI "buf *DBUF_BUF(dbuf *" db ); +.BI "void DBCREATE(dbuf *" db ); +.BI "void DBRESET(dbuf *" db ); +.BI "void DBDESTROY(dbuf *" db ); +.B "#define DBUF_INIT ..." + +.fi +All of the following functions and macros exist in two variants: +one with a name beginning +.BR buf_ , +.BR B , +or +.BR BUF_ , +and taking a first argument of type +.BR "buf *" ; +and a corresponding similarly named version with name beginning instead +.BR dbuf_ , +.BR DB , +or +.BR DBUF_ , +and taking a first argument of type +.BR "dbuf *" . +.nf + .BI "void buf_flip(buf *" b ); .BI "octet *BBASE(buf *" b ); .BI "octet *BLIM(buf *" b ); @@ -162,12 +498,15 @@ buf \- reading and writing stuff in buffers .BI "ptrdiff_t BSZ(buf *" b ); .BI "ptrdiff_t BLEN(buf *" b ); .BI "ptrdiff_t BLEFT(buf *" b ); +.BI "void BFLIP(buf *" b ); .BI "int buf_break(buf *" b ); +.BI "int BBREAK(buf *" b ); .BI "int BBAD(buf *" b ); .BI "int BOK(buf *" b ); .BI "int buf_ensure(buf *" b ", size_t " sz ); +.BI "int buf_tryextend(buf *" b ", size_t " sz ); .BI "int BENSURE(buf *" b ", size_t " sz ); .BI "octet *BSTEP(buf *" b ", size_t " sz ); @@ -175,23 +514,144 @@ buf \- reading and writing stuff in buffers .BI "void *buf_put(buf *" b ", const void *" p ", size_t " sz ); .BI "int buf_getbyte(buf *" b ); -.BI "int buf_putbyte(buf *" b ", int ch" ); -.BI "int buf_getu" suff "(buf *" b ", uint" suff " *" w ); +.BI "int buf_putbyte(buf *" b ", int " ch ); + +.BI "int buf_putstr(buf *" b ", const char *" p ", ...);" +.BI "int buf_vputstr(buf *" b ", const char *" p ", va_list *" ap ); + +.fi +For +.I suff +in +.BR 8 , +.BR 16 , +.BR 16l , +.BR 16b , +.BR 24 , +.BR 24l , +.BR 24b , +.BR 32 , +.BR 32l , +and +.BR 32b , +and, if a 64-bit integer type is available, +.BR 64 , +.BR 64l , +and +.BR 64b : +.nf .BI "int buf_putu" suff "(buf *" b ", uint" suff " " w ); -.BI "void *buf_getmem" suff "(buf *" b ", size_t *" sz ); -.BI "int buf_putmem" suff "(buf *" b ", const void *" p ", size_t " sz ); -.BI "int buf_getbuf" suff "(buf *" b ", buf *" bb ); -.BI "int buf_putbuf" suff "(buf *" b ", buf *" bb ); -.BI "int buf_getdstr" suff "(buf *" b ", dstr *" d ); -.BI "int buf_putdstr" suff "(buf *" b ", dstr *" d ); +.BI "int buf_getu" suff "(buf *" b ", uint" suff " *" w ); + +.fi +For +.I suff +in +.BR 64 , +.BR 64l , +and +.BR 64b : +.nf +.BI "int buf_putk" suff "(buf *" b ", kludge64 " w ); +.BI "int buf_getk" suff "(buf *" b ", kludge64 *" w ); + +.ta 2n +.BI "BUF_ENCLOSETAG(" tag ", buf *" b ", size_t " mk ", " check ", " poke ", size_t " lensz ) +.I " body" +.BI "BUF_ENCLOSEITAG(" tag ", buf *" b ", size_t " mk ", " W ) +.I " body" +.BI "BUF_ENCLOSEKTAG(" tag ", buf *" b ", size_t " mk ", " W ) +.I " body" +.BI "BUF_ENCLOSEZTAG(" tag ", buf *" b ) +.I " body" + +.fi +For +.I suff +in +.BR 8 , +.BR 16 , +.BR 16_L , +.BR 16_B , +.BR 24 , +.BR 24_L , +.BR 24_B , +.BR 32 , +.BR 32_L , +.BR 32_B , +.BR 64 , +.BR 64_L , +and +.BR 64_B , +.nf +.ta 2n +.BI "BUF_ENCLOSE" suff "(buf *" b ", size_t " mk ) +.I " body" + +.BI "BUF_ENCLOSEZ(buf *" b ) +.I " body" + +.fi +For +.I suff +in +.BR 8 , +.BR 16 , +.BR 16l , +.BR 16b , +.BR 24 , +.BR 24l , +.BR 24b , +.BR 32 , +.BR 32l , +.BR 32b , +.BR 64 , +.BR 64l , +.BR 64b , +and +.BR z : +.nf .BI "int buf_putstr" suff "(buf *" b ", const char *" p ); +.BI "int dbuf_putstr" suff "(dbuf *" db ", const char *" p ); +.BI "int buf_putstr" suff "(buf *" b ", const char *" p ", ...);" +.BI "int dbuf_putstr" suff "(dbuf *" db ", const char *" p ", ...);" +.BI "int buf_vputstr" suff "(buf *" b ", const char *" p ", va_list *" ap ); +.BI "int dbuf_vputstr" suff "(dbuf *" db ", const char *" p ", va_list *" ap ); +.BI "int buf_putdstr" suff "(buf *" b ", dstr *" d ); +.BI "int dbuf_putdstr" suff "(dbuf *" db ", dstr *" d ); +.BI "int buf_getdstr" suff "(buf *" b ", dstr *" d ); +.BI "int dbuf_getdstr" suff "(dbuf *" db ", dstr *" d ); +.BI "int buf_putbuf" suff "(buf *" b ", buf *" bb ); +.BI "int dbuf_putbuf" suff "(dbuf *" db ", buf *" bb ); +.BI "int buf_getbuf" suff "(buf *" b ", buf *" bb ); +.BI "int dbuf_getbuf" suff "(dbuf *" db ", buf *" bb ); +.BI "int buf_putmem" suff "(buf *" b ", const void *" p ", size_t " sz ); +.BI "int dbuf_putmem" suff "(dbuf *" db ", const void *" p ", size_t " sz ); +.BI "void *buf_getmem" suff "(buf *" b ", size_t *" sz ); +.BI "void d*buf_getmem" suff "(dbuf *" db ", size_t *" sz ); + .fi +For +.I suff +in +.BR 64 , +.BR 64l , +and +.BR 64b : +.nf +.BI "int buf_putf" suff "(buf *" b ", double " x ); +.BI "int dbuf_putf" suff "(dbuf *" db ", double " x ); +.BI "int buf_getf" suff "(buf *" b ", double *" x ); +.BI "int dbuf_getf" suff "(dbuf *" db ", double *" x ); +.fi +. .SH DESCRIPTION The .B buf interface allows relatively convenient reading and writing of structured binary data from and to fixed-size memory buffers. It's useful for formatting and parsing down network data packets, for example. +. .SS "Buffer basics" A buffer has three important pointers associated with it: .TP @@ -267,6 +727,10 @@ and to set .I current to .IR base . +There is a macro version, +.BR BFLIP , +which does the same thing, +but it may evaluate its buffer argument multiple times. .PP A buffer can be .IR broken , @@ -279,12 +743,19 @@ call and just check the brokenness state at the end of their run. .PP The function .B buf_break -will break a buffer. The macro +or its +macro equivalent +.B BBREAK +will break a buffer: +the function returns \-1 as a possible, but minor, convenience; +the macro expands to a statement and cannot return a value. +The macro .B BBAD reports true (nonzero) if its buffer argument is broken, or false (zero) otherwise; its counterpart .B BOK reports true if the buffer is OK, and false if it is broken. +. .SS "Low-level buffer access" Access to the data in the buffer is usually sequential. The .B BENSURE @@ -329,6 +800,7 @@ function writes bytes of data starting at .I p to the buffer. If it succeeded, it returns 0; otherwise it returns \-1. +. .SS "Formatted buffer access" The function .B buf_getbyte @@ -360,8 +832,65 @@ given, and returns zero; on failure, it returns \-1. The function .BI buf_putu suff write an integer. It returns zero on success or \-1 on failure. .PP +For (portability to) platforms without 64-bit integers, the functions +.B buf_getk64 +and +.B buf_putk64 +(and +.RB ` l '- +and +.RB ` b '-suffixed +variants) perform the necessary functionality, but acting on the +.B kludge64 +type; see +.BR bits (3). +.PP +The functions +.BR buf_getf64 , +.BR buf_getf64l , +and +.BR buf_getf64b +read 64-bit floating-point values +in IEEE\ 754 Binary64 format +from the buffer; +as usual, the suffix indicates the byte ordering convention. +On success, they store the result in +.BI *x +and return zero; +on failure, they break the buffer and return zero. +The functions +.BR buf_putf64 , +.BR buf_putf64l , +and +.BR buf_putf64b +write floating-point numbers +in IEEE\ 754 Binary64 format +from the buffer. +On success, they return zero; on failure, they return \-1. +Note that these functions use IEEE\ 754 format +even if this is not the platform-native floating-point representation. +.PP +The function +.B buf_putstrf +processes a +.BR printf (3)-like +format string and arguments, +writing the output to the buffer. +The function +.B buf_vputstrf +does the same, +except that it reads arguments from a +.B va_list +captured argument tail, +leaving the tail ready to read the next unprocessed argument. +Both functions return the number of bytes written on success +or \-1 on failure. +Note that these functions apply no length framing or termination. +.PP Functions which deal with block lengths assume the length is prefixed to -the data, and don't include themselves. They also have an additional +the data, and don't include themselves. They come in all of the integer +size variants, including 64-bits even on platforms without 64-bit integers; +they also have an additional .RB ` z ' variant, which deals with zero-terminated data. No checks are done on writing that the data written contains no zero bytes. @@ -401,8 +930,242 @@ function .BI buf_putstr suff writes a standard C null-terminated string to a buffer. All these functions return zero on success or \-1 on failure. +.PP +The function +.BI buf_putstrf suff +processes a +.BR printf (3)-like +format string and arguments, +writing the output to the buffer. +The function +.BI buf_vputstrf suff +does the same, +except that it reads arguments from a +.B va_list +captured argument tail, +leaving the tail ready to read the next unprocessed argument. +Both functions return the number of bytes written on success +or \-1 on failure. +These functions add framing around the output: +either a length prefix, or a trailing zero byte. +.PP +The +.BI BUF_ENCLOSE suff +macros are syntactically statement heads. +(Notice that these macros use +.RB ` _L ' +and +.RB ` _B ' +suffixes for little- and big-endian byte order.) +They leave space in the buffer for appropriate length framing, +and execute the following +.I body +statement +(which, of course, can be a compound statement enclosed in braces). +When the +.I body +completes, the macro fills in space +with the length of material written by the +.IR body . +The +.I mk +argument should be a variable of type +.B size_t +which will be overwritten by the macro. +If the material is so large that its won't fit in the space +then the buffer is broken. +The +.B BUF_ENCLOSEZ +macro is similar, +except that it just writes a terminating zero byte +after whatever material was written by the +.IR body . +.PP +The +.BR BUF_ENCLOSE ...\& +macros are based on lower-level machinery. +The +.B BUF_ENCLOSEITAG +macro takes an additional argument +.IR W ; +it leaves +.BI SZ_ W +bytes for the length, +checks that the length doesn't exceed +.BI MASK W \fR, +and stores the length using +.BI STORE W \fR; +all of these constants and macros are defined in +.BR . +The +.B BUF_ENCLOSEKTAG +is similar, except that it uses the +.B kludge64 +machinery to handle 64-bit length fields. +The +.B BUF_ENCLOSEZTAG +macro is superficially similar, +but much simpler, +since it all it does is write a zero byte after its +.I body +completes. +All of those macros also take an additional +.I tag +argument +used to scope the internal labels they construct: +see +.BR control (3) +for the details on how this works. +.PP +The +.B BUF_ENCLOSEITAG +and +.B BUF_ENCLOSEKTAG +macros are themselves built from a lower-level macro named +.BR BUF_ENCLOSETAG . +In place of the +.I W +argument, it takes three arguments: +.I check +is an expression which should evaluate true if the length +.B _delta +can be represented; +.I poke +is a macro, invoked as +.IB poke "(unsigned char *" p ", " size_t n ")" \fR, +which should store +.I n +at address +.IR p , +formatted in whatever way is appropriate; +and +.I lensz +is the amount of space, in bytes, to save for the length. +. +.SS "Dynamic buffers" +The type +.B dbuf +is a +.IR "dynamic buffer" . +It contains a buffer structure, +accessible using the +p.B DBUF_BUF +macro. +The ordinary buffer functions and macros can be used on this buffer, +though, for convenience, +there are similarly named functions and macros +which accept a +.B dbuf +argument directly. +There is +.I "no difference" +between the behaviour of the +.B "buf" +and +.B "dbuf" +functions. +.PP +A dynamic buffer is created by statically initializing it with +.BR DBUF_INIT , +or by calling +.BR dbuf_create +or its macro equivalent +.BR DBCREATE . +The memory backing a dynamic buffer can be freed by +.BR dbuf_destroy +or the macro equivalent +.BR DBDESTROY ; +these leave the buffer in the state established by initialization: +the buffer holds no resources, but is ready for immediate use. +.PP +A dynamic buffer contains an +.B buf +buffer, +called its +.I underlying +buffer. +The underlying buffer is accessible through the +.B DBUF_BUF +macro. +All of the above functions and macros can be applied +to a dynamic buffer's underlying buffer. +As a convenience, +corresponding to each of the functions and macros described above, +there is a version named with an initial +.RB ` d ' +or +.RB ` D ' +as appropriate, +which accepts a pointer to a dynamic buffer +rather than an ordinary buffer, +and acts on its underlying buffer. +Note that these functions are in no way special. +A dynamic buffer will grow automatically +in response to either kind of functions. +.PP +A freshly created buffer is in +.I write +mode, +and is empty, with. +In this state, it will automatically extend its backing storage +in response to +.B BENSURE +calls, rather than breaking. +As a result, +an +.I "a priori" +unpredictable amount of data can be written to a dynamic buffer +and it will automatically grow as necessary to accommodate it. +Of course, the +.B BSZ +and +.B BLEFT +queries are somewhat meaningless when applied to dynamic buffers \(en +though perfectly valid. +The critical function for this is +.B buf_tryextend +(also accessible as +.BR dbuf_tryextend ) +which attempts to arrange that at least +.I sz +unused bytes are available in the buffer \(en +i.e., that +.B BLEFT +would return at least +.IR sz . +If it succeeds, it returns zero; +it will fail if the buffer is not in write mode, +or if the buffer is not dynamic, +in which case it returns \-1. +It is unlikely that applications will call this function directly. +.PP +The +.B buf_flip +(or its macro equivalent) +switches the buffer to +.I read +mode, +in addition to its usual behaviour of +setting the buffer's limit to its current position +and its current position to its base. +In read mode, a dynamic buffer will no longer grow dynamically, +as one would expect. +.PP +The +.B dbuf_reset +function, +and its macro equivalent +.B DBRESET +(which may evaluate its argument multiple times) +will return a dynamic buffer to write mode, +and also restore its current position to its base and +clear its broken flag. +. .SH "SEE ALSO" +.BR bits (3), +.BR control (3), .BR dstr (3), .BR mLib (3). +. .SH AUTHOR Mark Wooding, diff --git a/struct/buf.c b/struct/buf.c index 9efc4fc..88934c9 100644 --- a/struct/buf.c +++ b/struct/buf.c @@ -27,10 +27,10 @@ /*----- Header files ------------------------------------------------------*/ -#include #include #include "buf.h" +#include "growbuf.h" #include "macros.h" /*----- Main code ---------------------------------------------------------*/ @@ -63,11 +63,7 @@ void buf_init(buf *b, void *p, size_t sz) * and ready for writing. */ -void dbuf_create(dbuf *db) -{ - db->_b.base = db->_b.p = db->_b.limit = 0; db->_b.f = BF_ALLOC | BF_WRITE; - db->a = &arena_stdlib; db->sz = 0; -} +void dbuf_create(dbuf *db) { DBCREATE(db); } /* --- @dbuf_reset@ --- * * @@ -89,11 +85,7 @@ void dbuf_reset(dbuf *db) { DBRESET(db); } * Use: Release all of the resources held by a dynamic buffer. */ -void dbuf_destroy(dbuf *db) -{ - if (db->_b.base) x_free(db->a, db->_b.base); - dbuf_create(db); -} +void dbuf_destroy(dbuf *db) { DBDESTROY(db); } /* --- @{,d}buf_break@ --- * * @@ -104,8 +96,8 @@ void dbuf_destroy(dbuf *db) * Use: Marks a buffer as broken. */ -int buf_break(buf *b) { b->f |= BF_BROKEN; return (-1); } -int (dbuf_break)(dbuf *db) { return (dbuf_break(db)); } +int buf_break(buf *b) { BBREAK(b); return (-1); } +int (dbuf_break)(dbuf *db) { DBBREAK(db); return (-1); } /* --- @{,d}buf_flip@ --- * * @@ -118,7 +110,7 @@ int (dbuf_break)(dbuf *db) { return (dbuf_break(db)); } */ void buf_flip(buf *b) { BFLIP(b); } -void (dbuf_flip)(dbuf *db) { dbuf_flip(db); } +void (dbuf_flip)(dbuf *db) { DBFLIP(db); } /* --- @{,d}buf_ensure@ --- * * @@ -147,22 +139,17 @@ int (dbuf_ensure)(dbuf *db, size_t sz) { return (dbuf_ensure(db, sz)); } int buf_tryextend(buf *b, size_t sz) { dbuf *db; - size_t newsz, len; - - if (~b->f&(BF_ALLOC | BF_WRITE)) - { b->f |= BF_BROKEN; return (-1); } - db = (dbuf *)b; - len = BLEN(&db->_b); sz += len; - if (db->sz >= sz) - newsz = db->sz; - else { - newsz = db->sz ? 2*db->sz : 64; - while (newsz < sz) { assert(newsz < ((size_t)-1)/2); newsz *= 2; } - if (!db->_b.base) db->_b.base = x_alloc(db->a, newsz); - else db->_b.base = x_realloc(db->a, db->_b.base, newsz, db->sz); - db->_b.p = db->_b.base + len; db->sz = newsz; + size_t want, len; + + if (sz <= BLEFT(b)) return (0); + if (~b->f&(BF_ALLOC | BF_WRITE)) { b->f |= BF_BROKEN; return (-1); } + + db = (dbuf *)b; len = DBLEN(db); want = sz + len; + if (db->sz < want) { + GROWBUF_EXTEND(db->a, db->_b.base, db->sz, want, 64, 1); + db->_b.p = db->_b.base + len; } - db->_b.limit = db->_b.base + newsz; + db->_b.limit = db->_b.base + db->sz; return (0); } int (dbuf_tryextend)(dbuf *db, size_t sz) @@ -374,10 +361,7 @@ static int findz(buf *b, size_t *nn) { octet *p; - if ((p = memchr(BCUR(b), 0, BLEFT(b))) == 0) { - buf_break(b); - return (-1); - } + if ((p = memchr(BCUR(b), 0, BLEFT(b))) == 0) { BBREAK(b); return (-1); } *nn = p - BCUR(b) + 1; return (0); } @@ -423,7 +407,7 @@ static void *getmem_k64(buf *b, size_t *nn_out, kludge64 k) size_t n; ASSIGN64(szmax, (size_t)-1); - if (CMP64(k, >, szmax)) { buf_break(b); return (-1); } + if (CMP64(k, >, szmax)) { BBREAK(b); return (-1); } n = GET64(size_t, k); *nn_out = n; return (buf_get(b, n)); } @@ -478,7 +462,7 @@ void *(dbuf_getmem64b)(dbuf *db, size_t *nn) { \ MUFFLE_WARNINGS_STMT \ (CLANG_WARNING("-Wtautological-constant-out-of-range-compare"), \ - { assert(sz <= MASK##W); }); \ + { if (sz > MASK##W) { BBREAK(b); return (-1); } }); \ if (buf_putu##w(b, sz) || buf_put(b, p, sz)) \ return (-1); \ return (0); \ @@ -526,7 +510,7 @@ int buf_putmemz(buf *b, const void *p, size_t n) { octet *q; - assert(!memchr(p, 0, n)); + if (memchr(p, 0, n)) { BBREAK(b); return (-1); } if ((q = buf_get(b, n + 1)) == 0) return (-1); memcpy(q, p, n); diff --git a/struct/buf.h b/struct/buf.h index 7dd5555..dd5f6e7 100644 --- a/struct/buf.h +++ b/struct/buf.h @@ -142,6 +142,11 @@ extern void buf_init(buf */*b*/, void */*p*/, size_t /*sz*/); */ extern void dbuf_create(dbuf */*db*/); +#define DBCREATE(db) do { \ + (db)->_b.base = (db)->_b.p = (db)->_b.limit = 0; \ + (db)->_b.f = BF_ALLOC | BF_WRITE; \ + (db)->a = &arena_stdlib; (db)->sz = 0; \ +} while (0) /* --- @dbuf_reset@ --- * * @@ -153,7 +158,6 @@ extern void dbuf_create(dbuf */*db*/); */ extern void dbuf_reset(dbuf */*db*/); - #define DBRESET(db) do { \ (db)->_b.p = (db)->_b.base; (db)->_b.limit = (db)->_b.base + (db)->sz; \ (db)->_b.f = ((db)->_b.f&~BF_BROKEN) | BF_WRITE; \ @@ -169,6 +173,11 @@ extern void dbuf_reset(dbuf */*db*/); */ extern void dbuf_destroy(dbuf */*db*/); +#define DBDESTROY(db) do { \ + if ((db)->_b.base) x_free((db)->a, (db)->_b.base); \ + (db)->_b.base = (db)->_b.p = (db)->_b.limit = 0; \ + (db)->_b.f = BF_ALLOC | BF_WRITE; (db)->sz = 0; \ +} while (0) /* --- @{,d}buf_break@ --- * * @@ -182,6 +191,8 @@ extern void dbuf_destroy(dbuf */*db*/); extern int buf_break(buf */*b*/); extern int dbuf_break(dbuf */*db*/); #define dbuf_break(db) (buf_break(DBUF_BUF(db))) +#define BBREAK(b) do { (b)->f |= BF_BROKEN; } while (0) +#define DBBREAK(db) BBREAK(DBUF_BUF(db)) /* --- @{,d}buf_flip@ --- * * @@ -601,7 +612,7 @@ BUF_DOSUFFIXES(BUF_DECL_PUTSTR_) /* --- @{,d}buf_getf64{,l,b} --- * * - * Arguments: @buf *b@ = pointer to a bfufer block + * Arguments: @buf *b@ = pointer to a buffer block * @double *x_out@ = where to put the result * * Returns: Zero on success, @-1@ on failure (and the buffer is broken). @@ -683,8 +694,8 @@ extern int dbuf_putf64b(dbuf */*db*/, double /*x*/); }) \ MC_AFTER(tag##__poke, { \ size_t _delta = BLEN(b) - (mk) - (lensz); \ - assert(check); \ - if (BOK(b)) poke(BBASE(b) + (mk), _delta); \ + if (!(check)) (b)->f |= BF_BROKEN; \ + else if (BOK(b)) poke(BBASE(b) + (mk), _delta); \ }) #define DBUF_ENCLOSETAG(tag, b, mk, check, poke, lensz) \ @@ -715,7 +726,7 @@ extern int dbuf_putf64b(dbuf */*db*/, double /*x*/); #define BUF_ENCLOSEITAG(tag, b, mk, W) \ BUF_ENCLOSETAG(tag, (b), (mk), (_delta <= MASK##W), STORE##W, SZ_##W) #define BUF_ENCLOSEKTAG(tag, b, mk, W) \ - BUF_ENCLOSE(tag, (b), (mk), 1, BUF_STORESZK##W, 8) + BUF_ENCLOSETAG(tag, (b), (mk), 1, BUF_STORESZK##W, 8) #define BUF_ENCLOSEZTAG(tag, b) \ MC_AFTER(tag##__zero, { buf_putbyte((b), 0); }) @@ -743,43 +754,43 @@ extern int dbuf_putf64b(dbuf */*db*/, double /*x*/); #define BUF_ENCLOSE8(b, mk) BUF_ENCLOSEITAG(encl, (b), (mk), 8) #define BUF_ENCLOSE16(b, mk) BUF_ENCLOSEITAG(encl, (b), (mk), 16) -#define BUF_ENCLOSE16_B(b, mk) BUF_ENCLOSEITAG(encl, (b), (mk), 16_B) #define BUF_ENCLOSE16_L(b, mk) BUF_ENCLOSEITAG(encl, (b), (mk), 16_L) +#define BUF_ENCLOSE16_B(b, mk) BUF_ENCLOSEITAG(encl, (b), (mk), 16_B) #define BUF_ENCLOSE24(b, mk) BUF_ENCLOSEITAG(encl, (b), (mk), 24) -#define BUF_ENCLOSE24_B(b, mk) BUF_ENCLOSEITAG(encl, (b), (mk), 24_B) #define BUF_ENCLOSE24_L(b, mk) BUF_ENCLOSEITAG(encl, (b), (mk), 24_L) +#define BUF_ENCLOSE24_B(b, mk) BUF_ENCLOSEITAG(encl, (b), (mk), 24_B) #define BUF_ENCLOSE32(b, mk) BUF_ENCLOSEITAG(encl, (b), (mk), 32) -#define BUF_ENCLOSE32_B(b, mk) BUF_ENCLOSEITAG(encl, (b), (mk), 32_B) #define BUF_ENCLOSE32_L(b, mk) BUF_ENCLOSEITAG(encl, (b), (mk), 32_L) +#define BUF_ENCLOSE32_B(b, mk) BUF_ENCLOSEITAG(encl, (b), (mk), 32_B) #ifdef HAVE_UINT64 # define BUF_ENCLOSE64(b, mk) BUF_ENCLOSEITAG(encl, (b), (mk), 64) -# define BUF_ENCLOSE64_B(b, mk) BUF_ENCLOSEITAG(encl, (b), (mk), 64_B) # define BUF_ENCLOSE64_L(b, mk) BUF_ENCLOSEITAG(encl, (b), (mk), 64_L) +# define BUF_ENCLOSE64_B(b, mk) BUF_ENCLOSEITAG(encl, (b), (mk), 64_B) #else # define BUF_ENCLOSE64(b, mk) BUF_ENCLOSEKTAG(encl, (b), (mk), 64) -# define BUF_ENCLOSE64_B(b, mk) BUF_ENCLOSEKTAG(encl, (b), (mk), 64_B) # define BUF_ENCLOSE64_L(b, mk) BUF_ENCLOSEKTAG(encl, (b), (mk), 64_L) +# define BUF_ENCLOSE64_B(b, mk) BUF_ENCLOSEKTAG(encl, (b), (mk), 64_B) #endif #define BUF_ENCLOSEZ(b) BUF_ENCLOSEZTAG(encl, (b)) #define DBUF_ENCLOSE8(db, mk) DBUF_ENCLOSEITAG(encl, (db), (mk), 8) #define DBUF_ENCLOSE16(db, mk) DBUF_ENCLOSEITAG(encl, (db), (mk), 16) -#define DBUF_ENCLOSE16_B(db, mk) DBUF_ENCLOSEITAG(encl, (db), (mk), 16_B) #define DBUF_ENCLOSE16_L(db, mk) DBUF_ENCLOSEITAG(encl, (db), (mk), 16_L) +#define DBUF_ENCLOSE16_B(db, mk) DBUF_ENCLOSEITAG(encl, (db), (mk), 16_B) #define DBUF_ENCLOSE24(db, mk) DBUF_ENCLOSEITAG(encl, (db), (mk), 24) -#define DBUF_ENCLOSE24_B(db, mk) DBUF_ENCLOSEITAG(encl, (db), (mk), 24_B) #define DBUF_ENCLOSE24_L(db, mk) DBUF_ENCLOSEITAG(encl, (db), (mk), 24_L) +#define DBUF_ENCLOSE24_B(db, mk) DBUF_ENCLOSEITAG(encl, (db), (mk), 24_B) #define DBUF_ENCLOSE32(db, mk) DBUF_ENCLOSEITAG(encl, (db), (mk), 32) -#define DBUF_ENCLOSE32_B(db, mk) DBUF_ENCLOSEITAG(encl, (db), (mk), 32_B) #define DBUF_ENCLOSE32_L(db, mk) DBUF_ENCLOSEITAG(encl, (db), (mk), 32_L) +#define DBUF_ENCLOSE32_B(db, mk) DBUF_ENCLOSEITAG(encl, (db), (mk), 32_B) #ifdef HAVE_UINT64 # define DBUF_ENCLOSE64(db, mk) DBUF_ENCLOSEITAG(encl, (db), (mk), 64) -# define DBUF_ENCLOSE64_B(db, mk) DBUF_ENCLOSEITAG(encl, (db), (mk), 64_B) # define DBUF_ENCLOSE64_L(db, mk) DBUF_ENCLOSEITAG(encl, (db), (mk), 64_L) +# define DBUF_ENCLOSE64_B(db, mk) DBUF_ENCLOSEITAG(encl, (db), (mk), 64_B) #else # define DBUF_ENCLOSE64(db, mk) DBUF_ENCLOSEKTAG(encl, (db), (mk), 64) -# define DBUF_ENCLOSE64_B(db, mk) DBUF_ENCLOSEKTAG(encl, (db), (mk), 64_B) # define DBUF_ENCLOSE64_L(db, mk) DBUF_ENCLOSEKTAG(encl, (db), (mk), 64_L) +# define DBUF_ENCLOSE64_B(db, mk) DBUF_ENCLOSEKTAG(encl, (db), (mk), 64_B) #endif #define DBUF_ENCLOSEZ(db) DBUF_ENCLOSEZTAG(encl, (db)) diff --git a/struct/darray.3 b/struct/darray.3 index a48bedc..6037c74 100644 --- a/struct/darray.3 +++ b/struct/darray.3 @@ -58,10 +58,11 @@ darray \- dense, dynamically resizing arrays .nf .B "#include " +.ta 2n .B "typedef struct {" -.B "\h'4n'size_t sz, len, off;" -.B "\h'4n'unsigned push, unshift;" -.B "\h'4n'arena *a;" +.B " size_t sz, len, off;" +.B " unsigned push, unshift;" +.B " arena *a;" .B "} da_base;" .B "#define DA_INIT ..." diff --git a/struct/darray.c b/struct/darray.c index aa7e4a2..0b6c223 100644 --- a/struct/darray.c +++ b/struct/darray.c @@ -34,6 +34,7 @@ #include "alloc.h" #include "arena.h" #include "darray.h" +#include "growbuf.h" /*----- Magic numbers -----------------------------------------------------*/ @@ -111,8 +112,8 @@ void *da_ensure(da_base *b, void *v, size_t sz, size_t n) * two which is big enough, starting at double the current size. */ - nsz = v ? b->sz + b->off : (DA_INITSZ >> 1); - do nsz <<= 1; while (nsz < rq); + nsz = b->sz + b->off; + GROWBUF_SIZE(nsz, rq, DA_INITSZ, sz); /* --- Reallocate the block --- * * @@ -219,8 +220,8 @@ void *da_shunt(da_base *b, void *v, size_t sz, size_t n) * two which is big enough, starting at double the current size. */ - nsz = v ? b->sz + b->off : (DA_INITSZ >> 1); - do nsz <<= 1; while (nsz < rq); + nsz = b->sz + b->off; + GROWBUF_SIZE(nsz, rq, DA_INITSZ, sz); /* --- Reallocate the block --- * * diff --git a/struct/dstr-putf.c b/struct/dstr-putf.c index 48c2f6a..c1bde8e 100644 --- a/struct/dstr-putf.c +++ b/struct/dstr-putf.c @@ -51,9 +51,9 @@ */ static int putch(void *out, int ch) - { dstr *d = out; DPUTC(d, ch); return (0); } + { dstr *d = out; DPUTC(d, ch); return (1); } static int putm(void *out, const char *p, size_t sz) - { dstr *d = out; DPUTM(d, p, sz); return (0); } + { dstr *d = out; DPUTM(d, p, sz); return (sz); } static int nputf(void *out, size_t maxsz, const char *p, ...) { diff --git a/struct/dstr.c b/struct/dstr.c index 7e154ae..eee34cc 100644 --- a/struct/dstr.c +++ b/struct/dstr.c @@ -33,6 +33,7 @@ #include "alloc.h" #include "dstr.h" +#include "growbuf.h" /*----- Tunable constants -------------------------------------------------*/ @@ -91,29 +92,7 @@ void dstr_reset(dstr *d) { DRESET(d); } */ void dstr_ensure(dstr *d, size_t sz) -{ - size_t rq = d->len + sz; - size_t nsz; - - /* --- If we have enough space, just leave it --- */ - - if (rq <= d->sz) - return; - - /* --- Grow the buffer --- */ - - nsz = d->sz; - - if (nsz == 0) - nsz = (DSTR_INITSZ >> 1); - do nsz <<= 1; while (nsz < rq); - - if (d->buf) - d->buf = x_realloc(d->a, d->buf, nsz, d->sz); - else - d->buf = x_alloc(d->a, nsz); - d->sz = nsz; -} + { GROWBUF_EXTEND(d->a, d->buf, d->sz, d->len + sz, DSTR_INITSZ, 1); } /* --- @dstr_putc@ --- * * diff --git a/struct/hash.3 b/struct/hash.3 index 19522ee..c067275 100644 --- a/struct/hash.3 +++ b/struct/hash.3 @@ -38,15 +38,16 @@ hash \- low-level hashtable implementation .nf .B "#include " +.ta 2n .B "typedef struct {" -.B "\h'4n'uint32 mask;" -.B "\h'4n'hash_base **v;" -.B "\h'4n'arena *a;" +.B " uint32 mask;" +.B " hash_base **v;" +.B " arena *a;" .B "} hash_table;" .B "typedef struct {" -.B "\h'4n'hash_base *next;" -.B "\h'4n'uint32 hash;" +.B " hash_base *next;" +.B " uint32 hash;" .B "} hash_base;" .B "typedef struct { ...\& } hash_iter;" diff --git a/struct/sym.3 b/struct/sym.3 index 045ea54..a2afcaf 100644 --- a/struct/sym.3 +++ b/struct/sym.3 @@ -36,10 +36,10 @@ sym \- symbol table manager .BI "void sym_create(sym_table *" t ); .BI "void sym_destroy(sym_table *" t ); -.ds mT \fBvoid *sym_find( -.BI "\*(mTsym_table *" t , -.BI "\h'\w'\*(mT'u'const char *" n ", long " l , -.BI "\h'\w'\*(mT'u'size_t " sz ", unsigned *" f ); +.ta \w'\fBvoid *sym_find('u +.BI "void *sym_find(sym_table *" t , +.BI " const char *" n ", long " l , +.BI " size_t " sz ", unsigned *" f ); .BI "void sym_remove(sym_table *" t ", void *" b ); .BI "const char *SYM_NAME(const void *" p ); diff --git a/sys/fdflags.3 b/sys/fdflags.3 index 3fd51fc..2ed2fa9 100644 --- a/sys/fdflags.3 +++ b/sys/fdflags.3 @@ -19,10 +19,10 @@ fdflags \- set file and file descriptor flags .nf .B "#include " -.ds mT \fBint fdflags( -.BI "\*(mTint " fd , -.BI "\h'\w'\*(mT'u'unsigned " fbic ", unsigned " fxor , -.BI "\h'\w'\*(mT'u'unsigned " fdbic ", unsigned " fdxor ); +.ta \w'\fBint fdflags('u +.BI "int fdflags(int " fd , +.BI " unsigned " fbic ", unsigned " fxor , +.BI " unsigned " fdbic ", unsigned " fdxor ); .fi .SH "DESCRIPTION" .B fdflags diff --git a/sys/lock.3 b/sys/lock.3 index 2d80fad..d7b18e5 100644 --- a/sys/lock.3 +++ b/sys/lock.3 @@ -7,10 +7,11 @@ lock \- oversimplified file locking interface .nf .B "#include " +.ta 2n .B "enum {" -.B "\h'4n'LOCK_UNLOCK = ...," -.B "\h'4n'LOCK_EXCL = ...," -.B "\h'4n'LOCK_NONEXCL = ..." +.B " LOCK_UNLOCK = ...," +.B " LOCK_EXCL = ...," +.B " LOCK_NONEXCL = ..." .B "};" .BI "int lock_file(int " fd ", unsigned " how ); diff --git a/sys/mdup.3 b/sys/mdup.3 index 1e1b836..f08c13e 100644 --- a/sys/mdup.3 +++ b/sys/mdup.3 @@ -26,9 +26,10 @@ mdup \- renumber file descriptors .nf .B "#include " +.ta 2n .B "typedef struct {" -.B "\h'4'int cur;" -.B "\h'4n'int want;" +.B " int cur;" +.B " int want;" .B "} mdup_fd;" .BI "int mdup(mdup_fd *" v ", size_t " n ");" @@ -116,13 +117,14 @@ int i; if (pipe(p_in) || pipe(p_out) || pipe(p_err)) goto error; if ((kid = fork()) < 0) goto error; if (!kid) { - if (dup2(p_in[0], STDIN_FILENO) < 0 || - dup2(p_out[1], STDOUT_FILENO) < 0 || - dup2(p_err[2], STDERR_FILENO) < 0 || - close(p_in[0]) || close(p_out[0]) || close(p_err[0]) || - close(p_in[1]) || close(p_out[1]) || close(p_err[1])) - _exit(127); - execvp("/bin/sh", "sh", "-c", "...", (char *)0); +.ta 2n 4n 2n+\w'\fBif ('u + if (dup2(p_in[0], STDIN_FILENO) < 0 || + dup2(p_out[1], STDOUT_FILENO) < 0 || + dup2(p_err[2], STDERR_FILENO) < 0 || + close(p_in[0]) || close(p_out[0]) || close(p_err[0]) || + close(p_in[1]) || close(p_out[1]) || close(p_err[1])) + _exit(127); + execvp("/bin/sh", "sh", "-c", "...", (char *)0); } \&... .VE @@ -145,6 +147,7 @@ the child. Here's how to rewrite the above function using .BR mdup . .VS +.ta 2n 4n 2n+\w'\fBmd[0].cur = p_out[1]; 'u #define P_INIT { \-1, \-1 } int p_in[2] = P_INIT, p_out[2] = P_INIT, p_err[2] = P_INIT; pid_t kid = -1; @@ -154,13 +157,13 @@ int i; if (pipe(p_in) || pipe(p_out) || pipe(p_err)) goto error; if ((kid = fork()) < 0) goto error; if (!kid) { - if (close(p_in[1] || close(p_out[0]) || close(p_err[0])) - goto _exit(127); - md[0].cur = p_in[0]; md[0].want = STDIN_FILENO; - md[1].cur = p_out[1]; md[1].want = STDOUT_FILENO; - md[2].cur = p_err[1]; md[2].want = STDERR_FILENO; - if (mdup(md, 3)) _exit(127); - execvp("/bin/sh", "sh", "-c", "...", (char *)0); + if (close(p_in[1] || close(p_out[0]) || close(p_err[0])) + goto _exit(127); + md[0].cur = p_in[0]; md[0].want = STDIN_FILENO; + md[1].cur = p_out[1]; md[1].want = STDOUT_FILENO; + md[2].cur = p_err[1]; md[2].want = STDERR_FILENO; + if (mdup(md, 3)) _exit(127); + execvp("/bin/sh", "sh", "-c", "...", (char *)0); } \&... .VE diff --git a/test/t/tvec-test.c b/test/t/tvec-test.c index 0d93bc1..b6ba605 100644 --- a/test/t/tvec-test.c +++ b/test/t/tvec-test.c @@ -239,6 +239,10 @@ static void test_copy_bytes memcpy(out->v.bytes.p, in->v.bytes.p, in->v.bytes.sz); } +static void test_copy_buffer + (const struct tvec_reg *in, struct tvec_reg *out, void *ctx) + { tvec_initbuffer(&out->v, &in->v, in->v.buf.sz); } + #define test_copy_int test_copy_simple #define test_copy_uint test_copy_simple #define test_copy_ienum test_copy_simple @@ -249,7 +253,6 @@ static void test_copy_bytes #define test_copy_flags test_copy_simple #define test_copy_float test_copy_simple #define test_copy_fltish test_copy_simple -#define test_copy_buffer test_copy_bytes #define COPYREG(name, i, ty, argslot, argval) \ static DSGINIT(const) struct tvec_regdef name##_copyregs[] = { \ @@ -493,7 +496,7 @@ static const struct tvec_remotefork sleep_testenv = { TVEC_REMOTEFORK(&sleep_subenv._env, 0) }; static const struct tvec_regdef sleep_regs[] = { - { "time", RV, &tvty_float, 0, { &tvflt_nonneg } }, + { "time", RV, &tvty_duration, 0, { &tvflt_nonneg } }, { "z", RVOUT, &tvty_float, 0, { &tvflt_nonneg } }, TVEC_ENDREGS }; diff --git a/test/testrig.3 b/test/testrig.3 index 07e1f48..b6e30f3 100644 --- a/test/testrig.3 +++ b/test/testrig.3 @@ -21,24 +21,25 @@ testrig \- generic test rig .B "#define TEST_FIELDMAX ..." +.ta 2n .B "typedef struct {" -.B "\h'4n'unsigned tests, failed;" +.B " unsigned tests, failed;" .B "} test_results"; .B "typedef struct {" -.BI "\h'4n'void (*cvt)(const char *" buf ", dstr *" d ); -.BI "\h'4n'void (*dump)(dstr *" d ", FILE *" fp ); +.BI " void (*cvt)(const char *" buf ", dstr *" d ); +.BI " void (*dump)(dstr *" d ", FILE *" fp ); .B "} test_type"; .B "typedef struct {" -.B "\h'4n'const char *name;" -.BI "\h'4n'void (*test)(dstr " dv "[]);" -.B "\h'4n'const test_type *f[TEST_FIELDMAX];" +.B " const char *name;" +.BI " void (*test)(dstr " dv "[]);" +.B " const test_type *f[TEST_FIELDMAX];" .B "} test_chunk"; .B "typedef struct {" -.B "\h'4n'const char *name;" -.B "\h'4n'const test_chunk *chunks;" +.B " const char *name;" +.B " const test_chunk *chunks;" .B "} test_suite"; .B "const test_type type_hex;" @@ -48,14 +49,12 @@ testrig \- generic test rig .B "const test_type type_ulong;" .B "const test_type type_uint32;" -.ds mT \fBint test_do( -.BI "\*(mTconst test_suite " suite [], -.BI "\h'\w'\*(mT'u'FILE *" fp , -.BI "\h'\w'\*(mT'u'test_results *" results ); -.ds mT \fBvoid test_run( -.BI "\*(mTint " argc ", char *" argv [], -.BI "\h'\w'\*(mT'u'const test_chunk " chunk [], -.BI "\h'\w'\*(mT'u'const char *" def ); +.ta \w'\fBint test_do('u +.BI "int test_do(const test_suite " suite [], +.BI " FILE *" fp ", test_results *" results ); +.ta \w'\fBvoid test_run('u +.BI "void test_run(int " argc ", char *" argv [], +.BI " const test_chunk " chunk "[], const char *" def ); .fi .SH DESCRIPTION .SS Structure diff --git a/test/tests.at b/test/tests.at index 456729d..22e200a 100644 --- a/test/tests.at +++ b/test/tests.at @@ -362,30 +362,33 @@ AT_CLEANUP ###-------------------------------------------------------------------------- AT_SETUP([tvec type-buffer]) -test_parse([buffer], [16], [16 B]) -test_parse([buffer], [16;?], [16 B]) -test_parse([buffer], [16 ;?], [16 B]) -test_parse([buffer], [16384], [16 kB]) -test_parse([buffer], [16777216], [16 MB]) -test_parse([buffer], [16k], [16 kB]) -test_parse([buffer], [16k;?], [16 kB]) -test_parse([buffer], [16k ;?], [16 kB]) -test_parse([buffer], [16 k], [16 kB]) -test_parse([buffer], [16 k;?], [16 kB]) -test_parse([buffer], [16 k ;?], [16 kB]) -test_parse([buffer], [16kB], [16 kB]) -test_parse([buffer], [16kB;?], [16 kB]) -test_parse([buffer], [16kB ;?], [16 kB]) -test_parse([buffer], [16 kB], [16 kB]) -test_parse([buffer], [16 kB;?], [16 kB]) -test_parse([buffer], [16 kB ;?], [16 kB]) +test_parse([buffer], [16], [16 B ; = 16 = 0x10]) +test_parse([buffer], [16;?], [16 B ; = 16 = 0x10]) +test_parse([buffer], [16 ;?], [16 B ; = 16 = 0x10]) +test_parse([buffer], [16384], [16 kB ; = 16384 = 0x4000]) +test_parse([buffer], [16777216], [16 MB ; = 16777216 = 0x01000000]) +test_parse([buffer], [16k], [16 kB ; = 16384 = 0x4000]) +test_parse([buffer], [16k;?], [16 kB ; = 16384 = 0x4000]) +test_parse([buffer], [16k ;?], [16 kB ; = 16384 = 0x4000]) +test_parse([buffer], [16 k], [16 kB ; = 16384 = 0x4000]) +test_parse([buffer], [16 k;?], [16 kB ; = 16384 = 0x4000]) +test_parse([buffer], [16 k ;?], [16 kB ; = 16384 = 0x4000]) +test_parse([buffer], [16kB], [16 kB ; = 16384 = 0x4000]) +test_parse([buffer], [16kB;?], [16 kB ; = 16384 = 0x4000]) +test_parse([buffer], [16kB ;?], [16 kB ; = 16384 = 0x4000]) +test_parse([buffer], [16 kB], [16 kB ; = 16384 = 0x4000]) +test_parse([buffer], [16 kB;?], [16 kB ; = 16384 = 0x4000]) +test_parse([buffer], [16 kB ;?], [16 kB ; = 16384 = 0x4000]) + +test_parse([buffer], [16777216@4096+17], + [16 MB @ 4 kB + 17 B ; = 16777216 @ 4096 + 17 = 0x01000000 @ 0x1000 + 0x11]) test_parserr([buffer], [16!], [3], [invalid buffer length `16!']) test_parserr([buffer], [16 !], [3], [invalid buffer length `16 !']) test_parserr([buffer], [16 k!], [3], [invalid buffer length `16 k!']) test_parserr([buffer], [16 kB!], [3], [invalid buffer length `16 kB!']) test_parserr([buffer], [16 kB !], - [3], [syntax error: expected end-of-line but found `!']) + [3], [syntax error: expected `@' but found `!']) test_parserr([buffer], [16 EB], [3], [buffer length `16 EB' out of range]) AT_CLEANUP diff --git a/test/tvec-core.c b/test/tvec-core.c index 51b46d2..6a98f73 100644 --- a/test/tvec-core.c +++ b/test/tvec-core.c @@ -33,6 +33,7 @@ #include #include "alloc.h" +#include "growbuf.h" #include "tvec.h" /*----- Output ------------------------------------------------------------*/ @@ -469,6 +470,7 @@ int tvec_nexttoken(struct tvec_state *tv) * * Arguments: @struct tvec_state *tv@ = test-vector state * @dstr *d@ = string to append the word to + * @const char **p_inout@ = pointer into string, updated * @const char *delims@ = additional delimiters to stop at * @const char *expect@, @va_list ap@ = what was expected * @@ -491,38 +493,54 @@ int tvec_nexttoken(struct tvec_state *tv) * word constituents, a null terminator is written to @d@, and * it is safe to treat the string in @d@ as being null- * terminated. + * + * If @p_inout@ is not null, then @*p_inout@ must be a pointer + * into @d->buf@, which will be adjusted so that it will + * continue to point at the same position even if the buffer is + * reallocated. As a subtle tweak, if @*p_inout@ initially + * points at the end of the buffer, then it will be adjusted to + * point at the beginning of the next word, rather than at the + * additional intervening space. */ -int tvec_readword(struct tvec_state *tv, dstr *d, const char *delims, - const char *expect, ...) +int tvec_readword(struct tvec_state *tv, dstr *d, const char **p_inout, + const char *delims, const char *expect, ...) { va_list ap; int rc; va_start(ap, expect); - rc = tvec_readword_v(tv, d, delims, expect, &ap); + rc = tvec_readword_v(tv, d, p_inout, delims, expect, &ap); va_end(ap); return (rc); } -int tvec_readword_v(struct tvec_state *tv, dstr *d, const char *delims, - const char *expect, va_list *ap) +int tvec_readword_v(struct tvec_state *tv, dstr *d, const char **p_inout, + const char *delims, const char *expect, va_list *ap) { + size_t pos = 0; int ch; + tvec_skipspc(tv); + ch = getc(tv->fp); if (!ch || ch == '\n' || ch == EOF || ch == ';' || (delims && strchr(delims, ch))) { if (expect) return (tvec_syntax(tv, ch, expect, ap)); else { ungetc(ch, tv->fp); return (-1); } } - if (d->len) DPUTC(d, ' '); + if (p_inout) pos = *p_inout - d->buf; + if (d->len) { + if (pos == d->len) pos++; + DPUTC(d, ' '); + } do { DPUTC(d, ch); ch = getc(tv->fp); } while (ch && ch != EOF && !isspace(ch) && (!delims || !strchr(delims, ch))); DPUTZ(d); if (ch != EOF) ungetc(ch, tv->fp); + if (p_inout) *p_inout = d->buf + pos; return (0); } @@ -1000,8 +1018,7 @@ int tvec_read(struct tvec_state *tv, const char *infile, FILE *fp) /* Read the group name. There may be leading and trailing * whitespace. */ - tvec_skipspc(tv); - DRESET(&d); tvec_readword(tv, &d, "];", "group name"); + DRESET(&d); tvec_readword(tv, &d, 0, "];", "group name"); tvec_skipspc(tv); ch = getc(tv->fp); if (ch != ']') tvec_syntax(tv, ch, "`]'"); @@ -1062,7 +1079,8 @@ int tvec_read(struct tvec_state *tv, const char *infile, FILE *fp) */ ungetc(ch, tv->fp); DRESET(&d); - if (tvec_readword(tv, &d, "=:;", "register name")) goto flush_line; + if (tvec_readword(tv, &d, 0, "=:;", "register name")) + goto flush_line; /* Now there should be a separator. */ tvec_skipspc(tv); ch = getc(tv->fp); @@ -1103,12 +1121,8 @@ int tvec_read(struct tvec_state *tv, const char *infile, FILE *fp) if (vd->regsz <= sizeof(rbuf)) r = &rbuf; else { - if (rsz < vd->regsz) { - xfree(r_alloc); - if (!rsz) rsz = 8*sizeof(void *); - while (rsz < vd->regsz) rsz *= 2; - r_alloc = xmalloc(rsz); - } + GROWBUF_REPLACE(&arena_stdlib, r_alloc, rsz, vd->regsz, + 8*sizeof(void *), 1); r = r_alloc; } diff --git a/test/tvec-output.c b/test/tvec-output.c index 8c9c214..cacd94c 100644 --- a/test/tvec-output.c +++ b/test/tvec-output.c @@ -673,7 +673,8 @@ static int human_nwritef(void *go, size_t maxsz, const char *p, ...) va_start(ap, p); n = gprintf_memputf(&h->outbuf, &h->outsz, maxsz, p, ap); va_end(ap); - return (layout_string(&h->lyt, h->outbuf, n)); + if (layout_string(&h->lyt, h->outbuf, n)) return (-1); + return (n); } static const struct gprintf_ops human_printops = @@ -1273,10 +1274,20 @@ struct tap_output { */ static int tap_writech(void *go, int ch) - { struct tap_output *t = go; return (layout_char(&t->lyt, ch)); } +{ + struct tap_output *t = go; + + if (layout_char(&t->lyt, ch)) return (-1); + else return (1); +} static int tap_writem(void *go, const char *p, size_t sz) - { struct tap_output *t = go; return (layout_string(&t->lyt, p, sz)); } +{ + struct tap_output *t = go; + + if (layout_string(&t->lyt, p, sz)) return (-1); + else return (sz); +} static int tap_nwritef(void *go, size_t maxsz, const char *p, ...) { @@ -1287,7 +1298,8 @@ static int tap_nwritef(void *go, size_t maxsz, const char *p, ...) va_start(ap, p); n = gprintf_memputf(&t->outbuf, &t->outsz, maxsz, p, ap); va_end(ap); - return (layout_string(&t->lyt, t->outbuf, n)); + if (layout_string(&t->lyt, t->outbuf, n)) return (-1); + return (n); } static const struct gprintf_ops tap_printops = diff --git a/test/tvec-remote.c b/test/tvec-remote.c index b87351b..1a3bd0e 100644 --- a/test/tvec-remote.c +++ b/test/tvec-remote.c @@ -44,6 +44,7 @@ #include "buf.h" #include "compiler.h" #include "fdflags.h" +#include "growbuf.h" #include "lbuf.h" #include "mdup.h" #include "quis.h" @@ -81,7 +82,7 @@ static void init_comms(struct tvec_remotecomms *rc) { - rc->bin = 0; rc->binsz = 0; dbuf_create(&rc->bout); + rc->bin = 0; rc->binsz = 0; DBCREATE(&rc->bout); rc->infd = rc->outfd = -1; rc->f = 0; } @@ -120,7 +121,7 @@ static void close_comms(struct tvec_remotecomms *rc) */ static void release_comms(struct tvec_remotecomms *rc) - { close_comms(rc); xfree(rc->bin); dbuf_destroy(&rc->bout); } + { close_comms(rc); xfree(rc->bin); DBDESTROY(&rc->bout); } /* --- @setup_comms@ --- * * @@ -378,13 +379,7 @@ static int receive_buffered(struct tvec_state *tv, if (rc->binlen - rc->binoff >= want) return (RECV_OK); /* If the buffer is too small then we must grow it. */ - if (want > rc->binsz) { - sz = rc->binsz; if (!sz) sz = RECVBUFSZ; - while (sz < want) { assert(sz < (size_t)-1/2); sz *= 2; } - if (!rc->bin) rc->bin = xmalloc(sz); - else rc->bin = xrealloc(rc->bin, sz, rc->binsz); - rc->binsz = sz; - } + GROWBUF_EXTEND(&arena_stdlib, rc->bin, rc->binsz, want, RECVBUFSZ, 1); /* Shunt the unused existing material to the start of the buffer. */ memmove(rc->bin, rc->bin + rc->binoff, rc->binlen - rc->binoff); @@ -712,12 +707,8 @@ int tvec_remoteserver(int infd, int outfd, const struct tvec_config *config) if (vd->regsz <= sizeof(rbuf)) r = &rbuf; else { - if (rsz < vd->regsz) { - xfree(r_alloc); - if (!rsz) rsz = 8*sizeof(void *); - while (rsz < vd->regsz) rsz *= 2; - r_alloc = xmalloc(rsz); - } + GROWBUF_REPLACE(&arena_stdlib, r_alloc, rsz, vd->regsz, + 8*sizeof(void *), 1); r = r_alloc; } diff --git a/test/tvec-types.c b/test/tvec-types.c index e76d4a1..068d565 100644 --- a/test/tvec-types.c +++ b/test/tvec-types.c @@ -107,7 +107,7 @@ static int unsigned_from_buf(buf *b, unsigned long *u_out) ASSIGN64(ulmax, ULONG_MAX); if (buf_getk64l(b, &k)) return (-1); - if (CMP64(k, >, ulmax)) return (-1); + if (CMP64(k, >, ulmax)) { buf_break(b); return (-1); } *u_out = GET64(unsigned long, k); return (0); } @@ -161,7 +161,7 @@ static int signed_from_buf(buf *b, long *i_out) else { CPL64(k, k); if (CMP64(k, <=, not_lmin)) *i_out = -(long)GET64(unsigned long, k) - 1; - else return (-1); + else { buf_break(b); return (-1); } } return (0); } @@ -387,6 +387,90 @@ static int parse_signed(long *i_out, const char *p, if (check_signed_range(i, ir, tv)) return (-1); *i_out = i; return (0); } +static const char size_units[] = "kMGTPEZY"; + +/* --- @parse_size@ --- * + * + * Arguments: @struct tvec_state *tv@ = test-vector state + * @size_t *u_out@ = where to put the answer + * @const char *delims@ = delimiters + * @const char *what@ = description of what we're parsing + * + * Returns: Zero on success, %$-1$% on failure. + * + * Use: Parse a memory size. + */ + +static int parse_size(struct tvec_state *tv, size_t *u_out, + const char *delims, const char *what) +{ + dstr d = DSTR_INIT; + const char *p, *unit; + unsigned long u, t; + int rc; + unsigned f = 0; +#define f_range 1u + + if (tvec_readword(tv, &d, 0, delims, what)) { rc = -1; goto end; } + p = d.buf; + if (parse_unsigned_integer(&u, &p, p)) goto bad; + if (!*p) tvec_readword(tv, &d, &p, delims, 0); + + if (u > (size_t)-1) goto rangerr; + for (t = u, unit = size_units; *unit; unit++) { + if (t > (size_t)-1/1024) f |= f_range; + else t *= 1024; + if (*p == *unit) { + if (f&f_range) goto rangerr; + u = t; p++; break; + } + } + if (*p == 'B') p++; + if (*p) goto bad; + + *u_out = u; rc = 0; +end: + dstr_destroy(&d); + return (rc); + +bad: + tvec_error(tv, "invalid %s `%s'", what, d.buf); + rc = -1; goto end; + +rangerr: + tvec_error(tv, "%s `%s' out of range", what, d.buf); + rc = -1; goto end; + +#undef f_range +} + +/* --- @format_size@ --- * + * + * Arguments: @const struct gprintf_ops *gops@ = print operations + * @void *go@ = print destination + * @unsigned long u@ = a size + * @unsigned style@ = style (@TVSF_...@) + * + * Returns: --- + * + * Use: Format @u@ as a size in bytes to the destination, expressing + * it with a unit prefix if this is possible exactly. + */ + +static void format_size(const struct gprintf_ops *gops, void *go, + unsigned long u, unsigned style) +{ + const char *unit; + + if (!u || u%1024) + gprintf(gops, go, "%lu%sB", u, style&TVSF_COMPACT ? "" : " "); + else { + for (unit = size_units, u /= 1024; + !(u%1024) && unit[1]; + u /= 1024, unit++); + gprintf(gops, go, "%lu%s%cB", u, style&TVSF_COMPACT ? "" : " ", *unit); + } +} /*----- Floating-point utilities ------------------------------------------*/ @@ -534,8 +618,8 @@ static void format_floating(const struct gprintf_ops *gops, void *go, * * Use: Parse a floating-point number from a string. Reports any * necessary errors. If @q_out@ is not null then trailing - * material is permitted and a pointer to it is left in - * @*q_out@; this will be null if there is no trailing material. + * material is permitted and a pointer to it (or the end of the + * string) is left in @*q_out@. */ static int parse_floating(double *x_out, const char **q_out, const char *p, @@ -547,11 +631,10 @@ static int parse_floating(double *x_out, const char **q_out, const char *p, double x; int olderr, rc; - if (q_out) *q_out = 0; - /* Check for special tokens. */ if (STRCMP(p, ==, "#nan")) { #ifdef NAN + if (q_out) *q_out = p + strlen(p); x = NAN; rc = 0; #else tvec_error(tv, "NaN not supported on this system"); @@ -562,6 +645,7 @@ static int parse_floating(double *x_out, const char **q_out, const char *p, else if (STRCMP(p, ==, "#inf") || STRCMP(p, ==, "#+inf") || STRCMP(p, ==, "+#inf")) { #ifdef INFINITY + if (q_out) *q_out = p + strlen(p); x = INFINITY; rc = 0; #else tvec_error(tv, "infinity not supported on this system"); @@ -571,6 +655,7 @@ static int parse_floating(double *x_out, const char **q_out, const char *p, else if (STRCMP(p, ==, "#-inf") || STRCMP(p, ==, "-#inf")) { #ifdef INFINITY + if (q_out) *q_out = p + strlen(p); x = -INFINITY; rc = 0; #else tvec_error(tv, "infinity not supported on this system"); @@ -593,9 +678,8 @@ static int parse_floating(double *x_out, const char **q_out, const char *p, /* Parse the number using the system parser. */ olderr = errno; errno = 0; x = strtod(p, &q); - if (!*q) /* nothing to do */; - else if (q_out) *q_out = q; - else { tvec_syntax(tv, *q, "end-of-line"); rc = -1; goto end; } + if (q_out) *q_out = q; + else if (*q) { tvec_syntax(tv, *q, "end-of-line"); rc = -1; goto end; } if (errno && (errno != ERANGE || (x > 0 ? -x : x) == HUGE_VAL)) { tvec_error(tv, "invalid floating-point number `%.*s': %s", (int)(q - p), p, strerror(errno)); @@ -1135,7 +1219,7 @@ static int read_compound_string(void **p_inout, size_t *sz_inout, ungetc(ch, tv->fp); if (cdc && flush_codec(cdc, &d, tv)) { rc = -1; goto end; } cdc = 0; - DRESET(&w); tvec_readword(tv, &w, ";", "character name"); + DRESET(&w); tvec_readword(tv, &w, 0, ";", "character name"); if (read_charname(&ch, w.buf, RCF_EOFOK)) { rc = tvec_error(tv, "unknown character name `%s'", d.buf); goto end; @@ -1148,7 +1232,7 @@ static int read_compound_string(void **p_inout, size_t *sz_inout, if (cdc && flush_codec(cdc, &d, tv)) { rc = -1; goto end; } cdc = 0; ungetc(ch, tv->fp); - DRESET(&w); tvec_readword(tv, &w, ";", "`!'-keyword"); + DRESET(&w); tvec_readword(tv, &w, 0, ";", "`!'-keyword"); /* Change bareword coding system. */ if (STRCMP(w.buf, ==, "!bare")) @@ -1167,7 +1251,7 @@ static int read_compound_string(void **p_inout, size_t *sz_inout, goto end; } DRESET(&w); - if (tvec_readword(tv, &w, ";{", "repeat count")) + if (tvec_readword(tv, &w, 0, ";{", "repeat count")) { rc = -1; goto end; } if (parse_unsigned_integer(&n, &q, w.buf)) { rc = tvec_error(tv, "invalid repeat count `%s'", w.buf); @@ -1213,7 +1297,8 @@ static int read_compound_string(void **p_inout, size_t *sz_inout, default: assert(ccl); ungetc(ch, tv->fp); DRESET(&w); - if (tvec_readword(tv, &w, ";", "%s-encoded fragment", ccl->name)) + if (tvec_readword(tv, &w, 0, ";", + "%s-encoded fragment", ccl->name)) { rc = -1; goto end; } if (!cdc) cdc = ccl->decoder(cdf); err = cdc->ops->code(cdc, w.buf, w.len, &d); @@ -1370,7 +1455,8 @@ static int parse_int(union tvec_regval *rv, const struct tvec_regdef *rd, dstr d = DSTR_INIT; int rc; - if (tvec_readword(tv, &d, ";", "signed integer")) { rc = -1; goto end; } + if (tvec_readword(tv, &d, 0, ";", "signed integer")) + { rc = -1; goto end; } if (parse_signed(&rv->i, d.buf, rd->arg.p, tv)) { rc = -1; goto end; } if (tvec_flushtoeol(tv, 0)) { rc = -1; goto end; } rc = 0; @@ -1385,7 +1471,8 @@ static int parse_uint(union tvec_regval *rv, const struct tvec_regdef *rd, dstr d = DSTR_INIT; int rc; - if (tvec_readword(tv, &d, ";", "unsigned integer")) { rc = -1; goto end; } + if (tvec_readword(tv, &d, 0, ";", "unsigned integer")) + { rc = -1; goto end; } if (parse_unsigned(&rv->u, d.buf, rd->arg.p, tv)) { rc = -1; goto end; } if (tvec_flushtoeol(tv, 0)) { rc = -1; goto end; } rc = 0; @@ -1612,7 +1699,7 @@ static int parse_float(union tvec_regval *rv, const struct tvec_regdef *rd, dstr d = DSTR_INIT; int rc; - if (tvec_readword(tv, &d, ";", "floating-point number")) + if (tvec_readword(tv, &d, 0, ";", "floating-point number")) { rc = -1; goto end; } if (parse_floating(&rv->f, 0, d.buf, rd->arg.p, tv)) { rc = -1; goto end; } @@ -1806,21 +1893,17 @@ static int parse_duration(union tvec_regval *rv, { const struct duration_unit *u; const char *q; - dstr d = DSTR_INIT; size_t pos; + dstr d = DSTR_INIT; double t; int rc; - if (tvec_readword(tv, &d, ";", "duration")) { rc = -1; goto end; } + if (tvec_readword(tv, &d, 0, ";", "duration")) { rc = -1; goto end; } if (parse_floating(&t, &q, d.buf, rd->arg.p ? rd->arg.p : &tvflt_nonneg, tv)) { rc = -1; goto end; } - if (!q) { - tvec_skipspc(tv); pos = d.len; - if (!tvec_readword(tv, &d, ";", 0)) q = d.buf + pos + 1; - } - - if (q) { + if (!*q) tvec_readword(tv, &d, &q, ";", 0); + if (*q) { for (u = duration_units; u->unit; u++) if (STRCMP(q, ==, u->unit)) { t *= u->scale; goto found_unit; } rc = tvec_syntax(tv, *q, "end-of-line"); goto end; @@ -2001,7 +2084,7 @@ static int frombuf_penum(buf *b, union tvec_regval *rv, if (signed_from_buf(b, &i)) return (-1); if (0 <= i && i < n) rv->p = (/*unconst*/ void *)pei->av[i].p; else if (i == -1) rv->p = 0; - else return (-1); + else { buf_break(b); return (-1); } return (0); } @@ -2032,7 +2115,7 @@ static int frombuf_penum(buf *b, union tvec_regval *rv, dstr d = DSTR_INIT; \ int rc; \ \ - if (tvec_readword(tv, &d, ";", "enumeration tag or " LITSTR_##tag_)) \ + if (tvec_readword(tv, &d, 0, ";", "enumeration tag or " LITSTR_##tag_)) \ { rc = -1; goto end; } \ for (a = ei->av; a->tag; a++) \ if (STRCMP(a->tag, ==, d.buf)) { FOUND_##tag_ goto done; } \ @@ -2293,7 +2376,7 @@ static int parse_flags(union tvec_regval *rv, const struct tvec_regdef *rd, /* Read the next item. */ DRESET(&d); - if (tvec_readword(tv, &d, "|;", "flag name or integer")) + if (tvec_readword(tv, &d, 0, "|;", "flag name or integer")) { rc = -1; goto end; } /* Try to find a matching entry in the table. */ @@ -2436,7 +2519,7 @@ static int tobuf_char(buf *b, const union tvec_regval *rv, if (0 <= rv->i && rv->i <= UCHAR_MAX) u = rv->i; else if (rv->i == EOF) u = MASK32; - else return (-1); + else { buf_break(b); return (-1); } return (buf_putu32l(b, u)); } @@ -2462,7 +2545,7 @@ static int frombuf_char(buf *b, union tvec_regval *rv, if (buf_getu32l(b, &u)) return (-1); if (0 <= u && u <= UCHAR_MAX) rv->i = u; else if (u == MASK32) rv->i = EOF; - else return (-1); + else { buf_break(b); return (-1); } return (0); } @@ -2553,7 +2636,8 @@ static int parse_char(union tvec_regval *rv, const struct tvec_regdef *rd, */ ungetc(ch, tv->fp); - if (tvec_readword(tv, &d, ";", "character name")) { rc = -1; goto end; } + if (tvec_readword(tv, &d, 0, ";", "character name")) + { rc = -1; goto end; } if (STRCMP(d.buf, !=, "#")) { if (read_charname(&ch, d.buf, RCF_EOFOK)) { rc = tvec_error(tv, "unknown character name `%s'", d.buf); @@ -3182,7 +3266,37 @@ void tvec_allocbytes(union tvec_regval *rv, size_t sz) /*----- Buffer type -------------------------------------------------------*/ -/* Buffers are initialized and released as binary strings. */ +/* --- @init_buffer@ --- * + * + * Arguments: @union tvec_regval *rv@ = register value + * @const struct tvec_regdef *rd@ = register definition + * + * Returns: --- + * + * Use: Initialize a register value. + * + * Buffer values values are initialized with a null pointer, + * zero length, and zero residue, modulus, and offset. + */ + +static void init_buffer(union tvec_regval *rv, const struct tvec_regdef *rd) + { rv->buf.p = 0; rv->buf.sz = rv->buf.a = rv->buf.m = rv->buf.off = 0; } + +/* --- @release_buffer@, @release_bytes@ --- * + * + * Arguments: @const union tvec_regval *rv@ = register value + * @const struct tvec_regdef *rd@ = register definition + * + * Returns: --- + * + * Use: Release resources held by a register value. + * + * Buffers are freed. + */ + +static void release_buffer(union tvec_regval *rv, + const struct tvec_regdef *rd) + { if (rv->buf.p) xfree(rv->buf.p - rv->buf.off); } /* --- @eq_buffer@ --- * * @@ -3193,14 +3307,19 @@ void tvec_allocbytes(union tvec_regval *rv, size_t sz) * * Use: Compare register values for equality. * - * Buffer values are equal if and only if their sizes are equal; - * their contents are %%\emph{not}%% compared. + * Buffer values are equal if and only if their sizes and + * alignment parameters are equal; their contents are + * %%\emph{not}%% compared. */ static int eq_buffer(const union tvec_regval *rv0, const union tvec_regval *rv1, const struct tvec_regdef *rd) - { return (rv0->bytes.sz == rv1->bytes.sz); } +{ + return (rv0->buf.sz == rv1->buf.sz && + rv0->buf.a == rv1->buf.a && + rv0->buf.m == rv1->buf.m); +} /* --- @tobuf_buffer@ --- * * @@ -3212,27 +3331,17 @@ static int eq_buffer(const union tvec_regval *rv0, * * Use: Serialize a register value to a buffer. * - * Buffer values are serialized as just their lengths, as - * unsigned integers. + * Buffer values are serialized as their lengths, residues, and + * moduli, as unsigned integers. */ static int tobuf_buffer(buf *b, const union tvec_regval *rv, const struct tvec_regdef *rd) - { return (unsigned_to_buf(b, rv->bytes.sz)); } - -/* --- @allocate_buffer@ --- * - * - * Arguments: @union tvec_regval *rv@ = register value - * @size_t sz@ = size to allocate - * - * Returns: --- - * - * Use: Allocate @sz@ bytes to the buffer and fill the space with a - * distinctive pattern. - */ - -static void allocate_buffer(union tvec_regval *rv, size_t sz) - { tvec_allocbytes(rv, sz); memset(rv->bytes.p, '?', sz); } +{ + return (unsigned_to_buf(b, rv->buf.sz) || + unsigned_to_buf(b, rv->buf.a) || + unsigned_to_buf(b, rv->buf.m)); +} /* --- @frombuf_buffer@ --- * * @@ -3252,11 +3361,14 @@ static void allocate_buffer(union tvec_regval *rv, size_t sz) static int frombuf_buffer(buf *b, union tvec_regval *rv, const struct tvec_regdef *rd) { - unsigned long u; + unsigned long sz, a, m; - if (unsigned_from_buf(b, &u)) return (-1); - if (u > (size_t)-1) return (-1); - allocate_buffer(rv, u); + if (unsigned_from_buf(b, &sz)) return (-1); + if (unsigned_from_buf(b, &a)) return (-1); + if (unsigned_from_buf(b, &m)) return (-1); + if (sz > (size_t)-1 || a > (size_t)-1 || m > (size_t)-1) + { buf_break(b); return (-1); } + rv->buf.sz = sz; rv->buf.a = a; rv->buf.m = m; return (0); } @@ -3279,56 +3391,42 @@ static int frombuf_buffer(buf *b, union tvec_regval *rv, * pattern. */ -static const char units[] = "kMGTPEZY"; - static int parse_buffer(union tvec_regval *rv, const struct tvec_regdef *rd, struct tvec_state *tv) { - dstr d = DSTR_INIT; - const char *q, *unit; - size_t pos; - unsigned long u, t; - int rc; - unsigned f = 0; -#define f_range 1u + unsigned long sz, a = 0, m = 0; + int ch, rc; - if (tvec_readword(tv, &d, ";", "buffer length")) { rc = -1; goto end; } - if (parse_unsigned_integer(&u, &q, d.buf)) goto bad; - if (!*q) { - tvec_skipspc(tv); pos = d.len; - if (!tvec_readword(tv, &d, ";", 0)) pos++; - q = d.buf + pos; - } + if (parse_size(tv, &sz, "@;", "buffer length")) { rc = -1; goto end; } + if (check_string_length(sz, rd->arg.p, tv)) { rc = -1; goto end; } - if (u > (size_t)-1) goto rangerr; - for (t = u, unit = units; *unit; unit++) { - if (t > (size_t)-1/1024) f |= f_range; - else t *= 1024; - if (*q == *unit) { - if (f&f_range) goto rangerr; - u = t; q++; break; - } + tvec_skipspc(tv); + ch = getc(tv->fp); + if (ch == ';' || ch == '\n') { ungetc(ch, tv->fp); goto done; } + else if (ch != '@') { rc = tvec_syntax(tv, ch, "`@'"); goto end; } + + if (parse_size(tv, &m, "+;", "alignment quantum")) { rc = -1; goto end; } + if (m == 1) m = 0; + + tvec_skipspc(tv); + ch = getc(tv->fp); + if (ch == ';' || ch == '\n') { ungetc(ch, tv->fp); goto done; } + else if (ch != '+') { rc = tvec_syntax(tv, ch, "`+'"); goto end; } + + if (parse_size(tv, &a, ";", "alignment offset")) { rc = -1; goto end; } + if (a >= m) { + rc = tvec_error(tv, "alignment offset %lu >= quantum %lu", + (unsigned long)a, (unsigned long)m); + goto end; } - if (*q == 'B') q++; - if (*q) goto bad; - if (check_string_length(u, rd->arg.p, tv)) { rc = -1; goto end; } +done: if (tvec_flushtoeol(tv, 0)) { rc = -1; goto end; } - allocate_buffer(rv, u); + rv->buf.sz = sz; rv->buf.a = a; rv->buf.m = m; rc = 0; end: - DDESTROY(&d); return (rc); - -bad: - tvec_error(tv, "invalid buffer length `%s'", d.buf); - rc = -1; goto end; - -rangerr: - tvec_error(tv, "buffer length `%s' out of range", d.buf); - rc = -1; goto end; - -#undef f_range + return (rc); } /* --- @dump_buffer@ --- * @@ -3352,22 +3450,80 @@ static void dump_buffer(const union tvec_regval *rv, unsigned style, const struct gprintf_ops *gops, void *go) { - const char *unit; - unsigned long u = rv->bytes.sz; - - if (!u || u%1024) - gprintf(gops, go, "%lu B", u); - else { - for (unit = units, u /= 1024; !(u%1024) && unit[1]; u /= 1024, unit++); - gprintf(gops, go, "%lu %cB", u, *unit); + format_size(gops, go, rv->buf.sz, style); + if (rv->buf.m) { + gprintf(gops, go, style&TVSF_COMPACT ? "@" : " @ "); + format_size(gops, go, rv->buf.m, style); + if (rv->buf.a) { + gprintf(gops, go, style&TVSF_COMPACT ? "+" : " + "); + format_size(gops, go, rv->buf.a, style); + } + } + if (!(style&TVSF_COMPACT)) { + gprintf(gops, go, " ; = %lu", rv->buf.sz); + if (rv->buf.m) { + gprintf(gops, go, " @ %lu", rv->buf.m); + if (rv->buf.a) gprintf(gops, go, " + %lu", rv->buf.a); + } + gprintf(gops, go, " = "); format_unsigned_hex(gops, go, rv->buf.sz); + if (rv->buf.m) { + gprintf(gops, go, " @ "); format_unsigned_hex(gops, go, rv->buf.m); + if (rv->buf.a) { + gprintf(gops, go, " + "); + format_unsigned_hex(gops, go, rv->buf.a); + } + } } } /* Buffer type definition. */ const struct tvec_regty tvty_buffer = { - init_bytes, release_bytes, eq_buffer, + init_buffer, release_buffer, eq_buffer, tobuf_buffer, frombuf_buffer, parse_buffer, dump_buffer }; +/* --- @tvec_initbuffer@ --- * + * + * Arguments: @union tvec_regval *rv@ = register value + * @const union tvec_regval *src@ = source buffer + * @size_t sz@ = size to allocate + * + * Returns: --- + * + * Use: Initialize the alignment parameters in @rv@ to match @src@, + * and the size to @sz@. + */ + +void tvec_initbuffer(union tvec_regval *rv, + const union tvec_regval *src, size_t sz) + { rv->buf.sz = sz; rv->buf.a = src->buf.a; rv->buf.m = src->buf.m; } + +/* --- @tvec_allocbuffer@ --- * + * + * Arguments: @union tvec_regval *rv@ = register value + * + * Returns: --- + * + * Use: Allocate @sz@ bytes to the buffer and fill the space with a + * distinctive pattern. + */ + +void tvec_allocbuffer(union tvec_regval *rv) +{ + unsigned char *p; size_t n; + + if (rv->buf.p) xfree(rv->buf.p - rv->buf.off); + + if (rv->buf.m < 2) { + rv->buf.p = xmalloc(rv->buf.sz); rv->buf.off = 0; + } else { + p = xmalloc(rv->buf.sz + rv->buf.m - 1); + n = (size_t)p%rv->buf.m; + rv->buf.off = (rv->buf.a - n + rv->buf.m)%rv->buf.m; + rv->buf.p = p + rv->buf.off; + } + memset(rv->buf.p, '?', rv->buf.sz); +} + /*----- That's all, folks -------------------------------------------------*/ diff --git a/test/tvec.3 b/test/tvec.3 index 06a1211..c0cb3c3 100644 --- a/test/tvec.3 +++ b/test/tvec.3 @@ -8,17 +8,7 @@ tvec \- test vector framework .SH SYNOPSIS .nf -.B "#include - - - -extern int tvec_serialize(const struct tvec_reg */*rv*/, - const struct tvec_regdef */*regs*/, - unsigned /*nr*/, size_t /*regsz*/, - void **/*p_out*/, size_t */*sz_out*/); - -extern int tvec_deserialize(struct tvec_reg */*rv*/, - const struct tvec_regdef */*regs*/, - unsigned /*nr*/, size_t /*regsz*/, - const void */*p*/, size_t /*sz*/); +.B "#include " +.B "union tvec_misc {" +.B diff --git a/test/tvec.h b/test/tvec.h index 1e1e154..b2ae674 100644 --- a/test/tvec.h +++ b/test/tvec.h @@ -194,6 +194,11 @@ union tvec_regval { double f; /* floating point */ struct { unsigned char *p; size_t sz; } bytes; /* binary string of bytes */ struct { char *p; size_t sz; } text; /* text string */ + struct { /* buffer */ + unsigned char *p; size_t sz; /* binary string */ + size_t a, m; /* residue and modulus */ + size_t off; /* offset into full buffer */ + } buf; #ifdef TVEC_REGSLOTS TVEC_REGSLOTS #endif @@ -514,7 +519,7 @@ enum { TVBU_BYTE, /* counting bytes (@rbuf >= 0@) */ TVBU_LIMIT /* (number of units) */ }; -struct bench_timing; /* forward declaration */ +struct bench_timing; /* include for the real definition */ struct tvec_outops { /* Output operations. */ @@ -1727,6 +1732,7 @@ extern int tvec_nexttoken(struct tvec_state */*tv*/); * * Arguments: @struct tvec_state *tv@ = test-vector state * @dstr *d@ = string to append the word to + * @const char **p_inout@ = pointer into string, updated * @const char *delims@ = additional delimiters to stop at * @const char *expect@, @va_list ap@ = what was expected * @@ -1749,14 +1755,23 @@ extern int tvec_nexttoken(struct tvec_state */*tv*/); * word constituents, a null terminator is written to @d@, and * it is safe to treat the string in @d@ as being null- * terminated. + * + * If @p_inout@ is not null, then @*p_inout@ must be a pointer + * into @d->buf@, which will be adjusted so that it will + * continue to point at the same position even if the buffer is + * reallocated. As a subtle tweak, if @*p_inout@ initially + * points at the end of the buffer, then it will be adjusted to + * point at the beginning of the next word, rather than at the + * additional intervening space. */ -extern PRINTF_LIKE(4, 5) +extern PRINTF_LIKE(5, 6) int tvec_readword(struct tvec_state */*tv*/, dstr */*d*/, - const char */*delims*/, const char */*expect*/, ...); + const char **/*p_inout*/, const char */*delims*/, + const char */*expect*/, ...); extern int tvec_readword_v(struct tvec_state */*tv*/, dstr */*d*/, - const char */*delims*/, const char */*expect*/, - va_list */*ap*/); + const char **/*p_inout*/, const char */*delims*/, + const char */*expect*/, va_list */*ap*/); /*----- Integer types: signed and unsigned --------------------------------*/ @@ -2419,13 +2434,24 @@ extern void tvec_allocbytes(union tvec_regval */*rv*/, size_t /*sz*/); /*----- Buffer type -------------------------------------------------------*/ /* Buffer registers are primarily used for benchmarking. Only a buffer's - * size is significant: its contents are ignored on comparison and output, - * and unspecified on input. + * allocation parameters are significant: its contents are ignored on + * comparison and output, and unspecified on input. + * + * The input format gives the buffer's size, and an optional alignment + * specification, in the form %|SZ [`@' M [`+' A]]|%. Each of %|SZ|%, %|M|% + * and %|A|% are sizes, as an integer, optionally suffixed with a unit `kB', + * `MB', `GB', `TB', `PB', `EB', `ZB', `YB' (with or without the `B') + * denoting a power of 1024. The %|SZ|% gives the (effective) buffer size. + * %|M|% is the `alignment quantum' and %|A|% is the `alignment offset'; both + * default to zero, but if %|M|% is nonzero then the start of the buffer is + * aligned such that it is %|A|% more than a multiple of %|M|% bytes. Note + * that %|M|% need not be a power of two, though this is common. * - * The input is simply the buffer size, as an integer, optionally suffixed - * with a unit `kB', `MB', `GB', `TB', `PB', `EB', `ZB', `YB' (with or - * without the `B') denoting a power of 1024. Units are used on output only - * when the size would be expressed exactly. + * Units other than `B' are used on output only when the size would be + * expressed exactly. + * + * Buffers are %%\emph{not}%% allocated by default. In benchmarks, this is + * best done in a @before@ function. * * No @claimeq@ functions or macros are provided for buffers because they * don't seem very useful. @@ -2433,6 +2459,33 @@ extern void tvec_allocbytes(union tvec_regval */*rv*/, size_t /*sz*/); extern const struct tvec_regty tvty_buffer; +/* --- @tvec_initbuffer@ --- * + * + * Arguments: @union tvec_regval *rv@ = register value + * @const union tvec_regval *src@ = source buffer + * @size_t sz@ = size to allocate + * + * Returns: --- + * + * Use: Initialize the alignment parameters in @rv@ to match @src@, + * and the size to @sz@. + */ + +extern void tvec_initbuffer(union tvec_regval */*rv*/, + const union tvec_regval */*src*/, size_t /*sz*/); + +/* --- @tvec_allocbuffer@ --- * + * + * Arguments: @union tvec_regval *rv@ = register value + * + * Returns: --- + * + * Use: Allocate @sz@ bytes to the buffer and fill the space with a + * distinctive pattern. + */ + +extern void tvec_allocbuffer(union tvec_regval */*rv*/); + /*----- That's all, folks -------------------------------------------------*/ #ifdef __cplusplus diff --git a/trace/trace.3 b/trace/trace.3 index b541cb8..1a40587 100644 --- a/trace/trace.3 +++ b/trace/trace.3 @@ -17,22 +17,21 @@ trace \- configurable tracing output .B "#include " .BI "void trace(unsigned " l ", const char *" f ", ...);" -.ds mT \fBvoid trace_block( -.BI "\*(mTunsigned " l ", const char *" s , -.BI "\h'\w'\*(mT'u'const void *" b ", size_t " sz ); +.ta \w'\fBvoid trace_block('u +.BI "void trace_block(unsigned " l ", const char *" s , +.BI " const void *" b ", size_t " sz ); .BI "void trace_on(FILE *" fp ", unsigned " l ); -.ds mT \fBvoid trace_custom( -.ds mU \*(mTvoid (*\fIfunc\fB)( -.BI "\*(mUconst char *" buf , -.BI "\h'\w'\*(mU'u'size_t " sz ", void *" v ), -.BI "\h'\w'\*(mT'u'void *" v ); +.ta \w'\fBvoid trace_custom('u +\w'\fBvoid (*\,\fIfunc\/\fB)('u +.BI "void trace_custom(void (*" func ")(const char *" buf , +.BI " size_t " sz ", void *" v ), +.BI " void *" v ); .BI "void trace_level(unsigned " l ); .BI "unsigned tracing(void);" -.ds mT \fBunsigned traceopt( -.BI "\*(mTconst trace_opt *" t ", const char *" p , -.BI "\h'\w'\*(mT'u'unsigned " f ", unsigned " bad ); +.ta \w'\fBunsigned traceopt('u +.BI "unsigned traceopt(const trace_opt *" t ", const char *" p , +.BI " unsigned " f ", unsigned " bad ); .BI T( statements\fR... ) .BI "IF_TRACING(unsigned " l ", " statements\fR... ) diff --git a/ui/mdwopt.3 b/ui/mdwopt.3 index f186e7d..253b635 100644 --- a/ui/mdwopt.3 +++ b/ui/mdwopt.3 @@ -7,20 +7,21 @@ mdwopt \- command-line option parser .nf .B "#include " +.ta 2n .B "typedef struct {" -.B "\h'4n'char *arg, *prog;" -.B "\h'4n'int opt, ind, err;" -.B "\h'4n'..." +.B " char *arg, *prog;" +.B " int opt, ind, err;" +.B " ..." .B "} mdwopt_data;" .B "char *optarg, optprog;" .B "int optopt, opterr, optind;" .B "struct option {" -.B "\h'4n'const char *name;" -.B "\h'4n'int has_arg;" -.B "\h'4n'int *flag;" -.B "\h'4n'int val;" +.B " const char *name;" +.B " int has_arg;" +.B " int *flag;" +.B " int val;" .B "};" .B "#define OPTF_NOARG = ..." @@ -40,23 +41,23 @@ mdwopt \- command-line option parser .B "#define OPTF_NEGATED = ..." -.ds mT \fBint mdwopt( -.BI "\*(mTint " argc ", char *const *" argv , -.BI "\h'\w'\*(mT'u'const char *" shortopt , -.BI "\h'\w'\*(mT'u'const struct option *" longopt ", int *" longind , -.BI "\h'\w'\*(mT'u'mdwopt_data *" data ", int " flags ); +.ta \w'\fBint mdwopt('u +.BI "int mdwopt(int " argc ", char *const *" argv , +.BI " const char *" shortopt , +.BI " const struct option *" longopt ", int *" longind , +.BI " mdwopt_data *" data ", int " flags ); .BI "int getopt(int " argc ", char *const *" argv ", const char *" o ); -.ds mT \fBint getopt_long( -.BI "\*(mTint " argc ", char *const *" argv , -.BI "\h'\w'\*(mT'u'const char * "shortopt , -.BI "\h'\w'\*(mT'u'const struct option *" longopt ", int *" longind ); +.ta \w'\fBint getopt_long('u +.BI "int getopt_long(int " argc ", char *const *" argv , +.BI " const char * "shortopt , +.BI " const struct option *" longopt ", int *" longind ); -.ds mT \fBint getopt_long_only( -.BI "\*(mTint " argc ", char *const *" argv , -.BI "\h'\w'\*(mT'u'const char * "shortopt , -.BI "\h'\w'\*(mT'u'const struct option *" longopt ", int *" longind ); +.ta \w'\fBint getopt_long_only('u +.BI "int getopt_long_only(int " argc ", char *const *" argv , +.BI " const char * "shortopt , +.BI " const struct option *" longopt ", int *" longind ); .fi .SH "OVERVIEW" The diff --git a/utils/Makefile.am b/utils/Makefile.am index 240a046..8deae22 100644 --- a/utils/Makefile.am +++ b/utils/Makefile.am @@ -56,7 +56,7 @@ EXTRA_DIST += t/bits-testgen ## Control flow. pkginclude_HEADERS += control.h -##LIBMANS += control.3 +LIBMANS += control.3 check_PROGRAMS += t/control.t t_control_t_SOURCES = t/control-test.c @@ -75,16 +75,16 @@ t_exc_t_LDFLAGS = -static ## Generalized formatting. pkginclude_HEADERS += gprintf.h libutils_la_SOURCES += gprintf.c -##LIBMANS += gprintf.3 +LIBMANS += gprintf.3 ## Linear regression. pkginclude_HEADERS += linreg.h libutils_la_SOURCES += linreg.c -##LIBMANS += linreg.3 +LIBMANS += linreg.3 ## Mathematics. pkginclude_HEADERS += maths.h -##LIBMANS += maths.3 +LIBMANS += maths.3 ## String handling. pkginclude_HEADERS += str.h diff --git a/utils/control.3 b/utils/control.3 index 79e7df9..be83be9 100644 --- a/utils/control.3 +++ b/utils/control.3 @@ -1,5 +1,18 @@ .\" -*-nroff-*- +.de VS +.sp 1 +.RS +.nf +.ft B +.. +.de VE +.ft R +.fi +.RE +.sp 1 +.. .TH control 3 "23 April 2023" "Straylight/Edgeware" "mLib utilities library" +. .SH NAME control \- control structure metaprogramming .\" @MC_BEFORE @@ -17,6 +30,7 @@ control \- control structure metaprogramming .\" @MC_ACT .\" @MC_LABEL .\" @MC_GOTO +. .SH SYNOPSIS .nf .B "#include " @@ -24,9 +38,11 @@ control \- control structure metaprogramming .BI MC_BEFORE( tag ", " stmts ") " body .BI MC_AFTER( tag ", " stmts ") " body .BI MC_WRAP( tag ", " before_stmt ", " onend_stmt ", " onbreak_stmt ") " body +.BI MC_FINALLY( tag ", " cleanup ") " body .BI MC_DOWHILE( tag ", " cond ") " body .BI MC_DECL( tag ", " decl ") " body .BI MC_LOOPELSE( tag ", " head ") " loop_body " \fR[\fBelse " else_body \fR] +.BI MC_LOOPBETWEEN( tag ", " setup ", " cond ", " step ") " loop_body " \fR[\fBelse " else_body \fR] .BI MC_TARGET( tag ", " stmt ") " body .BI MC_GOTARGET( tag ); @@ -37,80 +53,95 @@ control \- control structure metaprogramming .BI MC_LABEL( tag ) .BI MC_GOTO( tag ) .fi +. .SH DESCRIPTION The header file .B -defines a number of macros which are useful when defining new -control structures for C. They are inspired by Simon Tatham's article +defines a number of macros which are useful +when defining new control structures for C. +They are inspired by Simon Tatham's article .IR "Metaprogramming custom control structures in C", though these macros differ from Tatham's in a few respects. +. .SS "Common features" Each of these macros takes a .I tag -argument. A +argument. +A .I tag -is lexically like an identifier, except that it may begin with a digit, -so, for example, plain integers are acceptable tags. Each use of an -action macro by a user-level macro must have a distinct +is lexically like an identifier, +except that it may begin with a digit, +so, for example, plain integers are acceptable tags. +Each use of an action macro by a user-level macro +must have a distinct .IR tag . -If you're writing a new prefix action macro written in terms of these -existing actions, your macro should receive a +If you're writing a new prefix action macro +written in terms of these existing actions, +your macro should receive a .I tag -from its caller, and pass this tag, along with a distinctive component -of its own, down to any prefix actions that it calls; the +from its caller, +and pass this tag, +along with a distinctive component of its own, +down to any prefix actions that it calls; +the .IR tag s from each layer should be separated by a pair of underscores. .PP Some of these macros work by wrapping a loop around the .I body -statement. This interferes with the way that `free' +statement. +This interferes with the way that `free' .B break and .B continue statements within the .I body -behave: we say that these statements are +behave: +we say that these statements are .I captured by the macro. A .B break or .B continue -statement is `free' if it doesn't appear lexically within a loop or +statement is +.I free +if it doesn't appear lexically within a loop or (for .B break) .B switch statement that is part of the .IR body . So -.IP -.B "if (!x) break;" -.PP +.VS +if (!x) break; +.VE contains a free .B break -statement, while -.IP -.nf -.ft B +statement, +while +.VS +.ta 2n for (i = 0; i < n; i++) -\h'4n'if (interestingp(i)) break; -.ft -.fi -.PP + if (interestingp(i)) break; +.VE does not. .PP -Some of these macros take special care to give you control over what -happens when a captured +Some of these macros take special care +to give you control over what happens when a captured .B break -is executed. Alas, proper handling of +is executed. +Alas, proper handling of .B continue -doesn't seem possible. Free +doesn't seem possible. +Free .B break and .B continue statements .I within arguments to these macros are never captured. +. .SS "Prefix action macros" .B MC_BEFORE macro is the simplest to understand. Executing @@ -121,8 +152,9 @@ has the same effect as executing .I stmt followed by .IR body , -except that the whole thing is syntactically a single statement, so, for -example, it doesn't need to be enclosed in braces to be the body of a +except that the whole thing is syntactically a single statement, +so, for example, it doesn't need to be enclosed in braces +to be the body of a .B for loop. .B MC_BEFORE @@ -142,7 +174,8 @@ the same effect as executing .I stmt followed by .IR body . -Again, the whole thing is syntactically a single statement. However, +Again, the whole thing is syntactically a single statement. +However, .B MC_AFTER captures free .B break @@ -178,15 +211,46 @@ statement, then control abruptly continues with the .I onbreak statement, and .I onend -is not executed. Currently, if the +is not executed. +Currently, if the .I body executes a free .B continue -statement, then control abruptly continues with the +statement, +then control abruptly continues with the .I onend -statement, but this behaviour is a bug and may be fixed in the future. +statement, +but this behaviour is a bug and may be fixed in the future. +.PP +Executing +.IP +.BI MC_FINALLY( tag ", " cleanup ") " body .PP -.\" @@@ mc_finally +has the same effect as executing +.I body +followed by +.IR cleanup , +except that a free +.B break +statement within +.I body +will execute +.I cleanup +before propagating the +.B break +to the enclosing context. +A free +.B continue +statement currently causes control to continue abruptly with +.I cleanup +but this behaviour is a bug and may be fixed in the future. +The +.I cleanup +code is textually duplicated, +so there'll be some code bloat if this is very complex. +If it arranges to have private long-term state +then the two copies will not share this state, +so probably don't do this. .PP Executing .IP @@ -206,7 +270,8 @@ except that free .B break and .B continue -statements are captured. Currently, a free +statements are captured. +Currently, a free .B continue statement will simply abruptly terminate execution of the .IR body , @@ -224,47 +289,56 @@ The .B MC_DECL macro makes use of the fact that a .B for -statement can introduce a declaration into its body's scope in C99 and -C++; the macro is not available in C89. +statement can introduce a declaration +into its body's scope in C99 and C++; +the macro is not available in C89. .PP Executing .IP .nf +.ta 2n .BI MC_LOOPELSE( head ", " tag ") " -.RI \h'4n' loop_body +.I " loop_body" .RB [ else -.RI \h'4n' else_body ] +.IR " else_body" ] .fi .PP -results in Python-like loop behaviour. The +results in Python-like loop behaviour. +The .I head must be a valid loop head with one of the forms .IP +.nf .BI "while (" cond ")" -.br .BI "for (" decl "; " cond "; " next_expr ")" -.br .BI "MC_DOWHILE(" tag ", " cond ")" +.fi .PP The resulting loop executes the same as .IP .nf +.ta 2n .I head -.RI \h'4n' loop_body +.I " loop_body" .fi .PP If the loop ends abruptly, as a result of .BR break , -then control is passed to the statement following the loop in the usual -way. However, if the loop completes naturally, and the optional +then control is passed to the statement following the loop +in the usual way. +However, if the loop completes naturally, +and the optional .B else -clause is present, then the +clause is present, +then the .I else_body -is executed. A free +is executed. +A free .B continue statement within the .I loop_body -behaves normally. Free +behaves normally. +Free .B break and .B continue @@ -272,7 +346,63 @@ statements within the .I else_body are not captured. .PP -.\" @@@ loopbetween +Executing +.IP +.nf +.ta 2n +.BI MC_LOOPBETWEEN( tag ", " setup ", " cond ", " step ") " +.I " loop-body" +.RB [ else +.IR " else-body" ] +.fi +.PP +is similar to executing the +.B for +loop +.IP +.ta 2n +.nf +.BI "for (" setup "; " cond "; " step ") " +.I " loop-body" +.fi +.PP +except that, once the +.I loop_body +has finished, +the +.I step +expression evaluated, +and the +.I cond +evaluated and determined to be nonzero, +the +.I else_body +(if any) is executed before re-entering the +.IR loop_body . +This makes it a useful place to insert +any kind of interstitial material, +e.g., printing commas between list items. +Note that by the time the +.I else_body +is executed, +the decision has already been made +that another iteration will be performed, +and, in particular, the +.I step +has occurred. The +.I else_body +is therefore looking at the next item to be processed, +not the item that has just finished being processed. +The +.I cond +is textually duplicated, +so there'll be some code bloat if this is very complex. +If it somehow manages to have private long-term state +(e.g., as a result of declaring static variables +inside GCC statement expressions) +then the two copies will not share this state, +so probably don't do this. +. .SS "Lower-level machinery" Executing .IP @@ -284,7 +414,8 @@ Executing .B MC_GOTARGET immediately transfers control to .IR stmt , -with control continuing with the following statement, skipping the +with control continuing with the following statement, +skipping the .IR body . Free .B break @@ -294,16 +425,24 @@ statements in .I body are not captured. .PP -This is most commonly useful in loops in order to arrange the correct -behaviour of a free +This is most commonly useful in loops +in order to arrange the correct behaviour of a free .B break -within the loop body. See the example below, which shows the definition +within the loop body. +See the example below, +which shows the definition of .BR MC_LOOPELSE . .PP Executing .IP -.BI MC_ALLOWELSE( tag ") " main_body " \fR[\fBelse " else_body \fR] +.nf +.ta 2n +.BI MC_ALLOWELSE( tag ") " +.I " main_body" +.RB [ else +.IR " else_body" ] +.fi .PP has exactly the same effect as just .IR main_body . @@ -313,8 +452,9 @@ Executing .PP transfers control immediately to .I else_body -(if present); control then naturally transfers to the following -statement as usual. Free +(if present); +control then naturally transfers to the following statement as usual. +Free .B break or .B continue @@ -334,40 +474,41 @@ so things will likely to wrong if .I main_body is itself an .B if -statement: if +statement: +if .I main_body lacks an .B else -clause, then an +clause, +then an .B else intended to match .B MC_ALLOWELSE -will be mis-associated; and even if +will be mis-associated; +and even if .I main_body .I does have an .B else -clause, the resulting program text is likely to provoke a compiler -warning about `dangling +clause, +the resulting program text is likely to provoke a compiler warning +about `dangling .BR else '. .PP -Using these tools, it's relatively straightforward to define a macro -like +Using these tools, +it's relatively straightforward to define a macro like .BR MC_LOOPELSE , described above: -.IP -.nf -.ft B -#define MC_LOOPELSE(tag, head) \e -\h'4n'MC_TARGET(tag##__exit, { ; }) \e -\h'4n'MC_ALLOWELSE(tag##__else) \e -\h'4n'MC_AFTER(tag##__after, { MC_GOELSE(tag##__else); }) \e -\h'4n'head \e -\h'8n'MC_WRAP(tag##__body, { ; }, { ; }, \e -\h'8n+\w'MC_WRAP(tag##__body, ''{ MC_GOTARGET(tag##__exit); }) -.ft R -.fi -.PP +.VS +.ta 4n 4n+\w'\fBMC_WRAP(tag##__body, 'u \n(.lu-\n(.iu-4n +#define MC_LOOPELSE(tag, head) \e + MC_TARGET(tag##__exit, { ; }) \e + MC_ALLOWELSE(tag##__else) \e + MC_AFTER(tag##__after, { MC_GOELSE(tag##__else); }) \e + head \e + MC_WRAP(tag##__body, { ; }, { ; }, \e + { MC_GOTARGET(tag##__exit); }) +.VE The main `trick' for these control-flow macros is .BR MC_ACT , which wraps up a statement as an @@ -380,7 +521,8 @@ or .BR while : i.e., it must be completed by following it with a .I body -statement. Executing +statement. +Executing .IP .BI MC_ACT( stmt ") " body .PP @@ -388,9 +530,11 @@ has the same effect as simply executing .IR stmt ; the .I body -is usually ignored. Note that +is usually ignored. +Note that .B ; -is a valid statement which does nothing, so +is a valid statement which does nothing, +so .BI MC_ACT( stmt ); is also a valid statement with the same effect as .IR stmt . @@ -413,12 +557,13 @@ immediately transfers control to the .IR body . Note that .B MC_GOTO -is syntactically an action -(i.e., it's wrapped in -.BR MC_ACT ). +is syntactically an action, +i.e., it's wrapped in +.BR MC_ACT . The .IR tag s -here are scoped to the top-level source line, like all +here are scoped to the top-level source line, +like all .IR tag s in this macro package. .PP @@ -431,26 +576,26 @@ sometimes with one or two other statement heads thrown into the mix. For example, .B MC_AFTER is defined as -.IP -.nf -.ft B -#define MC_AFTER(tag, stmt) \e -\h'28n'MC_GOTO(tag##__body) \e -\h'4n'MC_LABEL(tag##__end) \e -\h'28n'MC_ACT(stmt) \e -\h'28n'for (;;) \e -\h'32n'MC_GOTO(tag##__end) \e -\h'4n'MC_LABEL(tag##__body) -.ft R -.fi -.PP -(The unusual layout is conventional, to make the overall structure of -the code clear despite visual interference from the labels.) +.VS +.ta 4n 28n 30n \n(.lu-\n(.iu-4n +#define MC_AFTER(tag, stmt) \e + MC_GOTO(tag##__body) \e + MC_LABEL(tag##__end) \e + MC_ACT(stmt) \e + for (;;) \e + MC_GOTO(tag##__end) \e + MC_LABEL(tag##__body) +.VE +(The unusual layout is conventional, +to make the overall structure of the code clear +despite visual interference from the labels.) The .I body -appears at the end, labelled as +appears at the end, +labelled as .IB tag __body \fR. -Control enters at the start, and is immediately transferred to the +Control enters at the start, +and is immediately transferred to the .I body ; but the .I body @@ -458,7 +603,8 @@ is enclosed in a .B for loop, so when the .I body -completes, the loop restarts, transferring control to +completes, the loop restarts, +transferring control to .IB tag __end and the .IR stmt . @@ -466,21 +612,33 @@ Since it is enclosed in .BR MC_ACT , once .I stmt -completes, control transfers to the following statement. -.SH BUGS +completes, +control transfers to the following statement. +. +.SH "BUGS" Some macros cause free .B break and/or .B continue statements to behave in unexpected ways. .PP -The need for tagging is ugly, and the restriction on having two -user-facing control-flow macros on the same line is objectionable. The -latter could be avoided by using nonstandard features such as GCC's +It's rather hard to use +.B MC_ALLOWELSE +in practice without provoking +.RB `dangling- else ' +warnings. +.PP +The need for tagging is ugly, +and the restriction on having two +user-facing control-flow macros on the same line is objectionable. +The latter could be avoided +by using nonstandard features such as GCC's .B __COUNTER__ -macro, but adopting that would do programmers a disservice by -introducing a hazard for those trying to port code to other compilers -which lack any such feature. +macro, +but adopting that would do programmers a disservice +by introducing a hazard for those +trying to port code to other compilers which lack any such feature. +. .SH "SEE ALSO" .BR mLib (3), .BR macros (3). @@ -488,5 +646,6 @@ which lack any such feature. Simon Tatham, .IR "Metaprogramming custom control structures in C", .BR "https://www.chiark.greenend.org.uk/~sgtatham/mp/" . +. .SH "AUTHOR" Mark Wooding, diff --git a/utils/gprintf.3 b/utils/gprintf.3 new file mode 100644 index 0000000..c7f8652 --- /dev/null +++ b/utils/gprintf.3 @@ -0,0 +1,227 @@ +.\" -*-nroff-*- +.de VS +.sp 1 +.RS +.nf +.ft B +.. +.de VE +.ft R +.fi +.RE +.sp 1 +.. +.de hP +.IP +.ft B +\h'-\w'\\$1\ 'u'\\$1\ \c +.ft P +.. +.ie t .ds o \(bu +.el .ds o o +. +.TH gprintf 3 "9 March 2024" "Straylight/Edgeware" "mLib utilities library" +. +.SH NAME +gprintf \- generalized output formatting +. +.SH SYNOPSIS +.nf +.B "#include " + +.ta 2n +.B "struct gprintf_ops {" +.BI " int (*putch)(void *" out ", int " ch ");" +.BI " int (*putm)(void *" out ", const char *" p ", size_t " sz ");" +.BI " int (*nputf)(void *" out ", size_t " maxsz ", const char *" p ", ...);" +.B "};" + +.BI "int gprintf(const struct gprintf_ops *" ops ", void *" out "," +.ta \w'\fBint gprintf('u +.BI " const char *" p ", ...);" +.BI "int vgprintf(const struct gprintf_ops *" ops ", void *" out "," +.ta \w'\fBint vgprintf('u +.BI " const char *" p ", va_list *" ap ");" + +.BI "int gprintf_memputf(char **" buf_inout ", size_t *" sz_inout "," +.ta \w'\fBint gprintf_memputf('u +.BI " size_t " maxsz ", const char *" p ", va_list " ap ");" + +.B "const struct gprintf_ops file_printops;" +.fi +. +.SH DESCRIPTION +The +.B "" +header file declares facilities for generalized output formatting +\(en i.e., +.BR printf (3)-like +formatting to arbitrary output sinks. +This is the mechanism underlying the +.BR dstr_putf (3) +and +.BR buf_putstrf...(3) +functions. +.PP +To use it, you must define a +.B "struct gprintf_ops" +structure, +providing functions to write the formatted output to your chosen sink. +Each function receives a void pointer argument named +.IR out , +which is simply the +.I out +argument passed to +.RB ( v ) gprintf , +and should return the number of characters that it wrote +\(en or at least some nonnegative value \(en +on success, +or \-1 if it encountered an error. +.PP +The three functions are: +.hP \*o +.BR putch : +write out the single character +.IR ch , +which is an integer holding an +.B "unsigned char" +value, as used by +.BR fputc (3). +.hP \*o +.BR putm : +write out +.I sz +characters from the buffer starting at +.IR p . +.hP \*o +.BR nputf : +process the format string +.I p +and arguments +.IR ap , +writing out the formatted output; +the output will not be longer than +.I maxsz +characters. +.PP +It may seem paradoxical for +.B gprintf +to require the backend to do string formatting, +since its entire purpose is to do string formatting for you; +but implementing the +.B nputf +function can typically be done straightforwardly enough by calling +.BR snprintf (3) +or similar. +Difficult cases can be dealt with using +.BR gprintf_memputf , +described below. +.PP +The +.B gprintf +function formats a string +.I p +together with its variable argument list, +using the provided output operations +.IR ops , +and passing them the pointer +.I out +when it calls on them. +The +.B vgprintf +function is similar, +except that it receives the format arguments as +.I "a pointer to" +a captured +.B va_list +argument tail. +The argument tail is updated in place, +and (on successful completion) +is left referring to the first unused argument. +.PP +The +.B gprintf_memputf +function is a utility for implementing +.B nputf +operations. +On entry, +.BI * buf_inout +should be a pointer to a buffer of +.BI * sz_inout +bytes, allocated from +.BR arena_global (3); +instead, +.BI * buf_inout +may be null +if +.BI * sz_inout +is zero. +The +.I maxsz +and +.I p +arguments are the maximum output size and format string passed to the +.B nputf +function, +and +.I ap +is the format-argument list, captured using +.BR va_start (3). +The function will adjust the buffer pointer and size as necessary, +write the formatted result to the buffer, null-terminated, +and return the actual output length. +The function is designed to be efficient when called multiple times, +retaining the same buffer across calls, +resizing it as necessary in a geometric progression. +When the buffer is no longer wanted, free it using +.BR xfree (3). +.PP +A typical +.B nputf +function using +.B gprintf_memputf +might look something like this. +.VS +.ta 2n +struct my_output { + /* output state */ + char *buf; + size_t sz; + /* ...\& other members ...\& */ +}; + +/* ...\& define putch and putm ...\& */ + +static int nputf(void *out, size_t maxsz, const char *p, ...) +{ + struct my_output *myout = out; + va_list ap; + int n; + + va_start(ap, p); + n = gprintf_memputf(&myout->buf, &myout->sz, maxsz, p, ap); + va_end(ap); + if (n > 0) n = putm(myout, myout->buf, n); + return (n); +} + +const struct gprintf_ops my_output_ops = { putch, putm, nputf }; + +/* ...\& */ + +struct my_output myout; + +myout.buf = 0; myout.sz = 0; +/* ...\& other initialization ...\& */ +gprintf(&my_output_ops, &myout, "Hello, %s!", "world"); +xfree(myout.buf); myout.buf = 0; myout.sz = 0; +/* ...\& other cleanup ...\& */ +.VE +. +.SH "SEE ALSO" +.BR buf (3), +.BR dstr (3), +.BR mLib (3). +. +.SH AUTHOR +Mark Wooding, diff --git a/utils/gprintf.c b/utils/gprintf.c index 602698c..9964d18 100644 --- a/utils/gprintf.c +++ b/utils/gprintf.c @@ -48,6 +48,7 @@ #include "darray.h" #include "dstr.h" #include "gprintf.h" +#include "growbuf.h" #include "macros.h" /*----- Tunable constants -------------------------------------------------*/ @@ -408,8 +409,8 @@ int vgprintf(const struct gprintf_ops *ops, void *out, /* --- Output the literal portion --- */ if (fs->n) { - if (ops->putm(out, fs->p, fs->n)) return (-1); - tot += fs->n; + n = ops->putm(out, fs->p, fs->n); if (n < 0) return (-1); + tot += n; } /* --- And now the variable portion --- */ @@ -419,8 +420,8 @@ int vgprintf(const struct gprintf_ops *ops, void *out, case 0: break; case '%': - if (ops->putch(out, '%')) return (-1); - tot++; break; + n = ops->putch(out, '%'); if (n < 0) return (-1); + tot += n; break; default: abort(); } @@ -500,8 +501,8 @@ int vgprintf(const struct gprintf_ops *ops, void *out, break; #else # define MSG "" - if (ops->putm(out, MSG, sizeof(MSG) - 1)) return (-1); - tot += sizeof(MSG) - 1; continue; + n = ops->putm(out, MSG, sizeof(MSG) - 1); if (n < 0) return (-1); + tot += n; continue; # undef MSG #endif case 's': @@ -526,14 +527,14 @@ int vgprintf(const struct gprintf_ops *ops, void *out, switch (fs->fmt) { #define CASE(code, ty) \ case fmt_##code: \ - i = ops->nputf(out, sz, dd.buf, fa[fs->arg].u.code); \ + n = ops->nputf(out, sz, dd.buf, fa[fs->arg].u.code); \ break; OUTPUT_FMTTYPES(CASE) #undef CASE default: abort(); } - if (i < 0) return (-1); - tot += i; + if (n < 0) return (-1); + tot += n; } /* --- We're done --- */ @@ -595,21 +596,13 @@ int gprintf(const struct gprintf_ops *ops, void *out, const char *p, ...) size_t gprintf_memputf(char **buf_inout, size_t *sz_inout, size_t maxsz, const char *p, va_list ap) { - char *buf = *buf_inout; - size_t sz = *sz_inout; int n; - if (sz <= maxsz) { - if (!sz) sz = 32; - while (sz <= maxsz) sz *= 2; - if (buf) xfree(buf); - buf = xmalloc(sz); *buf_inout = buf; *sz_inout = sz; - } - + GROWBUF_REPLACE(&arena_stdlib, *buf_inout, *sz_inout, maxsz, 64, 1); #ifdef HAVE_SNPRINTF - n = vsnprintf(buf, maxsz + 1, p, ap); + n = vsnprintf(*buf_inout, maxsz + 1, p, ap); #else - n = vsprintf(buf, p, ap); + n = vsprintf(*buf_inout, p, ap); #endif assert(0 <= n && n <= maxsz); return (n); diff --git a/utils/gprintf.h b/utils/gprintf.h index 8c0f87c..7bb0b04 100644 --- a/utils/gprintf.h +++ b/utils/gprintf.h @@ -53,38 +53,32 @@ extern const struct gprintf_ops file_printops; /*----- Functions provided ------------------------------------------------*/ -/* --- @vgprintf@ --- * +/* --- @gprintf@, @vgprintf@ --- * * * Arguments: @const struct gprintf_ops *ops@ = output operations * @void *out@ = context for output operations * @const char *p@ = pointer to @printf@-style format string - * @va_list *ap@ = argument handle - * - * Returns: The number of characters written to the string. - * - * Use: As for @gprintf@, but takes a reified argument tail. - */ - -extern int vgprintf(const struct gprintf_ops */*ops*/, void */*out*/, - const char */*p*/, va_list */*ap*/); - -/* --- @gprintf@ --- * - * - * Arguments: @const struct gprintf_ops *ops@ = output operations - * @void *out@ = context for output operations - * @const char *p@ = pointer to @printf@-style format string - * @...@ = argument handle + * @...@ = format arguments + * @va_list *ap@ = captured format-arguments tail * * Returns: The number of characters written to the string. * * Use: Formats a @printf@-like message and writes the result using * the given output operations. This is the backend machinery * for @dstr_putf@, for example. + * + * The @gprintf@ function receives its format arguments as a + * variable-length argument tail; the @vgprintf@ function + * receives them as %%\emph{a pointer to}%% a captured argument + * tail; it updates @*ap@, leaving it ready to read the next + * unused argument. */ extern PRINTF_LIKE(3, 4) int gprintf(const struct gprintf_ops */*ops*/, void */*out*/, const char */*p*/, ...); +extern int vgprintf(const struct gprintf_ops */*ops*/, void */*out*/, + const char */*p*/, va_list */*ap*/); /* --- @gprintf_memputf@ --- * * diff --git a/utils/linreg.3 b/utils/linreg.3 new file mode 100644 index 0000000..20f74ca --- /dev/null +++ b/utils/linreg.3 @@ -0,0 +1,73 @@ +.\" -*-nroff-*- +.TH linreg 3 "9 March 2024" "Straylight/Edgeware" "mLib utilities library" +.\" @linreg_init +.\" @linreg_update +.\" @linreg_fit +.\" @LINREG_INIT +. +.SH SYNOPSIS +.nf +.B "#include " + +.B "struct linreg { ...\& };" +.B "#define LINREG_INIT ..." + +.BI "void linreg_init(struct linreg *" lr ); +.BI "void linreg_update(struct linreg *" lr ", double " x ", double " y ); +.ta \w'void linreg_fit('u +.BI "void linreg_fit(struct linreg *" lr , +.BI " double *" m_out ", double *" c_out ", double *" r_out ); +.fi +. +.SH DESCRIPTION +The functions declared in the +.B +header perform simple linear regression. +.PP +The state for a linear regression is held in a +.BR "struct linreg" . +Such a structure can be initialized statically, +using the +.B LINREG_INIT macro, +or dynamically, by calling the +.B linreg_init +function. +.PP +Once a state is initialized, +points +.RI ( x ",\ " y ) +can be added by calling +.BR linreg_update . +Each call just performs a small and constant amount of computation; +the linear regression state uses a constant amount of storage +independent of the number of points. +.P +Finally, the +.B linreg_fit +function will return the results of the regression. +It calculates quantities +.I m +and +.I c +such that the line +.IR y "\ =\ " m "\ " x "\ +\ " c +is a reasonable approximation to the data points provided, +and a correlation coefficient +.I r +quantifying how good this approximation is. +These quantities are stored in +.BI * m_out \fR, +.BI * c_out \fR, +and +.BI * r_out \fR, +respectively; +any (or all, but that wouldn't be useful) of these pointers may be null, +to discard the corresponding output. +.PP +The linear regression state can be discarded without need for ceremony: +it holds no external resources. +.PP +Any half-decent introduction to statistics will explain these concepts. +. +.SH AUTHOR +Mark Wooding, diff --git a/utils/linreg.h b/utils/linreg.h index 2adb65f..8b4d06d 100644 --- a/utils/linreg.h +++ b/utils/linreg.h @@ -97,8 +97,6 @@ * which I'm not going to derive here. */ -/*----- Header files ------------------------------------------------------*/ - /*----- Data structures ---------------------------------------------------*/ struct linreg { diff --git a/utils/maths.3 b/utils/maths.3 new file mode 100644 index 0000000..0d6afa5 --- /dev/null +++ b/utils/maths.3 @@ -0,0 +1,36 @@ +.\" -*-nroff-*- +.TH linreg 3 "9 March 2024" "Straylight/Edgeware" "mLib utilities library" +.\" @NANPN +.\" @INFP +.\" @NEGP +. +.SH SYNOPSIS +.nf +.B "#include " + +.BI "int NANP(" floatish " " x ); +.BI "int INFP(" floatish " " x ); +.BI "int NEGP(" floatish " " x ); +.fi +. +.SH DESCRIPTION +The +.B +header declares some minor low-level floating-point utilities. +These are mostly redundant with C99, +but provided for portability to older platforms. +.PP +The +.B NANP +macro returns nonzero if its argument is not-a-number. +The +.B INFP +macro returns nonzero if its argument is infinite. +The +.B NEGP +macro returns nonzero if its argument is negative; +on IEEE\ 754 platforms with sufficient support, +it will correctly detect negative zero. +. +.SH AUTHOR +Mark Wooding, -- [mdw]