├── .gitignore ├── INSTALL.md ├── LICENSE.md ├── Makefile ├── NEWS ├── README.md ├── build ├── include │ └── .keep └── lib │ └── .keep ├── doc ├── cbl │ ├── arena.md │ ├── assert.md │ ├── except.md │ ├── memory.md │ └── text.md ├── cdsl │ ├── bitv.md │ ├── dlist.md │ ├── dwa.md │ ├── hash.md │ ├── list.md │ ├── set.md │ ├── stack.md │ └── table.md └── cel │ ├── conf.md │ └── opt.md └── src ├── cbl ├── arena.c ├── arena.h ├── assert.c ├── assert.h ├── except.c ├── except.h ├── memory.c ├── memory.h ├── memoryd.c ├── text.c └── text.h ├── cdsl ├── bitv.c ├── bitv.h ├── dlist.c ├── dlist.h ├── dwa.c ├── dwa.h ├── hash.c ├── hash.h ├── list.c ├── list.h ├── set.c ├── set.h ├── stack.c ├── stack.h ├── table.c └── table.h └── cel ├── conf.c ├── conf.h ├── opt.c └── opt.h /.gitignore: -------------------------------------------------------------------------------- 1 | doc/html 2 | doc/man3 3 | doc/pdf 4 | pkg/ 5 | build/include 6 | build/lib 7 | *.o 8 | -------------------------------------------------------------------------------- /INSTALL.md: -------------------------------------------------------------------------------- 1 | How to build and install ocelot 2 | =============================== 3 | 4 | This package does not provide an automated way to install the libraries. After 5 | building them, you need to copy by yourself the resulting files into 6 | appropriate places. 7 | 8 | Supported `make` targets are: 9 | 10 | - `all`: builds the libraries into the build directory. The directory has two 11 | sub-directories, one for header files and the other for static(`.a`) and 12 | shared objects(`.so`). Because of the memory library, two versions of `cbl` 13 | are generated with different names; `cbl` for production and `cbld` for 14 | debugging. Necessary headers are placed in the `build/include` directory. 15 | 16 | - `static`: same as `all` except that only static libraries (`.a`) are built 17 | into the build directory. Two versions of `cbl` are generated as explained. 18 | Necessary headers are placed in the `build/include` directory. 19 | 20 | - `clean`: deletes all files generated while building the libraries. 21 | 22 | Some libraries in `cbl` should be informed of the maximum alignment requirement 23 | imposed by the execution environment. If the macro named `MEM_MAXALIGN` is not 24 | defined, they try to guess the requirement. If the guess fails (and it often 25 | does), a program that depends on them also might fail. In such a case, an 26 | explicit value should be given by setting the `CFLAGS` variable as in: 27 | 28 | CFLAGS="-DMEM_MAXALIGN=8" make 29 | 30 | If you are on a 64-bit environment with support for 32-bit emulations and want 31 | 32-bit builds of the libraries, add `-m32` to `CFLAGS` as in: 32 | 33 | CFLAGS="-m32 -DMEM_MAXALIGN=8" make 34 | 35 | You can also build them as 64-bit binaries without `-m` flags. _Note that, 36 | however, even if the build is successful, `ocelot` does not take full advantage 37 | of 64-bit environments yet._ 38 | 39 | Some operations in the `dwa` library for double-word arithmetic perform much 40 | more efficiently when built with `DWA_USE_W` defined if your machine has 8-bit 41 | bytes and uses _little-endian_ byte order like 42 | [x86](https://en.wikipedia.org/wiki/X86): 43 | 44 | CFLAGS="-DMEM_MAXALIGN=8 -DDWA_USE_W" make 45 | 46 | After the libraries built, you can use them by linking and delivering with 47 | your product, or install them on your system. 48 | 49 | 50 | #### System-wide installation 51 | 52 | You need to identify proper places to put the libraries (e.g., `/usr/local/lib` 53 | in most cases, `/usr/local/lib32` for 32-bit builds on a 64-bit machine and 54 | `/usr/local/lib64` for 64-bit builds) and headers (e.g., `/usr/local/include`), 55 | and have permissions to place files there. 56 | 57 | If you have installed a previous version of `ocelot`, you probably want to get 58 | rid of that. For example, on my 64-bit [gentoo](https://www.gentoo.org) 59 | machine, the following instructions run as _root_ uninstall any previous 60 | installation of 32-bit builds of `ocelot`. 61 | 62 | rm -rf /usr/local/include/cbl /usr/local/include/cdsl /usr/local/include/cel 63 | rm /usr/local/lib/libcbl* /usr/local/lib/libcdsl* /usr/local/lib/libcel* 64 | 65 | To install a new 32-bit builds with their headers, run these: 66 | 67 | cp -R build/include/* /usr/local/include/ 68 | cp -d build/lib/* /usr/local/lib/ 69 | ldconfig 70 | 71 | where it is assumed that ld.so.conf has `/usr/local/lib` in it. `ocelot`'s 72 | `Makefile` is configured to kindly create necessary soft-links to shared 73 | objects, and the `-d` option to `cp` above preserves them. 74 | 75 | Installed successfully, you can use the libraries by including necessary 76 | headers in your code as in: 77 | 78 | #include /* use arena */ 79 | #include /* use hash */ 80 | ... 81 | 82 | and invoking your compiler with an option specifying the libraries to use, for 83 | example: 84 | 85 | cc -m32 myprg.c -lcdsl -lcbl 86 | 87 | Note that we are assuming 32-bit builds on a 64-bit machine, thus the `-m32` 88 | option. The order in which libraries are given to the compiler is significant; 89 | all of `cdsl` depend on `cbl`, which is the reason `-lcbl` follows `-lcdsl` in 90 | the arguments for the compiler. 91 | 92 | 93 | #### Local installation 94 | 95 | You can copy or move built libraries and headers to whatever place you want, 96 | and simply link them with your code as in: 97 | 98 | cc -I/path/to/headers -m32 myprg.c /path/to/libraries/libcel.a 99 | 100 | This links a static library (thus, `.a`), which includes the library into the 101 | resulting executable. Linking a shared library instead is also possible, but 102 | not recommended because it requires the location of the linked library when 103 | running the executable. 104 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | ocelot: a language extension library 2 | ==================================== 3 | 4 | `ocelot` contains several libraries for which slightly different copyright 5 | notices apply. Instead of providing several `LICENSE.md` files, the names of 6 | all libraries are listed here with their copyright notices merged. They are 7 | copied with a few changes for 8 | [markdown](https://daringfireball.net/projects/markdown/) from the `doxymain.h` 9 | files in the libraries. 10 | 11 | ------------------------------------------------------------------------------- 12 | 13 | I do not wholly hold the copyright of this library; it is mostly held by David 14 | Hanson as stated in his book, 15 | [C: Interfaces and Implementations](https://sites.google.com/site/cinterfacesimplementations/): 16 | 17 | Copyright (c) 1994,1995,1996,1997 by _David R. Hanson_. 18 | 19 | Permission is hereby granted, free of charge, to any person obtaining a copy of 20 | this software and associated documentation files (the "Software"), to deal in 21 | the Software without restriction, including without limitation the rights to 22 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 23 | of the Software, and to permit persons to whom the Software is furnished to do 24 | so, subject to the following conditions: 25 | 26 | The above copyright notice and this permission notice shall be included in all 27 | copies or substantial portions of the Software. 28 | 29 | This applies to: 30 | 31 | - arena (`cbl/arena`), 32 | - assertion (`cbl/assert`), 33 | - exception handling (`cbl/except`), 34 | - memory management (`cbl/memory`), 35 | - text (`cbl/text`), 36 | - bit-vector (`cdsl/bitv`), 37 | - doubly-linked list (`cdsl/dlist`), 38 | - hash (`cdsl/hash`), 39 | - list (`cdsl/list`), 40 | - set (`cdsl/set`), 41 | - stack (`cdsl/stack`) and 42 | - table (`cdsl/table`). 43 | 44 | ------------------------------------------------------------------------------- 45 | 46 | To parts I added or modified and the libraries I wrote from the scratch 47 | (configuration file library, `cel/conf` and option parsing library, `cel/opt`) 48 | the following copyright applies: 49 | 50 | Copyright (C) 2009-2014 by Woong Jun. 51 | 52 | The package was written so as to conform with the Standard C published by ISO 53 | 9899:1990 and ISO 9899:1999. 54 | 55 | Permission is hereby granted, free of charge, to any person obtaining a copy of 56 | this software and associated documentation files (the "Software"), to deal in 57 | the Software without restriction, including without limitation the rights to 58 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 59 | of the Software, and to permit persons to whom the Software is furnished to do 60 | so, subject to the following conditions: 61 | 62 | The above copyright notice and this permission notice shall be included in all 63 | copies or substantial portions of the Software. 64 | 65 | ------------------------------------------------------------------------------- 66 | 67 | The following disclaimer applies in common to all of libraries in this package: 68 | 69 | THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, 70 | INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 71 | FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 72 | OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 73 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 74 | OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 75 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 76 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 77 | IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 78 | OF SUCH DAMAGE. 79 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # Makefile for buliding ocelot 3 | # 4 | 5 | SHELL = /bin/sh 6 | 7 | .SUFFIXES: 8 | .SUFFIXES: .c .o 9 | 10 | AR = ar ruv 11 | RANLIB = ranlib 12 | CP = cp -f 13 | RM = rm -f 14 | MKDIR = mkdir 15 | GCC = gcc 16 | LN = ln -sf 17 | SHAREDOPT = -shared 18 | 19 | BLDDIR = build 20 | SRCDIR = src 21 | INCDIR = $B/include 22 | LIBDIR = $B/lib 23 | B = $(BLDDIR) 24 | S = $(SRCDIR) 25 | I = $(INCDIR) 26 | L = $(LIBDIR) 27 | 28 | M = 1 29 | N = 1 30 | 31 | 32 | ALL_CFLAGS = -I$I -fPIC $(CFLAGS) 33 | 34 | .c.o: 35 | $(CC) -o $@ -c $(CPPFLAGS) $(ALL_CFLAGS) $< 36 | 37 | 38 | CBLOBJS = $S/cbl/arena.o $S/cbl/assert.o $S/cbl/except.o $S/cbl/memory.o $S/cbl/text.o 39 | CBLDOBJS = $S/cbl/arena.o $S/cbl/assert.o $S/cbl/except.o $S/cbl/memoryd.o $S/cbl/text.o 40 | CDSLOBJS = $S/cdsl/bitv.o $S/cdsl/dlist.o $S/cdsl/dwa.o $S/cdsl/hash.o $S/cdsl/list.o \ 41 | $S/cdsl/set.o $S/cdsl/stack.o $S/cdsl/table.o 42 | CELOBJS = $S/cel/conf.o $S/cel/opt.o 43 | 44 | CBLHORG = $(CBLOBJS:.o=.h) 45 | CDSLHORG = $(CDSLOBJS:.o=.h) 46 | CELHORG = $(CELOBJS:.o=.h) 47 | HCPY = $I/cbl/arena.h $I/cbl/assert.h $I/cbl/except.h $I/cbl/memory.h $I/cbl/text.h \ 48 | $I/cdsl/bitv.h $I/cdsl/dlist.h $I/cdsl/dwa.h $I/cdsl/hash.h $I/cdsl/list.h \ 49 | $I/cdsl/set.h $I/cdsl/stack.h $I/cdsl/table.h \ 50 | $I/cel/conf.h $I/cel/opt.h 51 | 52 | STATICLIB = $L/libcbl.a $L/libcbld.a $L/libcdsl.a $L/libcel.a 53 | SHAREDLIB = $L/libcbl.so.$M.$N $L/libcbl.so.$M $L/libcbl.so \ 54 | $L/libcbld.so.$M.$N $L/libcbld.so.$M $L/libcbld.so \ 55 | $L/libcdsl.so.$M.$N $L/libcdsl.so.$M $L/libcdsl.so \ 56 | $L/libcel.so.$M.$N $L/libcel.so.$M $L/libcel.so 57 | 58 | all: $(HCPY) $(STATICLIB) $(SHAREDLIB) 59 | 60 | static: $(HCPY) $(STATICLIB) 61 | 62 | clean: 63 | $(RM) $(CBLOBJS) $(CBLDOBJS) $(CDSLOBJS) $(CELOBJS) $(HCPY) $(STATICLIB) $(SHAREDLIB) 64 | 65 | $L/libcbl.a: $(CBLOBJS) 66 | $(AR) $@ $(CBLOBJS); $(RANLIB) $@ || true 67 | 68 | $L/libcbld.a: $(CBLDOBJS) 69 | $(AR) $@ $(CBLDOBJS); $(RANLIB) $@ || true 70 | 71 | $L/libcdsl.a: $(CDSLOBJS) 72 | $(AR) $@ $(CDSLOBJS); $(RANLIB) $@ || true 73 | 74 | $L/libcel.a: $(CELOBJS) 75 | $(AR) $@ $(CELOBJS); $(RANLIB) $@ || true 76 | 77 | $L/libcbl.so.$M.$N: $(CBLOBJS) 78 | $(GCC) $(CFLAGS) $(SHAREDOPT) -Wl,-soname,libcbl.so.$M -o $@ $(CBLOBJS) 79 | 80 | $L/libcbld.so.$M.$N: $(CBLDOBJS) 81 | $(GCC) $(CFLAGS) $(SHAREDOPT) -Wl,-soname,libcbld.so.$M -o $@ $(CBLDOBJS) 82 | 83 | $L/libcdsl.so.$M.$N: $(CDSLOBJS) 84 | $(GCC) $(CFLAGS) $(SHAREDOPT) -Wl,-soname,libcdsl.so.$M -o $@ $(CDSLOBJS) 85 | 86 | $L/libcel.so.$M.$N: $(CELOBJS) 87 | $(GCC) $(CFLAGS) $(SHAREDOPT) -Wl,-soname,libcel.so.$M -o $@ $(CELOBJS) 88 | 89 | $L/libcbl.so.$M: $L/libcbl.so.$M.$N 90 | cd $L && $(LN) libcbl.so.$M.$N libcbl.so.$M 91 | 92 | $L/libcbld.so.$M: $L/libcbld.so.$M.$N 93 | cd $L && $(LN) libcbld.so.$M.$N libcbld.so.$M 94 | 95 | $L/libcdsl.so.$M: $L/libcdsl.so.$M.$N 96 | cd $L && $(LN) libcdsl.so.$M.$N libcdsl.so.$M 97 | 98 | $L/libcel.so.$M: $L/libcel.so.$M.$N 99 | cd $L && $(LN) libcel.so.$M.$N libcel.so.$M 100 | 101 | $L/libcbl.so: $L/libcbl.so.$M 102 | cd $L && $(LN) libcbl.so.$M libcbl.so 103 | 104 | $L/libcbld.so: $L/libcbld.so.$M 105 | cd $L && $(LN) libcbld.so.$M libcbld.so 106 | 107 | $L/libcdsl.so: $L/libcdsl.so.$M 108 | cd $L && $(LN) libcdsl.so.$M libcdsl.so 109 | 110 | $L/libcel.so: $L/libcel.so.$M 111 | cd $L && $(LN) libcel.so.$M libcel.so 112 | 113 | $(HCPY): $I/cbl $I/cdsl $I/cel $(CBLHORG) $(CDSLHORG) $(CELHORG) 114 | $(CP) $(CBLHORG) $I/cbl/ && \ 115 | $(CP) $(CDSLHORG) $I/cdsl/ && \ 116 | $(CP) $(CELHORG) $I/cel/ 117 | 118 | $I/cbl $I/cdsl $I/cel: 119 | $(MKDIR) $@ 120 | 121 | $S/cbl/arena.o: $S/cbl/arena.c $S/cbl/arena.h $S/cbl/assert.h $S/cbl/except.h 122 | $S/cbl/assert.o: $S/cbl/assert.c $S/cbl/assert.h $S/cbl/except.h 123 | $S/cbl/except.o: $S/cbl/except.c $S/cbl/except.h $S/cbl/assert.h 124 | $S/cbl/memory.o: $S/cbl/memory.c $S/cbl/memory.h $S/cbl/assert.h $S/cbl/except.h 125 | $S/cbl/memoryd.o: $S/cbl/memoryd.c $S/cbl/memory.h $S/cbl/assert.h $S/cbl/except.h 126 | $S/cbl/text.o: $S/cbl/text.c $S/cbl/text.h $S/cbl/assert.h $S/cbl/except.h $S/cbl/memory.h 127 | 128 | $S/cdsl/bitv.o: $S/cdsl/bitv.c $S/cdsl/bitv.h $S/cbl/assert.h $S/cbl/except.h $S/cbl/memory.h 129 | $S/cdsl/dlist.o: $S/cdsl/dlist.c $S/cdsl/dlist.h $S/cbl/assert.h $S/cbl/except.h $S/cbl/memory.h 130 | $S/cdsl/dwa.o: $S/cdsl/dwa.c $S/cdsl/dwa.h $S/cbl/assert.h $S/cbl/except.h 131 | $S/cdsl/hash.o: $S/cdsl/hash.c $S/cdsl/hash.h $S/cbl/assert.h $S/cbl/except.h $S/cbl/memory.h 132 | $S/cdsl/list.o: $S/cdsl/list.c $S/cdsl/list.h $S/cbl/assert.h $S/cbl/except.h $S/cbl/memory.h 133 | $S/cdsl/set.o: $S/cdsl/set.c $S/cdsl/set.h $S/cbl/assert.h $S/cbl/except.h $S/cbl/memory.h 134 | $S/cdsl/stack.o: $S/cdsl/stack.c $S/cdsl/stack.h $S/cbl/assert.h $S/cbl/except.h $S/cbl/memory.h 135 | $S/cdsl/table.o: $S/cdsl/table.c $S/cdsl/table.h $S/cbl/assert.h $S/cbl/except.h $S/cbl/memory.h 136 | 137 | $S/cel/conf.o: $S/cel/conf.c $S/cel/conf.h $S/cbl/assert.h $S/cbl/except.h $S/cbl/memory.h \ 138 | $S/cdsl/hash.h $S/cdsl/table.h 139 | $S/cel/opt.o: $S/cel/opt.c $S/cel/opt.h 140 | 141 | # end of Makefile 142 | -------------------------------------------------------------------------------- /NEWS: -------------------------------------------------------------------------------- 1 | What's new in ocelot? 2 | ===================== 3 | 4 | 2017-06-01 1.1.0 released with dwa 5 | 2015-11-20 1.0.0 released to replace opt_extend() with opt_reinit() 6 | 2015-11-02 soname adjusted to 0.4 to match release version 7 | 2015-11-02 0.4.0 released to add opt_extend() and opt_ambmstr() 8 | 2014-10-05 0.3.1 released with new documentation 9 | 2014-09-14 Documentation refined 10 | 2013-12-12 Moved the repository to git/github 11 | 2013-04-23 0.3.0 released with bit-vector 12 | 2011-07-24 stack_peek() added 13 | 2011-01-23 The project name changed to ocelot and some bugs fixed 14 | 2010-12-30 First release 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ocelot: a language extension library 2 | ==================================== 3 | 4 | `ocelot` is a collection of libraries to provide features that the C language 5 | lacks, various data structures that most programs use in common, and facilities 6 | for interaction between a program and its environment. 7 | 8 | This package collects libraries into three categories called `cbl`, `cdsl` and 9 | `cel`. Libraries belonging to `cbl`(C basic library) provide features that the 10 | the language lacks and include alternative memory allocators and an exception 11 | handling facility. Those to `cdsl`(C data structure library) implement various 12 | data structures frequently used by most programs. Those to `cel`(C environment 13 | library) aid interaction with the execution environment. 14 | 15 | The `src` directory contains sub-directories `cbl`, `cdsl` and `cel` for the 16 | libraries of each category: 17 | 18 | - `cbl`: C basic library 19 | - `arena.h/c`: arena library (lifetime-based memory allocator) 20 | - `assert.h/c`: assertion library 21 | - `except.h/c`: exception library 22 | - `memory.h/c`: memory library (for production) 23 | - `memory.h/memoryd.c`: memory library (for debugging) 24 | - `text.h/c`: text library (high-level string manipulation) 25 | - `cdsl`: C data structure library 26 | - `bitv.h/c`: bit-vector library 27 | - `dlist.h/c`: doubly-linked list library 28 | - `dwa.h/c`: double-word arithmetic library 29 | - `hash.h/c`: hash library 30 | - `list.h/c`: list library (singly-linked list) 31 | - `set.h/c`: set library 32 | - `stack.h/c`: stack library 33 | - `table.h/c`: table library 34 | - `cel`: C environment library 35 | - `conf.h/c`: configuration library (configuration file parser) 36 | - `opt.h/c`: option library (option parser) 37 | 38 | Libraries had been documented with [doxygen](https://www.doxygen.org), and 39 | changed to use [markdown](https://daringfireball.net/projects/markdown/) for 40 | easier maintenance and access. The `doc` directory contains 41 | [documentation](https://github.com/mycoboco/ocelot/tree/master/doc) for them. 42 | 43 | `INSTALL.md` explains how to build and install the libraries. For the copyright 44 | issues, see the accompanying `LICENSE.md` file. 45 | 46 | _As of the 0.4.0 release which breaks backward compatibility, the 47 | [soname](https://en.wikipedia.org/wiki/Soname) has been adjusted from 1.x to 48 | 0.x in order to match the release version._ 49 | 50 | If you have a question or suggestion, do not hesitate to contact me via email 51 | (woong.jun at gmail.com) or web (https://code.woong.org/). 52 | -------------------------------------------------------------------------------- /build/include/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mycoboco/ocelot/24c1760ba0904d0b22da6889f70c56d026aceaf2/build/include/.keep -------------------------------------------------------------------------------- /build/lib/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mycoboco/ocelot/24c1760ba0904d0b22da6889f70c56d026aceaf2/build/lib/.keep -------------------------------------------------------------------------------- /doc/cbl/arena.md: -------------------------------------------------------------------------------- 1 | C basic library: arena 2 | ====================== 3 | 4 | This document specifies the arena library which belongs to C basic library. The 5 | basic structure is from David Hanson's book, 6 | [C Interfaces and Implementations](https://sites.google.com/site/cinterfacesimplementations/). 7 | I modified the original implementation to make it conform to the C standard and 8 | to enhance its readability. 9 | 10 | The book explains its design and implementation in a very comprehensive way. 11 | Not to mention the copyright issues, the internals of the library is not 12 | explained here. A brief motivation, introduction and explanations for APIs, 13 | however, are given to aid the use of the library. 14 | 15 | 16 | ## 1. Introduction 17 | 18 | [`malloc()`](https://en.wikipedia.org/wiki/C_dynamic_memory_allocation) and 19 | other related functions from 20 | [``](https://en.wikipedia.org/wiki/Stdlib.h) provide facilities for 21 | the size-based memory allocation strategy. Each invocation to allocation 22 | functions requires a corresponding invocation to deallocation functions in 23 | order to avoid 24 | [the memory leakage problem](https://en.wikipedia.org/wiki/Memory_leak). Under 25 | certain circumstances the size-based memory allocator is not the best way to 26 | manage storage. 27 | 28 | For example, consider a script interpreter that accepts multiple scripts and 29 | parses them for execution in sequence. During running a script, many data 30 | structures including trees from a parser and tables for symbols should be 31 | maintained and those structures often require complicated patterns of memory 32 | allocation/deallocation. Besides, they should be correctly freed after the 33 | script has been processed and before processing of the next script starts off. 34 | In such a situation, a lifetime-based memory allocator can work better and 35 | simplify the patterns of (de)allocation. With a lifetime-based memory 36 | allocator, all storages allocated for processing a script is remembered 37 | somehow, and all the instances can be freed at a time when processing the 38 | script is finished. The arena library provides a lifetime-based memory 39 | allocator. 40 | 41 | This library reserves identifiers starting with `arena_` and `ARENA_`, and 42 | imports the assertion library and the exception handling library. Even if it 43 | does not depend on the memory management library, it also uses the identifier 44 | `MEM_MAXALIGN`. 45 | 46 | 47 | ### 1.1. How to use the library 48 | 49 | Basically, using the arena library to allocate storages does not quite differ 50 | from using `malloc()` or similars from ``. The differences are that 51 | every allocation function takes an additional argument to specify an arena, and 52 | that there is no need to invoke a deallocation function for each of allocated 53 | storages; a function to free all storages associated with an arena is provided. 54 | 55 | typeA *p = malloc(sizeof(*p)); 56 | /* ... */ 57 | typeB *q = malloc(sizeof(*p)); 58 | /* ... */ 59 | free(p); 60 | free(q); 61 | 62 | For example, suppose that `p` and `q` point to two areas that have been 63 | allocated at different places but can be freed at the same time. As the number 64 | of such instances increases, deallocating them gets more complicated and thus 65 | necessarily more error-prone. 66 | 67 | typeA *p = ARENA_ALLOC(myarena, sizeof(*p)); 68 | /* ... */ 69 | typeB *q = ARENA_ALLOC(myarena, sizeof(*q)); 70 | /* ... */ 71 | ARENA_FREE(myarena); 72 | 73 | On the other hand, if an allocator that the arena library offers is used, only 74 | one call to `ARENA_FREE()` frees all storages associated with the arena, 75 | `myarena`. 76 | 77 | As `` provides `malloc()` and `calloc()`, this library does 78 | `ARENA_ALLOC()` and `ARENA_CALLOC()` taking similar arguments. `ARENA_FREE()` 79 | does what `free()` does (actually, it does more as explained above). 80 | `ARENA_NEW()` creates an arena with which allocated storages will be 81 | associated, and `ARENA_DISPOSE()` destroys an arena, which means that an arena 82 | itself may be reused repeatedly even after all storages with it have been freed 83 | by `ARENA_FREE()`. 84 | 85 | _As in the debugging version of the memory management library, `MEM_MAXALIGN` 86 | indicates the common alignment factor; in other words, the alignment factor of 87 | pointers `malloc()` returns. If it is not given, the library tries to guess a 88 | proper value, but no guarantee that the guess is correct. Therefore, it is 89 | recommended to give a proper definition for `MEM_MAXALIGN` (via a compiler 90 | option like `-D`, if available)._ 91 | 92 | _Note that the arena library does not rely on the memory management library. 93 | It constitutes a completely different allocator. Thus, the debugging version of 94 | the memory management library cannot detect problems occurred in the storages 95 | maintained by the arena library._ 96 | 97 | 98 | ### 1.2. Boilerplate code 99 | 100 | Using an arena starts with creating one: 101 | 102 | arena_t *myarena = ARENA_NEW(); 103 | 104 | As in the memory library from `cbl`, you don't need to check the return value 105 | of `ARENA_NEW()`; an exception named `arena_exceptfailNew` will be raised if 106 | the creation fails (see the exception library for how to handle exceptions). 107 | Creating an arena is different from allocating a necessary storage. You can 108 | freely allocate storages that belong to an arena with `ARENA_ALLOC()` or 109 | `ARENA_CALLOC()` as in: 110 | 111 | sometype_t *p = ARENA_ALLOC(myarena, sizeof(*p)); 112 | othertype_t *q = ARENA_CALLOC(myarena, 10, sizeof(*q)); 113 | 114 | Again, you don't have to check the return value of these invocations. If no 115 | storage is able to be allocated, an exception, `arena_exceptfailAlloc` will be 116 | raised. Adjusting the size of the already allocated storage is not supported; 117 | there is no `ARENA_REALLOC()` or `ARENA_RESIZE()` that corresponds to 118 | `realloc()`. 119 | 120 | There are two ways to release storages from an arena: `ARENA_FREE()` and 121 | `ARENA_DISPOSE()`. 122 | 123 | ARENA_FREE(myarena); 124 | /* myarena can be reused */ 125 | ARENA_DISPOSE(myarena); 126 | 127 | `ARENA_FREE()` deallocates any storage belonging to an arena, while 128 | `ARENA_DISPOSE()` does the same job and also destroy the arena to make it no 129 | more usable. 130 | 131 | If you have a plan to use a tool like [Valgrind](https://valgrind.org/) to 132 | detect memory-related bugs, see explanations for `ARENA_DISPOSE()`. 133 | 134 | 135 | ## 2. APIs 136 | 137 | ### 2.1. Types 138 | 139 | #### `arena_t` 140 | 141 | `arena_t` represents an arena to which storages belong. 142 | 143 | 144 | ### 2.2. Exceptions 145 | 146 | See the exception library to see how to use exceptions. 147 | 148 | #### `const except_t arena_exceptfailNew` 149 | 150 | This exception is occurred when the library fails to create a new arena 151 | probably due to memory allocation failure. 152 | 153 | #### `const except_t arena_exceptfailAlloc` 154 | 155 | This exception is occurred when the library fails to allocate a new storage. 156 | 157 | 158 | ### 2.3. Creating an arean 159 | 160 | #### `arena_t *ARENA_NEW(void)` 161 | 162 | `ARENA_NEW()` allocates a new arena and initializes it to indicate an empty 163 | arena. 164 | 165 | ##### May raise 166 | 167 | `arena_exceptfailNew`. 168 | 169 | ##### Takes 170 | 171 | Nothing. 172 | 173 | ##### Returns 174 | 175 | A new arena created 176 | 177 | 178 | ### 2.3. (De)allocating storages 179 | 180 | #### `void *ARENA_ALLOC(arena_t *a, size_t n)` 181 | 182 | `ARENA_ALLOC()` allocates storage whose byte length is `n` for an arena `a`. 183 | 184 | ##### May raise 185 | 186 | `assert_exceptfail` (see the assertion library from `cbl`) and 187 | `arena_exceptfailAlloc`. 188 | 189 | ##### Takes 190 | 191 | | Name | In/out | Meaning | 192 | |:-----:|:------:|:----------------------------------------| 193 | | a | in/out | arena for which storage to be allocated | 194 | | n | in | size of storage requested in byte | 195 | 196 | ##### Returns 197 | 198 | A storage allocated for given arena. 199 | 200 | 201 | #### `void *ARENA_CALLOC(arena_t *a, size_t c, size_t n)` 202 | 203 | `ARENA_CALLOC()` allocates zero-filled storage of the size `c` * `p` for an 204 | arena `a`. 205 | 206 | ##### May raise 207 | 208 | `assert_exceptfail` (see the assertion library) and `arena_exceptfailAlloc`. 209 | 210 | ##### Takes 211 | 212 | | Name | In/out | Meaning | 213 | |:-----:|:------:|:----------------------------------------| 214 | | a | in/out | arena for which storage to be allocated | 215 | | c | in | number of items to be allocated | 216 | | n | in | size of an item in byte | 217 | 218 | ##### Returns 219 | 220 | A zero-filled storage allocated for an arena. 221 | 222 | 223 | #### `void ARENA_FREE(arena_t *a)` 224 | 225 | `ARENA_FREE()` deallocates all storages belonging to an arena `a`. The arena 226 | itselt is not destroyed. 227 | 228 | ##### May raise 229 | 230 | `assert_exceptfail` (see the assertion library). 231 | 232 | ##### Takes 233 | 234 | | Name | In/out | Meaning | 235 | |:-----:|:------:|:---------------------------------------| 236 | | a | in/out | arena whose storages to be deallocated | 237 | 238 | ##### Returns 239 | 240 | Nothing. 241 | 242 | 243 | ### 2.4. Destroying an arena 244 | 245 | #### `void ARENA_DISPOSE(arena_t **pa)` 246 | 247 | `ARENA_DISPOSE()` destroys an arena pointed to by `pa`. It releases storages 248 | belonging to a given arena and disposes it. Do not be confused with 249 | `arena_free()` which gets all storages of an arena to be deallocated but does 250 | not destroy the arena itself. 251 | 252 | Note that storages belonging to a free list(`freelist`, an internal list to 253 | keep freed storages for later use) are not deallocated by `ARENA_DISPOSE()`. A 254 | memory leakage detection tool might report that there is a leakage caused by 255 | the library, but that is intentional, not a bug. 256 | 257 | ##### May raise 258 | 259 | `assert_exceptfail` (see the assertion library). 260 | 261 | ##### Takes 262 | 263 | | Name | In/out | Meaning | 264 | |:-----:|:------:|:----------------------------| 265 | | pa | in/out | pointer to arena to dispose | 266 | 267 | ##### Returns 268 | 269 | Nothing. 270 | 271 | 272 | ## 3. Future directions 273 | 274 | ### 3.1. Minor changes 275 | 276 | To mimic the behavior of `calloc()` specified by the standard, it is required 277 | for `ARENA_CALLOC()` to successfully return a null pointer when it cannot 278 | allocate storage of the requested size. Because this does not allow overflow, 279 | it has to carefully check overflow when calculating the total size. 280 | 281 | 282 | ## 4. Contact me 283 | 284 | Visit [`code.woong.org`](https://code.woong.org) to get the latest version of 285 | this library. Any comments about the library are welcomed. If you have a 286 | proposal or question on the library just email me, and I will reply as soon as 287 | possible. 288 | 289 | 290 | ## 5. Copyright 291 | 292 | For the copyright issues, see `LICENSE.md`. 293 | -------------------------------------------------------------------------------- /doc/cbl/assert.md: -------------------------------------------------------------------------------- 1 | C basic library: assertion 2 | ========================== 3 | 4 | This document specifies the assertion library which belongs to C basic library. 5 | The basic structure is from David Hanson's book, 6 | [C Interfaces and Implementations](https://sites.google.com/site/cinterfacesimplementations/). 7 | I modified the original implementation to make it more appropriate for my other 8 | projects as well as to increase its readability; conformance is not an issue 9 | here because trying to replace an existing standard library by _knocking it 10 | out_ is already considered non-conforming. 11 | 12 | 13 | ## 1. Introduction 14 | 15 | Replacing `` with a version to support exceptions is useful based on 16 | some assumptions: 17 | - deactivating assertions in product code does not bring much benefit to the 18 | program's performance (only profiling would be able to say how much 19 | assertions degrades performance); 20 | - avoiding assertions showing cryptic diagnostic messages leads users into a 21 | more catastrophic situation like dereferencing a null pointer; 22 | - a diagnostic message like `Uncaught exception Assertion failed raised at 23 | file.c:54` without an expression that caused the assertion failure conveys 24 | equivalent information than messages from the standard version, when the 25 | programmer can access to the problematic file; and 26 | - dealing with assertion failures by an exception has an advantage that every 27 | assertion can be handled in a uniform way (see an example below). 28 | 29 | More detailed explanation on the motivation is given in Hanson's book, so I 30 | should stop here, but an introduction to and explanations for APIs are given to 31 | aid the use of the library. 32 | 33 | The assertion library reserves identifiers `assert` and `ASSERT`, and imports 34 | the exception library. 35 | 36 | 37 | ### 1.1. How to use the library 38 | 39 | Using the assertion Library is not quite different from using the standard 40 | version. Differently from Hanson's original implementation, this implementation 41 | includes the standard version `` if either `NDEBUG` or 42 | `ASSERT_STDC_VER` is `#define`d. 43 | 44 | The following example shows how to handle assertion failures in one place: 45 | 46 | #include "assert.h" 47 | #include "except.h" 48 | 49 | int main(void) 50 | { 51 | EXCEPT_TRY 52 | real_main(); 53 | EXCEPT_EXCEPT(assert_exceptfail) 54 | fprintf(stderr, 55 | "An internal error occurred with no way to recover.\n" 56 | "Please report this error to me@example.com.\n\n"); 57 | RERAISE; 58 | EXCEPT_END; 59 | 60 | return 0; 61 | } 62 | 63 | If you use an `ELSE` clause instead of the `EXCEPT` clause above, any uncaught 64 | exception lets the program print the message before aborting. (See the 65 | documentation of the exception library.) 66 | 67 | 68 | ## 2. APIs 69 | 70 | ### 2.1. Exceptions 71 | 72 | #### `const except_t assert_exceptfail` 73 | 74 | This exception is occurred when an assertion described by `assert()` fails 75 | 76 | 77 | ### 2.2. Assertion 78 | 79 | #### `void assert(e)` 80 | 81 | The macro `assert()` replaces the standard `assert()` to support exceptions. An 82 | activated assert() raises an exception named `assert_exceptfail`. The 83 | differences between this and the standard's version are that the exception 84 | version: 85 | - does not print the scalar expression `e`; and 86 | - does not abort; it merely raises an exception and let the exception handler 87 | decide what to do. 88 | 89 | ##### May raise 90 | 91 | `assert_exceptfail`. 92 | 93 | ##### Takes 94 | 95 | | Name | In/out | Meaning | 96 | |:-----:|:------:|:-----------------------------------------| 97 | | e | in | scalar expression to check for assertion | 98 | 99 | ##### Returns 100 | 101 | Nothing. 102 | 103 | 104 | 105 | ## 3. Contact me 106 | 107 | Visit [`code.woong.org`](https://code.woong.org) to get the latest version of 108 | this library. Any comments about the library are welcomed. If you have a 109 | proposal or question on the library just email me, and I will reply as soon as 110 | possible. 111 | 112 | 113 | ## 4. Copyright 114 | 115 | For the copyright issues, see `LICENSE.md`. 116 | -------------------------------------------------------------------------------- /doc/cbl/except.md: -------------------------------------------------------------------------------- 1 | C basic library: exception 2 | ========================== 3 | 4 | This document specifies the exception library which belongs to C basic library. 5 | The basic structure is from David Hanson's book, 6 | [C Interfaces and Implementations](https://sites.google.com/site/cinterfacesimplementations/). 7 | I modified the original implementation to make it more appropriate for my other 8 | projects, to conform to the C standard and to enhance its readability. 9 | 10 | The book explains its design and implementation in a very comprehensive way. 11 | Not to mention the copyright issues, the internals of the library is not 12 | explained here. Typical exception-handling constructs and emphasis on crucial 13 | issues and explanations for APIs, however, are given to aid the use of the 14 | library. 15 | 16 | 17 | ## 1. Introduction 18 | 19 | The library reserves identifiers starting with `except_` and `EXCEPT_`, and 20 | imports the assertion library. 21 | 22 | 23 | ### 1.1. How to use the library 24 | 25 | The following construct shows how a typical TRY-EXCEPT statement looks like. 26 | 27 | EXCEPT_TRY 28 | S; 29 | EXCEPT_EXCEPT(e1) 30 | S1; 31 | EXCEPT_EXCEPT(e2) 32 | S2; 33 | EXCEPT_ELSE 34 | S3; 35 | EXCEPT_END; 36 | 37 | `EXCEPT_TRY` starts a TRY-EXCEPT or TRY-FINALLY statement. The statements 38 | following `EXCEPT_TRY` (referred to as `S` in this example) are executed, and 39 | if an exception is occurred during that execution, the control moves to one of 40 | EXCEPT clauses with a matching exception or the ELSE clause. The statements 41 | `Sn` under the matched EXCEPT clause or ELSE clause are executed and the 42 | control moves to `EXCEPT_END`. 43 | 44 | EXCEPT_TRY 45 | S 46 | EXCEPT_ELSE 47 | S1 48 | EXCEPT_END; 49 | 50 | A construct without any EXCEPT clause is useful when catching all exceptions 51 | raised during execution of `S` in a uniform way. If other exception handers are 52 | established during execution of `S` only uncaught exceptions there move the 53 | control to the ELSE clause above. For example, any uncaught exception with no 54 | recovery (e.g., assertion failures or memory allocation failures) can be 55 | handled in the `main` function as follows: 56 | 57 | int main(void) 58 | { 59 | EXCEPT_TRY 60 | real_main(); 61 | EXCEPT_ELSE 62 | fprintf(stderr, 63 | "An internal error occurred with no way to recover.\n" 64 | "Please report this error to somebody@somewhere.\n\n"); 65 | EXCEPT_RERAISE; 66 | EXCEPT_END; 67 | 68 | return 0; 69 | } 70 | 71 | A TRY-FINALLY statement looks like: 72 | 73 | EXCEPT_TRY 74 | S; 75 | EXCEPT_FINALLY 76 | S1; 77 | EXCEPT_END; 78 | 79 | The statements following `EXCEPT_FINALLY` are executed regardless of occurrence 80 | of an exception, so if a kind of clean-up like closing open files or freeing 81 | allocated storage is necessary to be performed unconditionally, `S1` is the 82 | right place to put. If the exception caught by a TRY-FINALLY statement needs to 83 | be also handled by a TRY-EXCEPT statement, `EXCEPT_RERAISE` raises it again to 84 | give the previous handler (if any) a chance to handle it. 85 | 86 | Note that each group of the statements, say, `S`, `S1` and so on, constitutes 87 | an independent block; opening or closing braces are hidden in `EXCEPT_TRY`, 88 | `EXCEPT_EXCEPT`, `EXCEPT_FINALLY` and `EXCEPT_END`. Therefore variables 89 | declared in a block, say, `S` is not visible to other blocks, say, `S1`. 90 | 91 | And even if not explicitly specified in Hanson's book, it is possible to 92 | construct an exception handling statement which has both EXCEPT and FINALLY 93 | clauses, which looks like: 94 | 95 | EXCEPT_TRY 96 | S; 97 | EXCEPT_EXCEPT(e) 98 | Se; 99 | EXCEPT_FIANLLY 100 | Sf; 101 | EXCEPT_END; 102 | 103 | Looking into the implementation by combining those macros explains how it 104 | works. Finding when it is useful is up to users. 105 | 106 | 107 | ### 1.2. Caveats 108 | 109 | The exception handling mechanism given here is implemented using a non-local 110 | jump provided by [``](https://en.wikipedia.org/wiki/Setjmp.h). Every 111 | restriction applied to `` also applies to this library. For example, 112 | there is no guarantee that an updated `auto` variable preserves its last stored 113 | value if the update done between `setjmp()` and `longjmp()`. For example, 114 | 115 | { 116 | int i; 117 | 118 | EXCEPT_TRY 119 | i++; 120 | S; 121 | EXCEPT_EXCEPT(e1) 122 | S1; 123 | EXCEPT_TRY; 124 | } 125 | 126 | If an exception `e1` occurs, which moves the control to `S1`, it is unknown 127 | what the value of `i` is in the EXCEPT clause above. A way to walk around this 128 | problem is to declare `i` as `volatile` or `static`. (See the manpage for 129 | `setjmp()` and `longjmp()`.) 130 | 131 | At the first blush, this restriction seems too restrictive, but it is not. The 132 | restriction applies only to those non-volatile variables with automatic storage 133 | duration and belonged to the function containing `EXCEPT_TRY` (which has 134 | `setjmp()` in it), and only when they are modified between `setjmp()` 135 | (`EXCEPT_TRY`) and corresponding `longjmp()` (`EXCEPT_RAISE()`). 136 | 137 | One more thing to remember is that the ordinary `return` does not work in the 138 | statements `S` above because it knows nothing about maintaining the exception 139 | stack. Inside `S`, the exception frame has already been pushed to the exception 140 | stack. Returning from it without adjusting the stack by popping the current 141 | frame spoils the exception handling mechanism, which results in undefined 142 | behavior. `EXCEPT_RETURN` is provided for this purpose. It does the same thing 143 | as the ordinary `return` except that it adjusts the exception stack properly. 144 | Also note that `EXCEPT_RETURN` should not be used in a EXCEPT, ELSE or FINALLY 145 | clause; entering those clauses entails popping the current frame from the 146 | stack. 147 | 148 | In general, it is said that recovery from an erroneous situation gets easier if 149 | you have a way to handle it with an exception and its handler. In practice, 150 | with the implementation using a non-local jump facility like this library, 151 | however, that is not always true. If a program manages resources like allocated 152 | memory and open file pointers in a complicated fashion and an exception can be 153 | raised at any deeply nested level, it is very likely for you to lose control 154 | over them. In addition to it, keeping internal data structures consistent is 155 | also a problem. If an exception can be triggered during modifying fields of an 156 | internal data structure, it is never a trivia to guarantee consistency of that. 157 | Therefore, an exception handling facility this library provides is in fact best 158 | suited for handling various problematic circumstances in one spot and then 159 | terminating the program's execution almost immediately. If you would like your 160 | code to be tolerant to an exceptional case by, for example, making it revert to 161 | an inferior but reliable approach, you have to keep these facts in your mind. 162 | 163 | 164 | ### 1.3. Improvements 165 | 166 | A diagnostic message printed when an assertion is failed changes in C99 to 167 | include the name of a function where the failure occurs. This can be readily 168 | attained with a newly introduced predefined identifier `__func__`. To provide 169 | more useful information, if an implementation claims to support C99 by defining 170 | the macro `__STDC_VERSION__` properly, the library also includes the function 171 | name when making up the message output when an uncaught exception detected. For 172 | the explanation on `__func__` and `__STDC_VERSION__`, see ISO/IEC 9899:1999 or 173 | its later editions. 174 | 175 | 176 | ### 1.4. Boilerplate code 177 | 178 | To show a boilerplate code, suppose that a module named `mod` defines and may 179 | raise exceptions named `mod_exceptfail` and `mod_exceptmem`, and that code 180 | invoking the module is expected to install an exception handler for that 181 | exception. Now implementing the module `mod` looks in part like: 182 | 183 | const except_t mod_exceptfail = { "Some operation failed" }; 184 | const except_t mod_exceptmem = { "Memory operation failed" }; 185 | 186 | /* ... */ 187 | 188 | int mod_oper(int arg) 189 | { 190 | /* ... */ 191 | if (!p) 192 | EXCEPT_RAISE(mod_exceptfail); 193 | else if (p != q) 194 | EXCEPT_RAISE(mod_exceptmem); 195 | /* ... */ 196 | } 197 | 198 | where the names of exceptions and the contents of strings given as initializers 199 | are up to an user. Those initializer strings are printed out when the 200 | corresponding exception is raised but not caught by a user-installed handler. 201 | By installing an exception handler with a TRY-EXCEPT construct, code that 202 | invokes `mod_oper()` can handle exceptions `mod_oper()` may raise: 203 | 204 | EXCEPT_TRY 205 | result = mod_oper(value); 206 | /* ... */ 207 | EXCEPT_EXCEPT(mod_exceptfail) 208 | fprintf(stderr, "program: some operation failed; no way to recover\n"); 209 | EXCEPT_RERAISE; 210 | EXCEPT_EXCEPT(mod_exceptmem) 211 | fprintf(stderr, "program: memory allocation failed; internal buffer used\n"); 212 | /* prepare internal buffer and retry */ 213 | EXCEPT_END 214 | 215 | Note that exceptions other than `mod_exceptfail` and `mod_exceptmem` are 216 | uncaught by this handler and handed to an outer handler if any. 217 | 218 | 219 | ## 2. APIs 220 | 221 | ### 2.1. Types 222 | 223 | #### `except_t` 224 | 225 | The uniqueness of an exception is determined by the address of a member in 226 | `except_t`; so make an `except_t` object have static storage duration when 227 | declaring it. It is undefined what happens when using an exception object with 228 | automatic storage duration. 229 | 230 | 231 | ### 2.1. Raising exceptions 232 | 233 | There are two function-like macros to raise an exception; `EXCEPT_RAISE()` 234 | raises an exception given as an argument, while `EXCEPT_RERAISE()` re-raises 235 | the most recently raised exception, thus takes no argument. 236 | 237 | #### `void EXCEPT_RAISE(except_t e)` 238 | 239 | The `EXCEPT_RAISE()` macro raises an exception `e`. 240 | 241 | `EXCEPT_RAISE()` is in fact a macro and it takes the address of `e` before 242 | invocation to an actual function. An argument to `e`, therefore, has to be an 243 | lvalue. 244 | 245 | ##### May raise 246 | 247 | A given exception, `e`. 248 | 249 | ##### Takes 250 | 251 | | Name | In/out | Meaning | 252 | |:-----:|:------:|:-------------------| 253 | | e | in | exception to raise | 254 | 255 | ##### Returns 256 | 257 | Nothing. 258 | 259 | 260 | #### `void EXCEPT_RERAISE(void)` 261 | 262 | `EXCEPT_RERAISE()` macro re-raises the exception that has been raised most 263 | recently. 264 | 265 | ##### May raise 266 | 267 | The exception that has been raised most recently. 268 | 269 | ##### Returns 270 | 271 | Nothing. 272 | 273 | 274 | ### 2.2. TRY-EXCEPT construct 275 | 276 | TRY-EXCEPT and TRY-FINALLY statements can be constructed using several macros 277 | that help ordinary C code to look like having them. 278 | 279 | 280 | #### `EXCEPT_TRY` 281 | 282 | The `EXCEPT_TRY` macro starts a TRY statement. Statements (referred to as `S` 283 | hereafter) whose exception is to be handled in EXCEPT, ELSE or FINALLY clause 284 | follow. 285 | 286 | _Do not forget using `EXCEPT_RETURN` when returning within `S`. See 287 | `EXCEPT_RETURN` for more details. Besides, the TRY-EXCEPT/FINALLY statement 288 | uses the non-local jump mechanism provided by ``, which means any 289 | restriction applied to `` also applies to the TRY-EXCEPT/FINALLY 290 | statement._ 291 | 292 | 293 | #### `EXCEPT_EXCEPT(e)` 294 | 295 | The `EXCEPT_EXCEPT(e)` macro starts an EXCEPT(e) clause. When an exception `e` 296 | is raised during execution of `S`, statements following `EXCEPT_EXCEPT(e)` are 297 | executed. After finishing execution of those statements, the control moves to 298 | the end of the TRY-EXCEPT statement. 299 | 300 | 301 | #### `EXCEPT_ELSE` 302 | 303 | The `EXCEPT_ELSE` macro starts an ELSE clause. If there is no matched EXCEPT 304 | clause for a raised exception, the control moves to the statements following 305 | the ELSE clause. 306 | 307 | 308 | #### `EXCEPT_FINALLY` 309 | 310 | The `EXCEPT_FINALLY` macro starts a FINALLY clause. It is used to construct a 311 | TRY-FINALLY statement, which is useful when some clean-up is necessary before 312 | exiting the TRY-FINALLY statement; the statements within the FINALLY clause are 313 | executed whether or not an exception occurs. The `EXCEPT_RERAISE` macro can be 314 | used to hand over the not-yet-handled exception to the previous handler. 315 | 316 | _Remember that, raising an exception pops up the exception stack and re-raising 317 | an exception in a FINALLY clause has the effect to move the control to the 318 | outer (previous) handler. Also note that, even if not explicitly specified, a 319 | TRY-EXCEPT-FINALLY statement (there are both EXCEPT and FINALLY clauses) is 320 | possible and works as expected._ 321 | 322 | 323 | #### `EXCEPT_END` 324 | 325 | The `EXCEPT_END` macro ends a TRY-EXCEPT or TRY-FINALLY statement. If a raised 326 | exception is not handled by the current handler, it will be passed to the 327 | previous handler if any. 328 | 329 | 330 | ### 2.3. Returning within TRY-EXCEPT 331 | 332 | #### `EXCEPT_RETURN` 333 | 334 | The `EXCEPT_RETURN` macro returns the control to the caller function within a 335 | TRY-CATCH statement. 336 | 337 | In order to maintain the stack handling nested exceptions, the ordinary 338 | `return` statement should be avoided in statements following `EXCEPT_TRY`. 339 | Because `return` has no idea about the exception frame, `returning` without 340 | using `EXCEPT_RETURN` from statements following `EXCEPT_TRY` spoils the 341 | exception stack. `EXCEPT_RETURN` adjusts the stack properly by popping the 342 | current exception frame in such a case. 343 | 344 | _Note that the current exception frame is popped when an exception occurs 345 | during execution of statements following `EXCEPT_TRY` and before the control 346 | moves to one of EXCEPT, ELSE and FINALLY clauses, which means using 347 | `EXCEPT_RETURN` within those clauses is not allowed because it touches the 348 | previous, not the current, exception frame._ 349 | 350 | 351 | ## 3. Future directions 352 | 353 | ### 3.1. Stack traces 354 | 355 | The current implementation provides no information about the execution stack of 356 | a program when an occurred exception leads to abnormal termination. They have 357 | to track function calls by themselves to pinpoint the problem, which is surely 358 | a burden on programmers. Thus, showing stack traces on an uncaught exception 359 | would be useful especially when they include enough information like callers' 360 | names, calling sites and preferably arguments. 361 | 362 | 363 | ## 4. Contact me 364 | 365 | Visit [`code.woong.org`](https://code.woong.org) to get the latest version of 366 | this library. Any comments about the library are welcomed. If you have a 367 | proposal or question on the library just email me, and I will reply as soon as 368 | possible. 369 | 370 | 371 | ## 5. Copyright 372 | 373 | For the copyright issues, see `LICENSE.md`. 374 | -------------------------------------------------------------------------------- /doc/cdsl/dlist.md: -------------------------------------------------------------------------------- 1 | C data structure library: doubly-linked list 2 | ============================================ 3 | 4 | This document specifies the doubly-linked list library which belongs to C data 5 | structure library. The basic structure is from David Hanson's book, 6 | [C Interfaces and Implementations](https://sites.google.com/site/cinterfacesimplementations/). 7 | I modified the original implementation to make it more appropriate for my other 8 | projects, to speed up operations and to enhance its readability. 9 | 10 | The book explains its design and implementation in a very comprehensive way. 11 | Not to mention the copyright issues, the internals of the library is not to be 12 | explained here. Explanations for APIs, however, are given to aid the use of the 13 | library. 14 | 15 | 16 | ## 1. Introduction 17 | 18 | The doubly-linked list library is a typical implementation of a 19 | [doubly-linked list](https://en.wikipedia.org/wiki/Doubly_linked_list) in which 20 | nodes have two pointers to their next and previous nodes; a list with a 21 | unidirectional pointer 22 | (a [singly-linked list](https://en.wikipedia.org/wiki/Linked_list)) is 23 | implemented in the list library. The storage used to maintain a list itself is 24 | managed by the library, but any storage allocated for data stored in nodes 25 | should be managed by a user program. 26 | 27 | This library reserves identifiers starting with `dlist_` and `DLIST_`, and 28 | imports the assertion library (which requires the exception library) and the 29 | memory library. 30 | 31 | 32 | ### 1.1. How to use the library 33 | 34 | Similarly to other data structure libraries, a typical use of the doubly-linked 35 | list library follows this sequence: create, use and destroy. Except for 36 | functions to inspect lists, all other functions do one of them in various ways. 37 | 38 | As opposed to a singly-linked list, a doubly-linked list enables its nodes to 39 | be accessed randomly. To speed up such accesses, the library is revised from 40 | the original version so that a list remembers which node was last accessed. If 41 | a request is made to access a node that is next or previous to the remembered 42 | node, the library locates it starting from the remembered node. This is from 43 | observation that traversing a list from the head or the tail in sequence occurs 44 | frequently in many programs and can make a program making heavy use of lists 45 | run almost 3 times faster. Therefore, for good performance of your program, it 46 | is highly recommended that lists are traversed sequentially whenever possible. 47 | Do not forget that the current implementation requires the library to locate 48 | the desired node from the head or the tail for other types of accesses (that 49 | is, any access to a node that is not immediately next or previous to a 50 | remembered node). 51 | 52 | Using a list starts with creating one. The simplest way to do it is to call 53 | `dlist_new()`. `dlist_new()` returns an empty list, and if it fails to allocate 54 | storage for the list, an exception `mem_exceptfail` is raised rather than 55 | returning a null pointer. All functions that allocate storage signal a shortage 56 | of memory via the exception; no null pointer returned. There is another 57 | function to create a list: `dlist_list()` that accepts a sequence of data and 58 | creates a list containing them in each node. 59 | 60 | Once a list has been created, a new node can be inserted in various ways 61 | (`dlist_add()`, `dlist_addhead()` and `dlist_addtail()`) and an existing node 62 | can be removed from a list also in various ways (`dlist_remove()`, 63 | `dlist_remhead()` and `dlist_remtail()`). You can inspect the data of a node 64 | (`dlist_get()`) or replace it with new one (`dlist_put()`). In addition, you 65 | can find the number of nodes in a list (`dlist_length()`) or can rotate (or 66 | shift) a list (`dlist_shift()`). For an indexing scheme used when referring to 67 | existing nodes, see `dlist_get()`. For that used when referring to a position 68 | into which a new node inserted, see `dlist_add()`. 69 | 70 | `dlist_free()` destroys a list that is no longer necessary, but note that any 71 | storage that is allocated by a user program does not get freed with it; 72 | `dlist_free()` only returns back the storage allocated by the library. 73 | 74 | 75 | ### 1.2. Boilerplate code 76 | 77 | As an example, the following code creates a list and stores input characters 78 | into each node until `EOF` encountered. After read, it copies characters in 79 | nodes to continuous storage area to construct a string and prints the string. 80 | 81 | int c; 82 | char *p, *q; 83 | dlist_t *mylist; 84 | 85 | mylist = dlist_new(); 86 | 87 | while ((c = getc(stdin)) != EOF) { 88 | MEM_NEW(p); 89 | *p = c; 90 | dlist_addtail(mylist, p); 91 | } 92 | 93 | n = dlist_length(mylist); 94 | 95 | p = MEM_ALLOC(n+1); 96 | for (i = 0; i < n; i++) { 97 | p = dlist_get(mylist, i); 98 | q[i] = *p; 99 | MEM_FREE(p); 100 | } 101 | q[i] = '\0'; 102 | 103 | dlist_free(&mylist); 104 | 105 | puts(q); 106 | 107 | where `MEM_NEW()`, `MEM_ALLOC()` and `MEM_FREE()` come from the memory library. 108 | 109 | Note that, before adding a node to a list, unique storage to contain a 110 | character is allocated with `MEM_NEW()` and this storage is returned back by 111 | `MEM_FREE()` while copying characters into an allocated array. 112 | 113 | 114 | ## 2. APIs 115 | 116 | ### 2.1. Types 117 | 118 | #### `dlist_t` 119 | 120 | `dlist_t` represents a doubly-linked list. 121 | 122 | 123 | ### 2.2. Creating and destroying lists 124 | 125 | #### `dlist_t *dlist_new(void)` 126 | 127 | `dlist_new()` creates an empty list and returns it for further use. 128 | 129 | ##### May raise 130 | 131 | `mem_exceptfail` (see the memory library from `cbl`). 132 | 133 | ##### Takes 134 | 135 | Nothing. 136 | 137 | ##### Returns 138 | 139 | An empty new list. 140 | 141 | 142 | #### `dlist_t *dlist_list(void *data, ...)` 143 | 144 | `dlist_list()` constructs a doubly-linked list whose nodes contain a sequence 145 | of data given as arguments. The first argument is stored in the head (first) 146 | node, the second argument in the second node and so on. 147 | 148 | There should be a way to mark the end of the argument list, which a null 149 | pointer is for. Any argument following a null pointer argument is not invalid, 150 | but ignored. 151 | 152 | _Calling `dlist_list()` with one argument, a null pointer, is not treated as an 153 | error. Such a call request an empty list like calling `dlist_new()`._ 154 | 155 | ##### May raise 156 | 157 | `mem_exceptfail` (see the memory library). 158 | 159 | ##### Takes 160 | 161 | | Name | In/out | Meaning | 162 | |:-----:|:------:|:---------------------------------------| 163 | | data | in | data to store in head node of new list | 164 | | ... | in | other data to store in new list | 165 | 166 | ##### Returns 167 | 168 | A new list containing a given sequence of data. 169 | 170 | 171 | #### `void dlist_free(dlist_t **pdlist)` 172 | 173 | `dlist_free()` destroys a list by deallocating storages for each node and for 174 | the list itself. 175 | 176 | After a call to `dlist_free()`, the list does not exist (do not be confused by 177 | a non-existing list with an existing but empty list). If `pdlist` points to a 178 | null pointer, an assertion in `dlist_free()` fails. 179 | 180 | ##### May raise 181 | 182 | `assert_exceptfail` (see the assertion library from `cbl`). 183 | 184 | ##### Takes 185 | 186 | | Name | In/out | Meaning | 187 | |:------:|:------:|:---------------------------| 188 | | pdlist | in/out | pointer to list to destroy | 189 | 190 | ##### Returns 191 | 192 | Nothing. 193 | 194 | 195 | ### 2.3. Adding and removing nodes 196 | 197 | #### `void *dlist_add(dlist_t *dlist, long pos, void *data)` 198 | 199 | `dlist_add()` inserts a new node to a position specified by `pos`. 200 | 201 | The position is interpreted as follows: (5 nodes assumed to be in a list) 202 | 203 | 1 2 3 4 5 6 positive position values 204 | +-+ +-+ +-+ +-+ +-+ 205 | | |--| |--| |--| |--| | 206 | +-+ +-+ +-+ +-+ +-+ 207 | -5 -4 -3 -2 -1 0 non-positive position values 208 | 209 | Non-positive positions are useful when to locate without knowing the length of 210 | a list. If a list is empty both 0 and 1 are the valid values for a new node. 211 | Note that `pos - (dlist_length()+1)` gives a non-negative value for a positive 212 | `pos`, and `pos + (dlist_length()+1)` gives a positive value for a negative 213 | `pos`. 214 | 215 | ##### May raise 216 | 217 | `assert_exceptfail` (see the assertion library) and `mem_exceptfail` (see the 218 | memory library). 219 | 220 | ##### Takes 221 | 222 | | Name | In/out | Meaning | 223 | |:-----:|:------:|:----------------------------------------| 224 | | dlist | in/out | list to which new node will be inserted | 225 | | pos | in | position for new node | 226 | | data | in | data for new node | 227 | 228 | ##### Returns 229 | 230 | Data for a new node. 231 | 232 | 233 | #### `void *dlist_addhead(dlist_t *dlist, void *data)` 234 | 235 | `dlist_addhead()` inserts a new node before the head node; the new node will be 236 | the head node. `dlist_addhead()` is equivalent to dlist_add() with `1` given 237 | for the position. 238 | 239 | ##### May raise 240 | 241 | `assert_exceptfail` (see the assertion library) and `mem_exceptfail` (see the 242 | memory library). 243 | 244 | ##### Takes 245 | 246 | | Name | In/out | Meaning | 247 | |:-----:|:------:|:----------------------------------------| 248 | | dlist | in/out | list to which new node will be inserted | 249 | | data | in | data for new node | 250 | 251 | ##### Returns 252 | 253 | Data for a new node. 254 | 255 | 256 | #### `void *dlist_addtail(dlist_t *dlist, void *data)` 257 | 258 | `dlist_addtail()` inserts a new node after the last node; the index for the new 259 | node will be N if there are N nodes before the insertion. If a list is empty, 260 | `dlist_addtail()` and `dlist_addhead()` do the same job. `dlist_addtail()` is 261 | equivalent to `dlist_add()` with `0` given for the position. 262 | 263 | ##### May raise 264 | 265 | `assert_exceptfail` (see the assertion library) and `mem_exceptfail` (see the 266 | memory library). 267 | 268 | ##### Takes 269 | 270 | | Name | In/out | Meaning | 271 | |:-----:|:------:|:----------------------------------------| 272 | | dlist | in/out | list to which new node will be inserted | 273 | | data | in | data for new node | 274 | 275 | ##### Returns 276 | 277 | Data for a new node. 278 | 279 | 280 | #### `void *dlist_remove(dlist_t *dlist, long i)` 281 | 282 | `dlist_remove()` removes the `i`-th node from a list. For indexing, see 283 | `dlist_get()`. 284 | 285 | ##### May raise 286 | 287 | `assert_exceptfail` (see the assertion library). 288 | 289 | ##### Takes 290 | 291 | | Name | In/out | Meaning | 292 | |:-----:|:------:|:-------------------------------------| 293 | | dlist | in/out | list from which node will be removed | 294 | | i | in | index for node to remove | 295 | 296 | ##### Returns 297 | 298 | Data of the removed node. 299 | 300 | 301 | #### `void *dlist_remhead(dlist_t *dlist)` 302 | 303 | `dlist_remhead()` removes the first (head) node from a list. `dlist_remhead()` 304 | is equivalent to `dlist_remove()` with `0` for the position. 305 | 306 | ##### May raise 307 | 308 | `assert_exceptfail` (see the assertion library). 309 | 310 | ##### Takes 311 | 312 | | Name | In/out | Meaning | 313 | |:-----:|:------:|:-----------------------------------------------| 314 | | dlist | in/out | list from which the first node will be removed | 315 | 316 | ##### Returns 317 | 318 | Data of the removed node. 319 | 320 | 321 | #### `void *dlist_remtail(dlist_t *dlist)` 322 | 323 | `dlist_remtail()` removes the last (tail) node of a list. `dlist_remtail()` is 324 | equivalent to `dlist_remove()` with `dlist_length()-1` for the index. 325 | 326 | ##### May raise 327 | 328 | `assert_exceptfail` (see the assertion library). 329 | 330 | ##### Takes 331 | 332 | | Name | In/out | Meaning | 333 | |:-----:|:------:|:----------------------------------------------| 334 | | dlist | in/out | list from which the last node will be removed | 335 | 336 | ##### Returns 337 | 338 | Data of the removed node. 339 | 340 | 341 | ### 2.4. Handling lists 342 | 343 | #### `long dlist_length(const dlist_t *dlist)` 344 | 345 | `dlist_length()` returns the length of a list, the number of nodes in it. 346 | 347 | ##### May raise 348 | 349 | `assert_exceptfail` (see the assertion library). 350 | 351 | ##### Takes 352 | 353 | | Name | In/out | Meaning | 354 | |:-----:|:------:|:-----------------------------------| 355 | | dlist | in | list whose length will be returned | 356 | 357 | ##### Returns 358 | 359 | The length of a list (non-negative). 360 | 361 | 362 | #### `void *dlist_get(dlist_t *dlist, long i)` 363 | 364 | `dlist_get()` brings and return data in the `i`-th node in a list. The first 365 | node has the index 0 and the last has _n_-1 when there are _n_ nodes in a list. 366 | 367 | ##### May raise 368 | 369 | `assert_exceptfail` (see the assertion library). 370 | 371 | ##### Takes 372 | 373 | | Name | In/out | Meaning | 374 | |:-----:|:------:|:---------------------------------------| 375 | | dlist | in/out | list from which data will be retrieved | 376 | | i | in | index for node | 377 | 378 | ##### Returns 379 | 380 | Data retrieved from a node. 381 | 382 | 383 | #### `void *dlist_put(dlist_t *dlist, long i, void *data)` 384 | 385 | `dlist_put()` replaces the data stored in the `i`-th node with new given data 386 | and retrieves the old data. For indexing, see `dlist_get()`. 387 | 388 | ##### May raise 389 | 390 | `assert_exceptfail` (see the assertion library). 391 | 392 | ##### Takes 393 | 394 | | Name | In/out | Meaning | 395 | |:-----:|:------:|:---------------------------------| 396 | | dlist | in/out | list whose data will be replaced | 397 | | i | in | index for node | 398 | | data | in | new data for substitution | 399 | 400 | ##### Returns 401 | 402 | Old data stored in a node. 403 | 404 | 405 | #### `void dlist_shift(dlist_t *dlist, long n)` 406 | 407 | `dlist_shift()` shifts a list to right or left according to the value of `n`. 408 | 409 | A positive value indicates shift to right; for example shift by 1 means to make 410 | the tail node become the head node. Similarly, a negative value indicates shift 411 | to left; for example shift by -1 means to make the head node become the tail 412 | node. 413 | 414 | The absolute value of the shift distance specified by `n` should be equal to or 415 | less than the length of a list. For exmple, `dlist_shift(..., 7)` or 416 | `dlist_shift(..., -7)` is not allowed when there are only 6 nodes in a list. In 417 | such a case, `dlist_shift(..., 6)` or `dlist_shift(..., -6)` has no effect as 418 | `dlist_shift(..., 0)`. 419 | 420 | Note that it is a list itself that `dlist_shift()` shifts, not the head pointer 421 | of a list. 422 | 423 | ##### May raise 424 | 425 | `assert_exceptfail` (see the assertion library). 426 | 427 | ##### Takes 428 | 429 | | Name | In/out | Meaning | 430 | |:-----:|:------:|:--------------------------------| 431 | | dlist | in/out | list to shift | 432 | | n | in | direction and distance of shift | 433 | 434 | ##### Returns 435 | 436 | Nothing. 437 | 438 | 439 | ## 3. Contact me 440 | 441 | Visit [`code.woong.org`](https://code.woong.org) to get the latest version of 442 | this library. Any comments about the library are welcomed. If you have a 443 | proposal or question on the library just email me, and I will reply as soon as 444 | possible. 445 | 446 | 447 | ## 4. Copyright 448 | 449 | For the copyright issues, see `LICENSE.md`. 450 | -------------------------------------------------------------------------------- /doc/cdsl/hash.md: -------------------------------------------------------------------------------- 1 | C data structure library: hash 2 | ============================== 3 | 4 | This document specifies the hash list library which belongs to C data structure 5 | library. The basic structure is from David Hanson's book, 6 | [C Interfaces and Implementations](https://sites.google.com/site/cinterfacesimplementations/). 7 | I modified the original implementation to make it more appropriate for my other 8 | projects, to speed up operations, to add missing but useful facilities and to 9 | enhance its readability. 10 | 11 | The book explains its design and implementation in a very comprehensive way. 12 | Not to mention the copyright issues, the internals of the library is not to be 13 | explained here. Explanations for APIs, however, are given to aid the use of the 14 | library. 15 | 16 | 17 | ## 1. Introduction 18 | 19 | The hash library implements a 20 | [hash table](https://en.wikipedia.org/wiki/Hash_table) and is one of the most 21 | frequently used libraries; it is essential to get a hash key for datum before 22 | putting it into [tables](https://en.wikipedia.org/wiki/Associative_array) by 23 | the table library or into sets by the set library. The storage used to maintain 24 | the hash table is managed by the library and no function in the library demands 25 | memory allocation done by user code. 26 | 27 | This library reserves identifiers starting with `hash_` and `HASH_`, and 28 | imports the assertion library (which requires the exception library) and the 29 | memory library. 30 | 31 | 32 | ### 1.1. How to use the library 33 | 34 | The library provides one global hash table, so that there is no function that 35 | creates a table or destroy it. A user can start to use the hash table without 36 | its creation just by putting data to it using an appropriate function: 37 | `hash_string()` for C strings, `hash_int()` for signed integers and 38 | `hash_new()` for other arbitrary forms of data. Of course, because the library 39 | internally allocates storage to manage hash keys and values, functions to 40 | remove a certain key from the table and to completely clean up the table are 41 | offered: `hash_free()` and `hash_reset()`. 42 | 43 | In addition, strings are very often used to generate hash keys for them, so 44 | `hash_vload()` and `hash_aload()` are provided and useful especially when 45 | preloading several strings onto the table. 46 | 47 | 48 | ### 1.2. Caveats 49 | 50 | A common mistake made when using the hash library is to pass data to functions 51 | that expect a hash key without making one. For example, `table_put()` from the 52 | table library requires its second argument be a hash key, but it is likely to 53 | careless write the following code to put to a table referred to as `mytable` a 54 | string key and its relevant value: 55 | 56 | char *key, *val; 57 | /* ... */ 58 | table_put(mytable, key, val); 59 | 60 | This code, however, does not work because the second argument to `table_put()` 61 | should be a hash key not a raw string. Thus, the correct one should read as: 62 | 63 | table_put(mytable, hash_string(key), val); 64 | 65 | One more thing to note is that `hash_string()` and similar functions to 66 | generate a hash key is an actual function. If a hash key obtained from your 67 | data is frequently used in code, it is better for efficiency to have it in a 68 | variable rather than to call hash_string() several times. 69 | 70 | If your compiler rejects to compile the library with a diagnostic including 71 | `scatter[] assumes UCHAR_MAX < 256!` which says `CHAR_BIT` (the number of bits 72 | in a byte) in your environment is larger than 8, you have to add elements to 73 | the array `scatter` to make its total number match the number of characters in 74 | your implementation. `scatter` is used to map a character to a random number. 75 | For how to generate the array, see the explanation given for the array in code. 76 | 77 | 78 | ## 2. APIs 79 | 80 | ### 2.1. Creating hash strings 81 | 82 | #### `const char *hash_string(const char *str)` 83 | 84 | `hash_string()` returns a hash string for a given null-terminated string. It is 85 | equivalent to call `hash_new()` with the string and its length counted by 86 | `strlen()`. 87 | 88 | Note that the trailing null character is not counted and it is appended when 89 | storing into the hash table. An empty string which is consisted only of a null 90 | character is also a valid argument for `hash_string()`; see `hash_new()` for 91 | details. 92 | 93 | ##### May raise 94 | 95 | `assert_exceptfail` (see the assertion library from `cbl`) and `mem_exceptfail` 96 | (see the memory library from `cbl`). 97 | 98 | ##### Takes 99 | 100 | | Name | In/out | Meaning | 101 | |:-----:|:------:|:----------------------------------------------| 102 | | str | in | string for which hash string will be returned | 103 | 104 | ##### Returns 105 | 106 | A hash string for a null-terminated string. 107 | 108 | 109 | #### `const char *hash_int(long n)` 110 | 111 | `hash_int()` returns a hash string for a given, possibly signed, integer whose 112 | type is `long`. 113 | 114 | ##### May raise 115 | 116 | `mem_exceptfail` (see the memory library). 117 | 118 | ##### Takes 119 | 120 | | Name | In/out | Meaning | 121 | |:-----:|:------:|:-----------------------------------------------| 122 | | n | in | integer for which hash string will be returned | 123 | 124 | ##### Returns 125 | 126 | A hash string for an integer. 127 | 128 | 129 | #### `const char *hash_new(const char *byte, size_t len)` 130 | 131 | `hash_new()` returns a hash string for a given byte sequence, which may not end 132 | with a null character or may have a null character embedded in it. 133 | 134 | Even if it has "new" in its name, `hash_new()` just returns the existing hash 135 | string if there is already one created for the same byte sequence, which means 136 | there is only one instance of each byte sequence in the hash table; that is 137 | what hashing is for. 138 | 139 | An empty byte sequence which contains nothing and whose length is 0 is also 140 | valid. But remember that `byte` has a valid pointer value by pointing to a 141 | valid object even when `len` is zero; C allows no zero-sized object. 142 | 143 | _It leads to an unpredictable result to modify a hash string returned by the 144 | library._ 145 | 146 | ##### May raise 147 | 148 | `assert_exceptfail` (see the assertion library) and `mem_exceptfail` (see the 149 | memory library). 150 | 151 | ##### Takes 152 | 153 | | Name | In/out | Meaning | 154 | |:-----:|:------:|:-----------------------------------------------------| 155 | | byte | in | byte sequence for which hash string will be returned | 156 | | len | in | length of byte sequence | 157 | 158 | ##### Returns 159 | 160 | A hash string for a byte sequence. 161 | 162 | 163 | #### `void hash_vload(const char *, ...)` 164 | 165 | `hash_vload()` takes a possibly empty sequence of null-terminated strings and 166 | puts them into the hash table. There should be a way to mark the end of the 167 | argument list, which a null pointer is for. 168 | 169 | This is useful when a program needs to preload some strings to the hash table 170 | for later use. An array-version of `hash_vload()` is also provided; see 171 | `hash_aload()`. 172 | 173 | ##### May raise 174 | 175 | `mem_exceptfail` (see the memory library). 176 | 177 | ##### Takes 178 | 179 | | Name | In/out | Meaning | 180 | |:-----:|:------:|:--------------------------------------------| 181 | | str | in | null-terminated string to put to hash table | 182 | | ... | in | other such strings | 183 | 184 | ##### Returns 185 | 186 | Nothing. 187 | 188 | 189 | #### `void hash_aload(const char *[])` 190 | 191 | `hash_aload()` takes strings from an array of strings (precisely, an array of 192 | pointers to `char`) and puts them into the hash table. Because the function 193 | does not take the size of the string array, there should be a way to mark end 194 | of the array, which a null pointer is for. 195 | 196 | This is useful when a program needs to preload some strings to the hash table 197 | for later use. A variadic version is also provided; see `hash_vload()`. 198 | 199 | ##### May raise 200 | 201 | `mem_exceptfail` (see the memory library). 202 | 203 | ##### Takes 204 | 205 | | Name | In/out | Meaning | 206 | |:-----:|:------:|:------------------------------------------------------| 207 | | strs | in | array of null-terminated strings to put to hash table | 208 | 209 | ##### Returns 210 | 211 | Nothing. 212 | 213 | 214 | ### 2.2. Destroying the hash table 215 | 216 | #### `void hash_free(const char *)` 217 | 218 | `hash_free()` deallocates storage for a hash string, which effectively 219 | eliminates a hash string from the hash table. This facility is not used 220 | frequently by user code so that the original implementation does not provide 221 | it. 222 | 223 | ##### May raise 224 | 225 | `assert_exceptfail` (see the assertion library). 226 | 227 | ##### Takes 228 | 229 | | Name | In/out | Meaning | 230 | |:-----:|:------:|:----------------------| 231 | | byte | in | hash string to remove | 232 | 233 | ##### Returns 234 | 235 | Nothing. 236 | 237 | 238 | #### `void hash_reset(void)` 239 | 240 | `hash_reset()` deallocates all hash strings in the hash table and thus resets 241 | it. 242 | 243 | ##### May raise 244 | 245 | Nothing. 246 | 247 | ##### Takes 248 | 249 | Nothing. 250 | 251 | ##### Returns 252 | 253 | Nothing. 254 | 255 | 256 | ### 2.3. Miscellaneous 257 | 258 | #### `size_t hash_length(const char *byte)` 259 | 260 | Given a hash string, `hash_length()` returns its length. 261 | 262 | ##### May raise 263 | 264 | `assert_exceptfail` (see the assertion library). 265 | 266 | ##### Takes 267 | 268 | | Name | In/out | Meaning | 269 | |:-----:|:------:|:--------------------------------------------| 270 | | byte | in | byte sequence whose length will be returned | 271 | 272 | ##### Returns 273 | 274 | The length of a byte sequence. 275 | 276 | 277 | ## 3. Contact me 278 | 279 | Visit [`code.woong.org`](https://code.woong.org) to get the latest version of 280 | this library. Any comments about the library are welcomed. If you have a 281 | proposal or question on the library just email me, and I will reply as soon as 282 | possible. 283 | 284 | 285 | ## 4. Copyright 286 | 287 | For the copyright issues, see `LICENSE.md`. 288 | -------------------------------------------------------------------------------- /doc/cdsl/stack.md: -------------------------------------------------------------------------------- 1 | C data structure library: stack 2 | =============================== 3 | 4 | This document specifies the stack library which belongs to C data structure 5 | library. The basic structure is from David Hanson's book, 6 | [C Interfaces and Implementations](https://sites.google.com/site/cinterfacesimplementations/). 7 | I modified the original implementation to enhance its readability. 8 | 9 | The book explains its design and implementation in a very comprehensive way. 10 | Not to mention the copyright issues, the internals of the library is not to be 11 | explained here. Explanations for APIs, however, are given to aid the use of the 12 | library. 13 | 14 | 15 | ## 1. Introduction 16 | 17 | The stack library is a typical implementation of a 18 | [stack](https://en.wikipedia.org/wiki/Stack_%28abstract_data_type%29). Even if 19 | its implementation is very similar to that of the list library, the details are 20 | hidden behind an abstract type called `stack_t` because, unlike lists, 21 | revealing the implementation of a stack hardly brings benefit. The storage used 22 | to maintain a stack itself is managed by the library, but any storage allocated 23 | for data stored in a stack should be managed by a user program. 24 | 25 | This library reserves identifiers starting with `stack_` and `STACK_`, and 26 | imports the assertion library (which requires the exception library) and the 27 | memory library. 28 | 29 | 30 | ### 1.1. How to use the library 31 | 32 | Similarly for other data structure libraries, use of the stack library follows 33 | this sequence: create, use and destroy. If functions that allocate storage fail 34 | memory allocation, an exception `mem_exceptfail` is raised; therefore functions 35 | never return a null pointer. 36 | 37 | Using a stack starts with creating it. There is only one function provided to 38 | create a new stack, `stack_new()`. Calling it returns a new and empty stack. 39 | 40 | Once a stack has been created, you can push data into or pop it from a stack 41 | using `stack_push()` and `stack_pop()`, respectively. `stack_peek()` also can 42 | be used to see what is stored at the top of a stack without popping it out. 43 | Because popping an empty stack triggers an exception `assert_exceptfail`, 44 | calling `stack_empty()` is recommended to inspect if a stack is empty before 45 | applying `stack_pop()` to it. 46 | 47 | `stack_free()` destroys a stack that is no longer necessary, but note that any 48 | storage that is allocated by a user program does not get freed with it; 49 | `stack_free()` only returns back the storage allocated by the library. 50 | 51 | 52 | ### 1.2. Boilerplate code 53 | 54 | As an example, the following code creates a stack and pushes input characters 55 | into it until `EOF` encountered. After that, it prints the characters by 56 | popping the characters and destroy the stack. 57 | 58 | int c; 59 | char *p, *q; 60 | stack_t *mystack; 61 | 62 | mystack = stack_new(); 63 | while ((c = getc(stdin)) != EOF) { 64 | MEM_NEW(p); 65 | *p = c; 66 | stack_push(mystack, p); 67 | } 68 | 69 | while (!stack_empty(mystack)) { 70 | p = stack_peek(mystack); 71 | q = stack_pop(mystack); 72 | assert(p == q); 73 | putchar(*p); 74 | MEM_FREE(p); 75 | } 76 | putchar('\n'); 77 | 78 | stack_free(&mystack); 79 | 80 | where `MEM_NEW()` and `MEM_FREE()` come from the memory library. 81 | 82 | Note that before invoking `stack_pop()`, the stack is checked whether empty or 83 | not by `stack_empty()` and that when popping characters, the storage allocated 84 | for them gets freed. 85 | 86 | 87 | ## 2. APIs 88 | 89 | ### 2.1. Types 90 | 91 | #### `stack_t` 92 | 93 | `stack_t` represents a stack. 94 | 95 | 96 | ### 2.1. Creating and destroying stacks 97 | 98 | #### `stack_t *stack_new(void)` 99 | 100 | `stack_new()` creates a new stack and sets its relevant information to the 101 | initial. 102 | 103 | ##### May raise 104 | 105 | `mem_exceptfail` (see the memory library from `cbl`). 106 | 107 | ##### Takes 108 | 109 | Nothing. 110 | 111 | ##### Returns 112 | 113 | A created stack. 114 | 115 | 116 | #### `void stack_free(stack_t **stk)` 117 | 118 | `stack_free()` deallocates all storages for a stack and set the pointer passed 119 | through `stk` to a null pointer. 120 | 121 | Note that `stack_free()` does not deallocate any storage for the data in the 122 | stack to destroy, which must be done by a user. 123 | 124 | ##### May raise 125 | 126 | `assert_exceptfail` (see the assertion library from `cbl`). 127 | 128 | ##### Takes 129 | 130 | | Name | In/out | Meaning | 131 | |:----:|:------:|:----------------------------| 132 | | stk | in/out | pointer to stack to destroy | 133 | 134 | ##### Returns 135 | 136 | Nothing. 137 | 138 | 139 | ### 2.2. Handling stacks 140 | 141 | #### `void stack_push(stack_t *stk, void *data)` 142 | 143 | `stack_push()` pushes data into the top of a stack. 144 | 145 | There is no explicit limit on the maximum number of data that can be pushed 146 | into a stack. 147 | 148 | ##### May raise 149 | 150 | `assert_exceptfail` (see the assertion library) and `mem_exceptfail` (see the 151 | memory library). 152 | 153 | ##### Takes 154 | 155 | | Name | In/out | Meaning | 156 | |:----:|:------:|:-------------------------------------| 157 | | stk | in/out | stack into which data will be pushed | 158 | | data | in | data to push | 159 | 160 | ##### Returns 161 | 162 | Nothing. 163 | 164 | 165 | #### `void *stack_pop(stack_t *stk)` 166 | 167 | `stack_pop()` pops data from a stack. 168 | 169 | If the stack is empty, an exception is raised due to the assertion failure, so 170 | popping all data without knowing the number of nodes remained in the stack 171 | needs to use `stack_empty()` to decide when to stop. 172 | 173 | ##### May raise 174 | 175 | `assert_exceptfail` (see the assertion library). 176 | 177 | ##### Takes 178 | 179 | | Name | In/out | Meaning | 180 | |:----:|:------:|:-------------------------------------| 181 | | stk | in/out | stack from which data will be popped | 182 | 183 | ##### Returns 184 | 185 | Data popped from the a stack. 186 | 187 | 188 | #### `void *stack_peek(const stack_t *stk)` 189 | 190 | `stack_peek()` provides a way to inspect the top-most data in a stack without 191 | popping it up. 192 | 193 | ##### May raise 194 | 195 | `assert_exceptfail` (see the assertion library). 196 | 197 | ##### Takes 198 | 199 | | Name | In/out | Meaning | 200 | |:----:|:------:|:--------------| 201 | | stk | in | stack to peek | 202 | 203 | ##### Returns 204 | 205 | The top-most data in a stack. 206 | 207 | 208 | ### 2.3. Miscellaneous 209 | 210 | #### `int stack_empty(const stack_t *)` 211 | 212 | `stack_empty()` inspects if a stack is empty. 213 | 214 | ##### May raise 215 | 216 | `assert_exceptfail` (see the assertion library). 217 | 218 | ##### Takes 219 | 220 | | Name | In/out | Meaning | 221 | |:----:|:------:|:-----------------| 222 | | stk | in | stack to inspect | 223 | 224 | ##### Returns 225 | 226 | | Value | Meaning | 227 | |:-----:|:----------| 228 | | `0` | not empty | 229 | | `1` | empty | 230 | 231 | 232 | ## 3. Contact me 233 | 234 | Visit [`code.woong.org`](https://code.woong.org) to get the latest version of 235 | this library. Any comments about the library are welcomed. If you have a 236 | proposal or question on the library just email me, and I will reply as soon as 237 | possible. 238 | 239 | 240 | ## 4. Copyright 241 | 242 | For the copyright issues, see `LICENSE.md`. 243 | -------------------------------------------------------------------------------- /doc/cdsl/table.md: -------------------------------------------------------------------------------- 1 | C data structure library: table 2 | =============================== 3 | 4 | This document specifies the table library which belongs to C data structure 5 | library. The basic structure is from David Hanson's book, 6 | [C Interfaces and Implementations](https://sites.google.com/site/cinterfacesimplementations/). 7 | I modified the original implementation to make it more appropriate for my other 8 | projects and to enhance its readability. 9 | 10 | The book explains its design and implementation in a very comprehensive way. 11 | Not to mention the copyright issues, the internals of the library is not to be 12 | explained here. Explanations for APIs, however, are given to aid the use of the 13 | library. 14 | 15 | 16 | ## 1. Introduction 17 | 18 | The table library implements 19 | [tables](https://en.wikipedia.org/wiki/Associative_array) that are similar to 20 | arrays except that nothing is imposed on the type of indices; it is also known 21 | as a _dictionary_, _associative array_ or _map_ in some languages. A table 22 | stores a key and its associated value, and its user is free to put new 23 | key-value pairs, to retrieve a value from a key, to replace a value with a new 24 | one, and to get rid of key-value pairs from a table. The storage used to 25 | maintain a table itself is managed by the library, but any storage allocated 26 | for data stored in tables should be managed by a user program. 27 | 28 | This library reserves identifiers starting with `table_` and `TABLE_`, and 29 | imports the assertion library (which requires the exception library) and the 30 | memory library. 31 | 32 | 33 | ### 1.1. How to use the library 34 | 35 | Similarly for other data structure libraries, use of the table library follows 36 | this sequence: create, use and destroy. Except for functions to inspect tables, 37 | all other functions do one of them in various ways. 38 | 39 | `table_new()` that creates an empty table takes three unusual arguments. The 40 | first one is a hint for the expected length of the table it will create, and 41 | the other two are to specify user-defined functions that perform creation and 42 | comparison of hash values used to represent keys. Some important conditions 43 | that those functions have to satisfy are described in `table_new()`. 44 | 45 | Using a table starts with creating one using `table_new()`. It is important to 46 | give `table_new()` three arguments properly. If keys to a table are generated 47 | by the hash library, the second and third arguments can be granted null 48 | pointers, which lets internal functions used for the table. `table_new()` 49 | allocates a storage necessary for a table and if no allocation is possible, an 50 | exception is raised instead of returning a failure indicator like a null 51 | pointer. 52 | 53 | Once a table created, a key-value pair can be added to and removed from a table 54 | using `table_put()` and `table_remove()`. Adding a pair to a table also entails 55 | memory allocation, and thus an exception may be raised. `table_get()` takes a 56 | key and returns its associated value if any, and `table_length()` gives the 57 | number of key-value pairs in a table. 58 | 59 | There are two ways to apply some operations on every pair in a table; 60 | `table_map()` takes a user-defined function and calls it for each of key-value 61 | pairs, and `table_toarray()` converts a table into a dynamic array; the array 62 | converted from a table has keys in elements with even indices and values in 63 | those with odd indices. Storage for the generated array is allocated by the 64 | library (thus, an exception is possible again), but a user program is 65 | responsible for releasing the storage when the array is no longer necessary. 66 | 67 | `table_free()` takes a table and releases the storage used to maintain it. Note 68 | that any storage allocated by a user program to contain or represent keys and 69 | values is not deallocated by the library. 70 | 71 | 72 | ### 1.2. Boilerplate code 73 | 74 | As an example, the following code creates a table (whose expected length is set 75 | to 20 and keys are generated by the hash library), and uses it to compute the 76 | frequencies of different characters in the input. To explain details, it first 77 | inspects if the table already has an input character and its frequency in it. 78 | If found, the frequency is simply increased. Otherwise, storage to contain the 79 | frequency is allocated and put into the table after set properly. (This is 80 | intended to just show an example for using a table; an ordinary array is enough 81 | to print the frequencies of different characters that appear in the user 82 | input.) 83 | 84 | int c; 85 | char b; 86 | int *pfrq; 87 | table_t *mytab; 88 | const char *key; 89 | void **pa, **pb; 90 | 91 | mytab = table_new(20, NULL, NULL); 92 | 93 | while ((c = getchar()) != EOF) { 94 | b = c; 95 | key = hash_new(&b, 1); 96 | if ((pfrq = table_get(mytab, key))) == NULL) { 97 | MEM_NEW(pfrq); 98 | *pfrq = 0; 99 | } 100 | (*pfrq)++; 101 | table_put(mytab, key, pfrq); 102 | } 103 | 104 | pa = table_toarray(mytab, NULL); 105 | for (pb = pa; *pb; pb += 2) { 106 | printf("%c: %d\n", *(char *)pb[0], *(int *)pb[1]); 107 | MEM_FREE(pb[1]); 108 | } 109 | MEM_FREE(pa); 110 | 111 | hash_reset(); 112 | table_free(&mytab); 113 | 114 | where `hash_new()` and `hash_reset()` come from the hash library, and 115 | `MEM_NEW()` and `MEM_FREE()` from the memory library. 116 | 117 | Things to note include: 118 | - storages for keys and values to be stored into a table should be prepared by 119 | a user program, not the library, thus releasing them properly is up to the 120 | user program (`MEM_FREE()` in the `for` loop above and `hash_reset()` release 121 | them); 122 | - because a table has pointers to objects for the frequencies, not their 123 | values, increasing values in the objects effectively updates the table; and 124 | - an array generated by `table_toarray()` has to be deallocated by a user code. 125 | 126 | 127 | ## 2. APIs 128 | 129 | ### 2.1. Types 130 | 131 | #### `table_t` 132 | 133 | `table_t` represents a table. 134 | 135 | 136 | ### 2.2. Creating and Destroying tables 137 | 138 | #### `table_t *table_new(int, int cmp(), unsigned hash())` 139 | 140 | `table_new()` creates a new table. It takes some information on a table it will 141 | create: 142 | - `hint`: an estimated size of a table; 143 | - `cmp`: a user-provided function of type `int (const void *, const void *)` to 144 | compare members; and 145 | - `hash`: a user-provided function of type `unsigned (const void *)` to 146 | generatefor generating a hash value from a key 147 | 148 | `table_new()` determines the size of the internal hash table kept in a table 149 | based on `hint`. It never restricts the number of entries one can put into a 150 | table, but a better estimate probably gives better performance. 151 | 152 | A function given to `cmp` should be defined to take two arguments and to return 153 | a value less than, equal to or greater than zero to indicate that the first 154 | argument is less than, equal to or greater than the second argument, 155 | respectively. 156 | 157 | A function given to `hash` takes a key and returns a hash value that is to be 158 | used as an index for the internal hash table in a table. If the `cmp` function 159 | returns zero (which means they are equal) for two keys, the `hash` function 160 | must generate the same value for them. 161 | 162 | If a null pointer is given for `cmp` or `hash`, the default comparison or 163 | hashing function is used; see `defhashCmp()` and `defhashGen()`, in which case 164 | keys are assumed to be hash strings generated by the hash library. An example 165 | follows: 166 | 167 | table_t *mytable = table_new(hint, NULL, NULL); 168 | int *pi, val1, val2; 169 | /* ... */ 170 | table_put(hash_string("key1"), &val1); 171 | table_put(hash_string("key2"), &val2); 172 | pi = table_get(hash_string(key1)); 173 | assert(pi == &val1); 174 | 175 | ##### May raise 176 | 177 | `mem_exceptfail` (see the memory library from `cbl`). 178 | 179 | ##### Takes 180 | 181 | | Name | In/out | Meaning | 182 | |:-----:|:------:|:--------------------------------------| 183 | | hint | in | hint for the size of table | 184 | | cmp | in | user-defined comparison function | 185 | | hash | in | user-defined hash generation function | 186 | 187 | ##### Returns 188 | 189 | A new table created. 190 | 191 | 192 | #### `void table_free(table_t **ptable)` 193 | 194 | `table_free()` destroys a table by deallocating the storage for it and set a 195 | given pointer to the null pointer. As always, `table_free()` does not 196 | deallocate storages for values in the table, which must be done by a user. 197 | 198 | ##### May raise 199 | 200 | `assert_exceptfail` (see the assertion library from `cbl`). 201 | 202 | ##### Takes 203 | 204 | | Name | In/out | Meaning | 205 | |:------:|:------:|:----------------------------| 206 | | ptable | in/out | pointer to table to destroy | 207 | 208 | ##### Returns 209 | 210 | Nothing. 211 | 212 | 213 | ### 2.3. Handling data in a table 214 | 215 | #### `size_t table_length(const table_t *table)` 216 | 217 | `table_length()` returns the length of a table which is the number of key-value 218 | pairs in a table. 219 | 220 | ##### May raise 221 | 222 | `assert_exceptfail` (see the assertion library). 223 | 224 | ##### Takes 225 | 226 | | Name | In/out | Meaning | 227 | |:-----:|:------:|:------------------------------------| 228 | | table | in | table whose length will be returned | 229 | 230 | ##### Returns 231 | 232 | The length of a table. 233 | 234 | 235 | #### `void *table_put(table_t *table, const void *key, void *value)` 236 | 237 | `table_put()` replaces an existing value for a key with a new one and returns 238 | the previous value. If there is no existing one, the value is saved for the key 239 | and returns a null pointer. 240 | 241 | Note that both a key and a value are pointers. If values are, say, integers in 242 | an application, objects to contain them are necessary to put them into a table. 243 | 244 | ##### May raise 245 | 246 | `assert_exceptfail` (see the assertion library) and `mem_exceptfail` (see the 247 | memory library). 248 | 249 | ##### Takes 250 | 251 | | Name | In/out | Meaning | 252 | |:-----:|:------:|:------------------------------------| 253 | | table | in/out | table to which value will be stored | 254 | | key | in | key to store into a table | 255 | | value | in | value to store into a table | 256 | 257 | ##### Returns 258 | 259 | The previous value for the key or null pointer. 260 | 261 | _If the stored value was a null pointer, an ambiguous situation may occur._ 262 | 263 | 264 | #### `void *table_get(const table_t *table, const void *key)` 265 | 266 | `table_get()` returns a value associated with a key. 267 | 268 | ##### May raise 269 | 270 | `assert_exceptfail` (see the assertion library). 271 | 272 | ##### Takes 273 | 274 | | Name | In/out | Meaning | 275 | |:-----:|:------:|:---------------------------------------------------| 276 | | table | in | table from which a key-value pair will be searched | 277 | | key | in | key to find from table | 278 | 279 | ##### Returns 280 | 281 | The value for a given key or null pointer. 282 | 283 | _If the stored value was a null pointer, an ambiguous situation may occur._ 284 | 285 | 286 | #### `void *table_remove(table_t *table, const void *key)` 287 | 288 | `table_remove()` gets rid of a key-value pair for a key from a table. Note that 289 | `table_remove()` does not deallocate any storage for the pair to remove, which 290 | must be done by an user. 291 | 292 | ##### May raise 293 | 294 | `assert_exceptfail` (see the assertion library) 295 | 296 | ##### Takes 297 | 298 | | Name | In/out | Meaning | 299 | |:-----:|:------:|:--------------------------------------------------| 300 | | table | in | table from which a key-value pair will be removed | 301 | | key | in | key to remove | 302 | 303 | ##### Returns 304 | 305 | The value for a given key or null pointer. 306 | 307 | _If the stored value is a null pointer, an ambiguous situation may occur._ 308 | 309 | 310 | ### 2.4. Handling tables 311 | 312 | #### `void table_map(table_t *table, void apply(const void *, void **, void *), void *cl)` 313 | 314 | For each key-value pair in a table, `table_map()` calls a user-provided 315 | callback function; it is useful when doing some common task for each key-value 316 | pair. The callback function has a type of 317 | `void (const void *, void **, void *)`, and a key, a pointer to a value and the 318 | passing-by argument `cl` are passed to it. 319 | 320 | The pointer given in `cl` is passed to the third parameter of a user callback 321 | function, so can be used as a communication channel between the caller of 322 | `table_map()` and the callback. Because the callback has the address of a value 323 | through the second parameter, it is free to change its content. 324 | 325 | _The order in which a user-provided function is called for each key-value pair 326 | is unspecified._ 327 | 328 | ##### May raise 329 | 330 | `assert_exceptfail` (see the assertion library). 331 | 332 | ##### Takes 333 | 334 | | Name | In/out | Meaning | 335 | |:-----:|:------:|:------------------------------------------| 336 | | table | in/out | table with which `apply()` will be called | 337 | | apply | in | user-provided function (callback) | 338 | | cl | in | passing-by argument to `apply()` | 339 | 340 | ##### Returns 341 | 342 | Nothing. 343 | 344 | 345 | #### `void **table_toarray(const table_t *table, void *end)` 346 | 347 | `table_toarray()` converts key-value pairs stored in a table to an array. The 348 | last element of the constructed array is assigned by `end`, which is a null 349 | pointer in most cases. Do not forget to deallocate the array when it is 350 | unnecessary. 351 | 352 | The resulting array is consisted of key-value pairs: its elements with even 353 | indices have keys and those with odd indices have corresponding values. For 354 | example, the second element of a resulting array has a value corresponding to a 355 | key stored in the first element. Note that the end-marker given as `end` has no 356 | corresponding value element. 357 | 358 | _The size of an array generated from an empty table cannot be zero, because 359 | there is always an end-mark value._ 360 | 361 | _As in `table_map()`, the order in which an array is created for each key-value 362 | pair is unspecified._ 363 | 364 | ##### May raise 365 | 366 | `assert_exceptfail` (see the assertion library) and `mem_exceptfail` (see the 367 | memory library). 368 | 369 | ##### Takes 370 | 371 | | Name | In/out | Meaning | 372 | |:-----:|:------:|:-------------------------------------------------| 373 | | table | in | table from which an array will be generated | 374 | | end | in | end-mark to save in the last element of an array | 375 | 376 | ##### Returns 377 | 378 | An array generated from a table. 379 | 380 | 381 | ## 3. Future directions 382 | 383 | ### 3.1. Retaining sequence 384 | 385 | The table library offers two functions that scan every key-value pair 386 | maintained in tables. These functions visit key-value pairs in order that is 387 | dependent on the internal structure of the table, which might force a user to 388 | sort them properly if certain sequence is desired. It would be thus helpful to 389 | have those functions to retain sequence in which key-value pairs are stored 390 | into tables. 391 | 392 | 393 | ## 4. Contact me 394 | 395 | Visit [`code.woong.org`](https://code.woong.org) to get the latest version of 396 | this library. Any comments about the library are welcomed. If you have a 397 | proposal or question on the library just email me, and I will reply as soon as 398 | possible. 399 | 400 | 401 | ## 5. Copyright 402 | 403 | For the copyright issues, see `LICENSE.md`. 404 | -------------------------------------------------------------------------------- /src/cbl/arena.c: -------------------------------------------------------------------------------- 1 | /* 2 | * arena (cbl) 3 | */ 4 | 5 | #include /* size_t, NULL */ 6 | #include /* malloc, free */ 7 | #include /* memset */ 8 | #if __STDC_VERSION__ >= 199901L /* C99 supported */ 9 | #include /* uintptr_t */ 10 | #endif /* __STDC_VERSION__ */ 11 | 12 | #include "cbl/assert.h" /* assert with exception support */ 13 | #include "cbl/except.h" /* EXCEPT_RAISE, except_raise */ 14 | #include "arena.h" 15 | 16 | 17 | /* smallest multiple of y greater than or equal to x */ 18 | #define MULTIPLE(x, y) ((((x)+(y)-1)/(y)) * (y)) 19 | 20 | /* checks if pointer aligned properly */ 21 | #define ALIGNED(p) ((uintptr_t)(p) % sizeof(union align) == 0) 22 | 23 | /* max number of memory chunks in freelist */ 24 | #define FREE_THRESHOLD 10 25 | 26 | 27 | #if __STDC_VERSION__ >= 199901L /* C99 supported */ 28 | # ifndef UINTPTR_MAX /* C99, but uintptr_t not provided */ 29 | # error "No integer type to contain pointers without loss of information!" 30 | # endif /* UINTPTR_MAX */ 31 | #else /* C90, uintptr_t surely not supported */ 32 | typedef unsigned long uintptr_t; 33 | #endif /* __STDC_VERSION__ */ 34 | 35 | /* 36 | * An arena is consisted of a list of memory chunks. Each chuck has struct arena_t at its start 37 | * address, and a memory area that can be used by an application follows it. 38 | * 39 | * Three pointer members of struct arena_t point to somewhere in the previous memory chunk. For 40 | * example, suppose that there is only one memory chunk allocated so far. The head node (the node 41 | * pointed to by arena below) that is remembered by an application and passed to, say, 42 | * arena_free() has three pointers, each of which points to somewhere in that single allocated 43 | * chunk. And its arena_t parts are all set to a null pointer. The following depicts this 44 | * situation: 45 | * 46 | * +---+ <----\ +---+ <------ arena 47 | * | 0 | \------| | prev 48 | * +---+ +---+ 49 | * | 0 | /----| | avail 50 | * +---+ / +---+ 51 | * | 0 | / /---| | limit 52 | * +---+ / / +---+ 53 | * | . | / / 54 | * | A | <--/ / 55 | * | . | / 56 | * +---+ <---/ 57 | * 58 | * After one more chunk has been allocated, arena points to it, and the arena_t parts of the new 59 | * chunk point to the previous chunk marked A as shown below: 60 | * 61 | * +---+ <----\ +---+ <----\ +---+ <------ arena 62 | * | 0 | \------| | \------| | prev 63 | * +---+ +---+ +---+ 64 | * | 0 | /---| | /----| | avail 65 | * +---+ / +---+ / +---+ 66 | * | 0 | / /---| | / /---| | limit 67 | * +---+ / / +---+ / / +---+ 68 | * | . | / / | . | / / 69 | * | A | / / | B | <--/ / 70 | * | . | <--/ / | . | / 71 | * +---+ <---/ +---+ <---/ 72 | * 73 | * This can be thought as pushing a new memory chunk through the head node. 74 | */ 75 | struct arena_t { 76 | struct arena_t *prev; /* previously allocated chunk */ 77 | char *avail; /* start of available area in previous chunk */ 78 | char *limit; /* end of previous chunk */ 79 | }; 80 | 81 | /* 82 | * union align tries to automatically determine the maximum alignment requirement imposed by an 83 | * implementation; if you know the exact restriction, define MEM_MAXALIGN properly - a compiler 84 | * option like -D should be a proper place for it. If the guess is wrong, the memory library is 85 | * not guaranteed to work properly, or more severely programs may crash. 86 | * 87 | * Even if this library employs the prefix arena_ or ARENA_, the prefix of the memory library 88 | * MEM_ is used below because union align is first introduced there. 89 | */ 90 | union align { 91 | #ifdef MEM_MAXALIGN 92 | char pad[MEM_MAXALIGN]; 93 | #else 94 | int i; 95 | long l; 96 | long *lp; 97 | void *p; 98 | void (*fp)(void); 99 | float f; 100 | double d; 101 | long double ld; 102 | #endif 103 | }; 104 | 105 | /* 106 | * As shown in the explanation of arena_t, the memory area that is to be used by an application is 107 | * attached after the arena_t part. Because its starting address should be properly aligned as 108 | * those returned by malloc() are, it might be necessary to put some padding between the arena_t 109 | * part and the user memory area. union header do this job using union align. It ensures that 110 | * there is enough space for arena_t and the starting address of the following user area is 111 | * properly aligned; there should be some padding after the member a if necessary in order to make 112 | * the second element properly aligned in an array of the union header type. 113 | */ 114 | union header { 115 | arena_t b; 116 | union align a; 117 | }; 118 | 119 | 120 | /* exception for arena creation failure */ 121 | const except_t arena_exceptfailNew = { "Arena creation failed" }; 122 | 123 | /* exception for memory allocation failure */ 124 | const except_t arena_exceptfailAlloc = { "Arena allocation failed" }; 125 | 126 | 127 | /* 128 | * freelist is a list of free memory chunks. When arena_free() called, before it really releases 129 | * the storage with free(), it sets aside that chunk into freelist. This reduces necessity of 130 | * calling malloc(). On the other hand, if freelist maintains too many instances of free chunks, 131 | * invocations for allocation using other memory allocator (for example, mem_alloc() from the 132 | * memory library) would fail. That is why arena_free() keeps no more than FREE_THRESHOLD chunks 133 | * in freelist. 134 | * 135 | * Differently from memory chunks described by arena_t, chunks in the free list have their limit 136 | * member point to their own limits; see arena_t for comparison. 137 | */ 138 | static arena_t *freelist; 139 | 140 | /* 141 | * freenum is incresed when a memory chunk is pushed to freelist by arena_free() and decresed when 142 | * it is pushed back to the arena_t list by arena_alloc(). 143 | */ 144 | static int freenum; 145 | 146 | 147 | /* 148 | * creates a new arena 149 | */ 150 | arena_t *(arena_new)(void) 151 | { 152 | arena_t *arena; 153 | 154 | arena = malloc(sizeof(*arena)); /* use malloc() to separate arena library from a specific 155 | memory allocator like memory library */ 156 | if (!arena) 157 | EXCEPT_RAISE(arena_exceptfailNew); 158 | 159 | arena->prev = NULL; 160 | arena->limit = arena->avail = NULL; 161 | 162 | return arena; 163 | } 164 | 165 | 166 | /* 167 | * disposes an arena 168 | */ 169 | void (arena_dispose)(arena_t **parena) 170 | { 171 | assert(parena); 172 | assert(*parena); 173 | 174 | arena_free(*parena); 175 | free(*parena); 176 | *parena = NULL; 177 | } 178 | 179 | 180 | /* 181 | * allocates storage associated with an arena 182 | * 183 | * There are three cases where arena_alloc() successfully returns storage: 184 | * - if the first chunk in the arena_t list has enough space for the request, it is returned; 185 | * - otherwise, the first chunk in the free list (if any) is pushed back to the arena_t list and 186 | * go to the first step; 187 | * - if there is nothing in the free list, new storage is allocated by malloc() and pushed to the 188 | * arena_t list and go to the first step. 189 | * 190 | * Note that the second case is not able to make a sure find while the third is, which is why 191 | * arena_alloc() contains a loop. 192 | * 193 | * The original code in the book does not check if the limit or avail member of arena is a null 194 | * pointer; by definition, operations like comparing two pointers, subtracting a pointer from 195 | * another and adding an integer to a pointer result in undefined behavior if they are involved 196 | * with a null pointer, thus fixed here. 197 | */ 198 | #if __STDC_VERSION__ >= 199901L /* C99 version */ 199 | void *(arena_alloc)(arena_t *arena, size_t n, const char *file, const char *func, int line) 200 | #else /* C90 version */ 201 | void *(arena_alloc)(arena_t *arena, size_t n, const char *file, int line) 202 | #endif /* __STDC_VERSION__ */ 203 | { 204 | assert(arena); 205 | assert(n > 0); 206 | 207 | n = MULTIPLE(n, sizeof(union align)); 208 | 209 | assert(arena->limit >= arena->avail); 210 | /* first request or requested size > left size of first chunk */ 211 | while (!arena->limit || n > arena->limit - arena->avail) { 212 | arena_t *p; 213 | char *limit; 214 | 215 | if ((p = freelist) != NULL) { /* free chunks exist in freelist */ 216 | freelist = freelist->prev; 217 | freenum--; /* chunk to be pushed back to arena_t list, so decresed */ 218 | limit = p->limit; 219 | } else { /* allocation needed */ 220 | size_t m = sizeof(union header) + n + 10*1024; /* enough to save arena_t + requested 221 | size + extra (10Kb) */ 222 | p = malloc(m); 223 | if (!p) { 224 | if (!file) 225 | EXCEPT_RAISE(arena_exceptfailAlloc); 226 | else 227 | #if __STDC_VERSION__ >= 199901L /* C99 version */ 228 | except_raise(&arena_exceptfailAlloc, file, func, line); 229 | #else /* C90 version */ 230 | except_raise(&arena_exceptfailAlloc, file, line); 231 | #endif /* __STDC_VERSION__ */ 232 | } 233 | assert(ALIGNED(p)); /* checks if guess at alignment restriction holds - if fails, 234 | define MEM_MAXALIGN properly */ 235 | limit = (char *)p + m; 236 | } 237 | *p = *arena; /* copies previous arena info to newly allocated chunk */ 238 | /* makes head point to newly pushed chunk */ 239 | arena->avail = (char *)((union header *)p + 1); 240 | arena->limit = limit; 241 | arena->prev = p; 242 | } 243 | /* chunk having free space with enough size found */ 244 | arena->avail += n; 245 | 246 | return arena->avail - n; 247 | } 248 | 249 | 250 | /* 251 | * allocates zero-filled storage associated with an arena 252 | * 253 | * TODO: 254 | * - the C standard requires calloc() return a null pointer if it cannot allocates storage of 255 | * the size c * n in byte, which allows no overflow in computing the multiplication. 256 | * Overflow checking is necessary to mimic the behavior of calloc(). 257 | */ 258 | #if __STDC_VERSION__ >= 199901L /* C99 version */ 259 | void *(arena_calloc)(arena_t *arena, size_t c, size_t n, const char *file, const char *func, 260 | int line) 261 | #else /* C90 version */ 262 | void *(arena_calloc)(arena_t *arena, size_t c, size_t n, const char *file, int line) 263 | #endif /* __STDC_VERSION__ */ 264 | { 265 | void *p; 266 | 267 | assert(c > 0); 268 | 269 | #if __STDC_VERSION__ >= 199901L /* C99 version */ 270 | p = arena_alloc(arena, c*n, file, func, line); 271 | #else /* C90 version */ 272 | p = arena_alloc(arena, c*n, file, line); 273 | #endif /* __STDC_VERSION__ */ 274 | memset(p, '\0', c*n); 275 | 276 | return p; 277 | } 278 | 279 | 280 | /* 281 | * deallocates all storages belonging to an arena 282 | * 283 | * arena_free() does its job by by popping memory chunks belonging to an arena until it gets 284 | * empty. Those popped chunks are released by free() if there are already FREE_THRESHOLD chunks in 285 | * freelist, or pushed to freelist otherwise. 286 | */ 287 | void (arena_free)(arena_t *arena) 288 | { 289 | assert(arena); 290 | 291 | while (arena->prev) { 292 | arena_t tmp = *arena->prev; 293 | if (freenum < FREE_THRESHOLD) { /* need to set aside to freelist */ 294 | arena->prev->prev = freelist; /* prev of to-be-freed = existing freelist */ 295 | freelist = arena->prev; /* freelist = to-be-freed */ 296 | freenum++; 297 | freelist->limit = arena->limit; /* in the free list, each chunk has the limit member 298 | point to its own limit; see arena_t for 299 | comparison */ 300 | } else /* freelist is full; deallocate */ 301 | free(arena->prev); 302 | *arena = tmp; 303 | } 304 | 305 | /* all are freed here */ 306 | assert(!arena->limit); 307 | assert(!arena->avail); 308 | } 309 | 310 | /* end of arena.c */ 311 | -------------------------------------------------------------------------------- /src/cbl/arena.h: -------------------------------------------------------------------------------- 1 | /* 2 | * arena (cbl) 3 | */ 4 | 5 | #ifndef ARENA_H 6 | #define ARENA_H 7 | 8 | #include /* size_t */ 9 | 10 | #include "cbl/except.h" /* except_t */ 11 | 12 | 13 | /* arena */ 14 | typedef struct arena_t arena_t; 15 | 16 | 17 | /* exceptions for arena creation/allocation failure */ 18 | extern const except_t arena_exceptfailNew; 19 | extern const except_t arena_exceptfailAlloc; 20 | 21 | 22 | arena_t *arena_new(void); 23 | #if __STDC_VERSION__ >= 199901L /* C99 version */ 24 | void *arena_alloc(arena_t *, size_t, const char *, const char *, int); 25 | void *arena_calloc(arena_t *, size_t, size_t, const char *, const char *, int); 26 | #else /* C90 version */ 27 | void *arena_alloc(arena_t *, size_t, const char *, int); 28 | void *arena_calloc(arena_t *, size_t, size_t, const char *, int); 29 | #endif /* __STDC_VERSION__ */ 30 | void arena_free(arena_t *); 31 | void arena_dispose(arena_t **); 32 | 33 | 34 | /* macro wrappers for functions */ 35 | #define ARENA_NEW() (arena_new()) 36 | #define ARENA_DISPOSE(pa) (arena_dispose(pa)) 37 | #if __STDC_VERSION__ >= 199901L /* C99 version */ 38 | #define ARENA_ALLOC(a, n) (arena_alloc((a), (n), __FILE__, __func__, __LINE__)) 39 | #define ARENA_CALLOC(a, c, n) (arena_calloc((a), (c), (n), __FILE__, __func__, __LINE__)) 40 | #else /* C90 version */ 41 | #define ARENA_ALLOC(a, n) (arena_alloc((a), (n), __FILE__, __LINE__)) 42 | #define ARENA_CALLOC(a, c, n) (arena_calloc((a), (c), (n), __FILE__, __LINE__)) 43 | #endif /* __STDC_VERSION__ */ 44 | #define ARENA_FREE(a) (arena_free(a)) 45 | 46 | 47 | #endif /* ARENA_H */ 48 | 49 | /* end of arena.h */ 50 | -------------------------------------------------------------------------------- /src/cbl/assert.c: -------------------------------------------------------------------------------- 1 | /* 2 | * assertion (cbl) 3 | */ 4 | 5 | #include "cbl/assert.h" 6 | #include "cbl/except.h" /* except_t */ 7 | 8 | 9 | /* exception for assertion failure */ 10 | const except_t assert_exceptfail = { "Assertion failed" }; 11 | 12 | /* end of assert.c */ 13 | -------------------------------------------------------------------------------- /src/cbl/assert.h: -------------------------------------------------------------------------------- 1 | /* 2 | * assertion (cbl) 3 | */ 4 | 5 | #ifndef ASSERT_H 6 | #define ASSERT_H 7 | 8 | #include "cbl/except.h" /* except_t, EXCEPT_RAISE */ 9 | 10 | 11 | #if defined(NDEBUG) || defined(ASSERT_STDC_VER) /* standard version requested */ 12 | #include 13 | #else /* use "assert.h" supporting exception */ 14 | /* replaces standard assert() */ 15 | #define assert(e) ((void)((e) || (EXCEPT_RAISE(assert_exceptfail), 0))) 16 | #endif /* NDEBUG || ASSERT_STDC_VER */ 17 | 18 | 19 | /* exception for assertion failure */ 20 | extern const except_t assert_exceptfail; 21 | 22 | 23 | #endif /* ASSERT_H */ 24 | 25 | /* end of assert.h */ 26 | -------------------------------------------------------------------------------- /src/cbl/except.c: -------------------------------------------------------------------------------- 1 | /* 2 | * exception (cbl) 3 | */ 4 | 5 | #include /* NULL */ 6 | #include /* longjmp */ 7 | #include /* stderr, fprintf, fflush */ 8 | #include /* abort */ 9 | 10 | #include "cbl/assert.h" /* assert with exception support */ 11 | #include "except.h" 12 | 13 | 14 | /* stack for nested exceptions */ 15 | except_frame_t *except_stack = NULL; 16 | 17 | 18 | /* 19 | * raises an exception and set its information properly 20 | * 21 | * Note that the current exception frame is popped as soon as possible, which enables another 22 | * exception occurred while handling the current exception to be handled by the previous hander. 23 | * 24 | * TODO: 25 | * - it would be useful to show stack traces when an uncaught exception leads to abortion of a 26 | * program. The stack traces should include as much information as possible, for example, 27 | * names of caller functions, calling sites (file name, function name and line number) and 28 | * arguments. 29 | */ 30 | #if __STDC_VERSION__ >= 199901L /* C99 version */ 31 | void (except_raise)(const except_t *e, const char *file, const char *func, int line) 32 | #else /* C90 version */ 33 | void (except_raise)(const except_t *e, const char *file, int line) 34 | #endif /* __STDC_VERSION__ */ 35 | { 36 | except_frame_t *p = except_stack; /* current exception frame */ 37 | 38 | assert(e); 39 | if (!p) { /* no current exception frame */ 40 | fprintf(stderr, "Uncaught exception"); 41 | if (e->exception && e->exception[0] != '\0') 42 | fprintf(stderr, " %s", e->exception); 43 | else 44 | fprintf(stderr, " at 0x%p", (void *)e); 45 | #if __STDC_VERSION__ >= 199901L /* C99 version */ 46 | if (file && func && line > 0) 47 | fprintf(stderr, " raised at %s() %s:%d\n", func, file, line); 48 | #else /* C90 version */ 49 | if (file && line > 0) 50 | fprintf(stderr, " raised at %s:%d\n", file, line); 51 | #endif /* __STDC_VERSION__ */ 52 | fprintf(stderr, "Aborting...\n"); 53 | fflush(stderr); 54 | abort(); 55 | } else { 56 | /* set exception frame properly */ 57 | p->exception = e; 58 | p->file = file; 59 | #if __STDC_VERSION__ >= 199901L /* C99 supported */ 60 | p->func = func; 61 | #endif 62 | p->line = line; 63 | 64 | /* pop exception stack */ 65 | except_stack = except_stack->prev; 66 | 67 | /* exception raised but not handled yet, so EXCEPT_RAISED */ 68 | longjmp(p->env, EXCEPT_RAISED); 69 | } 70 | } 71 | 72 | /* end of except.c */ 73 | -------------------------------------------------------------------------------- /src/cbl/except.h: -------------------------------------------------------------------------------- 1 | /* 2 | * exception (cbl) 3 | */ 4 | 5 | #ifndef EXCEPT_H 6 | #define EXCEPT_H 7 | 8 | #include /* setjmp, jmp_buf */ 9 | 10 | 11 | /* exception */ 12 | typedef struct except_t { 13 | const char *exception; /* exception name */ 14 | } except_t; 15 | 16 | /* exception frame for nested exceptions */ 17 | typedef struct except_frame_t { 18 | struct except_frame_t *prev; /* previous exception frame */ 19 | jmp_buf env; /* jmp_buf for current exception */ 20 | const char *file; /* file name in which exception raised */ 21 | #if __STDC_VERSION__ >= 199901L /* C99 supported */ 22 | const char *func; /* function name in which exception raised */ 23 | #endif /* __STDC_VERSION__ */ 24 | int line; /* line number on which exception raised */ 25 | const except_t *exception; /* exception name */ 26 | } except_frame_t; 27 | 28 | /* exception handling state; 29 | EXCEPT_ENTERED is set to zero, return value from initial call to setjmp() */ 30 | enum { 31 | EXCEPT_ENTERED = 0, /* exception handling started and no exception raised */ 32 | EXCEPT_RAISED, /* exception raised and not handled yet */ 33 | EXCEPT_HANDLED, /* exception handled */ 34 | EXCEPT_FINALIZED /* exception finalized */ 35 | }; 36 | 37 | 38 | /* stack for nested exceptions */ 39 | extern except_frame_t *except_stack; 40 | 41 | 42 | #if __STDC_VERSION__ >= 199901L /* C99 version */ 43 | void except_raise(const except_t *, const char *, const char *, int); 44 | #else /* C90 version */ 45 | void except_raise(const except_t *, const char *, int); 46 | #endif /* __STDC_VERSION__ */ 47 | 48 | 49 | /* raises exceptions */ 50 | #if __STDC_VERSION__ >= 199901L /* C99 version */ 51 | #define EXCEPT_RAISE(e) except_raise(&(e), __FILE__, __func__, __LINE__) 52 | #define EXCEPT_RERAISE except_raise(except_frame.exception, except_frame.file, \ 53 | except_frame.func, except_frame.line) 54 | #else /* C90 version */ 55 | #define EXCEPT_RAISE(e) except_raise(&(e), __FILE__, __LINE__) 56 | #define EXCEPT_RERAISE except_raise(except_frame.exception, except_frame.file, except_frame.line) 57 | #endif /* __STDC_VERSION__ */ 58 | 59 | /* returns to caller within TRY-EXCEPT statement */ 60 | #define EXCEPT_RETURN switch(except_stack=except_stack->prev, 0) default: return 61 | 62 | /* starts TRY statement */ 63 | #define EXCEPT_TRY \ 64 | { \ 65 | volatile int except_flag; \ 66 | /* volatile */ except_frame_t except_frame; \ 67 | except_frame.prev = except_stack; \ 68 | except_stack = &except_frame; \ 69 | except_flag = setjmp(except_frame.env); \ 70 | if (except_flag == EXCEPT_ENTERED) { 71 | 72 | /* 73 | * starts EXCEPT(e) clause 74 | * 75 | * The indented if plays its role only when an EXCEPT clause follows statements S; it handles the 76 | * case where no exception raised during execution of S. 77 | */ 78 | #define EXCEPT_EXCEPT(e) \ 79 | if (except_flag == EXCEPT_ENTERED) \ 80 | except_stack = except_stack->prev; \ 81 | } else if (except_frame.exception == &(e)) { \ 82 | except_flag = EXCEPT_HANDLED; 83 | 84 | /* starts ELSE clause */ 85 | #define EXCEPT_ELSE \ 86 | if (except_flag == EXCEPT_ENTERED) \ 87 | except_stack = except_stack->prev; \ 88 | } else { \ 89 | except_flag = EXCEPT_HANDLED; 90 | 91 | /* starts FINALLY clause */ 92 | #define EXCEPT_FINALLY \ 93 | if (except_flag == EXCEPT_ENTERED) \ 94 | except_stack = except_stack->prev; \ 95 | } \ 96 | { \ 97 | if (except_flag == EXCEPT_ENTERED) \ 98 | except_flag = EXCEPT_FINALIZED; 99 | 100 | /* ends TRY-EXCEPT or TRY-FINALLY statement */ 101 | #define EXCEPT_END \ 102 | if (except_flag == EXCEPT_ENTERED) \ 103 | except_stack = except_stack->prev; \ 104 | } \ 105 | if (except_flag == EXCEPT_RAISED) \ 106 | EXCEPT_RERAISE; \ 107 | } 108 | 109 | 110 | #endif /* EXCEPT_H */ 111 | 112 | /* end of except.h */ 113 | -------------------------------------------------------------------------------- /src/cbl/memory.c: -------------------------------------------------------------------------------- 1 | /* 2 | * memory - production version (cbl) 3 | */ 4 | 5 | #include /* size_t */ 6 | #include /* malloc, calloc, realloc, free */ 7 | 8 | #include "cbl/assert.h" /* assert with exception support */ 9 | #include "cbl/except.h" /* EXCEPT_RAISE, except_raise */ 10 | #include "memory.h" 11 | 12 | #define UNUSED(id) ((void)(id)) 13 | 14 | 15 | /* exception for memory allocation failure */ 16 | const except_t mem_exceptfail = { "Allocation failed" }; 17 | 18 | 19 | /* 20 | * allocates storage of the size n in bytes 21 | */ 22 | #if __STDC_VERSION__ >= 199901L /* C99 version */ 23 | void *(mem_alloc)(size_t n, const char *file, const char *func, int line) 24 | #else /* C90 version */ 25 | void *(mem_alloc)(size_t n, const char *file, int line) 26 | #endif /* __STDC_VERSION__ */ 27 | { 28 | void *p; 29 | 30 | assert(n > 0); /* precludes zero-sized allocation */ 31 | 32 | p = malloc(n); 33 | if (!p) 34 | { 35 | if (!file) 36 | EXCEPT_RAISE(mem_exceptfail); 37 | else 38 | #if __STDC_VERSION__ >= 199901L /* C99 version */ 39 | except_raise(&mem_exceptfail, file, func, line); 40 | #else /* C90 version */ 41 | except_raise(&mem_exceptfail, file, line); 42 | #endif /* __STDC_VERSION__ */ 43 | } 44 | 45 | return p; 46 | } 47 | 48 | 49 | /* 50 | * allocates zero-filled storage of the size c * n in bytes 51 | */ 52 | #if __STDC_VERSION__ >= 199901L /* C99 version */ 53 | void *(mem_calloc)(size_t c, size_t n, const char *file, const char *func, int line) 54 | #else /* C90 version */ 55 | void *(mem_calloc)(size_t c, size_t n, const char *file, int line) 56 | #endif /* __STDC_VERSION__ */ 57 | { 58 | void *p; 59 | 60 | assert(c > 0); /* precludes zero-sized (de)allocation */ 61 | assert(n > 0); 62 | 63 | p = calloc(c, n); 64 | if (!p) 65 | { 66 | if (!file) 67 | EXCEPT_RAISE(mem_exceptfail); 68 | else 69 | #if __STDC_VERSION__ >= 199901L /* C99 version */ 70 | except_raise(&mem_exceptfail, file, func, line); 71 | #else /* C90 version */ 72 | except_raise(&mem_exceptfail, file, line); 73 | #endif /* __STDC_VERSION__ */ 74 | } 75 | 76 | return p; 77 | } 78 | 79 | 80 | /* 81 | * deallocates storage 82 | */ 83 | #if __STDC_VERSION__ >= 199901L /* C99 version */ 84 | void (mem_free)(void *p, const char *file, const char *func, int line) 85 | #else /* C90 version */ 86 | void (mem_free)(void *p, const char *file, int line) 87 | #endif /* __STDC_VERSION__ */ 88 | { 89 | UNUSED(file); 90 | #if __STDC_VERSION__ >= 199901L /* C99 version */ 91 | UNUSED(func); 92 | #endif /* __STDC_VERSION__ */ 93 | UNUSED(line); 94 | 95 | /* no need to test if p is null pointer */ 96 | free(p); 97 | } 98 | 99 | 100 | /* 101 | * adjust the size of storage 102 | */ 103 | #if __STDC_VERSION__ >= 199901L /* C99 version */ 104 | void *(mem_resize)(void *p, size_t n, const char *file, const char *func, int line) 105 | #else /* C90 version */ 106 | void *(mem_resize)(void *p, size_t n, const char *file, int line) 107 | #endif /* __STDC_VERSION__ */ 108 | { 109 | assert(p); 110 | assert(n > 0); /* precludes zero-sized allocation */ 111 | 112 | p = realloc(p, n); 113 | if (!p) 114 | { 115 | if (!file) 116 | EXCEPT_RAISE(mem_exceptfail); 117 | else 118 | #if __STDC_VERSION__ >= 199901L /* C99 version */ 119 | except_raise(&mem_exceptfail, file, func, line); 120 | #else /* C90 version */ 121 | except_raise(&mem_exceptfail, file, line); 122 | #endif /* __STDC_VERSION__ */ 123 | } 124 | 125 | return p; 126 | } 127 | 128 | 129 | /* 130 | * provides a dummy function for mem_log() that is activated in debugging version 131 | */ 132 | void (mem_log)(FILE *fp, void freefunc(FILE *, const mem_loginfo_t *), 133 | void resizefunc(FILE *, const mem_loginfo_t *)) 134 | { 135 | UNUSED(fp); 136 | UNUSED(freefunc); 137 | UNUSED(resizefunc); 138 | 139 | /* do nothing in production version */ 140 | } 141 | 142 | 143 | /* 144 | * provides a dummy function for mem_leak() that is activated in debugging version 145 | */ 146 | void (mem_leak)(void apply(const mem_loginfo_t *, void *), void *cl) 147 | { 148 | UNUSED(apply); 149 | UNUSED(cl); 150 | 151 | /* do nothing in production version */ 152 | } 153 | 154 | /* end of memory.c */ 155 | -------------------------------------------------------------------------------- /src/cbl/memory.h: -------------------------------------------------------------------------------- 1 | /* 2 | * memory (cbl) 3 | */ 4 | 5 | #ifndef MEMORY_H 6 | #define MEMORY_H 7 | 8 | #include /* size_t */ 9 | #include /* FILE */ 10 | 11 | #include "cbl/except.h" /* except_t */ 12 | 13 | 14 | /* info for invalid memory operation */ 15 | typedef struct mem_loginfo_t { 16 | const void *p; /* pointer value in invalid operation */ 17 | size_t size; /* requested size; meaningful with mem_resize() */ 18 | const char *ifile; /* file name for invalid operation */ 19 | const char *ifunc; /* function name for invalid operation */ 20 | int iline; /* line number for invalid operation */ 21 | const char *afile; /* file name for allocation */ 22 | const char *afunc; /* function name for allocation */ 23 | int aline; /* line number for allocation */ 24 | size_t asize; /* size of storage for allocation */ 25 | } mem_loginfo_t; 26 | 27 | 28 | /* exception for memory allocation failure */ 29 | extern const except_t mem_exceptfail; 30 | 31 | 32 | #if __STDC_VERSION__ >= 199901L /* C99 version */ 33 | void *mem_alloc(size_t, const char *, const char *, int); 34 | void *mem_calloc(size_t, size_t, const char *, const char *, int); 35 | void mem_free(void *, const char *, const char *, int); 36 | void *mem_resize(void *, size_t, const char *, const char *, int); 37 | #else /* C90 version */ 38 | void *mem_alloc(size_t, const char *, int); 39 | void *mem_calloc(size_t, size_t, const char *, int); 40 | void mem_free(void *, const char *, int); 41 | void *mem_resize(void *, size_t, const char *, int); 42 | #endif /* __STDC_VERSION__ */ 43 | void mem_log(FILE *, void (FILE *, const mem_loginfo_t *), void (FILE *, const mem_loginfo_t *)); 44 | void mem_leak(void (const mem_loginfo_t *, void *), void *); 45 | 46 | 47 | #if __STDC_VERSION__ >= 199901L /* C99 version */ 48 | #define MEM_ALLOC(n) (mem_alloc((n), __FILE__, __func__, __LINE__)) 49 | #define MEM_CALLOC(c, n) (mem_calloc((c), (n), __FILE__, __func__, __LINE__)) 50 | #else /* C90 version */ 51 | #define MEM_ALLOC(n) (mem_alloc((n), __FILE__, __LINE__)) 52 | #define MEM_CALLOC(c, n) (mem_calloc((c), (n), __FILE__, __LINE__)) 53 | #endif /* __STDC_VERSION__ */ 54 | 55 | /* allocates storage */ 56 | #define MEM_NEW(p) ((void)((p) = MEM_ALLOC(sizeof *(p)))) 57 | #define MEM_NEW0(p) ((void)((p) = MEM_CALLOC(1, sizeof *(p)))) 58 | 59 | /* deallocates or resize storage */ 60 | #if __STDC_VERSION__ >= 199901L /* C99 version */ 61 | #define MEM_FREE(p) ((void)(mem_free((p), __FILE__, __func__, __LINE__), (p)=0)) 62 | #define MEM_RESIZE(p, n) ((p) = mem_resize((p), (n), __FILE__, __func__, __LINE__)) 63 | #else /* C90 version */ 64 | #define MEM_FREE(p) ((void)(mem_free((p), __FILE__, __LINE__), (p)=0)) 65 | #define MEM_RESIZE(p, n) ((p) = mem_resize((p), (n), __FILE__, __LINE__)) 66 | #endif /* __STDC_VERSION__ */ 67 | 68 | 69 | #endif /* MEMORY_H */ 70 | 71 | /* end of memory.h */ 72 | -------------------------------------------------------------------------------- /src/cbl/text.h: -------------------------------------------------------------------------------- 1 | /* 2 | * text (cbl) 3 | */ 4 | 5 | #ifndef TEXT_H 6 | #define TEXT_H 7 | 8 | 9 | /* text */ 10 | typedef struct text_t { 11 | int len; /* length */ 12 | const char *str; /* string */ 13 | } text_t; 14 | 15 | /* top of stack-like text space */ 16 | typedef struct text_save_t text_save_t; 17 | 18 | 19 | /* predefined texts */ 20 | extern const text_t text_ucase; 21 | extern const text_t text_lcase; 22 | extern const text_t text_digits; 23 | extern const text_t text_null; 24 | 25 | 26 | text_t text_put(const char *); 27 | text_t text_gen(const char *, int); 28 | text_t text_box(const char *, int); 29 | char *text_get(char *, int, text_t); 30 | int text_pos(text_t, int); 31 | text_t text_sub(text_t, int, int); 32 | text_t text_cat(text_t, text_t); 33 | text_t text_dup(text_t, int); 34 | text_t text_reverse(text_t); 35 | text_t text_map(text_t, const text_t *, const text_t *); 36 | int text_cmp(text_t, text_t); 37 | int text_chr(text_t, int, int, int); 38 | int text_rchr(text_t, int, int, int); 39 | int text_upto(text_t, int, int, text_t); 40 | int text_rupto(text_t, int, int, text_t); 41 | int text_any(text_t, int, text_t); 42 | int text_many(text_t, int, int, text_t); 43 | int text_rmany(text_t, int, int, text_t); 44 | int text_find(text_t, int, int, text_t); 45 | int text_rfind(text_t, int, int, text_t); 46 | int text_match(text_t, int, int, text_t); 47 | int text_rmatch(text_t, int, int, text_t); 48 | text_save_t *text_save(void); 49 | void text_restore(text_save_t **); 50 | 51 | 52 | /* accesses character with position */ 53 | #define TEXT_ACCESS(t, i) ((t).str[((i) <= 0)? (i)+(t).len: (i)-1]) 54 | 55 | 56 | #endif /* TEXT_H */ 57 | 58 | /* end of text.h */ 59 | -------------------------------------------------------------------------------- /src/cdsl/bitv.c: -------------------------------------------------------------------------------- 1 | /* 2 | * bit-vector (cdsl) 3 | */ 4 | 5 | #include /* size_t, NULL */ 6 | #include /* memcpy */ 7 | 8 | #include "cbl/memory.h" /* MEM_ALLOC, MEM_NEW, MEM_FREE */ 9 | #include "cbl/assert.h" /* assert with exception support */ 10 | #include "bitv.h" 11 | 12 | 13 | #define BPW (8 * sizeof(unsigned long)) /* number of bits per word */ 14 | 15 | #define nword(len) (((len)+BPW-1) / BPW) /* number of words for bit-vector of length len */ 16 | #define nbyte(len) (((len)+8-1) / 8) /* number of bytes for bit-vector of length len */ 17 | 18 | #define BIT(set, n) (((set)->byte[(n)/8] >> ((n)%8)) & 1) /* extracts bit from bit-vector */ 19 | 20 | /* body for work on range */ 21 | #define range(op, fcmp, cmp) \ 22 | do { \ 23 | assert(set); \ 24 | assert(l <= h); \ 25 | assert(h < set->length); \ 26 | if (l/8 < h/8) { \ 27 | set->byte[l/8] op##= cmp msb[l%8]; \ 28 | { \ 29 | size_t i; \ 30 | for (i = l/8 + 1; i < h/8; i++) \ 31 | set->byte[i] = fcmp 0U; \ 32 | set->byte[h/8] op##= cmp lsb[l%8]; \ 33 | } \ 34 | } else \ 35 | set->byte[l/8] op##= cmp (msb[l%8] & lsb[h%8]); \ 36 | } while(0) 37 | 38 | /* body for set operations */ 39 | #define setop(eq, sn, tn, op) \ 40 | do { \ 41 | assert(s || t); \ 42 | if (s == t) return (eq); \ 43 | else if (!s) return (sn); \ 44 | else if (!t) return (tn); \ 45 | else { \ 46 | size_t i; \ 47 | bitv_t *set; \ 48 | assert(s->length == t->length); \ 49 | set = bitv_new(s->length); \ 50 | for (i = 0; i < nword(s->length); i++) \ 51 | set->word[i] = s->word[i] op t->word[i]; \ 52 | return set; \ 53 | } \ 54 | } while(0) 55 | 56 | 57 | /* bit masks for msbs */ 58 | static unsigned msb[] = { 59 | 0xFF, 0xFE, 0xFC, 0xF8, 0xF0, 0xE0, 0xC0, 0x80 60 | }; 61 | 62 | /* bit masks for lsbs */ 63 | static unsigned lsb[] = { 64 | 0x80, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC, 0xFE, 0xFF 65 | }; 66 | 67 | /* bit mask for paddings */ 68 | static unsigned pad[] = { 69 | 0xFF, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F 70 | }; 71 | 72 | 73 | /* 74 | * bit-vector 75 | * 76 | * struct bitv_t contains a sequence of words to implement a bit-vector whose length in bits is 77 | * held in length. For table-driven approaches, byte provides access to an individual byte in 78 | * words for a bit-vector. Unused padding bits or bytes are unavoidable without bit-wise storage 79 | * allocation, and they should have no effect on the result because always set to zeros. 80 | */ 81 | struct bitv_t { 82 | size_t length; /* length in bits for bit-vector */ 83 | unsigned char *byte; /* byte-wise access to bit-vector words */ 84 | unsigned long *word; /* words to contain bit-vector */ 85 | }; 86 | 87 | 88 | /* 89 | * creates a copy of a bit-vector 90 | */ 91 | static bitv_t *copy(const bitv_t *t) 92 | { 93 | bitv_t *set; 94 | 95 | assert(t); 96 | 97 | set = bitv_new(t->length); 98 | if (t->length != 0) 99 | memcpy(set->byte, t->byte, nbyte(t->length)); 100 | 101 | return set; 102 | } 103 | 104 | 105 | /* 106 | * creates a new bit-vector 107 | */ 108 | bitv_t *(bitv_new)(size_t len) { 109 | bitv_t *set; 110 | 111 | MEM_NEW(set); 112 | set->word = (len > 0)? MEM_CALLOC(nword(len), sizeof(unsigned long)): NULL; 113 | set->byte = (void *)set->word; 114 | set->length = len; 115 | 116 | return set; 117 | } 118 | 119 | 120 | /* 121 | * destroys a bit-vector 122 | */ 123 | void (bitv_free)(bitv_t **pset) { 124 | assert(pset); 125 | assert(*pset); 126 | 127 | MEM_FREE((*pset)->word); 128 | MEM_FREE(*pset); 129 | } 130 | 131 | 132 | /* 133 | * returns the length of a bit-vector 134 | */ 135 | size_t (bitv_length)(const bitv_t *set) { 136 | assert(set); 137 | 138 | return set->length; 139 | } 140 | 141 | 142 | /* 143 | * returns the number of bits set 144 | */ 145 | size_t (bitv_count)(const bitv_t *set) 146 | { 147 | static char count[] = { 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4 }; 148 | 149 | size_t i, c = 0; 150 | 151 | assert(set); 152 | 153 | for (i = 0; i < nbyte(set->length); i++) { 154 | unsigned char ch = set->byte[i]; 155 | c += count[ch & 0x0F] + count[ch >> 4]; 156 | } 157 | 158 | return c; 159 | } 160 | 161 | 162 | /* 163 | * gets a bit in a bit-vector 164 | */ 165 | int (bitv_get)(const bitv_t *set, size_t n) 166 | { 167 | assert(set); 168 | assert(n < set->length); 169 | 170 | return BIT(set, n); 171 | } 172 | 173 | 174 | /* 175 | * changes the value of a bit in a bit-vector 176 | */ 177 | int (bitv_put)(bitv_t *set, size_t n, int bit) 178 | { 179 | int prev; 180 | 181 | assert(set); 182 | assert((bit & 1) == bit); 183 | assert(n < set->length); 184 | 185 | prev = BIT(set, n); 186 | if (bit) 187 | set->byte[n/8] |= 1U << (n%8); 188 | else 189 | set->byte[n/8] &= ~(1U << (n%8)); 190 | 191 | return prev; 192 | } 193 | 194 | 195 | /* 196 | * sets bits to 1 in a bit-vector 197 | */ 198 | void (bitv_set)(bitv_t *set, size_t l, size_t h) 199 | { 200 | range(|, ~, +); 201 | } 202 | 203 | 204 | /* 205 | * clears bits in a bit-vector 206 | */ 207 | void (bitv_clear)(bitv_t *set, size_t l, size_t h) 208 | { 209 | range(&, +, ~); 210 | } 211 | 212 | 213 | /* 214 | * complements bits in a bit-vector 215 | */ 216 | void (bitv_not)(bitv_t *set, size_t l, size_t h) 217 | { 218 | range(^, ~, +); 219 | } 220 | 221 | 222 | /* 223 | * sets bits in a bit-vector with bit patterns 224 | */ 225 | void (bitv_setv)(bitv_t *set, unsigned char *v, size_t n) 226 | { 227 | size_t i; 228 | 229 | assert(set); 230 | assert(v); 231 | assert(n > 0); 232 | assert(n <= nbyte(set->length)); 233 | 234 | if (n == nbyte(set->length)) 235 | v[n-1] &= pad[set->length % 8]; 236 | for (i = 0; i < n; i++) 237 | set->byte[i] = v[i] & 0xFF; 238 | } 239 | 240 | 241 | /* 242 | * calls a user-provided function for each bit in a bit-vector 243 | */ 244 | void (bitv_map)(bitv_t *set, void apply(size_t, int, void *), void *cl) 245 | { 246 | size_t i; 247 | 248 | assert(set); 249 | 250 | for (i = 0; i < set->length; i++) 251 | apply(i, BIT(set, i), cl); 252 | } 253 | 254 | 255 | /* 256 | * compares two bit-vectors for equality 257 | */ 258 | int (bitv_eq)(const bitv_t *s, const bitv_t *t) 259 | { 260 | size_t i; 261 | 262 | assert(s); 263 | assert(t); 264 | assert(s->length == t->length); 265 | 266 | for (i = 0; i < nword(s->length); i++) 267 | if (s->word[i] != t->word[i]) 268 | return 0; 269 | return 1; 270 | } 271 | 272 | 273 | /* 274 | * compares two bit-vectors for subset 275 | */ 276 | int (bitv_leq)(const bitv_t *s, const bitv_t *t) 277 | { 278 | size_t i; 279 | 280 | assert(s); 281 | assert(t); 282 | assert(s->length == t->length); 283 | 284 | for (i = 0; i < nword(s->length); i++) 285 | if ((s->word[i] & ~t->word[i]) != 0) 286 | return 0; 287 | return 1; 288 | } 289 | 290 | 291 | /* 292 | * compares two bit-vectors for proper subset 293 | */ 294 | int (bitv_lt)(const bitv_t *s, const bitv_t *t) 295 | { 296 | size_t i; 297 | int lt = 0; 298 | 299 | assert(s); 300 | assert(t); 301 | assert(s->length == t->length); 302 | 303 | for (i = 0; i < nword(s->length); i++) 304 | if ((s->word[i] & ~t->word[i]) != 0) 305 | return 0; 306 | else if (s->word[i] != t->word[i]) 307 | lt = 1; 308 | return lt; 309 | } 310 | 311 | 312 | /* 313 | * returns a union of two bit-vectors 314 | */ 315 | bitv_t *(bitv_union)(const bitv_t *t, const bitv_t *s) 316 | { 317 | setop(copy(t), copy(t), copy(s), |); 318 | } 319 | 320 | 321 | /* 322 | * returns an intersection of two bit-vectors 323 | */ 324 | bitv_t *(bitv_inter)(const bitv_t *t, const bitv_t *s) 325 | { 326 | setop(copy(t), bitv_new(t->length), bitv_new(s->length), &); 327 | } 328 | 329 | 330 | /* 331 | * returns a difference of two bit-vectors 332 | */ 333 | bitv_t *(bitv_minus)(const bitv_t *t, const bitv_t *s) 334 | { 335 | setop(bitv_new(s->length), bitv_new(t->length), copy(s), & ~); 336 | } 337 | 338 | 339 | /* 340 | * returns a symmetric difference of two bit-vectors 341 | */ 342 | bitv_t *(bitv_diff)(const bitv_t *t, const bitv_t *s) 343 | { 344 | setop(bitv_new(s->length), copy(t), copy(s), ^); 345 | } 346 | 347 | /* end of bitv.c */ 348 | -------------------------------------------------------------------------------- /src/cdsl/bitv.h: -------------------------------------------------------------------------------- 1 | /* 2 | * bit-vector (cdsl) 3 | */ 4 | 5 | #ifndef BITV_H 6 | #define BITV_H 7 | 8 | #include /* size_t */ 9 | 10 | 11 | /* bit-vector */ 12 | typedef struct bitv_t bitv_t; 13 | 14 | 15 | bitv_t *bitv_new(size_t); 16 | void bitv_free(bitv_t **); 17 | size_t bitv_length(const bitv_t *); 18 | size_t bitv_count(const bitv_t *); 19 | int bitv_get(const bitv_t *, size_t); 20 | int bitv_put(bitv_t *, size_t, int); 21 | void bitv_set(bitv_t *, size_t, size_t); 22 | void bitv_clear(bitv_t *, size_t, size_t); 23 | void bitv_not(bitv_t *, size_t, size_t); 24 | void bitv_setv(bitv_t *, unsigned char *, size_t); 25 | void bitv_map(bitv_t *, void (size_t, int, void *), void *); 26 | int bitv_eq(const bitv_t *, const bitv_t *); 27 | int bitv_leq(const bitv_t *, const bitv_t *); 28 | int bitv_lt(const bitv_t *, const bitv_t *); 29 | bitv_t *bitv_union(const bitv_t *, const bitv_t *); 30 | bitv_t *bitv_inter(const bitv_t *, const bitv_t *); 31 | bitv_t *bitv_minus(const bitv_t *, const bitv_t *); 32 | bitv_t *bitv_diff(const bitv_t *, const bitv_t *); 33 | 34 | 35 | #endif /* BITV_H */ 36 | 37 | /* end of bitv.h */ 38 | -------------------------------------------------------------------------------- /src/cdsl/dlist.c: -------------------------------------------------------------------------------- 1 | /* 2 | * doubly-linked list (cdsl) 3 | */ 4 | 5 | #include /* LONG_MAX */ 6 | #include /* NULL */ 7 | #include /* va_start, va_arg, va_end, va_list */ 8 | 9 | #include "cbl/assert.h" /* assert with exception support */ 10 | #include "cbl/memory.h" /* MEM_NEW0, MEM_FREE, MEM_NEW */ 11 | #include "dlist.h" 12 | 13 | 14 | /* 15 | * doubly-linked list (a.k.a. ring) 16 | */ 17 | struct dlist_t { 18 | struct node { 19 | struct node *prev; /* previous node */ 20 | struct node *next; /* next node */ 21 | void *data; /* data */ 22 | } *head; /* start of list */ 23 | long length; /* length of list (number of nodes) */ 24 | long lastidx; /* index of last accessed node */ 25 | struct node *lastnode; /* last accessed node */ 26 | }; 27 | 28 | 29 | /* 30 | * creates an empty new list 31 | */ 32 | dlist_t *(dlist_new)(void) 33 | { 34 | dlist_t *dlist; 35 | 36 | MEM_NEW0(dlist); 37 | dlist->head = NULL; 38 | 39 | dlist->lastidx = 0; 40 | dlist->lastnode = NULL; 41 | 42 | return dlist; 43 | } 44 | 45 | 46 | /* 47 | * constructs a new list using a given sequence of data 48 | */ 49 | dlist_t *(dlist_list)(void *data, ...) 50 | { 51 | va_list ap; 52 | dlist_t *dlist = dlist_new(); 53 | 54 | va_start(ap, data); 55 | /* if data is null, following loop skipped and just dlist_new() returned */ 56 | for (; data; data = va_arg(ap, void *)) 57 | dlist_addtail(dlist, data); /* dlist_addtail() does dirty job */ 58 | va_end(ap); 59 | 60 | return dlist; 61 | } 62 | 63 | 64 | /* 65 | * destroys a list 66 | */ 67 | void (dlist_free)(dlist_t **pdlist) 68 | { 69 | struct node *p, /* node to be freed */ 70 | *q; /* next to node freed */ 71 | 72 | assert(pdlist); 73 | assert(*pdlist); 74 | 75 | if ((p = (*pdlist)->head) != NULL) { 76 | long n = (*pdlist)->length; 77 | for (; n-- > 0; p = q) { 78 | q = p->next; 79 | MEM_FREE(p); 80 | } 81 | } 82 | MEM_FREE(*pdlist); 83 | } 84 | 85 | 86 | /* 87 | * returns the length of a list 88 | */ 89 | long (dlist_length)(const dlist_t *dlist) 90 | { 91 | assert(dlist); 92 | return dlist->length; 93 | } 94 | 95 | 96 | /* 97 | * retrieves data stored in the i-th node in a list 98 | */ 99 | void *(dlist_get)(dlist_t *dlist, long i) 100 | { 101 | long n; 102 | struct node *q; 103 | 104 | assert(dlist); 105 | assert(i >= 0 && i < dlist->length); 106 | 107 | q = NULL; 108 | 109 | if (dlist->lastnode) { /* tries to use last access information */ 110 | if (dlist->lastidx == i) 111 | q = dlist->lastnode; 112 | else if (dlist->lastidx == i - 1 || (i == 0 && dlist->lastidx == dlist->length)) 113 | q = dlist->lastnode->next; 114 | else if (dlist->lastidx - 1 == i || (dlist->lastidx == 0 && i == dlist->length)) 115 | q = dlist->lastnode->prev; 116 | } 117 | 118 | if (!q) { 119 | q = dlist->head; 120 | if (i <= dlist->length / 2) 121 | for (n = i; n-- > 0; q = q->next) 122 | continue; 123 | else /* ex: length==5 && i==3 => n=2 */ 124 | for (n = dlist->length-i; n-- > 0; q = q->prev) 125 | continue; 126 | } 127 | 128 | dlist->lastidx = i; 129 | dlist->lastnode = q; 130 | 131 | return q->data; 132 | } 133 | 134 | 135 | /* 136 | * replaces data stored in a node with new given data 137 | */ 138 | void *(dlist_put)(dlist_t *dlist, long i, void *data) 139 | { 140 | long n; 141 | void *prev; 142 | struct node *q; 143 | 144 | assert(dlist); 145 | assert(i >= 0 && i < dlist->length); 146 | 147 | q = NULL; 148 | 149 | if (dlist->lastnode) { /* tries to use last access information */ 150 | if (dlist->lastidx == i) 151 | q = dlist->lastnode; 152 | else if (dlist->lastidx == i - 1 || (i == 0 && dlist->lastidx == dlist->length)) 153 | q = dlist->lastnode->next; 154 | else if (dlist->lastidx - 1 == i || (dlist->lastidx == 0 && i == dlist->length)) 155 | q = dlist->lastnode->prev; 156 | } 157 | 158 | if (!q) { 159 | q = dlist->head; 160 | if (i <= dlist->length / 2) 161 | for (n = i; n-- > 0; q = q->next) 162 | continue; 163 | else 164 | for (n = dlist->length-i; n-- > 0; q = q->prev) 165 | continue; 166 | } 167 | 168 | prev = q->data; 169 | q->data = data; 170 | 171 | return prev; 172 | } 173 | 174 | 175 | /* 176 | * adds a node after the last node 177 | */ 178 | void *(dlist_addtail)(dlist_t *dlist, void *data) 179 | { 180 | struct node *p, /* new node */ 181 | *head; 182 | 183 | assert(dlist); 184 | assert(dlist->length < LONG_MAX); 185 | 186 | MEM_NEW(p); 187 | if ((head = dlist->head) != NULL) { /* there is at least one node */ 188 | p->prev = head->prev; /* new->prev = head->prev == tail */ 189 | head->prev->next = p; /* head->prev->next == tail->next = new */ 190 | p->next = head; /* new->next = head */ 191 | head->prev = p; /* head->prev = new */ 192 | } else /* empty list */ 193 | dlist->head = p->prev = p->next = p; 194 | 195 | dlist->length++; 196 | p->data = data; 197 | 198 | return data; 199 | } 200 | 201 | 202 | /* 203 | * adds a new node before the head node 204 | */ 205 | void *(dlist_addhead)(dlist_t *dlist, void *data) 206 | { 207 | assert(dlist); 208 | 209 | dlist_addtail(dlist, data); 210 | dlist->head = dlist->head->prev; /* moves head node */ 211 | 212 | dlist->lastidx++; /* no need to check dlist->lastnode */ 213 | 214 | return data; 215 | } 216 | 217 | 218 | /* 219 | * adds a new node to a specified position in a list 220 | */ 221 | void *(dlist_add)(dlist_t *dlist, long pos, void *data) 222 | { 223 | assert(dlist); 224 | assert(pos >= -dlist->length); 225 | assert(pos <= dlist->length+1); 226 | assert(dlist->length < LONG_MAX); 227 | 228 | /* note that first branch below deals with empty list case; code revised to call dlist_addtail() 229 | rather than dlist_addhead() because former is simpler even if both do same */ 230 | if (pos == 0 || pos == dlist->length+1) 231 | return dlist_addtail(dlist, data); 232 | else if (pos == 1 || pos == -dlist->length) 233 | return dlist_addhead(dlist, data); 234 | else { /* inserting node to middle of list */ 235 | long i; 236 | struct node *p, /* new node */ 237 | *q; /* node to be next to new node */ 238 | 239 | /* find index of node that will become next of new node; 240 | if pos < 0, pos+(length+1) == positive value for same position */ 241 | pos = (pos < 0)? pos+(dlist->length+1)-1: pos-1; 242 | 243 | q = NULL; 244 | 245 | if (dlist->lastnode) { /* tries to use last access information */ 246 | if (dlist->lastidx == pos) 247 | q = dlist->lastnode; 248 | else if (dlist->lastidx == pos - 1 || (pos == 0 && dlist->lastidx == dlist->length)) 249 | q = dlist->lastnode->next; 250 | else if (dlist->lastidx - 1 == pos || (dlist->lastidx == 0 && pos == dlist->length)) 251 | q = dlist->lastnode->prev; 252 | 253 | /* adjusts last access information */ 254 | if (dlist->lastidx >= pos) 255 | dlist->lastidx++; 256 | } 257 | 258 | if (!q) { 259 | q = dlist->head; 260 | if (pos <= dlist->length / 2) 261 | for (i = pos; i-- > 0; q = q->next) 262 | continue; 263 | else 264 | for (i = dlist->length-pos; i-- > 0; q = q->prev) 265 | continue; 266 | } 267 | 268 | MEM_NEW(p); 269 | p->prev = q->prev; /* new->prev = to_be_next->prev */ 270 | q->prev->next = p; /* to_be_next->prev->next == to_be_prev->next = new */ 271 | p->next = q; /* new->next = to_be_next */ 272 | q->prev = p; /* to_be_next->prev = new */ 273 | dlist->length++; 274 | 275 | p->data = data; 276 | 277 | return data; 278 | } 279 | } 280 | 281 | 282 | /* 283 | * removes a node with a specific index from a list 284 | */ 285 | void *(dlist_remove)(dlist_t *dlist, long i) 286 | { 287 | long n; 288 | void *data; 289 | struct node *q; /* node to be removed */ 290 | 291 | assert(dlist); 292 | assert(dlist->length > 0); 293 | assert(i >= 0 && i < dlist->length); 294 | 295 | q = NULL; 296 | 297 | if (dlist->lastnode) { /* tries to use last access information */ 298 | if (dlist->lastidx == i) 299 | q = dlist->lastnode; 300 | else if (dlist->lastidx == i - 1 || (i == 0 && dlist->lastidx == dlist->length)) 301 | q = dlist->lastnode->next; 302 | else if (dlist->lastidx - 1 == i || (dlist->lastidx == 0 && i == dlist->length)) 303 | q = dlist->lastnode->prev; 304 | 305 | /* adjusts last access information */ 306 | if (dlist->lastidx > i) 307 | dlist->lastidx--; 308 | else if (dlist->lastidx == i) { 309 | dlist->lastnode = dlist->lastnode->next; 310 | if (dlist->lastidx == dlist->length - 1) 311 | dlist->lastidx = 0; 312 | } 313 | } 314 | 315 | if (!q) { 316 | q = dlist->head; 317 | if (i <= dlist->length/2) 318 | for (n = i; n-- > 0; q = q->next) 319 | continue; 320 | else 321 | for (n = dlist->length-i; n-- > 0; q = q->prev) 322 | continue; 323 | } 324 | 325 | if (i == 0) /* remove head */ 326 | dlist->head = dlist->head->next; 327 | data = q->data; 328 | q->prev->next = q->next; 329 | q->next->prev = q->prev; 330 | MEM_FREE(q); 331 | if (--dlist->length == 0) /* empty */ 332 | dlist->head = dlist->lastnode = NULL; /* also invalidates dlist->lastnode */ 333 | 334 | return data; 335 | } 336 | 337 | 338 | /* 339 | * removes the last node of a list 340 | * 341 | * Note that the last access information is invalidated only when it refers to the tail node which 342 | * will get removed. In such a case, moving lastnode to the tail's next or previous node is not 343 | * necessary because it will be the head or the tail after the removal and an access to the head 344 | * or the tail node does not entail performance degradation. 345 | */ 346 | void *(dlist_remtail)(dlist_t *dlist) 347 | { 348 | void *data; 349 | struct node *tail; 350 | 351 | assert(dlist); 352 | assert(dlist->length > 0); 353 | 354 | if (dlist->lastnode == dlist->head->prev) /* implies dlist->lastnode != NULL */ 355 | dlist->lastnode = NULL; /* invalidates dlist->lastnode */ 356 | 357 | tail = dlist->head->prev; 358 | data = tail->data; 359 | tail->prev->next = tail->next; /* tail->next == head */ 360 | tail->next->prev = tail->prev; /* tail->next->prev == head->prev */ 361 | MEM_FREE(tail); 362 | if (--dlist->length == 0) /* empty */ 363 | dlist->head = NULL; 364 | 365 | return data; 366 | } 367 | 368 | 369 | /* 370 | * removes the first node from a list 371 | */ 372 | void *(dlist_remhead)(dlist_t *dlist) 373 | { 374 | assert(dlist); 375 | assert(dlist->length > 0); 376 | 377 | /* dlist->lastnode adjusted in dlist_remtail() */ 378 | 379 | dlist->head = dlist->head->prev; /* turns head to tail */ 380 | 381 | return dlist_remtail(dlist); 382 | } 383 | 384 | 385 | /* 386 | * shifts a list to right or left 387 | */ 388 | void (dlist_shift)(dlist_t *dlist, long n) 389 | { 390 | long i; 391 | struct node *q; /* new head node after shift */ 392 | 393 | assert(dlist); 394 | assert(n >= -dlist->length); 395 | assert(n <= dlist->length); 396 | 397 | /* following adjustment to i differs from original code */ 398 | if (n >= 0) /* shift to right: head goes to left */ 399 | i = dlist->length - n; 400 | else /* shift to left; head goes to right */ 401 | i = -n; /* possibility of overflow in 2sC representation */ 402 | 403 | q = NULL; 404 | 405 | if (dlist->lastnode) { /* tries to use last access information */ 406 | if (dlist->lastidx == i) 407 | q = dlist->lastnode; 408 | else if (dlist->lastidx == i - 1 || (i == 0 && dlist->lastidx == dlist->length)) 409 | q = dlist->lastnode->next; 410 | else if (dlist->lastidx - 1 == i || (dlist->lastidx == 0 && i == dlist->length)) 411 | q = dlist->lastnode->prev; 412 | 413 | /* adjusts last access information */ 414 | if (dlist->lastidx < i) 415 | dlist->lastidx += (dlist->length - i); 416 | else 417 | dlist->lastidx -= i; 418 | } 419 | 420 | if (!q) { 421 | q = dlist->head; 422 | if (i <= dlist->length/2) 423 | for (n = i; n-- > 0; q = q->next) 424 | continue; 425 | else 426 | for (n = dlist->length-i; n-- > 0; q = q->prev) 427 | continue; 428 | } 429 | 430 | dlist->head = q; 431 | } 432 | 433 | /* end of dlist.c */ 434 | -------------------------------------------------------------------------------- /src/cdsl/dlist.h: -------------------------------------------------------------------------------- 1 | /* 2 | * doubly-linked list (cdsl) 3 | */ 4 | 5 | #ifndef DLIST_H 6 | #define DLIST_H 7 | 8 | 9 | /* doubly-linked list */ 10 | typedef struct dlist_t dlist_t; 11 | 12 | 13 | dlist_t *dlist_new(void); 14 | dlist_t *dlist_list(void *, ...); 15 | void dlist_free(dlist_t **); 16 | void *dlist_add(dlist_t *, long, void *); 17 | void *dlist_addhead(dlist_t *, void *); 18 | void *dlist_addtail(dlist_t *, void *); 19 | void *dlist_remove(dlist_t *, long); 20 | void *dlist_remhead(dlist_t *); 21 | void *dlist_remtail(dlist_t *); 22 | long dlist_length(const dlist_t *); 23 | void *dlist_get(dlist_t *, long); 24 | void *dlist_put(dlist_t *, long, void *); 25 | void dlist_shift(dlist_t *, long); 26 | 27 | 28 | #endif /* DLIST_H */ 29 | 30 | /* end of dlist.h */ 31 | -------------------------------------------------------------------------------- /src/cdsl/dwa.h: -------------------------------------------------------------------------------- 1 | /* 2 | * double-word arithmetic (cdsl) 3 | */ 4 | 5 | #ifndef DWA_H 6 | #define DWA_H 7 | 8 | /* operation type for dwa_bit() */ 9 | #define DWA_AND 0 10 | #define DWA_XOR 1 11 | #define DWA_OR 2 12 | 13 | #define DWA_WIDTH (sizeof(((dwa_t *)0)->u.v) * 8) /* # of bits in double-word */ 14 | #define DWA_BUFSIZE (1 + DWA_WIDTH + 1) /* buffer size for stringization */ 15 | 16 | #ifndef DWA_BASE_T 17 | #define DWA_BASE_T long 18 | #endif /* !DWA_BASE_T */ 19 | 20 | 21 | typedef unsigned DWA_BASE_T dwa_ubase_t; /* unsigned single-word base type */ 22 | typedef signed DWA_BASE_T dwa_base_t; /* signed single-word base type */ 23 | 24 | /* represents double-word integers */ 25 | typedef struct dwa_t { 26 | union { 27 | dwa_ubase_t w[2]; /* single-word alias */ 28 | unsigned char v[sizeof(dwa_ubase_t) * 2]; /* radix-256 representation; little endian */ 29 | } u; 30 | } dwa_t; 31 | 32 | 33 | /* min/max values for dwa_t */ 34 | extern dwa_t dwa_umax; 35 | extern dwa_t dwa_max; 36 | extern dwa_t dwa_min; 37 | 38 | /* useful constants */ 39 | extern dwa_t dwa_0; 40 | extern dwa_t dwa_1; 41 | extern dwa_t dwa_neg1; 42 | 43 | 44 | void dwa_prep(void); 45 | 46 | /* conversion from and to native integers */ 47 | dwa_t dwa_fromuint(dwa_ubase_t); 48 | dwa_t dwa_fromint(dwa_base_t); 49 | dwa_ubase_t dwa_touint(dwa_t); 50 | dwa_base_t dwa_toint(dwa_t); 51 | 52 | /* arithmetic */ 53 | dwa_t dwa_neg(dwa_t); 54 | dwa_t dwa_addu(dwa_t, dwa_t); 55 | dwa_t dwa_add(dwa_t, dwa_t); 56 | dwa_t dwa_subu(dwa_t, dwa_t); 57 | dwa_t dwa_sub(dwa_t, dwa_t); 58 | dwa_t dwa_mulu(dwa_t, dwa_t); 59 | dwa_t dwa_mul(dwa_t, dwa_t); 60 | dwa_t dwa_divu(dwa_t, dwa_t, int); 61 | dwa_t dwa_div(dwa_t, dwa_t, int); 62 | 63 | /* bit-wise */ 64 | dwa_t dwa_bcom(dwa_t); 65 | dwa_t dwa_lsh(dwa_t, int); 66 | dwa_t dwa_rshl(dwa_t, int); 67 | dwa_t dwa_rsha(dwa_t, int); 68 | dwa_t dwa_bit(dwa_t, dwa_t, int); 69 | 70 | /* comparison */ 71 | int dwa_cmpu(dwa_t, dwa_t); 72 | int dwa_cmp(dwa_t, dwa_t); 73 | 74 | /* conversion from and to string */ 75 | char *dwa_tostru(char *, dwa_t, int); 76 | char *dwa_tostr(char *, dwa_t, int); 77 | dwa_t dwa_fromstr(const char *, char **, int); 78 | 79 | /* conversion from and to floating-point */ 80 | dwa_t dwa_fromfp(long double); 81 | long double dwa_tofpu(dwa_t); 82 | long double dwa_tofp(dwa_t); 83 | 84 | 85 | #endif /* DWA_H */ 86 | 87 | /* end of dwa.h */ 88 | -------------------------------------------------------------------------------- /src/cdsl/hash.c: -------------------------------------------------------------------------------- 1 | /* 2 | * hash (cdsl) 3 | */ 4 | 5 | #include /* size_t, NULL */ 6 | #include /* sprintf */ 7 | #include /* memcmp, memcpy, strlen */ 8 | #include /* CHAR_BIT, UCHAR_MAX */ 9 | #include /* va_list, va_start, va_arg, va_end */ 10 | 11 | #include "cbl/assert.h" /* assert with exception support */ 12 | #include "cbl/memory.h" /* MEM_ALLOC, MEM_FREE */ 13 | #include "hash.h" 14 | 15 | #if UCHAR_MAX > 255 16 | #error "scatter[] assumes UCHAR_MAX < 256!" 17 | #endif 18 | 19 | 20 | /* 21 | * list for hash strings 22 | * 23 | * In this implementation, storage for str is allocated together when storage for struct hash_t 24 | * allocated. To be precise, the size of storage allocated for struct hash_t is not that of 25 | * struct hash_t, but includes len. Then the pointer str points to the extra space. This is 26 | * dipicted as follows: 27 | * 28 | * |--------- len ---------| 29 | * +------+------+------+-----------------------+ 30 | * | link | len | str | | 31 | * +------+------+------+-----------------------+ 32 | * | | str points to the beginning of the extra space 33 | * +----+ 34 | * 35 | * This structure simplifies (de)allocation of storages for hash nodes, and enables the memory 36 | * layout used by some pointer tricks; see hash_length(). 37 | * 38 | * In the original implementation, there was no facilities to deallocate storages for hash nodes 39 | * because in general the lifetime of a hash string persisted during the exceution of the program; 40 | * it is not an error according to the C standard. 41 | */ 42 | struct hash_t { 43 | struct hash_t *link; /* next hash string */ 44 | size_t len; /* length of hash string */ 45 | char *str; /* hash string */ 46 | }; 47 | 48 | 49 | /* buckets for hash string lists; 50 | size of bucket should be power of 2 (see hash_new()) */ 51 | static struct hash_t *bucket[2048]; 52 | 53 | 54 | /* 55 | * table to map characters to random numbers 56 | * 57 | * scatter is used to map to a random number a character of unsigned char type given as an array 58 | * index. The numbers are generated by the standard's rand(). This scatter table need to be 59 | * revised when the number of characters in an implementation is greater than 256. Because the 60 | * hash numbers are calculated using scatter, the size of bucket can have a less sophisticated 61 | * form than that in the table/set library. 62 | */ 63 | static unsigned long scatter[] = { 64 | 2078917053, 143302914, 1027100827, 1953210302, 755253631, 2002600785, 1405390230, 45248011, 65 | 1099951567, 433832350, 2018585307, 438263339, 813528929, 1703199216, 618906479, 573714703, 66 | 766270699, 275680090, 1510320440, 1583583926, 1723401032, 1965443329, 1098183682, 1636505764, 67 | 980071615, 1011597961, 643279273, 1315461275, 157584038, 1069844923, 471560540, 89017443, 68 | 1213147837, 1498661368, 2042227746, 1968401469, 1353778505, 1300134328, 2013649480, 306246424, 69 | 1733966678, 1884751139, 744509763, 400011959, 1440466707, 1363416242, 973726663, 59253759, 70 | 1639096332, 336563455, 1642837685, 1215013716, 154523136, 593537720, 704035832, 1134594751, 71 | 1605135681, 1347315106, 302572379, 1762719719, 269676381, 774132919, 1851737163, 1482824219, 72 | 125310639, 1746481261, 1303742040, 1479089144, 899131941, 1169907872, 1785335569, 485614972, 73 | 907175364, 382361684, 885626931, 200158423, 1745777927, 1859353594, 259412182, 1237390611, 74 | 48433401, 1902249868, 304920680, 202956538, 348303940, 1008956512, 1337551289, 1953439621, 75 | 208787970, 1640123668, 1568675693, 478464352, 266772940, 1272929208, 1961288571, 392083579, 76 | 871926821, 1117546963, 1871172724, 1771058762, 139971187, 1509024645, 109190086, 1047146551, 77 | 1891386329, 994817018, 1247304975, 1489680608, 706686964, 1506717157, 579587572, 755120366, 78 | 1261483377, 884508252, 958076904, 1609787317, 1893464764, 148144545, 1415743291, 2102252735, 79 | 1788268214, 836935336, 433233439, 2055041154, 2109864544, 247038362, 299641085, 834307717, 80 | 1364585325, 23330161, 457882831, 1504556512, 1532354806, 567072918, 404219416, 1276257488, 81 | 1561889936, 1651524391, 618454448, 121093252, 1010757900, 1198042020, 876213618, 124757630, 82 | 2082550272, 1834290522, 1734544947, 1828531389, 1982435068, 1002804590, 1783300476, 1623219634, 83 | 1839739926, 69050267, 1530777140, 1802120822, 316088629, 1830418225, 488944891, 1680673954, 84 | 1853748387, 946827723, 1037746818, 1238619545, 1513900641, 1441966234, 367393385, 928306929, 85 | 946006977, 985847834, 1049400181, 1956764878, 36406206, 1925613800, 2081522508, 2118956479, 86 | 1612420674, 1668583807, 1800004220, 1447372094, 523904750, 1435821048, 923108080, 216161028, 87 | 1504871315, 306401572, 2018281851, 1820959944, 2136819798, 359743094, 1354150250, 1843084537, 88 | 1306570817, 244413420, 934220434, 672987810, 1686379655, 1301613820, 1601294739, 484902984, 89 | 139978006, 503211273, 294184214, 176384212, 281341425, 228223074, 147857043, 1893762099, 90 | 1896806882, 1947861263, 1193650546, 273227984, 1236198663, 2116758626, 489389012, 593586330, 91 | 275676551, 360187215, 267062626, 265012701, 719930310, 1621212876, 2108097238, 2026501127, 92 | 1865626297, 894834024, 552005290, 1404522304, 48964196, 5816381, 1889425288, 188942202, 93 | 509027654, 36125855, 365326415, 790369079, 264348929, 513183458, 536647531, 13672163, 94 | 313561074, 1730298077, 286900147, 1549759737, 1699573055, 776289160, 2143346068, 1975249606, 95 | 1136476375, 262925046, 92778659, 1856406685, 1884137923, 53392249, 1735424165, 1602280572 96 | }; 97 | 98 | 99 | /* 100 | * returns a hash string for a string 101 | * 102 | * The masking macro for hash_string() is not provided because the macro would contain str twice. 103 | */ 104 | const char *(hash_string)(const char *str) 105 | { 106 | assert(str); 107 | return hash_new(str, strlen(str)); 108 | } 109 | 110 | 111 | /* 112 | * returns a hash string for a signed integer 113 | */ 114 | const char *(hash_int)(long n) 115 | { 116 | int len; 117 | char str[1 + (sizeof(long)*CHAR_BIT+2)/3 + 1]; 118 | /* sign + possible number of octal digits in long + null character */ 119 | 120 | len = sprintf(str, "%ld", n); /* len does not count null character */ 121 | 122 | return hash_new(str, len); 123 | } 124 | 125 | 126 | /* 127 | * returns a hash string for a byte sequence 128 | * 129 | * Adjusting h into the valid range of bucket originally should be performed by the remainder 130 | * operator (%). Because on most implementations it runs slow, the operation is replaced by the 131 | * bitwise AND operator (&) with an assumption that the size of bucket is a power of 2. In 132 | * general, it is said that choosing a power of 2 as the bucket size for hashing leads to 133 | * performance degration due to frequent conflicts. In this implementation, however, a power of 2 134 | * is chosen for speeding hash_new() up with scatter that helps to restrain conflicts; profiling 135 | * on various applications reported that hash_new() is heavily used so worth being optimized. 136 | */ 137 | const char *(hash_new)(const char *byte, size_t len) 138 | { 139 | size_t i; 140 | unsigned long h; 141 | struct hash_t *p; 142 | 143 | assert(byte); 144 | 145 | /* hashing */ 146 | for (h = 0, i = 0; i < len; i++) 147 | h = (h << 1) + scatter[(unsigned char)byte[i]]; 148 | h &= (sizeof(bucket)/sizeof(*bucket)-1); 149 | 150 | /* check if hash string already exists */ 151 | for (p = bucket[h]; p; p = p->link) 152 | if (len == p->len && memcmp(p->str, byte, len) == 0) 153 | return p->str; 154 | 155 | /* not exist, so creates new one */ 156 | p = MEM_ALLOC(sizeof(*p) + len + 1); /* +1 for null character */ 157 | p->len = len; 158 | p->str = (char *)(p + 1); /* note that p is of struct hash_t type here and no violation of 159 | alignment restriction is possible because of type of p->str */ 160 | memcpy(p->str, byte, len); /* zero len causes no problem */ 161 | p->str[len] = '\0'; 162 | p->link = bucket[h]; /* pushes new node to hash list */ 163 | bucket[h] = p; 164 | 165 | return p->str; 166 | } 167 | 168 | 169 | /* 170 | * returns the length of a hash string 171 | * 172 | * In the original implementation, hash_length() had to look through the entire hash table because 173 | * it was impossible to hash a byte sequence without knowing its length, while the revised 174 | * implementation (labelled as "fast version") takes advantage of a pointer trick which is more 175 | * dangerous. 176 | */ 177 | size_t (hash_length)(const char *byte) 178 | { 179 | #if 1 /* fast but dangerous version */ 180 | struct hash_t *p; 181 | 182 | assert(byte); 183 | 184 | p = (struct hash_t *)byte - 1; 185 | 186 | return p->len; 187 | #else /* original slow but safe version */ 188 | size_t i; 189 | struct hash_t *p; 190 | 191 | assert(byte); 192 | 193 | for (i = 0; i < sizeof(bucket)/sizeof(*bucket); i++) 194 | for (p = bucket[i]; p; p = p->link) 195 | if (p->str == byte) /* same address means same hash string */ 196 | return p->len; 197 | 198 | assert(!"invalid hash string -- should never reach here"); 199 | 200 | return 0; 201 | #endif /* two versions */ 202 | } 203 | 204 | 205 | /* 206 | * deallocates storage for a hash string 207 | * 208 | * It is impossible to provide a faster version of hash_free() using a pointer trick as done for 209 | * hash_length() because the information of the previous node is necessary to maintain the linkage 210 | * of a list. 211 | */ 212 | void (hash_free)(const char *byte) 213 | { 214 | size_t i; 215 | struct hash_t *p, 216 | **pp; /* double pointer to which next of deleted node to be stored */ 217 | 218 | assert(byte); 219 | 220 | for (i = 0; i < sizeof(bucket)/sizeof(*bucket); i++) 221 | for (p=bucket[i], pp=&bucket[i]; p; pp=&p->link, p=p->link) 222 | if (p->str == byte) { 223 | *pp = p->link; 224 | MEM_FREE(p); 225 | return; 226 | } 227 | 228 | assert(!"invalid hash string -- should never reach here"); 229 | } 230 | 231 | 232 | /* 233 | * resets the hash table by deallocating all hash strings in it 234 | */ 235 | void (hash_reset)(void) 236 | { 237 | size_t i; 238 | struct hash_t *p; 239 | 240 | for (i = 0; i < sizeof(bucket)/sizeof(*bucket); i++) { 241 | while ((p = bucket[i]) != NULL) { 242 | bucket[i] = p->link; 243 | MEM_FREE(p); 244 | } 245 | } 246 | } 247 | 248 | 249 | /* 250 | * puts a sequence of strings to the hash table 251 | */ 252 | void hash_vload(const char *str, ...) 253 | { 254 | va_list ap; 255 | 256 | va_start(ap, str); 257 | for (; str; str = va_arg(ap, const char *)) 258 | hash_string(str); 259 | va_end(ap); 260 | } 261 | 262 | 263 | /* 264 | * puts given strings to the hash table 265 | */ 266 | void hash_aload(const char *strs[]) 267 | { 268 | const char *str; 269 | 270 | for (str = strs[0]; str; strs++, str=*strs) 271 | hash_string(str); 272 | } 273 | 274 | /* end of hash.c */ 275 | -------------------------------------------------------------------------------- /src/cdsl/hash.h: -------------------------------------------------------------------------------- 1 | /* 2 | * hash (cdsl) 3 | */ 4 | 5 | #ifndef HASH_H 6 | #define HASH_H 7 | 8 | #include /* size_t */ 9 | 10 | 11 | const char *hash_string(const char *); 12 | const char *hash_int(long); 13 | const char *hash_new(const char *, size_t); 14 | void hash_vload(const char *, ...); 15 | void hash_aload(const char *[]); 16 | void hash_free(const char *); 17 | void hash_reset(void); 18 | size_t hash_length(const char *); 19 | 20 | 21 | #endif /* HASH_H */ 22 | 23 | /* end of hash.h */ 24 | -------------------------------------------------------------------------------- /src/cdsl/list.c: -------------------------------------------------------------------------------- 1 | /* 2 | * list (cdsl) 3 | */ 4 | 5 | #include /* va_list, va_start, va_arg, va_end */ 6 | #include /* NULL, size_t */ 7 | 8 | #include "cbl/assert.h" /* assert with exception support */ 9 | #include "cbl/memory.h" /* MEM_NEW, MEM_FREE, MEM_ALLOC */ 10 | #include "list.h" 11 | 12 | 13 | /* 14 | * pushes a new node to a list 15 | */ 16 | list_t *(list_push)(list_t *list, void *data) 17 | { 18 | list_t *p; 19 | 20 | MEM_NEW(p); 21 | p->data = data; 22 | p->next = list; 23 | 24 | return p; 25 | } 26 | 27 | 28 | /* 29 | * constructs a new list using a sequence of data 30 | */ 31 | list_t *(list_list)(void *data, ...) 32 | { 33 | va_list ap; 34 | list_t *list, 35 | **plist = &list; /* double pointer into which next data is to be stored */ 36 | 37 | va_start(ap, data); 38 | /* if data is null, following loop skipped and null returned */ 39 | for (; data; data = va_arg(ap, void *)) { 40 | MEM_NEW(*plist); 41 | (*plist)->data = data; 42 | plist = &(*plist)->next; 43 | } 44 | *plist = NULL; /* next of last node should be null */ 45 | va_end(ap); 46 | 47 | return list; 48 | } 49 | 50 | 51 | #if 0 /* non-pointer-to-pointer version */ 52 | list_t *(list_list)(void *data, ...) 53 | { 54 | va_list ap; 55 | list *list; 56 | 57 | /* consider three cases: 58 | 1) empty list 59 | 2) list has only one node 60 | 3) list has two or more nodes */ 61 | 62 | va_start(ap, data); 63 | list = NULL; 64 | for (; data; data = va_arg(ap, void *)) { 65 | if (!list) { /* empty list */ 66 | MEM_NEW(list); 67 | } else { /* one or more nodes exist */ 68 | MEM_NEW(list->next); 69 | list = list->next; 70 | } 71 | list->data = data; 72 | } 73 | if (list) /* at least one node */ 74 | list->next = NULL; 75 | va_end(ap); 76 | 77 | return list; 78 | } 79 | #endif /* disabled */ 80 | 81 | 82 | /* 83 | * appends a list to another 84 | * 85 | * TODO: 86 | * - the time complexity of the current implementation is O(N) where N indicates the number of 87 | * nodes in a list. With a circular list, where the next node of the last node set to the 88 | * head, it is possible for both pushing and appending to be done in a constant time. 89 | */ 90 | list_t *(list_append)(list_t *list, list_t *tail) 91 | { 92 | list_t **p = &list; /* double pointer whose next set to point to tail */ 93 | 94 | while (*p) /* find last node */ 95 | p = &(*p)->next; 96 | *p = tail; /* appending */ 97 | 98 | return list; 99 | } 100 | 101 | 102 | #if 0 /* non-pointer-to-pointer version */ 103 | list_t *(list_append)(list_t *list, list_t *tail) 104 | { 105 | /* consider four cases: 106 | 1) empty + empty 107 | 2) empty + one or more nodes 108 | 3) one or more nodes + empty 109 | 4) one or more nodes + one or more nodes */ 110 | 111 | if (!list) /* empty + tail */ 112 | list = tail; 113 | else { /* non-empty + tail */ 114 | while (list->next) 115 | list = list->next; 116 | list->next = tail; 117 | } 118 | 119 | return list; 120 | } 121 | #endif /* disabled */ 122 | 123 | 124 | /* 125 | * duplicates a list 126 | */ 127 | list_t *(list_copy)(const list_t *list) 128 | { 129 | list_t *head, 130 | **plist = &head; /* double pointer into which node copied */ 131 | 132 | for (; list; list = list->next) { 133 | MEM_NEW(*plist); 134 | (*plist)->data = list->data; 135 | plist = &(*plist)->next; 136 | } 137 | *plist = NULL; /* next of last node should be null */ 138 | 139 | return head; 140 | } 141 | 142 | 143 | #if 0 /* non-pointer-to-pointer version */ 144 | list_t *(list_copy)(const list_t *list) 145 | { 146 | list_t *head, *tlist; 147 | 148 | head = NULL; 149 | for (; list; list = list->next) { 150 | if (!head) { /* for first node */ 151 | MEM_NEW(head); 152 | tlist = head; 153 | } else { /* for rest */ 154 | MEM_NEW(tlist->next); 155 | tlist = tlist->next; 156 | } 157 | tlist->data = list->data; 158 | } 159 | if (head) /* at least one node */ 160 | tlist->next = NULL; 161 | 162 | return head; 163 | } 164 | #endif /* disabled */ 165 | 166 | 167 | /* 168 | * pops a node from a list and save its data into a pointer object 169 | */ 170 | list_t *(list_pop)(list_t *list, void **pdata) 171 | { 172 | if (list) { 173 | list_t *head = list->next; 174 | if (pdata) 175 | *pdata = list->data; 176 | MEM_FREE(list); 177 | return head; 178 | } else 179 | return list; 180 | } 181 | 182 | 183 | /* 184 | * reverses a list 185 | */ 186 | list_t *(list_reverse)(list_t *list) 187 | { 188 | list_t *head, *next; 189 | 190 | head = NULL; 191 | for (; list; list = next) { 192 | next = list->next; 193 | list->next = head; 194 | head = list; 195 | } 196 | 197 | return head; 198 | } 199 | 200 | 201 | #if 0 /* pointer-to-pointer version */ 202 | list_t *(list_reverse)(list_t *list) 203 | { 204 | list_t *head, *next, **plist; 205 | 206 | /* basic idea is to repeat: pick head and push to another list where 207 | list: picked head 208 | next: head after moving head 209 | head: head of another list */ 210 | 211 | head = NULL; 212 | while (list) { 213 | /* perpare to move head */ 214 | plist = &list->next; /* A */ 215 | next = list->next; 216 | /* make head another list's head */ 217 | *plist = head; /* B */ 218 | head = list; 219 | /* pick next node */ 220 | list = next; 221 | } 222 | 223 | /* no modification to list->next between A and B, so plist is unnecessary; 224 | removing it and using for statement result in original version above */ 225 | 226 | return head; 227 | } 228 | #endif /* disabled */ 229 | 230 | 231 | /* 232 | * counts the length of a list 233 | */ 234 | size_t (list_length)(const list_t *list) 235 | { 236 | size_t n; 237 | 238 | for (n = 0; list; list = list->next) 239 | n++; 240 | 241 | return n; 242 | } 243 | 244 | 245 | /* 246 | * destroys a list 247 | */ 248 | void (list_free)(list_t **plist) 249 | { 250 | list_t *next; 251 | 252 | assert(plist); 253 | for (; *plist; *plist = next) { 254 | next = (*plist)->next; 255 | MEM_FREE(*plist); 256 | } 257 | } 258 | 259 | 260 | /* 261 | * calls a user-provided function for each node in a list 262 | */ 263 | void (list_map)(list_t *list, void apply(void **, void *), void *cl) 264 | { 265 | assert(apply); 266 | 267 | for (; list; list = list->next) 268 | apply(&list->data, cl); 269 | } 270 | 271 | 272 | /* 273 | * converts a list to an array 274 | */ 275 | void **(list_toarray)(const list_t *list, void *end) 276 | { 277 | void **array; 278 | size_t i, n; 279 | 280 | n = list_length(list); 281 | array = MEM_ALLOC((n+1)*sizeof(*array)); 282 | for (i = 0; i < n; i++) { 283 | array[i] = list->data; 284 | list = list->next; 285 | } 286 | array[i] = end; 287 | 288 | return array; 289 | } 290 | 291 | /* end of list.c */ 292 | -------------------------------------------------------------------------------- /src/cdsl/list.h: -------------------------------------------------------------------------------- 1 | /* 2 | * list (cdsl) 3 | */ 4 | 5 | #ifndef LIST_H 6 | #define LIST_H 7 | 8 | #include /* size_t */ 9 | 10 | 11 | /* list node */ 12 | typedef struct list_t { 13 | void *data; /* data */ 14 | struct list_t *next; /* next node */ 15 | } list_t; 16 | 17 | 18 | list_t *list_list(void *, ...); 19 | list_t *list_append(list_t *, list_t *); 20 | list_t *list_push(list_t *, void *); 21 | list_t *list_copy(const list_t *); 22 | list_t *list_pop(list_t *, void **); 23 | void **list_toarray(const list_t *, void *); 24 | size_t list_length(const list_t *); 25 | void list_free(list_t **); 26 | void list_map(list_t *, void (void **, void *), void *); 27 | list_t *list_reverse(list_t *); 28 | 29 | 30 | /* iterates for each node in list */ 31 | #define LIST_FOREACH(pos, list) for ((pos) = (list); (pos); (pos)=(pos)->next) 32 | 33 | 34 | #endif /* LIST_H */ 35 | 36 | /* end of list.h */ 37 | -------------------------------------------------------------------------------- /src/cdsl/set.c: -------------------------------------------------------------------------------- 1 | /* 2 | * set (cdsl) 3 | */ 4 | 5 | #include /* INT_MAX */ 6 | #include /* size_t, NULL */ 7 | 8 | #include "cbl/memory.h" /* MEM_ALLOC, MEM_NEW, MEM_FREE */ 9 | #include "cbl/assert.h" /* assert with exception support */ 10 | #include "set.h" 11 | 12 | 13 | #define MAX(x, y) ((x) > (y)? (x): (y)) /* returns larger of two */ 14 | #define MIN(x, y) ((x) > (y)? (y): (x)) /* returns smaller of two */ 15 | 16 | 17 | /* 18 | * set implemented by hash table 19 | * 20 | * struct set_t contains some information on a set and a hash table to contain set members. A set 21 | * looks like the following: 22 | * 23 | * +-----+ -+- 24 | * | | | length, timestamp, cmp, hash, size, bucket 25 | * | | | 26 | * | | | ------+ 27 | * +-----+ -+- | bucket points to the start of hash table 28 | * | [ ] | <--------+ 29 | * | [ ] | 30 | * | [ ] | 31 | * | [ ] |-[ ]-[ ] linked list constructed by link member in struct member 32 | * | [ ] | 33 | * | [ ] |-[ ]-[ ]-[ ]-[ ] 34 | * +-----+ 35 | * 36 | * The number of buckets in a hash table is determined based on a hint given when set_new() is 37 | * invoked. When necessary to find a specific member in a set, cmp() and hash() are used to locate 38 | * it. The number of all buckets in a set is necessary when an array is generated to contain all 39 | * members in a set (see set_toarray()). timestamp is increased whenever a set is modified by 40 | * set_put() or set_remove(). It prevents a user-defined function invoked by set_map() from 41 | * modifying a set during traversing it. 42 | * 43 | * Note that a violation of the alignment restriction is possible because there is no guarantee 44 | * that the address to which bucket points is aligned properly for a pointer to struct member, 45 | * even if such a violation rarely happens in practice. Putting a dummy member of the struct 46 | * member pointer type at the beginning of struct set_t can solve the problem. 47 | * 48 | * TODO: 49 | * - some of the set operations can be improved if hash numbers are stored in struct member, 50 | * which enables a user-provided hashing function called only once for each member and a 51 | * user-provided comparison function called only when the hash numbers differ. 52 | */ 53 | struct set_t { 54 | /* struct binding *dummy; */ /* dummy member for alignment */ 55 | int size; /* number of buckets in hash table */ 56 | int (*cmp)(const void *x, const void *y); /* comparison function */ 57 | unsigned (*hash)(const void *x); /* hash generating function */ 58 | size_t length; /* number of members in set */ 59 | unsigned timestamp; /* number of modification to set */ 60 | struct member { 61 | struct member *link; /* next node in same bucket */ 62 | const void *member; /* member */ 63 | } **bucket; /* array of struct member */ 64 | }; 65 | 66 | 67 | /* 68 | * default function for comparing members 69 | * 70 | * defhashCmp() assumes two given members to be hash strings and compares them by comparing their 71 | * addresses for equality (see the hash library for details). 72 | * 73 | * Even if defhashCmp() returns either zero or non-zero for equality, a function provided to 74 | * set_new() by a user is supposed to retrun a value less than, equal to or greater than zero to 75 | * indicate that x is less than, equal to or greater than y, respectively. See set_new() for more 76 | * explanation. 77 | */ 78 | static int defhashCmp(const void *x, const void *y) 79 | { 80 | return (x != y); 81 | } 82 | 83 | 84 | /* 85 | * default function for generating a hash value 86 | * 87 | * defhashGen() generates a hash value for a hash string (see the hash library for details). It 88 | * converts a given pointer value to an unsigned integer and cuts off its two low-order bits 89 | * because they are verly likely to be zero due to the alignement restriction. It returns an 90 | * integer resulting from converting to a possibly narrow type. 91 | */ 92 | static unsigned defhashGen(const void *x) 93 | { 94 | return (unsigned long)x >> 2; 95 | } 96 | 97 | 98 | /* 99 | * creates a copy of a set 100 | * 101 | * copy() makes a copy of a set by allocating new storage for it. copy() is used for various set 102 | * operations and takes a new hint for the copied set because the size of sets resulting from set 103 | * operations may differ from those of the operand sets. See the implementation of set operations 104 | * to see how the hint for copy() is used. In order to avoid set_put()'s fruitless search it 105 | * directly puts members into a new set. 106 | */ 107 | static set_t *copy(set_t *t, int hint) 108 | { 109 | set_t *set; 110 | 111 | assert(t); 112 | 113 | set = set_new(hint, t->cmp, t->hash); 114 | { 115 | int i; 116 | struct member *q; 117 | 118 | for (i = 0; i < t->size; i++) 119 | for (q = t->bucket[i]; q; q = q->link) { 120 | struct member *p; 121 | const void *member = q->member; 122 | int i = set->hash(member) % set->size; 123 | 124 | MEM_NEW(p); 125 | p->member = member; 126 | p->link = set->bucket[i]; 127 | set->bucket[i] = p; 128 | set->length++; 129 | } 130 | } 131 | 132 | return set; 133 | } 134 | 135 | 136 | /* 137 | * creates a new set 138 | * 139 | * Even if a function given to cmp should return a value that can distinguish three different 140 | * cases for full generality, the current implementation needs only equal or non-equal conditions. 141 | * 142 | * A complicated way to determine the size of the hash table is hired here because the hash number 143 | * is computed by a user-provided callback. Compare this to the hash implementation where the size 144 | * has a less sophisticated form and the hash number is computed by the library. 145 | * 146 | * The interface for the set library demands more information than a particular implementation of 147 | * the interface might require. For example, an implementation using a hash table as given here 148 | * need not require the comparison function to distinguish three different cases, and an 149 | * implementation using a tree needs no hint and the hashing function passed through hash. They 150 | * are all required in the interface to allow for vairous implementation approaches. 151 | */ 152 | set_t *(set_new)(int hint, int cmp(const void *, const void *), unsigned hash(const void *)) 153 | { 154 | /* candidates for size of hash table; 155 | all are primes probably except INT_MAX */ 156 | static int primes[] = { 509, 509, 1021, 2053, 4093, 8191, 16381, 32771, 65521, INT_MAX }; 157 | 158 | int i; 159 | set_t *set; 160 | 161 | assert(hint >= 0); 162 | 163 | /* i starts with 1; size be largest smaller than hint */ 164 | for (i = 1; primes[i] < hint; i++) /* no valid range check; hint never larger than INT_MAX */ 165 | continue; 166 | 167 | set = MEM_ALLOC(sizeof(*set) + primes[i-1]*sizeof(set->bucket[0])); 168 | set->size = primes[i-1]; 169 | set->cmp = cmp? cmp: defhashCmp; 170 | set->hash = hash? hash: defhashGen; 171 | set->length = 0; 172 | set->timestamp = 0; 173 | 174 | set->bucket = (struct member **)(set + 1); 175 | for (i = 0; i < set->size; i++) 176 | set->bucket[i] = NULL; 177 | 178 | return set; 179 | } 180 | 181 | 182 | /* 183 | * inspects if a set contains a member 184 | */ 185 | int (set_member)(set_t *set, const void *member) 186 | { 187 | int i; 188 | struct member *p; 189 | 190 | assert(set); 191 | assert(member); 192 | 193 | i = set->hash(member) % set->size; 194 | for (p = set->bucket[i]; p; p = p->link) 195 | if (set->cmp(member, p->member) == 0) 196 | break; 197 | 198 | return (p != NULL); 199 | } 200 | 201 | 202 | /* 203 | * puts a member to a set 204 | */ 205 | void (set_put)(set_t *set, const void *member) 206 | { 207 | int i; 208 | struct member *p; 209 | 210 | assert(set); 211 | assert(member); 212 | 213 | i = set->hash(member) % set->size; 214 | 215 | for (p = set->bucket[i]; p; p = p->link) 216 | if (set->cmp(member, p->member) == 0) 217 | break; 218 | 219 | if (!p) { /* member not found, so allocate new one */ 220 | MEM_NEW(p); 221 | p->member = member; 222 | p->link = set->bucket[i]; /* push to hash table */ 223 | set->bucket[i] = p; 224 | set->length++; 225 | } else 226 | p->member = member; 227 | set->timestamp++; /* set modified */ 228 | } 229 | 230 | 231 | /* 232 | * removes a member from a set 233 | */ 234 | void *(set_remove)(set_t *set, const void *member) 235 | { 236 | int i; 237 | struct member **pp; /* pointer-to-pointer idiom used */ 238 | 239 | assert(set); 240 | assert(member); 241 | 242 | set->timestamp++; /* set modified */ 243 | 244 | i = set->hash(member) % set->size; 245 | 246 | for (pp = &set->bucket[i]; *pp; pp = &(*pp)->link) 247 | if (set->cmp(member, (*pp)->member) == 0) { /* member found */ 248 | struct member *p = *pp; 249 | *pp = p->link; 250 | member = p->member; 251 | MEM_FREE(p); 252 | set->length--; 253 | return (void *)member; /* remove constness */ 254 | } 255 | 256 | /* member not found */ 257 | return NULL; 258 | } 259 | 260 | 261 | /* 262 | * returns the length of a set 263 | */ 264 | size_t (set_length)(set_t *set) 265 | { 266 | assert(set); 267 | 268 | return set->length; 269 | } 270 | 271 | 272 | /* 273 | * destroys a set 274 | */ 275 | void (set_free)(set_t **pset) 276 | { 277 | assert(pset); 278 | assert(*pset); 279 | 280 | if ((*pset)->length > 0) { /* at least one member */ 281 | int i; 282 | struct member *p, *q; 283 | for (i = 0; i < (*pset)->size; i++) 284 | for (p = (*pset)->bucket[i]; p; p = q) { 285 | q = p->link; 286 | MEM_FREE(p); 287 | } 288 | } 289 | 290 | MEM_FREE(*pset); 291 | } 292 | 293 | 294 | /* 295 | * calls a user-provided function for each member in a set 296 | * 297 | * timestamp is checked in order to prevent a user-provided function from modifying a set by 298 | * calling set_put() or set_remove() that can touch the internal information of a set. If those 299 | * functions are called in a callback, it results in assertion failure (which may raise 300 | * assert_exceptfail). 301 | */ 302 | void (set_map)(set_t *set, void apply(const void *member, void *cl), void *cl) 303 | { 304 | int i; 305 | unsigned stamp; 306 | struct member *p; 307 | 308 | assert(set); 309 | assert(apply); 310 | 311 | stamp = set->timestamp; 312 | for (i = 0; i < set->size; i++) 313 | for (p = set->bucket[i]; p; p = p->link) { 314 | apply(p->member, cl); 315 | assert(set->timestamp == stamp); 316 | } 317 | } 318 | 319 | 320 | /* 321 | * converts a set to an array 322 | */ 323 | void **(set_toarray)(set_t *set, void *end) 324 | { 325 | int i, j; 326 | void **array; 327 | struct member *p; 328 | 329 | assert(set); 330 | 331 | array = MEM_ALLOC((set->length + 1) * sizeof(*array)); 332 | 333 | j = 0; 334 | for (i = 0; i < set->size; i++) 335 | for (p = set->bucket[i]; p; p = p->link) 336 | array[j++] = (void *)p->member; /* cast removes constness */ 337 | array[j] = end; 338 | 339 | return array; 340 | } 341 | 342 | 343 | /* 344 | * returns a union set of two sets 345 | * 346 | * TODO: 347 | * - the code can be modified so that the operation is performed on each pair of corresponding 348 | * buckets when two given sets have the same number of buckets. 349 | */ 350 | set_t *(set_union)(set_t *s, set_t *t) 351 | { 352 | if (!s) { /* s is empty, t not empty */ 353 | assert(t); 354 | return copy(t, t->size); 355 | } else if (!t) { /* t is empty, s not empty */ 356 | return copy(s, s->size); 357 | } else { /* both not empty */ 358 | /* makes copy of s; resulting set has at least as many members as bigger set does */ 359 | set_t *set = copy(s, MAX(s->size, t->size)); /* makes copy of s */ 360 | 361 | assert(s->cmp == t->cmp && s->hash == t->hash); /* same comparison/hashing method */ 362 | { /* inserts member of t to union set */ 363 | int i; 364 | struct member *q; 365 | for (i = 0; i < t->size; i++) 366 | for (q = t->bucket[i]; q; q = q->link) 367 | set_put(set, q->member); 368 | } 369 | 370 | return set; 371 | } 372 | } 373 | 374 | 375 | /* 376 | * returns an intersection of two sets 377 | * 378 | * TODO: 379 | * - the code can be modified so that the operation is performed on each pair of corresponding 380 | * buckets when two given sets have the same number of buckets. 381 | */ 382 | set_t *(set_inter)(set_t *s, set_t *t) 383 | { 384 | if (!s) { /* s is empty, t not empty */ 385 | assert(t); 386 | return set_new(t->size, t->cmp, t->hash); /* null set */ 387 | } else if (!t) { /* t is empty, s not empty */ 388 | return set_new(s->size, s->cmp, s->hash); /* null set */ 389 | } else if (s->length < t->length) { /* use of <= here results in infinite recursion */ 390 | /* smaller t->size means better performance below, makes t->size smaller */ 391 | return set_inter(t, s); 392 | } else { /* both not empty */ 393 | /* resulting set has at most as many members as smaller set does */ 394 | set_t *set = set_new(MIN(s->size, t->size), s->cmp, s->hash); 395 | 396 | assert(s->cmp == t->cmp && s->hash == t->hash); 397 | { /* pick member in t and put it to result set if s also has it */ 398 | int i; 399 | struct member *q; 400 | for (i = 0; i < t->size; i++) /* smaller t->size, better performance */ 401 | for (q = t->bucket[i]; q; q = q->link) 402 | if (set_member(s, q->member)) { 403 | struct member *p; 404 | const void *member = q->member; 405 | int i = set->hash(member) % set->size; 406 | 407 | MEM_NEW(p); 408 | p->member = member; 409 | p->link = set->bucket[i]; 410 | set->bucket[i] = p; 411 | set->length++; 412 | } 413 | } 414 | 415 | return set; 416 | } 417 | } 418 | 419 | 420 | /* 421 | * returns a difference set of two sets 422 | * 423 | * TODO: 424 | * - the code can be modified so that the operation is performed on each pair of corresponding 425 | * buckets when two given sets have the same number of buckets. 426 | */ 427 | set_t *(set_minus)(set_t *s, set_t *t) { 428 | if (!s) { /* s is empty, t not empty */ 429 | assert(t); 430 | return set_new(t->size, t->cmp, t->hash); /* (null set) - t = (null set) */ 431 | } else if (!t) { /* t is empty, s not empty */ 432 | return copy(s, s->size); /* s - (null set) = s */ 433 | } else { /* both not empty */ 434 | set_t *set = set_new(MIN(s->size, t->size), s->cmp, s->hash); 435 | 436 | assert(s->cmp == t->cmp && s->hash == t->hash); 437 | { /* pick member in s and put it to result set if t does not has it */ 438 | int i; 439 | struct member *q; 440 | for (i = 0; i < s->size; i++) 441 | for (q = s->bucket[i]; q; q = q->link) 442 | if (!set_member(t, q->member)) { 443 | struct member *p; 444 | const void *member = q->member; 445 | int i = set->hash(member) % set->size; 446 | 447 | MEM_NEW(p); 448 | p->member = member; 449 | p->link = set->bucket[i]; 450 | set->bucket[i] = p; 451 | set->length++; 452 | } 453 | } 454 | 455 | return set; 456 | } 457 | } 458 | 459 | 460 | /* 461 | * returns a symmetric difference of two sets 462 | * 463 | * TODO: 464 | * - the code can be modified so that the operation is performed on each pair of corresponding 465 | * buckets when two given sets have the same number of buckets. 466 | */ 467 | 468 | set_t *(set_diff)(set_t *s, set_t *t) 469 | { 470 | if (!s) { /* s is empty, t not empty */ 471 | assert(t); 472 | return copy(t, t->size); /* (null set)-t U t-(null set) = (null set) U t = t */ 473 | } else if (!t) { /* t is empty, s not empty */ 474 | return copy(s, s->size); /* s-(null set) U (null set)-s = s U (null set) = s */ 475 | } else { /* both not empty */ 476 | set_t *set = set_new(MIN(s->size, t->size), s->cmp, s->hash); 477 | 478 | assert(s->cmp == t->cmp && s->hash == t->hash); 479 | { /* set = t - s */ 480 | int i; 481 | struct member *q; 482 | 483 | for (i = 0; i < t->size; i++) 484 | for (q = t->bucket[i]; q; q = q->link) 485 | if (!set_member(s, q->member)) { 486 | struct member *p; 487 | const void *member = q->member; 488 | int i = set->hash(member) % set->size; 489 | 490 | MEM_NEW(p); 491 | p->member = member; 492 | p->link = set->bucket[i]; 493 | set->bucket[i] = p; 494 | set->length++; 495 | } 496 | } 497 | 498 | { /* set += (s - t) */ 499 | int i; 500 | struct member *q; 501 | 502 | for (i = 0; i < s->size; i++) 503 | for (q = s->bucket[i]; q; q = q->link) 504 | if (!set_member(t, q->member)) { 505 | struct member *p; 506 | const void *member = q->member; 507 | int i = set->hash(member) % set->size; 508 | 509 | MEM_NEW(p); 510 | p->member = member; 511 | p->link = set->bucket[i]; 512 | set->bucket[i] = p; 513 | set->length++; 514 | } 515 | } 516 | 517 | return set; 518 | } 519 | } 520 | 521 | /* end of set.c */ 522 | -------------------------------------------------------------------------------- /src/cdsl/set.h: -------------------------------------------------------------------------------- 1 | /* 2 | * set (cdsl) 3 | */ 4 | 5 | #ifndef SET_H 6 | #define SET_H 7 | 8 | #include /* size_t */ 9 | 10 | 11 | /* set */ 12 | typedef struct set_t set_t; 13 | 14 | 15 | set_t *set_new (int, int (const void *, const void *), unsigned (const void *)); 16 | void set_free(set_t **); 17 | size_t set_length(set_t *); 18 | int set_member(set_t *, const void *); 19 | void set_put(set_t *, const void *); 20 | void *set_remove(set_t *, const void *); 21 | void set_map(set_t *, void (const void *, void *), void *); 22 | void **set_toarray(set_t *, void *); 23 | set_t *set_union(set_t *, set_t *); 24 | set_t *set_inter(set_t *, set_t *); 25 | set_t *set_minus(set_t *, set_t *); 26 | set_t *set_diff(set_t *, set_t *); 27 | 28 | 29 | #endif /* SET_H */ 30 | 31 | /* end of set.h */ 32 | -------------------------------------------------------------------------------- /src/cdsl/stack.c: -------------------------------------------------------------------------------- 1 | /* 2 | * stack (cdsl) 3 | */ 4 | 5 | #include /* NULL */ 6 | 7 | #include "cbl/assert.h" /* assert with exception support */ 8 | #include "cbl/memory.h" /* MEM_NEW, MEM_FREE */ 9 | #include "stack.h" 10 | 11 | 12 | /* stack implemented by linked list */ 13 | struct stack_t { 14 | struct node { 15 | void *data; /* data contained in node */ 16 | struct node *next; /* next node */ 17 | } *head; /* head node of stack */ 18 | }; 19 | 20 | 21 | /* 22 | * creates a stack 23 | */ 24 | stack_t *(stack_new)(void) 25 | { 26 | stack_t *stk; 27 | 28 | MEM_NEW(stk); 29 | stk->head = NULL; 30 | 31 | return stk; 32 | } 33 | 34 | 35 | /* 36 | * inspects if a stack is empty 37 | */ 38 | int (stack_empty)(const stack_t *stk) 39 | { 40 | assert(stk); 41 | return (!stk->head); 42 | } 43 | 44 | 45 | /* 46 | * pushes data into a stack 47 | */ 48 | void (stack_push)(stack_t *stk, void *data) 49 | { 50 | struct node *t; 51 | 52 | assert(stk); 53 | MEM_NEW(t); 54 | t->data = data; 55 | t->next = stk->head; 56 | stk->head = t; 57 | } 58 | 59 | 60 | /* 61 | * pops data from a stack 62 | */ 63 | void *(stack_pop)(stack_t *stk) 64 | { 65 | void *data; 66 | struct node *t; 67 | 68 | assert(stk); 69 | assert(stk->head); 70 | 71 | t = stk->head; 72 | stk->head = t->next; 73 | data = t->data; 74 | MEM_FREE(t); 75 | 76 | return data; 77 | } 78 | 79 | 80 | /* 81 | * peeks the top-most data in a stack 82 | */ 83 | void *(stack_peek)(const stack_t *stk) 84 | { 85 | assert(stk); 86 | assert(stk->head); 87 | 88 | return (void *)((struct node *)stk->head)->data; 89 | } 90 | 91 | 92 | /* 93 | * destroys a stack 94 | */ 95 | void (stack_free)(stack_t **stk) 96 | { 97 | struct node *t, *u; 98 | 99 | assert(stk); 100 | assert(*stk); 101 | 102 | for (t = (*stk)->head; t; t = u) { 103 | u = t->next; 104 | MEM_FREE(t); 105 | } 106 | MEM_FREE(*stk); 107 | } 108 | 109 | /* end of stack.c */ 110 | -------------------------------------------------------------------------------- /src/cdsl/stack.h: -------------------------------------------------------------------------------- 1 | /* 2 | * stack (cdsl) 3 | */ 4 | 5 | #ifndef STACK_H 6 | #define STACK_H 7 | 8 | 9 | /* stack */ 10 | typedef struct stack_t stack_t; 11 | 12 | 13 | stack_t *stack_new(void); 14 | void stack_free(stack_t **); 15 | void stack_push(stack_t *, void *); 16 | void *stack_pop(stack_t *); 17 | void *stack_peek(const stack_t *); 18 | int stack_empty(const stack_t *); 19 | 20 | 21 | #endif /* STACK_H */ 22 | 23 | /* end of stack.h */ 24 | -------------------------------------------------------------------------------- /src/cdsl/table.c: -------------------------------------------------------------------------------- 1 | /* 2 | * table (cdsl) 3 | */ 4 | 5 | #include /* INT_MAX */ 6 | #include /* size_t, NULL */ 7 | 8 | #include "cbl/memory.h" /* MEM_ALLOC, MEM_FREE */ 9 | #include "cbl/assert.h" /* assert with exception support */ 10 | #include "table.h" 11 | 12 | 13 | /* 14 | * table implemented by hash table 15 | * 16 | * struct table_t contains information on a table and a hash table to contain key-value pairs. A 17 | * table looks like the following: 18 | * 19 | * +-----+ -+- 20 | * | | | size, cmp, hash, length, bucket 21 | * | | | 22 | * | | | ------+ 23 | * +-----+ -+- | bucket points to the start of hash table 24 | * | [ ] | <--------+ 25 | * | [ ] | 26 | * | [ ] | 27 | * | [ ] |-[ ]-[ ] linked list constructed by link member in struct binding 28 | * | [ ] | 29 | * | [ ] |-[ ]-[ ]-[ ]-[ ] 30 | * +-----+ 31 | * 32 | * The number of buckets in a hash table is determined based on a hint given when table_new() is 33 | * invoked. When necessary to find a specific key in a table, cmp() and hash() are used to locate 34 | * it. The number of all buckets in a table is necessary when an array is generated to contain all 35 | * key-value pairs in a table (see table_toarray()). timestamp is increased whenever a table is 36 | * modified by table_put() or table_remove(). It prevents a user-defined function invoked by 37 | * table_map() from modifying a table during traversing it. 38 | * 39 | * Note that a violation of the alignment restriction is possible because there is no guarantee 40 | * that the address to which bucket points is aligned properly for a pointer to struct binding, 41 | * even if such a violation rarely happens in practice. Putting a dummy member of the struct 42 | * binding pointer type at the beginning of struct table_t can solve the problem. 43 | */ 44 | struct table_t { 45 | /* struct binding *dummy; */ /* dummy member for alignment */ 46 | int size; /* number of buckets in hash table */ 47 | int (*cmp)(const void *, const void *); /* comparison function */ 48 | unsigned (*hash)(const void *); /* hash generation function */ 49 | size_t length; /* number of key-value pairs in table */ 50 | unsigned timestamp; /* number of modification to table */ 51 | struct binding { 52 | struct binding *link; /* next node in same bucket */ 53 | const void *key; /* key */ 54 | void *value; /* value */ 55 | } **bucket; /* array of struct binding */ 56 | }; 57 | 58 | 59 | /* 60 | * default function for comparing keys 61 | * 62 | * defhashCmp() assumes two given keys to be hash strings and compares them by comparing their 63 | * addresses for equality (see the hash library for details). 64 | * 65 | * Even if defhashCmp() returns either zero or non-zero for equality, a function provided to 66 | * table_new() by a user is supposed to retrun a value less than, equal to or greater than zero to 67 | * indicate that x is less than, equal to or greater than y, respectively. See table_new() for 68 | * more explanation. 69 | */ 70 | static int defhashCmp(const void *x, const void *y) 71 | { 72 | return (x != y); 73 | } 74 | 75 | 76 | /* 77 | * default function for generating a hash value 78 | * 79 | * defhashGen() generates a hash value for a hash string (see the hash library for details). It 80 | * converts a pointer value to an unsigned integer and cuts off its two low-order bits because 81 | * they are verly likely to be zero due to the alignement restriction. It returns an integer 82 | * resulting from converting to a possibly narrow type. 83 | */ 84 | static unsigned defhashGen(const void *key) 85 | { 86 | return (unsigned long)key >> 2; 87 | } 88 | 89 | 90 | /* 91 | * creates a new table 92 | * 93 | * Even if a function given to cmp should return a value that can distinguish three different 94 | * cases for full generality, the current implementation needs only equal or non-equal conditions. 95 | * 96 | * A complicated way to determine the size of the hash table is hired here because the hash number 97 | * is computed by a user-provided callback. Compare this to the hash library implementation where 98 | * the size has a less sophisticated form and the hash number is computed by the library. 99 | * 100 | * The interface for the table library demands more information than a particular implementation 101 | * of the interface might require. For example, an implementation using a hash table as given here 102 | * need not require the comparison function to distinguish three different cases, and an 103 | * implementation using a tree does not need hint and the hashing function passed through hash. 104 | * They are all required in the interface to allow for vairous implementation approaches. 105 | */ 106 | table_t *(table_new)(int hint, int cmp(const void *, const void *), unsigned hash(const void *)) 107 | { 108 | /* candidates for size of hash table; 109 | all are primes probably except INT_MAX */ 110 | static int prime[] = { 509, 509, 1021, 2053, 4093, 8191, 16381, 32771, 65521, INT_MAX }; 111 | 112 | int i; 113 | table_t *table; 114 | 115 | assert(hint >= 0); 116 | 117 | /* i starts with 1; size be largest smaller than hint */ 118 | for (i = 1; prime[i] < hint; i++) /* no valid range check; hint never larger than INT_MAX */ 119 | continue; 120 | 121 | table = MEM_ALLOC(sizeof(*table) + prime[i-1]*sizeof(table->bucket[0])); 122 | table->size = prime[i-1]; 123 | table->cmp = cmp? cmp: defhashCmp; 124 | table->hash = hash? hash: defhashGen; 125 | table->length = 0; 126 | table->timestamp = 0; 127 | 128 | table->bucket = (struct binding **)(table + 1); 129 | for (i = 0; i < table->size; i++) 130 | table->bucket[i] = NULL; 131 | 132 | return table; 133 | } 134 | 135 | 136 | /* 137 | * gets data for a key from a table 138 | */ 139 | void *(table_get)(const table_t *table, const void *key) 140 | { 141 | int i; 142 | struct binding *p; 143 | 144 | assert(table); 145 | assert(key); 146 | 147 | i = table->hash(key) % table->size; 148 | for (p = table->bucket[i]; p; p = p->link) 149 | if (table->cmp(key, p->key) == 0) /* same key found */ 150 | break; 151 | 152 | return p? p->value: NULL; 153 | } 154 | 155 | 156 | /* 157 | * puts a value for a key to a table 158 | */ 159 | void *(table_put)(table_t *table, const void *key, void *value) 160 | { 161 | int i; 162 | struct binding *p; 163 | void *prev; 164 | 165 | assert(table); 166 | assert(key); 167 | 168 | i = table->hash(key) % table->size; 169 | 170 | for (p = table->bucket[i]; p; p = p->link) 171 | if ((*table->cmp)(key, p->key) == 0) 172 | break; 173 | 174 | if (!p) { /* key not found, so allocate new one */ 175 | MEM_NEW(p); 176 | p->key = key; 177 | p->link = table->bucket[i]; /* push to hash table */ 178 | table->bucket[i] = p; 179 | table->length++; 180 | prev = NULL; /* no previous value */ 181 | } else 182 | prev = p->value; 183 | 184 | p->value = value; 185 | table->timestamp++; /* table modified */ 186 | 187 | return prev; 188 | } 189 | 190 | 191 | /* 192 | * returns the length of a table 193 | */ 194 | size_t (table_length)(const table_t *table) 195 | { 196 | assert(table); 197 | 198 | return table->length; 199 | } 200 | 201 | 202 | /* 203 | * calls a user-provided function for each key-value pair in a table 204 | * 205 | * timestamp is checked in order to prevent a user-provided function from modifying a table by 206 | * calling table_put() or table_remove() that can touch the internal information of a table. If 207 | * those functions are called in a callback, it results in assertion failure (which may raise 208 | * assert_exceptfail). 209 | * 210 | * TODO: 211 | * - it sometimes serves better to call a user callback for key-value pairs in order they are 212 | * stored. 213 | */ 214 | void (table_map)(table_t *table, void apply(const void *key, void **value, void *cl), void *cl) 215 | { 216 | int i; 217 | unsigned stamp; 218 | struct binding *p; 219 | 220 | assert(table); 221 | assert(apply); 222 | 223 | stamp = table->timestamp; 224 | for (i = 0; i < table->size; i++) 225 | for (p = table->bucket[i]; p; p = p->link) { 226 | apply(p->key, &p->value, cl); 227 | assert(table->timestamp == stamp); 228 | } 229 | } 230 | 231 | 232 | /* 233 | * removes a key-value pair from a table 234 | */ 235 | void *(table_remove)(table_t *table, const void *key) 236 | { 237 | int i; 238 | struct binding **pp; /* pointer-to-pointer idiom used */ 239 | 240 | assert(table); 241 | assert(key); 242 | 243 | table->timestamp++; /* table modified */ 244 | 245 | i = table->hash(key) % table->size; 246 | 247 | for (pp = &table->bucket[i]; *pp; pp = &(*pp)->link) 248 | if (table->cmp(key, (*pp)->key) == 0) { /* key found */ 249 | struct binding *p = *pp; 250 | void *value = p->value; 251 | *pp = p->link; 252 | MEM_FREE(p); 253 | table->length--; 254 | return value; 255 | } 256 | 257 | /* key not found */ 258 | return NULL; 259 | } 260 | 261 | 262 | /* 263 | * converts a table to an array 264 | * 265 | * TODO: 266 | * - it sometimes serves better to call a user callback for key-value pairs in order they are 267 | * stored. 268 | */ 269 | void **(table_toarray)(const table_t *table, void *end) 270 | { 271 | int i, j; 272 | void **array; 273 | struct binding *p; 274 | 275 | assert(table); 276 | 277 | array = MEM_ALLOC((2*table->length + 1) * sizeof(*array)); 278 | 279 | j = 0; 280 | for (i = 0; i < table->size; i++) 281 | for (p = table->bucket[i]; p; p = p->link) { 282 | array[j++] = (void *)p->key; /* cast removes constness */ 283 | array[j++] = p->value; 284 | } 285 | array[j] = end; 286 | 287 | return array; 288 | } 289 | 290 | 291 | /* 292 | * destroys a table 293 | */ 294 | void (table_free)(table_t **ptable) 295 | { 296 | assert(ptable); 297 | assert(*ptable); 298 | 299 | if ((*ptable)->length > 0) { /* at least one pair */ 300 | int i; 301 | struct binding *p, *q; 302 | for (i = 0; i < (*ptable)->size; i++) 303 | for (p = (*ptable)->bucket[i]; p; p = q) { 304 | q = p->link; 305 | MEM_FREE(p); 306 | } 307 | } 308 | 309 | MEM_FREE(*ptable); 310 | } 311 | 312 | /* end of table.c */ 313 | -------------------------------------------------------------------------------- /src/cdsl/table.h: -------------------------------------------------------------------------------- 1 | /* 2 | * table (cdsl) 3 | */ 4 | 5 | #ifndef TABLE_H 6 | #define TABLE_H 7 | 8 | #include /* size_t */ 9 | 10 | 11 | /* table */ 12 | typedef struct table_t table_t; 13 | 14 | 15 | table_t *table_new(int, int (const void *, const void *), unsigned (const void *)); 16 | void table_free(table_t **); 17 | size_t table_length(const table_t *); 18 | void *table_put(table_t *, const void *, void *); 19 | void *table_get(const table_t *, const void *); 20 | void *table_remove(table_t *, const void *); 21 | void table_map(table_t *, void (const void *, void **, void *), void *); 22 | void **table_toarray(const table_t *, void *); 23 | 24 | 25 | #endif /* TABLE_H */ 26 | 27 | /* end of table.h */ 28 | -------------------------------------------------------------------------------- /src/cel/conf.h: -------------------------------------------------------------------------------- 1 | /* 2 | * configuration (cel) 3 | */ 4 | 5 | #ifndef CONF_H 6 | #define CONF_H 7 | 8 | 9 | /* configuration description table element */ 10 | typedef struct conf_t { 11 | char *var; /* section name and variable name */ 12 | int type; /* type of variable */ 13 | char *defval; /* default value */ 14 | } conf_t; 15 | 16 | /* value types */ 17 | enum { 18 | CONF_TYPE_NO, /* cannot have type (not used in this library) */ 19 | CONF_TYPE_BOOL, /* has boolean (int) type */ 20 | CONF_TYPE_INT, /* has integer (long) type */ 21 | CONF_TYPE_UINT, /* has unsigned integer (unsigned long) type */ 22 | CONF_TYPE_REAL, /* has floating-point (double) type */ 23 | CONF_TYPE_STR /* has string (char *) type */ 24 | }; 25 | 26 | /* error codes */ 27 | enum { 28 | CONF_ERR_OK, /* everything is okay */ 29 | CONF_ERR_FILE, /* file not found */ 30 | CONF_ERR_IO, /* I/O error occurred */ 31 | CONF_ERR_SPACE, /* space in section/variable name */ 32 | CONF_ERR_CHAR, /* invalid character encountered */ 33 | CONF_ERR_LINE, /* invalid line encountered */ 34 | CONF_ERR_BSLASH, /* no following line for slicing */ 35 | CONF_ERR_SEC, /* section not found */ 36 | CONF_ERR_VAR, /* variable not found */ 37 | CONF_ERR_TYPE, /* data type mismatch */ 38 | CONF_ERR_MAX /* number of error codes */ 39 | }; 40 | 41 | /* control option masks */ 42 | enum { 43 | CONF_OPT_CASE = 0x01, /* case-sensitive variable/section names */ 44 | CONF_OPT_ESC = CONF_OPT_CASE << 1 /* supports escape sequence in quoted values */ 45 | }; 46 | 47 | 48 | int conf_preset(const conf_t *, int); 49 | size_t conf_init(FILE *, int); 50 | void conf_free(void); 51 | void conf_hashreset(void); 52 | const void *conf_conv(const char *, int); 53 | const void *conf_get(const char *); 54 | int conf_getbool(const char *, int); 55 | long conf_getint(const char *, long); 56 | unsigned long conf_getuint(const char *, unsigned long); 57 | double conf_getreal(const char *, double); 58 | const char *conf_getstr(const char *); 59 | int conf_set(const char *, const char *); 60 | int conf_section(const char *); 61 | int conf_errcode(void); 62 | const char *conf_errstr(int); 63 | 64 | 65 | #endif /* CONF_H */ 66 | 67 | /* end of conf.h */ 68 | -------------------------------------------------------------------------------- /src/cel/opt.h: -------------------------------------------------------------------------------- 1 | /* 2 | * option (cel) 3 | */ 4 | 5 | #ifndef OPT_H 6 | #define OPT_H 7 | 8 | 9 | /* 10 | * option description table element 11 | */ 12 | typedef struct opt_t { 13 | const char *lopt; /* long-named option */ 14 | int sopt; /* short-named option */ 15 | int *flag; /* flag varible or info about additional argument */ 16 | int arg; /* value for flag variable or type of additional argument */ 17 | } opt_t; 18 | 19 | /* 20 | * string-integer pairs for opt_val() 21 | */ 22 | typedef struct opt_val_t { 23 | const char *str; /* string to match */ 24 | int val; /* corresponding integral value */ 25 | } opt_val_t; 26 | 27 | /* argument conversion types */ 28 | enum { 29 | OPT_TYPE_NO, /* cannot have type */ 30 | OPT_TYPE_BOOL, /* has boolean (int) type */ 31 | OPT_TYPE_INT, /* has integer (long) type */ 32 | OPT_TYPE_UINT, /* has unsigned integer (unsigned long) type */ 33 | OPT_TYPE_REAL, /* has floating-point (double) type */ 34 | OPT_TYPE_STR /* has string (char *) type */ 35 | }; 36 | 37 | /* controls opt_val() */ 38 | enum { 39 | OPT_CMP_NORMSPC = 1, /* considers '_' and '-' equivalent to space */ 40 | OPT_CMP_CASEIN = OPT_CMP_NORMSPC << 1 /* performs case-insensitive comparison */ 41 | }; 42 | 43 | 44 | extern const char *opt_ambm[5]; /* ambiguous matches */ 45 | extern int opt_arg_req, opt_arg_no, opt_arg_opt; /* unique addresses for OPT_ARG_ macros */ 46 | 47 | 48 | const char *opt_init(const opt_t *, int *, char **[], const void **, const char *, int); 49 | int opt_parse(void); 50 | int opt_val(opt_val_t *, const char *, int); 51 | void opt_abort(void); 52 | const char *opt_ambmstr(void); 53 | const char *opt_errmsg(int); 54 | void opt_free(void); 55 | const char *opt_reinit(const opt_t *, int *, char **[], const void **); 56 | 57 | 58 | /* option-arguments */ 59 | #define OPT_ARG_REQ (&opt_arg_req) /* mandatory argument */ 60 | #define OPT_ARG_NO (&opt_arg_no) /* no argument taken */ 61 | #define OPT_ARG_OPT (&opt_arg_opt) /* optional argument */ 62 | 63 | 64 | #endif /* OPT_H */ 65 | 66 | /* end of opt.h */ 67 | --------------------------------------------------------------------------------