From: mdw Date: Wed, 17 Jun 1998 23:44:41 +0000 (+0000) Subject: Initial revision X-Git-Tag: 2.0.4~346 X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~mdw/git/mLib/commitdiff_plain/0875b58fcccadd756e11487185c2ac1d3ed8ab4d Initial revision --- 0875b58fcccadd756e11487185c2ac1d3ed8ab4d 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