From 0875b58fcccadd756e11487185c2ac1d3ed8ab4d Mon Sep 17 00:00:00 2001 Message-Id: <0875b58fcccadd756e11487185c2ac1d3ed8ab4d.1714004063.git.mdw@distorted.org.uk> From: Mark Wooding Date: Wed, 17 Jun 1998 23:44:41 +0000 Subject: [PATCH] Initial revision Organization: Straylight/Edgeware From: mdw --- .links | 6 + .skelrc | 8 + Makefile.am | 57 +++++ alloc.c | 110 +++++++++ alloc.h | 93 ++++++++ configure.in | 55 +++++ crc32.c | 176 +++++++++++++++ crc32.h | 93 ++++++++ dstr.c | 308 +++++++++++++++++++++++++ dstr.h | 256 +++++++++++++++++++++ dynarray.h | 279 +++++++++++++++++++++++ exc.c | 145 ++++++++++++ exc.h | 341 ++++++++++++++++++++++++++++ exctest.c | 41 ++++ mdwopt.c | 0 mdwopt.h | 0 quis.c | 92 ++++++++ quis.h | 79 +++++++ report.c | 92 ++++++++ report.h | 78 +++++++ sub.c | 260 +++++++++++++++++++++ sub.h | 127 +++++++++++ sym.c | 624 +++++++++++++++++++++++++++++++++++++++++++++++++++ sym.h | 197 ++++++++++++++++ testrig.c | 401 +++++++++++++++++++++++++++++++++ testrig.h | 102 +++++++++ trace.c | 178 +++++++++++++++ trace.h | 126 +++++++++++ track.c | 288 ++++++++++++++++++++++++ track.h | 217 ++++++++++++++++++ 30 files changed, 4829 insertions(+) create mode 100644 .links create mode 100644 .skelrc create mode 100644 Makefile.am create mode 100644 alloc.c create mode 100644 alloc.h create mode 100644 configure.in create mode 100644 crc32.c create mode 100644 crc32.h create mode 100644 dstr.c create mode 100644 dstr.h create mode 100644 dynarray.h create mode 100644 exc.c create mode 100644 exc.h create mode 100644 exctest.c create mode 100644 mdwopt.c create mode 100644 mdwopt.h create mode 100644 quis.c create mode 100644 quis.h create mode 100644 report.c create mode 100644 report.h create mode 100644 sub.c create mode 100644 sub.h create mode 100644 sym.c create mode 100644 sym.h create mode 100644 testrig.c create mode 100644 testrig.h create mode 100644 trace.c create mode 100644 trace.h create mode 100644 track.c create mode 100644 track.h diff --git a/.links b/.links new file mode 100644 index 0000000..e866fdd --- /dev/null +++ b/.links @@ -0,0 +1,6 @@ +COPYING +config.guess +config.sub +install-sh +missing +mkinstalldirs diff --git a/.skelrc b/.skelrc new file mode 100644 index 0000000..6d88ea1 --- /dev/null +++ b/.skelrc @@ -0,0 +1,8 @@ +;;; -*-emacs-lisp-*- + +(setq skel-alist + (append + '((author . "Straylight/Edgeware") + (full-title . "the mLib utilities library") + (program . "mLib")) + skel-alist)) diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..8997cf1 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,57 @@ +## Process this file with Automake to generate `Makefile.in' +## -*-Makefile-*- +## +## $Id: Makefile.am,v 1.1 1998/06/17 23:44:42 mdw Exp $ +## +## Building the distribution +## +## (c) 1998 Straylight/Edgeware +## + +##----- Licensing notice ---------------------------------------------------- +## +## This file is part of the mLib utilities library. +## +## mLib is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation; either version 2 of the License, or +## (at your option) any later version. +## +## mLib is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with mLib; if not, write to the Free Software Foundation, +## Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +##----- Revision history ---------------------------------------------------- +## +## $Log: Makefile.am,v $ +## Revision 1.1 1998/06/17 23:44:42 mdw +## Initial revision +## +## Revision 1.5 1997/08/13 17:55:43 mdw +## Add big GPL header. General tidying up. +## + +## --- Options --- + +AUTOMAKE_OPTIONS = foreign + +## --- What to build --- + +lib_LIBRARIES = libmLib.a + +include_HEADERS = \ + alloc.h crc32.h dstr.h dynarray.h exc.h mdwopt.h \ + quis.h report.h sub.h sym.h testrig.h trace.h track.h + +## --- Things to put in the library --- + +## libmLib_la_LDFLAGS = -version-info 1:0 + +libmLib_a_SOURCES = \ + alloc.c crc32.c dstr.c exc.c mdwopt.c quis.c \ + report.c sub.c sym.c testrig.h trace.c track.c diff --git a/alloc.c b/alloc.c new file mode 100644 index 0000000..b42718a --- /dev/null +++ b/alloc.c @@ -0,0 +1,110 @@ +/* -*-c-*- + * + * $Id: alloc.c,v 1.1 1998/06/17 23:44:42 mdw Exp $ + * + * Memory allocation functions + * + * (c) 1998 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of the mLib utilities library. + * + * mLib is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * mLib is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with mLib; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: alloc.c,v $ + * Revision 1.1 1998/06/17 23:44:42 mdw + * Initial revision + * + */ + +/*----- Header files ------------------------------------------------------*/ + +/* --- ANSI headers --- */ + +#include +#include +#include + +/* --- Local headers --- */ + +#include "alloc.h" +#include "exc.h" +#include "track.h" + +/*----- Functions provided ------------------------------------------------*/ + +/* --- @xmalloc@ --- * + * + * Arguments: @size_t sz@ = size of block to allocate + * + * Returns: Pointer to allocated block. + * + * Use: Allocates memory. If there's not enough memory, the + * exception @EXC_NOMEM@ is thrown. + */ + +void *xmalloc(size_t sz) +{ + void *p = malloc(sz); + if (!p) + THROW(EXC_NOMEM); + return (p); +} + +/* --- @xstrdup@ --- * + * + * Arguments: @const char *s@ = pointer to a string + * + * Returns: Pointer to a copy of the string. + * + * Use: Copies a string (like @strdup@ would, if it existed). If + * there's not enough memory, the exception @EXC_NOMEM@ is + * thrown. + */ + +char *xstrdup(const char *s) +{ + size_t sz = strlen(s) + 1; + char *p = xmalloc(sz); + memcpy(p, s, sz); + return (p); +} + +/* --- @xrealloc@ --- * + * + * Arguments: @void *p@ = pointer to a block of memory + * @size_t sz@ = new size desired for the block + * + * Returns: Pointer to the resized memory block (which is almost + * certainly not in the same place any more). + * + * Use: Resizes a memory block. If there's not enough memory, the + * exception @EXC_NOMEM@ is thrown. + */ + +void *xrealloc(void *p, size_t sz) +{ + p = realloc(p, sz); + if (!p) + THROW(EXC_NOMEM); + return (p); +} + +/*----- That's all, folks -------------------------------------------------*/ diff --git a/alloc.h b/alloc.h new file mode 100644 index 0000000..6d9d58a --- /dev/null +++ b/alloc.h @@ -0,0 +1,93 @@ +/* -*-c-*- + * + * $Id: alloc.h,v 1.1 1998/06/17 23:44:42 mdw Exp $ + * + * Memory allocation functions + * + * (c) 1998 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of the mLib utilities library. + * + * mLib is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * mLib is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with mLib; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: alloc.h,v $ + * Revision 1.1 1998/06/17 23:44:42 mdw + * Initial revision + * + */ + +#ifndef ALLOC_H +#define ALLOC_H + +#ifdef __cplusplus + extern "C" { +#endif + +#include + +/*----- Functions provided ------------------------------------------------*/ + +/* --- @xmalloc@ --- * + * + * Arguments: @size_t sz@ = size of block to allocate + * + * Returns: Pointer to allocated block. + * + * Use: Allocates memory. If there's not enough memory, the + * exception @EXC_NOMEM@ is thrown. + */ + +extern void *xmalloc(size_t /*sz*/); + +/* --- @xstrdup@ --- * + * + * Arguments: @const char *s@ = pointer to a string + * + * Returns: Pointer to a copy of the string. + * + * Use: Copies a string (like @strdup@ would, if it existed). If + * there's not enough memory, the exception @EXC_NOMEM@ is + * thrown. + */ + +extern char *xstrdup(const char */*s*/); + +/* --- @xrealloc@ --- * + * + * Arguments: @void *p@ = pointer to a block of memory + * @size_t sz@ = new size desired for the block + * + * Returns: Pointer to the resized memory block (which is almost + * certainly not in the same place any more). + * + * Use: Resizes a memory block. If there's not enough memory, the + * exception @EXC_NOMEM@ is thrown. + */ + +extern void *xrealloc(void */*p*/, size_t /*sz*/); + +/*----- That's all, folks -------------------------------------------------*/ + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/configure.in b/configure.in new file mode 100644 index 0000000..3701e74 --- /dev/null +++ b/configure.in @@ -0,0 +1,55 @@ +dnl -*-fundamental-*- +dnl +dnl $Id: configure.in,v 1.1 1998/06/17 23:44:42 mdw Exp $ +dnl +dnl Configuration script for mLib +dnl +dnl (c) 1998 Straylight/Edgeware +dnl + +dnl ----- Licensing notice -------------------------------------------------- +dnl +dnl This file is part of the mLib utilities library. +dnl +dnl mLib is free software; you can redistribute it and/or modify +dnl it under the terms of the GNU General Public License as published by +dnl the Free Software Foundation; either version 2 of the License, or +dnl (at your option) any later version. +dnl +dnl mLib is distributed in the hope that it will be useful, +dnl but WITHOUT ANY WARRANTY; without even the implied warranty of +dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +dnl GNU General Public License for more details. +dnl +dnl You should have received a copy of the GNU General Public License +dnl along with mLib; if not, write to the Free Software Foundation, +dnl Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +dnl ----- Revision history -------------------------------------------------- +dnl +dnl $Log: configure.in,v $ +dnl Revision 1.1 1998/06/17 23:44:42 mdw +dnl Initial revision +dnl + +dnl --- Boring boilerplate --- + +AC_INIT(exc.c) +AM_INIT_AUTOMAKE(mLib, 1.0) + +dnl --- Compiling and making libraries --- + +AC_PROG_CC +AC_PROG_INSTALL +AC_CHECK_PROG(AR, ar, ar) +mdw_GCC_FLAGS +AC_PROG_RANLIB +dnl AM_PROG_LIBTOOL + +dnl --- Useful options --- + +mdw_OPT_mLib_DEBUG(mLib) + +dnl --- Done --- + +AC_OUTPUT(Makefile) diff --git a/crc32.c b/crc32.c new file mode 100644 index 0000000..54e3f41 --- /dev/null +++ b/crc32.c @@ -0,0 +1,176 @@ +/* -*-c-*- + * + * $Id: crc32.c,v 1.1 1998/06/17 23:44:42 mdw Exp $ + * + * Calculating cyclic redundancy values (non-cryptographic!) + * + * (c) 1998 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of the mLib utilities library. + * + * mLib is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * mLib is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with mLib; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: crc32.c,v $ + * Revision 1.1 1998/06/17 23:44:42 mdw + * Initial revision + * + */ + +/*----- Header files ------------------------------------------------------*/ + +/* --- ANSI headers --- */ + +#include +#include +#include + +/* --- Local headers --- */ + +#include "crc32.h" + +/*----- CRC table ---------------------------------------------------------*/ + +/* --- Acknowledgements and information --- * + * + * This table was generated using a derivative of the Rocksoft Model CRC + * generator. The parameters given are: + * + * width = 32bits + * poly = 0x04C11DB7 + * init = -1 + * xorout = -1 + * reverse = yes + * check = crc(123456789) = 0xCBF43926 + */ + +unsigned long crc32_table[256] = { + 0x00000000L, 0x77073096L, 0xEE0E612CL, 0x990951BAL, + 0x076DC419L, 0x706AF48FL, 0xE963A535L, 0x9E6495A3L, + 0x0EDB8832L, 0x79DCB8A4L, 0xE0D5E91EL, 0x97D2D988L, + 0x09B64C2BL, 0x7EB17CBDL, 0xE7B82D07L, 0x90BF1D91L, + 0x1DB71064L, 0x6AB020F2L, 0xF3B97148L, 0x84BE41DEL, + 0x1ADAD47DL, 0x6DDDE4EBL, 0xF4D4B551L, 0x83D385C7L, + 0x136C9856L, 0x646BA8C0L, 0xFD62F97AL, 0x8A65C9ECL, + 0x14015C4FL, 0x63066CD9L, 0xFA0F3D63L, 0x8D080DF5L, + 0x3B6E20C8L, 0x4C69105EL, 0xD56041E4L, 0xA2677172L, + 0x3C03E4D1L, 0x4B04D447L, 0xD20D85FDL, 0xA50AB56BL, + 0x35B5A8FAL, 0x42B2986CL, 0xDBBBC9D6L, 0xACBCF940L, + 0x32D86CE3L, 0x45DF5C75L, 0xDCD60DCFL, 0xABD13D59L, + 0x26D930ACL, 0x51DE003AL, 0xC8D75180L, 0xBFD06116L, + 0x21B4F4B5L, 0x56B3C423L, 0xCFBA9599L, 0xB8BDA50FL, + 0x2802B89EL, 0x5F058808L, 0xC60CD9B2L, 0xB10BE924L, + 0x2F6F7C87L, 0x58684C11L, 0xC1611DABL, 0xB6662D3DL, + 0x76DC4190L, 0x01DB7106L, 0x98D220BCL, 0xEFD5102AL, + 0x71B18589L, 0x06B6B51FL, 0x9FBFE4A5L, 0xE8B8D433L, + 0x7807C9A2L, 0x0F00F934L, 0x9609A88EL, 0xE10E9818L, + 0x7F6A0DBBL, 0x086D3D2DL, 0x91646C97L, 0xE6635C01L, + 0x6B6B51F4L, 0x1C6C6162L, 0x856530D8L, 0xF262004EL, + 0x6C0695EDL, 0x1B01A57BL, 0x8208F4C1L, 0xF50FC457L, + 0x65B0D9C6L, 0x12B7E950L, 0x8BBEB8EAL, 0xFCB9887CL, + 0x62DD1DDFL, 0x15DA2D49L, 0x8CD37CF3L, 0xFBD44C65L, + 0x4DB26158L, 0x3AB551CEL, 0xA3BC0074L, 0xD4BB30E2L, + 0x4ADFA541L, 0x3DD895D7L, 0xA4D1C46DL, 0xD3D6F4FBL, + 0x4369E96AL, 0x346ED9FCL, 0xAD678846L, 0xDA60B8D0L, + 0x44042D73L, 0x33031DE5L, 0xAA0A4C5FL, 0xDD0D7CC9L, + 0x5005713CL, 0x270241AAL, 0xBE0B1010L, 0xC90C2086L, + 0x5768B525L, 0x206F85B3L, 0xB966D409L, 0xCE61E49FL, + 0x5EDEF90EL, 0x29D9C998L, 0xB0D09822L, 0xC7D7A8B4L, + 0x59B33D17L, 0x2EB40D81L, 0xB7BD5C3BL, 0xC0BA6CADL, + 0xEDB88320L, 0x9ABFB3B6L, 0x03B6E20CL, 0x74B1D29AL, + 0xEAD54739L, 0x9DD277AFL, 0x04DB2615L, 0x73DC1683L, + 0xE3630B12L, 0x94643B84L, 0x0D6D6A3EL, 0x7A6A5AA8L, + 0xE40ECF0BL, 0x9309FF9DL, 0x0A00AE27L, 0x7D079EB1L, + 0xF00F9344L, 0x8708A3D2L, 0x1E01F268L, 0x6906C2FEL, + 0xF762575DL, 0x806567CBL, 0x196C3671L, 0x6E6B06E7L, + 0xFED41B76L, 0x89D32BE0L, 0x10DA7A5AL, 0x67DD4ACCL, + 0xF9B9DF6FL, 0x8EBEEFF9L, 0x17B7BE43L, 0x60B08ED5L, + 0xD6D6A3E8L, 0xA1D1937EL, 0x38D8C2C4L, 0x4FDFF252L, + 0xD1BB67F1L, 0xA6BC5767L, 0x3FB506DDL, 0x48B2364BL, + 0xD80D2BDAL, 0xAF0A1B4CL, 0x36034AF6L, 0x41047A60L, + 0xDF60EFC3L, 0xA867DF55L, 0x316E8EEFL, 0x4669BE79L, + 0xCB61B38CL, 0xBC66831AL, 0x256FD2A0L, 0x5268E236L, + 0xCC0C7795L, 0xBB0B4703L, 0x220216B9L, 0x5505262FL, + 0xC5BA3BBEL, 0xB2BD0B28L, 0x2BB45A92L, 0x5CB36A04L, + 0xC2D7FFA7L, 0xB5D0CF31L, 0x2CD99E8BL, 0x5BDEAE1DL, + 0x9B64C2B0L, 0xEC63F226L, 0x756AA39CL, 0x026D930AL, + 0x9C0906A9L, 0xEB0E363FL, 0x72076785L, 0x05005713L, + 0x95BF4A82L, 0xE2B87A14L, 0x7BB12BAEL, 0x0CB61B38L, + 0x92D28E9BL, 0xE5D5BE0DL, 0x7CDCEFB7L, 0x0BDBDF21L, + 0x86D3D2D4L, 0xF1D4E242L, 0x68DDB3F8L, 0x1FDA836EL, + 0x81BE16CDL, 0xF6B9265BL, 0x6FB077E1L, 0x18B74777L, + 0x88085AE6L, 0xFF0F6A70L, 0x66063BCAL, 0x11010B5CL, + 0x8F659EFFL, 0xF862AE69L, 0x616BFFD3L, 0x166CCF45L, + 0xA00AE278L, 0xD70DD2EEL, 0x4E048354L, 0x3903B3C2L, + 0xA7672661L, 0xD06016F7L, 0x4969474DL, 0x3E6E77DBL, + 0xAED16A4AL, 0xD9D65ADCL, 0x40DF0B66L, 0x37D83BF0L, + 0xA9BCAE53L, 0xDEBB9EC5L, 0x47B2CF7FL, 0x30B5FFE9L, + 0xBDBDF21CL, 0xCABAC28AL, 0x53B39330L, 0x24B4A3A6L, + 0xBAD03605L, 0xCDD70693L, 0x54DE5729L, 0x23D967BFL, + 0xB3667A2EL, 0xC4614AB8L, 0x5D681B02L, 0x2A6F2B94L, + 0xB40BBE37L, 0xC30C8EA1L, 0x5A05DF1BL, 0x2D02EF8DL, +}; + +/*----- Functionc provided ------------------------------------------------*/ + +/* --- @crc32@ --- * + * + * Arguments: @unsigned long crc@ = carryover from previous call, or zero + * @const void *buf@ = pointer to buffer to check + * @size_t sz@ = size of the buffer + * + * Returns: The CRC updated by the new buffer. + * + * Use: A restartable CRC calculator. This is just a function + * wrapper for the macro version. + */ + +unsigned long crc32(unsigned long crc, const void *buf, size_t sz) +{ + unsigned long c; + CRC32(c, crc, buf, sz); + return (c); +} + +/*----- Test driver -------------------------------------------------------*/ + +#ifdef TEST_RIG + +#include + +int main(void) +{ + unsigned long crc = 0; + char buf[BUFSIZ]; + int r; + + do { + r = fread(buf, 1, sizeof(buf), stdin); + if (r > 0) + crc = crc32(crc, buf, r); + } while (r == sizeof(buf)); + + printf("crc32(stdin) = %08lx\n", crc); + return (0); +} + +#endif + +/*----- That's all, folks -------------------------------------------------*/ diff --git a/crc32.h b/crc32.h new file mode 100644 index 0000000..5d4b2fa --- /dev/null +++ b/crc32.h @@ -0,0 +1,93 @@ +/* -*-c-*- + * + * $Id: crc32.h,v 1.1 1998/06/17 23:44:42 mdw Exp $ + * + * Calculating cyclic redundancy values (non-cryptographic!) + * + * (c) 1998 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of the mLib utilities library. + * + * mLib is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * mLib is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with mLib; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: crc32.h,v $ + * Revision 1.1 1998/06/17 23:44:42 mdw + * Initial revision + * + */ + +#ifndef CRC32_H +#define CRC32_h + +#ifdef __cplusplus + extern "C" { +#endif + +/*----- External values ---------------------------------------------------*/ + +extern unsigned long crc32_table[256]; + +/*----- Macros ------------------------------------------------------------*/ + +/* --- @CRC32@ --- * + * + * Arguments: @unsigned long result@ = where to put the result + * @unsigned long crc@ = carryover from previous call, or zero + * @void *buf@ = pointer to buffer to check + * @size_t sz@ = size of the buffer + * + * Use: A restartable CRC calculator wrapped up in a macro. + */ + +#define CRC32(result, crc, buf, sz) do { \ + const unsigned char *_p = (const unsigned char *)(buf); \ + const unsigned char *_l = _p + (sz); \ + unsigned long _crc = ~(crc) & 0xffffffffu; \ + \ + while (_p < _l) \ + _crc = (_crc >> 8) ^ crc32_table[(*_p++ ^ _crc) & 0xffu]; \ + (result) = ~_crc & 0xffffffffu; \ +} while (0) + +/*----- Functions provided ------------------------------------------------*/ + +/* --- @crc32@ --- * + * + * Arguments: @unsigned long crc@ = carryover from previous call, or zero + * @const void *buf@ = pointer to buffer to check + * @size_t sz@ = size of the buffer + * + * Returns: The CRC updated by the new buffer. + * + * Use: A restartable CRC calculator. This is just a function + * wrapper for the macro version. + */ + +extern unsigned long crc32(unsigned long /*crc*/, + const void */*buf*/, size_t /*sz*/); + +/*----- That's all, folks -------------------------------------------------*/ + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/dstr.c b/dstr.c new file mode 100644 index 0000000..7299bf6 --- /dev/null +++ b/dstr.c @@ -0,0 +1,308 @@ +/* -*-c-*- + * + * $Id: dstr.c,v 1.1 1998/06/17 23:44:42 mdw Exp $ + * + * Handle dynamically growing strings + * + * (c) 1998 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of the mLib utilities library. + * + * mLib is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * mLib is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with mLib; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: dstr.c,v $ + * Revision 1.1 1998/06/17 23:44:42 mdw + * Initial revision + * + */ + +/*----- Header files ------------------------------------------------------*/ + +#include +#include +#include + +#include "alloc.h" +#include "dstr.h" + +/*----- Tunable constants -------------------------------------------------*/ + +#define DSTR_INITSZ 256 /* Initial buffer size */ + +/*----- Main code ---------------------------------------------------------*/ + +/* --- @dstr_create@ --- * + * + * Arguments: @dstr *d@ = pointer to a dynamic string block + * + * Returns: --- + * + * Use: Initialises a dynamic string. + */ + +void dstr_create(dstr *d) +{ + d->sz = 0; + d->len = 0; + d->buf = 0; +} + +/* --- @dstr_destroy@ --- * + * + * Arguments: @dstr *d@ = pointer to a dynamic string block + * + * Returns: --- + * + * Use: Reclaims the space used by a dynamic string. + */ + +void dstr_destroy(dstr *d) +{ + if (d->buf) + free(d->buf); + d->buf = 0; + d->len = 0; + d->sz = 0; +} + +/* --- @dstr_reset@ --- * + * + * Arguments: @dstr *d@ = pointer to a dynaimc string block + * + * Returns: --- + * + * Use: Resets a string so that new data gets put at the beginning. + */ + +void dstr_reset(dstr *d) +{ + d->len = 0; +} + +/* --- @dstr_ensure@ --- * + * + * Arguments: @dstr *d@ = pointer to a dynamic string block + * @size_t sz@ = amount of free space to ensure + * + * Returns: --- + * + * Use: Ensures that at least @sz@ bytes are available in the + * given string. + */ + +void dstr_ensure(dstr *d, size_t sz) +{ + size_t rq = d->len + sz; + size_t nsz; + + /* --- If we have enough space, just leave it --- */ + + if (rq <= d->sz) + return; + + /* --- Grow the buffer --- * + * + * For small buffers, just double the size. For big buffers, make them + * a multiple of some suitably large chunk size. + */ + + nsz = d->sz; + + do { + if (nsz == 0) + nsz = DSTR_INITSZ; + else if (d->sz < 0x1000) + nsz <<= 1; + else + nsz = (rq + 0x0fff) & ~0x0fff; + } while (rq > nsz); + + if (d->buf) + d->buf = xrealloc(d->buf, nsz); + else + d->buf = xmalloc(nsz); + d->sz = nsz; +} + +/* --- @dstr_putc@ --- * + * + * Arguments: @dstr *d@ = pointer to a dynamic string block + * @char ch@ = character to append + * + * Returns: --- + * + * Use: Appends a character to a string. + */ + +void dstr_putc(dstr *d, char ch) +{ + DPUTC(d, ch); +} + +/* --- @dstr_putz@ --- * + * + * Arguments: @dstr *d@ = pointer to a dynamic string block + * + * Returns: --- + * + * Use: Appends a null byte to a string. The null byte does not + * contribute to the string's length, and will be overwritten + * by subsequent `put' operations. + */ + +void dstr_putz(dstr *d) +{ + DPUTZ(d); +} + +/* --- @dstr_puts@ --- * + * + * Arguments: @dstr *d@ = pointer to a dynamic string block + * @const char *s@ = pointer to string to append + * + * Returns: --- + * + * Use: Appends a character string to a string. A trailing null + * byte is added, as for @dstr_putz@. + */ + +void dstr_puts(dstr *d, const char *s) +{ + DPUTS(d, s); +} + +/* --- @dstr_putd@ --- * + * + * Arguments: @dstr *d@ = pointer to a dynamic string block + * @const dstr *s@ = pointer to a dynamic string to append + * + * Returns: --- + * + * Use: Appends a dynamic string to a string. A trailing null + * byte is added, as for @dstr_putz@. + */ + +void dstr_putd(dstr *d, const dstr *s) +{ + DPUTD(d, s); +} + +/* --- @dstr_putm@ --- * + * + * Arguments: @dstr *d@ = pointer to a dynamic string block + * @const void *p@ = pointer to a block to append + * @size_t sz@ = size of the block + * + * Returns: Appends an arbitrary data block to a string. No trailing + * null is appended. + */ + +void dstr_putm(dstr *d, const void *p, size_t sz) +{ + DPUTM(d, p, sz); +} + +/* --- @dstr_tidy@ --- * + * + * Arguments: @dstr *d@ = pointer to a dynamic string block + * + * Returns: --- + * + * Use: Reduces the amount of memory used by a string. A trailing + * null byte is added, as for @dstr_putz@. + */ + +void dstr_tidy(dstr *d) +{ + dstr_putz(d); + d->buf = xrealloc(d->buf, d->len + 1); + d->sz = d->len + 1; +} + +/* --- @dstr_putline@ --- * + * + * Arguments: @dstr *d@ = pointer to a dynamic string block + * @FILE *fp@ = a stream to read from + * + * Returns: The number of characters read into the buffer, or @EOF@ if + * end-of-file was reached before any characters were read. + * + * Use: Appends the next line from the given input stream to the + * string. A trailing newline is not added; a trailing null + * byte is appended, as for @dstr_putz@. + */ + +int dstr_putline(dstr *d, FILE *fp) +{ + size_t left = d->sz - d->len; + size_t off = d->len; + int rd = 0; + int ch; + + for (;;) { + + /* --- Make sure there's some buffer space --- */ + + if (!left) { + dstr_ensure(d, 1); + left = d->sz - off; + } + + /* --- Read the next byte --- */ + + ch = getc(fp); + + /* --- End-of-file when no characters read is special --- */ + + if (ch == EOF && !rd) + return (EOF); + + /* --- End-of-file or newline ends the loop --- */ + + if (ch == EOF || ch == '\n') { + d->buf[off] = 0; + d->len = off; + return rd; + } + + /* --- Append the character and continue --- */ + + d->buf[off++] = ch; + left--; rd++; + } +} + +/* --- @dstr_write@ --- * + * + * Arguments: @dstr *d@ = pointer to a dynamic string block + * @FILE *fp@ = a stream to write on + * + * Returns: The number of bytes written (as for @fwrite@). + * + * Use: Writes a dynamic string to a file. + */ + +size_t dstr_write(dstr *d, FILE *fp) +{ + return (fwrite(d->buf, 1, d->len, fp)); +} + +/*----- That's all, folks -------------------------------------------------*/ diff --git a/dstr.h b/dstr.h new file mode 100644 index 0000000..b7240fe --- /dev/null +++ b/dstr.h @@ -0,0 +1,256 @@ +/* -*-c-*- + * + * $Id: dstr.h,v 1.1 1998/06/17 23:44:42 mdw Exp $ + * + * Handle dynamically growing strings + * + * (c) 1998 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of the mLib utilities library. + * + * mLib is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * mLib is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with mLib; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: dstr.h,v $ + * Revision 1.1 1998/06/17 23:44:42 mdw + * Initial revision + * + */ + +#ifndef DSTR_H +#define DSTR_H + +#ifdef __cplusplus + extern "C" { +#endif + +/*----- Rationale ---------------------------------------------------------* + * + * This file declares what is hopefully a fairly useful collection of + * primitive string handling functions. The idea is that the strings + * allocate memory for themselves as required. The @dstr@ routines don't + * assume any sort of terminator character, so arbitrary binary data can + * be stored in a dynamic string. With luck, this should put a stop to + * any buffer overflow problems. + */ + +/*----- Header files ------------------------------------------------------*/ + +#include + +/*----- Data structures ---------------------------------------------------*/ + +typedef struct dstr { + char *buf; /* Pointer to string buffer */ + size_t sz; /* Size of the buffer */ + size_t len; /* Length of the string */ +} dstr; + +/*----- Functions provided ------------------------------------------------*/ + +/* --- @dstr_create@ --- * + * + * Arguments: @dstr *d@ = pointer to a dynamic string block + * + * Returns: --- + * + * Use: Initialises a dynamic string. + */ + +extern void dstr_create(dstr */*d*/); + +/* --- @dstr_destroy@ --- * + * + * Arguments: @dstr *d@ = pointer to a dynamic string block + * + * Returns: --- + * + * Use: Reclaims the space used by a dynamic string. + */ + +extern void dstr_destroy(dstr */*d*/); + +/* --- @dstr_reset@ --- * + * + * Arguments: @dstr *d@ = pointer to a dynaimc string block + * + * Returns: --- + * + * Use: Resets a string so that new data gets put at the beginning. + */ + +extern void dstr_reset(dstr */*d*/); + +/* --- @dstr_ensure@ --- * + * + * Arguments: @dstr *d@ = pointer to a dynamic string block + * @size_t sz@ = amount of free space to ensure + * + * Returns: --- + * + * Use: Ensures that at least @sz@ bytes are available in the + * given string. + */ + +extern void dstr_ensure(dstr */*d*/, size_t /*sz*/); + +#define DENSURE(d, rq) do { \ + if ((d)->len + (rq) > (d)->sz) dstr_ensure((d), (rq)); \ +} while (0) + +/* --- @dstr_putc@ --- * + * + * Arguments: @dstr *d@ = pointer to a dynamic string block + * @char ch@ = character to append + * + * Returns: --- + * + * Use: Appends a character to a string. + */ + +extern void dstr_putc(dstr */*d*/, char /*ch*/); + +#define DPUTC(d, ch) do { \ + DENSURE((d), 1); \ + (d)->buf[(d)->len++] = (ch); \ +} while (0) + +/* --- @dstr_putz@ --- * + * + * Arguments: @dstr *d@ = pointer to a dynamic string block + * + * Returns: --- + * + * Use: Appends a null byte to a string. The null byte does not + * contribute to the string's length, and will be overwritten + * by subsequent `put' operations. + */ + +extern void dstr_putz(dstr */*d*/); + +#define DPUTZ(d) do { \ + DENSURE((d), 1); \ + (d)->buf[(d)->len] = 0; \ +} while (0) + +/* --- @dstr_puts@ --- * + * + * Arguments: @dstr *d@ = pointer to a dynamic string block + * @const char *s@ = pointer to string to append + * + * Returns: --- + * + * Use: Appends a character string to a string. A trailing null + * byte is added, as for @dstr_putz@. + */ + +extern void dstr_puts(dstr */*d*/, const char */*s*/); + +#define DPUTS(d, s) do { \ + size_t sz = strlen(s); \ + DENSURE((d), sz + 1); \ + memcpy((d)->buf + (d)->len, (s), sz + 1); \ + (d)->len += sz; \ +} while (0) + +/* --- @dstr_putd@ --- * + * + * Arguments: @dstr *d@ = pointer to a dynamic string block + * @const dstr *s@ = pointer to a dynamic string to append + * + * Returns: --- + * + * Use: Appends a dynamic string to a string. A trailing null + * byte is added, as for @dstr_putz@. + */ + +extern void dstr_putd(dstr */*d*/, const dstr */*s*/); + +#define DPUTD(d, s) do { \ + DENSURE((d), (s)->len + 1); \ + memcpy((d)->buf + (d)->len, (s)->buf, (s)->len); \ + (d)->len += (s)->len; \ + (d)->buf[(d)->len] = 0; \ +} while (0) + +/* --- @dstr_putm@ --- * + * + * Arguments: @dstr *d@ = pointer to a dynamic string block + * @const void *p@ = pointer to a block to append + * @size_t sz@ = size of the block + * + * Returns: Appends an arbitrary data block to a string. No trailing + * null is appended. + */ + +extern void dstr_putm(dstr */*d*/, const void */*p*/, size_t /*sz*/); + +#define DPUTM(d, p, sz) do { \ + DENSURE((d), (sz)); \ + memcpy((d)->buf + (d)->len, (p), (sz)); \ + (d)->len += (sz); \ +} while (0) + +/* --- @dstr_tidy@ --- * + * + * Arguments: @dstr *d@ = pointer to a dynamic string block + * + * Returns: --- + * + * Use: Reduces the amount of memory used by a string. A trailing + * null byte is added, as for @dstr_putz@. + */ + +extern void dstr_tidy(dstr */*d*/); + +/* --- @dstr_putline@ --- * + * + * Arguments: @dstr *d@ = pointer to a dynamic string block + * @FILE *fp@ = a stream to read from + * + * Returns: The number of characters read into the buffer, or @EOF@ if + * end-of-file was reached before any characters were read. + * + * Use: Appends the next line from the given input stream to the + * string. A trailing newline is not added; a trailing null + * byte is appended, as for @dstr_putz@. + */ + +extern int dstr_putline(dstr */*d*/, FILE */*fp*/); + +/* --- @dstr_write@ --- * + * + * Arguments: @dstr *d@ = pointer to a dynamic string block + * @FILE *fp@ = a stream to write on + * + * Returns: The number of bytes written (as for @fwrite@). + * + * Use: Writes a dynamic string to a file. + */ + +extern size_t dstr_write(dstr */*d*/, FILE */*fp*/); + +/*----- That's all, folks -------------------------------------------------*/ + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/dynarray.h b/dynarray.h new file mode 100644 index 0000000..32a7939 --- /dev/null +++ b/dynarray.h @@ -0,0 +1,279 @@ +/* -*-c-*- + * + * $Id: dynarray.h,v 1.1 1998/06/17 23:44:42 mdw Exp $ + * + * Dynamic arrays implementation + * + * (c) 1998 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of the mLib utilities library. + * + * mLib is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * mLib is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with mLib; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: dynarray.h,v $ + * Revision 1.1 1998/06/17 23:44:42 mdw + * Initial revision + * + */ + +#ifndef DYNARRAY_H +#define DYNARRAY_H + +#ifdef __cplusplus + extern "C" { +#endif + +/*----- Required header files ---------------------------------------------*/ + +#include +#include + +#ifndef SUB_H +# include "sub.h" +#endif + +#ifndef TRACK_H +# include "track.h" +#endif + +/*----- Horrific hacking -------------------------------------------------* + * + * Several bits of this code need extending arrays (in particular, the array + * handler and the label allocator), although the sizes of the objects being + * arrayed differs. + * + * This file contains (horrors!) a sort of C++ template-a-like which + * implements such growing arrays. Read on, if your constitution can stand + * it... + */ + +/* --- Macro: @DYNDECLS@ --- * + * + * Arguments: @prefix@ = prefix string which begins all external + * identifiers + * @obtype@ = the element type of the array + * @chunksize@ = the number of items in a chunk + * + * Use: Provides declarations suitable for use in a header file which + * define functions for manipulating the specific dynamic array + * described in the arguments. + */ + +#define DYNDECLS(prefix, obtype, chunksize) /* ... */ \ + \ +/* --- Define some constants --- */ \ + \ +enum { \ + prefix ## __mask = chunksize - 1, \ + prefix ## __size = chunksize \ +}; \ + \ +/* --- Type definitions --- */ \ + \ +typedef obtype prefix ## __object; \ + \ +typedef struct prefix ## _chunk { \ + struct prefix ## _chunk *next; \ + size_t base; \ + prefix ## __object o[prefix ## __size]; \ +} prefix ## _chunk; \ + \ +/* --- External routines --- */ \ + \ +/* --- @PREFIX_find@ --- * \ + * \ + * Arguments: @PREFIX_chunk **base@ = anchor address of chunk list \ + * @size_t index@ = index into array which we want \ + * \ + * Returns: Pointer to the object at appropriate index, or null \ + * \ + * Use: Indexes an item without creating it if it's not there \ + * already. \ + */ \ + \ +extern prefix ## __object *prefix ## _find(prefix ## _chunk **base, \ + size_t index); \ + \ +/* --- @PREFIX_new@ --- * \ + * \ + * Arguments: @PREFIX_chunk **base@ = anchor address of chunk list \ + * @size_t index@ = index into array which we want \ + * \ + * Returns: Pointer to the object at appropriate index \ + * \ + * Use: Indexes an item, creating it if necessary. \ + */ \ + \ +extern prefix ## __object *prefix ## _new(prefix ## _chunk **base, \ + size_t index); \ + \ +/* --- @PREFIX_free@ --- * \ + * \ + * Arguments: @PREFIX_chunk **base@ = anchor address of chunk list \ + * \ + * Returns: --- \ + * \ + * Use: Releases all the memory directly attached to an array. \ + */ \ + \ +extern void prefix ## _free(prefix ## _chunk **base); + +/* --- Macro: @DYNARRAY@ --- * + * + * Arguments: @prefix@ = prefix string for uniquification + * @init@ = how to initialise a chunk + * @kill@ = how to free a chunk + * + * Use: Builds template routines for dynamically growing arrays. + * The two arguments @init@ and @kill@ must use the macros + * described below. + */ + +#define DYNARRAY(prefix, init, kill) /* ... */ \ + \ +/* --- @PREFIX_find@ --- * \ + * \ + * Arguments: @PREFIX_chunk **base@ = anchor address of chunk list \ + * @size_t index@ = index into array which we want \ + * \ + * Returns: Pointer to the object at appropriate index, or null \ + * \ + * Use: Indexes an item without creating it if it's not there \ + * already. \ + */ \ + \ +prefix ## __object *prefix ## _find(prefix ## _chunk **base, \ + size_t index) \ +{ \ + size_t chunkid = index & ~prefix ## __mask; \ + prefix ## _chunk *p = *base; \ + index &= prefix ## __mask; \ + \ + for (;;) { \ + if (!p || p->base > chunkid) \ + return (0); \ + if (p->base == chunkid) \ + break; \ + p = p->next; \ + } \ + \ + return (p->o + index); \ +} \ + \ +/* --- @PREFIX_new@ --- * \ + * \ + * Arguments: @PREFIX_chunk **base@ = anchor address of chunk list \ + * @size_t index@ = index into array which we want \ + * \ + * Returns: Pointer to the object at appropriate index \ + * \ + * Use: Indexes an item, creating it if necessary. \ + */ \ + \ +prefix ## __object *prefix ## _new(prefix ## _chunk **base, \ + size_t index) \ +{ \ + size_t chunkid = index & ~prefix ## __mask; \ + prefix ## _chunk *p = (prefix ## _chunk *)base; \ + index &= prefix ## __mask; \ + \ + while (p->next && p->next->base < chunkid) \ + p = p->next; \ + \ + if (!p->next || p->next->base != chunkid) { \ + prefix ## _chunk *q = CREATE(prefix ## _chunk); \ + q->next = p->next; \ + p->next = q; \ + q->base = chunkid; \ + \ + DYN__GRABARGS(init, (q, prefix)); \ + } \ + \ + return (p->next->o + index); \ +} \ + \ +/* --- @PREFIX_free@ --- * \ + * \ + * Arguments: @PREFIX_chunk **base@ = anchor address of chunk list \ + * \ + * Returns: --- \ + * \ + * Use: Releases all the memory directly attached to an array. \ + */ \ + \ +void prefix ## _free(prefix ## _chunk **base) \ +{ \ + prefix ## _chunk *p = *base, *q; \ + \ + while (p) { \ + DYN__GRABARGS(kill, (p, prefix)); \ + q = p; \ + p = p->next; \ + DESTROY(q); \ + } \ + \ + *base = 0; \ +} + +/* --- A vile bit of hacking --- * + * + * All of this yukkiness is a perfectly legitimate consequence of the (daft) + * rules about when macro arguments get expanded. + */ + +#define DYN__ID(x) x +#define DYN__ID2(x, y) x, y +#define DYN__CONTORT(mac, args) mac args +#define DYN__GRABARGS(mac, args, more) \ + DYN__CONTORT(mac, (DYN__ID args, DYN__ID2 more)) + +/* --- Macro: @DYNITER@ --- * + * + * Arguments: @what@ = what to do for each item -- a macro or function + * which is passed the address of an item + * + * Use: Does something for each item. + */ + +#define DYNITER DYN__ITER , +#define DYN__ITER(what, chunk, prefix) do { \ + int i; \ + for (i = 0; i < prefix ## __size; i++) \ + what(chunk->o + i); \ +} while (0) + +/* --- Macro: @DYNNOP@ --- * + * + * Arguments: --- + * + * Use: Does nothing. + */ + +#define DYNNOP DYN__NOP, (dummy) +#define DYN__NOP(dummy, chunk, prefix) /* nop */ + +/*----- That's all, folks -------------------------------------------------*/ + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/exc.c b/exc.c new file mode 100644 index 0000000..1c6468d --- /dev/null +++ b/exc.c @@ -0,0 +1,145 @@ +/* -*-c-*- + * + * $Id: exc.c,v 1.1 1998/06/17 23:44:42 mdw Exp $ + * + * Structured exception handling in C + * + * (c) 1998 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of the mLib utilities library. + * + * mLib is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * mLib is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with mLib; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: exc.c,v $ + * Revision 1.1 1998/06/17 23:44:42 mdw + * Initial revision + * + */ + +#include +#include +#include + +#include "exc.h" + +/*----- Global variables --------------------------------------------------*/ + +__exc_hnd *__exc_list = 0; + +/*----- Functions ---------------------------------------------------------*/ + +/* --- @exc__duff@ --- * + * + * Arguments: @exc_extype type@ = type of duff exception + * @exc_exval val@ = extra data supplied + * + * Returns: Doesn't + * + * Use: Default handler when everything goes wrong. + */ + +static void exc__duff(exc_extype type, exc_exval val) +{ + fprintf(stderr, "fatal error: uncaught exception (type = %lu)\n", type); + abort(); +} + +/* --- @exc__duffproc@ --- * + * + * Current handler when there are no more exceptions left. + */ + +static exc__uncaught exc__duffproc = exc__duff; + +/* --- @exc_uncaught@ --- * + * + * Arguments: @void (*proc)(exc_extype type, exc_exval val) = new handler + * + * Returns: Pointer to the old handler value. + * + * Use: Sets the handler for uncaught exceptions. + */ + +exc__uncaught exc_uncaught(exc__uncaught proc) +{ + exc__uncaught p = exc__duffproc; + if (proc) + exc__duffproc = proc; + return (p); +} + +/* --- @__exc_throw@ --- * + * + * Arguments: @exc_extype type@ = type of exception to throw + * + * Returns: Doesn't + * + * Use: NOT FOR USER CONSUMPTION. Reads an appropriate exception + * value and throws an exception. + */ + +void __exc_throw(exc_extype type, ...) +{ + va_list ap; + exc_exval v; + + va_start(ap, type); + switch (type & 0xC0) { + case EXC_NOVAL: + v.i = 0; + break; + case EXC_INTVAL: + v.i = va_arg(ap, int); + break; + case EXC_PTRVAL: + v.p = va_arg(ap, void *); + break; + case EXC_STRVAL: + v.s = va_arg(ap, char *); + break; + } + va_end(ap); + __exc_rethrow(type, v); +} + +/* --- @__exc_rethrow@ --- * + * + * Arguments: @exc_extype type@ = type of exception to throw + * @exc_exval val@ = value of exception to throw + * + * Returns: Doesn't + * + * Use: NOT FOR USER CONSUMPTION. Does the donkey-work of raising + * an exception. + */ + +void __exc_rethrow(exc_extype type, exc_exval val) +{ + __exc_hnd *p = __exc_list; + if (!p) + exc__duffproc(type, val); + p->type = type; + p->val = val; + __exc_list = p->next; + longjmp(p->buf, type); +} + +/*----- That's all, folks -------------------------------------------------*/ diff --git a/exc.h b/exc.h new file mode 100644 index 0000000..a2e004e --- /dev/null +++ b/exc.h @@ -0,0 +1,341 @@ +/* -*-c-*- + * + * $Id: exc.h,v 1.1 1998/06/17 23:44:42 mdw Exp $ + * + * Structured exception handling in C + * + * (c) 1998 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of the mLib utilities library. + * + * mLib is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * mLib is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with mLib; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: exc.h,v $ + * Revision 1.1 1998/06/17 23:44:42 mdw + * Initial revision + * + */ + +#ifndef EXC_H +#define EXC_H + +#ifdef __cplusplus + extern "C" { +#endif + +#include + +/*----- Quick documentation -----------------------------------------------* + * + * This header file provides some exception handling facilities in C + * programs. It modifies the syntax of the language slightly, using the + * preprocessor. + * + * The `throw' expression returns no value. It has the syntax: + * + * THROW ( expr , expr ) + * + * The first expression must have type compatible with unsigned integer; it + * identifies an `exception type'. The second must have type compatible + * with pointer to void; it contains the `exception data'. Control is + * passed to the current exception handler. + * + * The `RETHROW' expression, valid only within an exception handler, causes + * the current exception to be thrown again. + * + * A `try' statement has the syntax: + * + * TRY stat CATCH stat END_TRY; + * + * The first statement is called the `test'; the second is the `handler'. + * During execution of the test, the handler is added to a stack of + * active exception handlers; the topmost handler on this stack is called + * the `current' handler. When execution of the test completes, the + * corresponding handler is removed from the stack. + * + * The test statement may complete in one of these ways: + * + * * Normal completion -- control reaches the end of the statement + * normally. + * + * * Throwing an exception -- an exception is thrown when the handler is + * the current exception handler. + * + * * By executing a `break' statement. + * + * * By executing the expression `EXIT_TRY' and transferring control to + * a point outside the entire `try' statement (e.g., executing a `goto' + * or `return' statement). + * + * Any other attempt to leave the test causes undefined behaviour. + * + * If an exception is thrown while the handler is the current exception + * handler, it is given control. The variables `exc_type' and `exc_val' + * denote the exception type and value respectively -- they are passed + * unchanged from the `throw' expression which caused the exception. + * A handler is deactivated before it is invoked; if it causes an + * exception to be thrown (and does not contain a nested `try' statement) + * control will be passed to an earlier active handler. + * + * Control is passed to handlers using the `longjmp' function. + * + * Example: + * + * TRY { + * ... something dangerous ... + * } CATCH switch (exc_type) { + * case EXC_INTERESTING: + * ... handle exception ... + * break; + * default: + * ... do tidying up ... + * RETHROW; + * } END_TRY; + */ + +/*----- Exception type allocation -----------------------------------------* + * + * Nobody allocates exception types, so we'll just have to try to get along + * without too many collisions. An exception type is an unsigned long, + * which gives us four bytes. The top two bytes identify the library which + * `owns' the exception, with special values zero meaning `defined as part + * of the system' and 0xFFFF providing a shared space of types which can + * be used by anyone as long as they don't get seen by anyone else. + * + * The lower byte pair encodes a type number, and a value which defines + * the type of the value field (see below). + */ + +/* --- Type type of an exception --- */ + +typedef unsigned long exc_extype; + +/* --- Build a byte pair from two characters --- * + * + * Note the icky casting to handle signed chars. + */ + +#define EXC_PAIR(x, y) (((unsigned long)(unsigned char)(x) << 8) | \ + (unsigned long)(unsigned char)(y)) + +/* --- Allocate an exception number --- */ + +#define EXC_ALLOC(owner, type) (((unsigned long)(owner) << 16) | \ + (unsigned long)(type)) + +/* --- Special owner codes --- */ + +#define EXC_GLOBAL 0u /* The global space defined here */ +#define EXC_SHARED 0xFFFFu /* The shared space for everyone */ + +/*----- Exception values --------------------------------------------------* + * + * Exception values can have several different types. This is a mess, and + * C doesn't handle it too well, but we can try. I'll encode the value type + * as part of the exception type, in the top bits of the bottom byte. Messy? + * You betcha. + */ + +/* --- Encoding a value type in an extype --- */ + +#define EXC_TYPECODE(t, w) (((w) & ~0xC0u) | ((t) & 0xC0u)) + +/* --- The various value types --- */ + +#define EXC_NOVAL 0x00u /* No interesting value */ +#define EXC_INTVAL 0x40u /* Integer value */ +#define EXC_PTRVAL 0x80u /* Arbitrary pointer value */ +#define EXC_STRVAL 0xC0u /* Pointer to character string */ + +/* --- Allocating exceptions with appropriate types --- */ + +#define EXC_ALLOCN(o, t) EXC_TYPECODE(EXC_NOVAL, EXC_ALLOC(o, t)) +#define EXC_ALLOCI(o, t) EXC_TYPECODE(EXC_INTVAL, EXC_ALLOC(o, t)) +#define EXC_ALLOCP(o, t) EXC_TYPECODE(EXC_PTRVAL, EXC_ALLOC(o, t)) +#define EXC_ALLOCS(o, t) EXC_TYPECODE(EXC_STRVAL, EXC_ALLOC(o, t)) + +/* --- A union representing the type --- */ + +typedef union exc_exval { + int i; + void *p; + char *s; +} exc_exval; + +/*----- Predefined exceptions ---------------------------------------------*/ + +/* --- @EXC_NOMEM@ --- * + * + * Value: --- + * + * Meaning: An attempt to allocate memory failed. + */ + +#define EXC_NOMEM EXC_ALLOCN(EXC_GLOBAL, 0u) + +/* --- @EXC_ERRNO@ --- * + * + * Value: @int errno@ = the error raised + * + * Meaning: Some kind of OS error occurred. + */ + +#define EXC_ERRNO EXC_ALLOCI(EXC_GLOBAL, 1u) + +/* --- @EXC_OSERROR@ --- * + * + * Value: @os_error *e@ = pointer to error block + * + * Meaning: For RISC OS programmers only: alternative way of propagating + * errors. + */ + +#define EXC_OSERROR EXC_ALLOCP(EXC_GLOBAL, 1u) + +/* --- @EXC_SIGNAL@ --- * + * + * Value: @int sig@ = signal number + * + * Meaning: Report the raising of a signal. + */ + +#define EXC_SIGNAL EXC_ALLOCI(EXC_GLOBAL, 2u) + +/* --- @EXC_FAIL@ --- * + * + * Value: @const char *p@ = pointer to expanatory string + * + * Meaning: Miscellaneous error. + */ + +#define EXC_FAIL EXC_ALLOCS(EXC_GLOBAL, 0xFFu) + +/*----- An exception handler block ----------------------------------------*/ + +/* --- Try to think of this as being opaque --- */ + +typedef struct __exc_hnd { + struct __exc_hnd *next; /* Pointer to next record down */ + exc_extype type; /* Type of this exception */ + exc_exval val; /* Value of this exception */ + jmp_buf buf; /* Jump buffer when exceptions hit */ +} __exc_hnd; + +/*----- Global variables --------------------------------------------------*/ + +extern __exc_hnd *__exc_list; /* List of active handlers */ + +/*----- Macros ------------------------------------------------------------*/ + +/* --- References to current exception type and value --- */ + +#define exc_type (__exc_ec.type) +#define exc_val (__exc_ec.val) +#define exc_i (__exc_ec.val.i) +#define exc_p (__exc_ec.val.p) +#define exc_s (__exc_ec.val.s) + +/* --- How it actually works --- * + * + * A `try' block is contained within a block which provides an exception + * handler buffer in automatic storage. This block is a loop, to allow + * `break' to escape from it. It adds the handler buffer to the top of a + * list, and does a `setjmp' to allow a return here following an exception. + * The `setjmp' returns zero for the `try' section, and nonzero if there's + * an exception to `catch'. It looks a little like this: + * + * do { + * __exc_hnd h; + * add_handler(&h); + * if (!setjmp(h.buf)) { + * do while (0); + * remove_handler(&h); + * } else + * + * } while (0) + * + * Everything else is ugly hacking to make things work. + */ + +/* --- Trying things which may cause exceptions --- */ + +#define TRY do { \ + volatile __exc_hnd __exc_ec; \ + __exc_ec.next = __exc_list; \ + __exc_list = (__exc_hnd *)&__exc_ec; \ + if (!setjmp(*(jmp_buf *)&__exc_ec.buf /* very nasty! */ )) { do + +#define EXIT_TRY do __exc_list = __exc_ec.next; while (0) +#define CATCH while (0); EXIT_TRY; } else + +#define END_TRY } while (0) + +/* --- Raising exceptions --- */ + +#define THROW __exc_throw +#define RETHROW __exc_rethrow(__exc_ec.type, __exc_ec.val) + +/*----- Functions ---------------------------------------------------------*/ + +/* --- @exc_uncaught@ --- * + * + * Arguments: @void (*proc)(exc_extype type, exc_exval val) = new handler + * + * Returns: Pointer to the old handler value. + * + * Use: Sets the handler for uncaught exceptions. + */ + +typedef void (*exc__uncaught)(exc_extype /*type*/, exc_exval /*val*/); +extern exc__uncaught exc_uncaught(exc__uncaught /*proc*/); + +/* --- @__exc_throw@ --- * + * + * Arguments: @exc_extype type@ = type of exception to throw + * + * Returns: Doesn't + * + * Use: NOT FOR USER CONSUMPTION. Reads an appropriate exception + * value and throws an exception. + */ + +extern void __exc_throw(exc_extype /*type*/, ...); + +/* --- @__exc_rethrow@ --- * + * + * Arguments: @exc_extype type@ = type of exception to throw + * @exc_exval val@ = value of exception to throw + * + * Returns: Doesn't + * + * Use: NOT FOR USER CONSUMPTION. Does the donkey-work of raising + * an exception. + */ + +extern void __exc_rethrow(exc_extype /*type*/, exc_exval /*val*/); + +/*----- That's all, folks -------------------------------------------------*/ + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/exctest.c b/exctest.c new file mode 100644 index 0000000..f9a9463 --- /dev/null +++ b/exctest.c @@ -0,0 +1,41 @@ +#include +#include "exc.h" + +void func(void) +{ + printf("cabbage\n"); + TRY { + printf("dibble\n"); + THROW(EXC_FAIL, "excession"); + printf("can't see me\n"); + } CATCH switch (exc_type) { + case 1: + printf("exc type 1 (not possible)\n"); + break; + case EXC_FAIL: + printf("exc type 2 (%s)\n", exc_s); + break; + default: + RETHROW; + } END_TRY; + + printf("fennel\n"); + THROW(EXC_ERRNO, 53); +} + +int main(void) +{ + printf("apple\n"); + TRY { + printf("banana\n"); + func(); + printf("can't see me\n"); + } CATCH switch (exc_type) { + default: + printf("%lu exception (val = %i)\n", exc_type, exc_i); + break; + } END_TRY; + printf("hello! __exc_list = %p\n", __exc_list); + + return (0); +} diff --git a/mdwopt.c b/mdwopt.c new file mode 100644 index 0000000..e69de29 diff --git a/mdwopt.h b/mdwopt.h new file mode 100644 index 0000000..e69de29 diff --git a/quis.c b/quis.c new file mode 100644 index 0000000..34b0f1f --- /dev/null +++ b/quis.c @@ -0,0 +1,92 @@ +/* -*-c-*- + * + * $Id: quis.c,v 1.1 1998/06/17 23:44:42 mdw Exp $ + * + * Setting the program name + * + * (c) 1998 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of the mLib utilities library. + * + * mLib is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * mLib is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with mLib; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: quis.c,v $ + * Revision 1.1 1998/06/17 23:44:42 mdw + * Initial revision + * + */ + +/*----- Header files ------------------------------------------------------*/ + +#include +#include +#include +#include + +#include "quis.h" + +/*----- Global variables --------------------------------------------------*/ + +const char *pn__name = ""; /* Program name */ + +/*----- Functions provided ------------------------------------------------*/ + +/* --- @quis@ --- * + * + * Arguments: --- + * + * Returns: Pointer to the program name. + * + * Use: Returns the program name. + */ + +const char *quis(void) { return (QUIS); } + +/* --- @ego@ --- * + * + * Arguments: @const char *p@ = pointer to program name + * + * Returns: --- + * + * Use: Tells mLib what the program's name is. + */ + +#ifndef PATHSEP +# if defined(__riscos) +# define PATHSEP '.' +# elif defined(__unix) || defined(unix) +# define PATHSEP '/' +# else +# define PATHSEP '\\' +# endif +#endif + +void ego(const char *p) +{ + const char *q = p; + while (*q) { + if (*q++ == PATHSEP) + p = q; + } + pn__name = p; +} + +/*----- That's all, folks -------------------------------------------------*/ diff --git a/quis.h b/quis.h new file mode 100644 index 0000000..b60084c --- /dev/null +++ b/quis.h @@ -0,0 +1,79 @@ +/* -*-c-*- + * + * $Id: quis.h,v 1.1 1998/06/17 23:44:42 mdw Exp $ + * + * Setting the program name + * + * (c) 1998 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of the mLib utilities library. + * + * mLib is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * mLib is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with mLib; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: quis.h,v $ + * Revision 1.1 1998/06/17 23:44:42 mdw + * Initial revision + * + */ + +#ifndef PROGNAME_H +#define PROGNAME_H + +#ifdef __cplusplus + extern "C" { +#endif + +/*----- Global variables --------------------------------------------------*/ + +extern const char *pn__name; + +/*----- Functions provided ------------------------------------------------*/ + +/* --- @quis@ --- * + * + * Arguments: --- + * + * Returns: Pointer to the program name. + * + * Use: Returns the program name. + */ + +extern const char *quis(void); +#define QUIS (pn__name) + +/* --- @ego@ --- * + * + * Arguments: @const char *p@ = pointer to program name + * + * Returns: --- + * + * Use: Tells mLib what the program's name is. + */ + +extern void ego(const char */*p*/); + +/*----- That's all, folks -------------------------------------------------*/ + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/report.c b/report.c new file mode 100644 index 0000000..7a7aacf --- /dev/null +++ b/report.c @@ -0,0 +1,92 @@ +/* -*-c-*- + * + * $Id: report.c,v 1.1 1998/06/17 23:44:42 mdw Exp $ + * + * Reporting errors and things + * + * (c) 1998 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of the mLib utilities library. + * + * mLib is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * mLib is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with mLib; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: report.c,v $ + * Revision 1.1 1998/06/17 23:44:42 mdw + * Initial revision + * + */ + +/*----- Header files ------------------------------------------------------*/ + +#include +#include +#include +#include + +#include "quis.h" +#include "report.h" + +/*----- Functions provided ------------------------------------------------*/ + +/* --- @moan@ --- * + * + * Arguments: @const char *f@ = a @printf@-style format string + * @...@ = other arguments + * + * Returns: --- + * + * Use: Reports an error. + */ + +void moan(const char *f, ...) +{ + va_list ap; + va_start(ap, f); + fprintf(stderr, "%s: ", QUIS); + vfprintf(stderr, f, ap); + va_end(ap); + putc('\n', stderr); +} + +/* --- @die@ --- * + * + * Arguments: @int status@ = exit status to return + * @const char *f@ = a @printf@-style format string + * @...@ = other arguments + * + * Returns: Never. + * + * Use: Reports an error and exits. Like @moan@ above, only more + * permanent. + */ + +void die(int status, const char *f, ...) +{ + va_list ap; + va_start(ap, f); + fprintf(stderr, "%s: ", QUIS); + vfprintf(stderr, f, ap); + va_end(ap); + putc('\n', stderr); + exit(status); +} + +/*----- That's all, folks -------------------------------------------------*/ diff --git a/report.h b/report.h new file mode 100644 index 0000000..16b4ab7 --- /dev/null +++ b/report.h @@ -0,0 +1,78 @@ +/* -*-c-*- + * + * $Id: report.h,v 1.1 1998/06/17 23:44:42 mdw Exp $ + * + * Reporting errors and things + * + * (c) 1998 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of the mLib utilities library. + * + * mLib is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * mLib is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with mLib; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: report.h,v $ + * Revision 1.1 1998/06/17 23:44:42 mdw + * Initial revision + * + */ + +#ifndef REPORT_H +#define REPORT_H + +#ifdef __cplusplus + extern "C" { +#endif + +/*----- Functions provided ------------------------------------------------*/ + +/* --- @moan@ --- * + * + * Arguments: @const char *f@ = a @printf@-style format string + * @...@ = other arguments + * + * Returns: --- + * + * Use: Reports an error. + */ + +extern void moan(const char *f, ...); + +/* --- @die@ --- * + * + * Arguments: @int status@ = exit status to return + * @const char *f@ = a @printf@-style format string + * @...@ = other arguments + * + * Returns: Never. + * + * Use: Reports an error and exits. Like @moan@ above, only more + * permanent. + */ + +extern void die(int status, const char *f, ...); + +/*----- That's all, folks -------------------------------------------------*/ + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/sub.c b/sub.c new file mode 100644 index 0000000..2f0fed3 --- /dev/null +++ b/sub.c @@ -0,0 +1,260 @@ +/* -*-c-*- + * + * $Id: sub.c,v 1.1 1998/06/17 23:44:42 mdw Exp $ + * + * Allocation of known-size blocks + * + * (c) 1998 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of the mLib utilities library. + * + * mLib is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * mLib is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with mLib; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: sub.c,v $ + * Revision 1.1 1998/06/17 23:44:42 mdw + * Initial revision + * + */ + +/*----- The big idea ------------------------------------------------------* + * + * This file provides an extra layer over @malloc@. It provides fast + * turnover for small blocks, and tries to minimise the per-block overhead. + * + * To do its job, @alloc@ must place an extra restriction on you: you must + * know the size of a block when you free it. Usually you'll have this + * information encoded in some way either in the block or in the thing that + * referenced it, so this isn't a hardship. + * + * It works fairly simply. If a request for a big block (as defined by the + * constants below) comes in, it gets sent on to @malloc@ unmolested. For + * small blocks, it goes straight to a `bin' -- a list containing free blocks + * of exactly that size, or the nearest bigger size we can manage. If the + * bin is empty, a `chunk' is allocated from @malloc@: this has enough room + * for lots of blocks of the requested size, so it ets split up and each + * individual small block is added to the bin list. The first block in the + * bin list is then removed and given to the caller. In this way, @malloc@ + * only stores its information once for lots of little blocks, so we save + * memory. Because I know where the correct bin is just from the block size, + * and I don't need to do any searching at all in the usual case (because the + * list isn't empty) I can get a speed advantage too. + * + * This code is almost certainly not ANSI conformant, although I'm not + * actually sure. If some kind soul would let me know how seriously I've + * violated the standard, and whether this is easily fixable, I'd be + * grateful. + */ + +/*----- Header files ------------------------------------------------------*/ + +/* --- ANSI headers --- */ + +#include +#include +#include + +/* --- Local headers --- */ + +#undef TRACK_ENABLE /* Can't track suballoc routines */ +#include "alloc.h" + +/*----- Configuration and tuning ------------------------------------------*/ + +/* --- The largest block I'll handle here --- * + * + * Anything larger will be handed on to @malloc@. + */ + +#define SUB_MAXBIN 256 + +/* --- Preferred chunk size --- * + * + * When a bin is empty, I'll allocate a large chunk of approximately this + * size and divvy it up into small bin-sized blocks. + */ + +#define SUB_CHUNK 4096 + +/*----- Other useful macros -----------------------------------------------*/ + +/* --- The granularity of bin buffers --- * + * + * All blocks allocated by the binner are a multiple of this size. I've + * chosen @void *@ because I need to store @void *@ things in here. + */ + +#define SUB_GRANULE sizeof(void *) + +/* --- Finding the right bin for a given size --- * + * + * This chooses the correct bin for an allocation. Input is the size of + * block wanted; result is the bin index. + */ + +#define SUB_BIN(x) (((x) + SUB_GRANULE - 1) / SUB_GRANULE) + +/* --- Convert a bin back to the block size --- * + * + * This gives the size of block contained in a given bin. + */ + +#define SUB_BINSZ(x) ((x) * SUB_GRANULE) + +/* --- Number of bins required --- */ + +#define SUB_BINS (SUB_MAXBIN / SUB_GRANULE + 1) + +/*----- Static variables --------------------------------------------------*/ + +static void *sub__bins[SUB_BINS]; +static size_t sub__sizes[SUB_BINS]; + +/*----- Main code ---------------------------------------------------------*/ + +/* --- @sub_alloc@ --- * + * + * Arguments: @size_t s@ = size of chunk wanted + * + * Returns: Pointer to a block at least as large as the one wanted. + * + * Use: Allocates a small block of memory. If there is no more + * memory left, the exception @EXC_NOMEM@ is raised. + */ + +void *sub_alloc(size_t s) +{ + int bin = SUB_BIN(s); + void *p; + + /* --- Handle oversize blocks --- */ + + if (bin >= SUB_BINS) + return (xmalloc(s)); + + /* --- If the bin is empty, find some memory --- */ + + if (!sub__bins[bin]) { + char *p, *q; + + p = xmalloc(sub__sizes[bin]); + q = p + sub__sizes[bin]; + + s = SUB_BINSZ(bin); + + q -= s; + *(void **)q = 0; + + while (q > p) { + q -= s; + *(void **)q = q + s; + } + + sub__bins[bin] = p; + } + + /* --- Extract the first block in the list --- */ + + p = sub__bins[bin]; + sub__bins[bin] = *(void **)p; + return (p); +} + +/* --- @sub_free@ --- * + * + * Arguments: @void *p@ = address of block to free + * @size_t s@ = size of block + * + * Returns: --- + * + * Use: Frees a block allocated by @sub_alloc@. + */ + +void sub_free(void *p, size_t s) +{ + int bin = SUB_BIN(s); + + if (bin >= SUB_BINS) + free(p); + else { + *(void **)p = sub__bins[bin]; + sub__bins[bin] = p; + } +} + +/* --- @sub_init@ --- * + * + * Arguments: --- + * + * Returns: --- + * + * Use: Initialises the magic allocator. + */ + +void sub_init(void) +{ + int i; + + /* --- Initialise the sizes bins --- */ + + for (i = 1; i < SUB_BINS; i++) { + sub__sizes[i] = ((SUB_CHUNK + SUB_BINSZ(i) - 1) / + SUB_BINSZ(i) * SUB_BINSZ(i)); + } +} + +/*----- Debugging code ----------------------------------------------------*/ + +#ifdef TEST_RIG + +#define BLOCKS 1024 +#define SIZE_MAX 2048 +#define ITERATIONS 500000 + +int main(void) +{ + static void *block[BLOCKS]; + static size_t size[BLOCKS]; + size_t allocced = 0; + int i; + long count; + + sub_init(); + + for (count = 0; count < ITERATIONS; count++) { + i = rand() % BLOCKS; + if (block[i]) { + sub_free(block[i], size[i]); + block[i] = 0; + allocced -= size[i]; + } else { + block[i] = sub_alloc(size[i] = + rand() % (SUB_MAXBIN - 128) + 128); + allocced += size[i]; + memset(block[i], 0, size[i]); /* trample allocated storage */ + } + } + + return (0); +} + +#endif + +/*----- That's all, folks -------------------------------------------------*/ diff --git a/sub.h b/sub.h new file mode 100644 index 0000000..e7bd633 --- /dev/null +++ b/sub.h @@ -0,0 +1,127 @@ +/* -*-c-*- + * + * $Id: sub.h,v 1.1 1998/06/17 23:44:42 mdw Exp $ + * + * Allocation of known-size blocks + * + * (c) 1998 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of the mLib utilities library. + * + * mLib is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * mLib is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with mLib; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: sub.h,v $ + * Revision 1.1 1998/06/17 23:44:42 mdw + * Initial revision + * + */ + +#ifndef SUB_H +#define SUB_H + +#ifdef __cplusplus + extern "C" { +#endif + +/*----- Required header files ---------------------------------------------*/ + +#include + +#ifndef ALLOC_H +# include "alloc.h" +#endif + +/*----- Functions provided ------------------------------------------------*/ + +/* --- @sub_alloc@ --- * + * + * Arguments: @size_t s@ = size of chunk wanted + * + * Returns: Pointer to a block at least as large as the one wanted. + * + * Use: Allocates a small block of memory. If there is no more + * memory left, the exception @EXC_NOMEM@ is raised. + */ + +#ifdef TRACK_ENABLE +# define sub_alloc(s) xmalloc(s) +#else +void *sub_alloc(size_t s); +#endif + +/* --- @sub_free@ --- * + * + * Arguments: @void *p@ = address of block to free + * @size_t s@ = size of block + * + * Returns: --- + * + * Use: Frees a block allocated by @sub_alloc@. + */ + +#ifdef TRACK_ENABLE +# define sub_free(p, s) free(p) +#else +void sub_free(void *p, size_t s); +#endif + +/* --- @CREATE@ --- * + * + * Arguments: @type@ = type of object required; must be passable to + * @sizeof@ + * + * Returns: Pointer to a block sufficiently big to hold an object of the + * named type. + * + * Use: Allocates a block of the required type. + */ + +#define CREATE(type) sub_alloc(sizeof(type)) + +/* --- @DESTROY@ --- * + * + * Arguments: @void *p@ = pointer to an object + * + * Returns: --- + * + * Use: Frees the thing pointed to by @p@. + */ + +#define DESTROY(p) sub_free(p, sizeof(*p)) + +/* --- @sub_init@ --- * + * + * Arguments: --- + * + * Returns: --- + * + * Use: Initialises the magic allocator. + */ + +void sub_init(void); + +/*----- That's all, folks -------------------------------------------------*/ + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/sym.c b/sym.c new file mode 100644 index 0000000..2cdbc24 --- /dev/null +++ b/sym.c @@ -0,0 +1,624 @@ +/* -*-c-*- + * + * $Id: sym.c,v 1.1 1998/06/17 23:44:42 mdw Exp $ + * + * Symbol table management + * + * (c) 1998 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of the mLib utilities library. + * + * mLib is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * mLib is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with mLib; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: sym.c,v $ + * Revision 1.1 1998/06/17 23:44:42 mdw + * Initial revision + * + */ + +/*----- Header files ------------------------------------------------------*/ + +/* --- ANSI headers --- */ + +#include +#include +#include + +/* --- Local headers --- */ + +#include "alloc.h" +#include "crc32.h" +#include "exc.h" +#include "sub.h" +#include "sym.h" +#include "track.h" + +/*----- Tuning parameters -------------------------------------------------*/ + +/* --- Initial hash table size --- * + * + * This is the initial @mask@ value. It must be of the form %$2^n - 1$%, + * so that it can be used to mask of the bottom bits of a hash value. + */ + +#define SYM_INITSZ 255 /* Size of a new hash table */ + +/* --- Maximum load factor --- * + * + * This parameter controls how much the table has to be loaded before the + * table is extended. The number of elements %$n$%, the number of bins %$b$% + * and the limit %$l$% satisfy the relation %$n < bl$%; if a new item is + * added to the table and this relation is found to be false, the table is + * doubled in size. + * + * The current function gives %$l = {3n \over 4}$%, which appears to be + * reasonable on the face of things. + */ + +#define SYM_LIMIT(n) (((n) * 3) >> 2) /* Load factor for growing table */ + +/*----- Useful macros -----------------------------------------------------*/ + +#define SYM_NAME(sy) \ + ((sy)->len > SYM_BUFSZ ? (sy)->name.p : (sy)->name.b) + +/*----- Main code ---------------------------------------------------------*/ + +/* --- @sym_createTable@ --- * + * + * Arguments: @sym_table *t@ = symbol table to initialise + * + * Returns: --- + * + * Use: Initialises the given symbol table. Raises @EXC_NOMEM@ if + * there isn't enough memory. + */ + +void sym_createTable(sym_table *t) +{ + size_t i; + + TRACK_CTX("symbol table creation"); + TRACK_PUSH; + + t->mask = SYM_INITSZ; + t->c = SYM_LIMIT(SYM_INITSZ); + t->a = xmalloc((t->mask + 1) * sizeof(sym_base *)); + + for (i = 0; i < SYM_INITSZ + 1; i++) + t->a[i] = 0; + + TRACK_POP; +} + +/* --- @sym_destroyTable@ --- * + * + * Arguments: @sym_table *t@ = pointer to symbol table in question + * + * Returns: --- + * + * Use: Destroys a symbol table, freeing all the memory it used to + * occupy. + */ + +void sym_destroyTable(sym_table *t) +{ + size_t i; + sym_base *p, *q; + + TRACK_CTX("symbol table deletion"); + TRACK_PUSH; + + for (i = 0; i <= t->mask; i++) { + p = t->a[i]; + while (p) { + q = p->next; + if (p->len > SYM_BUFSZ) + sub_free(p->name.p, p->len); + free(p); + p = q; + } + } + free(t->a); + + TRACK_POP; +} + +/* --- @sym_find@ --- * + * + * Arguments: @sym_table *t@ = pointer to symbol table in question + * @const char *n@ = pointer to symbol table to look up + * @long l@ = length of the name string or negative to measure + * @size_t sz@ = size of desired symbol object, or zero + * @unsigned *f@ = pointer to a flag, or null. + * + * Returns: The address of a @sym_base@ structure, or null if not found + * and @sz@ is zero. + * + * Use: Looks up a symbol in a given symbol table. The name is + * passed by the address of its first character. The length + * may be given, in which case the name may contain arbitrary + * binary data, or it may be given as a negative number, in + * which case the length of the name is calculated as + * @strlen(n) + 1@. + * + * The return value is the address of a pointer to a @sym_base@ + * block (which may have other things on the end, as above). If + * the symbol could be found, the return value points to the + * symbol block. If the symbol wasn't there, then if @sz@ is + * nonzero, a new symbol is created and its address is returned; + * otherwise a null pointer is returned. The exception + * @EXC_NOMEM@ is raised if the block can't be allocated. + * + * The value of @*f@ indicates whether a new symbol entry was + * created: a nonzero value indicates that an old value was + * found. + */ + +void *sym_find(sym_table *t, const char *n, long l, size_t sz, unsigned *f) +{ + unsigned long hash; /* Hash value for user's name */ + size_t len = l < 0 ? strlen(n) + 1 : l; /* Find length of user's name */ + sym_base *bin; /* Bin containing our item */ + sym_base *p, *q; /* Pointer wandering through list */ + + /* --- Find the correct bin --- */ + + CRC32(hash, 0, n, len); /* Find hash value for this name */ + bin = p = (sym_base *)(t->a + (hash & t->mask)); + + /* --- Search the bin list --- */ + + while (p->next) { + if (hash == p->next->hash && /* Check full hash values first */ + len == p->next->len && /* Then compare string lengths */ + !memcmp(n, SYM_NAME(p->next), len)) /* And finally compare names */ + { + /* --- Found a match --- * + * + * As a minor, and probably pointless, tweak, move the item to the + * front of its bin list. + */ + + q = p->next; /* Find the actual symbol block */ + p->next = q->next; /* Extract block from bin list */ + q->next = bin->next; /* Set up symbol's next pointer */ + bin->next = q; /* And reinsert the block */ + + /* --- Return the block --- */ + + if (f) *f = 1; /* Didn't fail to find the item */ + return (q); /* And return the block */ + } + + p = p->next; /* Move onto the next item */ + } + + /* --- Couldn't find the item there --- */ + + if (f) *f = 0; /* Failed to find the block */ + if (!sz) return (0); /* Return zero if not creating */ + + /* --- Create a new symbol block and initialise it --- */ + + { + TRACK_CTX("new symbol creation"); + TRACK_PUSH; + + p = xmalloc(sz); /* Create a new symbol block */ + p->next = bin->next; /* Set up the next pointer */ + p->hash = hash; /* Set up the hash value too */ + p->len = len; /* And set up the string length */ + if (len <= SYM_BUFSZ) + memcpy(p->name.b, n, len); /* And copy the string over */ + else { + TRY { + p->name.p = sub_alloc(len); /* Allocate a block for the name */ + memcpy(p->name.p, n, len); /* And copy the string over */ + } CATCH { + free(p); + TRACK_POP; + RETHROW; + } END_TRY; + } + + TRACK_POP; + } + + bin->next = p; /* Put the pointer into the bin */ + + /* --- Consider growing the array --- */ + + if (!--t->c) { + unsigned long m = t->mask + 1; /* Next set bit in has word */ + sym_base *p, *q, *r; /* More useful pointers */ + size_t i, lim; /* Loop counter and limit */ + + TRACK_CTX("symbol table extension"); + TRACK_PUSH; + + /* --- Update values in the anchor block --- */ + + TRY { + t->a = xrealloc(t->a, (t->mask + 1) * 2 * sizeof(sym_base *)); + } CATCH switch (exc_type) { + case EXC_NOMEM: + TRACK_POP; + return (p); + default: + TRACK_POP; + RETHROW; + } END_TRY; + + t->c = SYM_LIMIT(t->mask + 1); /* Set load value */ + t->mask = (t->mask + 1) * 2 - 1; /* Set the new mask value */ + + /* --- Now wander through the table rehashing things --- * + * + * This loop is very careful to avoid problems with aliasing. The items + * are dealt with from the end backwards to avoid overwriting bins before + * they've been processed. + */ + + lim = (t->mask + 1) >> 1; + for (i = 0; i < lim; i++) { + + /* --- Some initialisation --- */ + + r = t->a[i]; /* Find the list we're dissecting */ + p = (sym_base *)(t->a + i); /* Find bit-clear list */ + q = (sym_base *)(t->a + i + lim); /* And the bit-set lsit */ + + /* --- Now go through the @r@ list --- */ + + while (r) { + if (r->hash & m) /* Is the next bit set? */ + q = q->next = r; /* Yes, so fit up previous link */ + else + p = p->next = r; /* No, so fit up previous link */ + r = r->next; /* Move onto the next item */ + } + p->next = q->next = 0; /* Null terminate the new lists */ + } + + TRACK_POP; + } + + /* --- Finished that, so return the new symbol block --- */ + + return (p); +} + +/* --- @sym_remove@ --- * + * + * Arguments: @sym_table *i@ = pointer to a symbol table object + * @void *b@ = pointer to symbol table entry + * + * Returns: --- + * + * Use: Removes the object from the symbol table. The space occupied + * by the object and its name is freed; anything else attached + * to the entry should already be gone by this point. + */ + +void sym_remove(sym_table *t, void *b) +{ + /* --- A quick comment --- * + * + * Since the @sym_base@ block contains the hash, finding the element in the + * bin list is really quick -- it's not worth bothering with things like + * doubly linked lists. + */ + + sym_base *p = b; + sym_base *bin = (sym_base *)(t->a + (p->hash & t->mask)); + + /* --- Find the item in the bin list --- */ + + while (bin->next) { + if (bin->next == p) + break; + bin = bin->next; + } + if (!bin->next) + return; + + /* --- Now just remove the item from the list and free it --- * + * + * Oh, and bump the load counter. + */ + + bin->next = p->next; + if (p->len > SYM_BUFSZ) + sub_free(p->name.p, p->len); + free(p); + t->c++; +} + +/* --- @sym_createIter@ --- * + * + * Arguments: @sym_iter *i@ = pointer to an iterator object + * @sym_table *t@ = pointer to a symbol table object + * + * Returns: --- + * + * Use: Creates a new symbol table iterator which may be used to + * iterate through a symbol table. + */ + +void sym_createIter(sym_iter *i, sym_table *t) +{ + i->t = t; + i->i = 0; + i->n = 0; +} + +/* --- @sym_next@ --- * + * + * Arguments: @sym_iter *i@ = pointer to iterator object + * + * Returns: Pointer to the next symbol found, or null when finished. + * + * Use: Returns the next symbol from the table. Symbols are not + * returned in any particular order. + */ + +void *sym_next(sym_iter *i) +{ + sym_base *p; + + /* --- Find the next item --- */ + + while (!i->n) { + if (i->i > i->t->mask) + return (0); + i->n = i->t->a[i->i++]; + } + + /* --- Update the iterator block --- */ + + p = i->n; + i->n = p->next; + + /* --- Done --- */ + + return (p); +} + +/*----- Symbol table test code --------------------------------------------*/ + +#ifdef TEST_RIG + +#include +#include + +typedef struct sym_word { + sym_base base; + size_t i; +} sym_word; + + +/* --- What it does --- * + * + * Reads the file /usr/dict/words (change to some other file full of + * interesting and appropriate bits of text to taste) into a big buffer and + * picks apart into lines. Then picks lines at random and enters them into + * the symbol table. + */ + +int main(void) +{ + char *buff, *p, *lim; + size_t sz, done; + FILE *fp; + int i; + char **line; + sym_word **flag; + sym_table tbl; + int entries; + + /* --- Initialise for reading the file --- */ + + sz = BUFSIZ; + buff = xmalloc(sz + 1); + done = 0; + sub_init(); + + if ((fp = fopen("/usr/dict/words", "r")) == 0) + fprintf(stderr, "buggered ;-( (%s)\n", strerror(errno)); + + /* --- Read buffers of text --- * + * + * Read a buffer. If more to come, double the buffer size and try again. + * This is the method I recommended to comp.lang.c, so I may as well try + * it. + */ + + for (;;) { + i = fread(buff + done, 1, sz - done, fp); + done += i; + if (done != sz) + break; + sz <<= 1; + buff = xrealloc(buff, sz + 1); + } + + /* --- Count the lines --- */ + + lim = buff + done; + + sz = 1; + for (p = buff; p < lim; p++) + if (*p == '\n') sz++; + + /* --- Build a table of line starts --- */ + + line = xmalloc(sz * sizeof(char *)); + i = 0; + line[i++] = buff; + for (p = buff; p < lim; p++) + if (*p == '\n') *p = 0, line[i++] = p + 1; + *lim = 0; + + /* --- Build a table of lines --- * + * + * This reverses the mapping which the symbol table performs, so that its + * accuracy can be tested. + */ + + flag = xmalloc(sz * sizeof(sym_word *)); + for (i = 0; i < sz; i++) + flag[i] = 0; + entries = 0; + + sym_createTable(&tbl); + + for (;;) { + i = (unsigned)rand() % sz; + + switch (rand() % 5) + { + case 0: { + sym_word *w; + + printf("find `%s'\n", line[i]); + if ((rand() & 1023) == 0) { + putchar('.'); fflush(stdout); + } + + w = sym_find(&tbl, line[i], -1, 0, 0); + if (w != flag[i]) + printf("*** error: find `%s' gave %p not %p\n", + line[i], (void *)w, (void *)flag[i]); + else if (w && w->i != i) + printf("*** error: find sym for `%s' gives index %i not %i\n", + line[i], w->i, i); + } break; + + case 1: { + unsigned f; + sym_word *w; + + printf("create `%s'\n", line[i]); + if ((rand() & 1023) == 0) { + putchar('+'); fflush(stdout); + } + + w = sym_find(&tbl, line[i], -1, sizeof(sym_word), &f); + if (f) + { + if (w != flag[i]) + printf("*** error: create `%s' gave %p not %p\n", + line[i], (void *)w, (void *)flag[i]); + else if (w && w->i != i) + printf("*** error: create sym for `%s' gives index %i not %i\n", + line[i], w->i, i); + } + else + { + if (flag[i]) + printf("*** error: create `%s' gave new block, should be %p\n", + line[i], (void *)flag[i]); + else { + flag[i] = w; + w->i = i; + entries++; + } + } + } break; + + case 2: { + sym_iter it; + sym_word *w, **ntbl; + int v; + + if (!entries) + break; + v = (rand() % entries) == 0; + if (!v) + break; + printf("\niterated %i entries\n", entries); + break; + + printf("iterate\n"); + + ntbl = xmalloc(sz * sizeof(sym_word *)); + memcpy(ntbl, flag, sz * sizeof(sym_word *)); + sym_createIter(&it, &tbl); + + while ((w = sym_next(&it)) != 0) { + if (ntbl[w->i] == 0) + printf("*** error: iterate returned duff item %i\n", w->i); + else + ntbl[w->i] = 0; + } + + for (i = 0; i < sz; i++) + if (ntbl[i]) printf("*** error: iterate didn't return item %i\n", + i); + free(ntbl); + } break; + + case 3: { + sym_base *b; + int v = rand() & 255 ? 0 : 1; + break; + + printf("dump\n"); + + for (i = 0; i <= tbl.mask; i++) { + if (!tbl.a[i]) continue; + if (v) printf(" %i: ", i); + b = tbl.a[i]; + while (b) { + if ((b->hash & tbl.mask) != i) + printf("*** error: bad hash value found"); + if (v) printf("`%s'(%08lx:%lu) ", + line[((sym_word *)b)->i], + b->hash, + b->hash & tbl.mask); + b = b->next; + } + if (v) putchar('\n'); + } + } break; + + case 4: { + if (flag[i]) { + printf("remove `%s'\n", SYM_NAME(&flag[i]->base)); + if ((rand() & 1023) == 0) { + putchar('-'); fflush(stdout); + } + sym_remove(&tbl, flag[i]); + flag[i] = 0; + entries--; + } + } break; + } + + } + + return (0); +} + +#endif + +/*----- That's all, folks -------------------------------------------------*/ diff --git a/sym.h b/sym.h new file mode 100644 index 0000000..35a3fa6 --- /dev/null +++ b/sym.h @@ -0,0 +1,197 @@ +/* -*-c-*- + * + * $Id: sym.h,v 1.1 1998/06/17 23:44:42 mdw Exp $ + * + * Symbol table management + * + * (c) 1998 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of the mLib utilities library. + * + * mLib is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * mLib is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with mLib; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: sym.h,v $ + * Revision 1.1 1998/06/17 23:44:42 mdw + * Initial revision + * + */ + +#ifndef SYM_H +#define SYM_H + +#ifdef __cplusplus + extern "C" { +#endif + +/*----- Required headers --------------------------------------------------*/ + +#include + +/*----- Type definitions --------------------------------------------------*/ + +/* --- Symbol table --- * + * + * A @sym_table@ contains the information needed to manage a symbol table. + * Users shouldn't fiddle with this information directly, but it needs to be + * here so that objects of the correct type can be created. + */ + +typedef struct sym_table { + unsigned long mask; /* Bit mask for hashing purposes */ + size_t c; /* Down counter for growing table */ + struct sym_base **a; /* Array of hash bins */ +} sym_table; + +/* --- A symbol table entry --- * + * + * I don't care what actually gets stored in symbol entries because I don't + * create them: that's the responsibility of my client. All I care about + * here is that whatever gets passed to me is a structure whose first member + * is a @sym_base@. The ANSI guarantees about structure layout are + * sufficient to allow me to manipulate such objects. + */ + +#define SYM_BUFSZ 16 /* Size of local string buffer */ + +typedef struct sym_base { + struct sym_base *next; /* Next symbol in hash bin */ + unsigned long hash; /* Hash value for symbol's name */ + union { + char *p; /* Pointer to name string */ + char b[SYM_BUFSZ]; /* Buffer containing a short name */ + } name; /* Name of this symbol */ + size_t len; /* Length of the symbol's name */ +} sym_base; + +/* --- An iterator block --- */ + +typedef struct sym_iter { + sym_table *t; /* Symbol table being iterated */ + sym_base *n; /* Address of next item to return */ + size_t i; /* Index of next hash bin to use */ +} sym_iter; + +/*----- External functions ------------------------------------------------*/ + +/* --- @sym_createTable@ --- * + * + * Arguments: @sym_table *t@ = symbol table to initialise + * + * Returns: --- + * + * Use: Initialises the given symbol table. Raises @EXC_NOMEM@ if + * there isn't enough memory. + */ + +extern void sym_createTable(sym_table */*t*/); + +/* --- @sym_destroyTable@ --- * + * + * Arguments: @sym_table *t@ = pointer to symbol table in question + * + * Returns: --- + * + * Use: Destroys a symbol table, freeing all the memory it used to + * occupy. + */ + +extern void sym_destroyTable(sym_table */*t*/); + +/* --- @sym_find@ --- * + * + * Arguments: @sym_table *t@ = pointer to symbol table in question + * @const char *n@ = pointer to symbol table to look up + * @long l@ = length of the name string or negative to measure + * @size_t sz@ = size of desired symbol object, or zero + * @unsigned *f@ = pointer to a flag, or null. + * + * Returns: The address of a @sym_base@ structure, or null if not found + * and @sz@ is zero. + * + * Use: Looks up a symbol in a given symbol table. The name is + * passed by the address of its first character. The length + * may be given, in which case the name may contain arbitrary + * binary data, or it may be given as a negative number, in + * which case the length of the name is calculated as + * @strlen(n)@. + * + * The return value is the address of a pointer to a @sym_base@ + * block (which may have other things on the end, as above). If + * the symbol could be found, the return value points to the + * symbol block. If the symbol wasn't there, then if @sz@ is + * nonzero, a new symbol is created and its address is returned; + * otherwise a null pointer is returned. The exception + * @EXC_NOMEM@ is raised if the block can't be allocated. + * + * The value of @*f@ indicates whether a new symbol entry was + * created: a nonzero value indicates that an old value was + * found. + */ + +extern void *sym_find(sym_table */*t*/, const char */*n*/, long /*l*/, + size_t /*sz*/, unsigned */*f*/); + +/* --- @sym_remove@ --- * + * + * Arguments: @sym_table *i@ = pointer to a symbol table object + * @void *b@ = pointer to symbol table entry + * + * Returns: --- + * + * Use: Removes the object from the symbol table. The space occupied + * by the object and its name is freed; anything else attached + * to the entry should already be gone by this point. + */ + +extern void sym_remove(sym_table */*t*/, void */*b*/); + +/* --- @sym_createIter@ --- * + * + * Arguments: @sym_iter *i@ = pointer to an iterator object + * @sym_table *t@ = pointer to a symbol table object + * + * Returns: --- + * + * Use: Creates a new symbol table iterator which may be used to + * iterate through a symbol table. + */ + +extern void sym_createIter(sym_iter */*i*/, sym_table */*t*/); + +/* --- @sym_next@ --- * + * + * Arguments: @sym_iter *i@ = pointer to iterator object + * + * Returns: Pointer to the next symbol found, or null when finished. + * + * Use: Returns the next symbol from the table. Symbols are not + * returned in any particular order. + */ + +extern void *sym_next(sym_iter */*i*/); + +/*----- That's all, folks -------------------------------------------------*/ + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/testrig.c b/testrig.c new file mode 100644 index 0000000..fc8d21d --- /dev/null +++ b/testrig.c @@ -0,0 +1,401 @@ +/* -*-c-*- + * + * $Id: testrig.c,v 1.1 1998/06/17 23:44:42 mdw Exp $ + * + * Generic test driver + * + * (c) 1998 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of the mLib utilities library. + * + * mLib is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * mLib is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with mLib; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: testrig.c,v $ + * Revision 1.1 1998/06/17 23:44:42 mdw + * Initial revision + * + */ + +/*----- Header files ------------------------------------------------------*/ + +#include +#include +#include +#include +#include + +#include "dstr.h" +#include "report.h" +#include "quis.h" +#include "testrig.h" + +/*----- Static variables --------------------------------------------------*/ + +static dstr test__tok; + +enum { + tok_eof = 0x100, + tok_word +}; + +/*----- Main code ---------------------------------------------------------*/ + +/* --- @test__decode@ --- * + * + * Arguments: @int tok@ = token type to decode + * + * Returns: Pointer to a textual representation of the token. + * + * Use: Produces a readable representation of a token. + */ + +static const char *test__decode(int tok) +{ + static char buf[4]; + + switch (tok) { + case tok_eof: + return (""); + case tok_word: + return (test__tok.buf); + default: + buf[0] = tok; + buf[1] = 0; + return (buf); + } + return (""); +} + +/* --- @test__gettok@ --- * + * + * Arguments: @FILE *fp@ = file handle to read from + * + * Returns: Type of token read. + * + * Use: Reads a token from the input stream. + */ + +static int test__gettok(FILE *fp) +{ + int ch; + + /* --- Clear the token accumulator --- */ + + dstr_reset(&test__tok); + + /* --- Prime the lookahead character --- */ + +again: + ch = getc(fp); + + /* --- Skip leading whitespace --- */ + + while (isspace((unsigned char)ch)) + ch = getc(fp); + + /* --- Trap some special characters --- */ + + switch (ch) { + + /* --- Comments --- */ + + case '#': + do ch = getc(fp); while (ch != EOF && ch != '\n'); + goto again; + + /* --- End of file --- */ + + case EOF: + return (tok_eof); + + /* --- Quote characters --- */ + + case '`': + ch = '\''; + case '\'': + case '\"': { + int quote = ch; + + for (;;) { + ch = getc(fp); + if (ch == EOF || ch == quote) + break; + if (ch == '\\') { + ch = getc(fp); + if (ch == EOF) + ch = '\\'; + } + DPUTC(&test__tok, ch); + } + DPUTZ(&test__tok); + return (tok_word); + } + + /* --- Anything else is either a word or self-delimiting --- */ + + default: + if (isalnum((unsigned char)ch)) { + for (;;) { + DPUTC(&test__tok, ch); + ch = getc(fp); + if (ch == EOF || + ch == ';' || + ch == '\"' || ch == '\'' || ch == '`' || + isspace((unsigned char)ch)) + break; + if (ch == '\\') { + ch = getc(fp); + if (ch == EOF) + ch = '\\'; + } + } + ungetc(ch, fp); + DPUTZ(&test__tok); + return (tok_word); + } else + return (ch); + } +} + +/* --- @type_hex@ --- */ + +static void cvt_hex(const char *s, dstr *d) +{ + while (s[0] && s[1]) { + int x = s[0], y = s[1]; + if ('0' <= x && x <= '9') x -= '0'; + else if ('A' <= x && x <= 'F') x -= 'A' - 10; + else if ('a' <= x && x <= 'f') x -= 'a' - 10; + else x = 0; + if ('0' <= y && y <= '9') y -= '0'; + else if ('A' <= y && y <= 'F') y -= 'A' - 10; + else if ('a' <= y && y <= 'f') y -= 'a' - 10; + else y = 0; + DPUTC(d, (x << 4) + y); + s += 2; + } +} + +static void dump_hex(dstr *d, FILE *fp) +{ + const char *p, *q; + for (p = d->buf, q = p + d->len; p < q; p++) + fprintf(fp, "%02x", *(unsigned char *)p); +} + +test_type type_hex = { cvt_hex, dump_hex }; + +/* --- @type_string@ --- */ + +static void cvt_string(const char *s, dstr *d) +{ + DPUTS(d, s); +} + +static void dump_string(dstr *d, FILE *fp) +{ + dstr_write(d, fp); +} + +test_type type_string = { cvt_string, dump_string }; + +/* --- @type_int@ --- */ + +static void cvt_int(const char *s, dstr *d) +{ + DENSURE(d, sizeof(long)); + sscanf(s, "%i", (int *)d->buf + d->len); +} + +static void dump_int(dstr *d, FILE *fp) +{ + fprintf(fp, "%i", *(int *)(d->buf)); +} + +test_type type_int = { cvt_int, dump_int }; + +/* --- @test_run@ --- * + * + * Arguments: @int argc@ = number of command line arguments + * @char *argv[]@ = pointer to command line arguments + * @const test_chunk chunk[]@ = pointer to chunk definitions + * @const char *vec@ = name of default test vector file + * + * Returns: Doesn't. + * + * Use: Runs a set of test vectors to ensure that a component is + * working properly. + */ + +void test_run(int argc, char *argv[], + const test_chunk chunk[], + const char *vec) +{ + FILE *fp; + int i; + const test_chunk *cch; + dstr dv[TEST_FIELDMAX]; + int fail = 0, ok = 1; + int sofar = 0; + + /* --- Silly bits of initialisation --- */ + + ego(argv[0]); + + for (i = 0; i < TEST_FIELDMAX; i++) + dstr_create(&dv[i]); + + /* --- Parse command line arguments --- */ + + { + const char *p = 0; + + i = 0; + for (;;) { + if (!p || !*p) { + if (i >= argc - 1) + break; + p = argv[++i]; + if (strcmp(p, "--") == 0) { + i++; + break; + } + if (p[0] != '-' || p[1] == 0) + break; + p++; + } + switch (*p++) { + case 'h': + printf("%s test driver\n" + "Usage: %s [-f FILENAME]\n", QUIS, QUIS); + exit(0); + case 'f': + if (!*p) { + if (i >= argc - 1) + die(1, "option `-f' expects an argument"); + p = argv[++i]; + } + vec = p; + p = 0; + break; + default: + die(1, "option `-%c' unknown", p[-1]); + break; + } + } + } + + /* --- Start parsing from the file --- */ + + if ((fp = fopen(vec, "r")) == 0) + die(1, "couldn't open test vector file `%s': %s", vec, strerror(errno)); + + for (;;) { + int tok = test__gettok(fp); + + /* --- This is a reasonable place to stop --- */ + + if (tok == tok_eof) + break; + + /* --- Pick out the chunk name --- */ + + if (tok != tok_word) + die(1, "expected ; found `%s'", test__decode(tok)); + + /* --- Find the right chunk block --- */ + + for (cch = chunk; ; cch++) { + if (!cch->name) + goto skip_chunk; + if (strcmp(test__tok.buf, cch->name) == 0) + break; + } + + /* --- Past the open brace to the first chunk --- */ + + if ((tok = test__gettok(fp)) != '{') + die(1, "expected '{'; found `%s'", test__decode(tok)); + + /* --- Start on the test data now --- */ + + printf("%s: ", cch->name); + fflush(stdout); + sofar = 0; + ok = 1; + + for (;;) { + tok = test__gettok(fp); + + /* --- Accept a close brace --- */ + + if (tok == '}') + break; + + /* --- Otherwise I expect a list of words --- */ + + for (i = 0; cch->f[i]; i++) { + dstr_reset(&dv[i]); + if (tok != tok_word) + die(1, "expected ; found `%s'", test__decode(tok)); + cch->f[i]->cvt(test__tok.buf, &dv[i]); + tok = test__gettok(fp); + } + + /* --- And a terminating semicolon --- */ + + if (tok != ';') + die(1, "expected `;'; found `%s'", test__decode(tok)); + + /* --- Run the test code --- */ + + if (!cch->test(dv)) { + printf("%s: ", cch->name); + for (i = 0; i < sofar; i++) putchar('.'); + fail = 1; ok = 0; + } + sofar++; + putchar('.'); + fflush(stdout); + } + + puts(ok ? " ok" : " failed"); + fflush(stdout); + continue; + + skip_chunk: + if ((tok = test__gettok(fp)) != '{') + die(1, "expected '{'; found `%s'", test__decode(tok)); + for (;;) { + tok = test__gettok(fp); + if (tok == '}') + break; + while (tok == tok_word) + tok = test__gettok(fp); + if (tok != ';') + die(1, "expected `;'; found `%s'", test__decode(tok)); + } + } + + exit(fail); +} + +/*----- That's all, folks -------------------------------------------------*/ diff --git a/testrig.h b/testrig.h new file mode 100644 index 0000000..c8fa10b --- /dev/null +++ b/testrig.h @@ -0,0 +1,102 @@ +/* -*-c-*- + * + * $Id: testrig.h,v 1.1 1998/06/17 23:44:42 mdw Exp $ + * + * Generic test driver + * + * (c) 1998 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of the mLib utilities library. + * + * mLib is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * mLib is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with mLib; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: testrig.h,v $ + * Revision 1.1 1998/06/17 23:44:42 mdw + * Initial revision + * + */ + +#ifndef TESTER_H +#define TESTER_H + +#ifdef __cplusplus + extern "C" { +#endif + +/*----- Header files ------------------------------------------------------*/ + +#include + +#include "dstr.h" + +/*----- Magical numbers ---------------------------------------------------*/ + +#define TEST_FIELDMAX 16 /* Maximum fields in a line */ + +/*----- Data structures ---------------------------------------------------*/ + +/* --- Test field definition --- */ + +typedef struct test_type { + void (*cvt)(const char *buf, dstr *d); /* Conversion function */ + void (*dump)(dstr *d, FILE *fp); /* Dump function */ +} test_type; + +/* --- Test chunk definition --- */ + +typedef struct test_chunk { + const char *name; /* Name of this chunk */ + int (*test)(dstr dv[]); /* Test verification function */ + test_type *f[TEST_FIELDMAX]; /* Field definitions */ +} test_chunk; + +/*----- Predefined data types ---------------------------------------------*/ + +extern test_type type_hex; +extern test_type type_string; +extern test_type type_int; + +/*----- Functions provided ------------------------------------------------*/ + +/* --- @test_run@ --- * + * + * Arguments: @int argc@ = number of command line arguments + * @char *argv[]@ = pointer to command line arguments + * @const test_chunk chunk[]@ = pointer to chunk definitions + * @const char *def@ = name of default test vector file + * + * Returns: Doesn't. + * + * Use: Runs a set of test vectors to ensure that a component is + * working properly. + */ + +extern void test_run(int /*argc*/, char */*argv*/[], + const test_chunk /*chunk*/[], + const char */*def*/); + +/*----- That's all, folks -------------------------------------------------*/ + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/trace.c b/trace.c new file mode 100644 index 0000000..c99c682 --- /dev/null +++ b/trace.c @@ -0,0 +1,178 @@ +/* -*-c-*- + * + * $Id: trace.c,v 1.1 1998/06/17 23:44:42 mdw Exp $ + * + * Tracing functions for debugging + * + * (c) 1998 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of the mLib utilities library. + * + * mLib is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * mLib is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with mLib; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: trace.c,v $ + * Revision 1.1 1998/06/17 23:44:42 mdw + * Initial revision + * + */ + +/*----- Header files ------------------------------------------------------*/ + +/* --- ANSI headers --- */ + +#include +#include +#include +#include +#include + +/* --- Local headers --- */ + +#include "quis.h" +#include "trace.h" + +/*----- Private state information -----------------------------------------*/ + +static FILE *trace__fp = 0; /* Where does debugging go? */ +static unsigned int trace__lvl = 0; /* How much tracing gets done? */ + +/*----- Functions provided ------------------------------------------------*/ + +/* --- @trace@ --- * + * + * Arguments: @unsigned int l@ = trace level for output + * @const char *f@ = a @printf@-style format string + * @...@ = other arguments + * + * Returns: --- + * + * Use: Reports a message to the trace output. + */ + +void trace(unsigned int l, const char *f, ...) +{ + va_list ap; + if ((l & tracing()) == 0) + return; + va_start(ap, f); + fprintf(trace__fp, "*** %s: ", QUIS); + vfprintf(trace__fp, f, ap); + va_end(ap); + putc('\n', trace__fp); + fflush(trace__fp); +} + +/* --- @trace_block@ --- * + * + * Arguments: @unsigned int l@ = trace level for output + * @const char *s@ = some header string to write + * @const void *b@ = pointer to a block of memory to dump + * @size_t sz@ = size of the block of memory + * + * Returns: --- + * + * Use: Dumps the contents of a block to the trace output. + */ + +void trace_block(unsigned int l, const char *s, const void *b, size_t sz) +{ + const unsigned char *p = b; + size_t i; + unsigned long o = 0; + size_t c; + + /* --- Skip if the trace level is too high --- */ + + if ((l & tracing()) == 0) + return; + + /* --- Now start work --- */ + + fprintf(trace__fp, "*** %s: %s\n", QUIS, s); + + while (sz) { + fprintf(trace__fp, "*** %s: %08lx : ", QUIS, o); + for (i = 0; i < 8; i++) { + if (i < sz) + fprintf(trace__fp, "%02x ", p[i]); + else + fputs("** ", trace__fp); + } + fputs(": ", trace__fp); + for (i = 0; i < 8; i++) { + if (i < sz) + fputc(isprint(p[i]) ? p[i] : '.', trace__fp); + else + fputc('*', trace__fp); + } + fputc('\n', trace__fp); + c = (sz >= 8) ? 8 : sz; + sz -= c, p += c, o += c; + } + fflush(trace__fp); +} + +/* --- @trace_on@ --- * + * + * Arguments: @FILE *fp@ = a file to trace on + * @unsigned int l@ = trace level to set + * + * Returns: --- + * + * Use: Enables tracing to a file. + */ + +void trace_on(FILE *fp, unsigned int l) +{ + trace__fp = fp; + if (!trace__lvl) + trace__lvl = l; +} + +/* --- @trace_setLevel@ --- * + * + * Arguments: @unsigned int l@ = trace level to set + * + * Returns: --- + * + * Use: Sets the tracing level. + */ + +void trace_setLevel(unsigned int l) +{ + trace__lvl = l; +} + +/* --- @tracing@ --- * + * + * Arguments: --- + * + * Returns: Zero if not tracing, tracing level if tracing. + * + * Use: Informs the caller whether tracing is enabled. + */ + +unsigned int tracing(void) +{ + return (trace__fp ? trace__lvl : 0u); +} + +/*----- That's all, folks -------------------------------------------------*/ diff --git a/trace.h b/trace.h new file mode 100644 index 0000000..a6ffabe --- /dev/null +++ b/trace.h @@ -0,0 +1,126 @@ +/* -*-c-*- + * + * $Id: trace.h,v 1.1 1998/06/17 23:44:42 mdw Exp $ + * + * Tracing functions for debugging + * + * (c) 1998 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of the mLib utilities library. + * + * mLib is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * mLib is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with mLib; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: trace.h,v $ + * Revision 1.1 1998/06/17 23:44:42 mdw + * Initial revision + * + */ + +#ifndef TRACE_H +#define TRACE_H + +#ifdef __cplusplus + extern "C" { +#endif + +#include + +/*----- Functions provided ------------------------------------------------*/ + +/* --- @trace@ --- * + * + * Arguments: @unsigned int l@ = trace level for output + * @const char *f@ = a @printf@-style format string + * @...@ = other arguments + * + * Returns: --- + * + * Use: Reports a message to the trace output. + */ + +extern void trace(unsigned int /*l*/, const char */*f*/, ...); + +/* --- @trace_block@ --- * + * + * Arguments: @unsigned int l@ = trace level for output + * @const char *s@ = some header string to write + * @const void *b@ = pointer to a block of memory to dump + * @size_t sz@ = size of the block of memory + * + * Returns: --- + * + * Use: Dumps the contents of a block to the trace output. + */ + +extern void trace_block(unsigned int /*l*/, const char */*s*/, + const void */*b*/, size_t /*sz*/); + +/* --- @trace_on@ --- * + * + * Arguments: @FILE *fp@ = a file to trace on + * @unsigned int l@ = trace level to set + * + * Returns: --- + * + * Use: Enables tracing to a file. + */ + +extern void trace_on(FILE */*fp*/, unsigned int /*l*/); + +/* --- @trace_setLevel@ --- * + * + * Arguments: @unsigned int l@ = trace level to set + * + * Returns: --- + * + * Use: Sets the tracing level. + */ + +extern void trace_setLevel(unsigned int /*l*/); + +/* --- @tracing@ --- * + * + * Arguments: --- + * + * Returns: Zero if not tracing, tracing level if tracing. + * + * Use: Informs the caller whether tracing is enabled. + */ + +extern unsigned int tracing(void); + +/*----- Tracing macros ----------------------------------------------------*/ + +#ifndef NTRACE +# define T(x) x +# define IF_TRACING(l, x) if ((l) & tracing()) x +#else +# define T(x) +# define IF_TRACING(l, x) +#endif + +/*----- That's all, folks -------------------------------------------------*/ + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/track.c b/track.c new file mode 100644 index 0000000..d11f956 --- /dev/null +++ b/track.c @@ -0,0 +1,288 @@ +/* -*-c-*- + * + * $Id: track.c,v 1.1 1998/06/17 23:44:42 mdw Exp $ + * + * Tracing functions for debugging + * + * (c) 1998 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of the mLib utilities library. + * + * mLib is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * mLib is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with mLib; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: track.c,v $ + * Revision 1.1 1998/06/17 23:44:42 mdw + * Initial revision + * + */ + +/*----- Header files ------------------------------------------------------*/ + +/* --- ANSI headers --- */ + +#include +#include +#include +#include +#include + +/* --- Local headers --- */ + +#include "trace.h" +#include "track.h" + +/*----- Type definitions --------------------------------------------------*/ + +/* --- A track block --- * + * + * This gets prefixed to every block I manage. + */ + +typedef union track__block { + struct { + union track__block *next; /* Link to previous block */ + union track__block *prev; /* Link to next block */ + size_t sz; /* Size of the block */ + const char *ctx; /* Pointer to allocating context */ + } x; /* Main data area */ + long double _ld; /* Long double for alignment */ + void *_p; /* Void pointer for alignment */ +} track__block; + +/*----- Private state -----------------------------------------------------*/ + +/* --- Tracking memory usage --- */ + +static unsigned int track__used = 0; /* Count of bytes occupied */ +static track__block *track__list; /* List of allocated blocks */ + +/* --- Trace level for verbose messages --- */ + +static unsigned int track__vLevel = 0; + +/* --- Context tracking --- */ + +static track_ctx track__baseContext = { + 0, "[unknown context]" +}; + +static track_ctx *track__context = &track__baseContext; + +/*----- Functions provided ------------------------------------------------*/ + +/* --- @track_setLevel@ --- * + * + * Arguments: @unsigned int l@ = tracing level for allocation messages + * + * Returns: --- + * + * Use: Sets the trace level for allocation messages. + */ + +void track_setLevel(unsigned int l) +{ + track__vLevel = l; +} + +/* --- @track_pushContext@ --- * + * + * Arguments: @track_ctx *ctx@ = context holder to push + * + * Returns: --- + * + * Use: Pushes the given context block onto the stack. + */ + +void track_pushContext(track_ctx *ctx) +{ + ctx->next = track__context; + track__context = ctx; +} + +/* --- @track_popContext@ --- * + * + * Arguments: @track_ctx *ctx@ = context holder to pop + * + * Returns: --- + * + * Use: Removes the given context block from the stack. + */ + +void track_popContext(track_ctx *ctx) +{ + track__context = ctx->next; +} + +/* --- @track_malloc@ --- * + * + * Arguments: @size_t sz@ = size requested + * + * Returns: Pointer to allocated space, or null + * + * Use: Allocates memory, and tracks how much is allocated. + */ + +void *track_malloc(size_t sz) +{ + track__block *q = (malloc)(sz + sizeof(track__block)); + if (q) { + track__used += sz; + if (track__vLevel) { + trace(track__vLevel, "(track) allocated %lu at %p in %s", + (unsigned long)sz, (void *)(q + 1), track__context->s); + } + q->x.sz = sz; + q->x.next = track__list; + q->x.prev = 0; + q->x.ctx = track__context->s; + if (q->x.next) + q->x.next->x.prev = q; + track__list = q; + return (q + 1); + } + return (0); +} + +/* --- @track_free@ --- * + * + * Arguments: @void *p@ = pointer to an allocated block + * + * Returns: --- + * + * Use: Frees memory, and tracks how much is still allocated. + */ + +void track_free(void *p) +{ + track__block *q; + + if (!p) + return; + q = (track__block *)p - 1; + if (track__vLevel) { + trace(track__vLevel, "(track) freed %lu at %p for %s in %s", + (unsigned long)q->x.sz, (void *)(q + 1), + q->x.ctx, track__context->s); + } + if (q->x.next) + q->x.next->x.prev = q->x.prev; + if (q->x.prev) + q->x.prev->x.next = q->x.next; + else + track__list = q->x.next; + track__used -= q->x.sz; + (free)(q); +} + +/* --- @track_realloc@ --- * + * + * Arguments: @void *p@ = pointer to an allocated block + * @size_t sz@ = how big it wants to be + * + * Returns: Pointer to the new block. + * + * Use: Reallocates a block, tracking how much memory is still + * available. + */ + +void *track_realloc(void *p, size_t sz) +{ + size_t osz; + track__block *q, *qq; + if (p) { + q = (track__block *)p - 1; + osz = q->x.sz; + if (q->x.next) + q->x.next->x.prev = q->x.prev; + if (q->x.prev) + q->x.prev->x.next = q->x.next; + else + track__list = q->x.next; + } else { + q = 0; + osz = 0; + } + qq = (realloc)(q, sz + sizeof(track__block)); + if (qq) { + if (track__vLevel) { + trace(track__vLevel, + "(track) reallocated %lu at %p to %lu for %s in %s", + (unsigned long)osz, (void *)(q + 1), + (unsigned long)sz, (void *)(qq + 1), + qq->x.ctx, track__context->s); + } + qq->x.sz = sz; + qq->x.next = track__list; + qq->x.prev = 0; + if (qq->x.next) + qq->x.next->x.prev = qq; + track__list = qq; + track__used += sz - osz; + qq->x.sz = sz; + return (qq + 1); + } + return (0); +} + +/* --- @track_used@ --- * + * + * Arguments: --- + * + * Returns: A count of how much memory is used currently. + * + * Use: Returns the amount of memory which the @track_@-functions + * above have counted as being currently allocated. + */ + +unsigned long track_used(void) +{ + return (track__used); +} + +/* --- @track_list@ --- * + * + * Arguments: @unsigned int l@ = trace level to use + * + * Returns: --- + * + * Use: Traces a dump of the currently known blocks. Combined with + * a verbose dump of allocations and deallocations, and a + * good idea of which blocks were allocated where, this can + * be useful for locating memory leaks. It's not exactly a + * picnic, though. + */ + +void track_list(unsigned int l) +{ + track__block *q = track__list; + + if (!(tracing() & l)) + return; + + trace(l, "(track dump) Dumping all blocks. Stand well back..."); + while (q) { + trace(l, "(track dump) %p: %lu in %s", + (void *)(q + 1), (unsigned long)q->x.sz, q->x.ctx); + q = q->x.next; + } +} + +/*----- That's all, folks -------------------------------------------------*/ diff --git a/track.h b/track.h new file mode 100644 index 0000000..fa50269 --- /dev/null +++ b/track.h @@ -0,0 +1,217 @@ +/* -*-c-*- + * + * $Id: track.h,v 1.1 1998/06/17 23:44:42 mdw Exp $ + * + * Tracing functions for debugging + * + * (c) 1998 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of the mLib utilities library. + * + * mLib is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * mLib is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with mLib; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: track.h,v $ + * Revision 1.1 1998/06/17 23:44:42 mdw + * Initial revision + * + */ + +#ifndef TRACK_H +#define TRACK_H + +#ifdef __cplusplus + extern "C" { +#endif + +#include + +/*----- Options and conventions -------------------------------------------* + * + * The following macros affect the tracking system: + * + * @TRACK_ENABLE@: Enable tracking of memory allocations + * @TRACK_BLAME@: Register my context blocks in allocations + * + * The reason there are two switches is simple. It's often the case that a + * library routine allocates memory for its client. Therefore, whether we + * want to record the library or the client depends on how much we trust + * the two pieces of software. Setting @TRACK_ENABLE@ and @TRACK_BLAME@ + * suggests that the current source file might leak memory, so we want its + * context markers in the list. Setting @TRACK_ENABLE@ but not + * @TRACK_BLAME@ suggests that we trust this code, but not the code which + * calls it, so we want to preserve the caller's context markers. + * + * Got it? Good. + */ + +/*----- Type definitions --------------------------------------------------*/ + +/* --- A context buffer --- */ + +typedef struct track_ctx { + struct track_ctx *next; + const char *s; +} track_ctx; + +/*----- Functions provided ------------------------------------------------*/ + +/* --- @track_setLevel@ --- * + * + * Arguments: @unsigned int l@ = tracing level for allocation messages + * + * Returns: --- + * + * Use: Sets the trace level for allocation messages. + */ + +extern void track_setLevel(unsigned int /*l*/); + +/* --- @track_pushContext@ --- * + * + * Arguments: @track_ctx *ctx@ = context holder to push + * + * Returns: --- + * + * Use: Pushes the given context block onto the stack. + */ + +extern void track_pushContext(track_ctx */*ctx*/); + +/* --- @track_popContext@ --- * + * + * Arguments: @track_ctx *ctx@ = context holder to pop + * + * Returns: --- + * + * Use: Removes the given context block from the stack. + */ + +extern void track_popContext(track_ctx */*ctx*/); + +/* --- @track_malloc@ --- * + * + * Arguments: @size_t sz@ = size requested + * + * Returns: Pointer to allocated space, or null + * + * Use: Allocates memory, and tracks how much is allocated. + */ + +extern void *track_malloc(size_t /*sz*/); + +/* --- @track_free@ --- * + * + * Arguments: @void *p@ = pointer to an allocated block + * + * Returns: --- + * + * Use: Frees memory, and tracks how much is still allocated. + */ + +extern void track_free(void */*p*/); + +/* --- @track_realloc@ --- * + * + * Arguments: @void *p@ = pointer to an allocated block + * @size_t sz@ = how big it wants to be + * + * Returns: Pointer to the new block. + * + * Use: Reallocates a block, tracking how much memory is still + * available. + */ + +extern void *track_realloc(void */*p*/, size_t /*sz*/); + +/* --- @track_used@ --- * + * + * Arguments: --- + * + * Returns: A count of how much memory is used currently. + * + * Use: Returns the amount of memory which the @track_@-functions + * above have counted as being currently allocated. + */ + +extern unsigned long track_used(void); + +/* --- @track_list@ --- * + * + * Arguments: @unsigned int l@ = trace level to use + * + * Returns: --- + * + * Use: Traces a dump of the currently known blocks. Combined with + * a verbose dump of allocations and deallocations, and a + * good idea of which blocks were allocated where, this can + * be useful for locating memory leaks. It's not exactly a + * picnic, though. + */ + +extern void track_list(unsigned int l); + +/*----- Macro wrappers ----------------------------------------------------*/ + + +/* --- If tracking is to be done, set it up --- */ + +#ifdef TRACK_ENABLE +# undef malloc +# define malloc(sz) track_malloc(sz) +# undef free +# define free(p) track_free(p) +# undef realloc +# define realloc(p, sz) track_realloc(p, sz) +#endif + +/* --- Provide a context for doing track-related things --- */ + +#ifdef TRACK_ENABLE +# define TRACK(x) x +#else +# define TRACK(x) +#endif + +/* --- Handle contexts --- */ + +#if defined(TRACK_ENABLE) && defined(TRACK_BLAME) +# define TRACK_NCTX(name, string) track__context name = { 0, string } +# define TRACK_NPUSH(name) track_pushContext(name) +# define TRACK_NPOP(name) track_popContext(name) +# define TRACK_CTX(string) TRACK_NCTX(track__localContext, string) +# define TRACK_PUSH TRACK_NPUSH(track__localContext) +# define TRACK_POP TRACK_NPOP(track__localContext) +#else +# define TRACK_NCTX(name, string) +# define TRACK_NPUSH(name) ((void)0) +# define TRACK_NPOP(name) ((void)0) +# define TRACK_CTX(string) +# define TRACK_PUSH ((void)0) +# define TRACK_POP ((void)0) +#endif + +/*----- That's all, folks -------------------------------------------------*/ + +#ifdef __cplusplus + } +#endif + +#endif -- [mdw]