.nf
.ta 2n
.B "#include <mLib/lbuf.h>"
-
+.PP
.B "enum {"
.B " LBUF_CRLF,"
.B " LBUF_STRICTCRLF,"
.B " ..."
.B "};"
.B "#define LBUF_ENABLE ..."
-
+.PP
.B "typedef struct {"
.B " unsigned f;"
.B " ..."
.B "} lbuf;"
-
+.PP
.B "typedef void lbuf_func(char *" s ", size_t " len ", void *" p );
-
+.PP
.BI "void lbuf_flush(lbuf *" b ", char *" p ", size_t " len );
.BI "void lbuf_close(lbuf *" b );
.BI "size_t lbuf_free(lbuf *" b ", char **" p );
.nf
.ta 2n
.B "#include <mLib/pkbuf.h>"
-
+.PP
.B "enum {"
.B " PKBUF_ENABLE = ..."
.B "};"
-
+.PP
.B "typedef struct {"
.B " unsigned f;"
.B " ..."
.B "} pkbuf;"
-
+.PP
.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 );
-
+.PP
.BI "void pkbuf_flush(pkbuf *" pk ", octet *" p ", size_t " len );
.BI "void pkbuf_close(pkbuf *" pk );
.BI "size_t pkbuf_free(pkbuf *" pk ", octet **" p );
.B "#include <mLib/base64.h>"
.B "#include <mLib/base32.h>"
.B "#include <mLib/hex.h>"
-
+.PP
.ta 2n
.B "typedef struct {"
.B " char *indent;"
.B " unsigned maxline;"
.B " ..."
.B "} base64_ctx;"
-
+.PP
.ta \w'\fBvoid base64_encode('u
.BI "void base64_encode(base64_ctx *" ctx ,
.BI " const void *" p ", size_t " sz ,
.BI " const void *" p ", size_t " sz ,
.BI " dstr *" d );
.BI "void base64_init(base64_ctx *" ctx );
-
+.PP
.ta 2n
.B "typedef struct {"
.B " char *indent;"
.B " unsigned maxline;"
.B " ..."
.B "} base32_ctx;"
-
+.PP
.ta \w'\fBvoid base32_encode('u
.BI "void base32_encode(base32_ctx *" ctx ,
.BI " const void *" p ", size_t " sz ,
.BI " const void *" p ", size_t " sz ,
.BI " dstr *" d );
.BI "void base32_init(base32_ctx *" ctx );
-
+.PP
.ta 2n
.B "typedef struct {"
.B " char *indent;"
.B " unsigned maxline;"
.B " ..."
.B "} hex_ctx;"
-
+.PP
.ta \w'\fBvoid hex_encode('u
.BI "void hex_encode(hex_ctx *" ctx ,
.BI " const void *" p ", size_t " sz ,
.BR codec (3).
.SH "AUTHOR"
Mark Wooding, <mdw@distorted.org.uk>
-
+.PP
.B "#include <mLib/base64.h>"
.B "#include <mLib/base32.h>"
.B "#include <mLib/hex.h>"
-
+.PP
.B "#define CDCF_LOWERC ..."
.B "#define CDCF_IGNCASE ..."
.B "#define CDCF_NOEQPAD ..."
.B "#define CDCF_IGNINVCH ..."
.B "#define CDCF_IGNSPC ..."
.B "#define CDCF_IGNJUNK ..."
-
+.PP
.ta 2n
.B "enum {"
.B " CDCERR_OK = ...,"
.B " CDCERR_INVEQPAD = ...,"
.B " CDCERR_INVZPAD = ..."
.B "};"
-
+.PP
.B "typedef struct {"
.B " const char *name;"
.ta 2n +\w'\fBcodec *(*encoder)('u
.BI " codec *(*decoder)(unsigned " flags );
.B " ...\&"
.B "} codec_class;"
-
+.PP
.B "typedef struct {"
.B " const codec_ops *ops;"
.B "} codec;"
-
+.PP
.B "typedef struct {"
.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;"
-
+.PP
.B "codec_class null_codec_class;"
.B "codec_class base64_class, file64_class, base64url_class;"
.B "codec_class base32_class, base32hex_class;"
.B "codec_class hex_class;"
-
+.PP
.BI "const char *codec_strerror(int " err ");"
.fi
.SH DESCRIPTION
.sp 1
.fi
..
+.ie t \{\
+. de VP
+. sp .4v
+..
+\}
+.el \{\
+. de VP
+. sp
+..
+\}
.TH url 3 "20 June 1999" "Straylight/Edgeware" "mLib utilities library"
.SH NAME
url \- manipulation of form-urlencoded strings
.SH SYNOPSIS
.nf
.B "#include <mLib/url.h>"
-
+.PP
.ta 2n
.B "typedef struct {"
.B " unsigned f;"
.B " ..."
.B "} url_ectx;"
-
+.PP
.B "typedef struct {"
.B " unsigned f;"
.B " ..."
.B "} url_dctx;"
-
+.PP
.B "#define URLF_STRICT ..."
.B "#define URLF_LAX ..."
.B "#define URLF_SEMI ..."
-
+.PP
.BI "void url_initenc(url_ectx *" ctx );
.ta \w'\fBvoid url_enc('u
.BI "void url_enc(url_ectx *" ctx ", dstr *" d ,
.BI " const char *" name ", const char *" value );
-
+.PP
.BI "void url_initdec(url_dctx *" ctx ", const char *" p );
.BI "int url_dec(url_dctx *" ctx ", dstr *" n ", dstr *" v );
.fi
#include <mLib/dstr.h>
#include <mLib/sym.h>
#include <mLib/url.h>
-
+.VP
typedef struct {
sym_base _b;
char *v;
} val;
-
+.VP
void decode(sym_table *t, const char *p)
{
url_dctx c;
dstr n = DSTR_INIT, v = DSTR_INIT;
val *vv;
unsigned f;
-
+.VP
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);
}
dstr_destroy(&n); dstr_destroy(&v);
}
-
+.VP
void encode(sym_table *t, dstr *d)
{
sym_iter i;
url_ectx c;
val *v;
-
+.VP
url_initenc(&c);
for (sym_mkiter(&i, t); (v = sym_next(&i)) != 0; )
url_enc(&c, d, SYM_NAME(v), v->v);
by Ross N. Williams.
.SH "AUTHOR"
Mark Wooding, <mdw@distorted.org.uk>
-
.SH SYNOPSIS
.nf
.B "#include <mLib/crc32.h>"
-
+.PP
.BI "uint32 crc32(uint32 " crc ", const void *" buf ", size_t " sz );
.BI CRC32( result ", " crc ", " buf ", " sz )
.fi
static const struct tvec_env step_testenv = { 0, 0, 0, 0, run_step, 0, 0 };
static const struct tvec_regdef unihash_regs[] = {
- { "k", RK, &tvty_uint, 0, { &tvrange_u32 } },
- { "m", RM, &tvty_bytes, 0 },
- { "h", RH, &tvty_uint, 0, { &tvrange_u32 } },
+ { "k", &tvty_uint, RK, 0, { &tvrange_u32 } },
+ { "m", &tvty_bytes, RM, 0 },
+ { "h", &tvty_uint, RH, 0, { &tvrange_u32 } },
TVEC_ENDREGS
};
static const struct tvec_regdef crc32_regs[] = {
- { "m", RM, &tvty_bytes, 0 },
- { "h", RH, &tvty_uint, 0, { &tvrange_u32 } },
+ { "m", &tvty_bytes, RM, 0 },
+ { "h", &tvty_uint, RH, 0, { &tvrange_u32 } },
TVEC_ENDREGS
};
static const struct tvec_regdef bench_regs[] = {
- { "msz", RM, &tvty_buffer, TVRF_ID },
+ { "msz", &tvty_buffer, RM, TVRF_ID },
TVEC_ENDREGS
};
.BR unihash (3).
.SH "AUTHOR"
Mark Wooding, <mdw@distorted.org.uk>
-
.SH SYNOPSIS
.nf
.B "#include <mLib/unihash.h>"
-
+.PP
.B "typedef struct { ...\& } unihash_info;"
-
+.PP
.B "unihash_info unihash_global;"
-
+.PP
.BI "void unihash_setkey(unihash_info *" i ", uint32 " k );
.BI "uint32 UNIHASH_INIT(const unihash_info *" i );
.ta \w'\fBuint32 unihash_hash('u
.SH SYNOPSIS
.nf
.B "#include <mLib/alloc.h>"
-
+.PP
.BI "void *x_alloc(arena *" a ", size_t " sz );
.BI "char *x_strdup(arena *" a ", const char *" s );
.BI "void *x_realloc(arena *" a ", void *" p ", size_t " sz ", size_t " osz );
.BI "void x_free(arena *" a ", void *" p );
-
+.PP
.BI "void *xmalloc(size_t " sz );
.BI "void *xrealloc(void *" p ", size_t " sz ", size_t " osz );
.BI "char *xstrdup(const char *" s );
.BR mLib (3).
.SH AUTHOR
Mark Wooding, <mdw@distorted.org.uk>
-
.SH "SYNOPSIS"
.nf
.B "#include <mLib/arena.h>"
-
+.PP
.ta 2n
.B "typedef struct {"
.B " const struct arena_ops *ops";
.B "} arena;"
-
+.PP
.B "typedef struct {"
.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;"
-
+.PP
.BI "arena *arena_global;"
.BI "arena arena_stdlib;"
-
+.PP
.ta \w'\fBvoid *arena_fakerealloc('u
.BI "void *arena_fakerealloc(arena *" a ", void *" p ,
.BI " size_t " sz ", size_t " osz );
-
+.PP
.BI "void *a_alloc(arena *" a ", size_t " sz );
.BI "void *a_realloc(arena *" a ", void *" p ", size_t " sz ", size_t " osz );
.BI "void a_free(arena *" a );
-
+.PP
.BI "void *A_ALLOC(arena *" a ", size_t " sz );
.BI "void *A_REALLOC(arena *" a ", void *" p ", size_t " sz ", size_t " osz );
.BI "void A_FREE(arena *" a );
.SH SYNOPSIS
.nf
.B "#include <mLib/growbuf.h>"
-
+.PP
.BI "GROWBUF_SIZE(size_t " sz ", size_t " want ", " \c
.BI "size_t " init ", size_t " granule ");"
-
+.PP
.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 ");"
.SH "SYNOPSIS"
.nf
.B "#include <mLib/pool.h>"
-
+.PP
.B "typedef struct { ...\& } pool;"
-
+.PP
.ta 2n
.B "typedef struct {"
.B " pool_resource *next;"
.BI " void (*destroy)(pool_resource *" r );
.B "} pool_resource;"
-
+.PP
.B "typedef struct {"
.B " FILE *fp;"
.B " ..."
.B "} pool_file;"
-
+.PP
.BI "void pool_init(pool *" p ", arena *" a );
.BI "pool *pool_create(arena *" a );
.BI "pool *pool_sub(pool *" p );
.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 );
-
+.PP
.ta \w'\fBvoid POOL_ADD('u
.BI "void POOL_ADD(pool *" p ", pool_resource *" r ,
.BI " void (*" dfn ")(pool_resource *" r ));
.SH SYNOPSIS
.nf
.B "#include <mLib/sub.h>"
-
+.PP
.B "typedef struct { ...\& } subarena;"
-
+.PP
.BI "void subarena_create(subarena *" s ", arena *" a );
.BI "void subarena_destroy(subarena *" s );
.BI "void subarena_alloc(subarena *" s ", size_t " sz );
.BI "void subarena_free(subarena *" s ", void *" p ", size_t " sz );
-
+.PP
.B "void sub_init(void);"
.BI "void *sub_alloc(size_t " sz );
.BI "void sub_free(void *" p ", size_t " sz );
-
+.PP
.BI "void *A_CREATE(subarena *" s ", " type );
.BI "void A_DESTROY(subarena *" s ", " type " *" p );
.BI "void *CREATE(" type );
.SH SYNOPSIS
.nf
.B "#include <mLib/bres.h>"
-
+.PP
.B "typedef struct { ...\& } bres_client;"
-
+.PP
.ta \w'\fBvoid bres_byname('u
.BI "void bres_byname(bres_client *" rc ", const char *" name ,
.BI " void (*" func ")(struct hostent *" h ", void *" p ),
.SH SYNOPSIS
.nf
.B "#include <mLib/conn.h>"
-
+.PP
.B "typedef struct { ...\& } conn;"
-
+.PP
.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 );
-
+.PP
.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 );
-
+.PP
.BI "void conn_kill(conn *" c );
.fi
.SH DESCRIPTION
.SH "SYNOPSIS"
.nf
.B "#include <mLib/ident>"
-
+.PP
.B "typedef struct { ...\& } ident_request;"
-
+.PP
.ta 2n +2n
.B "enum ["
.B " IDENT_USERID = ...,"
.B " IDENT_ERROR = ...,"
.B " IDENT_BAD = ..."
.B "};"
-
+.PP
.B "typedef struct {"
.B " unsigned short sport, dport;"
.B " unsigned type;"
.B " char *error;"
.B " } u;"
.B "} ident_reply;"
-
+.PP
.BI "void ident_abort(ident_request *" rq );
.ta \w'\fBvoid ident('u
.BI "void ident(ident_request *" rq ", sel_state *" s ,
.SH SYNOPSIS
.nf
.B "#include <mLib/sel.h>"
-
+.PP
.ta 2n
.B "enum {"
.B " SEL_READ = ...,"
.B " SEL_EXC = ...,"
.B " SEL_MODES = ..."
.B "};"
-
+.PP
.B "typedef struct { ...\& } sel_state;"
.B "typedef struct { ...\& } sel_timer;"
.B "typedef struct { ...\& } sel_hook;"
-
+.PP
.B "typedef struct {"
.B " int fd;"
.B " ..."
.B "} sel_file;"
-
+.PP
.B "typedef struct {"
.B " int maxfd;"
.B " fd_set fd[SEL_MODES];"
.B " struct timeval tv, *tvp;"
.B " struct timeval now;"
.B "} sel_args;"
-
+.PP
.BI "typedef void (*sel_hookfn)(sel_state *" s ", sel_args *" a ", void *" p );
-
+.PP
.BI "void sel_init(sel_state *" s );
-
+.PP
.ta \w'\fBvoid sel_initfile('u
.BI "void sel_initfile(sel_state *" s ", sel_file *" f ,
.BI " int " fd ", unsigned " mode ,
.BI "void sel_addfile(sel_file *" f );
.BI "void sel_force(sel_file *" f );
.BI "void sel_rmfile(sel_file *" f );
-
+.PP
.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 );
-
+.PP
.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 );
-
+.PP
.BI "int sel_fdmerge(fd_set *" dest ", fd_set *" fd ", int " maxfd );
-
+.PP
.BI "int sel_select(sel_state *" s );
.fi
.SH "OVERVIEW"
.SH SYNOPSIS
.nf
.B "#include <mLib/selbuf.h>"
-
+.PP
.B "typedef struct { ...\& } selbuf;"
-
+.PP
.BI "void selbuf_enable(selbuf *" b );
.BI "void selbuf_disable(selbuf *" b );
.BI "void selbuf_setsize(selbuf *" b ", size_t " sz );
.SH SYNOPSIS
.nf
.B "#include <mLib/selpk.h>"
-
+.PP
.B "typedef struct { ...\& } selpk;"
-
+.PP
.BI "void selpk_enable(selpk *" pk );
.BI "void selpk_disable(selpk *" pk );
.BI "void selpk_want(selpk *" pk ", size_t " sz );
.SH SYNOPSIS
.nf
.B "#include <mLib/sig.h>"
-
+.PP
.B "typedef struct { ...\& } sig;"
-
+.PP
.ta \w'\fBvoid sig_add('u
.BI "void sig_add(sig *" s ", int " n ,
.BI " void (*" proc ")(int " n ", void *" p "), void *" p );
.SH SYNOPSIS
.nf
.B "#include <mLib/assoc.h>"
-
+.PP
.B "typedef struct { ...\& } assoc_table;"
.B "typedef struct { ...\& } assoc_base;"
-
+.PP
.BI "void assoc_create(assoc_table *" t );
.BI "void assoc_destroy(assoc_table *" t );
-
+.PP
.BI "void *assoc_find(assoc_table *" t ", atom *" a ", size_t " sz ", unsigned *" f );
.BI "void assoc_remove(assoc_table *" t ", void *" b );
-
+.PP
.BI "atom *ASSOC_ATOM(const void *" p );
-
+.PP
.BI "void assoc_mkiter(assoc_iter *" i ", assoc_table *" t );
.BI "void *assoc_next(assoc_iter *" i );
.fi
.SH SYNOPSIS
.nf
.B "#include <mLib/atom.h>"
-
+.PP
.B "typedef struct { ...\& } atom_table;"
.B "typedef struct { ...\& } atom;"
-
+.PP
.BI "void atom_createtable(atom_table *" t );
.BI "void atom_destroytable(atom_table *" t );
-
+.PP
.BI "atom *atom_intern(atom_table *" t ", const char *" p );
.BI "atom *atom_nintern(atom_table *" t ", const char *" p ", size_t " n );
.BI "atom *atom_gensym(atom_table *" t );
.BI "atom *INTERN(const char *" p );
.BI "atom *GENSYM;"
-
+.PP
.BI "const char *atom_name(const atom *" a );
.BI "size_t atom_len(const atom *" a );
.BI "uint32 atom_hash(const atom *" a );
.BI "const char *ATOM_NAME(const atom *" a );
.BI "size_t ATOM_LEN(const atom *" a );
.BI "uint32 ATOM_HASH(const atom *" a );
-
+.PP
.BI "void atom_mkiter(atom_iter *" i ", atom_table *" t );
.BI "atom *atom_next(atom_iter *" i );
-
+.PP
.BI "extern atom_table *ATOM_GLOBAL;"
.fi
.SH DESCRIPTION
.\" @BBAD
.\" @BOK
.\" @BENSURE
-
+.
.\" @DBBASE
.\" @DBLIM
.\" @DBCUR
.SH SYNOPSIS
.nf
.B "#include <mLib/dstr.h>"
-
+.PP
.B "typedef struct { ...\& } buf;"
.B "typedef struct { ...\& } dbuf;"
-
+.PP
.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 DBRESET(dbuf *" db );
.BI "void DBDESTROY(dbuf *" db );
.B "#define DBUF_INIT ..."
-
+.PP
.fi
All of the following functions and macros exist in two variants:
one with a name beginning
and taking a first argument of type
.BR "dbuf *" .
.nf
-
+.PP
.BI "void buf_flip(buf *" b );
.BI "octet *BBASE(buf *" b );
.BI "octet *BLIM(buf *" b );
.BI "ptrdiff_t BLEN(buf *" b );
.BI "ptrdiff_t BLEFT(buf *" b );
.BI "void BFLIP(buf *" b );
-
+.PP
.BI "int buf_break(buf *" b );
.BI "int BBREAK(buf *" b );
.BI "int BBAD(buf *" b );
.BI "int BOK(buf *" b );
-
+.PP
.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 );
-
+.PP
.BI "void *buf_get(buf *" b ", size_t " sz );
.BI "void *buf_put(buf *" b ", const void *" p ", size_t " sz );
-
+.PP
.BI "int buf_getbyte(buf *" b );
.BI "int buf_putbyte(buf *" b ", int " ch );
-
+.PP
.BI "int buf_putstr(buf *" b ", const char *" p ", ...);"
.BI "int buf_vputstr(buf *" b ", const char *" p ", va_list *" ap );
-
+.PP
.fi
For
.I suff
.nf
.BI "int buf_putu" suff "(buf *" b ", uint" suff " " w );
.BI "int buf_getu" suff "(buf *" b ", uint" suff " *" w );
-
+.PP
.fi
For
.I suff
.nf
.BI "int buf_putk" suff "(buf *" b ", kludge64 " w );
.BI "int buf_getk" suff "(buf *" b ", kludge64 *" w );
-
+.PP
.ta 2n
.BI "BUF_ENCLOSETAG(" tag ", buf *" b ", size_t " mk ", " check ", " poke ", size_t " lensz )
.I " body"
.I " body"
.BI "BUF_ENCLOSEZTAG(" tag ", buf *" b )
.I " body"
-
+.PP
.fi
For
.I suff
.ta 2n
.BI "BUF_ENCLOSE" suff "(buf *" b ", size_t " mk )
.I " body"
-
+.PP
.BI "BUF_ENCLOSEZ(buf *" b )
.I " body"
-
+.PP
.fi
For
.I suff
.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 );
-
+.PP
.fi
For
.I suff
.SH "SYNOPSIS"
.nf
.B "#include <mLib/darray.h>"
-
+.PP
.ta 2n
.B "typedef struct {"
.B " size_t sz, len, off;"
.B " unsigned push, unshift;"
.B " arena *a;"
.B "} da_base;"
-
+.PP
.B "#define DA_INIT ..."
-
+.PP
.B "#define DAEXC_UFLOW EXC_ALLOCN(EXC_MLIB, ...)"
.B "#define DAEXC_OFLOW EXC_ALLOCN(EXC_MLIB, ...)"
-
+.PP
.BI DA_DECL( type_v ", " type );
.BI "void DA_CREATE(" type_v " *" a );
.BI "void DA_DESTROY(" type_v " *" a );
-
+.PP
.BI "void DA_ENSURE(" type_v " *" a ", size_t " n );
.BI "void DA_SHUNT(" type_v " *" a ", size_t " n );
.BI "void DA_TIDY(" type_v " *" a );
.BI "void DA_RESET(" type_v " *" a );
-
+.PP
.IB type " *DA(" type_v " *" a );
.BI "size_t DA_LEN(" type_v " *" a );
.BI "size_t DA_SPARE(" type_v " *" a );
.BI "size_t DA_OFFSET(" type_v " *" a );
.BI "void DA_INCLUDE(" type_v " *" a ", size_t " i );
-
+.PP
.BI "void DA_EXTEND(" type_v " *" a ", long " n );
.BI "void DA_SHRINK(" type_v " *" a ", long " n );
.BI "void DA_SLIDE(" type_v " *" a ", long " n );
.BI "void DA_UNSLIDE(" type_v " *" a ", long " n );
-
+.PP
.BI "void DA_UNSAFE_EXTEND(" type_v " *" a ", long " n );
.BI "void DA_UNSAFE_SHRINK(" type_v " *" a ", long " n );
.BI "void DA_UNSAFE_SLIDE(" type_v " *" a ", long " n );
.BI "void DA_UNSAFE_UNSLIDE(" type_v " *" a ", long " n );
-
+.PP
.IB type " DA_FIRST(" type_v " *" a );
.IB type " DA_LAST(" type_v " *" a );
.BI "void DA_PUSH(" type_v " *" a ", " type " " x );
.IB type " DA_POP(" type_v " *" a );
.BI "void DA_UNSHIFT(" type_v " *" a ", " type " " x );
.IB type " DA_SHIFT(" type_v " *" a );
-
+.PP
.BI "void *da_ensure(da_base *" b ", void *" v ", size_t " sz ", size_t " n );
.BI "void *da_shunt(da_base *" b ", void *" v ", size_t " sz ", size_t " n );
.BI "void *da_tidy(da_base *" b ", void *" v ", size_t " sz );
macro with the same name as the array type only in uppercase which may
be used to prevent multiple declarations, e.g.,
.VS
+.ta 2n
#ifndef FOO_V
-# define FOO_V
- DA_DECL(foo_v, foo);
+# define FOO_V
+ DA_DECL(foo_v, foo);
#endif
.VE
The macro
.PP
Dynamic arrays are structures with the format
.VS
+.ta 2n
.BI "typedef struct " type_v " {"
-.B " da_base b;"
-.BI " " type " *v;"
+.B " da_base b;"
+.BI " " type " *v;"
.BI "} " type_v ";"
.VE
The pointer
.SH SYNOPSIS
.nf
.B "#include <mLib/dspool.h>"
-
+.PP
.B "typedef struct { ...\& } dspool;"
-
+.PP
.BI "void dspool_create(dspool *" p ", size_t " isz );
.BI "void dspool_destroy(dspool *" p );
.BI "dstr *dspool_get(dspool *" p );
.BI "void dspool_put(dspool *" p ", dstr *" d );
-
+.PP
.BI "void DSGET(dspool *" p ", " d );
.BI "void DSPUT(dspool *" p ", dstr *" d );
.fi
.SH SYNOPSIS
.nf
.B "#include <mLib/dstr.h>"
-
+.PP
.B "typedef struct { ...\& } dstr;"
.B "#define DSTR_INIT ..."
-
+.PP
.BI "void dstr_create(dstr *" d );
.BI "void dstr_destroy(dstr *" d );
.BI "void dstr_reset(dstr *" d );
-
+.PP
.BI "void dstr_ensure(dstr *" d ", size_t " sz );
.BI "void dstr_tidy(dstr *" d );
-
+.PP
.BI "void dstr_putc(dstr *" d ", int " ch );
.BI "void dstr_putz(dstr *" d );
.BI "void dstr_puts(dstr *" d ", const char *" s );
.BI "void dstr_putm(dstr *" d ", const void *" p ", size_t " sz );
.BI "int dstr_putline(dstr *" d ", FILE *" fp );
.BI "size_t dstr_write(const dstr *" d ", FILE *" fp );
-
+.PP
.BI "void DCREATE(dstr *" d );
.BI "void DDESTROY(dstr *" d );
.BI "void DRESET(dstr *" d );
\h'-\w'\\$1\ 'u'\\$1\ \c
.ft P
..
-.ie t .ds o \(bu
-.el .ds o o
+.ie t \{\
+. ds o \(bu
+. de VP
+. sp .4v
+..
+\}
+.el \{\
+. ds o o
+. de VP
+. sp
+..
+\}
.TH hash 3 "2 August 1999" "Straylight/Edgeware" "mLib utilities library"
.SH "NAME"
hash \- low-level hashtable implementation
.SH "SYNOPSIS"
.nf
.B "#include <mLib/hash.h>"
-
+.PP
.ta 2n
.B "typedef struct {"
.B " uint32 mask;"
.B " hash_base **v;"
.B " arena *a;"
.B "} hash_table;"
-
+.PP
.B "typedef struct {"
.B " hash_base *next;"
.B " uint32 hash;"
.B "} hash_base;"
-
+.PP
.B "typedef struct { ...\& } hash_iter;"
-
+.PP
.BI "void hash_create(hash_table *" t ", size_t " n );
.BI "void hash_destroy(hash_table *" t );
.BI "hash_base **hash_bin(hash_table *" t ", uint32 " hash );
.BI "void hash_remove(hash_table *" t ", hash_base *" b );
.BI "void hash_mkiter(hash_iter *" i ", hash_table *" t );
.BI "hash_base *hash_next(hash_iter *" i );
-
+.PP
.BI "hash_base **HASH_BIN(hash_table *" t ", uint32 " hash );
.BI "void HASH_MKITER(hash_iter *" i ", hash_table *" t );
.BI "void HASH_NEXT(hash_iter *" i ", " b );
hashtables. Code examples are given throughout. They assume the
following definitions:
.VS
+.ta 2n
/* --- A table of items --- */
-
+.VP
typedef struct item_table {
- hash_table t;
- size_t load;
+ hash_table t;
+ size_t load;
};
-
+.VP
/* --- An item --- */
-
+.VP
typedef struct item {
- hash_base b;
- const char *k;
- /* ... */
+ hash_base b;
+ const char *k;
+ /* ... */
} item;
.VE
The implementation presented here is simple but relatively bad. The
.PP
For example, an item table might be initialized like this:
.VS
+.ta 2n
void item_createtab(item_table *t)
{
- hash_create(&t->t, ITEM_INITSZ);
- t->load = ITEM_INITLOAD;
+ hash_create(&t->t, ITEM_INITSZ);
+ t->load = ITEM_INITLOAD;
}
.VE
A hashtable can be destroyed by calling
The usual way to deallocate the individual hashtable items is using the
iteration constructs described below.
.VS
+.ta 2n +2n
void item_destroytab(item_table *t)
{
- hash_iter i;
- hash_base *b;
- for (hash_mkiter(&i, &t->t); (b = hash_next(&i)) != 0; ) {
- item *ii = (item *)b;
- free(ii->k);
- /* ... */
- DESTROY(ii);
- }
- hash_destroy(&t->t);
+ hash_iter i;
+ hash_base *b;
+ for (hash_mkiter(&i, &t->t); (b = hash_next(&i)) != 0; ) {
+ item *ii = (item *)b;
+ free(ii->k);
+ /* ... */
+ DESTROY(ii);
+ }
+ hash_destroy(&t->t);
}
.VE
.sp -1
Once the bin list has been found, it's fairly easy to search for an
exact match. A simple search might look something like this:
.VS
+.ta 2n +2n +2n 20m
item *lookup(item_table *t, const char *k)
{
- uint32 h = hash(k); /* Hash @k@ somehow */
- hash_base **bin = HASH_BIN(&t->t, h);
- hash_base *b;
- for (b = *bin; b; b = b->next) {
- item *i = (item *)b;
- if (h == i->b.hash && strcmp(k, i->k) == 0)
- return (i);
- }
- return (0);
+ uint32 h = hash(k); /* Hash \fIk\fP somehow */
+ hash_base **bin = HASH_BIN(&t->t, h);
+ hash_base *b;
+ for (b = *bin; b; b = b->next) {
+ item *i = (item *)b;
+ if (h == i->b.hash && strcmp(k, i->k) == 0)
+ return (i);
+ }
+ return (0);
}
.VE
Insertion is also relatively trivial given the bin list head. Insertion
which is passed only the address of the hashtable. It returns nonzero
if extension was successful.
.VS
+.ta 2n +2n
item *add(item_table *t, const char *k, /* ... */)
{
- item *i;
- uint32 h;
- hash_base **bin;
-
- /* --- See if the item is already there --- */
-
- if ((i = = lookup(t, k)) != 0)
- return (i);
-
- /* --- Make a new hashtable item --- */
-
- i = CREATE(item);
- i->k = xstrdup(k);
- /* ... */
-
- /* --- Link it into the bin list --- */
-
- h = i->b.hash = hash(k);
- bin = HASH_BIN(&t->t, h);
- i->b.next = *bin;
- *bin = &i->b.next;
-
- /* --- Maybe extend the hashtable --- */
-
- if (t->load)
- t->load--;
- else if (hash_extend(&t->t))
- t->load = recalc_load(t);
-
- /* --- Done --- */
-
- return (i);
+ item *i;
+ uint32 h;
+ hash_base **bin;
+.VP
+ /* --- See if the item is already there --- */
+.VP
+ if ((i = lookup(t, k)) != 0)
+ return (i);
+.VP
+ /* --- Make a new hashtable item --- */
+.VP
+ i = CREATE(item);
+ i->k = xstrdup(k);
+ /* ... */
+.VP
+ /* --- Link it into the bin list --- */
+.VP
+ h = i->b.hash = hash(k);
+ bin = HASH_BIN(&t->t, h);
+ i->b.next = *bin;
+ *bin = &i->b.next;
+.VP
+ /* --- Maybe extend the hashtable --- */
+.VP
+ if (t->load)
+ t->load--;
+ else if (hash_extend(&t->t))
+ t->load = recalc_load(t);
+.VP
+ /* --- Done --- */
+.VP
+ return (i);
}
.VE
The
.RE
.sp 1
..
+.ie t \{\
+. de VP
+. sp .4v
+..
+\}
+.el \{\
+. de VP
+. sp
+..
+\}
.TH sym 3 "8 May 1999" "Straylight/Edgeware" "mLib utilities library"
.SH NAME
sym \- symbol table manager
.SH SYNOPSIS
.nf
.B "#include <mLib/sym.h>"
-
+.PP
.B "type struct { ...\& } sym_table;"
.B "type struct { ...\& } sym_base;"
.B "type struct { ...\& } sym_iter;"
-
+.PP
.BI "void sym_create(sym_table *" t );
.BI "void sym_destroy(sym_table *" t );
-
+.PP
.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 );
-
+.PP
.BI "const char *SYM_NAME(const void *" p );
.BI "size_t SYM_LEN(const void *" p );
.BI "uint32 SYM_HASH(const void *" p );
-
+.PP
.BI "void sym_mkiter(sym_iter *" i ", sym_table *" t );
.BI "void *sym_next(sym_iter *" i );
.fi
In this case, you'd define something like the following structure for
your values:
.VS
+.ta 2 20m
typedef struct val {
- sym_base _base; /* Symbol header */
- unsigned type; /* Type of this symbol */
- int dispoff; /* Which display variable is in */
- size_t frameoff; /* Offset of variable in frame */
+ sym_base _base; /* Symbol header */
+ unsigned type; /* Type of this symbol */
+ int dispoff; /* Which display variable is in */
+ size_t frameoff; /* Offset of variable in frame */
} val;
.VE
Given a pointer
.PP
You can look up a name in the table by saying something like:
.VS
+.ta 2n
val *v = sym_find(t, name, -1, 0, 0);
if (!v)
- error("unknown variable `%s'", name);
+ error("unknown variable `%s'", name);
.VE
You can add in a new variable by saying something like
.VS
+.ta 2n
unsigned f;
val *v = sym_find(t, name, -1, sizeof(val), &f);
if (f)
- error("variable `%s' already exists", name);
+ error("variable `%s' already exists", name);
/* fill in v */
.VE
You can examine all the variables in your symbol table by saying
something like:
.VS
+.ta 2n
sym_iter i;
val *v;
-
+.VP
for (sym_mkiter(&i, t); (v = sym_next(&i)) != 0; ) {
- /* ... */
+ /* ... */
}
.VE
That ought to be enough examples to be getting on with.
.SH SYNOPSIS
.nf
.B "#include <mLib/daemonize.h>"
-
+.PP
.B "void detachtty(void);"
.B "int daemonize(void);"
.fi
.SH "SYNOPSIS"
.nf
.B "#include <mLib/env.h>"
-
+.PP
.BI "char *env_get(sym_table *" t ", const char *" name );
.BI "void env_put(sym_table *" t ,
.BI " const char *" name ", const char *" value );
.SH "SYNOPSIS"
.nf
.B "#include <mLib/fdflags.h>"
-
+.PP
.ta \w'\fBint fdflags('u
.BI "int fdflags(int " fd ,
.BI " unsigned " fbic ", unsigned " fxor ,
.SH SYNOPSIS
.nf
.B "#include <mLib/fdpass.h>"
-
+.PP
.BI "ssize_t fdpass_send(int " sock ", int " fd ", const void *" p ", size_t " sz );
.BI "ssize_t fdpass_recv(int " sock ", int *" fd ", void *" p ", size_t " sz );
.fi
.SH SYNOPSIS
.nf
.B "#include <mLib/fwatch.h>"
-
+.PP
.B "typedef struct { ...\& } fwatch;"
-
+.PP
.BI "void fwatch_init(fwatch *" f ", const char *" name );
.BI "void fwatch_initfd(fwatch *" f ", int " fd );
.BI "int fwatch_update(fwatch *" f ", const char *" name );
.SH SYNOPSIS
.nf
.B "#include <mLib/lock.h>"
-
+.PP
.ta 2n
.B "enum {"
.B " LOCK_UNLOCK = ...,"
.B " LOCK_EXCL = ...,"
.B " LOCK_NONEXCL = ..."
.B "};"
-
+.PP
.BI "int lock_file(int " fd ", unsigned " how );
.fi
.SH DESCRIPTION
\h'-\w'\\$1\ 'u'\\$1\ \c
.ft P
..
-.ie t .ds o \(bu
-.el .ds o o
+.ie t \{\
+. ds o \(bu
+. de VP
+. sp .4v
+..
+\}
+.el \{\
+. ds o o
+. de VP
+. sp
+..
+\}
.TH mdup 3 "4 January" "Straylight/Edgeware" "mLib utilities library"
.SH NAME
mdup \- renumber file descriptors
.SH SYNOPSIS
.nf
.B "#include <mLib/mdup.h>"
-
+.PP
.ta 2n
.B "typedef struct {"
.B " int cur;"
.B " int want;"
.B "} mdup_fd;"
-
+.PP
.BI "int mdup(mdup_fd *" v ", size_t " n ");"
.fi
.SH DESCRIPTION
int p_in[2] = P_INIT, p_out[2] = P_INIT, p_err[2] = P_INIT;
pid_t kid = -1;
int i;
-
+.VP
if (pipe(p_in) || pipe(p_out) || pipe(p_err)) goto error;
if ((kid = fork()) < 0) goto error;
if (!kid) {
pid_t kid = -1;
mdup_fd md[3];
int i;
-
+.VP
if (pipe(p_in) || pipe(p_out) || pipe(p_err)) goto error;
if ((kid = fork()) < 0) goto error;
if (!kid) {
.BR mLib (3).
.SH AUTHOR
Mark Wooding, <mdw@distorted.org.uk>
-
.SH SYNOPSIS
.nf
.B "#include <mLib/tv.h>"
-
+.PP
.BI "void tv_add(struct timeval *" dst ,
.BI " const struct timeval *" a ,
.BI " const struct timeval *" b );
.BI " time_t " sec ", unsigned long " usec );
.BI "int tv_cmp(const struct timeval *" a ,
.BI " const struct timeval *" b );
-
+.PP
.B "int MILLION;"
.BI "void TV_ADD(struct timeval *" dst ,
.BI " const struct timeval *" a ,
## Benchmarking.
pkginclude_HEADERS += bench.h
libtest_la_SOURCES += bench.c
+LIBMANS += bench.3
## Old `testrig' testing framework.
pkginclude_HEADERS += testrig.h
libtest_la_SOURCES += tvec-output.c
libtest_la_SOURCES += tvec-types.c
libtest_la_SOURCES += tvec-main.c
-#LIBMANS += tvec.3
+LIBMANS += tvec.3
+LIBMANS += tvec-types.3
+LIBMANS += tvec-adhoc.3
+LIBMANS += tvec-main.3
+LIBMANS += tvec-env.3
+LIBMANS += tvec-tyimpl.3
+LIBMANS += tvec-output.3
libtest_la_SOURCES += tvec-bench.c
+LIBMANS += tvec-bench.3
+
libtest_la_SOURCES += tvec-remote.c
+LIBMANS += tvec-remote.3
+
libtest_la_SOURCES += tvec-timeout.c
+LIBMANS += tvec-timeout.3
check_PROGRAMS += t/tvec.t
t_tvec_t_SOURCES = t/tvec-test.c
--- /dev/null
+.\" -*-nroff-*-
+.ie t .ds , \h'\w'\ 'u/2u'
+.el .ds , \ \"
+.TH bench 3 "9 March 2024" "Straylight/Edgeware" "mLib utilities library"
+.\" @bench_createtimer
+.\" @bench_init
+.\" @bench_destroy
+.\" @bench_calibrate
+.\" @bench_measure
+.
+.SH SYNOPSIS
+.nf
+.B "#include <mLib/bench.h>"
+.PP
+.ta 2n
+.B "struct bench_time {"
+.B " unsigned f;"
+.B " kludge64 s;"
+.B " uint32 ns;"
+.B " kludge64 cy;"
+.B "};"
+.PP
+.B "struct bench_timing {"
+.B " unsigned f;"
+.B " double n;"
+.B " double t;"
+.B " double cy;"
+.B "};"
+.PP
+.B "struct bench_timerops {"
+.BI " void (*describe)(struct bench_timer *" bt ", dstr *" d );
+.BI " void (*now)(struct bench_timer *" bt ", struct bench_time *" t_out );
+.BI " void (*destroy)(struct bench_timer *" bt );
+.B "};"
+.B "struct bench_timer {"
+.B " const struct bench_timerops *ops;"
+.B "};"
+.PP
+.B "struct bench_state {"
+.B " unsigned f;"
+.B " double target_s;"
+.B " ..."
+.B "}";
+.PP
+.BI "typedef void bench_fn(unsigned long " n ", void *" ctx );
+.PP
+.B "#define BTF_TIMEOK ..."
+.B "#define BTF_CYOK ..."
+.B "#define BTF_CLB ..."
+.B "#define BTF_ANY (BTF_TIMEOK | BTF_CYOK)"
+.PP
+.B "struct bench_timer *bench_createtimer(void);"
+.PP
+.BI "int bench_init(struct bench_state *" b ", struct bench_timer *" tm );
+.BI "void bench_destroy(struct bench_state *" b );
+.BI "int bench_calibrate(struct bench_state *" b );
+.ta \w'\fBint bench_measure('u
+.BI "int bench_measure(struct bench_state *" b ", struct bench_timing *" t_out ,
+.BI " double " base ", bench_fn *" fn ", void *" ctx );
+.fi
+.
+.SH DESCRIPTION
+The header file
+.B "<mLib/bench.h>"
+provides declarations and defintions
+for performing low-level benchmarks.
+.PP
+The `main event' is
+.BR bench_measure .
+This function will be described in detail later,
+but, in brief,
+it calls a caller-provided function,
+instructing it to run adaptively chosen numbers of iterations,
+in order to get a reasonably reliable measurement of its running time,
+and then reports its results by filling in a structure.
+.PP
+With understanding this function as our objective,
+we must examine all of the pieces involved in making it work.
+.
+.SS Timers in general
+A
+.I timer
+is a gadget which is capable of reporting the current time,
+in seconds (ideally precise to tiny fractions of a second),
+and/or in CPU cycles.
+A timer is represented by a pointer to an object of type
+.BR "struct bench_timer" .
+This structure has a single member,
+.BR ops ,
+pointing to a
+.BR "struct bench_timerops" ,
+which is a table of function pointers;
+typically, a timer has more data following this,
+but this fact is not exposed to applications.
+.PP
+The function pointers in
+.B "struct bench_timerops"
+are as follows.
+The first argument,
+named
+.I tm
+must always point to the timer object itself.
+.TP
+.IB tm ->ops->describe( tm ", " d)
+Write a description of the timer to the dynamic string
+.IR d .
+.TP
+.IB tm ->ops->now( tm ", " t_out)
+Store the current time in
+.IR t_out .
+The
+.B struct bench_time
+used to represent the time reported by a timer
+is described in detail below.
+.TP
+.IB tm ->ops->destroy( tm )
+Destroy the timer,
+releasing all of the resources that it holds.
+.PP
+A time, a reported by a timer, is represented by the
+.BR "struct bench_time" .
+A passage-of-time measurement is stored in the
+.B s
+and
+.B ns
+members, holding seconds and nanoseconds respectively.
+(A timer need not have nanosecond precision.
+The exact interpretation of the time \(en
+e.g., whether it measures wallclock time,
+user-mode CPU time,
+or total thread CPU time \(en
+is a matter for the specific timer implementation.)
+A cycle count is stored in the
+.B cy
+member.
+The
+.B f
+member stores flags:
+.B BTF_TIMEOK
+is set if the passage-of-time measurement
+.B s
+and
+.B ns
+are valid; and
+.B BTF_CYOK
+is set if the cycle count
+.B cy
+is valid.
+Neither the time nor the cycle count need be measured
+relative to any particular origin.
+The mask
+.B BTF_ANY
+covers the
+.B BTF_TIMEOK
+and
+.B BTF_CYOK
+bits:
+hence,
+.IB f &BTF_ANY
+is nonzero (true)
+if the timer returned any valid timing information.
+.
+.SS The built-in timer
+The function
+.B bench_createtimer
+constructs and returns a timer.
+It takes a single argument,
+a string
+.IR config ,
+from which it reads configuration information.
+If
+.B bench_createtimer
+fails, it returns a null pointer.
+.PP
+The
+.I config
+pointer may safely be null,
+in which case a default configuration will be used.
+Applications
+.I should only
+set this pointer to a value supplied by a user,
+e.g., through a command-line argument,
+environment variable, or
+configuration file.
+.PP
+The built-in timer makes use of one or two
+.IR subtimers :
+a `clock' subtimer to measure the passage of time,
+and possibly a `cycle' subtimer to count CPU cycles.
+.PP
+The configuration string consists of a sequence of words
+separated by whitespace.
+There may be additional whitespace at the start and end of the string.
+The words recognized are as follows.
+.TP
+.B list
+Prints a list of the available clock and cycle subtimers
+to standard output.
+.TP
+.BI clock= t , ...
+Use the first of the listed clock subtimers
+to initialize successfully
+as the clock subtimer.
+If none of the subtimers can be initialized,
+then construction of the timer as a whole fails.
+.TP
+.BI cycle= t , ...
+Use the first of the listed subtimers
+to initialize successfully
+as the cycle subtimer.
+If none of the subtimers can be initialized,
+then construction of the timer as a whole fails.
+.PP
+The clock subtimers are as follows.
+Not all of them will be available on every platform.
+.TP
+.B posix-thread-cputime
+Measures the passage of time using
+.BR clock_gettime (2),
+specifying the
+.B CLOCK_\%THREAD_\%CPUTIME_\%ID
+clock.
+.TP
+.B stdc-clock
+Measures the passage of time using
+.BR clock (3).
+Since
+.BR clock (3)
+is part of the original ANSI\ C standard,
+this subtimer should always be available.
+However, it may produce unhelpful results
+if other threads are running.
+.PP
+The cycle subtimers are as follows.
+Not all of them will be available on every platform.
+.TP
+.B linux-perf-event
+Counts CPU cycles using the Linux-specific
+.BR perf_event_open (2)
+function to read the
+.BR PERF_\%COUNT_\%HW_\%CPU_\%CYCLES
+counter.
+Only available on Linux.
+It will fail to initialize
+if access to performance counters is restricted,
+e.g., because the
+.B /proc/sys/kernel/perf_event_paranoid
+level is too high.
+.TP
+.B x86-rdtsc
+Counts CPU cycles using the x86
+.B rdtsc
+instruction.
+This instruction is not really suitable for performance measurement:
+it gives misleading results on CPUs with variable clock frequency.
+.TP
+.B null
+A dummy cycle counter,
+which will initialize successfully
+and then fail to report cycle counts.
+This is a reasonable fallback in many situations.
+.PP
+The built-in preference order for clock subtimers,
+from most to least preferred, is
+.B posix-thread-cputime
+followed by
+.BR stdc-clock .
+The built-in preference order for cycle subtimers,
+from most to least preferred, is
+.B linux-perf-event
+followed by
+.BR x86-rdtsc ,
+and then
+.BR null .
+.
+.SS The benchmark state
+A
+.I benchmark state
+tracks the information needed to measure performance of functions.
+It is represented by a
+.B struct bench_state
+structure.
+.PP
+The benchmark state is initialized by calling
+.BR bench_init ,
+passing the address of the state structure to be initialized,
+and a pointer to a timer.
+If
+.B bench_init
+is called with a non-null timer pointer,
+then it will not fail;
+the benchmark state will be initialized,
+and the function returns zero.
+If the timer pointer is null,
+then
+.B bench_init
+attempts to construct a timer for itself
+by calling
+.BR bench_createtimer .
+If this succeeds,
+then the benchmark state will be initialized,
+and the function returns zero.
+In both cases,
+the timer becomes owned by the benchmark state:
+calling
+.B bench_destroy
+on the benchmark state will destroy the timer.
+If
+.B bench_init
+is called with a null timer pointer,
+and its attempt to create a timer for itself fails,
+then
+.B bench_init
+returns \-1;
+the benchmark state is not initialized
+and can safely be discarded;
+calling
+safe to call
+.B bench_destroy
+on the unsuccessfully benchmark state is safe and has no effect.
+.PP
+Calling
+.B bench_destroy
+on a benchmark state
+releases any resources it holds,
+most notably its timer, if any.
+.PP
+Although
+.B struct bench_state
+is defined in the header file,
+only two members are available for use by applications.
+.TP
+.B f
+A word containing flags.
+.TP
+.B target_s
+The target time for which to try run a benchmark, in seconds.
+After initialization, this is set to 1.0,
+though applications can override it.
+.PP
+Before the benchmark state can be used in measurements,
+it must be
+.IR calibrated .
+This is performed by calling
+.B bench_calibrate
+on the benchmark state.
+Calibration takes a noticeable amount of time
+(currently about 0.25\*,s),
+so it makes sense to defer it until it's known to be necessary.
+.PP
+Calibration is carried out separately, but in parallel,
+for the timer's passage-of-time measurement and cycle counter.
+Either or both of these calibrations can succeed or fail;
+if passage-of-time calibration fails,
+then cycle count calibration is impossible.
+.PP
+When it completes,
+.B bench_calibrate
+sets flag in the benchmark state's
+.B f
+member:
+if passage-of-time calibration succeeded,
+.B BTF_TIMEOK
+is set;
+if cycle-count calibration succeeded,
+.B BTF_CYOK
+is set;
+and the flag
+.B BTF_CLB
+is set unconditionally,
+as a persistent indication that calibration has been attempted.
+.PP
+The
+.B bench_calibrate
+function returns zero if it successfully calibrated
+at least the passage-of-time measurement;
+otherwise, it returns \-1.
+If
+.B bench_calibrate
+is called for a second or subsequent time on the same benchmark state,
+it returns immediately,
+either returning 0 or \-1
+according to whether passage-of-time had previously been calibrated.
+.
+.SS Timing functions
+A
+.I benchmark function
+has the signature
+.IP
+.BI "void " fn "(unsigned long " n ", void *" ctx );
+.PP
+When called, it should perform the operation to be measured
+.I n
+times.
+The
+.I ctx
+argument is a pointer passed into
+.B bench_measure
+for the benchmark function's own purposes.
+.PP
+The function
+.B bench_measure
+receives five arguments.
+.TP
+.I b
+points to the benchmark state to be used.
+.TP
+.I t_out
+is the address of a
+.BR struct bench_timing
+in which the measurement should be left.
+This structure is described below.
+.TP
+.I base
+is a count of the number of operations performed
+by each iteration of the benchmark function.
+.TP
+.I fn
+is a benchmark function, described above.
+.TP
+.I ctx
+is a pointer to be passed to the benchmark function.
+.B bench_measure
+does not interpret this pointer in any way.
+.PP
+The
+.B bench_measure
+function calls its benchark function repeatedly
+with different iteration counts
+.IR n ,
+with the objective that the call take approximately
+.B target_s
+seconds, as established in the benchmark state.
+(Currently, if
+.B target_s
+holds the value
+.IR t ,
+then
+.B bench_measure
+is satisfied when a call takes at least
+.IR t /\(sr2\*,s.)
+Once the function finds a satisfactory number of iterations,
+it stores the results in
+.BI * t_out \fR.
+If measurement succeeds, then
+.B bench_measure
+returns zero.
+If it fails \(en
+most likely because the timer failed \(en
+then it returns \-1.
+.PP
+A
+.B bench_timing
+structure reports the outcome of a successful measurement.
+It has four members.
+.TP
+.B f
+A flags word.
+.B BTF_TIMEOK
+is set if the passage-of-time measurement in
+.B t
+is valid;
+.B BTF_CYOK
+is set if the cycle count in
+.B cy
+is valid.
+.TP
+.B n
+The number of iterations performed by the benchmark function
+on its satisfactory run,
+multiplied by
+.IR base .
+.TP
+.B t
+The time taken for the satisfactory run of the benchmark function,
+in seconds.
+Only valid if
+.B BTF_TIMEOK
+is set in
+.BR f .
+.TP
+.B cy
+The number of CPU cycles used
+in the satisfactory run of the benchmark function,
+in seconds.
+Only valid if
+.B BTF_CYOK
+is set in
+.BR f .
+.
+.SH "SEE ALSO"
+.BR mLib (3).
+.
+.SH AUTHOR
+Mark Wooding, <mdw@distorted.org.uk>
static const struct tvec_vardef show_var =
{ sizeof(struct tvec_reg), common_setvar,
- { "@show", -1, &tvty_ienum, 0, { &tvenum_bool } } };
+ { "@show", &tvty_ienum, -1, 0, { &tvenum_bool } } };
static const struct tvec_vardef *common_findvar
(struct tvec_state *tv, const char *var, void **ctx_out, void *ctx)
#define COPYREG(name, i, ty, argslot, argval) \
static DSGINIT(const) struct tvec_regdef name##_copyregs[] = { \
- { #name, RVOUT, &tvty_##ty, 0, DSGINIT({ .argslot = argval }) }, \
+ { #name, &tvty_##ty, RVOUT, 0, DSGINIT({ .argslot = argval }) }, \
{ 0 } \
};
TYPEREGS(COPYREG)
#define SERREG(name, i, ty, argslot, argval) \
static DSGINIT(const) struct tvec_regdef name##_serregs[] = { \
- { #name, RV, &tvty_##ty, 0, DSGINIT({ .argslot = argval }) }, \
- { "buf", RSEROUT, &tvty_bytes }, \
- { "rc", RRC, &tvty_int, TVRF_OPT, { &tvrange_int } }, \
+ { #name, &tvty_##ty, RV, 0, \
+ DSGINIT({ .argslot = argval }) }, \
+ { "buf", &tvty_bytes, RSEROUT, 0 }, \
+ { "rc", &tvty_int, RRC, TVRF_OPT, \
+ { &tvrange_int } }, \
TVEC_ENDREGS \
}; \
static DSGINIT(const) struct tvec_regdef name##_deserregs[] = { \
- { "buf", RSER, &tvty_bytes }, \
- { #name, RVOUT, &tvty_##ty, 0, DSGINIT({ .argslot = argval }) }, \
- { "left", RLEFT, &tvty_uint, TVRF_OPT, { &tvrange_size } }, \
- { "rc", RRC, &tvty_int, TVRF_OPT, { &tvrange_int } }, \
+ { "buf", &tvty_bytes, RSER, 0 }, \
+ { #name, &tvty_##ty, RVOUT, 0, \
+ DSGINIT({ .argslot = argval }) }, \
+ { "left", &tvty_uint, RLEFT, TVRF_OPT, \
+ { &tvrange_size } }, \
+ { "rc", &tvty_int, RRC, TVRF_OPT, \
+ { &tvrange_int } }, \
TVEC_ENDREGS \
};
TYPEREGS(SERREG)
static DSGINIT(const) struct tvec_regdef multi_serialize_regs[] = {
#define DEFREG(name, i, ty, argslot, argval) \
- { #name, i, &tvty_##ty, TVRF_OPT, \
- DSGINIT({ .argslot = argval }) },
+ { #name, &tvty_##ty, i, TVRF_OPT, \
+ DSGINIT({ .argslot = argval }) },
TYPEREGS(DEFREG)
#undef DEFREG
- { "rc", RRC, &tvty_int, TVRF_OPT, { &tvrange_int } },
- { "serialized", RSEROUT, &tvty_bytes, TVRF_OPT },
- { "sabotage", RSAB, &tvty_ienum, TVRF_OPT, { ®_enum } },
+ { "rc", &tvty_int, RRC, TVRF_OPT, { &tvrange_int } },
+ { "serialized", &tvty_bytes, RSEROUT, TVRF_OPT },
+ { "sabotage", &tvty_ienum, RSAB, TVRF_OPT, { ®_enum } },
TVEC_ENDREGS
};
{ TVEC_REMOTEFORK(0, 0) };
static const struct tvec_regdef crash_regs[] = {
- { "crash", RSAB, &tvty_ienum, 0, { &tvenum_bool } },
- { "x", RV, &tvty_uint, 0, { &tvrange_uint } },
- { "z", RVOUT, &tvty_uint, 0, { &tvrange_uint } },
+ { "crash", &tvty_ienum, RSAB, 0, { &tvenum_bool } },
+ { "x", &tvty_uint, RV, 0, { &tvrange_uint } },
+ { "z", &tvty_uint, RVOUT, 0, { &tvrange_uint } },
TVEC_ENDREGS
};
{ TVEC_REMOTEFORK(&sleep_subenv._env, 0) };
static const struct tvec_regdef sleep_regs[] = {
- { "time", RV, &tvty_duration, 0, { &tvflt_nonneg } },
- { "z", RVOUT, &tvty_float, 0, { &tvflt_nonneg } },
+ { "time", &tvty_duration, RV, 0, { &tvflt_nonneg } },
+ { "z", &tvty_float, RVOUT, 0, { &tvflt_nonneg } },
TVEC_ENDREGS
};
.SH SYNOPSIS
.nf
.B "#include <mLib/testrig.h>"
-
+.PP
.B "#define TEST_FIELDMAX ..."
-
+.PP
.ta 2n
.B "typedef struct {"
.B " unsigned tests, failed;"
.B "} test_results";
-
+.PP
.B "typedef struct {"
.BI " void (*cvt)(const char *" buf ", dstr *" d );
.BI " void (*dump)(dstr *" d ", FILE *" fp );
.B "} test_type";
-
+.PP
.B "typedef struct {"
.B " const char *name;"
.BI " void (*test)(dstr " dv "[]);"
.B " const test_type *f[TEST_FIELDMAX];"
.B "} test_chunk";
-
+.PP
.B "typedef struct {"
.B " const char *name;"
.B " const test_chunk *chunks;"
.B "} test_suite";
-
+.PP
.B "const test_type type_hex;"
.B "const test_type type_string;"
.B "const test_type type_int;"
.B "const test_type type_long;"
.B "const test_type type_ulong;"
.B "const test_type type_uint32;"
-
+.PP
.ta \w'\fBint test_do('u
.BI "int test_do(const test_suite " suite [],
.BI " FILE *" fp ", test_results *" results );
--- /dev/null
+.\" -*-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
+. de VP
+. sp .4v
+..
+\}
+.el \{\
+. ds o o
+. de VP
+. sp
+..
+\}
+..
+.TH tvec-adhoc 3 "11 March 2024" "Straylight/Edgeware" "mLib utilities library"
+.SH NAME
+tvec-adhoc \- ad-hoc testing with the test vector framework
+.\" @tvec_adhocconfig
+.\" @tvec_adhoc
+.
+.\" @tvec_begingroup
+.\" @TVEC_BEGINGROUP
+.\" @tvec_endgroup
+.\" @TVEC_TESTGROUP
+.\" @TVEC_TESTGROUP_TAG
+.\" @tvec_begintest
+.\" @TVEC_BEGINTEST
+.\" @tvec_endtest
+.\" @TVEC_TEST
+.\" @TVEC_TEST_TAG
+.
+.\" @tvec_claim
+.\" @TVEC_CLAIM
+.\" @tvec_claim_eq
+.
+.SH SYNOPSIS
+.nf
+.B "#include <mLib/tvec.h>"
+.PP
+.BI "const struct tvec_config tvec_adhocconfig;"
+.BI "void tvec_adhoc(struct tvec_state *" tv ", struct tvec_test *" t );
+.PP
+.ta \w'\fBvoid tvec_begingroup('u
+.BI "void tvec_begingroup(struct tvec_state *" tv ", const char *" name ,
+.BI " const char *" file ", unsigned " lno );
+.BI "void TVEC_BEGINGROUP(struct tvec_state *" tv ", const char *" name );
+.BI "void tvec_endgroup(struct tvec_state *" tv );
+.BI "TVEC_TESTGROUP(" tv ", " name ") " body
+.BI "TVEC_TESTGROUP_TAG(" tag ", " tv ", " name ") " body
+.ta \w'\fBvoid tvec_begintest('u
+.BI "void tvec_begintest(struct tvec_state *" tv ,
+.BI " const char *" file ", unsigned " lno );
+.BI "void TVEC_BEGINTEST(struct tvec_state *" tv );
+.BI "void tvec_endtest(struct tvec_state *" tv );
+.BI "TVEC_TEST(" tv ") " body
+.BI "TVEC_TEST_TAG(" tag ", " tv ") " body
+.PP
+.ta \w'\fBint tvec_claim('u
+.BI "int tvec_claim(struct tvec_state *" tv ", int " ok ,
+.BI " const char *" file ", unsigned " lno ,
+.BI " const char *" msg ", ...);"
+.ta \w'\fBint tvec_claim_v('u
+.BI "int tvec_claim_v(struct tvec_state *" tv ", int " ok ,
+.BI " const char *" file ", unsigned " lno ,
+.BI " const char *" msg ", va_list *" ap );
+.BI "int TVEC_CLAIM(struct tvec_state *" tv ", int " cond );
+.ta \w'\fBint tvec_claim_eq('u
+.BI "int tvec_claim_eq(struct tvec_state *" tv ,
+.BI " const struct tvec_regty *" ty ,
+.BI " const union tvec_misc *" arg ,
+.BI " const char *" file ", unsigned " lno ,
+.BI " const char *" expr );
+.fi
--- /dev/null
+.\" -*-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
+. de VP
+. sp .4v
+..
+\}
+.el \{\
+. ds o o
+. de VP
+. sp
+..
+\}
+..
+.TH tvec-bench 3 "11 March 2024" "Straylight/Edgeware" "mLib utilities library"
+.SH NAME
+tvec-bench \- benchmarking with the test vector framework
+.\" @TVEC_BENCHENV
+.\" @TVEC_BENCHINIT
+.\" @tvec_benchreport
+.
+.SH SYNOPSIS
+.nf
+.B "#include <mLib/tvec.h>"
+.PP
+.ta 2n
+.B "struct tvec_benchenv {"
+.B " struct tvec_env _env;"
+.B " struct bench_state **bst;"
+.B " unsigned long niter;"
+.B " int riter, rbuf;"
+.B " const struct tvec_env *env;"
+.B "};"
+.B "struct bench_state *tvec_benchstate;"
+.B "#define TVEC_BENCHENV ..."
+.B "#define TVEC_BENCHINIT ..."
+.B "enum {"
+.B " TVBU_OP = ...,"
+.B " TVBU_BYTE = ...,"
+.B " ...,"
+.B " TVBU_LIMIT"
+.B "};"
+.PP
+.ta \w'\fBvoid tvec_benchreport('u
+.BI "void tvec_benchreport(const struct gprintf_ops *" gops ", void *" go ,
+.BI " unsigned " unit ", const struct bench_timing *" tm );
+.fi
}
static const struct tvec_vardef target_var =
- { sizeof(struct tvec_reg), setvar, { "@target", -1, &tvty_duration, 0 } };
+ { sizeof(struct tvec_reg), setvar, { "@target", &tvty_duration, -1, 0 } };
const struct tvec_vardef *tvec_benchfindvar
(struct tvec_state *tv, const char *var, void **ctx_out, void *ctx)
const struct tvec_reg *rin, *rout;
for (rd = tv->test->regs; rd->name; rd++) {
- if (rd->i >= tv->nrout) {
+ if (rd->i >= tv->cfg.nrout) {
if (!(f&TVMF_IN)) continue;
rin = TVEC_REG(tv, in, rd->i);
tvec_dumpreg(tv, TVRD_INPUT, rin->f&TVRF_LIVE ? &rin->v : 0, rd);
struct tvec_reg *r;
for (rd = tv->test->regs; rd->name; rd++) {
- assert(rd->i < tv->nreg); r = TVEC_REG(tv, in, rd->i);
+ assert(rd->i < tv->cfg.nreg); r = TVEC_REG(tv, in, rd->i);
rd->ty->init(&r->v, rd); r->f = 0;
- if (rd->i < tv->nrout)
+ if (rd->i < tv->cfg.nrout)
{ r = TVEC_REG(tv, out, rd->i); rd->ty->init(&r->v, rd); r->f = 0; }
}
}
struct tvec_reg *r;
for (rd = tv->test->regs; rd->name; rd++) {
- assert(rd->i < tv->nreg); r = TVEC_REG(tv, in, rd->i);
+ assert(rd->i < tv->cfg.nreg); r = TVEC_REG(tv, in, rd->i);
rd->ty->release(&r->v, rd); r->f = 0;
- if (rd->i < tv->nrout)
+ if (rd->i < tv->cfg.nrout)
{ r = TVEC_REG(tv, out, rd->i); rd->ty->release(&r->v, rd); r->f = 0; }
}
}
struct tvec_reg *r;
for (rd = tv->test->regs; rd->name; rd++) {
- assert(rd->i < tv->nreg);
- if (rd->i >= tv->nrout) continue;
+ assert(rd->i < tv->cfg.nreg);
+ if (rd->i >= tv->cfg.nrout) continue;
r = TVEC_REG(tv, out, rd->i);
rd->ty->release(&r->v, rd);
rd->ty->init(&r->v, rd);
const struct tvec_reg *rin, *rout;
for (rd = tv->test->regs; rd->name; rd++) {
- if (rd->i >= tv->nrout) continue;
+ if (rd->i >= tv->cfg.nrout) continue;
rin = TVEC_REG(tv, in, rd->i); rout = TVEC_REG(tv, out, rd->i);
if (!rin->f&TVRF_LIVE) continue;
if (!(rout->f&TVRF_LIVE) || !rd->ty->eq(&rin->v, &rout->v, rd))
if (!(tv->f&TVSF_OPEN)) return;
for (rd = t->regs; rd->name; rd++) {
- if (TVEC_REG(tv, in, rd->i)->f&TVRF_LIVE)
- { if (rd->i < tv->nrout) TVEC_REG(tv, out, rd->i)->f |= TVRF_LIVE; }
- else if (!(rd->f&TVRF_OPT)) {
+ if (TVEC_REG(tv, in, rd->i)->f&TVRF_LIVE) {
+ if (rd->i < tv->cfg.nrout)
+ TVEC_REG(tv, out, rd->i)->f |= TVRF_LIVE;
+ } else if (!(rd->f&TVRF_OPT)) {
tvec_error(tv, "required register `%s' not set in test `%s'",
rd->name, t->name);
f |= f_err;
{ "test-outcome", outcome_assoc, &outcome_range };
static const struct tvec_vardef outcome_vardef =
{ sizeof(struct tvec_reg), core_setvar,
- { "@outcome", 0, &tvty_uenum, 0, { &outcome_enum } } };
+ { "@outcome", &tvty_uenum, -1, 0, { &outcome_enum } } };
static const struct tvec_vardef *core_findvar
(struct tvec_state *tv, const char *name, void **ctx_out, void *ctx)
ch = getc(tv->fp); if (ch != ']') tvec_syntax(tv, ch, "`]'");
/* Find the matching test definition. */
- for (test = tv->tests; test->name; test++)
+ for (test = tv->cfg.tests; test->name; test++)
if (STRCMP(d.buf, ==, test->name)) goto found_test;
/* There wasn't one. Report the error. Muffle errors about the
tv_out->f = 0;
assert(config->nrout <= config->nreg);
- tv_out->nrout = config->nrout; tv_out->nreg = config->nreg;
- tv_out->regsz = config->regsz;
- tv_out->in = xmalloc(tv_out->nreg*tv_out->regsz);
- tv_out->out = xmalloc(tv_out->nrout*tv_out->regsz);
- for (i = 0; i < tv_out->nreg; i++) {
+ tv_out->cfg = *config;
+ tv_out->in = xmalloc(tv_out->cfg.nreg*tv_out->cfg.regsz);
+ tv_out->out = xmalloc(tv_out->cfg.nrout*tv_out->cfg.regsz);
+ for (i = 0; i < tv_out->cfg.nreg; i++) {
TVEC_REG(tv_out, in, i)->f = 0;
- if (i < tv_out->nrout) TVEC_REG(tv_out, out, i)->f = 0;
+ if (i < tv_out->cfg.nrout) TVEC_REG(tv_out, out, i)->f = 0;
}
for (i = 0; i < TVOUT_LIMIT; i++)
tv_out->curr[i] = tv_out->all[i] = tv_out->grps[i] = 0;
- tv_out->tests = config->tests; tv_out->test = 0;
+ tv_out->test = 0;
tv_out->infile = 0; tv_out->lno = 0; tv_out->fp = 0;
tv_out->output = o; tv_out->output->ops->bsession(tv_out->output, tv_out);
}
void tvec_adhoc(struct tvec_state *tv, struct tvec_test *t)
{
t->name = "<unset>"; t->regs = &no_regs; t->env = 0; t->fn = fakefn;
- tv->tests = t;
+ tv->cfg.tests = t;
}
/* --- @tvec_begingroup@ --- *
void tvec_begingroup(struct tvec_state *tv, const char *name,
const char *file, unsigned lno)
{
- struct tvec_test *t = (/*unconst*/ struct tvec_test *)tv->tests;
+ struct tvec_test *t = (/*unconst*/ struct tvec_test *)tv->cfg.tests;
t->name = name; tv->test = t;
tv->infile = file; tv->lno = tv->test_lno = lno;
--- /dev/null
+.\" -*-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
+. de VP
+. sp .4v
+..
+\}
+.el \{\
+. ds o o
+. de VP
+. sp
+..
+\}
+..
+.TH tvec-env 3 "11 March 2024" "Straylight/Edgeware" "mLib utilities library"
+.SH NAME
+tvec-env \- test vector framework environments
+.\" @TVEG_GREG
+.\" @TVEG_REG
+.
+.\" @tvec_skipgroup
+.\" @tvec_skipgroup_v
+.\" @tvec_skip
+.\" @tvec_skip_v
+.\" @tvec_fail
+.\" @tvec_fail_v
+.\" @tvec_dumpreg
+.
+.\" @tvec_checkregs
+.\" @tvec_mismatch
+.\" @tvec_check
+.\" @tvec_check_v
+.
+.\" @tvec_report
+.\" @tvec_report_v
+.\" @tvec_error
+.\" @tvec_notice
+.\" @tvec_unkreg
+.\" @tvec_dupreg
+.
+.\" @tvec_serialize
+.\" @tvec_deserialize
+.
+.\" @tvec_initregs
+.\" @tvec_releaseregs
+.\" @tvec_releaseoutputs
+.SH SYNOPSIS
+.nf
+.B "#include <mLib/tvec.h>"
+.PP
+.ta 2n
+.B "enum {"
+.B " TVRD_INPUT,"
+.B " TVRD_OUTPUT,"
+.B " TVRD_MATCH,"
+.B " TVRD_FOUND,"
+.B " TVRD_EXPECT,"
+.B " TVRD_LIMIT"
+.B "};"
+.B "struct tvec_state {"
+.B " unsigned f;"
+.B " struct tvec_config cfg;"
+.B " struct tvec_reg *in, *out;"
+.B " const struct tvec_test *test;"
+.B " ..."
+.B "};"
+.B "#define TVSF_SKIP ..."
+.B "#define TVSF_ACTIVE ..."
+.B "#define TVSF_OUTMASK ..."
+.B "#define TVSF_OUTSHIFT ..."
+.B "#define TVSF_XFAIL ..."
+.PP
+.ta \w'\fBtypedef int tvec_setvarfn('u
+.BI "typedef int tvec_setvarfn(struct tvec_state *" tv ", const char *" var ,
+.BI " const union tvec_regval *" rv ", void *" ctx );
+.ta \w'\fBtypedef int tvec_envsetupfn('u
+.BI "typedef void tvec_envsetupfn(struct tvec_state *" tv ,
+.BI " const struct tvec_env *" env ,
+.BI " void *" pctx ", void *" ctx );
+.ta 2n +\w'\fB('u
+.B "typedef const struct tvec_vardef *tvec_envfindvarfn"
+.BI " (struct tvec_state *" tv ", const char *" name ,
+.BI " void **" ctx_out ", void *" ctx );
+.BI "typedef void tvec_envbeforefn(struct tvec_state *" tv ", void *" ctx );
+.ta \w'\fBtypedef void tvec_envrunfn('u
+.BI "typedef void tvec_envrunfn(struct tvec_state *" tv ,
+.BI " tvec_testfn *" fn ", void *" ctx );
+.BI "typedef void tvec_envafterfn(struct tvec_state *" tv ", void *" ctx );
+.BI "typedef void tvec_envteardownfn(struct tvec_state *" tv ", void *" ctx );
+.ta 2n
+.B "struct tvec_env {"
+.B " size_t ctxsz;"
+.B " tvec_envsetupfn *setup;"
+.B " tvec_envfindvarfn *findvar;"
+.B " tvec_envbeforefn *before;"
+.B " tvec_envrunfn *run;"
+.B " tvec_envafterfn *after;"
+.B " tvec_envteardownfn *teardown;"
+.B "};"
+.PP
+.ta \w'\fBstruct tvec_reg *TVEC_GREG('u
+.BI "struct tvec_reg *TVEC_GREG(struct tvec_reg *" vec ,
+.BI " unsigned " i ", size_t " regsz );
+.ta \w'\fBstruct tvec_reg *TVEC_REG('u
+.BI "struct tvec_reg *TVEC_REG(struct tvec_state *" tv ", " vec ", unsigned " i );
+.PP
+.BI "void tvec_skipgroup(struct tvec_state *" tv ", const char *" excuse ", ...);"
+.ta \w'\fBvoid tvec_skipgroup_v('u
+.BI "void tvec_skipgroup_v(struct tvec_state *" tv ,
+.BI " const char *" excuse ", va_list *" ap );
+.BI "void tvec_skip(struct tvec_state *" tv ", const char *" excuse ", ...);"
+.ta \w'\fBvoid tvec_skip_v('u
+.BI "void tvec_skip_v(struct tvec_state *" tv ,
+.BI " const char *" excuse ", va_list *" ap );
+.BI "void tvec_fail(struct tvec_state *" tv ", const char *" detail ", ...);"
+.ta \w'\fBvoid tvec_fail_v('u
+.BI "void tvec_fail_v(struct tvec_state *" tv ,
+.BI " const char *" detail ", va_list *" ap );
+.ta \w'\fBvoid tvec_dumpreg('u
+.BI "void tvec_dumpreg(struct tvec_state *" tv ,
+.BI " unsigned " disp ", const union tvec_regval *" rv ,
+.BI " const struct tvec_regdef *" rd );
+.PP
+.BI "void tvec_checkregs(struct tvec_state *" tv );
+.BI "void tvec_mismatch(struct tvec_state *" tv ", unsigned " f );
+.BI "void tvec_check(struct tvec_state *" tv ", const char *" detail ", ...);"
+.ta \w'\fBvoid tvec_check_v('u
+.BI "void tvec_check_v(struct tvec_state *" tv ,
+.B "#define TVMF_IN ..."
+.B "#define TVMF_OUT ..."
+.PP
+.B "enum {"
+.B " TVLEV_NOTE = ...,"
+.B " TVLEV_ERR = ...,"
+.B " ..."
+.B "};"
+.ta \w'\fBvoid tvec_report('u
+.BI "void tvec_report(struct tvec_state *" tv ", unsigned " level ,
+.BI " const char *" msg ", ...);"
+.ta \w'\fBvoid tvec_report_v('u
+.BI "void tvec_report_v(struct tvec_state *" tv ", unsigned " level ,
+.BI " const char *" msg ", va_list *" ap );
+.BI "int tvec_error(struct tvec_state *" tv ", const char *" msg ", ...);"
+.BI "void tvec_notice(struct tvec_state *" tv ", const char *" msg ", ...);"
+.BI "int tvec_unkreg(struct tvec_state *" tv ", const char *" name );
+.BI "int tvec_dupreg(struct tvec_state *" tv ", const char *" name );
+.PP
+.ta \w'\fBint tvec_serialize('u
+.BI "int tvec_serialize(const struct tvec_reg *" rv ", buf *" b ,
+.BI " const struct tvec_regdef *" regs ,
+.BI " unsigned " nr ", size_t " regsz );
+.ta \w'\fBint tvec_deserialize('u
+.BI "int tvec_deserialize(struct tvec_reg *" rv ", buf *" b ,
+.BI " const struct tvec_regdef *" regs ,
+.BI " unsigned " nr ", size_t " regsz );
+.PP
+.BI "void tvec_initregs(struct tvec_state *" tv );
+.BI "void tvec_releaseregs(struct tvec_state *" tv );
+.BI "void tvec_releaseoutputs(struct tvec_state *" tv );
+.fi
--- /dev/null
+.\" -*-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
+. de VP
+. sp .4v
+..
+\}
+.el \{\
+. ds o o
+. de VP
+. sp
+..
+\}
+..
+.TH tvec-main 3 "11 March 2024" "Straylight/Edgeware" "mLib utilities library"
+.SH NAME
+tvec-main \- test vector framework program frontend
+.\" @tvec_parseargs
+.\" @tvec_readstdin
+.\" @tvec_readfile
+.\" @tvec_readarg
+.\" @tvec_readdflt
+.\" @tvec_readargs
+.\" @tvec_main
+.
+.SH SYNOPSIS
+.nf
+.B "#include <mLib/tvec.h>"
+.PP
+.ta \w'\fBvoid tvec_parseargs('u
+.BI "void tvec_parseargs(int " argc ", char *" argv "[],"
+.BI " struct tvec_state *" tv_out ,
+.BI " int *" argpos_out ,
+.BI " const struct tvec_config *" config );
+.BI "int tvec_readstdin(struct tvec_state *" tv );
+.BI "int tvec_readfile(struct tvec_state *" tv ", const char *" file );
+.BI "int tvec_readarg(struct tvec_state *" tv ", const char *" arg );
+.BI "int tvec_readdflt(struct tvec_state *" tv ", const char *" file );
+.ta \w'\fBvoid tvec_readargs('u
+.BI "void tvec_readargs(int " argc ", char *" argv "[],"
+.BI " struct tvec_state *" tv ,
+.BI " int *" argpos_out ", const char *" dflt );
+.ta \w'\fBvoid tvec_main('u
+.BI "void tvec_main(int " argc ", char *" argv "[],"
+.BI " const struct tvec_config *" config ", const char *" dflt );
+.fi
--- /dev/null
+.\" -*-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
+. de VP
+. sp .4v
+..
+\}
+.el \{\
+. ds o o
+. de VP
+. sp
+..
+\}
+..
+.TH tvec-output 3 "11 March 2024" "Straylight/Edgeware" "mLib utilities library"
+.SH NAME
+tvec-output \- test vector framework output driver interface
+.\" @tvec_strlevel
+.
+.SH SYNOPSIS
+.nf
+.B "#include <mLib/tvec.h>"
+.PP
+.ta 2n
+.B "enum {"
+.B " TVOUT_LOSE,"
+.B " TVOUT_SKIP,"
+.B " TVOUT_WIN,"
+.B " TVOUT_XFAIL,"
+.B " TVOUT_LIMIT"
+.B "};"
+.PP
+.B "struct tvec_state {"
+.B " unsigned f;"
+.B " const struct tvec_test *test;"
+.B " unsigned curr[TVOUT_LIMIT], all[TVOUT_LIMIT], grps[TVOUT_LIMIT];"
+.B " ..."
+.B "};"
+.B "#define TVSF_ERROR ..."
+.PP
+.B "struct tvec_output {"
+.B " const struct tvec_outops *ops;"
+.B "};"
+.B "struct tvec_outops {"
+.BI " void (*" bsession ")(struct tvec_output *" o ", struct tvec_state *" tv );
+.BI " int (*" esession ")(struct tvec_output *" o );
+.BI " void (*" bgroup ")(struct tvec_output *" o );
+.ta 2n +\w'\fBvoid (*\,\fIskipgroup\/\fB)('u
+.BI " void (*" skipgroup ")(struct tvec_output *" o ,
+.BI " const char *" excuse ", void *" ap );
+.BI " void (*" egroup ")(struct tvec_output *" o );
+.BI " void (*" btest ")(struct tvec_output *" o );
+.ta 2n +\w'\fBvoid (*\,\fIskip\/\fB)('u
+.BI " void (*" skip ")(struct tvec_output *" o ,
+.BI " const char *" excuse ", void *" ap );
+.ta 2n +\w'\fBvoid (*\,\fIfail\/\fB)('u
+.BI " void (*" fail ")(struct tvec_output *" o ,
+.BI " const char *" fail ", void *" ap );
+.ta 2n +\w'\fBvoid (*\,\fIdumpreg\/\fB)('u
+.BI " void (*" dumpreg ")(struct tvec_output *" o ,
+.BI " unsigned " disp ", const union tvec_regval *" rv ,
+.BI " const struct tvec_regdef *" rd );
+.BI " void (*" etest ")(struct tvec_output *" o ", unsigned " outcome );
+.ta 2n +\w'\fBvoid (*\,\fIbbench\/\fB)('u
+.BI " void (*" bbench ")(struct tvec_output *" o ,
+.BI " const char *" ident ", unsigned " unit );
+.ta 2n +\w'\fBvoid (*\,\fIebench\/\fB)('u
+.BI " void (*" ebench ")(struct tvec_output *" o ,
+.BI " const char *" ident ", unsigned " unit ,
+.BI " const struct bench_timing *" tm );
+.ta 2n +\w'\fBvoid (*\,\fIreport\/\fB)('u
+.BI " void (*" report ")(struct tvec_output *" o ", unsigned " level ,
+.BI " const char *" msg ", va_list *" ap );
+.BI " void (*" level ")(struct tvec_output *" o );
+.B "};"
+.PP
+.B "const char *tvec_strlevel(unsigned " level );
+.fi
--- /dev/null
+.\" -*-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
+. de VP
+. sp .4v
+..
+\}
+.el \{\
+. ds o o
+. de VP
+. sp
+..
+\}
+..
+.TH tvec-remote 3 "11 March 2024" "Straylight/Edgeware" "mLib utilities library"
+.SH NAME
+tvec-remote \- test vector framework remote invocation
+.\" @TVEC_REMOTEENV
+.\" @TVEC_FORK
+.\" @TVEC_EXEC
+.\" @tvec_fork
+.\" @tvec_exec
+.
+.\" @tvec_setprogress
+.\" @tvec_setprogress_v
+.
+.\" @tvec_remoteserver
+.
+.SH SYNOPSIS
+.nf
+.B "#include <mLib/tvec.h>"
+.PP
+.ta \w'\fBtypedef int tvec_connectfn('u
+.BI "typedef int tvec_connectfn(pid_t *" kid_out ", int *" infd_out ,
+.BI " int *" outfd_out ", int *" errfd_out ,
+.BI " struct tvec_state *" tv ,
+.BI " const struct tvec_remoteenv *" env );
+.PP
+.ta 2n
+.B "struct tvec_remoteenv_slots {"
+.B " tvec_connectfn *connect;"
+.B " const struct tvec_env *env;"
+.B " unsigned dflt_reconn;"
+.B "};"
+.B "struct tvec_remoteenv {"
+.B " struct tvec_env _env;"
+.B " struct tvec_remoteenv_slots r;"
+.B "};"
+.B "#define TVEC_REMOTEENV ..."
+.PP
+.B "#define TVXF_VALMASK ..."
+.B "#define TVXF_SIG ..."
+.B "#define TVXF_CAUSEMASK ..."
+.B "#define TVXST_RUN ..."
+.B "#define TVXST_EXIT ..."
+.B "#define TVXST_KILL ..."
+.B "#define TVXST_CONT ..."
+.B "#define TVXST_STOP ..."
+.B "#define TVXST_DISCONN ..."
+.B "#define TVXST_UNK ..."
+.B "#define TVXST_ERR ..."
+.PP
+.B "struct tvec_remotefork_slots {"
+.B " const struct tvec_test *tests;"
+.B "};"
+.B "struct tvec_remotefork {"
+.B " struct tvec_env _env;"
+.B " struct tvec_remoteenv_slots r;"
+.B " struct tvec_remotefork_slots f;"
+.B "};"
+.B "tvec_connectfn tvec_fork;"
+.BI "#define TVEC_REMOTEFORK(" subenv ", " tests ") ..."
+.PP
+.B "struct tvec_remoteexec_slots {"
+.B " const char *const *args;"
+.B "};"
+.B "struct tvec_remoteexec {"
+.B " struct tvec_env _env;"
+.B " struct tvec_remoteenv_slots r;"
+.B " struct tvec_remoteexec_slots x;"
+.B "};"
+.B "tvec_connectfn tvec_exec;"
+.BI "#define TVEC_REMOTEEXEC(" subenv ", " args ") ..."
+.PP
+.BI "void tvec_setprogress(const char *" status ", ...);"
+.BI "void tvec_setprogress_v(const char *" status ", va_list *" ap );
+.PP
+.ta \w'\fBint tvec_remoteserver('u
+.BI "int tvec_remoteserver(int " infd ", int " outfd ,
+.BI " const struct tvec_config *" config );
+.fi
if (BLEFT(&b)) goto bad;
/* Find the group given its name. */
- for (t = srvtv.tests; t->name; t++)
+ for (t = srvtv.cfg.tests; t->name; t++)
if (strlen(t->name) == sz && MEMCMP(t->name, ==, p, sz))
goto found_group;
rc = ioerr(&srvtv, &srvrc, "unknown test group `%.*s'",
/* Parse the packet payload. */
if (tvec_deserialize(srvtv.in, &b, srvtv.test->regs,
- srvtv.nreg, srvtv.regsz))
+ srvtv.cfg.nreg, srvtv.cfg.regsz))
goto bad;
if (BLEFT(&b)) goto bad;
/* Prepare the output registers and reset the test outcome.
* (The environment may force a skip.)
*/
- for (i = 0; i < srvtv.nrout; i++)
+ for (i = 0; i < srvtv.cfg.nrout; i++)
if (TVEC_REG(&srvtv, in, i)->f&TVRF_LIVE)
TVEC_REG(&srvtv, out, i)->f |= TVRF_LIVE;
srvtv.f |= TVSF_ACTIVE; srvtv.f &= ~TVSF_OUTMASK;
{ "exit-status", exit_flags, &tvrange_uint };
static const struct tvec_vardef exit_var =
{ sizeof(struct tvec_reg), setvar_local,
- { "@exit", -1, &tvty_flags, 0, { &exit_flaginfo } } };
+ { "@exit", &tvty_flags, -1, 0, { &exit_flaginfo } } };
/* Progress. */
static const struct tvec_vardef progress_var =
{ sizeof(struct tvec_reg), setvar_local,
- { "@progress", -1, &tvty_text, 0 } };
+ { "@progress", &tvty_text, -1, 0 } };
/* Reconnection. */
{ "remote-reconnection", reconn_assocs, &tvrange_uint };
static const struct tvec_vardef reconn_var =
{ sizeof(struct tvec_reg), setvar_local,
- { "@reconnect", -1, &tvty_uenum, 0, { &reconn_enuminfo } } };
+ { "@reconnect", &tvty_uenum, -1, 0, { &reconn_enuminfo } } };
/*----- Client ------------------------------------------------------------*/
buf *b = b_out;
const struct tvec_regdef *rd;
struct bench_timing bt;
- struct tvec_reg *reg = 0;
+ struct tvec_reg *reg = 0; size_t rsz = 0;
unsigned i;
int rc;
if (!rc)
tvec_dumpreg(tv, v, 0, rd);
else {
- if (!reg) reg = xmalloc(tv->regsz);
+ GROWBUF_REPLACE(&arena_stdlib, reg, rsz, tv->cfg.regsz,
+ 8*sizeof(void *), 1);
rd->ty->init(®->v, rd);
rc = rd->ty->frombuf(b, ®->v, rd);
if (!rc) tvec_dumpreg(tv, v, ®->v, rd);
/* Send the command to the server and handle output. */
QUEUEPK(tv, &r->rc, QF_FORCE, TVPK_TEST)
tvec_serialize(tv->in, DBUF_BUF(&r->rc.bout),
- tv->test->regs, tv->nreg, tv->regsz);
+ tv->test->regs, tv->cfg.nreg, tv->cfg.regsz);
else { goto fail; }
rc = handle_packets(tv, r, RCVF_ALLOWEOF, TVPK_TEST | TVPF_ACK, &b);
if (fork_common(&kid, &infd, &outfd, &errfd, tv)) { rc = -1; goto end; }
if (!kid) {
if (tv->fp) fclose(tv->fp);
- config.tests = rf->f.tests ? rf->f.tests : tv->tests;
- config.nrout = tv->nrout; config.nreg = tv->nreg;
- config.regsz = tv->regsz;
+ config.tests = rf->f.tests ? rf->f.tests : tv->cfg.tests;
+ config.nrout = tv->cfg.nrout; config.nreg = tv->cfg.nreg;
+ config.regsz = tv->cfg.regsz;
_exit(tvec_remoteserver(infd, outfd, &config));
}
--- /dev/null
+.\" -*-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
+. de VP
+. sp .4v
+..
+\}
+.el \{\
+. ds o o
+. de VP
+. sp
+..
+\}
+..
+.TH tvec-timeout 3 "11 March 2024" "Straylight/Edgeware" "mLib utilities library"
+.SH NAME
+tvec-timeout \- test vector framework timeouts
+.\" @TVEC_TIMEOUTENV
+.\" @TVEC_TIMEOUTINIT
+.
+.SH SYNOPSIS
+.nf
+.B "#include <mLib/tvec.h>"
+.PP
+.ta 2n
+.B "struct tvec_timeoutenv {"
+.B " struct tvec_env _env;"
+.B " int timer;"
+.B " double t;"
+.B " const struct tvec_env *env;"
+.B "};"
+.B "#define TVEC_TIMEOUTENV ..."
+.BI "#define TVEC_TIMEOUTINIT(" timer ", " t ") ..."
+.fi
static const struct tvec_vardef timeout_var =
{ sizeof(struct tvec_reg), setvar,
- { "@timeout", -1, &tvty_duration, 0 } };
+ { "@timeout", &tvty_duration, -1, 0 } };
static const struct tvec_iassoc timer_assocs[] = {
{ "REAL", ITIMER_REAL },
{ "interval-timer", timer_assocs, &tvrange_int };
static const struct tvec_vardef timer_var =
{ sizeof(struct tvec_reg), setvar,
- { "@timer", -1, &tvty_ienum, 0, { &timer_enum } } };
+ { "@timer", &tvty_ienum, -1, 0, { &timer_enum } } };
const struct tvec_vardef *tvec_timeoutfindvar
(struct tvec_state *tv, const char *var, void **ctx_out, void *ctx)
--- /dev/null
+.\" -*-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
+. de VP
+. sp .4v
+..
+\}
+.el \{\
+. ds o o
+. de VP
+. sp
+..
+\}
+..
+.TH tvec-tyimpl 3 "11 March 2024" "Straylight/Edgeware" "mLib utilities library"
+.SH NAME
+tvec-tyimpl \- test vector framework type implementation
+.\" @tvec_syntax
+.\" @tvec_syntax_v
+.
+.\" @tvec_skipspc
+.\" @tvec_flushtoeol
+.\" @tvec_nexttoken
+.\" @tvec_readword
+.\" @tvec_readword_v
+.
+.SH SYNOPSIS
+.nf
+.B "#include <mLib/tvec.h>"
+.PP
+.B "struct tvec_state {"
+.B " unsigned f;"
+.B " const char *infile; unsigned lno, test_lno;"
+.B " FILE *fp;"
+.B " ..."
+.B "};"
+.PP
+.B "struct tvec_regty {"
+.ta 2n +\w'\fBvoid (*init)('u
+.BI " void (*init)(union tvec_regval *" rv ,
+.BI " const struct tvec_regdef *" rd );
+.ta 2n +\w'\fBvoid (*release)('u
+.BI " void (*release)(union tvec_regval *" rv ,
+.BI " const struct tvec_regdef *" rd );
+.ta 2n +\w'\fBint (*eq)('u
+.BI " int (*eq)(const union tvec_regval *" rv0 ,
+.BI " const union tvec_regval *" rv1 ,
+.BI " const struct tvec_regdef *" rd );
+.ta 2n +\w'\fBint (*tobuf)('u
+.BI " int (*tobuf)(buf *" b ", const union tvec_regval *" rv ,
+.BI " const struct tvec_regdef *" rd );
+.ta 2n +\w'\fBint (*frombuf)('u
+.BI " int (*frombuf)(buf *" b ", union tvec_regval *" rv ,
+.BI " const struct tvec_regdef *" rd );
+.ta 2n +\w'\fBint (*parse)('u
+.BI " int (*parse)(union tvec_regval *" rv ", const struct tvec_regdef *" rd ,
+.BI " struct tvec_state *" tv );
+.ta 2n +\w'\fBvoid (*dump)('u
+.BI " void (*dump)(const union tvec_regval *" rv ,
+.BI " const struct tvec_regdef *" rd ,
+.BI " unsigned " style ,
+.BI " const struct gprintf_ops *" gops ", void *" go );
+.B "};"
+.B "#define TVSF_COMPACT ..."
+.PP
+.ta \w'\fBint tvec_syntax('u
+.BI "int tvec_syntax(struct tvec_state *" tv ", int " ch ,
+.BI " const char *" expect ", ...);"
+.ta \w'\fBint tvec_syntax_v('u
+.BI "int tvec_syntax(struct tvec_state *" tv ", int " ch ,
+.BI " const char *" expect ", va_list *" ap );
+.PP
+.B "#define TVFF_ALLOWANY ..."
+.B "void tvec_skipspc(struct tvec_state *" tv );
+.B "int tvec_flushtoeol(struct tvec_state *" tv );
+.B "int tvec_nexttoken(struct tvec_state *" tv );
+.ta \w'\fBint tvec_readword('u
+.BI "int tvec_readword(struct tvec_state *" tv ", dstr *" d ,
+.BI " const char **" p_inout ", const char *" delims ,
+.BI " const char *" expect ", ...);"
+.ta \w'\fBint tvec_readword_v('u
+.BI "int tvec_readword_v(struct tvec_state *" tv ", dstr *" d ,
+.BI " const char **" p_inout ", const char *" delims ,
+.BI " const char *" expect ", va_list *" ap );
+.fi
--- /dev/null
+.\" -*-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
+. de VP
+. sp .4v
+..
+\}
+.el \{\
+. ds o o
+. de VP
+. sp
+..
+\}
+..
+.TH tvec-types 3 "11 March 2024" "Straylight/Edgeware" "mLib utilities library"
+.SH NAME
+tvec-types \- test vector framework provided register types
+.\" @tvty_int
+.\" @tvty_uint
+.\" @tvty_float
+.\" @tvty_duration
+.\" @tvty_ienum
+.\" @tvty_uenum
+.\" @tvty_fenum
+.\" @tvty_penum
+.\" @tvty_flags
+.\" @tvty_char
+.\" @tvty_text
+.\" @tvty_bytes
+.\" @tvty_buffer
+.
+.\" @tvrange_schar
+.\" @tvrange_short
+.\" @tvrange_int
+.\" @tvrange_long
+.\" @tvrange_sbyte
+.\" @tvrange_i16
+.\" @tvrange_i32
+.\" @tvrange_uchar
+.\" @tvrange_ushort
+.\" @tvrange_uint
+.\" @tvrange_ulong
+.\" @tvrange_size
+.\" @tvrange_byte
+.\" @tvrange_u16
+.\" @tvrange_u32
+.
+.\" @tvflt_finite
+.\" @tvflt_nonneg
+.
+.\" @tvenum_bool
+.\" @tvenum_cmp
+.
+.\" @tvec_claimeq_int
+.\" @tvec_claimeq_uint
+.\" @tvec_claimeqish_float
+.\" @tvec_claimeq_float
+.\" @tvec_claimeq_ienum
+.\" @tvec_claimeq_uenum
+.\" @tvec_claimeq_fenum
+.\" @tvec_claimeq_penum
+.\" @tvec_claimeq_flags
+.\" @tvec_claimeq_char
+.\" @tvec_claimeq_text
+.\" @tvec_claimeq_textz
+.\" @tvec_claimeq_bytes
+.\" @TVEC_CLAIMEQ_INT
+.\" @TVEC_CLAIMEQ_UINT
+.\" @TVEC_CLAIMEQISH_FLOAT
+.\" @TVEC_CLAIMEQ_FLOAT
+.\" @TVEC_CLAIMEQ_IENUM
+.\" @TVEC_CLAIMEQ_UENUM
+.\" @TVEC_CLAIMEQ_FENUM
+.\" @TVEC_CLAIMEQ_PENUM
+.\" @TVEC_CLAIMEQ_FLAGS
+.\" @TVEC_CLAIMEQ_CHAR
+.\" @TVEC_CLAIMEQ_TEXT
+.\" @TVEC_CLAIMEQ_TEXTZ
+.\" @TVEC_CLAIMEQ_BYTES
+.
+.\" @tvec_parsedurunit
+.\" @tvec_alloctext
+.\" @tvec_allocbytes
+.\" @tvec_initbuffer
+.\" @tvec_allocbuffer
+.
+.SH SYNOPSIS
+.nf
+.B "#include <mLib/tvec.h>"
+.PP
+.B "const struct tvec_regty tvty_int, tvty_uint;"
+.PP
+.B "struct tvec_irange { long min, max; };"
+.B "struct tvec_urange { unsigned long min, max; };"
+.PP
+.ta 2n
+.B "const struct tvec_irange"
+.B " tvrange_schar, tvrange_short, tvrange_int, tvrange_long,"
+.B " tvrange_sbyte, tvrange_i16, tvrange_i32;"
+.B "const struct tvec_urange"
+.B " tvrange_uchar, tvrange_ushort, tvrange_uint,"
+.B " tvrange_ulong, tvrange_size,"
+.B " tvrange_byte, tvrange_u16, tvrange_u32;"
+.PP
+.ta \w'\fBint tvec_claimeq_int('u
+.BI "int tvec_claimeq_int(struct tvec_state *" tv ,
+.BI " long " i0 ", long " i1 ,
+.BI " const char *" file ", unsigned " lno ,
+.BI " const char *" expr );
+.BI "int TVEC_CLAIMEQ_INT(struct tvec_state *" tv ", long " i0 ", long " i1 );
+.ta \w'\fBint tvec_claimeq_uint('u
+.BI "int tvec_claimeq_uint(struct tvec_state *" tv ,
+.BI " unsigned long " u0 ", unsigned long " u1 ,
+.BI " const char *" file ", unsigned " lno ,
+.BI " const char *" expr );
+.ta \w'\fBint TVEC_CLAIMEQ_UINT('u
+.BI "int TVEC_CLAIMEQ_UINT(struct tvec_state *" tv ,
+.BI " unsigned long " u0 ", unsigned long " u1 );
+.PP
+.B "const struct tvec_regty tvty_float;"
+.PP
+.ta 2n
+.B "struct tvec_floatinfo {"
+.B " unsigned f;"
+.B " double min, max;"
+.B " double delta;"
+.B "};"
+.B "#define TVFF_NOMIN ..."
+.B "#define TVFF_NOMAX ..."
+.B "#define TVFF_NANOK ..."
+.B "#define TVFF_EQMASK ..."
+.B "#define TVFF_EXACT ..."
+.B "#define TVFF_ABSDELTA ..."
+.B "#define TVFF_RELDELTA ..."
+.PP
+.B "const struct tvec_floatinfo tvflt_finite, tvflt_nonneg;"
+.PP
+.ta \w'\fBint tvec_claimeqish_float('u
+.BI "int tvec_claimeqish_float(struct tvec_state *" tv ,
+.BI " double " f0 ", double " f1 ,
+.BI " unsigned " f ", double " delta ,
+.BI " const char *" file ", unsigned " lno ,
+.BI " const char *" expr );
+.ta \w'\fBint TVEC_CLAIMEQISH_FLOAT('u
+.BI "int TVEC_CLAIMEQISH_FLOAT(struct tvec_state *" tv ,
+.BI " double " f0 ", double " f1 ,
+.BI " unsigned " f ", double " delta );
+.ta \w'\fBint tvec_claimeq_float('u
+.BI "int tvec_claimeq_float(struct tvec_state *" tv ,
+.BI " double " f0 ", double " f1 ,
+.BI " const char *" file ", unsigned " lno ,
+.BI " const char *" expr );
+.ta \w'\fBint TVEC_CLAIMEQ_FLOAT('u
+.BI "int TVEC_CLAIMEQ_FLOAT(struct tvec_state *" tv ,
+.BI " double " f0 ", double " f1 );
+.PP
+.B "const struct tvec_regty tvty_duration;"
+.PP
+.BI "int tvec_parsedurunit(double *" scale_out ", const char **" p_inout );
+.PP
+.B "const struct tvec_regty tvty_ienum, tvty_uenum, tvty_fenum, tvty_penum;"
+.PP
+.B "struct tvec_iassoc { const char *tag; long i; };"
+.B "struct tvec_uassoc { const char *tag; unsigned long u; };"
+.B "struct tvec_fassoc { const char *tag; double f; };"
+.B "struct tvec_passoc { const char *tag; void *p; };"
+.B "#define TVEC_ENDENUM ..."
+.PP
+.ta 2n
+.B "struct tvec_ienuminfo {"
+.B " const char *name;"
+.B " const struct tvec_iassoc *av;"
+.B " const struct tvec_irange *ir;"
+.B "};"
+.B "struct tvec_uenuminfo {"
+.B " const char *name;"
+.B " const struct tvec_uassoc *av;"
+.B " const struct tvec_urange *ur;"
+.B "};"
+.B "struct tvec_fenuminfo {"
+.B " const char *name;"
+.B " const struct tvec_fassoc *av;"
+.B " const struct tvec_floatinfo *fi;"
+.B "};"
+.B "struct tvec_penuminfo {"
+.B " const char *name;"
+.B " const struct tvec_passoc *av;"
+.B "};"
+.B "const struct tvec_ienuminfo tvenum_bool;"
+.B "const struct tvec_ienuminfo tvenum_cmp;"
+.PP
+.ta \w'\fBint tvec_claimeq_ienum('u
+.BI "int tvec_claimeq_ienum(struct tvec_state *" tv ,
+.BI " const struct tvec_uenuminfo *" ei ,
+.BI " long " i0 ", long " i1 ,
+.BI " const char *" file ", unsigned " lno ,
+.BI " const char *" expr );
+.ta \w'\fBint TVEC_CLAIMEQ_IENUM('u
+.BI "int TVEC_CLAIMEQ_IENUM(struct tvec_state *" tv ,
+.BI " const struct tvec_uenuminfo *" ei ,
+.BI " long " i0 ", long " i1 );
+.ta \w'\fBint tvec_claimeq_uenum('u
+.BI "int tvec_claimeq_uenum(struct tvec_state *" tv ,
+.BI " const struct tvec_uenuminfo *" ei ,
+.BI " unsigned long " u0 ", unsigned long " u1 ,
+.BI " const char *" file ", unsigned " lno ,
+.BI " const char *" expr );
+.ta \w'\fBint TVEC_CLAIMEQ_UENUM('u
+.BI "int TVEC_CLAIMEQ_UENUM(struct tvec_state *" tv ,
+.BI " const struct tvec_uenuminfo *" ei ,
+.BI " unsigned long " u0 ", unsigned long " u1 );
+.ta \w'\fBint tvec_claimeq_fenum('u
+.BI "int tvec_claimeq_fenum(struct tvec_state *" tv ,
+.BI " const struct tvec_fenuminfo *" ei ,
+.BI " double " f0 ", double " f1 ,
+.BI " const char *" file ", unsigned " lno ,
+.BI " const char *" expr );
+.ta \w'\fBint TVEC_CLAIMEQ_FENUM('u
+.BI "int TVEC_CLAIMEQ_FENUM(struct tvec_state *" tv ,
+.BI " const struct tvec_fenuminfo *" ei ,
+.BI " double " f0 ", double " f1 );
+.ta \w'\fBint tvec_claimeq_penum('u
+.BI "int tvec_claimeq_penum(struct tvec_state *" tv ,
+.BI " const struct tvec_penuminfo *" ei ,
+.BI " const void *" p0 ", const void *" p1 ,
+.BI " const char *" file ", unsigned " lno ,
+.BI " const char *" expr );
+.ta \w'\fBint TVEC_CLAIMEQ_PENUM('u
+.BI "int TVEC_CLAIMEQ_PENUM(struct tvec_state *" tv ,
+.BI " const struct tvec_penuminfo *" ei ,
+.BI " const void *" p0 ", const void *" p1 );
+.PP
+.B "const struct tvec_regty tvty_flags;"
+.PP
+.B "struct tvec_flag { const char *name; unsigned long m, v; };"
+.B "#define TVEC_ENDFLAGS ..."
+.PP
+.ta 2n
+.B "struct tvec_flaginfo {"
+.B " const char *name;"
+.B " const struct tvec_flag *fv;"
+.B " const struct tvec_urange *range;"
+.B "};"
+.PP
+.ta \w'\fBint tvec_claimeq_flags('u
+.BI "int tvec_claimeq_flags(struct tvec_state *" tv ,
+.BI " const struct tvec_flaginfo *" fi ,
+.BI " unsigned long " f0 ", unsigned long " f1 ,
+.BI " const char *" file ", unsigned " lno ,
+.BI " const char *" expr );
+.ta \w'\fBint TVEC_CLAIMEQ_FLAGS('u
+.BI "int TVEC_CLAIMEQ_UENUM(struct tvec_state *" tv ,
+.BI " const struct tvec_flaginfo *" fi ,
+.BI " unsigned long " f0 ", unsigned long " f1 );
+.PP
+.B "const struct tvec_regty tvty_char;"
+.PP
+.ta \w'\fBint tvec_claimeq_char('u
+.BI "int tvec_claimeq_char(struct tvec_state *" tv ,
+.BI " int " ch0 ", int " ch1 ,
+.BI " const char *" file ", unsigned " lno ,
+.BI " const char *" expr );
+.BI "int TVEC_CLAIMEQ_CHAR(struct tvec_state *" tv ", int " ch0 ", int " ch1 );
+.PP
+.B "const struct tvec_regty tvty_text, tvty_bytes;"
+.PP
+.BI "void tvec_alloctext(union tvec_regval *" rv ", size_t " sz );
+.BI "void tvec_allocbytes(union tvec_regval *" rv ", size_t " sz );
+.PP
+.ta \w'\fBint tvec_claimeq_text('u
+.BI "int tvec_claimeq_text(struct tvec_state *" tv ,
+.BI " const char *" p0 ", size_t " sz0 ,
+.BI " const char *" p1 ", size_t " sz1 ,
+.BI " const char *" file ", unsigned " lno ,
+.BI " const char *" expr );
+.ta \w'\fBint TVEC_CLAIMEQ_TEXT('u
+.BI "int TVEC_CLAIMEQ_TEXT(struct tvec_state *" tv ,
+.BI " const char *" p0 ", size_t " sz0 ,
+.BI " const char *" p1 ", size_t " sz1 );
+.ta \w'\fBint tvec_claimeq_textz('u
+.BI "int tvec_claimeq_textz(struct tvec_state *" tv ,
+.BI " const char *" p0 ", const char *" p1 ,
+.BI " const char *" file ", unsigned " lno ,
+.BI " const char *" expr );
+.ta \w'\fBint TVEC_CLAIMEQ_TEXTZ('u
+.BI "int TVEC_CLAIMEQ_TEXTZ(struct tvec_state *" tv ,
+.BI " const char *" p0 " const char *" p1 );
+.ta \w'\fBint tvec_claimeq_bytes('u
+.BI "int tvec_claimeq_bytes(struct tvec_state *" tv ,
+.BI " const void *" p0 ", size_t " sz0 ,
+.BI " const void *" p1 ", size_t " sz1 ,
+.BI " const char *" file ", unsigned " lno ,
+.BI " const char *" expr );
+.ta \w'\fBint TVEC_CLAIMEQ_BYTES('u
+.BI "int TVEC_CLAIMEQ_BYTES(struct tvec_state *" tv ,
+.BI " const void *" p0 ", size_t " sz0 ,
+.BI " const void *" p1 ", size_t " sz1 );
+.PP
+.B "const struct tvec_regty tvty_buffer;"
+.PP
+.ta \w'\fBvoid tvec_initbuffer('
+.BI "void tvec_initbuffer(union tvec_regval *" rv ,
+.BI " const union tvec_regval *" ref ", size_t " sz );
+.BI "void tvec_allocbuffer(union tvec_regval *" rv );
+.fi
/* --- @tvec_initbuffer@ --- *
*
* Arguments: @union tvec_regval *rv@ = register value
- * @const union tvec_regval *src@ = source buffer
+ * @const union tvec_regval *ref@ = source buffer
* @size_t sz@ = size to allocate
*
* Returns: ---
*
- * Use: Initialize the alignment parameters in @rv@ to match @src@,
+ * Use: Initialize the alignment parameters in @rv@ to match @ref@,
* 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; }
+ const union tvec_regval *ref, size_t sz)
+ { rv->buf.sz = sz; rv->buf.a = ref->buf.a; rv->buf.m = ref->buf.m; }
/* --- @tvec_allocbuffer@ --- *
*
.\" -*-nroff-*-
-.TH tvec 3 "10 May 2023" "Straylight/Edgeware" "mLib utilities library"
+.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
+. de VP
+. sp .4v
+..
+\}
+.el \{\
+. ds o o
+. de VP
+. sp
+..
+\}
+..
+.TH tvec 3 "11 March 2024" "Straylight/Edgeware" "mLib utilities library"
.SH NAME
tvec \- test vector framework
-
-
-
-
+.\" @tvec_begin
+.\" @tvec_end
+.\" @tvec_read
+.\" @tvec_humanoutput
+.\" @tvec_tapoutput
+.\" @tvec_dfltoutput
+.
.SH SYNOPSIS
.nf
.B "#include <mLib/tvec.h>"
-
+.PP
+.ta 2n
.B "union tvec_misc {"
-.B
+.B " const void *p;"
+.B " long i;"
+.B " unsigned long u;"
+.B " double f;"
+.B "};"
+.B "enum {"
+.B " TVMISC_PTR,"
+.B " TVMISC_INT,"
+.B " TVMISC_UINT,"
+.B " TVMISC_FLT,"
+.B " ...,"
+.B " TVMISC_LIMIT,"
+.B "};"
+.PP
+.ta 2n +2n
+.B "union tvec_regval {"
+.B " long i;"
+.B " unsigned long u;"
+.B " void *p;"
+.B " double f;"
+.B " struct { char *p; size_t sz; } text;"
+.B " struct { unsigned char *p; size_t sz; } bytes;"
+.B " struct {"
+.B " unsigned char *p; size_t sz;"
+.B " size_t a, m;"
+.B " size_t off;"
+.B " } buf;"
+.B " TVEC_REGSLOTS"
+.B "};"
+.B "struct tvec_reg {"
+.B " unsigned f;"
+.B " union tvec_regval v;"
+.B "};"
+.B "#define TVRF_LIVE ..."
+.PP
+.ta 2n
+.B "struct tvec_regdef {"
+.B " const char *name;"
+.B " const struct tvec_regty *ty;"
+.B " unsigned i;"
+.B " unsigned f;"
+.B " union tvec_misc arg;"
+.B "};"
+.B "#define TVRF_OPT ..."
+.B "#define TVRF_ID ..."
+.B "#define TVEC_ENDREGS ..."
+.PP
+.B "struct tvec_state;"
+.PP
+.B "struct tvec_env;"
+.ta \w'\fBtypedef void tvec_testfn('u
+.BI "typedef void tvec_testfn(const struct tvec_reg *" in ,
+.BI " struct tvec_reg *" out ,
+.BI " void *" ctx );
+.B "struct tvec_test {"
+.B " const char *name;"
+.B " const struct tvec_regdef *regs;"
+.B " const struct tvec_env *env;"
+.B " tvec_testfn *fn;"
+.B "};"
+.B "#define TVEC_ENDTESTS ..."
+.PP
+.ta 2n
+.B "struct tvec_config {"
+.B " const struct tvec_test *tests;"
+.B " unsigned nrout, nreg;"
+.B " size_t regsz;"
+.B "};"
+.B "struct tvec_output;"
+.PP
+.ta \w'\fBvoid tvec_begin('u
+.BI "void tvec_begin(struct tvec_state *" tv_out ,
+.BI " const struct tvec_config *" config ,
+.BI " struct tvec_output *" o );
+.BI "int tvec_end(struct tvec_state *" tv );
+.BI "int tvec_read(struct tvec_state *" tv ", const char *" infile ", FILE *" fp );
+.PP
+.BI "extern struct tvec_output *tvec_humanoutput(FILE *" fp );
+.BI "extern struct tvec_output *tvec_tapoutput(FILE *" fp );
+.BI "extern struct tvec_output *tvec_dfltoutput(FILE *" fp );
+.fi
unsigned long u; /* unsigned integer */
void *p; /* pointer */
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 { unsigned char *p; size_t sz; } bytes; /* binary string of bytes */
struct { /* buffer */
unsigned char *p; size_t sz; /* binary string */
size_t a, m; /* residue and modulus */
*/
const char *name; /* register name (for input files) */
- unsigned i; /* register index */
const struct tvec_regty *ty; /* register type descriptor */
+ unsigned i; /* register index */
unsigned f; /* flags */
#define TVRF_OPT 1u /* optional register */
#define TVRF_ID 2u /* part of test identity */
/*----- Test descriptions -------------------------------------------------*/
+struct tvec_env;
+
typedef void tvec_testfn(const struct tvec_reg */*in*/,
struct tvec_reg */*out*/,
void */*ctx*/);
* anything to do with the test function's context.
*/
-struct tvec_env;
-
typedef int tvec_setvarfn(struct tvec_state */*tv*/, const char */*var*/,
const union tvec_regval */*rv*/, void */*ctx*/);
/* Called after a variable is read. Return zero on success or %$-1$% on
};
#define TVEC_ENDTESTS { 0, 0, 0, 0 }
-enum {
- /* Register output dispositions. */
-
- TVRD_INPUT, /* input-only register */
- TVRD_OUTPUT, /* output-only (input is dead) */
- TVRD_MATCH, /* matching (equal) registers */
- TVRD_FOUND, /* mismatching output register */
- TVRD_EXPECT, /* mismatching input register */
- TVRD_LIMIT /* (number of dispositions) */
-};
-
/*----- Test state --------------------------------------------------------*/
enum {
TVOUT_LIMIT /* (number of possible outcomes) */
};
+struct tvec_config {
+ /* An overall test configuration. */
+
+ const struct tvec_test *tests; /* the tests to be performed */
+ unsigned nrout, nreg; /* number of output/total regs */
+ size_t regsz; /* size of a register */
+};
+
struct tvec_state {
/* The primary state structure for the test vector machinery. */
+ /* Flags. Read-only for all callers. */
unsigned f; /* flags */
#define TVSF_SKIP 0x0001u /* skip this test group */
#define TVSF_OPEN 0x0002u /* test is open */
#define TVSF_XFAIL 0x0100u /* test expected to fail */
#define TVSF_MUFFLE 0x0200u /* muffle errors */
- /* Registers. Available to execution environments. */
- unsigned nrout, nreg; /* number of output/total registers */
- size_t regsz; /* size of register entry */
+ /* Test configuration. Read-only for all callers. */
+ struct tvec_config cfg; /* test configuration */
+
+ /* Registers. Available to execution environments, which may modify the
+ * contents of the active registers, as defined by the current test group,
+ * but not the vector pointers themselves or inactive registers.
+ */
struct tvec_reg *in, *out; /* register vectors */
- /* Test groups state. Available to output formatters. */
- const struct tvec_test *tests, *test; /* all tests and current test */
+ /* Test group state. Read-only for all callers. */
+ const struct tvec_test *test; /* current test */
/* Test scoreboard. Available to output formatters. */
unsigned curr[TVOUT_LIMIT], all[TVOUT_LIMIT], grps[TVOUT_LIMIT];
* @out@, and @i@ is an integer, then this evaluates to the address of the
* @i@th register in the selected vector.
*/
-#define TVEC_REG(tv, vec, i) TVEC_GREG((tv)->vec, (i), (tv)->regsz)
-
-struct tvec_config {
- /* An overall test configuration. */
-
- const struct tvec_test *tests; /* the tests to be performed */
- unsigned nrout, nreg; /* number of output/total regs */
- size_t regsz; /* size of a register */
-};
+#define TVEC_REG(tv, vec, i) TVEC_GREG((tv)->vec, (i), (tv)->cfg.regsz)
/*----- Output formatting -------------------------------------------------*/
const struct tvec_outops *ops; /* pointer to operations */
};
+enum {
+ /* Register output dispositions. */
+
+ TVRD_INPUT, /* input-only register */
+ TVRD_OUTPUT, /* output-only (input is dead) */
+ TVRD_MATCH, /* matching (equal) registers */
+ TVRD_FOUND, /* mismatching output register */
+ TVRD_EXPECT, /* mismatching input register */
+ TVRD_LIMIT /* (number of dispositions) */
+};
+
+#define TVEC_LEVELS(_) \
+ _(NOTE, "notice", 4) \
+ _(ERR, "ERROR", 8)
+enum {
+#define TVEC_DEFLEVEL(tag, name, val) TVLEV_##tag = val,
+ TVEC_LEVELS(TVEC_DEFLEVEL)
+#undef TVEC_DEFLEVEL
+ TVLEV_LIMIT
+};
+
/* Benchmarking details. */
enum {
TVBU_OP, /* counting operations of some kind */
/* Release any resources acquired by the driver. */
};
-#define TVEC_LEVELS(_) \
- _(NOTE, "notice", 4) \
- _(ERR, "ERROR", 8)
-
-enum {
-#define TVEC_DEFLEVEL(tag, name, val) TVLEV_##tag = val,
- TVEC_LEVELS(TVEC_DEFLEVEL)
-#undef TVEC_DEFLEVEL
- TVLEV_LIMIT
-};
-
/*----- Session lifecycle -------------------------------------------------*/
/* --- @tvec_begin@ --- *
/*----- Remote execution --------------------------------------------------*/
+struct tvec_remoteenv;
+
struct tvec_remotecomms {
int infd, outfd; /* input and output descriptors */
dbuf bout; /* output buffer */
struct tvec_timeoutenv {
struct tvec_env _env;
- int timer; /* the timer (@ITIMER_...@) */
+ int timer; /* the timer (@ITIMER_...@) */
double t; /* time to wait (in seconds) */
const struct tvec_env *env; /* subsidiary environment */
};
* message.
*/
-extern const char *tvec_strlevel(unsigned /*level*/);
+extern const char *tvec_strlevel(unsigned /*level*/);
/* --- @tvec_report@, @tvec_report_v@ --- *
*
extern PRINTF_LIKE(2, 3)
void tvec_notice(struct tvec_state */*tv*/, const char */*msg*/, ...);
+/* --- @tvec_unkreg@ --- *
+ *
+ * Arguments: @struct tvec_state *tv@ = test-vector state
+ * @const char *name@ = register or pseudoregister name
+ *
+ * Returns: %$-1$%.
+ *
+ * Use: Reports an error that the register or pseudoregister is
+ * unrecognized.
+ */
+
+extern int tvec_unkreg(struct tvec_state */*tv*/, const char */*name*/);
+
+/* --- @tvec_dupreg@ --- *
+ *
+ * Arguments: @struct tvec_state *tv@ = test-vector state
+ * @const char *name@ = register or pseudoregister name
+ *
+ * Returns: %$-1$%.
+ *
+ * Use: Reports an error that the register or pseudoregister has been
+ * assigned already in the current test.
+ */
+
+extern int tvec_dupreg(struct tvec_state */*tv*/, const char */*name*/);
+
/* --- @tvec_humanoutput@ --- *
*
* Arguments: @FILE *fp@ = output file to write on
extern int tvec_syntax_v(struct tvec_state */*tv*/, int /*ch*/,
const char */*expect*/, va_list */*ap*/);
-/* --- @tvec_unkreg@ --- *
- *
- * Arguments: @struct tvec_state *tv@ = test-vector state
- * @const char *name@ = register or pseudoregister name
- *
- * Returns: %$-1$%.
- *
- * Use: Reports an error that the register or pseudoregister is
- * unrecognized.
- */
-
-extern int tvec_unkreg(struct tvec_state */*tv*/, const char */*name*/);
-
-/* --- @tvec_dupreg@ --- *
- *
- * Arguments: @struct tvec_state *tv@ = test-vector state
- * @const char *name@ = register or pseudoregister name
- *
- * Returns: %$-1$%.
- *
- * Use: Reports an error that the register or pseudoregister has been
- * assigned already in the current test.
- */
-
-extern int tvec_dupreg(struct tvec_state */*tv*/, const char */*name*/);
-
/* --- @tvec_skipspc@ --- *
*
* Arguments: @struct tvec_state *tv@ = test-vector state
double delta; /* maximum tolerable difference */
};
+extern const struct tvec_floatinfo tvflt_finite, tvflt_nonneg;
+
/* --- @tvec_claimeqish_float@, @TVEC_CLAIMEQISH_FLOAT@ --- *
*
* Arguments: @struct tvec_state *tv@ = test-vector state
#define TVEC_CLAIMEQ_FLOAT(tv, f0, f1) \
(tvec_claimeq_float(tv, f0, f1, __FILE__, __LINE__, #f0 " /= " #f1))
-extern const struct tvec_floatinfo tvflt_finite, tvflt_nonneg;
-
/*----- Durations ---------------------------------------------------------*/
/* A duration measures a time interval in seconds. The input format consists
extern const struct tvec_regty tvty_text, tvty_bytes;
+/* --- @tvec_alloctext@, @tvec_allocbytes@ --- *
+ *
+ * Arguments: @union tvec_regval *rv@ = register value
+ * @size_t sz@ = required size
+ *
+ * Returns: ---
+ *
+ * Use: Allocated space in a text or binary string register. If the
+ * current register size is sufficient, its buffer is left
+ * alone; otherwise, the old buffer, if any, is freed and a
+ * fresh buffer allocated. These functions are not intended to
+ * be used to adjust a buffer repeatedly, e.g., while building
+ * output incrementally: (a) they will perform badly, and (b)
+ * the old buffer contents are simply discarded if reallocation
+ * is necessary. Instead, use a @dbuf@ or @dstr@.
+ *
+ * The @tvec_alloctext@ function sneakily allocates an extra
+ * byte for a terminating zero. The @tvec_allocbytes@ function
+ * doesn't do this.
+ */
+
+extern void tvec_alloctext(union tvec_regval */*rv*/, size_t /*sz*/);
+extern void tvec_allocbytes(union tvec_regval */*rv*/, size_t /*sz*/);
+
/* --- @tvec_claimeq_text@, @TVEC_CLAIMEQ_TEXT@ --- *
*
* Arguments: @struct tvec_state *tv@ = test-vector state
(tvec_claimeq(tv, p0, sz0, p1, sz1, __FILE__, __LINE__, \
#p0 "[" #sz0 "] /= " #p1 "[" #sz1 "]"))
-/* --- @tvec_alloctext@, @tvec_allocbytes@ --- *
- *
- * Arguments: @union tvec_regval *rv@ = register value
- * @size_t sz@ = required size
- *
- * Returns: ---
- *
- * Use: Allocated space in a text or binary string register. If the
- * current register size is sufficient, its buffer is left
- * alone; otherwise, the old buffer, if any, is freed and a
- * fresh buffer allocated. These functions are not intended to
- * be used to adjust a buffer repeatedly, e.g., while building
- * output incrementally: (a) they will perform badly, and (b)
- * the old buffer contents are simply discarded if reallocation
- * is necessary. Instead, use a @dbuf@ or @dstr@.
- *
- * The @tvec_alloctext@ function sneakily allocates an extra
- * byte for a terminating zero. The @tvec_allocbytes@ function
- * doesn't do this.
- */
-
-extern void tvec_alloctext(union tvec_regval */*rv*/, size_t /*sz*/);
-extern void tvec_allocbytes(union tvec_regval */*rv*/, size_t /*sz*/);
-
/*----- Buffer type -------------------------------------------------------*/
/* Buffer registers are primarily used for benchmarking. Only a buffer's
/* --- @tvec_initbuffer@ --- *
*
* Arguments: @union tvec_regval *rv@ = register value
- * @const union tvec_regval *src@ = source buffer
+ * @const union tvec_regval *ref@ = reference buffer
* @size_t sz@ = size to allocate
*
* Returns: ---
*
- * Use: Initialize the alignment parameters in @rv@ to match @src@,
+ * Use: Initialize the alignment parameters in @rv@ to match @ref@,
* and the size to @sz@.
*/
extern void tvec_initbuffer(union tvec_regval */*rv*/,
- const union tvec_regval */*src*/, size_t /*sz*/);
+ const union tvec_regval */*ref*/, size_t /*sz*/);
/* --- @tvec_allocbuffer@ --- *
*
.SH "SYNOPSIS"
.nf
.B "#include <mLib/trace.h>"
-
+.PP
.BI "void trace(unsigned " l ", const char *" f ", ...);"
.ta \w'\fBvoid trace_block('u
.BI "void trace_block(unsigned " l ", const char *" s ,
.BI " const void *" b ", size_t " sz );
-
+.PP
.BI "void trace_on(FILE *" fp ", unsigned " l );
.ta \w'\fBvoid trace_custom('u +\w'\fBvoid (*\,\fIfunc\/\fB)('u
.BI "void trace_custom(void (*" func ")(const char *" buf ,
.BI " void *" v );
.BI "void trace_level(unsigned " l );
.BI "unsigned tracing(void);"
-
+.PP
.ta \w'\fBunsigned traceopt('u
.BI "unsigned traceopt(const trace_opt *" t ", const char *" p ,
.BI " unsigned " f ", unsigned " bad );
-
+.PP
.BI T( statements\fR... )
.BI "IF_TRACING(unsigned " l ", " statements\fR... )
.fi
.SH "SYNOPSIS"
.nf
.B "#include <mLib/mdwopt.h>"
-
+.PP
.ta 2n
.B "typedef struct {"
.B " char *arg, *prog;"
.B " int opt, ind, err;"
.B " ..."
.B "} mdwopt_data;"
-
+.PP
.B "char *optarg, optprog;"
.B "int optopt, opterr, optind;"
-
+.PP
.B "struct option {"
.B " const char *name;"
.B " int has_arg;"
.B " int *flag;"
.B " int val;"
.B "};"
-
+.PP
.B "#define OPTF_NOARG = ..."
.B "#define OPTF_ARGREQ = ..."
.B "#define OPTF_ARGOPT = ..."
.B "#define OPTF_ARG = ..."
.B "#define OPTF_SWITCH = ..."
.B "#define OPTF_NEGATE = ..."
-
+.PP
.B "#define OPTF_NOLONGS = ..."
.B "#define OPTF_NOSHORTS = ..."
.B "#define OPTF_NUMBERS = ..."
.B "#define OPTF_ENVVAR = ..."
.B "#define OPTF_NOPROGNAME = ..."
.B "#define OPTF_NEGNUMBER = ..."
-
+.PP
.B "#define OPTF_NEGATED = ..."
-
+.PP
.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 );
-
+.PP
.BI "int getopt(int " argc ", char *const *" argv ", const char *" o );
-
+.PP
.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 );
-
+.PP
.ta \w'\fBint getopt_long_only('u
.BI "int getopt_long_only(int " argc ", char *const *" argv ,
.BI " const char * "shortopt ,
.SH SYNOPSIS
.nf
.B "#include <mLib/quis.h>"
-
+.PP
.BI "void ego(const char *" p );
.B "const char *quis(void);"
.B "const char *QUIS;"
.SH SYNOPSIS
.nf
.B "#include <mLib/report.h>"
-
+.PP
.BI "void moan(const char *" f ", ...);"
.BI "void die(int " status ", const char *" f ", ...);"
.fi
.SH SYNOPSIS
.nf
.B "#include <mLib/align.h>"
-
+.PP
.BI "size_t ALIGN(size_t " sz ");"
.fi
.SH DESCRIPTION
.SH SYNOPSIS
.nf
.B "#include <mLib/bits.h>"
-
+.PP
.BR "typedef " ... " octet;"
.BR "typedef " ... " uint16;"
.BR "typedef " ... " uint24;"
.BR "typedef " ... " uint32;"
.BR "typedef " ... " uint64;"
.BR "typedef " ... " kludge64;"
-
+.PP
.BI "#define TY_" we " " type
.BI "#define SZ_" we " \fR..."
.BI "#define MASK_" we " \fR..."
-
+.PP
.BI "#define DOUINTSZ(" f ") \fR..."
.BI "#define DOUINTCONV(" f ") \fR..."
-
+.PP
.IB type " U" w ( v );
-
+.PP
.IB type " LSL" w ( type " " v ", int " s );
.IB type " LSR" w ( type " " v ", int " s );
.IB type " ROL" w ( type " " v ", int " s );
.IB type " ROR" w ( type " " v ", int " s );
-
+.PP
.BI "octet GETBYTE(void *" p ", size_t " o );
.BI "void PUTBYTE(void *" p ", size_t " o ", octet " v );
-
+.PP
.IB type " LOAD" we "(void *" p );
.BI "void STORE" we "(void *" p ", " type " " v );
-
+.PP
.BI "void SET64(kludge64 &" d ", uint32 " h ", uint32 " l );
.BI "kludge64 X64(" hexh ", " hexl );
.BI "void ASSIGN64(kludge64 &" d ", " x );
.BR mLib (3).
.SH AUTHOR
Mark Wooding, <mdw@distorted.org.uk>
-
.SH SYNOPSIS
.nf
.B "#include <mLib/compiler.h>"
-
+.PP
.BI "int GCC_VERSION_P(" maj ", " min ");"
.BI "int CLANG_VERSION_P(" maj ", " min ");"
.fi
.SH SYNOPSIS
.nf
.B "#include <mLib/control.h>"
-
+.PP
.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_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]
-
+.PP
.BI MC_TARGET( tag ", " stmt ") " body
.BI MC_GOTARGET( tag );
.BI MC_ALLOWELSE( tag ") " main_body " \fR[\fBelse " else_body \fR]
.BI MC_GOELSE( tag );
-
+.PP
.BI MC_ACT( stmt )
.BI MC_LABEL( tag )
.BI MC_GOTO( tag )
.nf
.B "typedef void (*exc__uncaught)(exc_extype, exc_exval);"
.BI "exc__uncaught exc_uncaught(exc__uncaught " proc );
-
+.PP
.BI "exc_extype EXC_PAIR(unsigned char " x ", unsigned char " y );
.BI "exc_extype EXC_ALLOC(exc_extype " owner ", exc_extype " type );
.BI "exc_extype EXC_ALLOCN(exc_extype " owner ", exc_extype " type );
\h'-\w'\\$1\ 'u'\\$1\ \c
.ft P
..
-.ie t .ds o \(bu
-.el .ds o o
+.ie t \{\
+. ds o \(bu
+. de VP
+. sp .4v
+..
+\}
+.el \{\
+. ds o o
+. de VP
+. sp
+..
+\}
.
.TH gprintf 3 "9 March 2024" "Straylight/Edgeware" "mLib utilities library"
.
.SH SYNOPSIS
.nf
.B "#include <mLib/gprintf.h>"
-
+.PP
.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 "};"
-
+.PP
.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 ");"
-
+.PP
.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 ");"
-
+.PP
.B "const struct gprintf_ops file_printops;"
.fi
.
size_t sz;
/* ...\& other members ...\& */
};
-
+.VP
/* ...\& define putch and putm ...\& */
-
+.VP
static int nputf(void *out, size_t maxsz, const char *p, ...)
{
struct my_output *myout = out;
va_list ap;
int n;
-
+.VP
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);
}
-
+.VP
const struct gprintf_ops my_output_ops = { putch, putm, nputf };
-
+.VP
/* ...\& */
-
+.VP
struct my_output myout;
-
+.VP
myout.buf = 0; myout.sz = 0;
/* ...\& other initialization ...\& */
gprintf(&my_output_ops, &myout, "Hello, %s!", "world");
.SH SYNOPSIS
.nf
.B "#include <mLib/linreg.h>"
-
+.PP
.B "struct linreg { ...\& };"
.B "#define LINREG_INIT ..."
-
+.PP
.BI "void linreg_init(struct linreg *" lr );
.BI "void linreg_update(struct linreg *" lr ", double " x ", double " y );
.ta \w'void linreg_fit('u
.SH SYNOPSIS
.nf
.B "#include <mLib/macros.h>"
-
+.PP
.BI "size_t N(" array ");"
.BI "STR(" tokens\fR... ")"
.BI "GLUE(" tokens\fR... ", " tokens\fR... ")"
.BI "STATIC_ASSERT(" cond ", " msg ");"
-
+.PP
.BI "ISALNUM(int " ch ");"
.BI "ISALPHA(int " ch ");"
.BI "ISASCII(int " ch ");"
.BI "TOASCII(int " ch ");"
.BI "TOLOWER(int " ch ");"
.BI "TOUPPER(int " ch ");"
-
+.PP
.BI "MEMCMP(const void *" x ", " op ", const void *" y ", size_t " n ");"
.BI "STRCMP(const char *" x ", " op ", const char *" y ");"
.BI "STRNCMP(const char *" x ", " op ", const char *" y ", size_t " n ");"
-
+.PP
.BI "void DISCARD(" scalar ");"
.BI "void IGNORE(" variable ");"
-
+.PP
.BI "DEPRECATED(" msg ")"
.BI "EXECL_LIKE(" ntrail ")"
.BI "IGNORABLE"
.BI "NORETURN"
.BI "PRINTF_LIKE(" fmt-index ", " arg-index ")"
.BI "SCANF_LIKE(" fmt-index ", " arg-index ")"
-
+.PP
.BI "MUFFLE_WARNINGS_DECL(" warns ", " decls ")"
.BI "MUFFLE_WARNINGS_EXPR(" warns ", " expr ")"
.BI "MUFFLE_WARNINGS_STMT(" warns ", " stmt ")"
-
+.PP
.BI "GCC_WARNING(" option ")"
.BI "CLANG_WARNING(" option ")"
.fi
.SH SYNOPSIS
.nf
.B "#include <mLib/maths.h>"
-
+.PP
.BI "int NANP(" floatish " " x );
.BI "int INFP(" floatish " " x );
.BI "int NEGP(" floatish " " x );
.sp 1
.fi
..
+.ie t \{\
+. de VP
+. sp .4v
+..
+\}
+.el \{\
+. de VP
+. sp
+..
+\}
.TH str 3 "20 June 1999" "Straylight/Edgeware" "mLib utilities library"
.SH NAME
str \- small string utilities
.SH SYNOPSIS
.nf
.B "#include <mLib/str.h>"
-
+.PP
.BI "char *str_qword(char **" pp ", unsigned " f );
.BI "size_t str_qsplit(char *" p ", char *" v "[], size_t " c ,
.BI " char **" rest ", unsigned " f );
char *v[3];
size_t n;
char *q;
-
+.VP
n = str_split(p, v, 3, &q);
.VE
following the call to
static const struct tvec_urange ur_eight = { 8, 8 };
static const struct tvec_urange ur_shift = { 0, 63 };
static const struct tvec_regdef shift_regs[] = {
- { "x", RX, &tvty_bytes, 0, { &ur_eight } },
- { "n", RN, &tvty_uint, 0, { &ur_shift } },
- { "z", RZ, &tvty_bytes, 0, { &ur_eight } },
+ { "x", &tvty_bytes, RX, 0, { &ur_eight } },
+ { "n", &tvty_uint, RN, 0, { &ur_shift } },
+ { "z", &tvty_bytes, RZ, 0, { &ur_eight } },
TVEC_ENDREGS
};
static const struct tvec_regdef arith_regs[] = {
- { "x", RX, &tvty_bytes, 0, { &ur_eight } },
- { "y", RY, &tvty_bytes, 0, { &ur_eight } },
- { "z", RZ, &tvty_bytes, 0, { &ur_eight } },
+ { "x", &tvty_bytes, RX, 0, { &ur_eight } },
+ { "y", &tvty_bytes, RY, 0, { &ur_eight } },
+ { "z", &tvty_bytes, RZ, 0, { &ur_eight } },
TVEC_ENDREGS
};
static const struct tvec_test tests[] = {
- { "lsl64", shift_regs, 0, test_LSL },
- { "lsr64", shift_regs, 0, test_LSR },
- { "rol64", shift_regs, 0, test_ROL },
- { "ror64", shift_regs, 0, test_ROR },
- { "add64", arith_regs, 0, test_ADD },
- { "sub64", arith_regs, 0, test_SUB },
+ { "lsl64", shift_regs, 0, test_LSL },
+ { "lsr64", shift_regs, 0, test_LSR },
+ { "rol64", shift_regs, 0, test_ROL },
+ { "ror64", shift_regs, 0, test_ROR },
+ { "add64", arith_regs, 0, test_ADD },
+ { "sub64", arith_regs, 0, test_SUB },
TVEC_ENDTESTS
};
static const struct tvec_env swap_testenv = { 0, 0, 0, 0, swap_test, 0, 0 };
static const struct tvec_regdef versioncmp_regs[] = {
- { "v0", RV0, &tvty_text, 0 },
- { "v1", RV1, &tvty_text, 0 },
- { "rc", RRC, &tvty_ienum, 0, { &tvenum_cmp } },
+ { "v0", &tvty_text, RV0, 0 },
+ { "v1", &tvty_text, RV1, 0 },
+ { "rc", &tvty_ienum, RRC, 0, { &tvenum_cmp } },
TVEC_ENDREGS
};
.SH SYNOPSIS
.nf
.B "#include <mLib/versioncmp.h>"
-
+.PP
.BI "int versioncmp(const char *" va ", const char *" vb ");"
.BI "int VERSIONCMP(const char *" va ", " op ", const char *" vb ");"
.fi