[med-svn] [kmer-tools] 02/07: Imported Upstream version 0~20150520+2004
Afif Elghraoui
afif-guest at moszumanska.debian.org
Fri May 22 09:26:54 UTC 2015
This is an automated email from the git hooks/post-receive script.
afif-guest pushed a commit to branch master
in repository kmer-tools.
commit af8304a09c24472c7c175965a74a6cb93596e104
Author: Afif Elghraoui <afif at ghraoui.name>
Date: Thu May 21 19:39:56 2015 -0700
Imported Upstream version 0~20150520+2004
---
README.atac | 100 +
README.compiling | 34 +-
README.meryl | 156 ++
atac-driver/atac.pl | 40 +-
configure.sh | 4 +-
libkmer/Make.include | 6 +-
libkmer/percentCovered.C | 66 -
libseq/fastaFile.C | 4 +-
libseq/fastqFile.C | 2 +
libutil/intervalList.H | 2 +-
libutil/kazlib/Make.include | 27 -
libutil/kazlib/blast.pl | 33 -
libutil/kazlib/dict.c | 1238 ----------
libutil/kazlib/dict.h | 142 --
libutil/kazlib/docs/CHANGES | 290 ---
libutil/kazlib/docs/MUST_READ | 25 -
libutil/kazlib/docs/README | 66 -
libutil/kazlib/docs/docs.ist | 4 -
libutil/kazlib/docs/docs.ltx | 4155 ----------------------------------
libutil/kazlib/drivers/dict-main.c | 300 ---
libutil/kazlib/drivers/except-main.c | 57 -
libutil/kazlib/drivers/hash-main.c | 187 --
libutil/kazlib/drivers/list-main.c | 152 --
libutil/kazlib/drivers/sfx-main.c | 41 -
libutil/kazlib/except.c | 347 ---
libutil/kazlib/except.h | 147 --
libutil/kazlib/hash.c | 837 -------
libutil/kazlib/hash.h | 238 --
libutil/kazlib/list.c | 766 -------
libutil/kazlib/list.h | 152 --
libutil/kazlib/sfx.c | 1138 ----------
libutil/kazlib/sfx.h | 46 -
meryl/Make.include | 19 +-
meryl/asmMerQC.C | 396 ----
{libkmer => meryl}/kmer-mask.C | 0
meryl/m-heap.H | 152 --
meryl/m.C | 118 -
meryl/mapMers-depth.C | 216 +-
meryl/maskMers.C | 7 +
meryl/testPositionBias.C | 117 -
40 files changed, 465 insertions(+), 11362 deletions(-)
diff --git a/README.atac b/README.atac
new file mode 100644
index 0000000..0156bfa
--- /dev/null
+++ b/README.atac
@@ -0,0 +1,100 @@
+atac - assembly-to-assembly comparasion, comparative mapping between
+ two genome assemblies (same species), or between two different
+ genomes (cross species).
+
+Described in the publication:
+
+S. Istrail, et al. "Whole-genome shotgun assembly and comparison of
+human genome assemblies" PNAS, Feb 2004; 101: 1916-1921
+
+Copyright (C) 2002, and GNU GPL, PE Corporation (NY) through the Celera Genomics Group
+Copyright (C) 2003-2004, and GNU GPL, Applied Biosystems
+Copyright (C) 2004-2015, and GNU GPL, Brian Walenz
+
+=======================================================================
+
+Content:
+
+I. What is atac?
+II. Command line usage
+III. Input/Output
+IV. Affiliated tools
+V. Terms of use
+VI. Support
+
+
+I. What is atac?
+
+atac computes a one-to-one pairwise alignment of large DNA sequences. It first
+finds the unique k-mers in each sequence, chains them to larger blocks, and
+fills in spaces between blocks. It was written primarily to transfer annotations
+between different assemblies of the human genome.
+
+The output is a set of ungapped 'matches', and a set of gapped 'runs' formed from
+the matches. Each match or run associates one sequence with the other sequence.
+The association is 'unique', in that there is no other (sizeable) associations
+for either sequence. Thus, large repeats and duplications are not present in
+the output - they appear as unmapped regions.
+
+Though the output is always pairwise, atac can cache intermediate results to speed
+a comparisons of multiple sequences.
+
+
+II. Command line usage
+
+A simple invocation:
+
+atac.pl \
+ -dir ecoli-k-vs-o \
+ -meryldir atac-sequences \
+ -id1 K -seq2 /data/references/ecolik12.fasta \
+ -id2 O -seq1 /data/references/ecolio157.fasta
+
+Run with no options for a list of parameters.
+
+See http://kmer.sourceforge.net/wiki/index.php/Getting_Started_with_ATAC for more.
+
+
+III. Input/Output
+
+Input is two multi-FASTA files. The files must be uncompressed.
+
+Output is in two self-documenting text files, reported at the end of the run:
+
+ Finished! Output is:
+ matches and runs -- /work/ecoli-k-vs-o/KvsO.atac
+ clumps -- /work/ecoli-k-vs-o/KvsO.*clump*.atac
+
+
+IV. Affiliated tools
+
+N/A
+
+
+V. Terms of use
+
+This program is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the
+Free Software Foundation; either version 2 of the License, or (at your
+option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received (LICENSE.txt) a copy of the GNU General
+Public License along with this program; if not, you can obtain one from
+http://www.gnu.org/licenses/gpl.txt or by writing to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+VI. Support
+
+Brian Walenz (brianwalenz at users.sourceforge.net)
+
+Please check the parent project's Sourceforge page at
+http://kmer.sourceforge.net for details and updates.
+
+
+Last updated: May 11, 2015
diff --git a/README.compiling b/README.compiling
index f8acebb..1112da8 100644
--- a/README.compiling
+++ b/README.compiling
@@ -10,9 +10,6 @@ There! That wasn't tough at all, was it!?
The software is compiled in place, and installed into a directory named
after the OS/architecture, for example, Linux-amd64.
-ESTmapper and A2Amapper are NOT installed by this process; see section 3
-below.
-
----------------------------------------
Detailed Instructions:
@@ -33,20 +30,21 @@ python and gmake.
Python (http://www.python.org/) is a freely available programming
language. It is frequently installed by many OS installations.
+It is only needed by ATAC/A2Amapper. If python is not installed,
+ATAC/A2Amapper will not be built.
+
Version 2.4+ is recommended.
Version 2.3 has seen limited testing and seems to work.
Version 2.2 might work, but is unsupported.
-Python is only needed by ATAC/A2Amapper. If python is not installed,
-ATAC/A2Amapper will not be built.
-
0.2) gmake.
The GNU make program (gmake) is used to build the software. The BSD
make will not work.
-Version 3.81 is strongly recommended.
-Version 3.80 works, but needs to be patched. See build/patches/README.
+gmake v 3.81 or higher is REQUIRED. This shouldn't be a problem, as that
+was released in April 2006. It _was_ a problem when most of this software
+was written.
----------
1) Configuration
@@ -74,26 +72,20 @@ in an unusual location, edit the script.
% gmake
-gmake v 3.81 or higher is REQUIRED.
+If gmake crashes or returns
-If this crashes or returns
gmake: *** No rule to make target `.all', needed by `all'. Stop.
-then you unfortunately need to update your gmake to version 3.81.
+
+then you need to update your gmake to at least version 3.81.
----------
3) Installation
-'gmake install' will copy all the executables into an OS/architecture
-specific 'bin' directory, for example, into FreeBSD-amd64/bin or
-Linux-i686/bin. ESTmapper and ATAC/A2Amapper are NOT installed by this
-process.
-
-To install ESTmapper, 'cd ESTmapper && sh install.sh location'. This
-will copy the ESTmapper binaries to 'location/bin' and 'location/lib'.
+% gmake install
-To install ATAC/A2Amapper, 'cd atac-driver && sh install.sh location'.
-This will copy the ATAC/A2Amapper binaries to 'location/bin' and
-'location/lib'.
+will copy all executables, libraries and header files into an
+OS/architecture specific directory. Binaries, for example, will be in
+FreeBSD-amd64/bin or Linux-i686/bin.
----------
4) Other build targets
diff --git a/README.meryl b/README.meryl
new file mode 100644
index 0000000..64d8e0d
--- /dev/null
+++ b/README.meryl
@@ -0,0 +1,156 @@
+meryl - in- and out-of-core kmer counting and utilities.
+
+Copyright (C) 2002, and GNU GPL, PE Corporation (NY) through the Celera Genomics Group
+Copyright (C) 2003-2004, and GNU GPL, Applied Biosystems
+Copyright (C) 2004-2015, and GNU GPL, Brian Walenz
+
+=======================================================================
+
+Content:
+
+I. What is meryl?
+II. Command line usage
+III. Input/Output
+IV. Affiliated tools
+V. Terms of use
+VI. Support
+
+
+I. What is meryl?
+
+meryl computes the kmer content of genomic sequences. Kmer content is
+represented as a list of kmers and the number of times each occurs in the
+input sequences. The kmer can be restricted to only the forward kmer, only
+the reverse kmer, or the canonical kmer (lexicographically smaller of the forward and reverse
+kmer at each location). Meryl can report the histogram of counts,
+the list of kmers and their counts, or can perform mathematical and set operations
+on the processed data files.
+
+The meryl process can run in one large memory batch, in many small memory batches, or under SGE
+control, all with or without using multiple CPU cores.
+
+The maximum kmer size is effectively unlimited, but set at compile time. Larger kmers use more
+memory, and are inefficient for counting smaller kmers, and since most applications have involved
+kmers less than 32 bases, the default compile time limit is 32 bases.
+
+The output of meryl is two binary files, called a meryl database, which can be
+quickly dumped to provide a histogram of counts, or the actual counts.
+A C++ library is supplied for direct access to the files.
+
+The meryl program can perform many mathematical and set operations on
+multiple database files: min, minexist, max, add, sub,abs, and, nand, or,
+xor, lessthan, lessthanorequal, greaterthan, greatherthanorequal, and equal.
+
+The ATAC pipeline uses meryl to find the unique kmers in two sequences ('lessthanorequal 1')
+then computes the 'and' of them to find the unique kmers that exist in
+both sequences.
+
+
+II. Command line usage
+
+A simple invocation:
+
+meryl -B -C -m 22 -s /data/references/ecolik12.fasta -o ecoli-22mers
+
+The above command will build (-B) a kmer database (-o ecoli-22mers)
+of the canonical (-C) 22-mers (-m 22) in the FASTA file ecolik12.fasta.
+The two output files are ecoli-22mers.mcidx and ecoli-22mers.mcdat.
+
+meryl -Dh -s ecoli-22mers > ecoli-22mers.fasta
+
+The above command will dump a histogram of the kmers in the 'ecoli-22mers'
+database. The histogram has four columns:
+
+ kmer-count number-of-kmers fraction-distinct fraction-total
+ [example]
+
+The first line tells us that there are X kmers that occur exactly once, that
+these sequences make up XX% of lthe kmer composition, and that these sequences
+account for YY% of all the kmers in the input.
+
+meryl -M and -s seq1 -s seq2 -o both
+
+The above command will report the kmers that are present in both meryl databases
+'seq1' and 'seq1', writing them to a new meryl database 'both'.
+
+Run with no options for a list of parameters.
+
+See http://kmer.sourceforge.net/wiki/index.php/Getting_Started_with_Meryl for more.
+
+
+III. Input/Output
+
+For counting kmers, input is exactly one multi-FASTA or FASTQ file. The
+file must be uncompressed.
+
+For processing databases, an input database is supplied by the prefix of the two
+files: the prefix of 'ecoli-ms22.mcidx' and 'ecoli-ms22.mcdat' is 'ecoli-ms22'.
+
+Output is a 'meryl database' consisting of two binary files, '*.mcidx' and '*.mcdat'.
+
+Output of the histogram command is a single text file to stdout.
+
+Output of the threshold dump is a multi-FASTA file, with the name of the sequence
+set to the count, and the sequence set to the kmer.
+
+
+IV. Affiliated tools
+
+Several additional kmer counting and analysis programs are included in the meryl package.
+
+simple - The obvious array based kmer counter. It will allocate 4 bytes per
+ kmer; k=16 will need 16 GB to run.
+
+ NEEDS UPDATE
+
+mapMers - Report stats of mapping kmers to sequences. Three modes of opeeration:
+ -stats repotrs mean, min and max for each sequence, along with a
+ log2 histogram of the counts
+ -regions reports regions with kmer coverage
+ -details reports for each kmer in the sequence, the forward and reverse
+ count in the database
+
+mapMers-depth - Reports, for each sequence ordinal 's' and position 'p':
+ -count the count (c) of the single kmer that starts at position (p).
+ Format: 's p c'
+ -depth the number (n) of kmers that span position (p). Format: 's p n'
+ -stats the min (m), max (M), ave (a) count of all mers that span
+ position (p). Format: 's p m M a t n'
+ (also reports total count (t) and number of kmers (n))
+
+kmer-mask - Mask and filter set of sequences (presumed to be reads) by kmer content.
+ Masking can be done to retain novel sequence not in the database, or to retain
+ confirmed sequence present in the database. Filtering will segregate sequences
+ fully, partially or not masked.
+
+existDB - (installed by libkmer) Management of existDB files.
+positionDB - (installed by libkmer) Management of posDB files.
+
+
+V. Terms of use
+
+This program is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the
+Free Software Foundation; either version 2 of the License, or (at your
+option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received (LICENSE.txt) a copy of the GNU General
+Public License along with this program; if not, you can obtain one from
+http://www.gnu.org/licenses/gpl.txt or by writing to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+VI. Support
+
+Brian Walenz (brianwalenz at users.sourceforge.net)
+
+Please check the parent project's Sourceforge page at
+http://kmer.sourceforge.net for details and updates.
+
+
+Last updated: May 13, 2015
diff --git a/atac-driver/atac.pl b/atac-driver/atac.pl
index 8368b01..7386fe3 100755
--- a/atac-driver/atac.pl
+++ b/atac-driver/atac.pl
@@ -318,14 +318,46 @@ sub findSources {
die "File '$seq1' doesn't exist for alias $id1.\n" if (! -e $seq1);
die "File '$seq2' doesn't exist for alias $id2.\n" if (! -e $seq2);
- system("ln -s $seq1 $MERYLdir/$id1.fasta") if (! -e "$MERYLdir/$id1.fasta");
- system("ln -s $seq2 $MERYLdir/$id2.fasta") if (! -e "$MERYLdir/$id2.fasta");
+ #system("ln -s $seq1 $MERYLdir/$id1.fasta") if (! -e "$MERYLdir/$id1.fasta");
+ #system("ln -s $seq2 $MERYLdir/$id2.fasta") if (! -e "$MERYLdir/$id2.fasta");
- system("ln -s ${seq1}idx $MERYLdir/$id1.fastaidx") if (! -e "$MERYLdir/$id1.fastaidx") && (-e "${seq1}idx");
- system("ln -s ${seq2}idx $MERYLdir/$id2.fastaidx") if (! -e "$MERYLdir/$id2.fastaidx") && (-e "${seq2}idx");
+ #system("ln -s ${seq1}idx $MERYLdir/$id1.fastaidx") if (! -e "$MERYLdir/$id1.fastaidx") && (-e "${seq1}idx");
+ #system("ln -s ${seq2}idx $MERYLdir/$id2.fastaidx") if (! -e "$MERYLdir/$id2.fastaidx") && (-e "${seq2}idx");
+
+ removeNewLines($seq1, "$MERYLdir/$id1.fasta");
+ removeNewLines($seq2, "$MERYLdir/$id2.fasta");
+}
+
+
+
+sub removeNewLines ($$) {
+ my $in = shift @_;
+ my $ot = shift @_;
+
+ return if (-e "$ot");
+
+ print STDERR "Rewriting '$in' to '$ot', removing newlines.\n";
+
+ open(F, "< $in") or die "Failed to open '$in' for reading: $!\n";
+ open(O, "> $ot") or die "Failed to open '$ot' for writing: $!\n";
+
+ $_ = <F>; print O $_;
+
+ while (<F>) {
+ if (m/^>/) {
+ print O "\n";
+ } else {
+ chomp;
+ }
+
+ print O $_;
+ }
+ close(O);
+ close(F);
}
+
# Check that meryl is finished for each of the inputs
#
sub countMers {
diff --git a/configure.sh b/configure.sh
index b27fd8a..95a0bf0 100755
--- a/configure.sh
+++ b/configure.sh
@@ -367,8 +367,8 @@ PERL := /usr/bin/perl
.O := .o
CLD := \${CC}
CXXLD := \${CXX}
-CCDEP := gcc -MM -MG
-CXXDEP := g++ -MM -MG
+CCDEP := \${CC} -MM -MG
+CXXDEP := \${CXX} -MM -MG
CLIBS += -lm -lbz2
CXXLIBS += -lm -lbz2
PYTHON := $PYTHON
diff --git a/libkmer/Make.include b/libkmer/Make.include
index 36ee1c5..7e678d9 100644
--- a/libkmer/Make.include
+++ b/libkmer/Make.include
@@ -20,16 +20,14 @@ src := $/existDB-create-from-fasta.C \
$/positionDB.C \
$/positionDB.H
-$/.CXX_SRCS := $(filter %.C,${src}) $/driver-existDB.C $/driver-posDB.C $/percentCovered.C $/kmer-mask.C
+$/.CXX_SRCS := $(filter %.C,${src}) $/driver-existDB.C $/driver-posDB.C
$/.CXX_INCS := $(filter %.H,${src})
-$/.CXX_EXES := $/existDB $/positionDB $/percentCovered $/kmer-mask
+$/.CXX_EXES := $/existDB $/positionDB
$/.CXX_LIBS := $/libkmer.a
$/.CLEAN := $/*.o
$/libkmer.a: $(filter %.o,${src:.C=.o})
$/existDB: $/driver-existDB.o $/libkmer.a ${LIBMERYL/}libmeryl.a ${LIBSEQ/}libseq.a ${LIBBIO/}libbio.a ${LIBUTL/}libutil.a
$/positionDB: $/driver-posDB.o $/libkmer.a ${LIBMERYL/}libmeryl.a ${LIBSEQ/}libseq.a ${LIBBIO/}libbio.a ${LIBUTL/}libutil.a
-$/percentCovered: $/percentCovered.o $/libkmer.a ${LIBMERYL/}libmeryl.a ${LIBSEQ/}libseq.a ${LIBBIO/}libbio.a ${LIBUTL/}libutil.a
-$/kmer-mask: $/kmer-mask.o $/libkmer.a ${LIBMERYL/}libmeryl.a ${LIBSEQ/}libseq.a ${LIBBIO/}libbio.a ${LIBUTL/}libutil.a
$(eval $/%.d $/%.o: CXXFLAGS+= -I${LIBMERYL/} -I${LIBBIO/} -I${LIBSEQ/} -I${LIBUTL/})
diff --git a/libkmer/percentCovered.C b/libkmer/percentCovered.C
deleted file mode 100644
index 947ff30..0000000
--- a/libkmer/percentCovered.C
+++ /dev/null
@@ -1,66 +0,0 @@
-#include "util++.H"
-#include "bio++.H"
-#include "existDB.H"
-
-#include "seqCache.H"
-#include "seqStream.H"
-#include "merStream.H"
-
-int
-main(int argc, char **argv) {
- char *merFile = 0L;
- char *queryFile = 0L;
-
- int arg=1;
- while (arg < argc) {
- if (strcmp(argv[arg], "-m") == 0) {
- merFile = argv[++arg];
- } else if (strcmp(argv[arg], "-q") == 0) {
- queryFile = argv[++arg];
- } else {
- fprintf(stderr, "Unknown arg '%s'\n", argv[arg]);
- }
- arg++;
- }
-
- existDB *E = new existDB(merFile, 22, existDBnoFlags, 0, ~uint32ZERO);
- seqCache *Q = new seqCache(queryFile);
- seqInCore *S = Q->getSequenceInCore();
-
- intervalList<uint64> IL;
- speedCounter SC(" %8f frags (%8.5f frags/sec)\r", 1, 1000, true);
-
- while (S) {
- merStream *MS = new merStream(new kMerBuilder(22),
- new seqStream(S->sequence(), S->sequenceLength()),
- true, true);
-
- IL.clear();
-
- while (MS->nextMer()) {
- if (E->exists(MS->theFMer())) {
- IL.add(MS->thePositionInSequence(), 22);
- }
- }
-
- IL.merge();
-
- if (IL.sumOfLengths() > 0) {
- fprintf(stdout, "%5.2f\n",
- 100.0 * IL.sumOfLengths() / (double)S->sequenceLength());
- }
-
- delete MS;
- delete S;
-
- SC.tick();
-
- S = Q->getSequenceInCore();
- }
-
- delete Q;
- delete E;
-
- return(0);
-}
-
diff --git a/libseq/fastaFile.C b/libseq/fastaFile.C
index a55bf38..b2cfaaa 100644
--- a/libseq/fastaFile.C
+++ b/libseq/fastaFile.C
@@ -320,9 +320,11 @@ fastaFile::clear(void) {
strcpy(_typename, "FastA");
+ _randomAccessSupported = true;
+
_numberOfSequences = 0;
- _rb = 0L;
+ _rb = 0L;
memset(&_header, 0, sizeof(fastaFileHeader));
_index = 0L;
_names = 0L;
diff --git a/libseq/fastqFile.C b/libseq/fastqFile.C
index 8042582..14dffb5 100644
--- a/libseq/fastqFile.C
+++ b/libseq/fastqFile.C
@@ -317,6 +317,8 @@ fastqFile::clear(void) {
strcpy(_typename, "Fastq");
+ _randomAccessSupported = true;
+
_numberOfSequences = 0;
_rb = 0L;
diff --git a/libutil/intervalList.H b/libutil/intervalList.H
index 315cace..79f8036 100644
--- a/libutil/intervalList.H
+++ b/libutil/intervalList.H
@@ -77,7 +77,7 @@ public:
_list = 0L;
#ifdef _GLIBCXX_PARALLEL
- // Don't use the parallel sort, not with the expense of starting threads.
+ // Don't use the parallel sort, not worth the expense of starting threads.
__gnu_sequential::sort(id, id + idlen);
#else
std::sort(id, id + idlen);
diff --git a/libutil/kazlib/Make.include b/libutil/kazlib/Make.include
deleted file mode 100644
index 7ecb369..0000000
--- a/libutil/kazlib/Make.include
+++ /dev/null
@@ -1,27 +0,0 @@
-# -*- makefile -*-
-
-src := $/dict.c \
- $/dict.h \
- $/except.c \
- $/except.h \
- $/hash.c \
- $/hash.h \
- $/list.c \
- $/list.h \
- $/sfx.c \
- $/sfx.h
-
-tst := $/dict-main.c \
- $/except-main.c \
- $/hash-main.c \
- $/list-main.c \
- $/sfx-main.c
-
-$/.C_SRCS :=$(filter %.c,${src})
-$/.CXX_SRCS :=$(filter %.C,${src})
-$/.CXX_LIBS :=$/libkaz.a
-
-$/.CLEAN := $/*.o
-
-$/libkaz.a: ${$/.C_SRCS:.c=.o} ${$/.CXX_SRCS:.C=.o}
-
diff --git a/libutil/kazlib/blast.pl b/libutil/kazlib/blast.pl
deleted file mode 100755
index 63351c9..0000000
--- a/libutil/kazlib/blast.pl
+++ /dev/null
@@ -1,33 +0,0 @@
-#!/usr/bin/perl
-
-#
-# This is a program whose output can be piped to the test drivers for
-# hash.c and dict.c. It inserts a bunch of data and then deletes it all.
-#
-# The $modulus should be a prime number. This ensures that the $modulus - 1
-# generated keys are all distinct. The $factor_i and $factor_d values need not
-# be prime, but it should not be a multiple of $modulus (including zero),
-# otherwise a sequence of duplicate keys will be generated: choose numbers
-# in the range [1, $modulus - 1]. Choosing 1 means that
-# insertions (or deletions) will take place in order.
-# The purpose of using the prime modulus number is to generate a repeatable
-# sequence of unique keys that is (possibly) not in sorted order.
-#
-
-# $modulus = 200003;
-# $factor_i = 100;
-# $factor_d = 301;
-
-$modulus = 6113;
-$factor_i = 1669;
-$factor_d = 2036;
-
-for ($i = 1; $i < $modulus; $i++) {
- printf("a %d %d\n", ($i * $factor_i) % $modulus, $i);
-}
-
-for ($i = 1; $i < $modulus; $i++) {
- printf("d %d\n", ($i * $factor_d) % $modulus);
-}
-
-print "t\nq\n"
diff --git a/libutil/kazlib/dict.c b/libutil/kazlib/dict.c
deleted file mode 100644
index cd98498..0000000
--- a/libutil/kazlib/dict.c
+++ /dev/null
@@ -1,1238 +0,0 @@
-/*
- * Dictionary Abstract Data Type
- * Copyright (C) 1997 Kaz Kylheku <kaz at ashi.footprints.net>
- *
- * Free Software License:
- *
- * All rights are reserved by the author, with the following exceptions:
- * Permission is granted to freely reproduce and distribute this software,
- * possibly in exchange for a fee, provided that this copyright notice appears
- * intact. Permission is also granted to adapt this software to produce
- * derivative works, as long as the modified versions carry this copyright
- * notice and additional notices stating that the work has been modified.
- * This source code may be translated into executable form and incorporated
- * into proprietary software; there is no requirement for such software to
- * contain a copyright notice related to this source.
- *
- */
-
-#define NDEBUG
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <stddef.h>
-#include <assert.h>
-#define DICT_IMPLEMENTATION
-#include "dict.h"
-
-// bpw 20050309 define this to use a qsort(3) compatible sort function,
-// requiring two dereferences to get the data instead of one.
-//
-#define BE_QSORT_COMPATIBLE
-
-/*
- * These macros provide short convenient names for structure members,
- * which are embellished with dict_ prefixes so that they are
- * properly confined to the documented namespace. It's legal for a
- * program which uses dict to define, for instance, a macro called ``parent''.
- * Such a macro would interfere with the dnode_t struct definition.
- * In general, highly portable and reusable C modules which expose their
- * structures need to confine structure member names to well-defined spaces.
- * The resulting identifiers aren't necessarily convenient to use, nor
- * readable, in the implementation, however!
- */
-
-#define left dict_left
-#define right dict_right
-#define parent dict_parent
-#define color dict_color
-#define key dict_key
-#define data dict_data
-
-#define nilnode dict_nilnode
-#define nodecount dict_nodecount
-#define maxcount dict_maxcount
-#define compare dict_compare
-#define allocnode dict_allocnode
-#define freenode dict_freenode
-#define context dict_context
-#define dupes dict_dupes
-
-#define dictptr dict_dictptr
-
-#define dict_root(D) ((D)->nilnode.left)
-#define dict_nil(D) (&(D)->nilnode)
-#define DICT_DEPTH_MAX 64
-
-static dnode_t *dnode_alloc(void *context);
-static void dnode_free(dnode_t *node, void *context);
-
-/*
- * Perform a ``left rotation'' adjustment on the tree. The given node P and
- * its right child C are rearranged so that the P instead becomes the left
- * child of C. The left subtree of C is inherited as the new right subtree
- * for P. The ordering of the keys within the tree is thus preserved.
- */
-
-static void rotate_left(dnode_t *upper)
-{
- dnode_t *lower, *lowleft, *upparent;
-
- lower = upper->right;
- upper->right = lowleft = lower->left;
- lowleft->parent = upper;
-
- lower->parent = upparent = upper->parent;
-
- /* don't need to check for root node here because root->parent is
- the sentinel nil node, and root->parent->left points back to root */
-
- if (upper == upparent->left) {
- upparent->left = lower;
- } else {
- assert (upper == upparent->right);
- upparent->right = lower;
- }
-
- lower->left = upper;
- upper->parent = lower;
-}
-
-/*
- * This operation is the ``mirror'' image of rotate_left. It is
- * the same procedure, but with left and right interchanged.
- */
-
-static void rotate_right(dnode_t *upper)
-{
- dnode_t *lower, *lowright, *upparent;
-
- lower = upper->left;
- upper->left = lowright = lower->right;
- lowright->parent = upper;
-
- lower->parent = upparent = upper->parent;
-
- if (upper == upparent->right) {
- upparent->right = lower;
- } else {
- assert (upper == upparent->left);
- upparent->left = lower;
- }
-
- lower->right = upper;
- upper->parent = lower;
-}
-
-/*
- * Do a postorder traversal of the tree rooted at the specified
- * node and free everything under it. Used by dict_free().
- */
-
-static void free_nodes(dict_t *dict, dnode_t *node, dnode_t *nil)
-{
- if (node == nil)
- return;
- free_nodes(dict, node->left, nil);
- free_nodes(dict, node->right, nil);
- dict->freenode(node, dict->context);
-}
-
-/*
- * This procedure performs a verification that the given subtree is a binary
- * search tree. It performs an inorder traversal of the tree using the
- * dict_next() successor function, verifying that the key of each node is
- * strictly lower than that of its successor, if duplicates are not allowed,
- * or lower or equal if duplicates are allowed. This function is used for
- * debugging purposes.
- */
-
-static int verify_bintree(dict_t *dict)
-{
- dnode_t *first, *next;
-
- first = dict_first(dict);
-
- if (dict->dupes) {
- while (first && (next = dict_next(dict, first))) {
-#ifdef BE_QSORT_COMPATIBLE
- if (dict->compare(&first->key, &next->key) > 0)
- return 0;
-#else
- if (dict->compare(first->key, next->key) > 0)
- return 0;
-#endif
- first = next;
- }
- } else {
- while (first && (next = dict_next(dict, first))) {
-#ifdef BE_QSORT_COMPATIBLE
- if (dict->compare(&first->key, &next->key) >= 0)
- return 0;
-#else
- if (dict->compare(first->key, next->key) >= 0)
- return 0;
-#endif
- first = next;
- }
- }
- return 1;
-}
-
-
-/*
- * This function recursively verifies that the given binary subtree satisfies
- * three of the red black properties. It checks that every red node has only
- * black children. It makes sure that each node is either red or black. And it
- * checks that every path has the same count of black nodes from root to leaf.
- * It returns the blackheight of the given subtree; this allows blackheights to
- * be computed recursively and compared for left and right siblings for
- * mismatches. It does not check for every nil node being black, because there
- * is only one sentinel nil node. The return value of this function is the
- * black height of the subtree rooted at the node ``root'', or zero if the
- * subtree is not red-black.
- */
-
-static unsigned int verify_redblack(dnode_t *nil, dnode_t *root)
-{
- unsigned height_left, height_right;
-
- if (root != nil) {
- height_left = verify_redblack(nil, root->left);
- height_right = verify_redblack(nil, root->right);
- if (height_left == 0 || height_right == 0)
- return 0;
- if (height_left != height_right)
- return 0;
- if (root->color == dnode_red) {
- if (root->left->color != dnode_black)
- return 0;
- if (root->right->color != dnode_black)
- return 0;
- return height_left;
- }
- if (root->color != dnode_black)
- return 0;
- return height_left + 1;
- }
- return 1;
-}
-
-/*
- * Compute the actual count of nodes by traversing the tree and
- * return it. This could be compared against the stored count to
- * detect a mismatch.
- */
-
-static dictcount_t verify_node_count(dnode_t *nil, dnode_t *root)
-{
- if (root == nil)
- return 0;
- else
- return 1 + verify_node_count(nil, root->left)
- + verify_node_count(nil, root->right);
-}
-
-/*
- * Verify that the tree contains the given node. This is done by
- * traversing all of the nodes and comparing their pointers to the
- * given pointer. Returns 1 if the node is found, otherwise
- * returns zero. It is intended for debugging purposes.
- */
-
-static int verify_dict_has_node(dnode_t *nil, dnode_t *root, dnode_t *node)
-{
- if (root != nil) {
- return root == node
- || verify_dict_has_node(nil, root->left, node)
- || verify_dict_has_node(nil, root->right, node);
- }
- return 0;
-}
-
-
-/*
- * Dynamically allocate and initialize a dictionary object.
- */
-
-dict_t *dict_create(dictcount_t maxcount, dict_comp_t comp)
-{
- dict_t *new = malloc(sizeof *new);
-
- if (new) {
- new->compare = comp;
- new->allocnode = dnode_alloc;
- new->freenode = dnode_free;
- new->context = NULL;
- new->nodecount = 0;
- new->maxcount = maxcount;
- new->nilnode.left = &new->nilnode;
- new->nilnode.right = &new->nilnode;
- new->nilnode.parent = &new->nilnode;
- new->nilnode.color = dnode_black;
- new->dupes = 0;
- }
- return new;
-}
-
-/*
- * Select a different set of node allocator routines.
- */
-
-void dict_set_allocator(dict_t *dict, dnode_alloc_t al,
- dnode_free_t fr, void *context)
-{
- assert (dict_count(dict) == 0);
- assert ((al == NULL && fr == NULL) || (al != NULL && fr != NULL));
-
- dict->allocnode = al ? al : dnode_alloc;
- dict->freenode = fr ? fr : dnode_free;
- dict->context = context;
-}
-
-/*
- * Free a dynamically allocated dictionary object. Removing the nodes
- * from the tree before deleting it is required.
- */
-
-void dict_destroy(dict_t *dict)
-{
- assert (dict_isempty(dict));
- free(dict);
-}
-
-/*
- * Free all the nodes in the dictionary by using the dictionary's
- * installed free routine. The dictionary is emptied.
- */
-
-void dict_free_nodes(dict_t *dict)
-{
- dnode_t *nil = dict_nil(dict), *root = dict_root(dict);
- free_nodes(dict, root, nil);
- dict->nodecount = 0;
- dict->nilnode.left = &dict->nilnode;
- dict->nilnode.right = &dict->nilnode;
-}
-
-/*
- * Obsolescent function, equivalent to dict_free_nodes
- */
-
-void dict_free(dict_t *dict)
-{
-#ifdef KAZLIB_OBSOLESCENT_DEBUG
- assert ("call to obsolescent function dict_free()" && 0);
-#endif
- dict_free_nodes(dict);
-}
-
-/*
- * Initialize a user-supplied dictionary object.
- */
-
-dict_t *dict_init(dict_t *dict, dictcount_t maxcount, dict_comp_t comp)
-{
- dict->compare = comp;
- dict->allocnode = dnode_alloc;
- dict->freenode = dnode_free;
- dict->context = NULL;
- dict->nodecount = 0;
- dict->maxcount = maxcount;
- dict->nilnode.left = &dict->nilnode;
- dict->nilnode.right = &dict->nilnode;
- dict->nilnode.parent = &dict->nilnode;
- dict->nilnode.color = dnode_black;
- dict->dupes = 0;
- return dict;
-}
-
-/*
- * Initialize a dictionary in the likeness of another dictionary
- */
-
-void dict_init_like(dict_t *dict, const dict_t *template)
-{
- dict->compare = template->compare;
- dict->allocnode = template->allocnode;
- dict->freenode = template->freenode;
- dict->context = template->context;
- dict->nodecount = 0;
- dict->maxcount = template->maxcount;
- dict->nilnode.left = &dict->nilnode;
- dict->nilnode.right = &dict->nilnode;
- dict->nilnode.parent = &dict->nilnode;
- dict->nilnode.color = dnode_black;
- dict->dupes = template->dupes;
-
- assert (dict_similar(dict, template));
-}
-
-/*
- * Remove all nodes from the dictionary (without freeing them in any way).
- */
-
-static void dict_clear(dict_t *dict)
-{
- dict->nodecount = 0;
- dict->nilnode.left = &dict->nilnode;
- dict->nilnode.right = &dict->nilnode;
- dict->nilnode.parent = &dict->nilnode;
- assert (dict->nilnode.color == dnode_black);
-}
-
-
-/*
- * Verify the integrity of the dictionary structure. This is provided for
- * debugging purposes, and should be placed in assert statements. Just because
- * this function succeeds doesn't mean that the tree is not corrupt. Certain
- * corruptions in the tree may simply cause undefined behavior.
- */
-
-int dict_verify(dict_t *dict)
-{
- dnode_t *nil = dict_nil(dict), *root = dict_root(dict);
-
- /* check that the sentinel node and root node are black */
- if (root->color != dnode_black)
- return(0 * fprintf(stderr, "dict_verify()-- Root node not black!\n"));
- if (nil->color != dnode_black)
- return(0 * fprintf(stderr, "dict_verify()-- Nil node not black!\n"));
- if (nil->right != nil)
- return(0 * fprintf(stderr, "dict_verify()-- Nul->right not Nil!\n"));
- /* nil->left is the root node; check that its parent pointer is nil */
- if (nil->left->parent != nil)
- return(0 * fprintf(stderr, "dict_verify()-- Nul->left->parent is not Nil!\n"));
- /* perform a weak test that the tree is a binary search tree */
- if (!verify_bintree(dict))
- return(0 * fprintf(stderr, "dict_verify()-- Not a binary search tree!\n"));
- /* verify that the tree is a red-black tree */
- if (!verify_redblack(nil, root))
- return(0 * fprintf(stderr, "dict_verify()-- Not a red-black tree!\n"));
- if (verify_node_count(nil, root) != dict_count(dict))
- return(0 * fprintf(stderr, "dict_verify()-- Node count is wrong!\n"));
- return 1;
-}
-
-/*
- * Determine whether two dictionaries are similar: have the same comparison and
- * allocator functions, and same status as to whether duplicates are allowed.
- */
-
-int dict_similar(const dict_t *left, const dict_t *right)
-{
- if (left->compare != right->compare)
- return 0;
-
- if (left->allocnode != right->allocnode)
- return 0;
-
- if (left->freenode != right->freenode)
- return 0;
-
- if (left->context != right->context)
- return 0;
-
- if (left->dupes != right->dupes)
- return 0;
-
- return 1;
-}
-
-/*
- * Locate a node in the dictionary having the given key.
- * If the node is not found, a null a pointer is returned (rather than
- * a pointer that dictionary's nil sentinel node), otherwise a pointer to the
- * located node is returned.
- */
-
-dnode_t *dict_lookup(dict_t *dict, const void *key)
-{
- dnode_t *root = dict_root(dict);
- dnode_t *nil = dict_nil(dict);
- dnode_t *saved;
- int result;
-
- /* simple binary search adapted for trees that contain duplicate keys */
-
- while (root != nil) {
-#ifdef BE_QSORT_COMPATIBLE
- result = dict->compare(&key, &root->key);
-#else
- result = dict->compare(key, root->key);
-#endif
- if (result < 0)
- root = root->left;
- else if (result > 0)
- root = root->right;
- else {
- if (!dict->dupes) { /* no duplicates, return match */
- return root;
- } else { /* could be dupes, find leftmost one */
- do {
- saved = root;
- root = root->left;
-#ifdef BE_QSORT_COMPATIBLE
- while (root != nil && dict->compare(&key, &root->key))
- root = root->right;
-#else
- while (root != nil && dict->compare(key, root->key))
- root = root->right;
-#endif
- } while (root != nil);
- return saved;
- }
- }
- }
-
- return NULL;
-}
-
-/*
- * Look for the node corresponding to the lowest key that is equal to or
- * greater than the given key. If there is no such node, return null.
- */
-
-dnode_t *dict_lower_bound(dict_t *dict, const void *key)
-{
- dnode_t *root = dict_root(dict);
- dnode_t *nil = dict_nil(dict);
- dnode_t *tentative = 0;
-
- while (root != nil) {
-#ifdef BE_QSORT_COMPATIBLE
- int result = dict->compare(&key, &root->key);
-#else
- int result = dict->compare(key, root->key);
-#endif
-
- if (result > 0) {
- root = root->right;
- } else if (result < 0) {
- tentative = root;
- root = root->left;
- } else {
- if (!dict->dupes) {
- return root;
- } else {
- tentative = root;
- root = root->left;
- }
- }
- }
-
- return tentative;
-}
-
-/*
- * Look for the node corresponding to the greatest key that is equal to or
- * lower than the given key. If there is no such node, return null.
- */
-
-dnode_t *dict_upper_bound(dict_t *dict, const void *key)
-{
- dnode_t *root = dict_root(dict);
- dnode_t *nil = dict_nil(dict);
- dnode_t *tentative = 0;
-
- while (root != nil) {
-#ifdef BE_QSORT_COMPATIBLE
- int result = dict->compare(&key, &root->key);
-#else
- int result = dict->compare(key, root->key);
-#endif
-
- if (result < 0) {
- root = root->left;
- } else if (result > 0) {
- tentative = root;
- root = root->right;
- } else {
- if (!dict->dupes) {
- return root;
- } else {
- tentative = root;
- root = root->right;
- }
- }
- }
-
- return tentative;
-}
-
-/*
- * Insert a node into the dictionary. The node should have been
- * initialized with a data field. All other fields are ignored.
- * The behavior is undefined if the user attempts to insert into
- * a dictionary that is already full (for which the dict_isfull()
- * function returns true).
- */
-
-void dict_insert(dict_t *dict, dnode_t *node, const void *key)
-{
- dnode_t *where = dict_root(dict), *nil = dict_nil(dict);
- dnode_t *parent = nil, *uncle, *grandpa;
- int result = -1;
-
- node->key = key;
-
- assert (!dict_isfull(dict));
- assert (!dict_contains(dict, node));
- assert (!dnode_is_in_a_dict(node));
-
- /* basic binary tree insert */
-
- while (where != nil) {
- parent = where;
-#ifdef BE_QSORT_COMPATIBLE
- result = dict->compare(&key, &where->key);
-#else
- result = dict->compare(key, where->key);
-#endif
- /* trap attempts at duplicate key insertion unless it's explicitly allowed */
- assert (dict->dupes || result != 0);
- if (result < 0)
- where = where->left;
- else
- where = where->right;
- }
-
- assert (where == nil);
-
- if (result < 0)
- parent->left = node;
- else
- parent->right = node;
-
- node->parent = parent;
- node->left = nil;
- node->right = nil;
-
- dict->nodecount++;
-
- /* red black adjustments */
-
- node->color = dnode_red;
-
- while (parent->color == dnode_red) {
- grandpa = parent->parent;
- if (parent == grandpa->left) {
- uncle = grandpa->right;
- if (uncle->color == dnode_red) { /* red parent, red uncle */
- parent->color = dnode_black;
- uncle->color = dnode_black;
- grandpa->color = dnode_red;
- node = grandpa;
- parent = grandpa->parent;
- } else { /* red parent, black uncle */
- if (node == parent->right) {
- rotate_left(parent);
- parent = node;
- assert (grandpa == parent->parent);
- /* rotation between parent and child preserves grandpa */
- }
- parent->color = dnode_black;
- grandpa->color = dnode_red;
- rotate_right(grandpa);
- break;
- }
- } else { /* symmetric cases: parent == parent->parent->right */
- uncle = grandpa->left;
- if (uncle->color == dnode_red) {
- parent->color = dnode_black;
- uncle->color = dnode_black;
- grandpa->color = dnode_red;
- node = grandpa;
- parent = grandpa->parent;
- } else {
- if (node == parent->left) {
- rotate_right(parent);
- parent = node;
- assert (grandpa == parent->parent);
- }
- parent->color = dnode_black;
- grandpa->color = dnode_red;
- rotate_left(grandpa);
- break;
- }
- }
- }
-
- dict_root(dict)->color = dnode_black;
-
- assert (dict_verify(dict));
-}
-
-/*
- * Delete the given node from the dictionary. If the given node does not belong
- * to the given dictionary, undefined behavior results. A pointer to the
- * deleted node is returned.
- */
-
-dnode_t *dict_delete(dict_t *dict, dnode_t *delete)
-{
- dnode_t *nil = dict_nil(dict), *child, *delparent = delete->parent;
-
- /* basic deletion */
-
- assert (!dict_isempty(dict));
- assert (dict_contains(dict, delete));
-
- /*
- * If the node being deleted has two children, then we replace it with its
- * successor (i.e. the leftmost node in the right subtree.) By doing this,
- * we avoid the traditional algorithm under which the successor's key and
- * value *only* move to the deleted node and the successor is spliced out
- * from the tree. We cannot use this approach because the user may hold
- * pointers to the successor, or nodes may be inextricably tied to some
- * other structures by way of embedding, etc. So we must splice out the
- * node we are given, not some other node, and must not move contents from
- * one node to another behind the user's back.
- */
-
- if (delete->left != nil && delete->right != nil) {
- dnode_t *next = dict_next(dict, delete);
- dnode_t *nextparent = next->parent;
- dnode_color_t nextcolor = next->color;
-
- assert (next != nil);
- assert (next->parent != nil);
- assert (next->left == nil);
-
- /*
- * First, splice out the successor from the tree completely, by
- * moving up its right child into its place.
- */
-
- child = next->right;
- child->parent = nextparent;
-
- if (nextparent->left == next) {
- nextparent->left = child;
- } else {
- assert (nextparent->right == next);
- nextparent->right = child;
- }
-
- /*
- * Now that the successor has been extricated from the tree, install it
- * in place of the node that we want deleted.
- */
-
- next->parent = delparent;
- next->left = delete->left;
- next->right = delete->right;
- next->left->parent = next;
- next->right->parent = next;
- next->color = delete->color;
- delete->color = nextcolor;
-
- if (delparent->left == delete) {
- delparent->left = next;
- } else {
- assert (delparent->right == delete);
- delparent->right = next;
- }
-
- } else {
- assert (delete != nil);
- assert (delete->left == nil || delete->right == nil);
-
- child = (delete->left != nil) ? delete->left : delete->right;
-
- child->parent = delparent = delete->parent;
-
- if (delete == delparent->left) {
- delparent->left = child;
- } else {
- assert (delete == delparent->right);
- delparent->right = child;
- }
- }
-
- delete->parent = NULL;
- delete->right = NULL;
- delete->left = NULL;
-
- dict->nodecount--;
-
- assert (verify_bintree(dict));
-
- /* red-black adjustments */
-
- if (delete->color == dnode_black) {
- dnode_t *parent, *sister;
-
- dict_root(dict)->color = dnode_red;
-
- while (child->color == dnode_black) {
- parent = child->parent;
- if (child == parent->left) {
- sister = parent->right;
- assert (sister != nil);
- if (sister->color == dnode_red) {
- sister->color = dnode_black;
- parent->color = dnode_red;
- rotate_left(parent);
- sister = parent->right;
- assert (sister != nil);
- }
- if (sister->left->color == dnode_black
- && sister->right->color == dnode_black) {
- sister->color = dnode_red;
- child = parent;
- } else {
- if (sister->right->color == dnode_black) {
- assert (sister->left->color == dnode_red);
- sister->left->color = dnode_black;
- sister->color = dnode_red;
- rotate_right(sister);
- sister = parent->right;
- assert (sister != nil);
- }
- sister->color = parent->color;
- sister->right->color = dnode_black;
- parent->color = dnode_black;
- rotate_left(parent);
- break;
- }
- } else { /* symmetric case: child == child->parent->right */
- assert (child == parent->right);
- sister = parent->left;
- assert (sister != nil);
- if (sister->color == dnode_red) {
- sister->color = dnode_black;
- parent->color = dnode_red;
- rotate_right(parent);
- sister = parent->left;
- assert (sister != nil);
- }
- if (sister->right->color == dnode_black
- && sister->left->color == dnode_black) {
- sister->color = dnode_red;
- child = parent;
- } else {
- if (sister->left->color == dnode_black) {
- assert (sister->right->color == dnode_red);
- sister->right->color = dnode_black;
- sister->color = dnode_red;
- rotate_left(sister);
- sister = parent->left;
- assert (sister != nil);
- }
- sister->color = parent->color;
- sister->left->color = dnode_black;
- parent->color = dnode_black;
- rotate_right(parent);
- break;
- }
- }
- }
-
- child->color = dnode_black;
- dict_root(dict)->color = dnode_black;
- }
-
- assert (dict_verify(dict));
-
- return delete;
-}
-
-/*
- * Allocate a node using the dictionary's allocator routine, give it
- * the data item.
- */
-
-int dict_alloc_insert(dict_t *dict, const void *key, void *data)
-{
- dnode_t *node = dict->allocnode(dict->context);
-
- if (node) {
- dnode_init(node, data);
- dict_insert(dict, node, key);
- return 1;
- }
- return 0;
-}
-
-void dict_delete_free(dict_t *dict, dnode_t *node)
-{
- dict_delete(dict, node);
- dict->freenode(node, dict->context);
-}
-
-/*
- * Return the node with the lowest (leftmost) key. If the dictionary is empty
- * (that is, dict_isempty(dict) returns 1) a null pointer is returned.
- */
-
-dnode_t *dict_first(dict_t *dict)
-{
- dnode_t *nil = dict_nil(dict), *root = dict_root(dict), *left;
-
- if (root != nil)
- while ((left = root->left) != nil)
- root = left;
-
- return (root == nil) ? NULL : root;
-}
-
-/*
- * Return the node with the highest (rightmost) key. If the dictionary is empty
- * (that is, dict_isempty(dict) returns 1) a null pointer is returned.
- */
-
-dnode_t *dict_last(dict_t *dict)
-{
- dnode_t *nil = dict_nil(dict), *root = dict_root(dict), *right;
-
- if (root != nil)
- while ((right = root->right) != nil)
- root = right;
-
- return (root == nil) ? NULL : root;
-}
-
-/*
- * Return the given node's successor node---the node which has the
- * next key in the the left to right ordering. If the node has
- * no successor, a null pointer is returned rather than a pointer to
- * the nil node.
- */
-
-dnode_t *dict_next(dict_t *dict, dnode_t *curr)
-{
- dnode_t *nil = dict_nil(dict), *parent, *left;
-
- if (curr->right != nil) {
- curr = curr->right;
- while ((left = curr->left) != nil)
- curr = left;
- return curr;
- }
-
- parent = curr->parent;
-
- while (parent != nil && curr == parent->right) {
- curr = parent;
- parent = curr->parent;
- }
-
- return (parent == nil) ? NULL : parent;
-}
-
-/*
- * Return the given node's predecessor, in the key order.
- * The nil sentinel node is returned if there is no predecessor.
- */
-
-dnode_t *dict_prev(dict_t *dict, dnode_t *curr)
-{
- dnode_t *nil = dict_nil(dict), *parent, *right;
-
- if (curr->left != nil) {
- curr = curr->left;
- while ((right = curr->right) != nil)
- curr = right;
- return curr;
- }
-
- parent = curr->parent;
-
- while (parent != nil && curr == parent->left) {
- curr = parent;
- parent = curr->parent;
- }
-
- return (parent == nil) ? NULL : parent;
-}
-
-void dict_allow_dupes(dict_t *dict)
-{
- dict->dupes = 1;
-}
-
-#undef dict_count
-#undef dict_isempty
-#undef dict_isfull
-#undef dnode_get
-#undef dnode_put
-#undef dnode_getkey
-
-dictcount_t dict_count(dict_t *dict)
-{
- return dict->nodecount;
-}
-
-int dict_isempty(dict_t *dict)
-{
- return dict->nodecount == 0;
-}
-
-int dict_isfull(dict_t *dict)
-{
- return dict->nodecount == dict->maxcount;
-}
-
-int dict_contains(dict_t *dict, dnode_t *node)
-{
- return verify_dict_has_node(dict_nil(dict), dict_root(dict), node);
-}
-
-static dnode_t *dnode_alloc(void *context)
-{
- return malloc(sizeof *dnode_alloc(NULL));
-}
-
-static void dnode_free(dnode_t *node, void *context)
-{
- free(node);
-}
-
-dnode_t *dnode_create(void *data)
-{
- dnode_t *new = malloc(sizeof *new);
- if (new) {
- new->data = data;
- new->parent = NULL;
- new->left = NULL;
- new->right = NULL;
- }
- return new;
-}
-
-dnode_t *dnode_init(dnode_t *dnode, void *data)
-{
- dnode->data = data;
- dnode->parent = NULL;
- dnode->left = NULL;
- dnode->right = NULL;
- return dnode;
-}
-
-void dnode_destroy(dnode_t *dnode)
-{
- assert (!dnode_is_in_a_dict(dnode));
- free(dnode);
-}
-
-void *dnode_get(dnode_t *dnode)
-{
- return dnode->data;
-}
-
-const void *dnode_getkey(dnode_t *dnode)
-{
- return dnode->key;
-}
-
-void dnode_put(dnode_t *dnode, void *data)
-{
- dnode->data = data;
-}
-
-int dnode_is_in_a_dict(dnode_t *dnode)
-{
- return (dnode->parent && dnode->left && dnode->right);
-}
-
-void dict_process(dict_t *dict, void *context, dnode_process_t function)
-{
- dnode_t *node = dict_first(dict), *next;
-
- while (node != NULL) {
- /* check for callback function deleting */
- /* the next node from under us */
- assert (dict_contains(dict, node));
- next = dict_next(dict, node);
- function(dict, node, context);
- node = next;
- }
-}
-
-static void load_begin_internal(dict_load_t *load, dict_t *dict)
-{
- load->dictptr = dict;
- load->nilnode.left = &load->nilnode;
- load->nilnode.right = &load->nilnode;
-}
-
-void dict_load_begin(dict_load_t *load, dict_t *dict)
-{
- assert (dict_isempty(dict));
- load_begin_internal(load, dict);
-}
-
-void dict_load_next(dict_load_t *load, dnode_t *newnode, const void *key)
-{
- dict_t *dict = load->dictptr;
- dnode_t *nil = &load->nilnode;
-
- assert (!dnode_is_in_a_dict(newnode));
- assert (dict->nodecount < dict->maxcount);
-
-#ifndef NDEBUG
- if (dict->nodecount > 0) {
-#ifdef BE_QSORT_COMPATIBLE
- if (dict->dupes)
- assert (dict->compare(&nil->left->key, &key) <= 0);
- else
- assert (dict->compare(&nil->left->key, &key) < 0);
-#else
- if (dict->dupes)
- assert (dict->compare(nil->left->key, key) <= 0);
- else
- assert (dict->compare(nil->left->key, key) < 0);
-#endif
- }
-#endif
-
- newnode->key = key;
- nil->right->left = newnode;
- nil->right = newnode;
- newnode->left = nil;
- dict->nodecount++;
-}
-
-void dict_load_end(dict_load_t *load)
-{
- dict_t *dict = load->dictptr;
- dnode_t *tree[DICT_DEPTH_MAX] = { 0 };
- dnode_t *curr, *dictnil = dict_nil(dict), *loadnil = &load->nilnode, *next;
- dnode_t *complete = 0;
- dictcount_t fullcount = DICTCOUNT_T_MAX, nodecount = dict->nodecount;
- dictcount_t botrowcount;
- unsigned baselevel = 0, level = 0, i;
-
- assert (dnode_red == 0 && dnode_black == 1);
-
- while (fullcount >= nodecount && fullcount)
- fullcount >>= 1;
-
- botrowcount = nodecount - fullcount;
-
- for (curr = loadnil->left; curr != loadnil; curr = next) {
- next = curr->left;
-
- if (complete == NULL && botrowcount-- == 0) {
- assert (baselevel == 0);
- assert (level == 0);
- baselevel = level = 1;
- complete = tree[0];
-
- if (complete != 0) {
- tree[0] = 0;
- complete->right = dictnil;
- while (tree[level] != 0) {
- tree[level]->right = complete;
- complete->parent = tree[level];
- complete = tree[level];
- tree[level++] = 0;
- }
- }
- }
-
- if (complete == NULL) {
- curr->left = dictnil;
- curr->right = dictnil;
- curr->color = level % 2;
- complete = curr;
-
- assert (level == baselevel);
- while (tree[level] != 0) {
- tree[level]->right = complete;
- complete->parent = tree[level];
- complete = tree[level];
- tree[level++] = 0;
- }
- } else {
- curr->left = complete;
- curr->color = (level + 1) % 2;
- complete->parent = curr;
- tree[level] = curr;
- complete = 0;
- level = baselevel;
- }
- }
-
- if (complete == NULL)
- complete = dictnil;
-
- for (i = 0; i < DICT_DEPTH_MAX; i++) {
- if (tree[i] != 0) {
- tree[i]->right = complete;
- complete->parent = tree[i];
- complete = tree[i];
- }
- }
-
- dictnil->color = dnode_black;
- dictnil->right = dictnil;
- complete->parent = dictnil;
- complete->color = dnode_black;
- dict_root(dict) = complete;
-
- assert (dict_verify(dict));
-}
-
-void dict_merge(dict_t *dest, dict_t *source)
-{
- dict_load_t load;
- dnode_t *leftnode = dict_first(dest), *rightnode = dict_first(source);
-
- assert (dict_similar(dest, source));
-
- if (source == dest)
- return;
-
- dest->nodecount = 0;
- load_begin_internal(&load, dest);
-
- for (;;) {
- if (leftnode != NULL && rightnode != NULL) {
-#ifdef BE_QSORT_COMPATIBLE
- if (dest->compare(&leftnode->key, &rightnode->key) < 0)
- goto copyleft;
- else
- goto copyright;
-#else
- if (dest->compare(leftnode->key, rightnode->key) < 0)
- goto copyleft;
- else
- goto copyright;
-#endif
- } else if (leftnode != NULL) {
- goto copyleft;
- } else if (rightnode != NULL) {
- goto copyright;
- } else {
- assert (leftnode == NULL && rightnode == NULL);
- break;
- }
-
- copyleft:
- {
- dnode_t *next = dict_next(dest, leftnode);
- #ifndef NDEBUG
- leftnode->left = NULL; /* suppress assertion in dict_load_next */
- #endif
- dict_load_next(&load, leftnode, leftnode->key);
- leftnode = next;
- continue;
- }
-
- copyright:
- {
- dnode_t *next = dict_next(source, rightnode);
-#ifndef NDEBUG
- rightnode->left = NULL;
-#endif
- dict_load_next(&load, rightnode, rightnode->key);
- rightnode = next;
- continue;
- }
- }
-
- dict_clear(source);
- dict_load_end(&load);
-}
diff --git a/libutil/kazlib/dict.h b/libutil/kazlib/dict.h
deleted file mode 100644
index 2bab634..0000000
--- a/libutil/kazlib/dict.h
+++ /dev/null
@@ -1,142 +0,0 @@
-/*
- * Dictionary Abstract Data Type
- * Copyright (C) 1997 Kaz Kylheku <kaz at ashi.footprints.net>
- *
- * Free Software License:
- *
- * All rights are reserved by the author, with the following exceptions:
- * Permission is granted to freely reproduce and distribute this software,
- * possibly in exchange for a fee, provided that this copyright notice appears
- * intact. Permission is also granted to adapt this software to produce
- * derivative works, as long as the modified versions carry this copyright
- * notice and additional notices stating that the work has been modified.
- * This source code may be translated into executable form and incorporated
- * into proprietary software; there is no requirement for such software to
- * contain a copyright notice related to this source.
- *
- */
-
-#ifndef DICT_H
-#define DICT_H
-
-#include <limits.h>
-#ifdef KAZLIB_SIDEEFFECT_DEBUG
-#include "sfx.h"
-#endif
-
-/*
- * Blurb for inclusion into C++ translation units
- */
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-typedef unsigned long dictcount_t;
-#define DICTCOUNT_T_MAX ULONG_MAX
-
-/*
- * The dictionary is implemented as a red-black tree
- */
-
-typedef enum { dnode_red, dnode_black } dnode_color_t;
-
-typedef struct dnode_t {
-#if defined(DICT_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG)
- struct dnode_t *dict_left;
- struct dnode_t *dict_right;
- struct dnode_t *dict_parent;
- dnode_color_t dict_color;
- const void *dict_key;
- void *dict_data;
-#else
- int dict_dummy;
-#endif
-} dnode_t;
-
-typedef int (*dict_comp_t)(const void *, const void *);
-typedef dnode_t *(*dnode_alloc_t)(void *);
-typedef void (*dnode_free_t)(dnode_t *, void *);
-
-typedef struct dict_t {
-#if defined(DICT_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG)
- dnode_t dict_nilnode;
- dictcount_t dict_nodecount;
- dictcount_t dict_maxcount;
- dict_comp_t dict_compare;
- dnode_alloc_t dict_allocnode;
- dnode_free_t dict_freenode;
- void *dict_context;
- int dict_dupes;
-#else
- int dict_dummmy;
-#endif
-} dict_t;
-
-typedef void (*dnode_process_t)(dict_t *, dnode_t *, void *);
-
-typedef struct dict_load_t {
-#if defined(DICT_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG)
- dict_t *dict_dictptr;
- dnode_t dict_nilnode;
-#else
- int dict_dummmy;
-#endif
-} dict_load_t;
-
-extern dict_t *dict_create(dictcount_t, dict_comp_t);
-extern void dict_set_allocator(dict_t *, dnode_alloc_t, dnode_free_t, void *);
-extern void dict_destroy(dict_t *);
-extern void dict_free_nodes(dict_t *);
-extern void dict_free(dict_t *);
-extern dict_t *dict_init(dict_t *, dictcount_t, dict_comp_t);
-extern void dict_init_like(dict_t *, const dict_t *);
-extern int dict_verify(dict_t *);
-extern int dict_similar(const dict_t *, const dict_t *);
-extern dnode_t *dict_lookup(dict_t *, const void *);
-extern dnode_t *dict_lower_bound(dict_t *, const void *);
-extern dnode_t *dict_upper_bound(dict_t *, const void *);
-extern void dict_insert(dict_t *, dnode_t *, const void *);
-extern dnode_t *dict_delete(dict_t *, dnode_t *);
-extern int dict_alloc_insert(dict_t *, const void *, void *);
-extern void dict_delete_free(dict_t *, dnode_t *);
-extern dnode_t *dict_first(dict_t *);
-extern dnode_t *dict_last(dict_t *);
-extern dnode_t *dict_next(dict_t *, dnode_t *);
-extern dnode_t *dict_prev(dict_t *, dnode_t *);
-extern dictcount_t dict_count(dict_t *);
-extern int dict_isempty(dict_t *);
-extern int dict_isfull(dict_t *);
-extern int dict_contains(dict_t *, dnode_t *);
-extern void dict_allow_dupes(dict_t *);
-extern int dnode_is_in_a_dict(dnode_t *);
-extern dnode_t *dnode_create(void *);
-extern dnode_t *dnode_init(dnode_t *, void *);
-extern void dnode_destroy(dnode_t *);
-extern void *dnode_get(dnode_t *);
-extern const void *dnode_getkey(dnode_t *);
-extern void dnode_put(dnode_t *, void *);
-extern void dict_process(dict_t *, void *, dnode_process_t);
-extern void dict_load_begin(dict_load_t *, dict_t *);
-extern void dict_load_next(dict_load_t *, dnode_t *, const void *);
-extern void dict_load_end(dict_load_t *);
-extern void dict_merge(dict_t *, dict_t *);
-
-#if defined(DICT_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG)
-#ifdef KAZLIB_SIDEEFFECT_DEBUG
-#define dict_isfull(D) (SFX_CHECK(D)->dict_nodecount == (D)->dict_maxcount)
-#else
-#define dict_isfull(D) ((D)->dict_nodecount == (D)->dict_maxcount)
-#endif
-#define dict_count(D) ((D)->dict_nodecount)
-#define dict_isempty(D) ((D)->dict_nodecount == 0)
-#define dnode_get(N) ((N)->dict_data)
-#define dnode_getkey(N) ((N)->dict_key)
-#define dnode_put(N, X) ((N)->dict_data = (X))
-#endif
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/libutil/kazlib/docs/CHANGES b/libutil/kazlib/docs/CHANGES
deleted file mode 100644
index 3c949eb..0000000
--- a/libutil/kazlib/docs/CHANGES
+++ /dev/null
@@ -1,290 +0,0 @@
-New in 1.20
-
- 1. Bugfix in except.h. Modified non-volatile auto variables were
- being accessed after longjmp.
-
-New in 1.19
-
- 1. Rewrite of broken dict_free.
- 2. Fixed embarassing build breakages that accidentally went into 1.18
- 3. Function hash_scan_delete_free renamed to hash_scan_delfree to be
- distinct from hash_scan_delete in the first 14 characters.
- 4. To resolve inconsistencies between hash_free and dict_free,
- and a difference between the actual behavior of hash_free and
- the documented behavior, these two functions are marked obsolescent.
- The functions dict_free_nodes and hash_free_nodes are provided.
- The obsolescent functions continue to work as before, for now.
- 5. Documentation of hash_free is fixed to say that it also subjects
- the hash to hash_destroy, which is what the implementation does.
- 6. Documentation states what release it is for.
-
-New in 1.18
-
- 1. Error in assert expression in list_merge fixed.
- 2. Semantics of list_merge extended to allow list to be merged
- onto itself (which is a noop).
- 3. Clarified interface specification of list_transfer and list_extract;
- the source and destination list may be the same object.
- 4. New functions:
- dict_init_like: create a dictionary similar to another one;
- dict_similar: determine whether two dictionaries are similar;
- dict_merge: merge contents of one dictionary to another.
- 5. Dictionary test main can juggle multiple dictionaries, and test
- dict_merge.
- 6. If a hash node is inserted into some hash, it is a now a constraint
- violation to insert it again into some hash.
- 7. The hash_scan_delete_free function has been implemented; it is to
- hash_scan_delete what hash_delete_free is to hash_delete.
-
-New in 1.17
-
- Carl van Tast <vanTast at netway.at>:
- 1. Removed references to ``safe malloc'' from some comments.
- 2. Swapped ``allowed'' and ``not allowed'' in comment to
- verify_bintree.
- 3. Fixed comment to list_next: this function never returns the
- sentinel.
- 4. lnode_pool_init: nodes[i].prev = nodes instead of nodes + 1. This
- saves one or two CPU cycles :-) and it gives a valid address even
- if we have a (somewhat pathological) pool with just one element.
-
- Kaz:
- 5. Dropped extra parameter from tree rotation functions in dict.c. Should
- shave a few cycles.
- 6. Fixed error in the duplicate key iteration idiom example in the
- documentation (see the section on dict_upper_bound).
- 7. Forgotten #include <string.h> added to hash.c
-
-New in 1.16
-
- 1. Added an interface for loading the contents of a dictionary from an
- ordered sequence. This is done in O(n) time by a direct bottom-up
- construction of the red-black tree, making it much faster than
- the O(n log n) process of inserting each element.
- 2. Miscellaneous cleanup: missing const qualifiers were added
- to key pointer parameters, some incorrect comments fixed;
- spelling errors corrected in documentation.
-
-New in 1.15
-
- 1. Another potential exception handling memory leak fixed. This one
- has to do with throwing an exception from within a try-catch region
- in which an exception was just caught. The new exception replaces
- the old without the old's dynamic memory being disposed of.
- 2. Restrictions added on except_rethrow.
- 3. Exception module must now be explicitly initialized with except_init.
- 4. Structure members in exception header renamed to adhere to documented
- namespace.
- 5. The exwrap.[ch] source files are gone. There is support for memory
- allocation with exception handling in except.c, which supports user
- defined allocators.
- 6. Three bugfixes to sfx parser. First, unary operators take a cast
- expression, not a unary expression. Secondly, sizeof doesn't throw a syntax
- error anymore on things that look like casts, but maybe are not.
- Thirdly, empty parentheses weren't handled right in treatment of
- ambiguous expressions, e.g. (a)() was declared a syntax error.
- 7. Changed the representation of hash table chains. They are now
- singly linked lists, which means that the overhead of managing
- back pointers is gone. Only deletion is slightly more complicated
- now because it has to search from the beginning of the chain.
- [Rationale: this is okay, since chains are supposed to be short
- in a hash table!]
- 8. Rewritten test main() in list.c. It's now more like the others
- with a menu. Previously it was essentially a file sorting program.
- 9. New function: list_find. Exhaustively searches the list for a
- matching entry, returns pointer to node if found.
-
-New in 1.14
-
- 1. Got rid of some overbearing copyright restrictions. There is no need for
- executables to contain copyright notices. In fact, there are no
- restrictions on the use, or distribution in executable form.
- 2. Tiny tweak in red-black fixup code of dict_insert.
- 3. Keys in hash and dict are declared const void * now in all functions
- rather than plain void *. This means that casts are no longer
- necessary when calling insert or lookup functions with const
- data as the key. But casts of the return value of hnode_getkey
- or dnode_getkey may be required.
- 4. Fixed compile breakage of except.c when posix thread support enabled.
- 5. Side effect assertion interface now performs caching, to avoid
- parsing the same expressions over and over again. Thus debugging with
- KAZLIB_SIDEEFFECT_DEBUG incurs a smaller performance hit.
- 6. Major bugfix to sfx expression parser. The function dealing with
- disambiguating casts had to be rewritten to do more sophisticated
- lookahead and backtracking. It all started with Mark Brady discovered
- that (a++)+b was being incorrectly diagnosed as a syntax error.
- 7. Added documentation. more examples for uses of dictionaries, and
- exception handling. Some documentation about the internals
- of exception handling added. Changed document format for narrower
- margins, reducing page count and increasing readability.
- 8. Bugfix in except_rethrow. It was freeing the dynamic data of the
- exception even though it's not handled yet.
-
-New in 1.13
-
- 1. Fixed some potential memory leaks in except.c.
- 2. Finished all interface documentation. All that is left now
- is to flesh out the implementation notes.
- 3. Fixed a bug in POSIX threaded variant of except.c. Null
- function pointer dereference in unhandled exception case.
- 4. Macros beginning with E[A-Z] have been renamed to stay out
- of space reserved for <errno.h>.
- 5. Identifiers in exwrap.[ch] have been renamed from having
- ex_ prefixed to having exwrap_ prefixes.
-
-New in 1.12
-
- 1. COOL! New module for detecting side effects in C expressions.
- 2. Serious bugfix in hash_init(). The computation of the initial hash
- mask was completely botched up. Historically this code has seen little
- testing because hashing over a user supplied table is not extendible.
- Users of hash_create() are not affected.
- 3. Tried to make computation of hash_val_t_bit more threadsafe. It should
- be okay if writes to int objects are atomic, and concurrent writes of
- the same int value to a given object are safe.
- 4. Makefile renamed to Makefile.gcc. Makevile.vc added. The rename
- is retroactive to all prior releases.
- 5. OPAQUE_DEBUG becomes KAZLIB_OPAQUE_DEBUG and TEST_MAIN becomes
- KAZLIB_TEST_MAIN. In general, macros that affect how the modules
- build should be confined to a special namespace.
- 6. New KAZLIB_SIDEEFFECT_DEBUG feature to enable diagnosis of side
- effect expressions being passed to macros that evaluate their arguments
- more than once.
-
-New in 1.11
-
- 1. Improvements in experimental exception handling module:
- except_throwf has been added which takes printf-like arguments;
- except_checked_cleanup_pop has been added to provide a measure
- of safety; there is now a way to pass arbitrary data from the throw site
- to the catch.
- 2. Improvements in dict_insert. A redundant call to the comparison function
- has been eliminated, resulting in one fewer comparisons per insert
- operation! Also a redundant test has been removed from the controlling
- expression of the fixup loop, taking advantage of the fact that nil
- is always black, and hence the root node always has a black parent.
- 3. Small change in dict_delete. A test in the fixup loop has been eliminated
- by temporarily coloring the root node red. See comment and diff between
- dict.c revision 1.25 and 1.26.
- 4. Test program blast.pl deletes keys out of order; to get in order
- delete, initialize $factor_d to 1.
-
-New in 1.10
-
- 1. The dict_init function now correctly initializes allocator-related
- members of the dict structure.
- 2. Tiny optimization in dict_lookup---less frequent cases tested last.
- 3. Added list_extract, for extracting list slices (more general than
- list_transfer).
- 4. Incorporated changes from Loic Dachary: hash_free() has been
- added for deleting all nodes; hash and compare functions
- from the hash.c test code are now available to the user as
- defaults if null pointers are given to hash_init() or
- hash_create(); and hash_set_allocator restores the default
- allocator routines if null pointers are given to it.
- 5. Changes to dict analogous to hash: dict_free() added, etc.
- 6. New exception handling module added (experimental).
- 7. Much new documentation.
-
-New in 1.9
-
- 1. Third argument of list_transfer may be null, in which case no nodes
- are transferred. [Rationale: allows empty source list to be treated
- without special case testing when all nodes are being transferred.]
- 2. Two new functions added to dict: dict_upper_bound and dict_lower_bound.
- These allow for inexact and range searches.
-
-New in 1.8
-
- 1. New improved hashing function in the hash.c test code. It turns out that
- when I changed the hash table algorithm, the blast.pl testcase was
- hashing all to a single chain due to the pathologically bad hashing
- function. The new hashing function should be good enough for general use.
- It uses each nybble of the key to index a table of 16 random 32 bit integers.
- These integers are XOR-ed into the hash value which is rotated after each
- XOR.
- 2. Spurious semicolon removed from the #define of HASH_VAL_T_BIT.
- 3. I fixed some incorrect comments in hash.c which still talked about the
- old algorithm from release 1.5 and older.
- 4. The smalloc.c module is no longer supported. It's still in RCS but it's not
- tagged as being part of release 1.8, and is not used by any of the other
- sources. The standard library memory allocation functions are now used
- directly. [Rationale: smalloc.c is overkill and interferes with
- integration of the other source files into projects. Conscientious programmer
- already ahve their own tools for debugging allocator corruption, anyway.]
-
-New in 1.7
-
- 1. Missing #include <stdlib.h> added to smalloc.h
- 2. The dict_delete() functions internals have been changed to make it much
- more sane. This function no longer has the potential to return a node
- other than the one that is passed to it.
- 3. The changes to dict_delete() also fix a serious bug in dict_process().
- The dict_process computes a pointer to a node's successor before
- invoking the user callback to process a node. If the user callback calls
- dict_delete() on the node, under the old dict_delete() semantics it was
- possible for the successor to get deleted instead. Thus dict_process()
- could end up with an invalid pointer.
- 4. The changes to dict_delete() also mean that key and value information will
- never be relocated from one node to another. User code can now rely on this
- convenient assumption.
-
-New in 1.6
-
- 1. The extendible hashing algorithm internals have changed. This
- has a potential impact on the behavior with respect to hashing functions
- which were written to work well specifically with the old hashing
- scheme. For a silly reason, in the old hashing scheme, the top N bits
- were always taken from the results of a hashing function, for a hash
- table size of 2^N chains. In the new scheme, the bottom N bits are taken
- instead. [Rationale: This is change makes it easier to write portable
- hashing functions and simplifies the functions that expand or contract
- the table, making them more efficient.]
- 2. Added const qualifiers to the rcsid[] and right[] char arrays,
- which shuts up the GCC compiler from complaining that these are
- unused statics.
-
-New in 1.5
-
- 1. First two arguments to list_prune_graft() are reversed. The leftmost
- argument is now the destination list. Moreover, the function has been
- renamed list_transfer(). [Rationale: this ordering of parameters is
- consistent with list_merge(), and the standard C <string.h> functions
- also pass destination pointers on the left. Renaming the function
- protects against incorrect use.]
-
- 2. Red-Black tree dictionaries now support duplicate keys. [Rationale:
- duplicate keys could be useful in some applications.] When a dictionary
- is created or initialized, it does not allow duplicate keys. The
- function dict_allow_dupes() is used to set a flag in a dictionary to
- henceforth allow duplicates. Once made, the decision to allow
- duplicates cannot be reversed. [Rationale: toggling between allowing
- and disallowing duplicates does not seem useful. Once duplicates are
- admitted, there is no point in disallowing duplicates.] When a key is
- sought in tree that currently allows duplicates, the leftmost node
- containing that key is chosen from among the nodes that contain
- duplicates of the key. Then dict_next() can be used to fetch the
- remaining duplicates one by one. No particular order among the
- duplicates may be assumed. However, for what it may be worth, the order
- between any two duplicates is preserved for as long as they both remain
- in the dictionary.
-
- 3. The function prototypes in the header files have been modified to eliminate
- parameter names. [Rationale: parameter names in prototypes have only
- documentary value, and may clash with macro identifiers defined in other
- headers.]
-
- 4. Dictionary and hash table now has support for automatic allocation of
- nodes in the insert and delete operations, which means that the user
- can add items in one operation instead of the two operations of
- allocating a node and inserting it. [Rationale: ease of use.] There is
- support for user-defined allocators; the default allocators use the
- smalloc.c routines. For any instance of a dict_t or hash_t object, the
- user can override the allocator functions by supplying his or her
- own pointers to suitable functions, and a context pointer that
- will be passed to these functions when they are called through that
- particular dict_t or hash_t instance. [Rationale: flexibility, ease of
- use, promotes good design.] The funtion pointers can only be set when
- the data structure is empty. [Rationale: it is undesirable to switch to
- a different allocator when there are nodes in the dictionary; it might
- lead to the error of freeing a node with an incorrect allocator.]
diff --git a/libutil/kazlib/docs/MUST_READ b/libutil/kazlib/docs/MUST_READ
deleted file mode 100644
index 20ca12e..0000000
--- a/libutil/kazlib/docs/MUST_READ
+++ /dev/null
@@ -1,25 +0,0 @@
-Greetings, Programmer!
-
-I gather that because you are reading this, you are probably considering using
-the C language translation units included here in your own software. If that
-is the case, I would like to know who you are and urge you to contact me.
-
-Here is why: I rove over this code periodically looking for defects. In fact,
-I use it in my own programming projects. If I discover a defect, I will
-notify everyone who I know is a user of this software. If there is a serious
-defect in some code that you are using in your software project, wouldn't you
-want to be informed? In fact, there is no question that you _need_ to be
-informed!
-
-Here is what you do: simply send an e-mail message to kaz at ashi.footprints.net
-with the subject "kazlib" and the body "I am a user". Be sure that your message
-has a good return address. I will manually add your e-mail address to a list
-which I will use only for the purpose of notifications regarding Kazlib. You
-will receive a reply to the effect that you are added.
-
-If ever you should wish to be removed from this list, simply ask and it shall
-be done.
-
-Yours in earnest,
-
- Kaz Kylheku
diff --git a/libutil/kazlib/docs/README b/libutil/kazlib/docs/README
deleted file mode 100644
index 08f14a1..0000000
--- a/libutil/kazlib/docs/README
+++ /dev/null
@@ -1,66 +0,0 @@
-This collection of data structures is maintained by
-Kaz Kylheku <kaz at ashi.footprints.net>
-
-INSTRUCTIONS
-
-Simply add the necessary .c and .h files to your project. Include the
-appropriate .h file in any translation unit that interfaces with one or more of
-the kazlib modules. Then compile and link the modules together with your program.
-
-To use kazlib in a C++ project, don't compile them with a C++ compiler.
-Compile with a C compiler, and include the header files in
-your C++ translation units. Then link together the translated C and C++.
-As of release 1.2, the header files should work with C++.
-
-IMPORTANT NOTES
-
-1. Self checks
-
-The modules in this collection perform extensive self-checks, some of
-which make the performance really poor (by actually raising the overall
-asymptotic complexity of an operation, for example from O(log N) to O(N). The
-instrumentation assertions can be disabled by compiling with the NDEBUG macro
-defined.
-
-You can check that your project does not violate the principles of
-implementation hiding in connection with its use of the kazlib modules. This
-is accomplished by defining the macro KAZLIB_OPAQUE_DEBUG at the beginning of
-any translation unit which includes the kazlib header files. Note that
-whereas this will detect violations, it will not result in a translation
-that can be linked against the kazlib. When you are done checking, turn
-off KAZLIB_OPAQUE_DEBUG and recompile. If your compiler has a special ``check only''
-mode which enables it to perform syntax and type checking without doing
-an actual translation (similar to lint), it may be a time-saving idea to
-use it in conjunction with KAZLIB_OPAQUE_DEBUG.
-
-2. Macros with side effects
-
-Some of the kazlib header files define macros that evaluate their arguments
-more than once. This means that if expressions with side effects are passed
-to these macros, undesirable and undefined behavior will happen. There is
-support in Kazlib for catching these kinds of bugs: compile with
-KAZLIB_SIDEEFFECT_DEBUG, and add the except.c and sfx.c modules to your
-object. The macros will now parse their expressions at run time to diagnose
-the presence of side effects and function calls. It's easy to add this support
-to your own code!
-
-3. Thread support
-
-POSIX thread support is enabled by predefining KAZLIB_POSIX_THREADS. Currently
-only the exception-handling module has any need for this. When compiled that
-way, it provides thread-safe exception handling. Threads can independently
-throw exceptions and each thread can install its own specific catcher
-for unhandled exceptions. Moreover, each thread can register its own
-memory allocator functions.
-
-Note: this variant of the code also depends on the ability to cast between void
-* and function pointers, which is a common language extension.
-
-4. CVS identification
-
-The source files contain declarations of a static char array variable called
-rcsid. This contains an expansion of the CVS identification of each module,
-making it possible to determine the ``bill of materials'' that went into an
-executable build. I have now wrapped the declarations of these rcsid[] arrays
-so they are conditional on KAZLIB_RCSID being defined. For many users, these
-are just a waste of space.
diff --git a/libutil/kazlib/docs/docs.ist b/libutil/kazlib/docs/docs.ist
deleted file mode 100644
index 808c029..0000000
--- a/libutil/kazlib/docs/docs.ist
+++ /dev/null
@@ -1,4 +0,0 @@
-preamble
-"\\begin{theindex}\n\\addcontentsline{toc}{section}{Index}\n"
-postamble
-"\n\\end{theindex}\n"
diff --git a/libutil/kazlib/docs/docs.ltx b/libutil/kazlib/docs/docs.ltx
deleted file mode 100644
index 139f212..0000000
--- a/libutil/kazlib/docs/docs.ltx
+++ /dev/null
@@ -1,4155 +0,0 @@
-\documentclass{article}
-\usepackage{makeidx}
-\usepackage[margin=1.0in]{geometry}
-\makeatletter
-\newcommand{\defsubsection}{\@startsection
- {subsection}
- {2}
- {0pt}
- {2.0ex plus 0.1ex minus 0.05ex}
- {-0pt}
- {\normalfont\normalsize\bfseries}}
-\newcommand{\defsubsubsection}{\@startsection
- {subsection}
- {3}
- {0ex}
- {2.0ex plus 0.1ex minus 0.05ex}
- {1.0ex}
- {\normalfont\normalsize\bfseries}}
-\renewcommand{\paragraph}{\@startsection
- {paragraph}
- {4}
- {0ex}
- {2.0ex plus 0.1ex minus 0.05ex}
- {1.0ex}
- {\normalsize\bfseries}}
-\makeatother
-\title{Kazlib---Reusable Components\\for C Programming}
-\author{Kaz Kylheku}
-\date{Release 1.20\\July 24, 2001}
-\makeindex
-\setcounter{tocdepth}{1}
-\setcounter{secnumdepth}{4}
-\begin{document}
-\catcode`\_=11
-\def\indextype#1{\index{#1@{\tt #1} type}}
-\def\indexmacro#1{\index{#1@{\tt #1} macro}}
-\def\indexobject#1{\index{#1@{\tt #1} object}}
-\def\indexfunc#1{\index{#1@{\tt #1} function}}
-\def\indexenum#1{\index{#1@{\tt #1} enum constant}}
-\def\synopsis{\paragraph*{Synopsis}}
-\def\constraints{\paragraph*{Constraints}}
-\def\description{\paragraph*{Description}}
-\def\example{\paragraph*{Example}}
-\maketitle
-\abstract{The aim of the Kazlib project is to provide a well-documented
-programming interface featuring commonly needed programming abstractions,
-accompanied by a high quality, portable reference implementation.
-Kazlib consists of four independent components: a list module, a hash table
-module, a dictionary module and an exception handling module. The reference
-implementations of the first three of these are based on, respectively, the
-following algorithms: doubly linked circular list with sentinel node,
-extendible hashing, and red-black tree.}
-\tableofcontents
-\section{Introduction}
-This document establishes the provisions required of an implementation of the
-Kazlib library, and describes a reference implementation thereof.
-This document specifies
-\begin{itemize}
-\item the names and types of identifiers and preprocessor symbols made
- available by each component;
-\item identifier name spaces reserved for future use by each component;
-\item the interface syntax and semantics of each component operation;
-\item the conditions required for the well-defined execution of each operation;
-\item the externally visible behavior of each component, including global
- side effects and the effects on the subject data structures;
-
-\item and the implementation language of Kazlib.
-\end{itemize}
-Furthermore, this document describes, but does not specify
-\begin{itemize}
-\item the implementation details of structure objects manipulated by the
- operations of each component;
-\item objects and functions that are defined by the implementation of
- each component but are not externally visible;
-\item the algorithms and implementation details of the operations.
-\end{itemize}
-Finally, this document does {\em not\/} specify or describe
-\begin{itemize}
-\item the specific choices for parameters which may be adjusted by an
- installation or implementation of Kazlib.
-\item the size of any data structure which will exceed the capacity of
- a particular installation.
-\item the mechanisms or procedures for the translation of Kazlib and
- their integration with other translation units.
-\end{itemize}
-
-\section{References}
-\label{sec:references}
-
-\begin{trivlist}
-\item ISO 9899:1990, {\it Programming Languages---C.}
-\item {\it Introduction to Algorithms}, Thomas H. Cormen, Charles E.
-Leiserson, Ronald L. Rivest, eighth printing, 1992.
-\end{trivlist}
-
-\section{Definitions and conventions}
-The following terms shall be interpreted in accordance with the definitions
-below. Other terms appearing in this document shall be defined upon their
-first mention, indicated by {\it italic\/} type. Any terms not explicitly
-defined in this document should be interpreted according to ISO 9899-1990,
-clause 3. Failing that, they should be interpreted according to other works
-listed in section \ref{sec:references}.
-\nobreak
-\defsubsection{implementation}: A library and set of C language headers
-which conforms to the specifications of this Document.
-\index{production mode}
-\indexmacro{NDEBUG}
-\defsubsection{production mode}: A mode of operating the implementation
-in such a way that maximum efficiency of execution is achieved at the expense
-of the verification of constraints. An implementation shall provide
-a production mode, which is enabled in an implementation-defined
-manner.\footnote{An implementation may have to supply a separate set of
-libraries for production and for verification use, for instance. The
-manner of selecting libraries varies with each programming environment.} Each
-translation unit of the program which includes a Kazlib header shall ensure that the macro {\tt
-NDEBUG} is defined prior to the inclusion of that header, otherwise the
-implementation is not said to be operated in production mode.
-\index{verification mode}
-\defsubsection{verification mode}: A mode of operating the implementation in
-such a way that maximum error checking is obtained at the cost of
-execution efficiency. An implementation shall provide a verification mode, which
-is enabled in an implementation-defined manner. If any translation unit which
-includes a Kazlib header defines the macro name {\tt NDEBUG}\footnote{The
-intent is that the standard {\tt assert} macro may be exploited
-by the implementation's headers for the purpose of provisioning verification
-mode.} prior to including that header, the implementation is not said to be in
-verification mode. The least requirements of a Kazlib implementation operated
-in verification mode, is that it shall stop translation or execution of any
-program which violates a constraint.
-\index{undefined behavior}
-\defsubsection{undefined behavior}: Behavior of a program, upon violation of a
-requirement with respect to the use of Kazlib, or upon use of corrupt or
-incorrect data, for which this document does not impose any requirements.
-Additional undefined behaviors are:
-\begin{itemize}
-\item any behavior that is undefined by the C language standard;
-\item evaluation of an object whose contents are indeterminate;
-\item a violation of any explicit constraint stated in
-this document, if that program was built using Kazlib in production
-mode;\footnote{The intent is that violations of constraints are diagnosed by
-the implementation in verification mode, and hence do not lead to undefined
-behavior.}
-\item a violation of any requirement stated in this document that
-is not designated as a constraint, and is introduced using the word
-{\it shall}; and
-\item any other construct for which no definition of behavior can be deduced
-from this document.
-\end{itemize}
-If a program invokes undefined behavior of any kind, the Kazlib implementation
-is absolved from any requirements as to what events should ensue. The
-implementation may respond by invoking undefined behavior in the C language
-sense, or it may detect the behavior and terminate with a diagnostic message.
-\defsubsection{implementation-defined}: An adjective which, when appearing
-in the description of a feature, represents a requirement that the
-implementor must supply a definition, and document that
-definition. This adjective is applied to both behavior and to results.
-Implementation-defined behavior is behavior which depends on the
-characteristics of an implementation.\footnote{It is not considered adequate
-for the implementor to allow implementation-defined behavior to produce
-unpredictable effects or to terminate the program when such behavior is
-invoked.} When said of a result,
-implementation-defined means that a value is successfully computed, but depends
-on the characteristics of the implementation. It is possible for the presence of a
-requirement on a program to be described as implementation-defined, giving the
-implementor a choice whether to make that requirement or not. If a program
-violates a requirement whose presence is implementation-defined, that program's
-behavior is undefined in any implementation which elects to in fact impose that
-requirement.
-\index{implementation-defined}
-\defsubsection{unpredictable result}: A successfully computed value which is
-unreliable because some procedure or data failed to satisfy a property required
-by the computation.
-\defsubsection{constraint}: A semantic restriction with which a program must
-comply. Some sections of this Document contain paragraphs under the heading
-{\it Constraints\/} which list all constraints pertaining to the described
-feature. When operated in production mode, the Kazlib implementation
-is not required to diagnose constraint violations. When operated in
-verification mode, the Kazlib implementation must halt translation or
-execution of a program which violates a constraint.
-\index{constraint}
-\defsubsection{comparison function}: A function which accepts two arguments
-\index{comparison function}
-of type \verb|const void *| and returns a value of type int based on
-a ranking comparison of these arguments, and which satisfies the following
-additional semantic properties. If the two arguments are deemed to be equal, the
-function must return zero. If the first argument is determined to have a
-greater rank than the second, a positive value is returned. Otherwise if the
-first argument is determined to have a lesser rank than the second, a negative
-value is returned. The rank is computed as if each value has associated with it
-an integer, not necessarily unique, and as if these integers are compared for ordinary equality or
-inequality when values are said to be compared. The assignment of integers is
-up to the designer of the comparison function, and does not change between
-successive invocations of the function.\footnote{Of course, an actual
-comparison function need not assign actual integer ranks to data items, but it
-must behave as if such ranks were assigned.}
-If a comparison function is invoked in the context of an operation on some data
-structure, it shall not invoke any operation on any component of that same
-structure.\footnote{Thus, if a comparison function is invoked from, for
-instance, {\tt list_sort}, it must not call any list operations that
-inspect or modify the list being sorted, or any of its constituent nodes.}
-\defsubsection{opaque data type}: A data type whose precise definition is
-not documented, and which is intended to be manipulated only using the
-documented interface, which consists of a set of functions. Many data types in
-Kazlib are described as opaque. A program which bypasses the documented
-interfaces in inspecting or manipulating these data types invokes undefined
-behavior, and is not portable among Kazlib implementations.
-\defsubsection{user}: \index{user} The program which uses Kazlib.
-\defsubsection{user data}: \index{user data} Data provided by the program
-to which Kazlib stores a pointer, but otherwise does not inspect or modify.
-
-\section{Environment}
-\label{sec:environment}
-
-The translation and use of Kazlib requires a conforming, hosted implementation
-of the C language which meets the following additional minimal requirements:
-\begin{enumerate}
-\item The C implementation distinguishes external names by at least their
-initial 15 characters\footnote{The ISO 9899:1990 standard demands only that
-external names be distinguished by their initial six characters.}. External
-names that are distinct in their first 15 characters are treated by the
-implementation as distinct names. Upper and lower case letters in external
-identifiers need not be treated as distinct.
-\item The C implementation does not claim the identifier \verb|__cplusplus|
-for its internal use as a preprocessor symbol or keyword.
-\end{enumerate}
-If Kazlib headers are used by a C++ program, the C++ implementation
-meets these additional requirements:
-\begin{enumerate}
-\item the C++ implementation identifies itself by predefining the preprocessor
-symbol \verb|__cplusplus|;
-\item the C++ implementation is be capable of linkage against
-the C implementation with which the Kazlib source files units were translated.
-\end{enumerate}
-The Kazlib headers shall not make use of any names that are claimed
-by the C++ programming language, and shall ensure that the \verb|extern "C"|
-mechanism is used for all declarations when they are included into a C++
-translation unit, or otherwise provide compatibility with C++.\footnote{The
-intent is that the Kazlib implementation could, in principle, provide
-a separate set of headers for use with each language.}
-
-In programming environments that support the programming mechanism of multiple
-threads of execution an implementation of Kazlib may be designated as {\it
-thread safe}. To be called thread safe, it must guarantee that the use of an
-object by one thread cannot visibly interact or interfere with the concurrent
-or interleaved use of another object by another thread. If a Kazlib
-implementation that is not thread safe is provided for an environment which
-supports threads, it shall be accompanied by documentation which describes
-the extent of this limitation.
-
-A Kazlib implementation can also be designated as being {\it async safe}.
-The minimum requirement for this designation is that an operation on an object
-can be interrupted by delivery of an asynchronous signal and from within the
-catching function for that signal, it is safe to perform an operation on
-another object. An implementation shall document that it is async safe,
-or the extent to which it fails to be async safe.
-
-\section{General restrictions}
-
-\subsection{Headers}
-
-The Kazlib headers may be included in any order, and may be included more than
-once. Prior to the inclusion of a Kazlib header, the translation unit shall not
-define any macro name that has the same spelling as a C language keyword. The
-Kazlib headers may behave as though they include arbitrary standard C headers,
-so any requirements related to the inclusion of standard headers apply to
-Kazlib headers. A header shall be included before the first reference to any
-of the functions, types or macros that it defines.
-
-If one or more preprocessor symbols whose names begin with the sequence
-\verb|KAZLIB_| are defined prior to the inclusion of a Kazlib header,
-the behavior is implementation-defined.
-
-\subsection{Reserved macros}
-
-A Kazlib header defines all of the macros explicitly listed in the section of
-this document that defines the contents of that header. It may also define
-additional macros that belong to the macro namespace reserved by that header.
-The translation unit that includes the header shall not \verb|#define| or
-\verb|#undef| any of these macros.
-
-A header may define function-like macros that supplement existing functions,
-provided that such macros do not cause multiple evaluation of arguments except
-as explicitly permitted, and are safe to use wherever the corresponding
-function call would be. These function-like macros may be subject to
-\verb|#undef|.\footnote{In principle, an implementation may provide, within the
-reserved namespaces, additional functions not specified in this document, and
-function-like macro equivalents of these functions. A program that uses such
-identifiers in a block or function scope should use {\tt \#undef} on these
-identifiers prior to their use.}
-
-\subsection{Reserved symbols}
-
-Each Kazlib header provides file scope declarations for the typedef names,
-struct tags, enum constants and function names listed in its corresponding
-section in this document. Moreover, each header may define additional such
-names that fall into the documented reserved namespaces.
-
-The behavior is undefined if a translation unit that includes a Kazlib header
-defines any identifier that is the same as an identifier reserved by the header
-in the same scope and namespace.\footnote{Therefore, it is permitted to redeclare
-or redefine the identifiers reserved by a previously included Kazlib header,
-provided that the declarations or definitions are in a different namespace or
-scope. Reserved names may be redeclared in a block scope, or used as
-statement labels which have function scope and are in their own namespace.}
-
-The behavior is also undefined if the program contains a definition of an
-object or function with external linkage whose name matches an external object
-of unction defined by Kazlib component that is used as part of the
-program, or whose name is in a namespace reserved by that
-component.\footnote{This restriction exists whether or not the corresponding Kazlib
-header is included.}
-
-Lastly, the behavior is undefined if a translation unit defines a macro whose
-name is in the space of reserved symbols of a Kazlib header that is included in
-that translation unit.
-
-\subsection{Argument aliasing}
-
-Kazlib provides functions that operate on objects of various types. Pointers
-to objects are passed to these functions, thereby giving rise to the
-possibility of {\it aliasing}---passing of objects that wholly or partially
-overlap. The program shall not present aliased objects to any Kazlib function.
-Objects of distinct types shall not be aliased in a function call under any
-circumstances.
-The aliasing of two or more objects of compatible type is permitted only as
-explicitly documented in the description of a function; in all such
-circumstances, only exact overlapping is permitted.\footnote{That is to say,
-where explicitly allowed, a pointer to the same object may be specified for two
-(or more) parameters of like type.}
-
-\subsection{Object initialization}
-
-The Kazlib opaque data types can only be initialized with the initialization
-functions provided by the Kazlib library, or by implementation-defined
-initialization functions.\footnote{Of course, the use of implementation-defined
-functions results in programs that are not portable among library
-implementations.} An opaque object that is initialized by a method other than
-by being passed to an appropriate initialization function, or that is not
-initialized at all, has indeterminate contents. A pointer to an object having
-indeterminate contents may be passed to an initialization function; the object
-then has well-determined contents.
-
-An object whose initialization function is capable of indicating failure is
-considered indeterminate if the attempt to initialize that object using that
-function does in fact fail. The program shall not attempt to deinitialize such
-an object. The implementation shall reclaim any resources that were allocated
-for an object whose initialization failed. This reclamation need
-not be immediate, but may be delayed; however, the delay shall not
-give rise to the possibility of resource leaks in any correct program.
-
-Those objects for which deinitialization operations are defined should be
-subject to these operations when these objects are no longer needed. Failure
-to apply the deinitialization functions may result in the leakage of resources.
-
-\subsection{Object copying}
-
-Certain data types may be sensitive to their own location in memory. This
-means that copying their values by assignment or \verb|memcpy| results in the
-copy having an indeterminate value which cannot be used. All opaque types in
-Kazlib are assumed to have this property; copying the value of an opaquely
-typed object to another suitably typed object causes the destination
-object to have indeterminate contents.
-
-\section{List component}
-
-The List component provides a set of functions, macros and type declarations
-which together provide a library for maintaining a possibly empty ordered set
-of elements, called a {\it list}. This list has the following properties:
-\index{List}\begin{enumerate}
-\item If the list is not empty, a first and last element can be identified.
- In a list having only one element, that one element is both the first and
- last element.
-\item Each element that is not the last element has another element as its
- {\it successor}.
- \index{successor!of a list element}
- \index{List!successor of an element}
-\item Each element that is not the first element has a {\it
- predecessor}.
- \index{predecessor!of a list element}
- \index{List!predecessor of an element}
-\item No element is the predecessor or successor of more than one element.
-\item If one element is the successor of another, the other is necessarily the
- predecessor of the first.
-\item Each element is associated with arbitrary {\it satellite\/} data.
-\end{enumerate}
-The {\it size} of a list, also known as the {\it list count}, is simply the
-number of elements contained in it.\index{size!of a list}\index{List!count}
-
-A list imposes a maximum value on the number of nodes that may be in it
-simultaneously. This is known as the list's {\it capacity}. A list that
-has the maximum number of nodes is said to be full.
-
-\subsection{Interface}
-
-\subsubsection{The {\tt list.h} header}
-
-Each C or C++ translation unit that is to use the functionality of
-the List component shall include the header \verb|list.h|. This header
-shall contain declarations of types and external functions, and definitions of
-macros.
-The following typedef names shall be defined:\index{List!typedef names}
-\index{typedefs!defined by List}
-\begin{verbatim}
- list_t listcount_t
- lnode_t lnodepool_t
-\end{verbatim}
-In addition, the following structure tags may be defined:\index{List!tag names}
-\index{tags!defined by List}
-\begin{verbatim}
- struct list_t
- struct lnode_t
- struct lnodepool_t
-\end{verbatim}
-The following external function names shall be declared:
-\index{List!function names}\index{functions!defined by List}
-\begin{verbatim}
- list_append list_prev
- list_contains list_process
- list_count list_return_nodes
- list_create list_sort
- list_del_first list_find
- list_del_last list_transfer
- list_delete list_verify
- list_destroy lnode_borrow
- list_destroy_nodes lnode_create
- list_extract lnode_destroy
- list_first lnode_get
- list_init lnode_init
- list_ins_after lnode_is_in_a_list
- list_ins_before lnode_pool_create
- list_is_sorted lnode_pool_destroy
- list_isempty lnode_pool_init
- list_isfull lnode_pool_isempty
- list_last lnode_pool_isfrom
- list_merge lnode_put
- list_next lnode_return
- list_prepend
-\end{verbatim}
-The following preprocessor symbols (macros) shall be defined:
-\index{List!macro names}\index{macros!defined by List}
-\indexmacro{LISTCOUNT_T_MAX}
-\indexmacro{LIST_H}
-\begin{verbatim}
- LISTCOUNT_T_MAX
- LIST_H\end{verbatim}
-\index{symbols!reserved by List}\index{List!reserved symbols}
-Macro identifiers which begin with the upper-case prefix \verb|LIST| are
-reserved for future extensions to the \verb|list.h| header, as are
-names in the ordinary and tag namespaces which begin with
-\verb|list_| or \verb|lnode_|. External names which begin with \verb|list_| or
-\verb|lnode_| are reserved by the Kazlib library regardless of what header
-files are included.
-
-\subsubsection{The {\tt list_t} type}
-
-\indextype{list_t}
-The type \verb|list_t| is an opaque data type which maintains information about the
-current state of a single list. A list consists of an instance of the
-\verb|list_t| type, plus zero or more instances of the type \verb|lnode_t|. An
-instance of the \verb|list_t| type can be dynamically created using the
-\verb|list_create| function, and destroyed by the \verb|list_destroy| function.
-Alternately, the program can declare an object of type \verb|list_t| and have
-it initialized via the \verb|list_init| function.
-
-\subsubsection{The {\tt listcount_t} type}
-
-\indextype{listcount_t}
-\indexmacro{LISTCOUNT_T_MAX}
-The type \verb|listcount_t| is an unsigned integral type which represents
-the number of nodes in a list. The specific choice of unsigned integral type
-is implementation defined. The \verb|LISTCOUNT_T_MAX| macro expands to a
-constant expression of type \verb|listcount_t| which specifies the maximum
-value of that type.\footnote{For example, if the implementation defines
-{\tt listcount_t} as an alias for the type unsigned long, then
-{\tt LISTCOUNT_T_MAX} must have the same value as {\tt ULONG_MAX}.}
-
-\subsubsection{The {\tt lnode_t} type}
-
-\indextype{lnode_t}
-The type \verb|lnode_t| is an opaque type that represents a single node of a
-list. A node contains a a reference to satellite data provided by the user,
-and also stores the key that is associated with the node when it is inserted.
-Nodes may be dynamically created by the \verb|lnode_create| function.
-Alternately, the program may supply an \verb|lnode_t| object that can be
-initialized by the \verb|lnode_init| function.
-
-\subsubsection{The {\tt lnodepool_t} type}
-
-\indextype{lnodepool_t}
-The \verb|lnodepool_t| type provides an alternate method for supplying list
-nodes to the application. A user-supplied or dynamically allocated fixed size
-array of nodes is converted into a a {\it pool\/} of nodes from which free
-nodes may be obtained and to which they may be returned. A user-supplied node
-pool is created by the function \verb|lnode_pool_init| which requires a pointer
-to an object of type \verb|lnode_pool_t|, a pointer to the first element of an
-array of \verb|lnode_t| objects, as well as an integer representing the size of
-the array. Alternately, the function \verb|lnode_pool_create| will dynamically
-allocate an object of type \verb|lnode_pool_t| containing the specified number
-of list nodes.
-
-\subsubsection{The {\tt list_append} function}
-
- \indexfunc{list_append}
- \index{List!appending a node}
- \index{append node to list}
- \synopsis
- \begin{verbatim}
- void list_append(list_t *, lnode_t *);\end{verbatim}
-
- \constraints
- The second argument shall not refer to a node that is already in a list
- or in a list node pool. The first argument shall not refer to a list
- that is full.
-
- \description
- The append operation causes the node pointed at by the second
- argument to become the last node in the list pointed at by the first
- argument.\footnote{That is to say, after the operation, the
- {\tt list_last} function, when applied to the list, shall return a pointer
- to that node.}
-
- If the first argument is an expression with side effects, the behavior
- is undefined.\footnote{Thus, the implementation may provide a macro
- version of {\tt list_append} which evaluates the first argument
- more than once.}
- \index{macros!and side effects}
-
-\subsubsection{The {\tt list_contains} function}
-
- \indexfunc{list_contains}
- \index{List!testing for presence of node}
- \nobreak
- \synopsis
- \begin{verbatim}
- int list_contains(list_t *, lnode_t *node);\end{verbatim}
- \nobreak
- \description
- \nobreak
- The \verb|list_contains| function shall return 1 if the node
- pointed at by the second argument is in the list pointed at by the first
- argument. Otherwise, it shall return 0.
-
-\subsubsection{The {\tt list_count} function}
-
- \indexfunc{list_count}
- \index{List!count}
- \index{List!size}
- \synopsis
- \begin{verbatim}
- listcount_t list_count(list_t *);\end{verbatim}
-
- \description
-
- The \verb|list_count| function returns a value which represents the number
- of nodes currently stored in the list pointed at by the argument.
-
-\subsubsection{The {\tt list_create} function}
-
- \indexfunc{list_create}
- \index{List!creation of}
- \index{create!list object}
- \synopsis
- \begin{verbatim}
- list_t *list_create(listcount_t);\end{verbatim}
-
- \description
- The \verb|list_create| function instantiates and initializes an object of
- type \verb|list_t|, and returns a pointer to it unless insufficient
- resources exist for the creation of the object, in which case a null
- pointer is returned.
-
- The value of the function's argument establishes, for the entire duration
- of the list object, its capacity.
-
- The newly created list object is empty.
-
-\subsubsection{The {\tt list_del_first} function}
-
- \index{List!first node}
- \indexfunc{list_del_first}
- \index{List!deletion}
- \index{delete!first node of a list}
- \synopsis
- \begin{verbatim}
- lnode_t *list_del_first(list_t *);\end{verbatim}
-
- \constraints
- The argument shall not point to an empty list.
-
- \description
- The \verb|list_del_first| function removes the first node from the
- list pointed at by the argument and returns a pointer to that
- node.
-
- If the argument is an expression with side effects, the behavior is
- undefined.\index{macros!and side effects}
-
-\subsubsection{The {\tt list_del_last} function}
-
- \index{List!last node}
- \indexfunc{list_del_last}
- \index{List!deletion}
- \index{delete!last node of a list}
- \synopsis
- \begin{verbatim}
- lnode_t *list_del_last(list_t *);\end{verbatim}
-
- \constraints
- The argument shall not point to an empty list.
-
- \description
- The \verb|list_del_last| function removes the last node from the list
- specified by the argument, and returns a pointer to that node. If,
- prior to the operation, that node had a predecessor, that predecessor
- shall become the new last node of the list. Otherwise, the list
- shall become empty.
-
- The new value of the list count shall be one less than its value
- prior to the call to this function.
-
- If the argument is an expression with side effects, the behavior is
- undefined.\index{macros!and side effects}
-
-\subsubsection{The {\tt list_delete} function}
-
- \indexfunc{list_delete}
- \index{List!deletion}
- \index{delete!arbitrary node of a list}
- \synopsis
- \begin{verbatim}
- lnode_t *list_delete(list_t *, lnode_t *);\end{verbatim}
-
- \constraints
- The second argument shall point to a node that is inside the list
- pointed at by the first argument.
-
- \description
- The \verb|list_delete| function removes the node pointed at by its
- second argument from the list pointed at by its first argument.
- A pointer to the deleted node is returned.
-
-\subsubsection{The {\tt list_destroy} function}
-
- \indexfunc{list_destroy}
- \index{List!destruction of}
- \synopsis
- \begin{verbatim}
- void list_destroy(list_t *);\end{verbatim}
-
- \constraints
- The argument shall point to an empty list.
-
- \description
- The empty list pointed at by the argument is destroyed. If the list has
- not been created by a call to the \verb|list_create| function, the
- behavior is undefined.
-
- A pointer that previously referred to a list that has been disposed by
- \verb|list_destroy| has an indeterminate value.
-
-\subsubsection{The {\tt list_destroy_nodes} function}
-
- \indexfunc{list_destroy_nodes}
- \synopsis
- \begin{verbatim}
- void list_destroy_nodes(list_t *);\end{verbatim}
-
- \description
- The nodes, if any, contained in the list pointed at by the argument are
- disposed of as if by a call to the \verb|lnode_destroy| function. If any
- node contained in the list was created by means other than the
- \verb|lnode_create| function, the behavior is undefined.
-
- After the operation, the list is empty.
-
- Any pointer that referred to any of the destroyed nodes takes on an
- indeterminate value.
-
-\subsubsection{The {\tt list_extract} function}
-
- \index{List!node range extraction}
- \indexfunc{list_extract}
- \synopsis
- \begin{verbatim}
- void list_extract(list_t *, list_t *, lnode_t *, lnode_t *);\end{verbatim}
-
- \constraints
- The second argument points to the {\it source list}. The third
- argument is either null, or points to a node that is an occupant
- of the source list. This node is called the {\it starting node}.
- The fourth argument is either null, or points to a node that is
- an occupant of the source list. This node is called the {\it ending
- node}. If the starting node and ending node are both specified, and are
- distinct nodes, then the starting node shall appear earlier in the source
- list than the ending node.
-
- The transfer request shall not call for the capacity of the destination
- list to be exceeded.
-
- \description
- The \verb|list_extract| function moves nodes from the source
- list to the {\it destination list\/} pointed at by the first
- argument.\footnote{This right-to-left direction of transfer is consistent
- with the semantics of standard C library functions such as {\tt memmove} or
- {\tt strcpy}.}
-
- If the third and fourth arguments are not null, the entire range of nodes
- from the starting node and to the ending node, inclusive, is transferred
- from the source list to the end of the destination list, where they appear
- in their original order. Other nodes in the source list, if any, are
- unaffected.
-
- If the third and fourth arguments both point to the same node, that
- node alone is transferred to the end of the destination list.
-
- If either the third argument or the fourth argument is null, or both are null,
- no transfer of nodes takes place.
-
- The source and destination list may be the same object.
-
-\subsubsection{The {\tt list_first} function}
-
- \index{List!first node}
- \indexfunc{list_first}
- \synopsis
- \begin{verbatim}
- lnode_t *list_first(list_t *);\end{verbatim}
-
- \description
- If the list pointed at by the argument is an empty list, a null pointer
- is returned. Otherwise, a pointer to the first node in that list is
- returned.
-
- If the argument is an expression with side effects, the behavior is
- undefined.\index{macros!and side effects}
-
-\subsubsection{The {\tt list_init} function}
-
- \indexfunc{list_init}
- \synopsis
- \begin{verbatim}
- list_t *list_init(list_t *, listcount_t);\end{verbatim}
-
- \constraints
- The second argument shall not have a zero value.
-
- \description
- The \verb|list_init| function initializes the list object pointed at by the
- first argument, turning it into a valid, empty list. If the object is an
- already initialized list, the behavior is undefined. A list returned by
- \verb|list_create| is considered initialized. The second argument
- specifies the maximum number of nodes that may simultaneously occupy the
- list.
-
- The value returned is that of the first argument.
-
-\subsubsection{The {\tt list_ins_after} function}
-
- \indexfunc{list_ins_after}
- \index{insert!node into list}
- \index{List!insertion}
- \synopsis
- \begin{verbatim}
- void list_ins_after(list_t *, lnode_t *, lnode_t *);\end{verbatim}
-
- \constraints
- The first argument shall point to a list that is not already full. The
- second argument shall point to a node, called the {\it new node}, that is not
- already an occupant of the list pointed at by the first argument, nor
- of any other list or node pool object. The third
- argument shall point to a node, called the {\it reference node}, that is an
- occupant of the list.
-
- \description
- The new node becomes an occupant of the list, such that its predecessor
- is the reference node. If the reference node has a successor, the
- new node is inserted between the reference node and that successor.
- Otherwise, the new node becomes the last node of the list.
-
-\subsubsection{The {\tt list_ins_before} function}
-
- \indexfunc{list_ins_before}
- \index{insert!node into list}
- \index{List!insertion}
- \synopsis
- \begin{verbatim}
- void list_ins_before(list_t *, lnode_t *, lnode_t *);\end{verbatim}
-
- \constraints
- The first argument shall point to a list that is not already full. The
- second argument shall point to a node, called the {\it new node}, that is not
- already an occupant of the list pointed at by the first argument, nor
- of any other list or node pool object. The third
- argument shall point to a node, called the {\it reference node}, that is an
- occupant of the list.
-
- \description
- The new node becomes an occupant of the list, such that its successor
- is the reference node. If the reference node has a predecessor, the
- new node is inserted between the reference node and that predecessor.
- Otherwise, the new node becomes the first node of the list.
-
-\subsubsection{The {\tt list_is_sorted} function}
-\label{list:is:sorted}
- \indexfunc{list_is_sorted}
-
- \synopsis
- \begin{verbatim}
- int list_is_sorted(list_t *,
- int (const void *, const void *));\end{verbatim}
-
- \description
- The first argument points to a list object. The second is assumed to
- point to a comparison function.
-
- If the list has exactly one node or is empty, $1$ is returned
- unconditionally. Otherwise, nodes of the list are examined to
- determine whether they are in a sorted order according to the comparison
- function. This is true if the integer ranks of their data items,
- examined from the first node of the list through to the last node, form a
- monotonically increasing sequence. If the nodes are in order, the value $1$
- is returned. Otherwise $0$ is returned.
-
- If the list has two or more nodes, and the second argument is a pointer to
- a function that has the correct type, but does not satisfy the semantic
- properties of a comparison function, the result is unpredictable, but is
- guaranteed to be one of the values~$0$~or~$1$.
-
-\subsubsection{The {\tt list_isempty} function}
-
- \indexfunc{list_isempty}
- \synopsis
- \begin{verbatim}
- int list_isempty(list_t *);\end{verbatim}
-
- \description
- The \verb|list_isempty| function returns $1$ if the list pointed at by
- the first argument is empty. Otherwise it returns $0$.
-
-\subsubsection{The {\tt list_isfull} function}
-
- \indexfunc{list_isfull}
- \synopsis
- \begin{verbatim}
- int list_isfull(list_t *);\end{verbatim}
-
- \description
- The \verb|list_isfull| function returns $1$ if the list pointed at by
- the first argument is full. Otherwise it returns $0$.
- A list is considered full when it contains the maximum number of nodes
- that was specified upon its initialization.
-
- If the argument is an expression with side effects, the behavior is
- undefined.\index{macros!and side effects}
-
-\subsubsection{The {\tt list_last} function}
-
- \index{List!last node}
- \indexfunc{list_last}
- \synopsis
- \begin{verbatim}
- lnode_t *list_last(list_t *);\end{verbatim}
-
- \description
- If the list pointed at by its first argument is empty, the \verb|list_last|
- function returns a null pointer. Otherwise it returns a pointer to the
- last node.
-
- If the argument is an expression with side effects, the behavior is
- undefined.\index{macros!and side effects}
-
-\subsubsection{The {\tt list_merge} function}
-
- \index{List!merge operation}
- \indexfunc{list_merge}
- \synopsis
- \begin{verbatim}
- void list_merge(list_t *, list_t *,
- int (const void *, const void *));\end{verbatim}
-
- \constraints
- The list pointed at by the first argument is called the {\it destination
- list}. The second argument points to the {\it source list}. The third
- argument points to a comparison function. The sum of the number of nodes
- occupying the source list and the destination list shall not exceed the
- maximum number of nodes that are permitted to occupy the destination list.
- Furthermore, both the source and destination list shall be sorted such that
- a call to \verb|list_is_sorted| given a pointer to either list as a first
- argument, and the pointer to the comparison function as its second
- argument, shall yield the value $1$.
-
- \description
- Nodes from the sorted source list are merged into the sorted destination
- list. After the operation, the source list is empty and the destination
- list contains all of the nodes it contained prior to the operation, as well
- as all of the nodes that the source list contained. The nodes are in sorted
- order according to the comparison function.
-
- If the third argument is a pointer to a function that has the correct type,
- but does not fulfill the semantic properties of a comparison function, the
- order of the nodes in the destination list is unpredictable.
-
- If the source and destination list are the same object, the
- \verb|list_merge| operation has no effect.
-
-\subsubsection{The {\tt list_next} function}
-
- \indexfunc{list_next}
- \synopsis
- \begin{verbatim}
- lnode_t *list_next(list_t *, lnode_t *);\end{verbatim}
-
- \constraints
- The node pointed at by the second argument is an occupant of the list pointed
- at by the first argument.
-
- \description
- If the node pointed at by the second argument has a successor, a pointer to
- that successor is returned. Otherwise, a null pointer is returned.
-
- If the second argument is an expression which has side effects, the behavior
- is undefined.\index{macros!and side effects}
-
-\subsubsection{The {\tt list_prepend} function}
-
- \indexfunc{list_prepend}
- \index{List!prepending a node}
- \index{prepend node to list}
- \synopsis
- \begin{verbatim}
- void list_prepend(list_t *, lnode_t *);\end{verbatim}
-
- \constraints
- The second argument shall not refer to a node that is already in a list
- or in a list node pool. The first argument shall not refer to a list
- that is full.
-
- \description
- The prepend operation causes the node pointed at by the second
- argument to become the first node in the list pointed at by the first
- argument. After the operation, the \verb|list_first| function, when
- applied to the list, shall return a pointer to that node.
- If, prior to to the operation, the list is empty, then the prepended node
- shall become the first node in that list, otherwise, the prepended node
- becomes the predecessor of what was previously the first node.
-
- If the first argument is an expression with side effects, the behavior
- is undefined.\index{macros!and side effects}
-
-\subsubsection{The {\tt list_prev} function}
-
- \indexfunc{list_prev}
- \synopsis
- \begin{verbatim}
- lnode_t *list_prev(list_t *, lnode_t *);\end{verbatim}
-
- \constraints
- The node pointed at by the second argument is an occupant of the list pointed
- at by the first argument.
-
- \description
- If the node pointed at by the second argument has a predecessor, a pointer to
- that predecessor is returned. Otherwise, a null pointer is returned.
-
- If the second argument is an expression which has side effects, the behavior
- \index{macros!and side effects}
- is undefined.
-
-\subsubsection{The {\tt list_process} function}
-
- \indexfunc{list_process}
- \synopsis
- \begin{verbatim}
- void list_process(list_t *, void *,
- void (*)(list_t *, lnode_t *, void *));\end{verbatim}
- \nobreak
- \description
- The \verb|list_process| function iterates over the nodes of a list,
- and for each node invokes a callback function.\footnote{In most cases,
- it is more convenient and preferable to
- iterate over the list using explicit calls to {\tt list_first}
- and {\tt list_next}.}
- The second argument is a {\it context pointer\/} which can have any value.
- The third argument of
- \verb|list_process| shall be a pointer to a function which is compatible
- with the specified type. If the list contains one or more nodes,
- then the function is invoked once for each node, in order from first
- to last. On each invocation, the first argument of the callback is a
- pointer to the list; the second argument is a pointer to a node, called
- the {\it subject node}; and the third argument repeats the context pointer
- value that was originally passed to \verb|list_process|.
-
- The callback function may delete the subject node by, for instance, calling
- \verb|list_delete|. It may insert new nodes to any place in the list;
- however, if such an insertion causes the subject node to acquire
- a new successor, it is implementation-defined whether upon returning
- from the callback function, the traversal shall continue with the
- new successor, or with the original successor.
-
- The callback function, and any function invoked from the callback
- function, shall not destroy the list or make any modifications
- other than the insertion of new nodes, or the deletion of the
- subject node.
-
- The callback function may recursively invoke \verb|list_process| for the
- same list or for a different list; the callback invocations arising out of
- the nested call inherit all of the restrictions of the outer callback in
- addition to being subject to the usual restrictions.\footnote{This means,
- for instance, that if two callbacks are in progress for different
- subject nodes from the same list, the inner callback may not delete
- its subject node, because it inherits the restriction that the only
- permitted deletion is the outer callback's subject node.}
-
- The callback function may freely operate on a different list,
- subject to any inherited restrictions.
-
-\subsubsection{The {\tt list_return_nodes} function}
-
- \indexfunc{list_return_nodes}
- \synopsis
- \begin{verbatim}
- void list_return_nodes(list_t *, lnodepool_t *);\end{verbatim}
-
- \description
-
- Every node in the list specified by the first argument
- is returned to the node pool specified by the second argument
- If the list contains a node that has not been allocated
- from that node pool, the behavior is undefined.
-
-\subsubsection{The {\tt list_sort} function}
-
- \index{List!sort operation}
- \indexfunc{list_sort}
- \synopsis
- \begin{verbatim}
- void list_sort(list_t *, int (const void *, const void *));\end{verbatim}
-
- \description
-
- The \verb|list_sort| function changes the order of the nodes of the list
- specified by the first argument according to the comparison function
- pointed at by the second argument.
-
- If the list is empty, or contains only one node, the comparison function is
- not called.
-
- Whenever the comparison function is invoked, its arguments are are the data
- pointers stored in two distinct nodes of the list.
-
-\subsubsection{The {\tt list_find} function}
-
- \index{List!find operation}
- \indexfunc{list_find}
- \synopsis
- \begin{verbatim}
- lnode_t *list_find(list_t *,
- const void *, int (const void *, const void *));\end{verbatim}
-
- \description
-
- The \verb|list_find| function exhaustively searches the key for a node
- whose satellite data matches a search key according to the comparison
- function. The first argument is the list to be searched, the second
- argument specifies the search key and the third argument is a pointer
- to the comparison function.
-
- The comparison function is invoked to compare the key against the
- satellite data of successive nodes of the list, starting with the first
- node. A pointer to the first node for which the comparison function returns
- zero is returned.
-
- If the list is empty, or the comparison function returns non-zero for
- each item, a null pointer is returned.
-
-\subsubsection{The {\tt list_transfer} function}
-
- \index{List!node transfer}
- \indexfunc{list_transfer}
- \synopsis
- \begin{verbatim}
- void list_transfer(list_t *, list_t *, lnode_t *);\end{verbatim}
-
- \constraints
- The third argument is either null, or it points at a node which is an
- occupant of the list pointed at by the second argument.
-
- The transfer request shall not call for the capacity of the destination
- list to be exceeded.
-
- \description
- The \verb|list_transfer| function moves nodes from the list
- pointed at by the second argument to the list pointed at by
- the first argument.
-
- If the third argument is not null, it specifies the node in the source list
- at which the transfer begins. That node, its successor, and all
- subsequent nodes, are transferred to the end of the destination list where
- they appear in their original order. Other nodes in the source list are
- unaffected.
-
- If the third argument is null, no transfer of nodes takes place.
-
- The source and destination list may be the same object.
-
- If \verb|DL|, \verb|SL| and \verb|SN| are appropriately typed expressions,
- the function call
-
-\begin{verbatim}
- void list_transfer(DL, SL, SN);
-\end{verbatim}
- is equivalent to
-\begin{verbatim}
- list_extract(DL, SL, SN, list_last(SL));
-\end{verbatim}
- except that \verb|SL| is evaluated only once.
-
-\subsubsection{The {\tt list_verify} function}
-
- \indexfunc{list_verify}
- \synopsis
- \begin{verbatim}
- int list_verify(list_t *list);\end{verbatim}
-
- \description
- The intent of the \verb|list_verify| function is to perform a verification
- on the list object, regardless of whether the Kazlib implementation is
- operated in verification or production mode. If the list objects
- and its constituent nodes have been correctly manipulated, and the
- program has not caused any undefined behaviors, the value $1$ is returned.
- Otherwise, the function may be able to, but is not guaranteed to, detect
- corruption, and return the value zero.
-
-\subsubsection{The {\tt lnode_borrow} function}
-
- \indexfunc{lnode_borrow}
- \synopsis
- \begin{verbatim}
- lnode_t *lnode_borrow(lnodepool_t *, void *);\end{verbatim}
-
- \description
-
- The \verb|lnode_borrow| function allocates a node from
- the pool managed by the given \verb|lnodepool_t| object.
- If the request succeeds, a pointer to the node is returned. If the object
- has run out of nodes, the return value is a null pointer.
-
-\subsubsection{The {\tt lnode_create} function}
-
- \indexfunc{lnode_create}
- \synopsis
- \begin{verbatim}
- lnode_t *lnode_create(void *);\end{verbatim}
-
- \description
-
- The \verb|lnode_create| function dynamically allocates a list node,
- stores in it the data value specified in the argument and
- returns a pointer to it. The allocation is performed by a call to the
- standard \verb|malloc| function. If the allocation fails, a null
- pointer is returned.
-
-\subsubsection{The {\tt lnode_destroy} function}
-
- \indexfunc{lnode_destroy}
- \synopsis
- \begin{verbatim}
- void lnode_destroy(lnode_t *);\end{verbatim}
-
- \description
-
- The \verb|lnode_destroy| function destroys a list node that has been
- allocated with the \verb|lnode_create| function. The value of any pointer
- that referred to the node that was thus freed is indeterminate.
-
- If the node is currently the occupant of a list, the behavior is undefined
- if the list is subsequently used.
-
-\subsubsection{The {\tt lnode_get} function}
-
- \indexfunc{lnode_get}
- \synopsis
- \begin{verbatim}
- void *lnode_get(lnode_t *);\end{verbatim}
-
- \description
-
- The \verb|lnode_get| function retrieves the \verb|void *| data value
- associated with a node.\footnote{This is the {\bf only} interface for
- retrieving the data element.}
-
-\subsubsection{The {\tt lnode_init} function}
-
- \indexfunc{lnode_init}
- \synopsis
- \begin{verbatim}
- lnode_t *lnode_init(lnode_t *, void *);\end{verbatim}
-
- The \verb|lnode_init| function initializes the contents
- of the specified list node object, assigning it the
- data value specified as the second argument.
- The first argument is a pointer which refers to
- a data object that has a suitable size and alignment
- for the representation of an \verb|lnode_t| type.
- After initialization with \verb|lnode_init|, the object is subsequently
- eligible as an operand to the functions of the List component.
-
-\subsubsection{The {\tt lnode_is_in_a_list} function}
-
- \indexfunc{lnode_is_in_a_list}
- \synopsis
- \begin{verbatim}
- int lnode_is_in_a_list(lnode_t *);\end{verbatim}
-
- \description
-
- The \verb|lnode_is_in_a_list| function determines whether the given node is
- an occupant of some list. If the node is in a list, the function returns
- the value $1$. If the node is not in any list, the return value is zero.
-
-\subsubsection{The {\tt lnode_pool_create} function}
-
- \indexfunc{lnode_pool_create}
- \synopsis
- \begin{verbatim}
- lnodepool_t *lnode_pool_create(listcount_t);\end{verbatim}
-
- \constraints
-
- The value of the argument shall not be zero.
-
- \description
-
- The \verb|lnode_pool_create| function dynamically allocates,
- by means of the standard library function \verb|malloc|
- a node pool object containing the number of nodes specified
- as the first argument. If not enough resources are available,
- a null pointer is returned, otherwise a pointer to the
- \verb|lnodepool_t| object is returned.
-
-\subsubsection{The {\tt lnode_pool_destroy} function}
-
- \indexfunc{lnode_pool_destroy}
- \synopsis
- \begin{verbatim}
- void lnode_pool_destroy(lnodepool_t *);\end{verbatim}
-
- \description
-
- The \verb|lnode_pool_destroy| function deallocates a
- node pool that was allocated by \verb|lnode_pool_create|.
- The value of any pointer which referred to the
- node pool object becomes indeterminate.
-
-\subsubsection{The {\tt lnode_pool_init} function}
-
- \indexfunc{lnode_pool_init}
- \synopsis
- \begin{verbatim}
- lnodepool_t *lnode_pool_init(lnodepool_t *,
- lnode_t *, listcount_t);\end{verbatim}
-
- \constraints
-
- The third argument, which specifies the node count, shall not be zero.
-
- \description
-
- The \verb|lnode_pool_init| function initializes a data object
- that has a suitable size and alignment to represent an
- \verb|lnodepool_t| type. A pointer to this object is passed
- as the first argument. The node pool thus created draws nodes
- from an array specified by the second argument, which shall be a pointer to
- an object that can behave like an array of \verb|lnode_t| objects.
- The third argument specifies the number of elements in this array.
-
- After this function, the object pointed at by the \verb|lnodepool_t *|
- argument is eligible for use with the node pool management functions
- of the List component. Nodes may be drawn from the pool and returned to it.
-
- As long as the pool continues to be used, the program should not directly
- manipulate the node array. In particular, if the program modifies any
- part of the array, then the behavior is undefined if the
- \verb|lnodepool_t| object or any nodes drawn from it are subsequently
- passed to a List function. The program shall not directly use the array
- elements as independent \verb|lnode_t| objects while the array is
- associated with the pool; in particular, it shall not pass these elements
- to Kazlib functions that operate on \verb|lnode_t|.
-
- The behavior is undefined if the same array is associated with more than
- one node pool object, or if two node pool objects are given overlapping
- arrays.
-
- The node array is managed in an manner that is specific to the
- implementation; the intent is that each element of the array represents a
- distinct node object, a pointer to which can be returned in response to an
- allocation request.
-
- The \verb|lnode_pool_init| function returns a copy of the first argument.
-
-\subsubsection{The {\tt lnode_pool_isempty} function}
-
- \indexfunc{lnode_pool_isempty}
- \synopsis
- \begin{verbatim}
- int lnode_pool_isempty(lnodepool_t *);\end{verbatim}
-
- \description
-
- The \verb|lnode_pool_isempty| function tests the
- specified \verb|lnodepool_t| object for ability to supply nodes.
- If the object has been
- subject to so many requests that it is no longer capable of
- of supplying additional list nodes, the value $1$ is returned.
- Otherwise the return value returned is zero.
-
-\subsubsection{The {\tt lnode_pool_isfrom} function}
-
- \indexfunc{lnode_pool_isfrom}
- \synopsis
- \begin{verbatim}
- int lnode_pool_isfrom(lnodepool_t *, lnode_t *);\end{verbatim}
-
- \description
-
- The function \verb|lnode_pool_isfrom|, intended to serve as a software
- verification aid, determines whether a list node originates from
- a particular node pool. The return value is $1$ if this relationship is
- true, otherwise zero.
-
-\subsubsection{The {\tt lnode_put} function}
-
- \indexfunc{lnode_put}
- \synopsis
- \begin{verbatim}
- void lnode_put(lnode_t *, void *);\end{verbatim}
-
- \description
-
- The function \verb|lnode_put| replaces the data element
- associated with the list node.
-
-\subsubsection{The {\tt lnode_return} function}
-
- \indexfunc{lnode_return}
- \synopsis
- \begin{verbatim}
- void lnode_return(lnodepool_t *, lnode_t *);\end{verbatim}
-
- \constraints
-
- The node pointed at by the second argument was derived by an allocation
- request from the pool pointed at by the first argument.\footnote{In
- other words, the {\tt lnode_pool_isfrom} function, were it called with
- the same two arguments, would return $1$ if this constraint is met.}
-
- Furthermore, the node must not be the occupant of a list.
-
- \description
-
- The \verb|lnode_return| function returns a node back to the node pool from
- which it came. The node must not be subsequently used as an argument to any
- List functions, until it happens to be allocated again. The pointer to
- the node object remains valid, and may be returned by a subsequent
- allocation request from the same node pool.
-
-\subsection{Implementation}
-\index{List!reference implementation}
-
-This section describes the elements of the reference implementation of the
-List component. No requirement is imposed that an implementation should
-follow the reference implementation. The same is true of the
-implementation notes for the other components.
-
-\subsubsection{Types}
-\index{implementation!List types}
-\index{typedefs!implementation of List}
-
-The reference List implementation is a doubly-linked circular list
-\index{sentinel node!of linked list}
-with a {\it sentinel node}. The node structure type is defined like this:
-\begin{verbatim}
- typedef struct lnode_t {
- struct lnode_t *list_next;
- struct lnode_t *list_prev;
- void *list_data;
- } lnode_t;
-\end{verbatim}
-and the list structure is defined like this:
-\begin{verbatim}
- typedef struct list_t {
- lnode_t list_nilnode;
- listcount_t list_nodecount;
- listcount_t list_maxcount;
- } list_t;
-\end{verbatim}
-The \verb|list_nilnode| member of the list object is the sentinel. It is
-always present in the list, never deleted. When the list is empty, the sentinel
-node's \verb|list_next| and \verb|list_prev| pointers simply point back at the sentinel
-node. The \verb|list_maxcount| member of the list tells how many nodes may be
-inserted and \verb|list_nodecount| keeps track of the actual count.
-
-The reason the sentinel node is called \verb|list_nilnode| is that it
-acts as the successor of a list's tail node, if there is one,
-and as the predecessor of the first node. In a linked list implementation
-that does not use a sentinel node, the \verb|list_next| pointer of
-the the tail node and the \verb|list_prev| pointer of the first node would
-be null.
-
-Note that prefixed names are used for all of the structure members. This is so
-that the header file conforms to the documented namespace. If, for example, the
-\verb|list_nilnode| member were simply called \verb|nilnode|, then
-if the program contained somewhere a macro called \verb|nilnode|, there would
-be a potential clash. If the program defined \verb|nilnode| prior to including
-the \verb|list.h| header, the declaration of \verb|struct list_t| would
-be confounded. If the program defined \verb|nilnode| after
-including \verb|list.h|, the definition would interfere with \verb|list.h|
-macros whose replacement text refers to the \verb|nilnode| member.
-
-For programming convenience, the list implementation source file defines short
-macro names for the structure members:
-\begin{verbatim}
- #define next list_next
- #define prev list_prev
- #define data list_data
-\end{verbatim}
-... and so forth. These names are private to the translation unit, which
-includes only standard ANSI C headers. Some of the examples in this section
-make use of the short names; it is assumed that these macros are in effect.
-
-\subsubsection{Selected operations}
-\index{implementation!List operations}
-
-\paragraph{Retrieving the first node}
-\index{List!first node}
-
-Given a pointer \verb|P| to a \verb|list_t| type, the \verb|list_first|
-function examines the value of \verb|P->nilnode.next| which points
-at the head node if the list is not empty. If the list is empty,
-then this expression points back at the sentinel node. In
-other words, the comparison
-\begin{verbatim}
- P->nilnode.next == &P->nilnode
-\end{verbatim}
-yields true when the list is empty. In this case, the interface requires that
-a null pointer be returned by \verb|list_first|. The implementation actually
-uses the above test, through a test for \verb|P->nodecount| being equal to
-zero is also possible.
-
-In general, any operation which produces a pointer to the nilnode that must be
-returned back to the calling program must test for that case and return a null
-pointer instead to satisfy the interface requirements.
-
-\paragraph{Node deletion}
-\index{List!deletion}
-
-Thanks to the use of the sentinel node, the list deletion operation doesn't
-have to test for special cases. A node in the middle of the list is
-deleted in exactly the same way as the first or the last node:
-\begin{verbatim}
- lnode_t *list_delete(list_t *list, lnode_t *del)
- {
- lnode_t *next = del->next;
- lnode_t *prev = del->prev;
-
- assert (list_contains(list, del));
-
- prev->next = next;
- next->prev = prev;
- list->nodecount--;
-
- del->next = del->prev = NULL;
-
- return del;
- }
-\end{verbatim}
-Quite simply, the successor and predecessor of the deleted node are connected
-together so that the deleted node is spliced out from the list. If the node is
-the last remaining one, then the sentinel node serves as both the successor and
-the predecessor. The effect of the deletion then is to set the sentinel's next
-and previous links to point to itself, as they did initially when the list was
-previously empty.
-
-The next and prev pointers are set to null not only for enhanced error checking
-in language implementations that trap dereferences of null pointers,
-but also to indicate that the node is not on any list. The interface
-function \verb|lnode_is_in_a_list| makes use of this.
-
-It's worth discussing in some detail why the values of expressions
-\verb|del->next| and \verb|del->prev| are cached in local variables. The
-actual statements that splice the node out of the list could instead have been
-written:
-\begin{verbatim}
- del->prev->next = del->next;
- del->next->prev = del->prev;
-\end{verbatim}
-However, this causes some compilers to generate less than optimal code because
-they fail to apply common subexpression elimination to the double
-occurrence of \verb|del->next|. Caching this expression in a local variable
-helps to get better code by making the semantics more obvious. In any case,
-modern compilers tend to do a good job of caching locals in high speed storage,
-particularly on architectures generously endowed with registers, so using a few
-extra locals is unlikely to lead to worse target code. The principle of using
-local variables to perform ``manual CSE'' is applied throughout the Kazlib
-reference implementation.
-
-\paragraph{Node insertion}
-Node insertion is also simple, thanks to the sentinel node which makes
-the doubly linked list circular. All insertions are done using
-the functions \verb|list_ins_before| and \verb|list_ins_after|.
-These are very similar, so it suffices to show \verb|list_ins_before|:
-\begin{verbatim}
- void list_ins_before(list_t *list, lnode_t *new, lnode_t *this)
- {
- lnode_t *that = this->prev;
-
- assert (new != NULL);
- assert (!list_contains(list, new));
- assert (!lnode_is_in_a_list(new));
- assert (this == list_nil(list) || list_contains(list, this));
- assert (list->nodecount + 1 > list->nodecount);
-
- new->next = this;
- new->prev = that;
- that->next = new;
- this->prev = new;
- list->nodecount++;
-
- assert (list->nodecount <= list->maxcount);
- }
-\end{verbatim}
-The node \verb|this| is the one before which the new node is being
-inserted. Internally, the pointer \verb|that| points to the
-node after which the insertion takes place. In other words, the function
-inserts the node \verb|new| in between \verb|this| and \verb|that|.
-
-Note the copious assertions which verify all of the documented constraints:
-that the node is not already on the list, or any other list, that the reference
-node \verb|this| is in the list, and that the list capacity won't be exceeded,
-and that the node count doesn't overflow its type.
-
-\index{List!insertion}
-
-\section{Hash component}
-
-The Hash component provides a means to manage collections of elements, called
-hashes, that are not ordered. Each element in the collection has a unique key,
-which is used for searching and inserting. The intent is that the
-implementation is based on extendible hashing, and the interface allows for
-user-defined hashing functions. The number of elements that can be stored
-in a hash is limited; maximum number of entries in a hash is known as its
-{\it capacity}.
-
-\subsection{Interface}
-
-\subsubsection{The {\tt hash.h} header}
-
-Each C or C++ translation unit that is to use the functionality of the Hash
-component shall include the header \verb|hash.h|. This header shall
-contain declarations of types and external functions, and definitions of
-macros. The following typedef names shall be
-defined:\index{Hash!typedef names}
-\index{typedefs!defined by Hash}
-\begin{verbatim}
- hash_t hashcount_t
- hnode_t hash_val_t
- hash_comp_t hnode_alloc_t
- hscan_t hnode_free_t
- hash_fun_t
-\end{verbatim}
-In addition, the following structure tags may be defined:\index{Hash!tag names}
-\index{tags!defined by Hash}
-\begin{verbatim}
- struct hash_t
- struct hnode_t
- struct hscan_t
-\end{verbatim}
-The following external function names shall be declared:
-\index{Hash!function names}\index{functions!defined by Hash}
-\begin{verbatim}
- hash_create hash_count
- hash_set_allocator hash_size
- hash_destroy hash_isfull
- hash_free_nodes hash_isempty
- hash_init hash_scan_begin
- hash_insert hash_scan_next
- hash_lookup hash_scan_delete
- hash_delete hash_scan_delfree
- hash_alloc_insert hash_verify
- hash_delete_free hnode_create
- hnode_put hnode_init
- hnode_get hnode_destroy
- hnode_getkey hash_free
-\end{verbatim}
-\index{Hash!external objects}
-In addition, the external object name
-\begin{verbatim}
- hash_val_t_bit
-\end{verbatim}
-shall be declared. The following preprocessor symbols (macros) shall be
-defined: \index{Hash!macro names}\index{macros!defined by Hash}
-\indexmacro{HASHCOUNT_T_MAX}
-\indexmacro{HASH_VAL_T_BIT}
-\indexmacro{HASH_VAL_T_MAX}
-\indexmacro{HASH_H}
-\begin{verbatim}
- HASHCOUNT_T_MAX
- HASH_VAL_T_BIT
- HASH_H\end{verbatim}
-\index{symbols!reserved by Hash}\index{Hash!reserved symbols}
-Macro identifiers which begin with the upper-case prefix \verb|HASH| are
-reserved for future extensions to the \verb|hash.h| header, as are
-names in the ordinary and tag namespaces which begin with \verb|hash_|,
-\verb|hnode_| or \verb|hscan_|. External names which begin with \verb|hash_|,
-\verb|hnode_| or \verb|hscan_| are reserved by the Kazlib library regardless of
-what headers are included.
-
-\subsubsection{The {\tt hash_t} type}
-
-\indextype{hash_t}
-The type \verb|hash_t| is an opaque data type which maintains information about
-the current state of a single hash. From the programmer's viewpoint, a hash
-consists of an instance of the \verb|hash_t| type, plus zero or more instances
-of the type \verb|hnode_t|. An instance of the \verb|hash_t| type can be
-dynamically created using the \verb|hash_create| function, and destroyed by the
-\verb|hash_destroy| function. Alternately, the program can declare an object
-of type \verb|hash_t| and have it initialized via the \verb|hash_init|
-function. When initializing a hash this way, the user must also provide
-a fixed-size array of \verb|hnode_t *| objects which serves as the hash table.
-\footnote{A hash initialized this way does not support extendible hashing,
-because there is no mechanism for growing the user-supplied array.}
-
-\subsubsection{The {\tt hnode_t} type}
-
-\indextype{hnode_t}
-The \verb|hnode_t| type is an opaque type that represents a single element
-that can be inserted into a hash. A hash node contains a a reference to
-satellite data provided by the user. Nodes may be dynamically created by the
-\verb|hnode_create| function. Alternately, the program may supply an
-\verb|hnode_t| object that can be initialized by the \verb|hnode_init|
-function.
-
-\subsubsection{The {\tt hash_comp_t} type}
-
-\indextype{hash_comp_t}
-The \verb|hash_comp_t| type is a typedef name for the pointer-to-function type
-\begin{verbatim}
- int (*)(const void *, const void *);
-\end{verbatim}
-In the context of the Hash component, this type denotes pointers to
-comparison functions.
-
-\subsubsection{The {\tt hscan_t} type}
-
-\indextype{hscan_t}
-The \verb|hscan_t| typedef stands for an opaque type which represents
-context information for traversing a hash. It is initialized by the
-\verb|hash_scan_begin| function, which specifies a hash to be
-traversed. Successive elements are retrieved using the \verb|hash_scan_next|
-function, which eventually indicates that no more elements
-remain. Inserting to, or deleting from a hash other than using
-the function \verb|hash_scan_delete| causes any \verb|hscan_t|
-objects that refer to it to become indeterminate.
-
-\subsubsection{The {\tt hashcount_t} type}
-
-\indextype{hashcount_t}
-\indexmacro{HASHCOUNT_T_MAX}
-This is an unsigned integral type which is capable of representing the number
-of nodes in a hash.
-The \verb|HASHCOUNT_T_MAX| macro expands to a
-constant expression of type \verb|hashcount_t| which specifies the maximum
-value of that type.
-
-\subsubsection{The {\tt hash_val_t} type}
-
-\indextype{hash_val_t}
-\indexmacro{HASH_VAL_T_MAX}
-The \verb|hash_val_t| type is an unsigned integral type capable of
-holding at least 32 bits. The purpose of this type is to represent the
-output values of hashing functions.
-The \verb|HASH_VAL_T_MAX| macro expands to a
-constant expression of type \verb|hash_val_t| which specifies the maximum
-value of that type.
-
-\subsubsection{The {\tt hnode_alloc_t} type}
-
-\index{Hash!allocator function}
-The \verb|hnode_alloc_t| identifier is a typedef name for the pointer-to-function
-type
-\begin{verbatim}
- hnode_t *(*)(void *);
-\end{verbatim}
-In other words, a pointer to a function that takes a \verb|void *|
-argument and returns a pointer to \verb|hnode_t|.
-A function of this type which meets certain behavior criteria may be
-registered with a \verb|hash_t| object as node allocator, together
-with a compatible deallocator function. The \verb|void *| argument
-passes user-specified context information through to the
-allocator routines (see section \ref{section:hash_set_allocator}).
-
-\subsubsection{The {\tt hnode_free_t} type}
-
-\index{Hash!deallocator function}
-The \verb|hnode_free_t| identifier is a typedef name for the
-pointer-to-function type
-\begin{verbatim}
- void (*)(hnode_t *, void *);
-\end{verbatim}
-A function of this type which meets certain behavior criteria may be
-registered with a \verb|hash_t| object as node deallocator
-together with a compatible allocator function.
-
-\subsubsection{The {\tt hash_fun_t} type}
-
-\index{hashing function}
-The \verb|hash_fun_t| identifier is a typedef name for the
-pointer-to-function type
-\begin{verbatim}
- hash_val_t (*hash_fun_t)(const void *);
-\end{verbatim}
-A function of this type which behaves a certain way is called
-a {\it hashing function}. To be a viable hashing function, such
-a function must take a pointer to a key object, and produce
-an integer value that depends only on the contents of the key,
-and possibly on information that does not change over the lifetime of any hash
-for which that hashing function is used. Additional requirements for hashing
-functions are introduced later.
-
-\subsubsection{The {\tt hash_val_t_bit} object}
-
- \indexobject{hash_val_t_bit}
- \synopsis
- \begin{verbatim}
- extern int hash_val_t_bit;\end{verbatim}
-
- \description
-
- The \verb|hash_val_t_bit| object of type int has a fixed value
- which counts the number of bits in the \verb|hash_val_t| object.
- The program shall not store a value into this object.
-
- The value of \verb|hash_val_t_bit| need not be correct until the
- first successful call to \verb|hash_create| or to \verb|hash_init|
- completes.
-
- The implementation shall provide the macro \verb|HASH_VAL_T_BIT| which
- expands to a non-lvalue expression that has the same value and type as the
- object, but which may be a constant expression.\footnote{The intent of
- providing these values is to ease the implementation of portable hashing
- functions that take advantage of all of the available bits of a given
- Kazlib implementation. Alternately, hashing functions may be constructed to
- only use the lower 32 bits of the type.}
-
-\subsubsection{The {\tt hash_create} function}
-
- \indexfunc{hash_create}
- \index{Hash!creation of}
- \index{create!hash object}
- \synopsis
- \begin{verbatim}
- hash_t *hash_create(hashcount_t, hash_comp_t, hash_fun_t);\end{verbatim}
-
- \description
-
- If sufficient resources exist, the \verb|hash_create| function instantiates
- and initializes an object of type \verb|hash_t| and returns a pointer to
- it. Otherwise it returns a null pointer.
-
- The first argument establishes the capacity of the hash, which is
- initially empty.
-
- The second argument is a pointer to a comparison function that will be
- associated with the \verb|hash_t| object for its entire duration.
-
- \index{hashing function}
- The third argument is either null or a pointer to a hashing function
- that is permanently associated with the object. If it is null, a {\it default
- hashing function\/} is assigned by the implementation.
-
- The hashing function shall be invoked with an argument that is one
- of the keys that are being inserted into, or sought after, in the
- hash. The hashing function must produce the same value each time it
- is called for a given key. It is up to the hash user to define the
- representation of keys, to manage their storage, and to provide a matching
- hashing function. The hash stores only generic \verb|void *| pointers to
- keys.
-
- The default hashing function assumes that keys are null terminated
- strings. That is to say, it behaves as though its \verb|void *|
- argument points to the first elements of an array of \verb|unsigned|
- \verb|char|, the last of which is a null character. The use of
- the default hashing function with keys that do not have this representation
- results in undefined behavior.
-
-\subsubsection{The {\tt hash_set_allocator} function}
-
- \indexfunc{hash_set_allocator}
- \label{section:hash_set_allocator}
-
- \synopsis
- \begin{verbatim}
- void hash_set_allocator(hash_t *, hnode_alloc_t,
- hnode_free_t, void *);\end{verbatim}
-
- \constraints
-
- The second and third arguments---the function pointers---shall either
- both be null, or both be non-null. The hash pointed at by the first
- argument shall be empty.
-
- \description
-
- When a hash is initialized, it is outfitted with a pair of default
- node allocation functions. These functions may be replaced with functions
- supplied by the program by calling the \verb|hash_set_allocator| function
- and specifying two suitable pointers. If these pointers are null, the
- default functions are restored.
-
- These functions are called to allocate and free \verb|hnode_t|
- objects by the functions \verb|hash_alloc_insert|
- and \verb|hash_delete_free| (see sections
- \ref{section:hash_delete_free} and \ref{section:hash_alloc_insert}).
-
- If sufficient resources exist, the allocation function shall
- return a pointer to a unique storage object that is large enough
- and suitably aligned to represent an object of type \verb|dnode_t|.
- Otherwise, the function shall return a null pointer.
-
- The deallocation function shall be capable of disposing of the
- objects created by the matching allocator function.
-
-
-\subsubsection{The {\tt hash_destroy} function}
-
- \indexfunc{hash_destroy}
- \synopsis
- \begin{verbatim}
- void hash_destroy(hash_t *);\end{verbatim}
-
- \constraints
-
- The hash pointed at by the first argument shall be empty.
-
- \description
-
- The \verb|hash_destroy| function deinitializes and deallocates a hash
- that was created with \verb|hash_create|.
- All pointers and \verb|hscan_t| objects that referred to the hash become
- indeterminate.
-
-\subsubsection{The {\tt hash_free_nodes} function}
-
- \indexfunc{hash_free_nodes}
- \synopsis
- \begin{verbatim}
- void hash_free_nodes(hash_t *);\end{verbatim}
-
- \description
-
- The \verb|hash_free_nodes| function removes each node from
- the hash and destroys it as if by calling \verb|hash_delete_free|
- (Section \ref{section:hash_delete_free}). The order in which
- the nodes are destroyed is unspecified.
-
-\subsubsection{The {\tt hash_free} function}
-
- \indexfunc{hash_free}
- \synopsis
- \begin{verbatim}
- void hash_free(hash_t *);\end{verbatim}
-
- \description
-
- Every node in the hash is removed from the hash and is then subject to the
- deallocation function. The overall effect is as if the function
- \verb|hash_delete_free| (Section \ref{section:hash_delete_free}) were
- invoked on each node, and then \verb|hash_destroy| invoked on the
- hash itself.
-
- This function is obsolescent, and will be removed from some future revision
- of this document.
-
-\subsubsection{The {\tt hash_init} function}
-
- \indexfunc{hash_init}
- \synopsis
- \begin{verbatim}
- hash_t *hash_init(hash_t *, hashcount_t, hash_comp_t,
- hash_fun_t, hnode_t **, hashcount_t);
- \end{verbatim}
-
- \constraints
-
- The last argument, which specifies the size of the program-supplied table,
- shall be integral power of two that is greater than one---that is to say, an
- integer of the form $2^k$ where $k$ is a positive integer.
-
- \description
-
- The \verb|hash_init| function configures the specified \verb|hash_t| object
- to use a specified array of \verb|hnode_t *| pointer objects as a table.
- The user is responsible for providing storage for the \verb|hash_t|
- object and the array. As in the \verb|hash_create| interface,
- the second parameter specifies the capacity, and the subsequent
- arguments specify the comparison and hashing function, respectively.
- The last two arguments specify the table of pointers. The array object
- shall have at least as many elements as indicated by the last parameter,
- otherwise the behavior is undefined. The call to \verb|hash_init| is said
- to register the array with the hash.
-
- The program shall not register the same array with more than one hash.
- More specifically, once the program modifies a registered array, or
- registers it with another hash, it must discontinue use of the first hash.
- \footnote{Note that no explicit deinitialization function is provided to
- dissociate the array. A program disposes of a hash created by
- {\tt hash_init} by discontinuing its use.}
-
-\subsubsection{The {\tt hash_insert} function}
-
- \indexfunc{hash_insert}
- \label{section:hash_insert}
- \synopsis
- \begin{verbatim}
- void hash_insert(hash_t *, hnode_t *, const void *);\end{verbatim}
-
- \constraints
- The hash is not full. The key specified by the \verb|void *| parameter
- does not already exist in the specified hash. The node specified
- by the second parameter is not already inserted into a hash.
-
- \description
- The \verb|hash_insert| function adds a new node to a hash. The user
- must supply a node object that was initialized with \verb|hnode_init|
- or dynamically created with \verb|hnode_create|. If the node is
- already inserted into the same hash or any other hash, the behavior
- is undefined.
-
- A program may modify a key or node that has been inserted into a hash, or
- cause the storage of the key or the node to become invalid. However, any
- subsequent use of the hash invokes undefined behavior, with the following
- exception: the data pointer stored within a node may be modified using the
- \verb|hnode_put| function.
-
- The \verb|hash_insert| function invokes the hashing function callback with
- the key pointer as the argument.
-
- The \verb|hash_insert| function may need to acquire additional storage in
- order to support hash table growth. If the storage allocation fails, the
- function shall fully recover, and insert the node without growing the
- table.
-
- The Hash implementation shall not modify the storage referenced by a key,
- and shall not access it other than indirectly through the supplied hashing
- and comparison functions.
-
-\subsubsection{The {\tt hash_lookup} function}
-
- \indexfunc{hash_lookup}
- \synopsis
- \begin{verbatim}
- hnode_t *hash_lookup(hash_t *, const void *);\end{verbatim}
-
- \description
-
- The \verb|hash_lookup| function searches the given hash for a node
- matching the given key. Unless the hash is empty, the key shall be
- compared against one or more keys that are already in the hash,
- using the comparison function. The key pointer may
- be identical to one that has already been inserted into the
- hash.\footnote {In that case, the comparison function must correctly
- cope with aliased parameters}.
-
- If the key is found in the hash, a pointer to the corresponding node
- is returned.\footnote{The corresponding node is the one that was specified
- in the call to {\tt hash_insert} together with the matching key.}
-
- If the key is not found, a null pointer is returned.
-
-\subsubsection{The {\tt hash_delete} function}
-
- \indexfunc{hash_delete}
- \synopsis
- \begin{verbatim}
- hnode_t *hash_delete(hash_t *, hnode_t *);\end{verbatim}
-
- \constraints
- The specified node is an occupant of the given hash.
-
- \description
- The \verb|hash_delete| function removes from the given hash a
- node that has previously been inserted into it. The key under
- which the node was inserted is also removed from the hash.\footnote{Thus
- the program may arbitrarily manipulate the removed key without destroying
- the integrity of the hash.}
-
- Any existing \verb|hscan_t| iterator which is associated with the
- hash becomes indeterminate.\footnote{To delete the current node during hash
- table traversal, the {\tt hash_scan_delete} function must be used
- instead.}
-
-
-\subsubsection{The {\tt hash_alloc_insert} function}
-
- \label{section:hash_alloc_insert}
- \indexfunc{hash_alloc_insert}
-
- \synopsis
- \begin{verbatim}
- int hash_alloc_insert(hash_t *, const void *, void *);\end{verbatim}
-
- \constraints
-
- The second argument specifies the insertion key. The hash shall not
- already contain this key.
-
- \description
-
- The \verb|hash_alloc_insert| function dynamically allocates and
- initializes a \verb|hnode_t| object and inserts it into the
- given hash. The second argument and third arguments are pointers
- to user data and key objects, either of which may be null.
-
- The allocation is performed by a call to the default allocation
- function, or to the function that was configured using
- \verb|hash_set_allocator| (Section \ref{section:hash_set_allocator}).
-
- If the allocation succeeds, the insertion is performed and
- the value 1 is returned. If the allocation fails, no insertion is
- performed and 0 is returned.
-
-\subsubsection{The {\tt hash_delete_free} function}
-
- \label{section:hash_delete_free}
- \indexfunc{hash_delete_free}
-
- \synopsis
- \begin{verbatim}
- void hash_delete_free(hash_t *, hnode_t *)
- \end{verbatim}
-
- \constraints
- The given node can be found within the given hash.
-
- \description
- The \verb|hash_delete_free| function is the reverse of
- \verb|hash_alloc_insert|. It removes the given node form the
- hash as if by a call to \verb|hash_delete| and then deletes it using the
- default or user-defined allocator (Section
- \ref{section:hash_set_allocator}). If the given node had not been created
- using \verb|hash_alloc_insert|, the behavior is undefined.
-
-\subsubsection{The {\tt hnode_put} function}
-
- \indexfunc{hnode_put}
- \synopsis
- \begin{verbatim}
- void hnode_put(hnode_t *, void *);\end{verbatim}
-
- \description
- The function \verb|hnode_put| replaces the data element
- associated with the hash node.
-
-\subsubsection{The {\tt hnode_get} function}
-
- \indexfunc{hnode_get}
- \synopsis
- \begin{verbatim}
- void *hnode_get(hnode_t *);\end{verbatim}
-
- \description
- The \verb|hnode_get| function retrieves the \verb|void * | data value
- associated with the given hash node.
-
-\subsubsection{The {\tt hnode_getkey} function}
-
- \indexfunc{hnode_getkey}
- \synopsis
- \begin{verbatim}
- const void *hnode_getkey(hnode_t *);\end{verbatim}
-
- \description
-
- The \verb|hnode_getkey| function retrieves the \verb|void *| key value
- associated with the given node. A node acquires an associated key
- when it is inserted into a hash (see section \ref{section:hash_insert}).
- Invoking \verb|hnode_getkey| on a node that has not been inserted
- into a hash results in undefined behavior.
-
-\subsubsection{The {\tt hash_count} function}
-
- \indexfunc{hash_count}
- \synopsis
- \begin{verbatim}
- hashcount_t hash_count(hash_t *);\end{verbatim}
-
- \description
- The \verb|hash_count| function returns a value which represents the number
- of nodes currently stored in the hash pointed at by the argument.
-
-\subsubsection{The {\tt hash_size} function}
-
- \indexfunc{hash_size}
- \synopsis
- \begin{verbatim}
- hashcount_t hash_size(hash_t *hash)\end{verbatim}
-
- \description
- The \verb|hash_size| function returns an implementation-defined value that
- depends on the number of entries in the given hash. The intent is that the
- value represent the size of the internal hash table managed by the given
- hash.
-
-\subsubsection{The {\tt hash_isfull} function}
-
- \indexfunc{hash_isfull}
- \synopsis
- \begin{verbatim}
- int hash_isfull(hash_t *);\end{verbatim}
-
- \description
- The \verb|hash_isfull| function returns 1 if the hash is full,
- otherwise it returns 0.
-
- If the argument is an expression with side effects, the behavior is
- undefined.\index{macros!and side effects}
-
-
-\subsubsection{The {\tt hash_isempty} function}
-
- \indexfunc{hash_isempty}
- \synopsis
- \begin{verbatim}
- int hash_isempty(hash_t *);\end{verbatim}
-
- \description
- The \verb|hash_isempty| function returns 1 if the given hash is empty,
- otherwise it returns 0.
-
-\subsubsection{The {\tt hash_scan_begin} function}
-
- \indexfunc{hash_scan_begin}
- \synopsis
- \begin{verbatim}
- void hash_scan_begin(hscan_t *, hash_t *);\end{verbatim}
-
- \description
- The \verb|hash_scan_begin| initializes the \verb|hscan_t| iterator object,
- preparing it for a traversal of the given hash.
-
- After this initialization, if the hash is modified in any way by
- the performance of an insertion or deletion operation, the
- value of the \verb|hscan_t| object becomes indeterminate,
- with one exception: the \verb|hash_scan_delete| function or the
- \verb|hash_scan_delfree| function may be used to delete the current
- node.
-
-\subsubsection{The {\tt hash_scan_next} function}
-
- \indexfunc{hash_scan_next}
- \synopsis
- \begin{verbatim}
- hnode_t *hash_scan_next(hscan_t *);\end{verbatim}
-
- \description
- If any unvisited nodes remain, the \verb|hash_scan_next| function advances
- to the next one and returns a pointer to it. Otherwise, it returns a null
- pointer. Repeated invocations of \verb|hash_scan_next| return a pointer to
- every node that has been inserted into the table, in no particular order,
- such that no node is reported twice.
-
-\subsubsection{The {\tt hash_scan_delete} function}
-
- \indexfunc{hash_scan_delete}
- \synopsis
- \begin{verbatim}
- hnode_t *hash_scan_delete(hash_t *, hnode_t *);
- \end{verbatim}
-
- \constraints
- The specified node is an occupant of the given hash.
-
- \description
- This function is almost exactly like \verb|hash_delete| except that it may
- be used to delete a node that has been most recently obtained from
- \verb|hash_scan_next| without destroying the validity of the \verb|hscan_t|
- iterator from which the node was obtained.
-
-\subsubsection{The {\tt hash_scan_delfree} function}
-
- \label{section:hash_scan_delfree}
- \indexfunc{hash_scan_delfree}
-
- \synopsis
- \begin{verbatim}
- void hash_scan_delfree(hash_t *, hnode_t *)
- \end{verbatim}
-
- \constraints
- The given node can be found within the given hash.
-
- \description
- The \verb|hash_scan_delfree| function is similar to
- \verb|hash_delete_free|. It removes the given node form the
- hash and then deletes it using the default or user-defined allocator
- (Section \ref{section:hash_set_allocator}). If the given node
- had not been created using \verb|hash_alloc_insert|, the behavior
- is undefined.
-
- The deletion from the hash is performed as if by a call to
- \verb|hash_scan_delete|, thus it is safe to delete a node that
- was most recently obtained from a \verb|hash_scan_next| without
- destroying the validity of the \verb|hscan_t| iterator.
-
-\subsubsection{The {\tt hash_verify} function}
-
- \indexfunc{hash_verify}
- \synopsis
- \begin{verbatim}
- int hash_verify(hash_t *hash);\end{verbatim}
-
- \description
- The intent of the \verb|hash_verify| function is to perform a verification
- on the hash object, regardless of whether the Kazlib implementation is
- operated in verification or production mode. If the hash object
- and its constituent nodes have been correctly manipulated, and the
- program has not caused any undefined behaviors, the value $1$ is returned.
- Otherwise, the function may be able to, but is not guaranteed to, detect
- corruption, and return the value zero.
-
-\subsubsection{The {\tt hnode_create} function}
-
- \indexfunc{hnode_create}
- \synopsis
- \begin{verbatim}
- hnode_t *hnode_create(void *);\end{verbatim}
-
- \description
- The \verb|hnode_create| function dynamically allocates a hash node,
- stores in it the data value specified in the argument and
- returns a pointer to it. The allocation is performed by a call to the
- standard \verb|malloc| function. If the allocation fails, a null
- pointer is returned.
-
- The node's key pointer remains indeterminate until it is the subject of a
- \verb|hash_insert| operation.
-
-\subsubsection{The {\tt hnode_init} function}
-
- \indexfunc{hnode_init}
- \synopsis
- \begin{verbatim}
- hnode_t *hnode_init(hnode_t *, void *);\end{verbatim}
-
- \description
- The \verb|hnode_init| function initializes the contents
- of the specified hash node object, assigning it the
- data value specified as the second argument.
- The first argument is a pointer which refers to
- a data object that has a suitable size and alignment
- for the representation of an \verb|hnode_t| type.
- After initialization with \verb|hnode_init|, the object is subsequently
- eligible as an operand to the functions of the hash component,
- other than \verb|hnode_getkey|.
-
- The node's key pointer remains indeterminate until it is the subject of a
- \verb|hash_insert| operation.
-
-\subsubsection{The {\tt hnode_destroy} function}
-
- \indexfunc{hnode_destroy}
- \synopsis
- \begin{verbatim}
- void hnode_destroy(hnode_t *);\end{verbatim}
-
- \description
- The \verb|hnode_destroy| function destroys a hash node that has been
- allocated with the \verb|hnode_create| function. The value of any pointer
- that referred to the node that was thus freed is indeterminate.
-
- If the node is currently the occupant of a hash, the behavior is undefined
- if the hash is subsequently used.
-
-\subsection{Implementation}
-
-TODO
-
-\section{Dictionary component}
-
-\index{Dictionary}
-The Dictionary component provides a means to manage ordered sequences of
-elements, having the following properties:
-\begin{enumerate}
-\item If the dictionary is not empty, a first and last element can be identified.
- In a dictionary having only one element, that one element is both the first and
- last element.
-\item Each element that is not the last element has another element as its
- {\it successor}.
- \index{successor!of a dictionary element}
- \index{Dictionary!successor of an element}
-\item Each element that is not the first element has a {\it predecessor}.
- \index{predecessor!of a dictionary element}
- \index{Dictionary!predecessor of an element}
-\item No element is the predecessor or successor of more than one element.
-\item If one element is the successor of another, the other is necessarily the
- predecessor of the first.
-\item Each element is associated with a piece of information known as
- the key. The sequence is ordered according to the relation imposed
- by the comparison function: the key of an element compares
- greater than or equal to the key of its predecessor.
-\item If duplicate keys are present, then elements
- having the same key form a subsequence with no other keys in it, which
- follows from the previous property. No additional ordering is imposed
- within such subsequences.
-\item Each element is associated with arbitrary satellite data.
-\end{enumerate}
-
-The Dictionary component supports efficient operations over such ordered
-sequences: such as insertion, deletion, ordered traversal, as well as exact and
-range searches.\footnote{The implicit association of keys and satellite data,
-together with the ability of efficiently search by key to retrieve data, gives
-rise to the term {\it dictionary}. A dictionary need not be ordered; a hash can
-therefore also be considered to be a kind of dictionary; the Kazlib
-nomenclature is somewhat unfortunate in that regard.}
-
-The number of elements that can be stored in a dictionary is limited; maximum
-number of entries in a dictionary is known as its {\it capacity}.
-
-\subsection{Interface}
-
-\subsubsection{The {\tt dict.h} header}
-
-Each C or C++ translation unit that is to use the functionality of the Dict
-component shall include the header \verb|dict.h|. This header shall
-contain declarations of types and external functions, and definitions of
-macros. The following typedef names shall be
-defined:\index{Dict!typedef names}
-\index{typedefs!defined by Dict}
-\begin{verbatim}
- dict_t dnode_process_t
- dnode_t dnode_alloc_t
- dictcount_t dnode_free_t
- dict_comp_t dict_load_t
-\end{verbatim}
-In addition, the following structure tags may be defined:\index{Dict!tag names}
-\index{tags!defined by Dict}
-\begin{verbatim}
- struct dict_t
- struct dnode_t
-\end{verbatim}
-The following external function names shall be declared:
-\index{Dict!function names}\index{functions!defined by Dict}
-\begin{verbatim}
- dict_create dict_count
- dict_set_allocator dict_isempty
- dict_destroy dict_isfull
- dict_free_nodes dict_contains
- dict_init dict_allow_dupes
- dict_verify dnode_is_in_a_dict
- dict_lookup dnode_create
- dict_lower_bound dnode_init
- dict_upper_bound dnode_destroy
- dict_insert dnode_get
- dict_delete dnode_getkey
- dict_alloc_insert dnode_put
- dict_delete_free dict_process
- dict_first dict_load_begin
- dict_last dict_load_next
- dict_next dict_load_end
- dict_prev dict_free
-\end{verbatim}
-The following preprocessor symbols shall be
-defined: \index{Dict!macro names}\index{macros!defined by Dict}
-\indexmacro{DICTCOUNT_T_MAX}
-\indexmacro{DICT_H}
-\begin{verbatim}
- DICTCOUNT_T_MAX
- DICT_H\end{verbatim}
-\index{symbols!reserved by Dict}\index{Dict!reserved symbols}
-Macro identifiers which begin with the upper-case prefix \verb|DICT| are
-reserved for future extensions to the \verb|dict.h| header, as are
-names in the ordinary and tag namespaces which begin with \verb|dict_|
-or \verb|dnode_|. External names which begin with \verb|dict_|
-or \verb|dnode_| are reserved by the Kazlib library regardless of
-what headers are included.
-
-\subsubsection{The {\tt dict_t} type}
-
-\indextype{dict_t}
-The type \verb|dict_t| is an opaque data type which represents a single
-dictionary. A dictionary consists of an instance of the \verb|dict_t| type,
-plus zero or more instances of the type \verb|dnode_t|. An object of type
-\verb|dict_t| can be initialized by the \verb|dict_init| function. Alternately,
-the \verb|dict_create| function will dynamically allocate and initialize a
-dictionary. An empty dictionary created by \verb|dict_create| may be disposed
-of using \verb|dict_destroy|.
-
-\subsubsection{The {\tt dnode_t} type}
-
-\indextype{dnode_t}
-The \verb|dnode_t| type represents a single entry in a dictionary called a
-dictionary node. The object stores a pointer to user data, and a key pointer
-that is assigned to the dictionary node at the time when it is inserted into
-the dictionary. A \verb|dnode_t| may be dynamically created using
-\verb|dnode_create| and destroyed using \verb|dnode_destroy|. Alternately,
-the program may supply storage for a \verb|dnode_t| object and initialize
-it using the \verb|dnode_init| function.
-
-\subsubsection{The {\tt dictcount_t} type}
-
-\indextype{dictcount_t}
-\indexmacro{DICTCOUNT_T_MAX}
-This is an unsigned integral type which is capable of representing the number
-of nodes in a dictionary. The \verb|DICTCOUNT_T_MAX| macro expands to a
-constant expression of type \verb|dictcount_t| which specifies the maximum
-value of that type.
-
-\subsubsection{The {\tt dict_comp_t} type}
-
-\indextype{dict_comp_t}
-The \verb|dict_comp_t| type is a typedef name for the pointer-to-function type
-\begin{verbatim}
- int (*)(const void *, const void *);
-\end{verbatim}
-In the context of the Dictionary component, this type denotes pointers to
-comparison functions.
-
-\subsubsection{The {\tt dnode_process_t} type}
-
-\indextype{dnode_process_t}
-The type \verb|dnode_process_t| is a typedef name for the pointer-to-function type
-\begin{verbatim}
- void (*)(dict_t *, dnode_t *, void *);
-\end{verbatim}
-In the context of the Dictionary component, this is the type of a
-dictionary node processing function (See section \ref{section:dict_process}).
-The first two parameters identify a dictionary and the node within that
-dictionary that is being processed. The third argument is a context pointer.
-
-\subsubsection{The {\tt dnode_alloc_t} type}
-
-\indextype{dnode_alloc_t}
-The type \verb|dnode_alloc_t| is a typedef name for the pointer-to-function type
-\begin{verbatim}
- dnode_t *(*)(void *);
-\end{verbatim}
-A function compatible with this type which meets certain other criteria may be
-registered with a \verb|dict_t| object as a node allocator function
-(See section \ref{section:dict_set_allocator}).
-
-\subsubsection{The {\tt dnode_free_t} type}
-
-\indextype{dnode_free_t}
-The type \verb|dnode_free_t| is a typedef name for the pointer-to-function type
-\begin{verbatim}
- void (*)(dnode_t *, void *);
-\end{verbatim}
-A function compatible with this type which meets certain other criteria may be
-registered with a \verb|dict_t| object as a node deallocator function.
-(See section \ref{section:dict_set_allocator}).
-
-\subsubsection{The {\tt dict_load_t} type}
-
-\indextype{dict_load_t}
-
-The \verb|dict_load_t| type is opaque, and represents a context structure
-used during the process of constructing a dictionary from an ordered list
-of nodes. (See sections \ref{section:dict_load_begin} to
-\ref{section:dict_load_end}).
-
-\subsubsection{The {\tt dict_create} function}
-
- \indexfunc{dict_create}
- \index{Dictionary!creation of}
- \index{create!dictionary object}
-
- \synopsis
- \begin{verbatim}
- dict_t *dict_create(dictcount_t, dict_comp_t);\end{verbatim}
-
- \description
- The \verb|dict_create| function allocates a new
- object of type \verb|dict_t| and initializes it to act as
- a dictionary.
-
- If insufficient resources exist for the allocation,
- a null pointer is returned, otherwise a pointer to the dictionary
- is returned.
-
- The first argument specifies the capacity of the dictionary,
- which is initially empty.
-
- The second argument is a comparison function that is used for comparing
- keys during insertion and searching operations, and is associated
- with the dictionary for its entire duration.
-
-\subsubsection{The {\tt dict_set_allocator} function}
-
- \label{section:dict_set_allocator}
- \indexfunc{dict_set_allocator}
-
- \synopsis
- \begin{verbatim}
- void dict_set_allocator(dict_t *, dnode_alloc_t,
- dnode_free_t, void *);\end{verbatim}
-
- \constraints
-
- The second and third arguments---the function pointers---shall either
- both be null, or both be non-null. The dictionary pointed at by the first
- argument shall be empty.
-
- \description
-
- When a dictionary is initialized, it is outfitted with a pair of default
- node allocation functions. These functions may be replaced with functions
- supplied by the program by calling the \verb|dict_set_allocator| function
- and specifying two suitable pointers. If these pointers are null, the
- default functions are restored.
-
- These functions are called to allocate and free \verb|dnode_t|
- objects by the functions \verb|dict_alloc_insert|
- and \verb|dict_delete_free| (see sections
- \ref{section:dict_delete_free} and \ref{section:dict_alloc_insert}).
-
- If sufficient resources exist, the allocation function shall
- return a pointer to a unique storage object that is large enough
- and suitably aligned to represent an object of type \verb|dnode_t|.
- Otherwise, the function shall return a null pointer.
-
- The deallocation function shall be capable of disposing of the
- objects created by the matching allocator function.
-
-\subsubsection{The {\tt dict_destroy} function}
-
- \indexfunc{dict_destroy}
-
- \synopsis
- \begin{verbatim}
- void dict_destroy(dict_t *);\end{verbatim}
-
- \constraints
-
- The dictionary pointed at by the first argument shall be empty.
-
- \description
-
- The \verb|dict_destroy| function deinitializes and deallocates a dictionary
- object that was created by \verb|dict_create|. All pointers that
- referred to the dictionary become indeterminate.
-
-\subsubsection{The {\tt dict_free_nodes} function}
-
- \indexfunc{dict_free_nodes}
-
- \synopsis
- \begin{verbatim}
- void dict_free_nodes(dict_t *);\end{verbatim}
-
- \description
-
- Every node in the dictionary is removed from the dictionary and is then
- subject to the deallocation function, as if the function
- \verb|dict_delete_free| (Section \ref{section:dict_delete_free}) were
- invoked on each node, in some unspecified order.
-
-\subsubsection{The {\tt dict_free} function}
-
- \indexfunc{dict_free}
-
- \synopsis
- \begin{verbatim}
- void dict_free(dict_t *);\end{verbatim}
-
- \description
-
- This function is obsolescent, and will be removed from some future revision
- of this document. It is equivalent to \verb|dict_free_nodes|.
-
-\subsubsection{The {\tt dict_init} function}
-
- \indexfunc{dict_init}
-
- \synopsis
- \begin{verbatim}
- dict_t *dict_init(dict_t *, dictcount_t, dict_comp_t);\end{verbatim}
-
- \description
-
- The \verb|dict_init| function prepares specified \verb|dict_t| object
- to behave as a dictionary that may subsequently be used with the other
- dictionary functions.
-
- The first argument points to the \verb|dict_t| object to be initialized.
- The second argument specifies the capacity of the dictionary. The third
- argument is a pointer to the comparison function which shall be associated
- with the dictionary for its entire duration.
-
-\subsubsection{The {\tt dict_verify} function}
-
- \indexfunc{dict_verify}
-
- \synopsis
- \begin{verbatim}
- int dict_verify(dict_t *);\end{verbatim}
-
- \description
-
- The intent of the \verb|dict_verify| function is to perform a verification
- on the dictionary object, regardless of whether the Kazlib implementation
- is operated in verification or production mode. If the dictionary object
- and its constituent nodes have been correctly manipulated, and the program
- has not caused any undefined behaviors, the value $1$ is returned.
- Otherwise, the function may be able to, but is not guaranteed to, detect
- corruption, and return the value zero.
-
-\subsubsection{The {\tt dict_lookup} function}
-
- \indexfunc{dict_lookup}
-
- \synopsis
- \begin{verbatim}
- dnode_t *dict_lookup(dict_t *, const void *);\end{verbatim}
-
- \description
- The \verb|dict_lookup| function searches the given dictionary for a node
- matching the given key. Unless the dictionary is empty, the key shall be
- compared against one or more keys that are already in the dictionary, using
- the comparison function. The key pointer may be identical to one that has
- already been inserted into the dictionary.
-
- If the key is found in the dictionary, a pointer to the corresponding node
- is returned.
-
- If the key is not found, a null pointer is returned.
-
- If the dictionary contains more than one key which matches the search
- key, then the first key in the subsequence of duplicate keys is returned.
-
-\subsubsection{The {\tt dict_lower_bound} function}
-
- \indexfunc{dict_lower_bound}
-
- \synopsis
- \begin{verbatim}
- dnode_t *dict_lower_bound(dict_t *, const void *);\end{verbatim}
-
- \description
-
- The \verb|dict_lower_bound| function searches the dictionary in a manner
- similar to \verb|dict_lookup|.
-
- If the given key exists in the dictionary, the behavior is exactly the same
- as \verb|dict_lookup|.
-
- However, if the key is not found, then the node which has the smallest key
- that is greater than the search key is returned. If no such key exists
- (because the search key is higher than any other key in the dictionary
- or the dictionary is empty) then a null pointer is returned.
-
- \example
- Suppose that pointer \verb|d| refers to a dictionary whose registered
- comparison function performs lexicographic comparisons on ordinary
- C strings, similar to \verb|strcmp|. To iterate over all keys that
- begin with the letter \verb|d|, the following idiom can be used:
- \begin{verbatim}
- dict_t *d;
- dnode_t *n, *start, *end;
- /*...*/
- start = dict_lower_bound(d, "d");
- end = dict_lower_bound(d, "e");
- for (n = start; n != end; n = dict_next(d, n)) {
- /* n points to each node in turn whose
- key starts with 'd' */
- }
- \end{verbatim}
- Note that if the dictionary is empty, or has keys which are all lower
- than \verb|"d"|, then both \verb|start| and \verb|end| shall be null
- pointers, and the loop body will never execute since the two are equal.
- Also note that if there are keys that begin with \verb|d| and the
- dictionary's last node has a key that starts with \verb|d|, then \verb|end|
- is null, otherwise \verb|end| points to the first key that doesn't begin
- with \verb|d|. In both cases, the loop will terminate after processing the
- last \verb|d| key, because \verb|dict_next| shall produce a pointer that is
- equal to \verb|end|.
-
-\subsubsection{The {\tt dict_upper_bound} function}
-
- \indexfunc{dict_upper_bound}
-
- \synopsis
- \begin{verbatim}
- dnode_t *dict_upper_bound(dict_t *, const void *);\end{verbatim}
-
- \description
-
- The \verb|dict_upper_bound| function searches the dictionary in a manner
- similar to \verb|dict_lookup|.
-
- If the given key exists in the dictionary, the behavior is exactly the same
- as \verb|dict_lookup| with one difference:
- If the dictionary contains more than one key which matches the search
- key, then the last key in the sequence of duplicates is returned,
- rather than the first.
-
- However, if the key is not found, then the node which has the greatest key
- that is lower than the search key is returned. If no such key exists
- (because the search key is lower than any other key in the dictionary
- or the dictionary is empty) then a null pointer is returned.
-
- \example
- The following idiom can be used to iterate over a sequence of duplicate
- keys without the overhead of performing a full comparison before each
- iteration to detect the first non-matching key.
- \begin{verbatim}
- dict_t *d;
- void *key;
- dnode_t *n, *start, *end;
-
- /* ... Initialize d, and key. ...*/
- start = dict_lower_bound(d, key);
- end = dict_upper_bound(d, key);
-
- /* advance end to first non-matching key */
- if (end != 0)
- end = dict_next(d, end);
- else
- end = start; /* start == dict_first(d) in this case */
-
- for (n = start; n != end; n = dict_next(d, n)) {
- /* n points to duplicate keys in turn */
- }
- \end{verbatim}
- Immediately prior to the execution of the if statement, exactly one of the
- following conditions is true:
- \begin{itemize}
- \item The key was found in the dictionary; \verb|start| points to the
- first duplicate node and \verb|end| points to the last.
- \item The dictionary has only higher keys than the search key; \verb|start|
- points to the first node in the dictionary and \verb|end| is null.
- \item The dictionary has only lower keys than the search key; \verb|end|
- points to the last node in the dictionary, and \verb|start| is null.
- \item The dictionary has both lower and higher keys; \verb|end| and \verb|start|
- point to two consecutive nodes, respectively, such that the node
- pointed at by \verb|end| has a lower key than the search key and
- the node pointed at by \verb|start| has a higher key.
- \item The dictionary is empty; \verb|start| and \verb|end| are null.
- \end{itemize}
- The if statement ensures that if the dictionary contains no matching
- keys, than \verb|start| and \verb|end| are equal, and if the dictionary
- contains one or more matching keys, than \verb|end| points to the first
- non-matching node, or is null if there is no such node. Thus the loop
- performs correctly in all circumstances.
-
-\subsubsection{The {\tt dict_insert} function}
-
- \label{section:dict_insert}
- \indexfunc{dict_insert}
-
- \synopsis
- \begin{verbatim}
- void dict_insert(dict_t *, dnode_t *, const void *);\end{verbatim}
-
- \constraints
- The dictionary is not full. If the dictionary has not been configured
- to allow duplicate keys, the key specified by the \verb|void *| parameter
- does not already exist in the dictionary.
-
- \description
- The \verb|dict_insert| function adds a new node to a dictionary. The user
- must supply a node object that was initialized with \verb|dnode_init| or
- dynamically created with \verb|dnode_create|. If the node is already
- inserted into the same dictionary or any other dictionary, the behavior is
- undefined.
-
- Duplicate keys may be inserted into a dictionary only if the dictionary
- has been configured to permit duplicate keys (see section
- \ref{section:dict_allow_dupes}). If this is the case, it is also
- permissible to insert the same key more than once: the implementation shall
- not distinguish between distinct keys that are declared equal by a
- correctly designed comparison function, and two key pointers that refer to
- the same key.
-
- A program may modify a key or node that has been inserted into a
- dictionary, or cause the storage of the key or the node to become invalid.
- However, any subsequent use of the dictionary invokes undefined behavior, with
- the following exception: the data pointer stored within a node may be
- modified using the \verb|dnode_put| function.
-
- The Dictionary implementation shall not modify the storage referenced by a
- key, and shall not access it other than indirectly through the supplied
- comparison function.
-
-\subsubsection{The {\tt dict_delete} function}
-
- \indexfunc{dict_delete}
-
- \synopsis
- \begin{verbatim}
- dnode_t *dict_delete(dict_t *, dnode_t *);\end{verbatim}
-
- \constraints
- The specified node is an occupant of the given dictionary.
-
- \description
- The \verb|dict_delete| function removes from the given dictionary a
- node that has previously been inserted into it. The key under
- which the node was inserted is also removed from the dictionary.
-
-\subsubsection{The {\tt dict_alloc_insert} function}
-
- \label{section:dict_alloc_insert}
- \indexfunc{dict_alloc_insert}
-
- \synopsis
- \begin{verbatim}
- int dict_alloc_insert(dict_t *, const void *, void *);\end{verbatim}
-
- \constraints
-
- The second argument specifies the insertion key. The dictionary shall not
- already contain this key unless it has been configured as allowing
- duplicates.
-
- \description
-
- The \verb|dict_alloc_insert| function dynamically allocates and
- initializes a \verb|dnode_t| object and inserts it into the
- given dictionary. The second argument and third arguments are pointers
- to user data and key objects, either of which may be null.
-
- The allocation is performed by a call to the default allocation
- function, or to the function that was configured using
- \verb|dict_set_allocator| (Section \ref{section:dict_set_allocator}).
-
- If the allocation succeeds, the insertion is performed and
- the value 1 is returned. If the allocation fails, no insertion is
- performed and 0 is returned.
-
-\subsubsection{The {\tt dict_delete_free} function}
-
- \label{section:dict_delete_free}
- \indexfunc{dict_delete_free}
-
- \synopsis
- \begin{verbatim}
- void dict_delete_free(dict_t *, dnode_t *);\end{verbatim}
-
- \constraints
- The given node can be found within the given dictionary.
-
- \description
- The \verb|dict_delete_free| function is the reverse of
- \verb|dict_alloc_insert|. It removes the given node form the
- dictionary and then deletes it using the default or user-defined allocator
- (Section \ref{section:dict_set_allocator}). If the given node
- had not been created using \verb|dict_alloc_insert|, the behavior
- is undefined.
-
-\subsubsection{The {\tt dict_first} function}
-
- \indexfunc{dict_first}
-
- \synopsis
- \begin{verbatim}
- dnode_t *dict_first(dict_t *);\end{verbatim}
-
- \description
- If the dictionary pointed at by the argument is empty, a null pointer
- is returned. Otherwise, a pointer to the first node in that dictionary is
- returned.
-
-\subsubsection{The {\tt dict_last} function}
-
- \indexfunc{dict_last}
-
- \synopsis
- \begin{verbatim}
- dnode_t *dict_last(dict_t *);\end{verbatim}
-
- \description
- If the dictionary pointed at by the argument is empty, a null pointer
- is returned. Otherwise, a pointer to the last node in that dictionary is
- returned.
-
-
-\subsubsection{The {\tt dict_next} function}
-
- \indexfunc{dict_next}
-
- \synopsis
- \begin{verbatim}
- dnode_t *dict_next(dict_t *, dnode_t *);\end{verbatim}
-
- \constraints
- The node pointed at by the second argument is an occupant of the dictionary
- pointed at by the first argument.
-
- \description
- If the node pointed at by the second argument has a successor, a pointer to
- that successor is returned. Otherwise, a null pointer is returned.
-
- \example
- The \verb|dict_first| and \verb|dict_next| functions can be used together
- to iterate over all of the elements of the dictionary, as in the following
- idiom:
- \begin{verbatim}
- dict_t *d;
- dnode_t *n;
- /*...*/
- for (n = dict_first(d); n != 0; n = dict_next(d, n)) {
- /* n points to each node in turn */
- }
- \end{verbatim}
-
-\subsubsection{The {\tt dict_prev} function}
-
- \indexfunc{dict_prev}
-
- \synopsis
- \begin{verbatim}
- dnode_t *dict_prev(dict_t *, dnode_t *);\end{verbatim}
-
- \constraints
- The node pointed at by the second argument is an occupant of the dictionary
- pointed at by the first argument.
-
- \description
- If the node pointed at by the second argument has a predecessor, a pointer
- to that predecessor is returned. Otherwise, a null pointer is returned.
-
-\subsubsection{The {\tt dict_count} function}
-
- \indexfunc{dict_count}
-
- \synopsis
- \begin{verbatim}
- dictcount_t dict_count(dict_t *);\end{verbatim}
-
- \description
- The \verb|dict_count| function returns a value which represents the number
- of nodes currently stored in the dictionary pointed at by the argument.
-
-\subsubsection{The {\tt dict_isempty} function}
-
- \indexfunc{dict_isempty}
-
- \synopsis
- \begin{verbatim}
- int dict_isempty(dict_t *);\end{verbatim}
-
- \description
- The \verb|dict_isempty| function returns 1 if the given dictionary is
- empty, otherwise it returns 0.
-
-\subsubsection{The {\tt dict_isfull} function}
-
- \indexfunc{dict_isfull}
-
- \synopsis
- \begin{verbatim}
- int dict_isfull(dict_t *);\end{verbatim}
-
- \description
- The \verb|dict_isfull| function returns 1 if the dictionary is full,
- otherwise it returns 0.
-
- If the argument is an expression with side effects, the behavior is
- undefined.\index{macros!and side effects}
-
-\subsubsection{The {\tt dict_contains} function}
-
- \indexfunc{dict_contains}
-
- \synopsis
- \begin{verbatim}
- int dict_contains(dict_t *, dnode_t *);\end{verbatim}
-
- \description
- The \verb|dict_contains| function searches the given dictionary to
- determine whether the given node is an occupant. If the node is found, 1 is
- returned, otherwise 0 is returned.\footnote{The intent is to support
- verification. The search may be inefficient compared to {\tt
- dict_lookup}.}
-
-\subsubsection{The {\tt dict_allow_dupes} function}
-
- \label{section:dict_allow_dupes}
- \indexfunc{dict_allow_dupes}
-
- \synopsis
- \begin{verbatim}
- void dict_allow_dupes(dict_t *);\end{verbatim}
-
- \constraints
- The dictionary specified by the first argument shall be empty.
-
- \description
- The \verb|dict_allow_dupes| function configures the given dictionary to
- support duplicate keys. This can only be done when the dictionary is empty,
- and the change cannot be reverted.
-
-\subsubsection{The {\tt dnode_is_in_a_dict} function}
-
- \indexfunc{dnode_is_in_a_dict}
-
- \synopsis
- \begin{verbatim}
- int dnode_is_in_a_dict(dnode_t *);\end{verbatim}
-
- \description
- The \verb|dnode_is_in_a_dict| function reports whether the given node
- is currently the occupant of some dictionary. If so, 1 is returned.
- Otherwise 0 is returned.
-
-\subsubsection{The {\tt dnode_create} function}
-
- \indexfunc{dnode_create}
-
- \synopsis
- \begin{verbatim}
- dnode_t *dnode_create(void *);\end{verbatim}
-
- \description
- The \verb|dnode_create| function dynamically allocates a dictionary node,
- stores in it the data value specified in the argument and
- returns a pointer to it. The allocation is performed by a call to the
- standard \verb|malloc| function. If the allocation fails, a null
- pointer is returned.
-
- The node's key pointer remains indeterminate until it is the subject of a
- \verb|dict_insert| operation.
-
-\subsubsection{The {\tt dnode_init} function}
-
- \indexfunc{dnode_init}
-
- \synopsis
- \begin{verbatim}
- dnode_t *dnode_init(dnode_t *, void *);\end{verbatim}
-
- \description
- The \verb|dnode_init| function initializes the contents
- of the specified dictionary node object, assigning it the
- data value specified as the second argument.
- The first argument is a pointer which refers to
- a data object that has a suitable size and alignment
- for the representation of an \verb|dnode_t| type.
- After initialization with \verb|dnode_init|, the object is subsequently
- eligible as an operand to the functions of the dictionary component,
- other than \verb|dnode_getkey|.
-
- The node's key pointer remains indeterminate until it is the subject of a
- \verb|dict_insert| operation.
-
-\subsubsection{The {\tt dnode_destroy} function}
-
- \indexfunc{dnode_destroy}
-
- \synopsis
- \begin{verbatim}
- void dnode_destroy(dnode_t *);\end{verbatim}
-
- \description
- The \verb|dnode_destroy| function destroys a dictionary node that has been
- allocated with \verb|dnode_create|. The value of any pointer
- that referred to the node that was thus freed is indeterminate.
-
- If the node is currently the occupant of a dictionary, the behavior is
- undefined if the hash is subsequently used.
-
-\subsubsection{The {\tt dnode_get} function}
-
- \indexfunc{dnode_get}
-
- \synopsis
- \begin{verbatim}
- void *dnode_get(dnode_t *);\end{verbatim}
-
- \description
- The \verb|dnode_get| function retrieves the \verb|void * | data value
- associated with the given dictionary node.
-
-\subsubsection{The {\tt dnode_getkey} function}
-
- \indexfunc{dnode_getkey}
-
- \synopsis
- \begin{verbatim}
- const void *dnode_getkey(dnode_t *);\end{verbatim}
-
- \description
-
- The \verb|dnode_getkey| function retrieves the \verb|void *| key value
- associated with the given node. A node acquires an associated key
- when it is inserted into a dictionary (see section \ref{section:dict_insert}).
- Invoking \verb|dnode_getkey| on a node that has not been inserted
- into a dictionary results in undefined behavior.
-
-\subsubsection{The {\tt dnode_put} function}
-
- \indexfunc{dnode_put}
-
- \synopsis
- \begin{verbatim}
- void dnode_put(dnode_t *, void *);\end{verbatim}
-
- \description
- The function \verb|dnode_put| replaces the data element
- associated with the dictionary node.
-
-\subsubsection{The {\tt dict_process} function}
-
- \label{section:dict_process}
- \indexfunc{dict_process}
-
- \synopsis
- \begin{verbatim}
- void dict_process(dict_t *, void *, dnode_process_t);\end{verbatim}
-
- \description
- The \verb|dict_process| function iterates over the nodes of a dict,
- and for each node invokes a callback function.\footnote{In most cases,
- it is more convenient and preferable to
- iterate over the dict using explicit calls to {\tt dict_first}
- and {\tt dict_next}.}
- The second argument is a {\it context pointer\/} which can have any value.
- The third argument of
- \verb|dict_process| shall be a pointer to a function which is compatible
- with the specified type. If the dict contains one or more nodes,
- then the function is invoked once for each node, in order from first
- to last. On each invocation, the first argument of the callback is a
- pointer to the dict; the second argument is a pointer to a node, called
- the {\it subject node}; and the third argument repeats the context pointer
- value that was originally passed to \verb|dict_process|.
-
- The callback function may delete the subject node by, for instance, calling
- \verb|dict_delete|. It may insert new nodes into the dictionary;
- however, if such an insertion causes the subject node to acquire
- a new successor, it is implementation-defined whether upon returning
- from the callback function, the traversal shall continue with the
- new successor, or with the original successor.
-
- The callback function, and any function invoked from the callback
- function, shall not destroy the dictionary or make any modifications
- other than the insertion of new nodes, or the deletion of the
- subject node.
-
- The callback function may recursively invoke \verb|dict_process| for the
- same dictionary or for a different dictionary; the callback invocations arising out of
- the nested call inherit all of the restrictions of the outer callback in
- addition to being subject to the usual restrictions.\footnote{This means,
- for instance, that if two callbacks are in progress for different
- subject nodes from the same dictionary, the inner callback may not delete
- its subject node, because it inherits the restriction that the only
- permitted deletion is the outer callback's subject node.}
-
- The callback function may freely operate on a different dictionary,
- subject to any inherited restrictions.
-
-\subsubsection{The {\tt dict_load_begin} function}
-
- \label{section:dict_load_begin}
- \indexfunc{dict_load_begin}
-
- \synopsis
- \begin{verbatim}
- void dict_load_begin(dict_load_t *, dict_t *);\end{verbatim}
-
- \constraints
- The dictionary specified by the second argument is empty.
-
- \description
- The \verb|dict_load_begin| function prepares a context object
- for the task of constructing the contents of a dictionary out of
- a sequence of elements which is already sorted according to the
- sorting function of the dictionary.\footnote{This process is more efficient
- than inserting all of the elements into a dictionary using {\tt dict_insert}.
- In the reference implementation, this process runs in linear time, or $O(n)$
- whereas construction by repeated insertions runs in $O(n\log n)$ time.}
- The actual construction is performed
- by zero or more calls to \verb|dict_load_next| and is finalized by
- \verb|dict_load_end|.
-
- The \verb|dict_load_begin| function is said to bind the dictionary
- and context object together; the only way to unbind the two
- is by calling \verb|dict_load_end| on the context object.
-
- The program shall not manipulate a dictionary that is bound to
- a context object, other than by calling \verb|dict_load_next|.
-
- The program shall not attempt to bind a dictionary to more than one context
- object simultaneously, or a context object to more than one dictionary
- simultaneously.
-
-\subsubsection{The {\tt dict_load_next} function}
-
- \label{section:dict_load_next}
- \indexfunc{dict_load_next}
-
- \synopsis
- \begin{verbatim}
- void dict_load_next(dict_load_t *, dnode_t *, const void *);\end{verbatim}
-
- \constraints
- The node pointed at by the second argument is not an occupant of
- any dictionary. The key specified by the third argument is greater
- than or equal to all keys specified in previous calls to
- \verb|dict_load_next| in the context of the same construction,
- according to the comparison function of the dictionary that is
- being constructed. That is to say, successive calls specify monotonically
- increasing keys.
- The dictionary is not full.
-
- \description
- The \verb|dict_load_next| function continues the construction of a
- dictionary from an ordered list of elements by specifying the next
- node in the sequence, along with its key. After this call, the node
- is considered to be inserted into the dictionary as if by
- \verb|dict_insert|.
-
-\subsubsection{The {\tt dict_load_end} function}
-
- \label{section:dict_load_end}
- \indexfunc{dict_load_end}
-
- \synopsis
- \begin{verbatim}
- void dict_load_end(dict_load_t *);\end{verbatim}
-
- \description
- The \verb|dict_load_end| function finalizes the construction of
- a dictionary from a ordered sequence. It breaks the binding between
- the \verb|dict_load_t| context object and the dictionary.
-
-\subsection{Implementation}
-
-TODO
-
-\section{Exception component}
-\label{section:exception_component}
-\index{Exception}
-
-The Exception component provides distributed error handling in the form of
-exceptions, behind an interface designed to be implementable using only the
-portable features of standard C. The features of this interface are:
-\begin{itemize}
-\item the ability to set up nested try-catch regions which declare specific
-exceptions that they can handle;
-\item grouped exceptions, allowing handlers to catch specific exceptions,
-or any exception within a group;
-\item the ability to designate a function that is called in the event
-that an exception is thrown that has no handler.
-\item a mechanism for releasing resources acquired by code that is terminated
-by an exception;
-\item the ability to pass dynamically allocated data from the throw site to the
-catch site.
-\end{itemize}
-
-An exception is simply a means of returning to a prior place in the program's
-execution. The ANSI C language provides crude, but portable, exception handling
-consisting of the \verb|jmp_buf| type, the \verb|setjmp| macro and the
-\verb|longjmp| function. The Kazlib Exception component can be implemented in
-terms of these primitives. The constraint to implementability in standard C
-leads to a number of concessions:
-\begin{itemize}
-\item A program can leave cleanup regions and try-catch regions by improper
-means, such as using \verb|goto|, \verb|return| or \verb|break|. This is
-difficult to diagnose, and is simply documented as undefined behavior.
-There is no support in the standard language for designating code that is
-executed whenever a statement block terminates by any means.
-\item For the same reason, the exception handling interface described here
-has an explicit mechanism for deallocation of resources associated with
-statement blocks that are terminated by exceptions. This interface is
-not as convenient as language support for automatic cleanup. Correct
-management of temporary dynamic resources using this interface requires
-programmer discipline.
-\item The requirement to be able to use \verb|setjmp| to save a context
-to be later returned to during exception processing brings in restrictions
-related to non-volatile objects. If non-volatile objects are modified
-between the time an exception handling region is initiated and the time
-an exception is caught in the region, these objects have indeterminate
-values.\footnote{This liberty in ANSI C allows compiler
-or library writers to implement {\tt setjmp} as a simple mechanism that
-takes a snapshot of the machine context. Objects that are optimized into
-special storage---such as registers---and whose values change since the
-context saving operation will be clobbered when the context is restored
-by {\tt longjmp}.}
-\end{itemize}
-
-\subsection{Interface}
-
-\subsubsection{The {\tt except.h} header}
-
-Each C or C++ translation unit that is to use the functionality of the Exception
-component shall include the header \verb|except.h|. This header shall
-contain declarations of types and external functions, and definitions of
-macros. The following typedef names shall be
-defined:\index{Exception!typedef names}
-\begin{verbatim}
- except_id_t
- except_t
-\end{verbatim}
-The following external function names shall be declared:
-\index{Exception!function names}\index{functions!defined by Exception}
-\begin{verbatim}
- except_init except_group
- except_deinit except_message
- except_rethrow except_data
- except_throw except_take_data
- except_throwd except_set_allocator
- except_throwf except_alloc
- except_unhandled_catcher except_free
- except_code
-\end{verbatim}
-The following preprocessor symbols shall be
-defined: \index{Exception!macro names}\index{macros!defined by Exception}
-\indexmacro{XCEPT_H}
-\begin{verbatim}
- XCEPT_H except_cleanup_pop
- XCEPT_GROUP_ANY except_checked_cleanup_pop
- XCEPT_CODE_ANY except_try_push
- XCEPT_BAD_ALLOC except_try_pop
- except_cleanup_push
-\end{verbatim}
-Finally, these two enum constants are defined:
-\begin{verbatim}
- except_no_call
- except_call
-\end{verbatim}
-\index{symbols!reserved by Exception}\index{Exception!reserved symbols} Macro
-identifiers which begin with the upper-case prefix \verb|XCEPT|\footnote{The
-prefix {\tt XCEPT} is used rather than {\tt EXCEPT} because ISO 9899 reserves
-preprocessor symbols beginning with {\tt E} followed by a digit or
-capital letter for future extensions to the {\tt <errno.h>} header.}
-are reserved for future extensions to the \verb|except.h|
-header, as are names in the ordinary and tag namespaces which begin with
-\verb|except_|. External names which begin with \verb|except_| are reserved by
-the Kazlib library regardless of what headers are included.
-
-\subsubsection{The {\tt except_id_t} type}
-
-\label{section:except_id_t}
-\indextype{except_id_t}
-\indexmacro{XCEPT_GROUP_ANY}
-\indexmacro{XCEPT_CODE_ANY}
-The type \verb|except_id_t| is an aggregate consisting of two unsigned long
-values which represent an {\it exception group\/} and {\it exception code},
-respectively, in that order.\footnote{Thus, the program may initialize
-an {\tt except_id_t} object using two brace-enclosed initializers which
-specify the group and code.} An exception group is a value which identifies a
-group of related exceptions. An exception code is a value which identifies a
-specific exception uniquely within a group. The codes are assigned by the
-program designer. The Exception component reserves only the group and code
-values of zero, which, when used to specify a catch, match any value.
-
-The preprocessor symbols \verb|XCEPT_GROUP_ANY| and
-\verb|XCEPT_CODE_ANY| each expand to a constant integral expression having the
-value zero. These symbols are intended, in a catch specification, to clearly
-convey that any exception or any group is being caught.
-
-The preprocessor symbol \verb|XCEPT_BAD_ALLOC| expands to an integral constant
-expression having the value 1. This symbol is intended to represent the
-standard exception group for failed memory allocations.
-(See section \ref{section:except_throwf}).
-
-The exception groups from 1 to 15 are reserved for implementation use.
-
-\subsubsection{The {\tt except_t} type}
-
-\indextype{except_t}
-An object of type \verb|except_t| keeps track of all of the information that is
-passed when an exception is thrown, and is known as an {\it exception
-descriptor}. The type is opaque, hence the program shall manipulate this type
-using only the interface functions provided.
-
-\subsubsection{The {\tt except_init} function}
-
- \indexfunc{except_init}
-
- \synopsis
- \begin{verbatim}
- int except_init(void);\end{verbatim}
-
- \description
- The \verb|except_init| function allocates resources needed by the
- Exception component. Before using any of the other exception interface
- functions or macros, the program shall perform at least one successful call
- to \verb|except_init|.
-
- If the initialization succeeds, \verb|except_init| returns 1. Otherwise
- it returns 0.
-
- The \verb|except_init| function may be called more than once. After a
- successful call, every subsequent call shall be successful up to an
- implementation-defined maximum number of repetitions, which shall be at least
- as large as the \verb|INT_MAX| from \verb|limits.h|. \footnote{
- The intent is to support, but not enforce, a style of global initialization
- whereby each module which requires the use of another module calls its
- initialization function from its own initialization function. Only the
- first such call performs the initialization of the module; subsequent calls
- merely increment a counter. During deinitialization, the counter is
- decremented and cleanup takes place when the counter reaches zero.}
-
-\subsubsection{The {\tt except_deinit} function}
-
- \indexfunc{except_deinit}
-
- \synopsis
- \begin{verbatim}
- void except_deinit(void);\end{verbatim}
-
- \description
- The \verb|except_deinit| function releases the resources
- that were allocated by \verb|except_init|.
-
- For the resource deallocation to actually take place, the
- \verb|except_deinit| must be called as many times as the
- number of times \verb|except_init| was successfully called.
-
- If \verb|except_deinit| is called more times than \verb|except_init| is
- successfully called, the behavior is undefined.
-
-\subsubsection{The {\tt except_rethrow} function}
-
- \indexfunc{except_rethrow}
-
- \synopsis
- \begin{verbatim}
- void except_rethrow(except_t *);\end{verbatim}
-
- \description
- The rethrow function is used to rethrow a caught exception. The argument
- shall not be null. An exception shall not be rethrown from outside of the
- {\it try-catch region\/} in which it was caught. An exception shall not be
- rethrown from a try-catch region other than the one in which it was caught.
- It shall not be rethrown from a try-catch or cleanup region enclosed within
- the one in which it was caught.
-
- When an exception is rethrown, the search for a handler does not begin with
- the region in which the exception was caught. Instead, this region is
- terminated, and the search continues with the enclosing one, if one
- exists.
-
-\subsubsection{The {\tt except_throw} function}
-
- \indexfunc{except_throw}
-
- \synopsis
- \begin{verbatim}
- void except_throw(long, long, const char *);\end{verbatim}
-
- \constraints
- The first two arguments specify the exception group and code,
- respectively. Neither of these arguments shall be zero.
-
- \description
- The \verb|except_throw| function causes an exception to be thrown.
-
- If the throw takes place in a try-catch region where an exception
- was just caught, this original exception is considered handled. In
- this case, the new exception is still eligible for handling by the
- same try-catch region.
-
- The third argument points to the first character of a string
- which becomes the {\it exception message}. Because the throwing of
- the exception may cause the current statement block to terminate,
- this string data shall be non-local. It may be a string literal, since the
- implementation shall not modify the message, or it may be an ordinary
- object of static duration. If it is dynamic data, it becomes the handler's
- responsibility to extract the message from the caught exception and
- free the data.\footnote{The programmer should consider using
- {\tt except_throwd} to pass arbitrary dynamic data from the throw
- site to the try-catch region.}
-
- The \verb|except_throw| function does not return. The implementation
- searches for a suitable try-catch region starting with the one
- initiated by the most recent \verb|except_try_push|. If there
- is no enclosing region, the search fails. Otherwise if a match is found,
- execution continues at the start of the target try-catch region, appearing
- to be a second return from \verb|except_try_push| distinguished by a non-null
- value of the \verb|except_t *| object.
-
- If no match is found during exception processing, the exception is
- handled internally by the implementation. The implementation then
- calls the currently registered function for catching unhandled
- exceptions (see section \ref{section:except_unhandled_catcher}).
-
- The default catcher for unhandled exceptions shall terminate the program
- with a diagnostic which identifies the code, group and exception message.
-
- During the search for an exception handler, cleanup handlers may be
- encountered. They are removed from the inside out and called with
- their registered arguments. This process is called {\it unwinding}.
- \index{unwinding}
-
-\subsubsection{The {\tt except_throwd} function}
-
- \indexfunc{except_throwd}
-
- \synopsis
- \begin{verbatim}
- void except_throwd(long, long, const char *, void *);\end{verbatim}
-
- \constraints
- The first two arguments specify the exception group and code,
- respectively. Neither of these arguments shall be zero.
-
- \description
- The \verb|except_throwd| function is the same as \verb|except_throw| in
- every respect except that it has an additional \verb|void *| parameter. A
- null argument may be used for this parameter, or it may be any valid
- pointer value.
-
- When the exception is handled, and the handler does not remove this pointer
- using \verb|except_take_data| then the implementation shall automatically
- invoke the function \verb|except_free| on this pointer.
-
-\subsubsection{The {\tt except_throwf} function}
-
- \indexfunc{except_throwf}
- \label{section:except_throwf}
-
- \synopsis
- \begin{verbatim}
- void except_throwf(long, long, const char *, ...);\end{verbatim}
-
- \constraints
- The first two arguments specify the exception group and code,
- respectively. Neither of these arguments shall be zero.
-
- \description
-
- This function is almost exactly the same as \verb|except_throw|
- except that the exception message is not directly specified.
- Instead, the \verb|char *| argument specifies a format string which may be
- followed by trailing arguments. The format string and trailing arguments
- are interpreted as the format string and arguments of the standard C
- function \verb|printf| and are subject to the same requirements.
-
- The format string is interpreted, and the results of formatting are placed into
- buffer provided by the implementation. The implementation shall provide
- space for at least 1024 bytes of storage for the result of the formatting,
- including the null terminator byte. If the formatting requires more space
- than the implementation provides, the behavior is undefined.
-
- The results of the formatted print shall become the exception message
- of the thrown exception.
-
- If the implementation is unable to allocate resources for the formatted
- message, it shall throw a code 1 exception having an unspecified code in
- group \verb|XCEPT_BAD_ALLOC| with an implementation-defined message.
- (See section \ref{section:except_id_t}).
-
-\subsubsection{The {\tt except_unhandled_catcher} function}
-
- \label{section:except_unhandled_catcher}
- \indexfunc{except_unhandled_catcher}
-
- \synopsis
- \begin{verbatim}
- void (*except_unhandled_catcher(void (*)(except_t *)))
- (except_t *);\end{verbatim}
-
- \description
- The \verb|except_unhandled_catcher| function installs a new
- function for catching unhandled exceptions. The argument is a
- pointer to a catching function that returns nothing, and accepts a pointer
- of type \verb|except_t *|. A pointer to the previously installed
- catching function is returned. If the program did not previously
- install a catching function, then a pointer to the default catching
- function is returned. The program may retain this pointer and
- use it to reinstall the default function.
-
- A function for catching unhandled exceptions should not return. If it
- returns, the implementation shall terminate the program with a diagnostic.
-
-\subsubsection{The {\tt except_code} function}
-
- \indexfunc{except_code}
-
- \synopsis
- \begin{verbatim}
- unsigned long except_code(except_t *);\end{verbatim}
-
- \description
- The \verb|except_code| is an accessor function which returns the
- exception code of the given exception descriptor.
-
-\subsubsection{The {\tt except_group} function}
-
- \indexfunc{except_group}
-
- \synopsis
- \begin{verbatim}
- unsigned long except_group(except_t *);\end{verbatim}
-
- \description
- The \verb|except_group| is an accessor function which returns the
- exception group of the given exception descriptor.
-
-\subsubsection{The {\tt except_message} function}
-
- \indexfunc{except_message}
-
- \synopsis
- \begin{verbatim}
- const char *except_message(except_t *);\end{verbatim}
-
- \description
- The \verb|except_group| is an accessor function which returns
- a pointer to the string of text that was specified when the
- exception was thrown (the exception message).
-
-\subsubsection{The {\tt except_data} function}
-
- \indexfunc{except_data}
-
- \synopsis
- \begin{verbatim}
- void *except_data(except_t *);\end{verbatim}
-
- \description
- The \verb|except_group| returns the data pointer that
- was specified in the \verb|except_throwd| call.
- If the exception was not thrown by \verb|except_throwd|
- the return value is unspecified.
-
-
-\subsubsection{The {\tt except_take_data} function}
-
- \indexfunc{except_take_data}
-
- \synopsis
- \begin{verbatim}
- void *except_take_data(except_t *);\end{verbatim}
-
- \description
- The \verb|except_take_data| returns the data pointer that
- was specified in the \verb|except_throwd| call, and
- updates the exception descriptor so that the pointer is
- set to null.
-
- If the exception was not thrown by \verb|except_throwd|
- the result is unspecified.
-
-\subsubsection{The {\tt except_cleanup_push} macro}
-
- \indexmacro{except_cleanup_push}
-
- \synopsis
- \begin{verbatim}
- void except_cleanup_push(void (*)(void *), void *);\end{verbatim}
-
- \description
- The call to \verb|except_cleanup_push| shall be matched with a call to
- \verb|except_cleanup_pop| which must occur in the same statement block at
- the same level of nesting.\footnote{This requirement allows an implementation
- to provide an {\tt except_cleanup_push} macro which opens up a statement
- block and a {\tt except_cleanup_pop} which closes the statement block.
- The space for the registered pointers can then be efficiently allocated
- from automatic storage.}
-
- The \verb|except_cleanup_push| macro registers a cleanup handler that will
- be called if an exception subsequently occurs before the matching
- \verb|except_cleanup_pop| is executed, and is not intercepted and handled by
- a try-catch region that is nested between the two.
-
- The first argument to \verb|except_cleanup_push| is a pointer
- to the cleanup handler, a function that returns nothing and takes
- a single argument of type \verb|void *|. The second argument
- is a \verb|void *| value that is registered along with the handler.
- This value is what is passed to the registered handler, should it
- be called.
-
- Cleanup handlers are called in the reverse order of their nesting: inner
- handlers are called before outer handlers.
-
- The program shall not leave the cleanup region between the call to the macro
- \verb|except_cleanup_push| and the matching call to
- \verb|except_cleanup_pop| by means other than throwing an exception, or
- calling \verb|except_cleanup_pop|.
-
- Within the call to the cleanup handler, it is possible that new exceptions
- may happen. Such exceptions must be handled before the cleanup handler
- terminates. If the call to the cleanup handler is terminated by an
- exception, the behavior is undefined.\footnote{The exception which triggered
- the cleanup is not yet caught; thus the program would be effectively trying
- to replace an exception with one that isn't in a well-defined state.}
-
-\subsubsection{The {\tt except_cleanup_pop} macro}
-
- \indexmacro{except_cleanup_pop}
- \label{section:except_cleanup_pop}
-
- \synopsis
- \begin{verbatim}
- void except_cleanup_pop(int);\end{verbatim}
-
- \description
- A call to the \verb|except_cleanup_pop| macro shall match each
- call to \verb|except_cleanup_push| which shall be in the
- same statement block at the same nesting level. It shall
- match the most recent such a call that is not matched
- by a previous \verb|except_cleanup_pop| at the same level.
-
- This macro causes the registered cleanup handler to be removed. If, and
- only if the argument is other than zero, the cleanup handler is called.
- In that case, the registered context pointer is passed to the cleanup
- handler.
-
- \indexenum{except_no_call}
- \indexenum{except_call}
- The enumeration constants \verb|except_no_call| and \verb|except_call|
- may be used as arguments to this function instead of
- the equivalent constants \verb|0| and \verb|1|.
-
- The program shall not leave the region between the call to the macro
- \verb|except_cleanup_push| and the matching call to
- \verb|except_cleanup_pop| other than by throwing an exception, or
- by executing the \verb|except_cleanup_pop|.
-
-\subsubsection{The {\tt except_checked_cleanup_pop} macro}
-
- \indexmacro{except_checked_cleanup_pop}
-
- \synopsis
- \begin{verbatim}
- void except_checked_cleanup_pop(void (*)(void *), int);\end{verbatim}
-
- \constraints
- The first pointer-to-function argument shall match the pointer value that
- was registered by the matching \verb|except_cleanup_push| macro.
-
- \description
- The \verb|except_checked_cleanup_pop| macro may be used as an alternative to
- \verb|except_cleanup_pop|. In verification mode, the constraint serves to
- provide additional safety by making an explicit declaration regarding which
- handler is being called (or ignored, as the case may be).
-
- The program shall not leave the region between the call to the macro
- \verb|except_cleanup_push| and the call to
- \verb|except_checked_cleanup_pop| by means other than throwing an
- exception, or executing the latter macro.
-
-\subsubsection{The {\tt except_try_push} macro}
-
- \indexmacro{except_try_push}
- \label{section:except_try_push}
-
- \synopsis
- \begin{verbatim}
- void except_try_push(const except_id_t [],
- size_t, except_t **);\end{verbatim}
-
- \description
- The \verb|except_try_push| marks the beginning of a try-catch region
- of the program. It must be matched by a \verb|except_try_pop| written in
- the same statement block at the same level of nesting, which
- terminates the try-catch region. Regions may be nested.
-
- The program shall not leave a try-catch region other than by throwing
- an exception or by executing the \verb|except_try_pop|.\footnote{Thus,
- leaving the try-catch region using {\tt goto}, {\tt return},
- {\tt break} or {\tt continue} leads to undefined behavior.}
-
- The first argument is a pointer to the first element of an array of
- \verb|except_id_t| objects, the number of elements of which is specified by
- the second argument. The array specifies which exceptions are caught.
- The implementation shall treat this array as read-only.\footnote{Thus,
- the program may allocate the array in static storage.}
-
- The third argument of \verb|except_try_push| shall point to an object
- of type \verb|except_t *|. After the call to \verb|except_try_push|,
- the program shall inspect the value of this object. A null value indicates
- that no exception has been thrown. A non-null value indicates that an
- exception was thrown, and is now caught. In other words, when an exception
- is caught by a try-catch region, then control passes from the throw site
- back to the first statement after the \verb|except_try_push| statement of
- the try-catch region. This case is distinguished from an ordinary return by
- the non-null value of the pointer object that was specified by the third
- argument of the earlier call to \verb|except_try_push|.
-
- An exception is considered handled if it is caught in a try-catch region
- which subsequently terminates by executing its \verb|except_try_pop| or by
- throwing another exception. When an exception is considered handled, any
- dynamic data that was associated with that exception is
- freed.\footnote{Dynamic data may be explicitly associated with an exception
- using {\tt except_throwd}. Other types of throw may associate unspecified
- dynamic data.} It's possible for more than one exception to be active
- at once. During the processing of one exception, a try-catch region
- which catches the exception may execute a nested try-catch region
- in which independent exception processing takes place. Provided that
- no exception escapes from the inner try-catch region, the original
- exception remains pending. But if an exception escapes from the inner
- region, it causes the original exception to be handled.\footnote{Thus, a
- given try-catch region cannot catch multiple exceptions concurrently.}
-
- The caught exception may be rethrown by calling \verb|except_rethrow|,
- specifying the the value of the caught exception descriptor as the
- argument. Rethrowing a caught exception causes the innermost try-catch
- region to terminate, but the exception is not considered handled. The
- search for a handler continues with the second most enclosing region.
-
- Throwing a new exception during the handling of a caught exception may
- cause the {\it same\/} try-catch region to catch that exception; the
- try-catch region is not terminated until it is determined that it doesn't
- catch the new exception.
-
- Each entry in the array of \verb|except_id_t| objects specifies what
- exceptions are caught by the try-catch region. When an exception is
- thrown, the implementation searches for the inner-most try-catch region
- which has at least one match for the thrown exception in its catch
- specification array.
-
- A match occurs when a specification exactly matches the group and code of
- the thrown exception. If a catch specification is for group 0, then it
- matches any group. If a catch specification is for code 0, then it matches
- any exception code. A catch specification of group 0 and code 0 catches all
- exceptions.
-
- Non-volatile automatic variables that are local to the function containing
- the try-catch region, and that are modified after \verb|except_try_push|
- begins the try-catch region have indeterminate values when an exception is
- caught.
-
- Once a caught exception is handled or re-thrown, the value of the
- \verb|except_t *| pointer which referenced it becomes indeterminate.
- If a re-thrown exception is caught again, the implementation shall
- produce a valid \verb|except_t *| pointer.
-
- \example
- The following example illustrates the use of \verb|except_try_push| and
- related macros and functions.
- \begin{verbatim}
- #include <stdlib.h>
- #include <assert.h>
- #include "except.h"
-
- #define MY_GROUP 42
- #define MY_CODE 1
-
- static void func_that_throws(void)
- {
- except_throw(MY_GROUP, MY_CODE, "this is an exception");
- }
-
- static void func_that_cleans_up(void)
- {
- void *local_data = malloc(10);
-
- except_cleanup_push(free, local_data);
- func_that_throws();
- except_checked_cleanup_pop(free, except_call);
- }
-
- void func_that_catches(void)
- {
- /* catch specification */
- static const except_id_t catch_spec[] = {
- { MY_GROUP, XCEPT_CODE_ANY }
- };
- /* exception handle */
- except_t *exc;
-
- except_try_push(catch_spec, 1, &exc);
-
- /*
- * Start of try-catch region: when exception is
- * thrown, control returns here.
- */
-
- if (exc == 0) {
- /* try code that may throw an exception */
-
- func_that_cleans_up();
- } else {
- /* handle exception that was thrown */
-
- assert (except_group(exc) == MY_GROUP);
- printf("exception caught: %s %ld %ld\n",
- except_message(exc),
- except_group(exc), except_code(exc));
-
- goto terminate; /* ERROR! jumping out of try-catch */
- }
-
- /* end of try-catch region */
-
- except_try_pop();
- terminate:
- ;
- }
- \end{verbatim}
- In this example, the function \verb|func_that_catches| is intended to be
- called first. It sets up a try-catch region which traps exceptions having
- the group identification \verb|MY_GROUP| (or 42). Any code within that
- group is caught because the code catch was specified as
- \verb|XCEPT_CODE_ANY|. When the \verb|except_try_push| macro is executed,
- it sets the value of \verb|exc| to null. Then \verb|func_that_cleans_up| is
- called, which throws an exception in the \verb|MY_GROUP| group. This
- exception is caught, so control resumes at the top of the try-catch region,
- with \verb|exc| set to a non-null value. Thus the else clause of the if
- statement is now executed. The handling code simply prints the exception
- message on standard output, as well as the numeric group and code. The
- subsequent goto statement demonstrates a serious programming error.
-
- The \verb|func_that_cleans_up| function illustrates the use of cleanup
- regions. Dynamic memory is allocated which must not be allowed to leak
- when an exception is thrown, so a cleanup handler is set up to free the
- memory in that event. The standard C function \verb|free| happens to have,
- the right type signature and semantics that it can be used directly as a
- cleanup handler. Should no exception be thrown, the cleanup pop macro
- will perform the call to the cleanup handler, because it is invoked with
- argument \verb|except_call|.
-
-\subsubsection{The {\tt except_try_pop} macro}
-
- \indexmacro{except_try_pop}
-
- \synopsis
- \begin{verbatim}
- void except_try_pop(void);\end{verbatim}
-
- \description
-
- The \verb|except_try_pop| macro terminates a try-catch region. It must
- match a previous \verb|except_try_push| macro in the same statement
- block at the same level of nesting which is not already matched by an
- earlier \verb|except_try_pop|.
-
-\subsubsection{The {\tt except_set_allocator} function}
-
- \indexfunc{except_set_allocator}
- \label{section:except_set_allocator}
-
- \synopsis
- \begin{verbatim}
- void except_set_allocator(void *(*)(size_t), void (*)(void *));\end{verbatim}
-
- \description
- The \verb|except_set_allocator| function installs a pair of allocator
- routines that will be used by the Exception component for future allocation
- and deallocation requests.
-
- The first argument points to a function that resembles the standard C
- \verb|malloc| in type and semantics. The second argument points to a
- function that similarly resembles the standard C function \verb|free|.
-
- The default allocators are \verb|malloc| and \verb|free|.
- The call
- \begin{verbatim}
- except_set_allocator(malloc, free);
- \end{verbatim}
- may be used to restore these default allocator functions.
-
- The program shall not call \verb|except_set_allocator| if an exception
- was thrown and has not yet been handled.\footnote{Doing so could, for example,
- create a mismatch whereby a pointer to data allocated with the previously installed
- allocator function would be passed to the new deallocator function.}
-
- The allocator function shall create a unique object consisting of at least
- as many bytes of storage as indicated by the value of the argument.
- The pointer returned shall be suitably aligned to represent an object
- of any type. If insufficient resources exist, the pointer returned shall be
- null. Requesting an object of zero size may produce a unique pointer
- that shall be acceptable to the deallocator function, or a null pointer.
-
- The deallocator function shall be capable of destroying objects created
- by the corresponding allocator function. Passing a null pointer to the
- deallocator shall have no effect.
-
-\subsubsection{The {\tt except_alloc} function}
-
- \indexfunc{except_alloc}
-
- \synopsis
- \begin{verbatim}
- void *except_alloc(size_t);\end{verbatim}
-
- \description
- The \verb|except_alloc| function allocates memory using the default
- memory allocator or one installed by the program.
- (See section \ref{section:except_set_allocator}).
-
- If the allocation succeeds, a non-null pointer to the allocated object is
- returned.
-
- If the allocator indicates failure by returning a null pointer,
- then instead of returning, \verb|except_alloc| throws exception code 1
- in the group \verb|XCEPT_BAD_ALLOC| (See section \ref{section:except_id_t}).
-
- If a zero size request is specified, then an exception is thrown or
- a non-null pointer is returned, depending on the treatment of such
- requests by the underlying allocator.
-
-\subsubsection{The {\tt except_free} function}
-
- \indexfunc{except_free}
-
- \synopsis
- \begin{verbatim}
- void *except_free(void *);\end{verbatim}
-
- \description
-
- The \verb|except_free| function releases memory that was allocated
- using \verb|except_alloc|. The deallocation is performed using the
- default allocator or one installed by the program.
-
- If an object is allocated by \verb|except_alloc|, then a
- different allocator is installed, and the object is freed using
- \verb|except_free|, the behavior is undefined.
-
-\subsection{Implementation}
-\index{Exception component!reference implementation}
-
-Described here is a reference implementation of the exception handling
-interface that is covered in section \ref{section:exception_component}
-The reference implementation requires only a conforming ANSI C implementation.
-In particular, the actual mechanism for passing control from an exception throw
-to a catch handler is based on the standard C \verb|setjmp| macro and
-\verb|longjmp| function.
-
-\subsubsection{Overview}
-
-The core structure in the exception handling implementation is a stack that is
-composed of a mixture of two types of nodes: cleanup nodes and catch nodes.
-When an exception is thrown, the stack nodes are popped and processed starting
-with the topmost one.
-
-The nodes are efficiently allocated in automatic storage by the macros
-\verb|except_cleanup_push| and \verb|except_try_push|. These macros
-open up a new statement block and declare the node information in automatic
-storage. These objects are then pushed onto the stack. The corresponding macros
-\verb|except_cleanup_pop| and \verb|except_try_pop| pop the node off the stack
-and close the statement block.
-
-An static variable keeps track of the stack top. In the multi-threaded variant
-of the code which is based on the POSIX threading interface, there is a
-thread-specific stack top created using the thread-specific function
-pthread_key_create. Using global variables is a compromise that simplifies the
-interface; the throw functions simply ``know'' where the thread's exception
-stack is, so the context information doesn't have to be passed around.
-
-\subsubsection{Stack nodes}
-
-A node in the exception handling stack contains a pointer to the next
-node below, followed by a type field and a union which together keep
-track of the appropriate type-specific data:
-\begin{verbatim}
- enum except_stacktype {
- XCEPT_CLEANUP, XCEPT_CATCHER
- };
-
- struct except_stacknode {
- struct except_stacknode *except_down;
- enum except_stacktype except_type;
- union {
- struct except_catch *except_catcher;
- struct except_cleanup *except_cleanup;
- } except_info;
- };
-\end{verbatim}
-The union overlaps pointers to structures instead of structures in order to
-save space: there is a disparity in size between a cleanup node and a catch
-node, so making them both use the same amount of space would be wasteful.
-The space saving comes at a price, because the pointers themselves take up
-extra space and time is spent initializing them. Some casting trickery
-could be used to create a stack having two different kinds of structures
-without the use of unions.
-
-\paragraph{Cleanup nodes}
-
-Cleanup nodes act as placeholders for a pointer to a cleanup handler function
-and a context pointer to be passed to that function. The type-dependent
-component of the cleanup node is declared like this:
-\begin{verbatim}
- struct except_cleanup {
- void (*except_func)(void *);
- void *except_context;
- };
-\end{verbatim}
-The cleanup handler is invoked when the node is popped during exception
-processing. A cleanup handler may also be invoked when the cleanup node is
-removed by executing \verb|except_cleanup_pop| or
-\verb|except_checked_cleanup_pop|. Whether or not this happens depends on the
-integer parameter that is documented in section
-\ref{section:except_cleanup_pop}.
-
-\paragraph{Catch nodes}
-
-The catch node structure is more complicated than the cleanup node.
-Its definition depends on two additional types, \verb|except_id_t|
-and \verb|except_t|, both of which also make play a role in the exception
-component's interface.
-\begin{verbatim}
- typedef struct {
- unsigned long except_group;
- unsigned long except_code;
- } except_id_t;
-
- typedef struct {
- except_id_t except_id;
- const char *except_message;
- void *except_dyndata;
- } except_t;
-
- struct except_catch {
- const except_id_t *except_id;
- size_t except_size;
- except_t except_obj;
- jmp_buf except_jmp;
- };
-\end{verbatim}
-The \verb|except_id| member of the \verb|except_catch| structure is a pointer to the
-array of \verb|except_id_t| objects which specify what exceptions the node
-catches. The \verb|except_size| member specifies the number of elements in the array.
-Both of these values are derived directly from the arguments of the
-\verb|except_try_push| macro (see section \ref{section:except_try_push}). The
-\verb|except_obj| member provides storage for the caught exception. This member is
-the means by which the thrown exception is communicated to the try-catch region
-where it is caught. It contains the group and code identifiers, the exception
-message and, optionally, the pointer to arbitrary exception data. The
-\verb|except_jmp| member is the standard C \verb|jmp_buf|---a place for saving the
-execution context so that it's possible to pass control, via \verb|longjmp|
-from the place where an exception is thrown to the place where it is caught.
-
-If, during the search for an exception handler, a catch node is encountered
-which matches the thrown exception, the node remains the stack. The exception
-information is stored into into the node's \verb|except_obj| member and a
-\verb|longjmp| is executed to return to the try-catch region in which the node
-was allocated and pushed. Because the node is still on the stack, it's possible
-to throw another exception which is caught again by the same node. When an
-exception is thus caught, control resumes just after the \verb|except_throw|
-which placed the node onto the stack. The pointer passed into \verb|except_throw|
-is updated to point to the \verb|except_obj| member of the catch structure.
-The program can then use the portable accessor functions such as
-\verb|except_code| to gain information about the caught exception and handle it
-accordingly.
-
-\index{external names|see {functions}}
-\index{reference implementation|see {implementation}}
-\index{names|see {symbols}}
-\index{identifiers|see {symbols}}
-\index{structure names|see{tags}}
-\index{preprocessor symbols|see{macros}}
-\index{defines|see{macros}}
-\index{reserved symbols|see{symbols}}
-\index{symbols!preprocessor|see{macros}}
-\index{symbols!type names|see{typedefs}}
-\index{symbols!function names|see{functions}}
-\printindex
-
-\end{document}
diff --git a/libutil/kazlib/drivers/dict-main.c b/libutil/kazlib/drivers/dict-main.c
deleted file mode 100644
index 08f2e7a..0000000
--- a/libutil/kazlib/drivers/dict-main.c
+++ /dev/null
@@ -1,300 +0,0 @@
-#include <stdio.h>
-#include <string.h>
-#include <ctype.h>
-#include <stdarg.h>
-
-typedef char input_t[256];
-
-static int tokenize(char *string, ...)
-{
- char **tokptr;
- va_list arglist;
- int tokcount = 0;
-
- va_start(arglist, string);
- tokptr = va_arg(arglist, char **);
- while (tokptr) {
- while (*string && isspace((unsigned char) *string))
- string++;
- if (!*string)
- break;
- *tokptr = string;
- while (*string && !isspace((unsigned char) *string))
- string++;
- tokptr = va_arg(arglist, char **);
- tokcount++;
- if (!*string)
- break;
- *string++ = 0;
- }
- va_end(arglist);
-
- return tokcount;
-}
-
-static int comparef(const void *key1, const void *key2)
-{
- return strcmp(key1, key2);
-}
-
-static char *dupstring(char *str)
-{
- int sz = strlen(str) + 1;
- char *new = malloc(sz);
- if (new)
- memcpy(new, str, sz);
- return new;
-}
-
-static dnode_t *new_node(void *c)
-{
- static dnode_t few[5];
- static int count;
-
- if (count < 5)
- return few + count++;
-
- return NULL;
-}
-
-static void del_node(dnode_t *n, void *c)
-{
-}
-
-static int prompt = 0;
-
-static void construct(dict_t *d)
-{
- input_t in;
- int done = 0;
- dict_load_t dl;
- dnode_t *dn;
- char *tok1, *tok2, *val;
- const char *key;
- char *help =
- "p turn prompt on\n"
- "q finish construction\n"
- "a <key> <val> add new entry\n";
-
- if (!dict_isempty(d))
- puts("warning: dictionary not empty!");
-
- dict_load_begin(&dl, d);
-
- while (!done) {
- if (prompt)
- putchar('>');
- fflush(stdout);
-
- if (!fgets(in, sizeof(input_t), stdin))
- break;
-
- switch (in[0]) {
- case '?':
- puts(help);
- break;
- case 'p':
- prompt = 1;
- break;
- case 'q':
- done = 1;
- break;
- case 'a':
- if (tokenize(in+1, &tok1, &tok2, (char **) 0) != 2) {
- puts("what?");
- break;
- }
- key = dupstring(tok1);
- val = dupstring(tok2);
- dn = dnode_create(val);
-
- if (!key || !val || !dn) {
- puts("out of memory");
- free((void *) key);
- free(val);
- if (dn)
- dnode_destroy(dn);
- }
-
- dict_load_next(&dl, dn, key);
- break;
- default:
- putchar('?');
- putchar('\n');
- break;
- }
- }
-
- dict_load_end(&dl);
-}
-
-int main(void)
-{
- input_t in;
- dict_t darray[10];
- dict_t *d = &darray[0];
- dnode_t *dn;
- int i;
- char *tok1, *tok2, *val;
- const char *key;
-
- char *help =
- "a <key> <val> add value to dictionary\n"
- "d <key> delete value from dictionary\n"
- "l <key> lookup value in dictionary\n"
- "( <key> lookup lower bound\n"
- ") <key> lookup upper bound\n"
- "# <num> switch to alternate dictionary (0-9)\n"
- "j <num> <num> merge two dictionaries\n"
- "f free the whole dictionary\n"
- "k allow duplicate keys\n"
- "c show number of entries\n"
- "t dump whole dictionary in sort order\n"
- "m make dictionary out of sorted items\n"
- "p turn prompt on\n"
- "s switch to non-functioning allocator\n"
- "q quit";
-
- for (i = 0; i < sizeof darray / sizeof *darray; i++)
- dict_init(&darray[i], DICTCOUNT_T_MAX, comparef);
-
- for (;;) {
- if (prompt)
- putchar('>');
- fflush(stdout);
-
- if (!fgets(in, sizeof(input_t), stdin))
- break;
-
- switch(in[0]) {
- case '?':
- puts(help);
- break;
- case 'a':
- if (tokenize(in+1, &tok1, &tok2, (char **) 0) != 2) {
- puts("what?");
- break;
- }
- key = dupstring(tok1);
- val = dupstring(tok2);
-
- if (!key || !val) {
- puts("out of memory");
- free((void *) key);
- free(val);
- }
-
- if (!dict_alloc_insert(d, key, val)) {
- puts("dict_alloc_insert failed");
- free((void *) key);
- free(val);
- break;
- }
- break;
- case 'd':
- if (tokenize(in+1, &tok1, (char **) 0) != 1) {
- puts("what?");
- break;
- }
- dn = dict_lookup(d, tok1);
- if (!dn) {
- puts("dict_lookup failed");
- break;
- }
- val = dnode_get(dn);
- key = dnode_getkey(dn);
- dict_delete_free(d, dn);
-
- free(val);
- free((void *) key);
- break;
- case 'f':
- dict_free(d);
- break;
- case 'l':
- case '(':
- case ')':
- if (tokenize(in+1, &tok1, (char **) 0) != 1) {
- puts("what?");
- break;
- }
- dn = 0;
- switch (in[0]) {
- case 'l':
- dn = dict_lookup(d, tok1);
- break;
- case '(':
- dn = dict_lower_bound(d, tok1);
- break;
- case ')':
- dn = dict_upper_bound(d, tok1);
- break;
- }
- if (!dn) {
- puts("lookup failed");
- break;
- }
- val = dnode_get(dn);
- puts(val);
- break;
- case 'm':
- construct(d);
- break;
- case 'k':
- dict_allow_dupes(d);
- break;
- case 'c':
- printf("%lu\n", (unsigned long) dict_count(d));
- break;
- case 't':
- for (dn = dict_first(d); dn; dn = dict_next(d, dn)) {
- printf("%s\t%s\n", (char *) dnode_getkey(dn),
- (char *) dnode_get(dn));
- }
- break;
- case 'q':
- exit(0);
- break;
- case '\0':
- break;
- case 'p':
- prompt = 1;
- break;
- case 's':
- dict_set_allocator(d, new_node, del_node, NULL);
- break;
- case '#':
- if (tokenize(in+1, &tok1, (char **) 0) != 1) {
- puts("what?");
- break;
- } else {
- int dictnum = atoi(tok1);
- if (dictnum < 0 || dictnum > 9) {
- puts("invalid number");
- break;
- }
- d = &darray[dictnum];
- }
- break;
- case 'j':
- if (tokenize(in+1, &tok1, &tok2, (char **) 0) != 2) {
- puts("what?");
- break;
- } else {
- int dict1 = atoi(tok1), dict2 = atoi(tok2);
- if (dict1 < 0 || dict1 > 9 || dict2 < 0 || dict2 > 9) {
- puts("invalid number");
- break;
- }
- dict_merge(&darray[dict1], &darray[dict2]);
- }
- break;
- default:
- putchar('?');
- putchar('\n');
- break;
- }
- }
-
- return 0;
-}
diff --git a/libutil/kazlib/drivers/except-main.c b/libutil/kazlib/drivers/except-main.c
deleted file mode 100644
index fdb64db..0000000
--- a/libutil/kazlib/drivers/except-main.c
+++ /dev/null
@@ -1,57 +0,0 @@
-#include <stdio.h>
-#include <ctype.h>
-
-static void cleanup(void *arg)
-{
- printf("cleanup(\"%s\") called\n", (char *) arg);
-}
-
-static void bottom_level(void)
-{
- char buf[256];
- printf("throw exception? "); fflush(stdout);
- fgets(buf, sizeof buf, stdin);
-
- if (buf[0] >= 0 && toupper(buf[0]) == 'Y')
- except_throw(1, 1, "nasty exception");
-}
-
-static void top_level(void)
-{
- except_cleanup_push(cleanup, "argument");
- bottom_level();
- except_cleanup_pop(0);
-}
-
-int main(int argc, char **argv)
-{
- static const except_id_t catch[] = { { 1, 1 }, { 1, 2 } };
- except_t *ex;
-
- /*
- * Nested exception ``try blocks''
- */
-
- /* outer */
- except_try_push(catch, 2, &ex);
- if (!ex) {
- /* inner */
- except_try_push(catch, 2, &ex);
- if (!ex) {
- top_level();
- } else {
- /* inner catch */
- printf("caught exception (inner): \"%s\", s=%ld, c=%ld\n",
- except_message(ex), except_group(ex), except_code(ex));
- except_rethrow(ex);
- }
- except_try_pop();
- } else {
- /* outer catch */
- printf("caught exception (outer): \"%s\", s=%ld, c=%ld\n",
- except_message(ex), except_group(ex), except_code(ex));
- }
- except_try_pop();
- except_throw(99, 99, "exception in main");
- return 0;
-}
diff --git a/libutil/kazlib/drivers/hash-main.c b/libutil/kazlib/drivers/hash-main.c
deleted file mode 100644
index 0a08542..0000000
--- a/libutil/kazlib/drivers/hash-main.c
+++ /dev/null
@@ -1,187 +0,0 @@
-#include <stdio.h>
-#include <ctype.h>
-#include <stdarg.h>
-
-typedef char input_t[256];
-
-static int tokenize(char *string, ...)
-{
- char **tokptr;
- va_list arglist;
- int tokcount = 0;
-
- va_start(arglist, string);
- tokptr = va_arg(arglist, char **);
- while (tokptr) {
- while (*string && isspace((unsigned char) *string))
- string++;
- if (!*string)
- break;
- *tokptr = string;
- while (*string && !isspace((unsigned char) *string))
- string++;
- tokptr = va_arg(arglist, char **);
- tokcount++;
- if (!*string)
- break;
- *string++ = 0;
- }
- va_end(arglist);
-
- return tokcount;
-}
-
-static char *dupstring(char *str)
-{
- int sz = strlen(str) + 1;
- char *new = malloc(sz);
- if (new)
- memcpy(new, str, sz);
- return new;
-}
-
-static hnode_t *new_node(void *c)
-{
- static hnode_t few[5];
- static int count;
-
- if (count < 5)
- return few + count++;
-
- return NULL;
-}
-
-static void del_node(hnode_t *n, void *c)
-{
-}
-
-int main(void)
-{
- input_t in;
- hash_t *h = hash_create(HASHCOUNT_T_MAX, 0, 0);
- hnode_t *hn;
- hscan_t hs;
- char *tok1, *tok2, *val;
- const char *key;
- int prompt = 0;
-
- char *help =
- "a <key> <val> add value to hash table\n"
- "d <key> delete value from hash table\n"
- "l <key> lookup value in hash table\n"
- "n show size of hash table\n"
- "c show number of entries\n"
- "t dump whole hash table\n"
- "+ increase hash table (private func)\n"
- "- decrease hash table (private func)\n"
- "b print hash_t_bit value\n"
- "p turn prompt on\n"
- "s switch to non-functioning allocator\n"
- "q quit";
-
- if (!h)
- puts("hash_create failed");
-
- for (;;) {
- if (prompt)
- putchar('>');
- fflush(stdout);
-
- if (!fgets(in, sizeof(input_t), stdin))
- break;
-
- switch(in[0]) {
- case '?':
- puts(help);
- break;
- case 'b':
- printf("%d\n", hash_val_t_bit);
- break;
- case 'a':
- if (tokenize(in+1, &tok1, &tok2, (char **) 0) != 2) {
- puts("what?");
- break;
- }
- key = dupstring(tok1);
- val = dupstring(tok2);
-
- if (!key || !val) {
- puts("out of memory");
- free((void *) key);
- free(val);
- }
-
- if (!hash_alloc_insert(h, key, val)) {
- puts("hash_alloc_insert failed");
- free((void *) key);
- free(val);
- break;
- }
- break;
- case 'd':
- if (tokenize(in+1, &tok1, (char **) 0) != 1) {
- puts("what?");
- break;
- }
- hn = hash_lookup(h, tok1);
- if (!hn) {
- puts("hash_lookup failed");
- break;
- }
- val = hnode_get(hn);
- key = hnode_getkey(hn);
- hash_scan_delfree(h, hn);
- free((void *) key);
- free(val);
- break;
- case 'l':
- if (tokenize(in+1, &tok1, (char **) 0) != 1) {
- puts("what?");
- break;
- }
- hn = hash_lookup(h, tok1);
- if (!hn) {
- puts("hash_lookup failed");
- break;
- }
- val = hnode_get(hn);
- puts(val);
- break;
- case 'n':
- printf("%lu\n", (unsigned long) hash_size(h));
- break;
- case 'c':
- printf("%lu\n", (unsigned long) hash_count(h));
- break;
- case 't':
- hash_scan_begin(&hs, h);
- while ((hn = hash_scan_next(&hs)))
- printf("%s\t%s\n", (char*) hnode_getkey(hn),
- (char*) hnode_get(hn));
- break;
- case '+':
- grow_table(h); /* private function */
- break;
- case '-':
- shrink_table(h); /* private function */
- break;
- case 'q':
- exit(0);
- break;
- case '\0':
- break;
- case 'p':
- prompt = 1;
- break;
- case 's':
- hash_set_allocator(h, new_node, del_node, NULL);
- break;
- default:
- putchar('?');
- putchar('\n');
- break;
- }
- }
-
- return 0;
-}
diff --git a/libutil/kazlib/drivers/list-main.c b/libutil/kazlib/drivers/list-main.c
deleted file mode 100644
index 6f462e4..0000000
--- a/libutil/kazlib/drivers/list-main.c
+++ /dev/null
@@ -1,152 +0,0 @@
-#include <stdio.h>
-#include <string.h>
-#include <ctype.h>
-#include <stdarg.h>
-
-typedef char input_t[256];
-
-static int tokenize(char *string, ...)
-{
- char **tokptr;
- va_list arglist;
- int tokcount = 0;
-
- va_start(arglist, string);
- tokptr = va_arg(arglist, char **);
- while (tokptr) {
- while (*string && isspace((unsigned char) *string))
- string++;
- if (!*string)
- break;
- *tokptr = string;
- while (*string && !isspace((unsigned char) *string))
- string++;
- tokptr = va_arg(arglist, char **);
- tokcount++;
- if (!*string)
- break;
- *string++ = 0;
- }
- va_end(arglist);
-
- return tokcount;
-}
-
-static int comparef(const void *key1, const void *key2)
-{
- return strcmp(key1, key2);
-}
-
-static char *dupstring(char *str)
-{
- int sz = strlen(str) + 1;
- char *new = malloc(sz);
- if (new)
- memcpy(new, str, sz);
- return new;
-}
-
-int main(void)
-{
- input_t in;
- list_t *l = list_create(LISTCOUNT_T_MAX);
- lnode_t *ln;
- char *tok1, *val;
- int prompt = 0;
-
- char *help =
- "a <val> append value to list\n"
- "d <val> delete value from list\n"
- "l <val> lookup value in list\n"
- "s sort list\n"
- "c show number of entries\n"
- "t dump whole list\n"
- "p turn prompt on\n"
- "q quit";
-
- if (!l)
- puts("list_create failed");
-
- for (;;) {
- if (prompt)
- putchar('>');
- fflush(stdout);
-
- if (!fgets(in, sizeof(input_t), stdin))
- break;
-
- switch(in[0]) {
- case '?':
- puts(help);
- break;
- case 'a':
- if (tokenize(in+1, &tok1, (char **) 0) != 1) {
- puts("what?");
- break;
- }
- val = dupstring(tok1);
- ln = lnode_create(val);
-
- if (!val || !ln) {
- puts("allocation failure");
- if (ln)
- lnode_destroy(ln);
- free(val);
- break;
- }
-
- list_append(l, ln);
- break;
- case 'd':
- if (tokenize(in+1, &tok1, (char **) 0) != 1) {
- puts("what?");
- break;
- }
- ln = list_find(l, tok1, comparef);
- if (!ln) {
- puts("list_find failed");
- break;
- }
- list_delete(l, ln);
- val = lnode_get(ln);
- lnode_destroy(ln);
- free(val);
- break;
- case 'l':
- if (tokenize(in+1, &tok1, (char **) 0) != 1) {
- puts("what?");
- break;
- }
- ln = list_find(l, tok1, comparef);
- if (!ln)
- puts("list_find failed");
- else
- puts("found");
- break;
- case 's':
- list_sort(l, comparef);
- break;
- case 'c':
- printf("%lu\n", (unsigned long) list_count(l));
- break;
- case 't':
- for (ln = list_first(l); ln != 0; ln = list_next(l, ln))
- puts(lnode_get(ln));
- break;
- case 'q':
- exit(0);
- break;
- case '\0':
- break;
- case 'p':
- prompt = 1;
- break;
- default:
- putchar('?');
- putchar('\n');
- break;
- }
- }
-
- return 0;
-}
diff --git a/libutil/kazlib/drivers/sfx-main.c b/libutil/kazlib/drivers/sfx-main.c
deleted file mode 100644
index fda683b..0000000
--- a/libutil/kazlib/drivers/sfx-main.c
+++ /dev/null
@@ -1,41 +0,0 @@
-#include <stdlib.h>
-
-int main(int argc, char **argv)
-{
- char expr_buf[256];
- char *expr, *ptr;
- sfx_rating_t eff;
-
- for (;;) {
- if (argc < 2) {
- expr = expr_buf;
- if (fgets(expr_buf, sizeof expr_buf, stdin) == 0)
- break;
- if ((ptr = strchr(expr_buf, '\n')) != 0)
- *ptr = 0;
- } else {
- expr = (argv++)[1];
- if (!expr)
- break;
- }
-
- if (!sfx_determine(expr, &eff)) {
- printf("expression '%s' has a syntax error\n", expr);
- return EXIT_FAILURE;
- }
-
- switch (eff) {
- case sfx_none:
- printf("expression '%s' has no side effects\n", expr);
- break;
- case sfx_potential:
- printf("expression '%s' may have side effects\n", expr);
- break;
- case sfx_certain:
- printf("expression '%s' has side effects\n", expr);
- break;
- }
- }
-
- return 0;
-}
diff --git a/libutil/kazlib/except.c b/libutil/kazlib/except.c
deleted file mode 100644
index c915dda..0000000
--- a/libutil/kazlib/except.c
+++ /dev/null
@@ -1,347 +0,0 @@
-/*
- * Portable Exception Handling for ANSI C.
- * Copyright (C) 1999 Kaz Kylheku <kaz at ashi.footprints.net>
- *
- * Free Software License:
- *
- * All rights are reserved by the author, with the following exceptions:
- * Permission is granted to freely reproduce and distribute this software,
- * possibly in exchange for a fee, provided that this copyright notice appears
- * intact. Permission is also granted to adapt this software to produce
- * derivative works, as long as the modified versions carry this copyright
- * notice and additional notices stating that the work has been modified.
- * This source code may be translated into executable form and incorporated
- * into proprietary software; there is no requirement for such software to
- * contain a copyright notice related to this source.
- *
- */
-
-#include <assert.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <stdarg.h>
-#include <limits.h>
-#include "except.h"
-
-#define XCEPT_BUFFER_SIZE 1024
-
-#define group except_group
-#define code except_code
-#define id except_id
-#define message except_message
-#define dyndata except_dyndata
-#define func except_func
-#define context except_context
-#define id except_id
-#define size except_size
-#define obj except_obj
-#define jmp except_jmp
-#define down except_down
-#define type except_type
-#define catcher except_catcher
-#define cleanup except_cleanup
-#define info except_info
-
-#ifdef KAZLIB_POSIX_THREADS
-
-#include <pthread.h>
-
-static pthread_mutex_t init_mtx = PTHREAD_MUTEX_INITIALIZER;
-static int init_counter;
-static pthread_key_t top_key;
-static pthread_key_t uh_key;
-static pthread_key_t alloc_key;
-static pthread_key_t dealloc_key;
-static void unhandled_catcher(except_t *);
-
-#define get_top() ((struct except_stacknode *) pthread_getspecific(top_key))
-#define set_top(T) (pthread_setspecific(top_key, (T)), (void)((T) == (struct except_stacknode *) 0))
-#define set_catcher(C) (pthread_setspecific(uh_key, (void *) (C)), (void)((C) == (void (*)(except_t *)) 0))
-#define set_alloc(A) (pthread_setspecific(alloc_key, (void *) (A)), (void)((A) == (void *(*)(size_t)) 0))
-#define set_dealloc(D) (pthread_setspecific(dealloc_key, (void *) (D)), (void)((D) == (void (*)(void *)) 0))
-
-static void (*get_catcher(void))(except_t *)
-{
- void (*catcher)(except_t *) = (void (*)(except_t *)) pthread_getspecific(uh_key);
- return (catcher == 0) ? unhandled_catcher : catcher;
-}
-
-static void *(*get_alloc(void))(size_t)
-{
- void *(*alloc)(size_t) = (void *(*)(size_t)) pthread_getspecific(alloc_key);
- return (alloc == 0) ? malloc : alloc;
-}
-
-static void (*get_dealloc(void))(void *)
-{
- void (*dealloc)(void *) = (void (*)(void *)) pthread_getspecific(dealloc_key);
- return (dealloc == 0) ? free : dealloc;
-}
-
-int except_init(void)
-{
- int retval = 1;
-
- pthread_mutex_lock(&init_mtx);
-
- assert (init_counter < INT_MAX);
-
- if (init_counter++ == 0) {
- int top_ok = (pthread_key_create(&top_key, 0) == 0);
- int uh_ok = (pthread_key_create(&uh_key, 0) == 0);
- int alloc_ok = (pthread_key_create(&alloc_key, 0) == 0);
- int dealloc_ok = (pthread_key_create(&dealloc_key, 0) == 0);
-
- if (!top_ok || !uh_ok || !alloc_ok || !dealloc_ok) {
- retval = 0;
- init_counter = 0;
- if (top_ok)
- pthread_key_delete(top_key);
- if (uh_ok)
- pthread_key_delete(uh_key);
- if (alloc_ok)
- pthread_key_delete(alloc_key);
- if (dealloc_ok)
- pthread_key_delete(dealloc_key);
- }
- }
-
- pthread_mutex_unlock(&init_mtx);
-
- return retval;
-}
-
-void except_deinit(void)
-{
- pthread_mutex_lock(&init_mtx);
-
- assert (init_counter > 0);
-
- if (--init_counter == 0) {
- pthread_key_delete(top_key);
- pthread_key_delete(uh_key);
- pthread_key_delete(alloc_key);
- pthread_key_delete(dealloc_key);
- }
-
- pthread_mutex_unlock(&init_mtx);
-}
-
-#else /* no thread support */
-
-static int init_counter;
-static void unhandled_catcher(except_t *);
-static void (*uh_catcher_ptr)(except_t *) = unhandled_catcher;
-static void *(*allocator)(size_t) = malloc;
-static void (*deallocator)(void *) = free;
-static struct except_stacknode *stack_top;
-
-#define get_top() (stack_top)
-#define set_top(T) (stack_top = (T))
-#define get_catcher() (uh_catcher_ptr)
-#define set_catcher(C) (uh_catcher_ptr = (C))
-#define get_alloc() (allocator)
-#define set_alloc(A) (allocator = (A))
-#define get_dealloc() (deallocator)
-#define set_dealloc(D) (deallocator = (D))
-
-int except_init(void)
-{
- assert (init_counter < INT_MAX);
- init_counter++;
- return 1;
-}
-
-void except_deinit(void)
-{
- assert (init_counter > 0);
- init_counter--;
-}
-
-#endif
-
-
-static int match(const volatile except_id_t *thrown, const except_id_t *caught)
-{
- int group_match = (caught->group == XCEPT_GROUP_ANY || caught->group == thrown->group);
- int code_match = (caught->code == XCEPT_CODE_ANY || caught->code == thrown->code);
-
- return group_match && code_match;
-}
-
-static void do_throw(except_t *except)
-{
- struct except_stacknode *top;
-
- assert (except->id.group != 0 && except->id.code != 0);
-
- for (top = get_top(); top != 0; top = top->down) {
- if (top->type == XCEPT_CLEANUP) {
- top->info.cleanup->func(top->info.cleanup->context);
- } else {
- struct except_catch *catcher = top->info.catcher;
- const except_id_t *pi = catcher->id;
- size_t i;
-
- assert (top->type == XCEPT_CATCHER);
- except_free(catcher->obj.dyndata);
-
- for (i = 0; i < catcher->size; pi++, i++) {
- if (match(&except->id, pi)) {
- catcher->obj = *except;
- set_top(top);
- longjmp(catcher->jmp, 1);
- }
- }
- }
- }
-
- set_top(top);
- get_catcher()(except); /* unhandled exception */
- abort();
-}
-
-static void unhandled_catcher(except_t *except)
-{
- fprintf(stderr, "Unhandled exception (\"%s\", group=%ld, code=%ld)\n",
- except->message, except->id.group, except->id.code);
- abort();
-}
-
-static void stack_push(struct except_stacknode *node)
-{
- node->down = get_top();
- set_top(node);
-}
-
-void except_setup_clean(struct except_stacknode *esn,
- struct except_cleanup *ecl, void (*cleanf)(void *), void *context)
-{
- esn->type = XCEPT_CLEANUP;
- ecl->func = cleanf;
- ecl->context = context;
- esn->info.cleanup = ecl;
- stack_push(esn);
-}
-
-void except_setup_try(struct except_stacknode *esn,
- struct except_catch *ech, const except_id_t id[], size_t size)
-{
- ech->id = id;
- ech->size = size;
- ech->obj.dyndata = 0;
- esn->type = XCEPT_CATCHER;
- esn->info.catcher = ech;
- stack_push(esn);
-}
-
-struct except_stacknode *except_pop(void)
-{
- struct except_stacknode *top = get_top();
- set_top(top->down);
- return top;
-}
-
-void except_rethrow(except_t *except)
-{
- struct except_stacknode *top = get_top();
- assert (top != 0);
- assert (top->type == XCEPT_CATCHER);
- assert (&top->info.catcher->obj == except);
- set_top(top->down);
- do_throw(except);
-}
-
-void except_throw(long group, long code, const char *msg)
-{
- except_t except;
-
- except.id.group = group;
- except.id.code = code;
- except.message = msg;
- except.dyndata = 0;
-
- do_throw(&except);
-}
-
-void except_throwd(long group, long code, const char *msg, void *data)
-{
- except_t except;
-
- except.id.group = group;
- except.id.code = code;
- except.message = msg;
- except.dyndata = data;
-
- do_throw(&except);
-}
-
-void except_throwf(long group, long code, const char *fmt, ...)
-{
- char *buf = except_alloc(XCEPT_BUFFER_SIZE);
- va_list vl;
-
- va_start (vl, fmt);
- vsprintf(buf, fmt, vl);
- va_end (vl);
- except_throwd(group, code, buf, buf);
-}
-
-void (*except_unhandled_catcher(void (*new_catcher)(except_t *)))(except_t *)
-{
- void (*old_catcher)(except_t *) = get_catcher();
- set_catcher(new_catcher);
- return old_catcher;
-}
-
-#undef except_code
-#undef except_group
-#undef except_message
-#undef except_data
-
-unsigned long except_code(except_t *ex)
-{
- return ex->id.code;
-}
-
-unsigned long except_group(except_t *ex)
-{
- return ex->id.group;
-}
-
-const char *except_message(except_t *ex)
-{
- return ex->message;
-}
-
-void *except_data(except_t *ex)
-{
- return ex->dyndata;
-}
-
-void *except_take_data(except_t *ex)
-{
- void *data = ex->dyndata;
- ex->dyndata = 0;
- return data;
-}
-
-void except_set_allocator(void *(*alloc)(size_t), void (*dealloc)(void *))
-{
- set_alloc(alloc);
- set_dealloc(dealloc);
-}
-
-void *except_alloc(size_t size)
-{
- void *ptr = get_alloc()(size);
-
- if (ptr == 0)
- except_throw(XCEPT_BAD_ALLOC, 0, "out of memory");
- return ptr;
-}
-
-void except_free(void *ptr)
-{
- get_dealloc()(ptr);
-}
diff --git a/libutil/kazlib/except.h b/libutil/kazlib/except.h
deleted file mode 100644
index 3131fb9..0000000
--- a/libutil/kazlib/except.h
+++ /dev/null
@@ -1,147 +0,0 @@
-/*
- * Portable Exception Handling for ANSI C.
- * Copyright (C) 1999 Kaz Kylheku <kaz at ashi.footprints.net>
- *
- * Free Software License:
- *
- * All rights are reserved by the author, with the following exceptions:
- * Permission is granted to freely reproduce and distribute this software,
- * possibly in exchange for a fee, provided that this copyright notice appears
- * intact. Permission is also granted to adapt this software to produce
- * derivative works, as long as the modified versions carry this copyright
- * notice and additional notices stating that the work has been modified.
- * This source code may be translated into executable form and incorporated
- * into proprietary software; there is no requirement for such software to
- * contain a copyright notice related to this source.
- *
- */
-
-#ifndef XCEPT_H
-#define XCEPT_H
-
-#include <setjmp.h>
-#include <stdlib.h>
-#include <assert.h>
-
-#define XCEPT_GROUP_ANY 0
-#define XCEPT_CODE_ANY 0
-#define XCEPT_BAD_ALLOC 1
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-enum { except_no_call, except_call };
-
-typedef struct {
- unsigned long except_group;
- unsigned long except_code;
-} except_id_t;
-
-typedef struct {
- except_id_t volatile except_id;
- const char *volatile except_message;
- void *volatile except_dyndata;
-} except_t;
-
-struct except_cleanup {
- void (*except_func)(void *);
- void *except_context;
-};
-
-struct except_catch {
- const except_id_t *except_id;
- size_t except_size;
- except_t except_obj;
- jmp_buf except_jmp;
-};
-
-enum except_stacktype {
- XCEPT_CLEANUP, XCEPT_CATCHER
-};
-
-struct except_stacknode {
- struct except_stacknode *except_down;
- enum except_stacktype except_type;
- union {
- struct except_catch *except_catcher;
- struct except_cleanup *except_cleanup;
- } except_info;
-};
-
-/* private functions made external so they can be used in macros */
-void except_setup_clean(struct except_stacknode *,
- struct except_cleanup *, void (*)(void *), void *);
-void except_setup_try(struct except_stacknode *,
- struct except_catch *, const except_id_t [], size_t);
-struct except_stacknode *except_pop(void);
-
-/* public interface functions */
-int except_init(void);
-void except_deinit(void);
-void except_rethrow(except_t *);
-void except_throw(long, long, const char *);
-void except_throwd(long, long, const char *, void *);
-void except_throwf(long, long, const char *, ...);
-void (*except_unhandled_catcher(void (*)(except_t *)))(except_t *);
-unsigned long except_code(except_t *);
-unsigned long except_group(except_t *);
-const char *except_message(except_t *);
-void *except_data(except_t *);
-void *except_take_data(except_t *);
-void except_set_allocator(void *(*)(size_t), void (*)(void *));
-void *except_alloc(size_t);
-void except_free(void *);
-
-#define except_code(E) ((E)->except_id.except_code)
-#define except_group(E) ((E)->except_id.except_group)
-#define except_message(E) ((E)->except_message)
-#define except_data(E) ((E)->except_dyndata)
-
-#ifdef __cplusplus
-}
-#endif
-
-/*
- * void except_cleanup_push(void (*)(void *), void *);
- * void except_cleanup_pop(int);
- * void except_checked_cleanup_pop(void (*)(void *), int);
- * void except_try_push(const except_id_t [], size_t, except_t **);
- * void except_try_pop(void);
- */
-
-#define except_cleanup_push(F, C) \
- { \
- struct except_stacknode except_sn; \
- struct except_cleanup except_cl; \
- except_setup_clean(&except_sn, &except_cl, F, C)
-
-#define except_cleanup_pop(E) \
- except_pop(); \
- if (E) \
- except_cl.except_func(except_cl.except_context); \
- }
-
-#define except_checked_cleanup_pop(F, E) \
- except_pop(); \
- assert (except_cl.except_func == (F)); \
- if (E) \
- except_cl.except_func(except_cl.except_context); \
- }
-
-#define except_try_push(ID, NUM, PPE) \
- { \
- struct except_stacknode except_sn; \
- struct except_catch except_ch; \
- except_setup_try(&except_sn, &except_ch, ID, NUM); \
- if (setjmp(except_ch.except_jmp)) \
- *(PPE) = &except_ch.except_obj; \
- else \
- *(PPE) = 0
-
-#define except_try_pop() \
- except_free(except_ch.except_obj.except_dyndata); \
- except_pop(); \
- }
-
-#endif
diff --git a/libutil/kazlib/hash.c b/libutil/kazlib/hash.c
deleted file mode 100644
index 2140e66..0000000
--- a/libutil/kazlib/hash.c
+++ /dev/null
@@ -1,837 +0,0 @@
-/*
- * Hash Table Data Type
- * Copyright (C) 1997 Kaz Kylheku <kaz at ashi.footprints.net>
- *
- * Free Software License:
- *
- * All rights are reserved by the author, with the following exceptions:
- * Permission is granted to freely reproduce and distribute this software,
- * possibly in exchange for a fee, provided that this copyright notice appears
- * intact. Permission is also granted to adapt this software to produce
- * derivative works, as long as the modified versions carry this copyright
- * notice and additional notices stating that the work has been modified.
- * This source code may be translated into executable form and incorporated
- * into proprietary software; there is no requirement for such software to
- * contain a copyright notice related to this source.
- *
- */
-
-#include <stdlib.h>
-#include <stddef.h>
-#include <assert.h>
-#include <string.h>
-#define HASH_IMPLEMENTATION
-#include "hash.h"
-
-#define INIT_BITS 6
-#define INIT_SIZE (1UL << (INIT_BITS)) /* must be power of two */
-#define INIT_MASK ((INIT_SIZE) - 1)
-
-#define next hash_next
-#define key hash_key
-#define data hash_data
-#define hkey hash_hkey
-
-#define table hash_table
-#define nchains hash_nchains
-#define nodecount hash_nodecount
-#define maxcount hash_maxcount
-#define highmark hash_highmark
-#define lowmark hash_lowmark
-#define compare hash_compare
-#define function hash_function
-#define allocnode hash_allocnode
-#define freenode hash_freenode
-#define context hash_context
-#define mask hash_mask
-#define dynamic hash_dynamic
-
-#define table hash_table
-#define chain hash_chain
-
-static hnode_t *hnode_alloc(void *context);
-static void hnode_free(hnode_t *node, void *context);
-static hash_val_t hash_fun_default(const void *key);
-static int hash_comp_default(const void *key1, const void *key2);
-
-int hash_val_t_bit;
-
-/*
- * Compute the number of bits in the hash_val_t type. We know that hash_val_t
- * is an unsigned integral type. Thus the highest value it can hold is a
- * Mersenne number (power of two, less one). We initialize a hash_val_t
- * object with this value and then shift bits out one by one while counting.
- * Notes:
- * 1. HASH_VAL_T_MAX is a Mersenne number---one that is one less than a power
- * of two. This means that its binary representation consists of all one
- * bits, and hence ``val'' is initialized to all one bits.
- * 2. While bits remain in val, we increment the bit count and shift it to the
- * right, replacing the topmost bit by zero.
- */
-
-static void compute_bits(void)
-{
- hash_val_t val = HASH_VAL_T_MAX; /* 1 */
- int bits = 0;
-
- while (val) { /* 2 */
- bits++;
- val >>= 1;
- }
-
- hash_val_t_bit = bits;
-}
-
-/*
- * Verify whether the given argument is a power of two.
- */
-
-static int is_power_of_two(hash_val_t arg)
-{
- if (arg == 0)
- return 0;
- while ((arg & 1) == 0)
- arg >>= 1;
- return (arg == 1);
-}
-
-/*
- * Compute a shift amount from a given table size
- */
-
-static hash_val_t compute_mask(hashcount_t size)
-{
- assert (is_power_of_two(size));
- assert (size >= 2);
-
- return size - 1;
-}
-
-/*
- * Initialize the table of pointers to null.
- */
-
-static void clear_table(hash_t *hash)
-{
- hash_val_t i;
-
- for (i = 0; i < hash->nchains; i++)
- hash->table[i] = NULL;
-}
-
-/*
- * Double the size of a dynamic table. This works as follows. Each chain splits
- * into two adjacent chains. The shift amount increases by one, exposing an
- * additional bit of each hashed key. For each node in the original chain, the
- * value of this newly exposed bit will decide which of the two new chains will
- * receive the node: if the bit is 1, the chain with the higher index will have
- * the node, otherwise the lower chain will receive the node. In this manner,
- * the hash table will continue to function exactly as before without having to
- * rehash any of the keys.
- * Notes:
- * 1. Overflow check.
- * 2. The new number of chains is twice the old number of chains.
- * 3. The new mask is one bit wider than the previous, revealing a
- * new bit in all hashed keys.
- * 4. Allocate a new table of chain pointers that is twice as large as the
- * previous one.
- * 5. If the reallocation was successful, we perform the rest of the growth
- * algorithm, otherwise we do nothing.
- * 6. The exposed_bit variable holds a mask with which each hashed key can be
- * AND-ed to test the value of its newly exposed bit.
- * 7. Now loop over each chain in the table and sort its nodes into two
- * chains based on the value of each node's newly exposed hash bit.
- * 8. The low chain replaces the current chain. The high chain goes
- * into the corresponding sister chain in the upper half of the table.
- * 9. We have finished dealing with the chains and nodes. We now update
- * the various bookeeping fields of the hash structure.
- */
-
-static void grow_table(hash_t *hash)
-{
- hnode_t **newtable;
-
- assert (2 * hash->nchains > hash->nchains); /* 1 */
-
- newtable = realloc(hash->table,
- sizeof *newtable * hash->nchains * 2); /* 4 */
-
- if (newtable) { /* 5 */
- hash_val_t mask = (hash->mask << 1) | 1; /* 3 */
- hash_val_t exposed_bit = mask ^ hash->mask; /* 6 */
- hash_val_t chain;
-
- assert (mask != hash->mask);
-
- for (chain = 0; chain < hash->nchains; chain++) { /* 7 */
- hnode_t *low_chain = 0, *high_chain = 0, *hptr, *next;
-
- for (hptr = newtable[chain]; hptr != 0; hptr = next) {
- next = hptr->next;
-
- if (hptr->hkey & exposed_bit) {
- hptr->next = high_chain;
- high_chain = hptr;
- } else {
- hptr->next = low_chain;
- low_chain = hptr;
- }
- }
-
- newtable[chain] = low_chain; /* 8 */
- newtable[chain + hash->nchains] = high_chain;
- }
-
- hash->table = newtable; /* 9 */
- hash->mask = mask;
- hash->nchains *= 2;
- hash->lowmark *= 2;
- hash->highmark *= 2;
- }
- assert (hash_verify(hash));
-}
-
-/*
- * Cut a table size in half. This is done by folding together adjacent chains
- * and populating the lower half of the table with these chains. The chains are
- * simply spliced together. Once this is done, the whole table is reallocated
- * to a smaller object.
- * Notes:
- * 1. It is illegal to have a hash table with one slot. This would mean that
- * hash->shift is equal to hash_val_t_bit, an illegal shift value.
- * Also, other things could go wrong, such as hash->lowmark becoming zero.
- * 2. Looping over each pair of sister chains, the low_chain is set to
- * point to the head node of the chain in the lower half of the table,
- * and high_chain points to the head node of the sister in the upper half.
- * 3. The intent here is to compute a pointer to the last node of the
- * lower chain into the low_tail variable. If this chain is empty,
- * low_tail ends up with a null value.
- * 4. If the lower chain is not empty, we simply tack the upper chain onto it.
- * If the upper chain is a null pointer, nothing happens.
- * 5. Otherwise if the lower chain is empty but the upper one is not,
- * If the low chain is empty, but the high chain is not, then the
- * high chain is simply transferred to the lower half of the table.
- * 6. Otherwise if both chains are empty, there is nothing to do.
- * 7. All the chain pointers are in the lower half of the table now, so
- * we reallocate it to a smaller object. This, of course, invalidates
- * all pointer-to-pointers which reference into the table from the
- * first node of each chain.
- * 8. Though it's unlikely, the reallocation may fail. In this case we
- * pretend that the table _was_ reallocated to a smaller object.
- * 9. Finally, update the various table parameters to reflect the new size.
- */
-
-static void shrink_table(hash_t *hash)
-{
- hash_val_t chain, nchains;
- hnode_t **newtable, *low_tail, *low_chain, *high_chain;
-
- assert (hash->nchains >= 2); /* 1 */
- nchains = hash->nchains / 2;
-
- for (chain = 0; chain < nchains; chain++) {
- low_chain = hash->table[chain]; /* 2 */
- high_chain = hash->table[chain + nchains];
- for (low_tail = low_chain; low_tail && low_tail->next; low_tail = low_tail->next)
- ; /* 3 */
- if (low_chain != 0) /* 4 */
- low_tail->next = high_chain;
- else if (high_chain != 0) /* 5 */
- hash->table[chain] = high_chain;
- else
- assert (hash->table[chain] == NULL); /* 6 */
- }
- newtable = realloc(hash->table,
- sizeof *newtable * nchains); /* 7 */
- if (newtable) /* 8 */
- hash->table = newtable;
- hash->mask >>= 1; /* 9 */
- hash->nchains = nchains;
- hash->lowmark /= 2;
- hash->highmark /= 2;
- assert (hash_verify(hash));
-}
-
-
-/*
- * Create a dynamic hash table. Both the hash table structure and the table
- * itself are dynamically allocated. Furthermore, the table is extendible in
- * that it will automatically grow as its load factor increases beyond a
- * certain threshold.
- * Notes:
- * 1. If the number of bits in the hash_val_t type has not been computed yet,
- * we do so here, because this is likely to be the first function that the
- * user calls.
- * 2. Allocate a hash table control structure.
- * 3. If a hash table control structure is successfully allocated, we
- * proceed to initialize it. Otherwise we return a null pointer.
- * 4. We try to allocate the table of hash chains.
- * 5. If we were able to allocate the hash chain table, we can finish
- * initializing the hash structure and the table. Otherwise, we must
- * backtrack by freeing the hash structure.
- * 6. INIT_SIZE should be a power of two. The high and low marks are always set
- * to be twice the table size and half the table size respectively. When the
- * number of nodes in the table grows beyond the high size (beyond load
- * factor 2), it will double in size to cut the load factor down to about
- * about 1. If the table shrinks down to or beneath load factor 0.5,
- * it will shrink, bringing the load up to about 1. However, the table
- * will never shrink beneath INIT_SIZE even if it's emptied.
- * 7. This indicates that the table is dynamically allocated and dynamically
- * resized on the fly. A table that has this value set to zero is
- * assumed to be statically allocated and will not be resized.
- * 8. The table of chains must be properly reset to all null pointers.
- */
-
-hash_t *hash_create(hashcount_t maxcount, hash_comp_t compfun,
- hash_fun_t hashfun)
-{
- hash_t *hash;
-
- if (hash_val_t_bit == 0) /* 1 */
- compute_bits();
-
- hash = malloc(sizeof *hash); /* 2 */
-
- if (hash) { /* 3 */
- hash->table = malloc(sizeof *hash->table * INIT_SIZE); /* 4 */
- if (hash->table) { /* 5 */
- hash->nchains = INIT_SIZE; /* 6 */
- hash->highmark = INIT_SIZE * 2;
- hash->lowmark = INIT_SIZE / 2;
- hash->nodecount = 0;
- hash->maxcount = maxcount;
- hash->compare = compfun ? compfun : hash_comp_default;
- hash->function = hashfun ? hashfun : hash_fun_default;
- hash->allocnode = hnode_alloc;
- hash->freenode = hnode_free;
- hash->context = NULL;
- hash->mask = INIT_MASK;
- hash->dynamic = 1; /* 7 */
- clear_table(hash); /* 8 */
- assert (hash_verify(hash));
- return hash;
- }
- free(hash);
- }
-
- return NULL;
-}
-
-/*
- * Select a different set of node allocator routines.
- */
-
-void hash_set_allocator(hash_t *hash, hnode_alloc_t al,
- hnode_free_t fr, void *context)
-{
- assert (hash_count(hash) == 0);
- assert ((al == 0 && fr == 0) || (al != 0 && fr != 0));
-
- hash->allocnode = al ? al : hnode_alloc;
- hash->freenode = fr ? fr : hnode_free;
- hash->context = context;
-}
-
-/*
- * Free every node in the hash using the hash->freenode() function pointer, and
- * cause the hash to become empty.
- */
-
-void hash_free_nodes(hash_t *hash)
-{
- hscan_t hs;
- hnode_t *node;
- hash_scan_begin(&hs, hash);
- while ((node = hash_scan_next(&hs))) {
- hash_scan_delete(hash, node);
- hash->freenode(node, hash->context);
- }
- hash->nodecount = 0;
- clear_table(hash);
-}
-
-/*
- * Obsolescent function for removing all nodes from a table,
- * freeing them and then freeing the table all in one step.
- */
-
-void hash_free(hash_t *hash)
-{
-#ifdef KAZLIB_OBSOLESCENT_DEBUG
- assert ("call to obsolescent function hash_free()" && 0);
-#endif
- hash_free_nodes(hash);
- hash_destroy(hash);
-}
-
-/*
- * Free a dynamic hash table structure.
- */
-
-void hash_destroy(hash_t *hash)
-{
- assert (hash_val_t_bit != 0);
- assert (hash_isempty(hash));
- free(hash->table);
- free(hash);
-}
-
-/*
- * Initialize a user supplied hash structure. The user also supplies a table of
- * chains which is assigned to the hash structure. The table is static---it
- * will not grow or shrink.
- * 1. See note 1. in hash_create().
- * 2. The user supplied array of pointers hopefully contains nchains nodes.
- * 3. See note 7. in hash_create().
- * 4. We must dynamically compute the mask from the given power of two table
- * size.
- * 5. The user supplied table can't be assumed to contain null pointers,
- * so we reset it here.
- */
-
-hash_t *hash_init(hash_t *hash, hashcount_t maxcount,
- hash_comp_t compfun, hash_fun_t hashfun, hnode_t **table,
- hashcount_t nchains)
-{
- if (hash_val_t_bit == 0) /* 1 */
- compute_bits();
-
- assert (is_power_of_two(nchains));
-
- hash->table = table; /* 2 */
- hash->nchains = nchains;
- hash->nodecount = 0;
- hash->maxcount = maxcount;
- hash->compare = compfun ? compfun : hash_comp_default;
- hash->function = hashfun ? hashfun : hash_fun_default;
- hash->dynamic = 0; /* 3 */
- hash->mask = compute_mask(nchains); /* 4 */
- clear_table(hash); /* 5 */
-
- assert (hash_verify(hash));
-
- return hash;
-}
-
-/*
- * Reset the hash scanner so that the next element retrieved by
- * hash_scan_next() shall be the first element on the first non-empty chain.
- * Notes:
- * 1. Locate the first non empty chain.
- * 2. If an empty chain is found, remember which one it is and set the next
- * pointer to refer to its first element.
- * 3. Otherwise if a chain is not found, set the next pointer to NULL
- * so that hash_scan_next() shall indicate failure.
- */
-
-void hash_scan_begin(hscan_t *scan, hash_t *hash)
-{
- hash_val_t nchains = hash->nchains;
- hash_val_t chain;
-
- scan->table = hash;
-
- /* 1 */
-
- for (chain = 0; chain < nchains && hash->table[chain] == 0; chain++)
- ;
-
- if (chain < nchains) { /* 2 */
- scan->chain = chain;
- scan->next = hash->table[chain];
- } else { /* 3 */
- scan->next = NULL;
- }
-}
-
-/*
- * Retrieve the next node from the hash table, and update the pointer
- * for the next invocation of hash_scan_next().
- * Notes:
- * 1. Remember the next pointer in a temporary value so that it can be
- * returned.
- * 2. This assertion essentially checks whether the module has been properly
- * initialized. The first point of interaction with the module should be
- * either hash_create() or hash_init(), both of which set hash_val_t_bit to
- * a non zero value.
- * 3. If the next pointer we are returning is not NULL, then the user is
- * allowed to call hash_scan_next() again. We prepare the new next pointer
- * for that call right now. That way the user is allowed to delete the node
- * we are about to return, since we will no longer be needing it to locate
- * the next node.
- * 4. If there is a next node in the chain (next->next), then that becomes the
- * new next node, otherwise ...
- * 5. We have exhausted the current chain, and must locate the next subsequent
- * non-empty chain in the table.
- * 6. If a non-empty chain is found, the first element of that chain becomes
- * the new next node. Otherwise there is no new next node and we set the
- * pointer to NULL so that the next time hash_scan_next() is called, a null
- * pointer shall be immediately returned.
- */
-
-
-hnode_t *hash_scan_next(hscan_t *scan)
-{
- hnode_t *next = scan->next; /* 1 */
- hash_t *hash = scan->table;
- hash_val_t chain = scan->chain + 1;
- hash_val_t nchains = hash->nchains;
-
- assert (hash_val_t_bit != 0); /* 2 */
-
- if (next) { /* 3 */
- if (next->next) { /* 4 */
- scan->next = next->next;
- } else {
- while (chain < nchains && hash->table[chain] == 0) /* 5 */
- chain++;
- if (chain < nchains) { /* 6 */
- scan->chain = chain;
- scan->next = hash->table[chain];
- } else {
- scan->next = NULL;
- }
- }
- }
- return next;
-}
-
-/*
- * Insert a node into the hash table.
- * Notes:
- * 1. It's illegal to insert more than the maximum number of nodes. The client
- * should verify that the hash table is not full before attempting an
- * insertion.
- * 2. The same key may not be inserted into a table twice.
- * 3. If the table is dynamic and the load factor is already at >= 2,
- * grow the table.
- * 4. We take the bottom N bits of the hash value to derive the chain index,
- * where N is the base 2 logarithm of the size of the hash table.
- */
-
-void hash_insert(hash_t *hash, hnode_t *node, const void *key)
-{
- hash_val_t hkey, chain;
-
- assert (hash_val_t_bit != 0);
- assert (node->next == NULL);
- assert (hash->nodecount < hash->maxcount); /* 1 */
- assert (hash_lookup(hash, key) == NULL); /* 2 */
-
- if (hash->dynamic && hash->nodecount >= hash->highmark) /* 3 */
- grow_table(hash);
-
- hkey = hash->function(key);
- chain = hkey & hash->mask; /* 4 */
-
- node->key = key;
- node->hkey = hkey;
- node->next = hash->table[chain];
- hash->table[chain] = node;
- hash->nodecount++;
-
- assert (hash_verify(hash));
-}
-
-/*
- * Find a node in the hash table and return a pointer to it.
- * Notes:
- * 1. We hash the key and keep the entire hash value. As an optimization, when
- * we descend down the chain, we can compare hash values first and only if
- * hash values match do we perform a full key comparison.
- * 2. To locate the chain from among 2^N chains, we look at the lower N bits of
- * the hash value by anding them with the current mask.
- * 3. Looping through the chain, we compare the stored hash value inside each
- * node against our computed hash. If they match, then we do a full
- * comparison between the unhashed keys. If these match, we have located the
- * entry.
- */
-
-hnode_t *hash_lookup(hash_t *hash, const void *key)
-{
- hash_val_t hkey, chain;
- hnode_t *nptr;
-
- hkey = hash->function(key); /* 1 */
- chain = hkey & hash->mask; /* 2 */
-
- for (nptr = hash->table[chain]; nptr; nptr = nptr->next) { /* 3 */
- if (nptr->hkey == hkey && hash->compare(nptr->key, key) == 0)
- return nptr;
- }
-
- return NULL;
-}
-
-/*
- * Delete the given node from the hash table. Since the chains
- * are singly linked, we must locate the start of the node's chain
- * and traverse.
- * Notes:
- * 1. The node must belong to this hash table, and its key must not have
- * been tampered with.
- * 2. If this deletion will take the node count below the low mark, we
- * shrink the table now.
- * 3. Determine which chain the node belongs to, and fetch the pointer
- * to the first node in this chain.
- * 4. If the node being deleted is the first node in the chain, then
- * simply update the chain head pointer.
- * 5. Otherwise advance to the node's predecessor, and splice out
- * by updating the predecessor's next pointer.
- * 6. Indicate that the node is no longer in a hash table.
- */
-
-hnode_t *hash_delete(hash_t *hash, hnode_t *node)
-{
- hash_val_t chain;
- hnode_t *hptr;
-
- assert (hash_lookup(hash, node->key) == node); /* 1 */
- assert (hash_val_t_bit != 0);
-
- if (hash->dynamic && hash->nodecount <= hash->lowmark
- && hash->nodecount > INIT_SIZE)
- shrink_table(hash); /* 2 */
-
- chain = node->hkey & hash->mask; /* 3 */
- hptr = hash->table[chain];
-
- if (hptr == node) { /* 4 */
- hash->table[chain] = node->next;
- } else {
- while (hptr->next != node) { /* 5 */
- assert (hptr != 0);
- hptr = hptr->next;
- }
- assert (hptr->next == node);
- hptr->next = node->next;
- }
-
- hash->nodecount--;
- assert (hash_verify(hash));
-
- node->next = NULL; /* 6 */
- return node;
-}
-
-int hash_alloc_insert(hash_t *hash, const void *key, void *data)
-{
- hnode_t *node = hash->allocnode(hash->context);
-
- if (node) {
- hnode_init(node, data);
- hash_insert(hash, node, key);
- return 1;
- }
- return 0;
-}
-
-void hash_delete_free(hash_t *hash, hnode_t *node)
-{
- hash_delete(hash, node);
- hash->freenode(node, hash->context);
-}
-
-/*
- * Exactly like hash_delete, except does not trigger table shrinkage. This is to be
- * used from within a hash table scan operation. See notes for hash_delete.
- */
-
-hnode_t *hash_scan_delete(hash_t *hash, hnode_t *node)
-{
- hash_val_t chain;
- hnode_t *hptr;
-
- assert (hash_lookup(hash, node->key) == node);
- assert (hash_val_t_bit != 0);
-
- chain = node->hkey & hash->mask;
- hptr = hash->table[chain];
-
- if (hptr == node) {
- hash->table[chain] = node->next;
- } else {
- while (hptr->next != node)
- hptr = hptr->next;
- hptr->next = node->next;
- }
-
- hash->nodecount--;
- assert (hash_verify(hash));
- node->next = NULL;
-
- return node;
-}
-
-/*
- * Like hash_delete_free but based on hash_scan_delete.
- */
-
-void hash_scan_delfree(hash_t *hash, hnode_t *node)
-{
- hash_scan_delete(hash, node);
- hash->freenode(node, hash->context);
-}
-
-/*
- * Verify whether the given object is a valid hash table. This means
- * Notes:
- * 1. If the hash table is dynamic, verify whether the high and
- * low expansion/shrinkage thresholds are powers of two.
- * 2. Count all nodes in the table, and test each hash value
- * to see whether it is correct for the node's chain.
- */
-
-int hash_verify(hash_t *hash)
-{
- hashcount_t count = 0;
- hash_val_t chain;
- hnode_t *hptr;
-
- if (hash->dynamic) { /* 1 */
- if (hash->lowmark >= hash->highmark)
- return 0;
- if (!is_power_of_two(hash->highmark))
- return 0;
- if (!is_power_of_two(hash->lowmark))
- return 0;
- }
-
- for (chain = 0; chain < hash->nchains; chain++) { /* 2 */
- for (hptr = hash->table[chain]; hptr != 0; hptr = hptr->next) {
- if ((hptr->hkey & hash->mask) != chain)
- return 0;
- count++;
- }
- }
-
- if (count != hash->nodecount)
- return 0;
-
- return 1;
-}
-
-/*
- * Test whether the hash table is full and return 1 if this is true,
- * 0 if it is false.
- */
-
-#undef hash_isfull
-int hash_isfull(hash_t *hash)
-{
- return hash->nodecount == hash->maxcount;
-}
-
-/*
- * Test whether the hash table is empty and return 1 if this is true,
- * 0 if it is false.
- */
-
-#undef hash_isempty
-int hash_isempty(hash_t *hash)
-{
- return hash->nodecount == 0;
-}
-
-static hnode_t *hnode_alloc(void *context)
-{
- return malloc(sizeof *hnode_alloc(NULL));
-}
-
-static void hnode_free(hnode_t *node, void *context)
-{
- free(node);
-}
-
-
-/*
- * Create a hash table node dynamically and assign it the given data.
- */
-
-hnode_t *hnode_create(void *data)
-{
- hnode_t *node = malloc(sizeof *node);
- if (node) {
- node->data = data;
- node->next = NULL;
- }
- return node;
-}
-
-/*
- * Initialize a client-supplied node
- */
-
-hnode_t *hnode_init(hnode_t *hnode, void *data)
-{
- hnode->data = data;
- hnode->next = NULL;
- return hnode;
-}
-
-/*
- * Destroy a dynamically allocated node.
- */
-
-void hnode_destroy(hnode_t *hnode)
-{
- free(hnode);
-}
-
-#undef hnode_put
-void hnode_put(hnode_t *node, void *data)
-{
- node->data = data;
-}
-
-#undef hnode_get
-void *hnode_get(hnode_t *node)
-{
- return node->data;
-}
-
-#undef hnode_getkey
-const void *hnode_getkey(hnode_t *node)
-{
- return node->key;
-}
-
-#undef hash_count
-hashcount_t hash_count(hash_t *hash)
-{
- return hash->nodecount;
-}
-
-#undef hash_size
-hashcount_t hash_size(hash_t *hash)
-{
- return hash->nchains;
-}
-
-static hash_val_t hash_fun_default(const void *key)
-{
- static unsigned long randbox[] = {
- 0x49848f1bU, 0xe6255dbaU, 0x36da5bdcU, 0x47bf94e9U,
- 0x8cbcce22U, 0x559fc06aU, 0xd268f536U, 0xe10af79aU,
- 0xc1af4d69U, 0x1d2917b5U, 0xec4c304dU, 0x9ee5016cU,
- 0x69232f74U, 0xfead7bb3U, 0xe9089ab6U, 0xf012f6aeU,
- };
-
- const unsigned char *str = key;
- hash_val_t acc = 0;
-
- while (*str) {
- acc ^= randbox[(*str + acc) & 0xf];
- acc = (acc << 1) | (acc >> 31);
- acc &= 0xffffffffU;
- acc ^= randbox[((*str++ >> 4) + acc) & 0xf];
- acc = (acc << 2) | (acc >> 30);
- acc &= 0xffffffffU;
- }
- return acc;
-}
-
-static int hash_comp_default(const void *key1, const void *key2)
-{
- return strcmp(key1, key2);
-}
diff --git a/libutil/kazlib/hash.h b/libutil/kazlib/hash.h
deleted file mode 100644
index e8213f7..0000000
--- a/libutil/kazlib/hash.h
+++ /dev/null
@@ -1,238 +0,0 @@
-/*
- * Hash Table Data Type
- * Copyright (C) 1997 Kaz Kylheku <kaz at ashi.footprints.net>
- *
- * Free Software License:
- *
- * All rights are reserved by the author, with the following exceptions:
- * Permission is granted to freely reproduce and distribute this software,
- * possibly in exchange for a fee, provided that this copyright notice appears
- * intact. Permission is also granted to adapt this software to produce
- * derivative works, as long as the modified versions carry this copyright
- * notice and additional notices stating that the work has been modified.
- * This source code may be translated into executable form and incorporated
- * into proprietary software; there is no requirement for such software to
- * contain a copyright notice related to this source.
- *
- */
-
-#ifndef HASH_H
-#define HASH_H
-
-#include <limits.h>
-#ifdef KAZLIB_SIDEEFFECT_DEBUG
-#include "sfx.h"
-#endif
-
-/*
- * Blurb for inclusion into C++ translation units
- */
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-typedef unsigned long hashcount_t;
-#define HASHCOUNT_T_MAX ULONG_MAX
-
-typedef unsigned long hash_val_t;
-#define HASH_VAL_T_MAX ULONG_MAX
-
-extern int hash_val_t_bit;
-
-#ifndef HASH_VAL_T_BIT
-#define HASH_VAL_T_BIT ((int) hash_val_t_bit)
-#endif
-
-/*
- * Hash chain node structure.
- * Notes:
- * 1. This preprocessing directive is for debugging purposes. The effect is
- * that if the preprocessor symbol KAZLIB_OPAQUE_DEBUG is defined prior to the
- * inclusion of this header, then the structure shall be declared as having
- * the single member int __OPAQUE__. This way, any attempts by the
- * client code to violate the principles of information hiding (by accessing
- * the structure directly) can be diagnosed at translation time. However,
- * note the resulting compiled unit is not suitable for linking.
- * 2. This is a pointer to the next node in the chain. In the last node of a
- * chain, this pointer is null.
- * 3. The key is a pointer to some user supplied data that contains a unique
- * identifier for each hash node in a given table. The interpretation of
- * the data is up to the user. When creating or initializing a hash table,
- * the user must supply a pointer to a function for comparing two keys,
- * and a pointer to a function for hashing a key into a numeric value.
- * 4. The value is a user-supplied pointer to void which may refer to
- * any data object. It is not interpreted in any way by the hashing
- * module.
- * 5. The hashed key is stored in each node so that we don't have to rehash
- * each key when the table must grow or shrink.
- */
-
-typedef struct hnode_t {
-#if defined(HASH_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG) /* 1 */
- struct hnode_t *hash_next; /* 2 */
- const void *hash_key; /* 3 */
- void *hash_data; /* 4 */
- hash_val_t hash_hkey; /* 5 */
-#else
- int hash_dummy;
-#endif
-} hnode_t;
-
-/*
- * The comparison function pointer type. A comparison function takes two keys
- * and produces a value of -1 if the left key is less than the right key, a
- * value of 0 if the keys are equal, and a value of 1 if the left key is
- * greater than the right key.
- */
-
-typedef int (*hash_comp_t)(const void *, const void *);
-
-/*
- * The hashing function performs some computation on a key and produces an
- * integral value of type hash_val_t based on that key. For best results, the
- * function should have a good randomness properties in *all* significant bits
- * over the set of keys that are being inserted into a given hash table. In
- * particular, the most significant bits of hash_val_t are most significant to
- * the hash module. Only as the hash table expands are less significant bits
- * examined. Thus a function that has good distribution in its upper bits but
- * not lower is preferrable to one that has poor distribution in the upper bits
- * but not the lower ones.
- */
-
-typedef hash_val_t (*hash_fun_t)(const void *);
-
-/*
- * allocator functions
- */
-
-typedef hnode_t *(*hnode_alloc_t)(void *);
-typedef void (*hnode_free_t)(hnode_t *, void *);
-
-/*
- * This is the hash table control structure. It keeps track of information
- * about a hash table, as well as the hash table itself.
- * Notes:
- * 1. Pointer to the hash table proper. The table is an array of pointers to
- * hash nodes (of type hnode_t). If the table is empty, every element of
- * this table is a null pointer. A non-null entry points to the first
- * element of a chain of nodes.
- * 2. This member keeps track of the size of the hash table---that is, the
- * number of chain pointers.
- * 3. The count member maintains the number of elements that are presently
- * in the hash table.
- * 4. The maximum count is the greatest number of nodes that can populate this
- * table. If the table contains this many nodes, no more can be inserted,
- * and the hash_isfull() function returns true.
- * 5. The high mark is a population threshold, measured as a number of nodes,
- * which, if exceeded, will trigger a table expansion. Only dynamic hash
- * tables are subject to this expansion.
- * 6. The low mark is a minimum population threshold, measured as a number of
- * nodes. If the table population drops below this value, a table shrinkage
- * will occur. Only dynamic tables are subject to this reduction. No table
- * will shrink beneath a certain absolute minimum number of nodes.
- * 7. This is the a pointer to the hash table's comparison function. The
- * function is set once at initialization or creation time.
- * 8. Pointer to the table's hashing function, set once at creation or
- * initialization time.
- * 9. The current hash table mask. If the size of the hash table is 2^N,
- * this value has its low N bits set to 1, and the others clear. It is used
- * to select bits from the result of the hashing function to compute an
- * index into the table.
- * 10. A flag which indicates whether the table is to be dynamically resized. It
- * is set to 1 in dynamically allocated tables, 0 in tables that are
- * statically allocated.
- */
-
-typedef struct hash_t {
-#if defined(HASH_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG)
- struct hnode_t **hash_table; /* 1 */
- hashcount_t hash_nchains; /* 2 */
- hashcount_t hash_nodecount; /* 3 */
- hashcount_t hash_maxcount; /* 4 */
- hashcount_t hash_highmark; /* 5 */
- hashcount_t hash_lowmark; /* 6 */
- hash_comp_t hash_compare; /* 7 */
- hash_fun_t hash_function; /* 8 */
- hnode_alloc_t hash_allocnode;
- hnode_free_t hash_freenode;
- void *hash_context;
- hash_val_t hash_mask; /* 9 */
- int hash_dynamic; /* 10 */
-#else
- int hash_dummy;
-#endif
-} hash_t;
-
-/*
- * Hash scanner structure, used for traversals of the data structure.
- * Notes:
- * 1. Pointer to the hash table that is being traversed.
- * 2. Reference to the current chain in the table being traversed (the chain
- * that contains the next node that shall be retrieved).
- * 3. Pointer to the node that will be retrieved by the subsequent call to
- * hash_scan_next().
- */
-
-typedef struct hscan_t {
-#if defined(HASH_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG)
- hash_t *hash_table; /* 1 */
- hash_val_t hash_chain; /* 2 */
- hnode_t *hash_next; /* 3 */
-#else
- int hash_dummy;
-#endif
-} hscan_t;
-
-extern hash_t *hash_create(hashcount_t, hash_comp_t, hash_fun_t);
-extern void hash_set_allocator(hash_t *, hnode_alloc_t, hnode_free_t, void *);
-extern void hash_destroy(hash_t *);
-extern void hash_free_nodes(hash_t *);
-extern void hash_free(hash_t *);
-extern hash_t *hash_init(hash_t *, hashcount_t, hash_comp_t,
- hash_fun_t, hnode_t **, hashcount_t);
-extern void hash_insert(hash_t *, hnode_t *, const void *);
-extern hnode_t *hash_lookup(hash_t *, const void *);
-extern hnode_t *hash_delete(hash_t *, hnode_t *);
-extern int hash_alloc_insert(hash_t *, const void *, void *);
-extern void hash_delete_free(hash_t *, hnode_t *);
-
-extern void hnode_put(hnode_t *, void *);
-extern void *hnode_get(hnode_t *);
-extern const void *hnode_getkey(hnode_t *);
-extern hashcount_t hash_count(hash_t *);
-extern hashcount_t hash_size(hash_t *);
-
-extern int hash_isfull(hash_t *);
-extern int hash_isempty(hash_t *);
-
-extern void hash_scan_begin(hscan_t *, hash_t *);
-extern hnode_t *hash_scan_next(hscan_t *);
-extern hnode_t *hash_scan_delete(hash_t *, hnode_t *);
-extern void hash_scan_delfree(hash_t *, hnode_t *);
-
-extern int hash_verify(hash_t *);
-
-extern hnode_t *hnode_create(void *);
-extern hnode_t *hnode_init(hnode_t *, void *);
-extern void hnode_destroy(hnode_t *);
-
-#if defined(HASH_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG)
-#ifdef KAZLIB_SIDEEFFECT_DEBUG
-#define hash_isfull(H) (SFX_CHECK(H)->hash_nodecount == (H)->hash_maxcount)
-#else
-#define hash_isfull(H) ((H)->hash_nodecount == (H)->hash_maxcount)
-#endif
-#define hash_isempty(H) ((H)->hash_nodecount == 0)
-#define hash_count(H) ((H)->hash_nodecount)
-#define hash_size(H) ((H)->hash_nchains)
-#define hnode_get(N) ((N)->hash_data)
-#define hnode_getkey(N) ((N)->hash_key)
-#define hnode_put(N, V) ((N)->hash_data = (V))
-#endif
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/libutil/kazlib/list.c b/libutil/kazlib/list.c
deleted file mode 100644
index 818b427..0000000
--- a/libutil/kazlib/list.c
+++ /dev/null
@@ -1,766 +0,0 @@
-/*
- * List Abstract Data Type
- * Copyright (C) 1997 Kaz Kylheku <kaz at ashi.footprints.net>
- *
- * Free Software License:
- *
- * All rights are reserved by the author, with the following exceptions:
- * Permission is granted to freely reproduce and distribute this software,
- * possibly in exchange for a fee, provided that this copyright notice appears
- * intact. Permission is also granted to adapt this software to produce
- * derivative works, as long as the modified versions carry this copyright
- * notice and additional notices stating that the work has been modified.
- * This source code may be translated into executable form and incorporated
- * into proprietary software; there is no requirement for such software to
- * contain a copyright notice related to this source.
- *
- */
-
-
-#include <stdlib.h>
-#include <stddef.h>
-#include <assert.h>
-#define LIST_IMPLEMENTATION
-#include "list.h"
-
-#define next list_next
-#define prev list_prev
-#define data list_data
-
-#define pool list_pool
-#define fre list_free
-#define size list_size
-
-#define nilnode list_nilnode
-#define nodecount list_nodecount
-#define maxcount list_maxcount
-
-#define list_nil(L) (&(L)->nilnode)
-#define list_first_priv(L) ((L)->nilnode.next)
-#define list_last_priv(L) ((L)->nilnode.prev)
-#define lnode_next(N) ((N)->next)
-#define lnode_prev(N) ((N)->prev)
-
-/*
- * Initialize a list object supplied by the client such that it becomes a valid
- * empty list. If the list is to be ``unbounded'', the maxcount should be
- * specified as LISTCOUNT_T_MAX, or, alternately, as -1. The value zero
- * is not permitted.
- */
-
-list_t *list_init(list_t *list, listcount_t maxcount)
-{
- assert (maxcount != 0);
- list->nilnode.next = &list->nilnode;
- list->nilnode.prev = &list->nilnode;
- list->nodecount = 0;
- list->maxcount = maxcount;
- return list;
-}
-
-/*
- * Dynamically allocate a list object using malloc(), and initialize it so that
- * it is a valid empty list. If the list is to be ``unbounded'', the maxcount
- * should be specified as LISTCOUNT_T_MAX, or, alternately, as -1.
- */
-
-list_t *list_create(listcount_t maxcount)
-{
- list_t *new = malloc(sizeof *new);
- if (new) {
- assert (maxcount != 0);
- new->nilnode.next = &new->nilnode;
- new->nilnode.prev = &new->nilnode;
- new->nodecount = 0;
- new->maxcount = maxcount;
- }
- return new;
-}
-
-/*
- * Destroy a dynamically allocated list object.
- * The client must remove the nodes first.
- */
-
-void list_destroy(list_t *list)
-{
- assert (list_isempty(list));
- free(list);
-}
-
-/*
- * Free all of the nodes of a list. The list must contain only
- * dynamically allocated nodes. After this call, the list
- * is empty.
- */
-
-void list_destroy_nodes(list_t *list)
-{
- lnode_t *lnode = list_first_priv(list), *nil = list_nil(list), *tmp;
-
- while (lnode != nil) {
- tmp = lnode->next;
- lnode->next = NULL;
- lnode->prev = NULL;
- lnode_destroy(lnode);
- lnode = tmp;
- }
-
- list_init(list, list->maxcount);
-}
-
-/*
- * Return all of the nodes of a list to a node pool. The nodes in
- * the list must all have come from the same pool.
- */
-
-void list_return_nodes(list_t *list, lnodepool_t *pool)
-{
- lnode_t *lnode = list_first_priv(list), *tmp, *nil = list_nil(list);
-
- while (lnode != nil) {
- tmp = lnode->next;
- lnode->next = NULL;
- lnode->prev = NULL;
- lnode_return(pool, lnode);
- lnode = tmp;
- }
-
- list_init(list, list->maxcount);
-}
-
-/*
- * Insert the node ``new'' into the list immediately after ``this'' node.
- */
-
-void list_ins_after(list_t *list, lnode_t *new, lnode_t *this)
-{
- lnode_t *that = this->next;
-
- assert (new != NULL);
- assert (!list_contains(list, new));
- assert (!lnode_is_in_a_list(new));
- assert (this == list_nil(list) || list_contains(list, this));
- assert (list->nodecount + 1 > list->nodecount);
-
- new->prev = this;
- new->next = that;
- that->prev = new;
- this->next = new;
- list->nodecount++;
-
- assert (list->nodecount <= list->maxcount);
-}
-
-/*
- * Insert the node ``new'' into the list immediately before ``this'' node.
- */
-
-void list_ins_before(list_t *list, lnode_t *new, lnode_t *this)
-{
- lnode_t *that = this->prev;
-
- assert (new != NULL);
- assert (!list_contains(list, new));
- assert (!lnode_is_in_a_list(new));
- assert (this == list_nil(list) || list_contains(list, this));
- assert (list->nodecount + 1 > list->nodecount);
-
- new->next = this;
- new->prev = that;
- that->next = new;
- this->prev = new;
- list->nodecount++;
-
- assert (list->nodecount <= list->maxcount);
-}
-
-/*
- * Delete the given node from the list.
- */
-
-lnode_t *list_delete(list_t *list, lnode_t *del)
-{
- lnode_t *next = del->next;
- lnode_t *prev = del->prev;
-
- assert (list_contains(list, del));
-
- prev->next = next;
- next->prev = prev;
- list->nodecount--;
-
- del->next = del->prev = NULL;
-
- return del;
-}
-
-/*
- * For each node in the list, execute the given function. The list,
- * current node and the given context pointer are passed on each
- * call to the function.
- */
-
-void list_process(list_t *list, void *context,
- void (* function)(list_t *list, lnode_t *lnode, void *context))
-{
- lnode_t *node = list_first_priv(list), *next, *nil = list_nil(list);
-
- while (node != nil) {
- /* check for callback function deleting */
- /* the next node from under us */
- assert (list_contains(list, node));
- next = node->next;
- function(list, node, context);
- node = next;
- }
-}
-
-/*
- * Dynamically allocate a list node and assign it the given piece of data.
- */
-
-lnode_t *lnode_create(void *data)
-{
- lnode_t *new = malloc(sizeof *new);
- if (new) {
- new->data = data;
- new->next = NULL;
- new->prev = NULL;
- }
- return new;
-}
-
-/*
- * Initialize a user-supplied lnode.
- */
-
-lnode_t *lnode_init(lnode_t *lnode, void *data)
-{
- lnode->data = data;
- lnode->next = NULL;
- lnode->prev = NULL;
- return lnode;
-}
-
-/*
- * Destroy a dynamically allocated node.
- */
-
-void lnode_destroy(lnode_t *lnode)
-{
- assert (!lnode_is_in_a_list(lnode));
- free(lnode);
-}
-
-/*
- * Initialize a node pool object to use a user-supplied set of nodes.
- * The ``nodes'' pointer refers to an array of lnode_t objects, containing
- * ``n'' elements.
- */
-
-lnodepool_t *lnode_pool_init(lnodepool_t *pool, lnode_t *nodes, listcount_t n)
-{
- listcount_t i;
-
- assert (n != 0);
-
- pool->pool = nodes;
- pool->fre = nodes;
- pool->size = n;
- for (i = 0; i < n - 1; i++) {
- nodes[i].next = nodes + i + 1;
- }
- nodes[i].next = NULL;
- nodes[i].prev = nodes; /* to make sure node is marked ``on list'' */
- return pool;
-}
-
-/*
- * Create a dynamically allocated pool of n nodes.
- */
-
-lnodepool_t *lnode_pool_create(listcount_t n)
-{
- lnodepool_t *pool;
- lnode_t *nodes;
-
- assert (n != 0);
-
- pool = malloc(sizeof *pool);
- if (!pool)
- return NULL;
- nodes = malloc(n * sizeof *nodes);
- if (!nodes) {
- free(pool);
- return NULL;
- }
- lnode_pool_init(pool, nodes, n);
- return pool;
-}
-
-/*
- * Determine whether the given pool is from this pool.
- */
-
-int lnode_pool_isfrom(lnodepool_t *pool, lnode_t *node)
-{
- listcount_t i;
-
- /* this is carefully coded this way because ANSI C forbids pointers
- to different objects from being subtracted or compared other
- than for exact equality */
-
- for (i = 0; i < pool->size; i++) {
- if (pool->pool + i == node)
- return 1;
- }
- return 0;
-}
-
-/*
- * Destroy a dynamically allocated pool of nodes.
- */
-
-void lnode_pool_destroy(lnodepool_t *p)
-{
- free(p->pool);
- free(p);
-}
-
-/*
- * Borrow a node from a node pool. Returns a null pointer if the pool
- * is exhausted.
- */
-
-lnode_t *lnode_borrow(lnodepool_t *pool, void *data)
-{
- lnode_t *new = pool->fre;
- if (new) {
- pool->fre = new->next;
- new->data = data;
- new->next = NULL;
- new->prev = NULL;
- }
- return new;
-}
-
-/*
- * Return a node to a node pool. A node must be returned to the pool
- * from which it came.
- */
-
-void lnode_return(lnodepool_t *pool, lnode_t *node)
-{
- assert (lnode_pool_isfrom(pool, node));
- assert (!lnode_is_in_a_list(node));
-
- node->next = pool->fre;
- node->prev = node;
- pool->fre = node;
-}
-
-/*
- * Determine whether the given list contains the given node.
- * According to this function, a list does not contain its nilnode.
- */
-
-int list_contains(list_t *list, lnode_t *node)
-{
- lnode_t *n, *nil = list_nil(list);
-
- for (n = list_first_priv(list); n != nil; n = lnode_next(n)) {
- if (node == n)
- return 1;
- }
-
- return 0;
-}
-
-/*
- * A more generalized variant of list_transfer. This one removes a
- * ``slice'' from the source list and appends it to the destination
- * list.
- */
-
-void list_extract(list_t *dest, list_t *source, lnode_t *first, lnode_t *last)
-{
- listcount_t moved = 1;
-
- assert (first == NULL || list_contains(source, first));
- assert (last == NULL || list_contains(source, last));
-
- if (first == NULL || last == NULL)
- return;
-
- /* adjust the destination list so that the slice is spliced out */
-
- first->prev->next = last->next;
- last->next->prev = first->prev;
-
- /* graft the splice at the end of the dest list */
-
- last->next = &dest->nilnode;
- first->prev = dest->nilnode.prev;
- dest->nilnode.prev->next = first;
- dest->nilnode.prev = last;
-
- while (first != last) {
- first = first->next;
- assert (first != list_nil(source)); /* oops, last before first! */
- moved++;
- }
-
- /* assert no overflows */
- assert (source->nodecount - moved <= source->nodecount);
- assert (dest->nodecount + moved >= dest->nodecount);
-
- /* assert no weirdness */
- assert (moved <= source->nodecount);
-
- source->nodecount -= moved;
- dest->nodecount += moved;
-
- /* assert list sanity */
- assert (list_verify(source));
- assert (list_verify(dest));
-}
-
-
-/*
- * Split off a trailing sequence of nodes from the source list and relocate
- * them to the tail of the destination list. The trailing sequence begins
- * with node ``first'' and terminates with the last node of the source
- * list. The nodes are added to the end of the new list in their original
- * order.
- */
-
-void list_transfer(list_t *dest, list_t *source, lnode_t *first)
-{
- listcount_t moved = 1;
- lnode_t *last;
-
- assert (first == NULL || list_contains(source, first));
-
- if (first == NULL)
- return;
-
- last = source->nilnode.prev;
-
- source->nilnode.prev = first->prev;
- first->prev->next = &source->nilnode;
-
- last->next = &dest->nilnode;
- first->prev = dest->nilnode.prev;
- dest->nilnode.prev->next = first;
- dest->nilnode.prev = last;
-
- while (first != last) {
- first = first->next;
- moved++;
- }
-
- /* assert no overflows */
- assert (source->nodecount - moved <= source->nodecount);
- assert (dest->nodecount + moved >= dest->nodecount);
-
- /* assert no weirdness */
- assert (moved <= source->nodecount);
-
- source->nodecount -= moved;
- dest->nodecount += moved;
-
- /* assert list sanity */
- assert (list_verify(source));
- assert (list_verify(dest));
-}
-
-void list_merge(list_t *dest, list_t *sour,
- int compare (const void *, const void *))
-{
- lnode_t *dn, *sn, *tn;
- lnode_t *d_nil = list_nil(dest), *s_nil = list_nil(sour);
-
- /* Nothing to do if source and destination list are the same. */
- if (dest == sour)
- return;
-
- /* overflow check */
- assert (list_count(sour) + list_count(dest) >= list_count(sour));
-
- /* lists must be sorted */
- assert (list_is_sorted(sour, compare));
- assert (list_is_sorted(dest, compare));
-
- dn = list_first_priv(dest);
- sn = list_first_priv(sour);
-
- while (dn != d_nil && sn != s_nil) {
- if (compare(lnode_get(dn), lnode_get(sn)) >= 0) {
- tn = lnode_next(sn);
- list_delete(sour, sn);
- list_ins_before(dest, sn, dn);
- sn = tn;
- } else {
- dn = lnode_next(dn);
- }
- }
-
- if (dn != d_nil)
- return;
-
- if (sn != s_nil)
- list_transfer(dest, sour, sn);
-}
-
-void list_sort(list_t *list, int compare(const void *, const void *))
-{
- list_t extra;
- listcount_t middle;
- lnode_t *node;
-
- if (list_count(list) > 1) {
- middle = list_count(list) / 2;
- node = list_first_priv(list);
-
- list_init(&extra, list_count(list) - middle);
-
- while (middle--)
- node = lnode_next(node);
-
- list_transfer(&extra, list, node);
- list_sort(list, compare);
- list_sort(&extra, compare);
- list_merge(list, &extra, compare);
- }
- assert (list_is_sorted(list, compare));
-}
-
-lnode_t *list_find(list_t *list, const void *key, int compare(const void *, const void *))
-{
- lnode_t *node;
-
- for (node = list_first_priv(list); node != list_nil(list); node = node->next) {
- if (compare(lnode_get(node), key) == 0)
- return node;
- }
-
- return 0;
-}
-
-
-/*
- * Return 1 if the list is in sorted order, 0 otherwise
- */
-
-int list_is_sorted(list_t *list, int compare(const void *, const void *))
-{
- lnode_t *node, *next, *nil;
-
- next = nil = list_nil(list);
- node = list_first_priv(list);
-
- if (node != nil)
- next = lnode_next(node);
-
- for (; next != nil; node = next, next = lnode_next(next)) {
- if (compare(lnode_get(node), lnode_get(next)) > 0)
- return 0;
- }
-
- return 1;
-}
-
-/*
- * Get rid of macro functions definitions so they don't interfere
- * with the actual definitions
- */
-
-#undef list_isempty
-#undef list_isfull
-#undef lnode_pool_isempty
-#undef list_append
-#undef list_prepend
-#undef list_first
-#undef list_last
-#undef list_next
-#undef list_prev
-#undef list_count
-#undef list_del_first
-#undef list_del_last
-#undef lnode_put
-#undef lnode_get
-
-/*
- * Return 1 if the list is empty, 0 otherwise
- */
-
-int list_isempty(list_t *list)
-{
- return list->nodecount == 0;
-}
-
-/*
- * Return 1 if the list is full, 0 otherwise
- * Permitted only on bounded lists.
- */
-
-int list_isfull(list_t *list)
-{
- return list->nodecount == list->maxcount;
-}
-
-/*
- * Check if the node pool is empty.
- */
-
-int lnode_pool_isempty(lnodepool_t *pool)
-{
- return (pool->fre == NULL);
-}
-
-/*
- * Add the given node at the end of the list
- */
-
-void list_append(list_t *list, lnode_t *node)
-{
- list_ins_before(list, node, &list->nilnode);
-}
-
-/*
- * Add the given node at the beginning of the list.
- */
-
-void list_prepend(list_t *list, lnode_t *node)
-{
- list_ins_after(list, node, &list->nilnode);
-}
-
-/*
- * Retrieve the first node of the list
- */
-
-lnode_t *list_first(list_t *list)
-{
- if (list->nilnode.next == &list->nilnode)
- return NULL;
- return list->nilnode.next;
-}
-
-/*
- * Retrieve the last node of the list
- */
-
-lnode_t *list_last(list_t *list)
-{
- if (list->nilnode.prev == &list->nilnode)
- return NULL;
- return list->nilnode.prev;
-}
-
-/*
- * Retrieve the count of nodes in the list
- */
-
-listcount_t list_count(list_t *list)
-{
- return list->nodecount;
-}
-
-/*
- * Remove the first node from the list and return it.
- */
-
-lnode_t *list_del_first(list_t *list)
-{
- return list_delete(list, list->nilnode.next);
-}
-
-/*
- * Remove the last node from the list and return it.
- */
-
-lnode_t *list_del_last(list_t *list)
-{
- return list_delete(list, list->nilnode.prev);
-}
-
-
-/*
- * Associate a data item with the given node.
- */
-
-void lnode_put(lnode_t *lnode, void *data)
-{
- lnode->data = data;
-}
-
-/*
- * Retrieve the data item associated with the node.
- */
-
-void *lnode_get(lnode_t *lnode)
-{
- return lnode->data;
-}
-
-/*
- * Retrieve the node's successor. If there is no successor,
- * NULL is returned.
- */
-
-lnode_t *list_next(list_t *list, lnode_t *lnode)
-{
- assert (list_contains(list, lnode));
-
- if (lnode->next == list_nil(list))
- return NULL;
- return lnode->next;
-}
-
-/*
- * Retrieve the node's predecessor. See comment for lnode_next().
- */
-
-lnode_t *list_prev(list_t *list, lnode_t *lnode)
-{
- assert (list_contains(list, lnode));
-
- if (lnode->prev == list_nil(list))
- return NULL;
- return lnode->prev;
-}
-
-/*
- * Return 1 if the lnode is in some list, otherwise return 0.
- */
-
-int lnode_is_in_a_list(lnode_t *lnode)
-{
- return (lnode->next != NULL || lnode->prev != NULL);
-}
-
-
-int list_verify(list_t *list)
-{
- lnode_t *node = list_first_priv(list), *nil = list_nil(list);
- listcount_t count = list_count(list);
-
- if (node->prev != nil)
- return 0;
-
- if (count > list->maxcount)
- return 0;
-
- while (node != nil && count--) {
- if (node->next->prev != node)
- return 0;
- node = node->next;
- }
-
- if (count != 0 || node != nil)
- return 0;
-
- return 1;
-}
diff --git a/libutil/kazlib/list.h b/libutil/kazlib/list.h
deleted file mode 100644
index 97abc2f..0000000
--- a/libutil/kazlib/list.h
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * List Abstract Data Type
- * Copyright (C) 1997 Kaz Kylheku <kaz at ashi.footprints.net>
- *
- * Free Software License:
- *
- * All rights are reserved by the author, with the following exceptions:
- * Permission is granted to freely reproduce and distribute this software,
- * possibly in exchange for a fee, provided that this copyright notice appears
- * intact. Permission is also granted to adapt this software to produce
- * derivative works, as long as the modified versions carry this copyright
- * notice and additional notices stating that the work has been modified.
- * This source code may be translated into executable form and incorporated
- * into proprietary software; there is no requirement for such software to
- * contain a copyright notice related to this source.
- *
- */
-
-#ifndef LIST_H
-#define LIST_H
-
-#include <limits.h>
-
-#ifdef KAZLIB_SIDEEFFECT_DEBUG
-#include "sfx.h"
-#define LIST_SFX_CHECK(E) SFX_CHECK(E)
-#else
-#define LIST_SFX_CHECK(E) (E)
-#endif
-
-/*
- * Blurb for inclusion into C++ translation units
- */
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-typedef unsigned long listcount_t;
-#define LISTCOUNT_T_MAX ULONG_MAX
-
-typedef struct lnode_t {
-#if defined(LIST_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG)
- struct lnode_t *list_next;
- struct lnode_t *list_prev;
- void *list_data;
-#else
- int list_dummy;
-#endif
-} lnode_t;
-
-typedef struct lnodepool_t {
-#if defined(LIST_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG)
- struct lnode_t *list_pool;
- struct lnode_t *list_free;
- listcount_t list_size;
-#else
- int list_dummy;
-#endif
-} lnodepool_t;
-
-typedef struct list_t {
-#if defined(LIST_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG)
- lnode_t list_nilnode;
- listcount_t list_nodecount;
- listcount_t list_maxcount;
-#else
- int list_dummy;
-#endif
-} list_t;
-
-lnode_t *lnode_create(void *);
-lnode_t *lnode_init(lnode_t *, void *);
-void lnode_destroy(lnode_t *);
-void lnode_put(lnode_t *, void *);
-void *lnode_get(lnode_t *);
-int lnode_is_in_a_list(lnode_t *);
-
-#if defined(LIST_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG)
-#define lnode_put(N, D) ((N)->list_data = (D))
-#define lnode_get(N) ((N)->list_data)
-#endif
-
-lnodepool_t *lnode_pool_init(lnodepool_t *, lnode_t *, listcount_t);
-lnodepool_t *lnode_pool_create(listcount_t);
-void lnode_pool_destroy(lnodepool_t *);
-lnode_t *lnode_borrow(lnodepool_t *, void *);
-void lnode_return(lnodepool_t *, lnode_t *);
-int lnode_pool_isempty(lnodepool_t *);
-int lnode_pool_isfrom(lnodepool_t *, lnode_t *);
-
-list_t *list_init(list_t *, listcount_t);
-list_t *list_create(listcount_t);
-void list_destroy(list_t *);
-void list_destroy_nodes(list_t *);
-void list_return_nodes(list_t *, lnodepool_t *);
-
-listcount_t list_count(list_t *);
-int list_isempty(list_t *);
-int list_isfull(list_t *);
-int list_contains(list_t *, lnode_t *);
-
-void list_append(list_t *, lnode_t *);
-void list_prepend(list_t *, lnode_t *);
-void list_ins_before(list_t *, lnode_t *, lnode_t *);
-void list_ins_after(list_t *, lnode_t *, lnode_t *);
-
-lnode_t *list_first(list_t *);
-lnode_t *list_last(list_t *);
-lnode_t *list_next(list_t *, lnode_t *);
-lnode_t *list_prev(list_t *, lnode_t *);
-
-lnode_t *list_del_first(list_t *);
-lnode_t *list_del_last(list_t *);
-lnode_t *list_delete(list_t *, lnode_t *);
-
-void list_process(list_t *, void *, void (*)(list_t *, lnode_t *, void *));
-
-int list_verify(list_t *);
-
-#if defined(LIST_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG)
-#define lnode_pool_isempty(P) ((P)->list_free == 0)
-#define list_count(L) ((L)->list_nodecount)
-#define list_isempty(L) ((L)->list_nodecount == 0)
-#define list_isfull(L) (LIST_SFX_CHECK(L)->list_nodecount == (L)->list_maxcount)
-#define list_next(L, N) (LIST_SFX_CHECK(N)->list_next == &(L)->list_nilnode ? NULL : (N)->list_next)
-#define list_prev(L, N) (LIST_SFX_CHECK(N)->list_prev == &(L)->list_nilnode ? NULL : (N)->list_prev)
-#define list_first(L) list_next(LIST_SFX_CHECK(L), &(L)->list_nilnode)
-#define list_last(L) list_prev(LIST_SFX_CHECK(L), &(L)->list_nilnode)
-#endif
-
-#if defined(LIST_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG)
-#define list_append(L, N) list_ins_before(LIST_SFX_CHECK(L), N, &(L)->list_nilnode)
-#define list_prepend(L, N) list_ins_after(LIST_SFX_CHECK(L), N, &(L)->list_nilnode)
-#define list_del_first(L) list_delete(LIST_SFX_CHECK(L), list_first(L))
-#define list_del_last(L) list_delete(LIST_SFX_CHECK(L), list_last(L))
-#endif
-
-/* destination list on the left, source on the right */
-
-void list_extract(list_t *, list_t *, lnode_t *, lnode_t *);
-void list_transfer(list_t *, list_t *, lnode_t *first);
-void list_merge(list_t *, list_t *, int (const void *, const void *));
-void list_sort(list_t *, int (const void *, const void *));
-lnode_t *list_find(list_t *, const void *, int (const void *, const void *));
-int list_is_sorted(list_t *, int (const void *, const void *));
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/libutil/kazlib/sfx.c b/libutil/kazlib/sfx.c
deleted file mode 100644
index 829a53d..0000000
--- a/libutil/kazlib/sfx.c
+++ /dev/null
@@ -1,1138 +0,0 @@
-/*
- * SFX---A utility which tries to determine whether a given C expression
- * is free of side effects. This can be used for verifying that macros which
- * expand their arguments more than once are not being accidentally misused.
- *
- * Copyright (C) 1999 Kaz Kylheku <kaz at ashi.footprints.net>
- *
- * Free Software License:
- *
- * All rights are reserved by the author, with the following exceptions:
- * Permission is granted to freely reproduce and distribute this software,
- * possibly in exchange for a fee, provided that this copyright notice appears
- * intact. Permission is also granted to adapt this software to produce
- * derivative works, as long as the modified versions carry this copyright
- * notice and additional notices stating that the work has been modified.
- * This source code may be translated into executable form and incorporated
- * into proprietary software; there is no requirement for such software to
- * contain a copyright notice related to this source.
- *
- */
-
-#include <ctype.h>
-#include <assert.h>
-#include <stdio.h>
-#include <string.h>
-#include "except.h"
-#include "sfx.h"
-#include "hash.h"
-#ifdef KAZLIB_POSIX_THREADS
-#include <pthread.h>
-#endif
-
-/*
- * Exceptions
- */
-
-#define SFX_EX 0x34DB9C4A
-#define SFX_SYNERR 1
-
-/*
- * Cache entry
- */
-
-typedef struct {
- hnode_t node;
- const char *expr;
- sfx_rating_t eff;
-} sfx_entry_t;
-
-/*
- * Parsing context structure
- */
-
-typedef struct {
- const unsigned char *start;
- const unsigned char *input;
- size_t size;
- sfx_rating_t eff;
-} context_t;
-
-/*
- * Declarator type: abstract, concrete or both
- */
-
-typedef enum {
- decl_abstract, decl_concrete, decl_both
-} decl_t;
-
-static void init_context(context_t *ctx, const unsigned char *expr)
-{
- ctx->input = ctx->start = expr;
- ctx->size = strlen((const char *) expr) + 1;
- ctx->eff = sfx_none;
-}
-
-static void assign_context(context_t *copy, context_t *orig)
-{
- *copy = *orig;
-}
-
-static void set_effect(context_t *ctx, sfx_rating_t eff)
-{
- assert (eff == sfx_none || eff == sfx_potential || eff == sfx_certain);
-
- if (eff > ctx->eff)
- ctx->eff = eff;
-}
-
-static void reset_effect(context_t *ctx)
-{
- ctx->eff = sfx_none;
-}
-
-static sfx_rating_t get_effect(context_t *ctx)
-{
- return ctx->eff;
-}
-
-static int skip_ws(context_t *expr)
-{
- while (*expr->input != 0 && isspace(*expr->input))
- expr->input++;
-
- return (*expr->input == 0);
-}
-
-static int get_next(context_t *expr)
-{
- int ret = *expr->input;
- if (ret)
- expr->input++;
- return ret;
-}
-
-static int get_next_skip_ws(context_t *expr)
-{
- if (!skip_ws(expr))
- return *expr->input++;
- return 0;
-}
-
-static const unsigned char *get_ptr(context_t *expr)
-{
- return expr->input;
-}
-
-static void skip_n(context_t *ctx, size_t n)
-{
- assert ((size_t) (ctx->input - ctx->start) <= ctx->size - n);
- ctx->input += n;
-}
-
-static void put_back(context_t *expr, int ch)
-{
- if (ch)
- expr->input--;
-}
-
-static int peek_next(context_t *expr)
-{
- return *expr->input;
-}
-
-static void syntax_error(void)
-{
- except_throw(SFX_EX, SFX_SYNERR, "syntax_error");
-}
-
-static void match_hard(context_t *expr, int match)
-{
- int ch = get_next(expr);
- if (ch != match)
- syntax_error();
-}
-
-static void chk_comma(context_t *);
-
-static void skip_ident(context_t *expr)
-{
- int ch = get_next(expr);
-
- if (!isalpha(ch) && ch != '_')
- syntax_error();
-
- do {
- ch = get_next(expr);
- } while (isalnum(ch) || ch == '_');
-
- put_back(expr, ch);
-}
-
-static void skip_constant(context_t *expr)
-{
- int ch = get_next(expr);
-
- assert (isdigit(ch) || ch == '.');
-
- do {
- ch = get_next(expr);
- if (ch == 'e' || ch == 'E') {
- ch = get_next(expr);
- if (ch == '+' || ch == '-') {
- ch = get_next(expr);
- if (!isdigit(ch))
- syntax_error();
- }
- }
- } while (ch != 0 && (isalnum(ch) || ch == '.'));
-
- put_back(expr, ch);
-}
-
-static void skip_strlit(context_t *expr)
-{
- int ch = get_next(expr);
-
- assert (ch == '"');
-
- do {
- ch = get_next(expr);
- if (ch == '\\') {
- get_next(expr);
- continue;
- }
- } while (ch != 0 && ch != '"');
-
- if (ch != '"')
- syntax_error();
-}
-
-static void skip_charlit(context_t *expr)
-{
- int ch = get_next(expr);
-
- assert (ch == '\'');
-
- do {
- ch = get_next(expr);
- if (ch == '\\') {
- get_next(expr);
- continue;
- }
- } while (ch != 0 && ch != '\'');
-
- if (ch != '\'')
- syntax_error();
-}
-
-static void chk_spec_qual_list(context_t *expr)
-{
- skip_ws(expr);
- skip_ident(expr);
-
- for (;;) {
- int ch;
-
- skip_ws(expr);
- ch = peek_next(expr);
-
- if (!isalpha(ch) && ch != '_')
- break;
-
- skip_ident(expr);
- }
-}
-
-static int speculate(void (*chk_func)(context_t *), context_t *expr, context_t *copy, int nextchar)
-{
- static const except_id_t catch[] = { { SFX_EX, XCEPT_CODE_ANY } };
- except_t *ex;
- volatile int result = 0;
- assign_context(copy, expr);
-
- except_try_push(catch, 1, &ex);
-
- if (ex == 0) {
- chk_func(copy);
- if (nextchar) {
- skip_ws(copy);
- match_hard(copy, nextchar);
- }
- result = 1;
- }
-
- except_try_pop();
-
- return result;
-}
-
-static void chk_pointer_opt(context_t *expr)
-{
- for (;;) {
- int ch = get_next_skip_ws(expr);
-
- if (ch != '*') {
- put_back(expr, ch);
- break;
- }
-
- skip_ws(expr);
-
- ch = peek_next(expr);
-
- if (ch == '*')
- continue;
- if (!isalpha(ch) && ch != '_')
- break;
-
- skip_ident(expr);
- }
-}
-
-static void chk_decl(context_t *, decl_t);
-
-static void chk_parm_decl(context_t *expr)
-{
- chk_spec_qual_list(expr);
- chk_decl(expr, decl_both);
-}
-
-static void chk_parm_type_list(context_t *expr)
-{
- for (;;) {
- int ch;
-
- chk_parm_decl(expr);
-
- ch = get_next_skip_ws(expr);
-
- if (ch != ',') {
- put_back(expr, ch);
- break;
- }
-
- ch = get_next_skip_ws(expr);
-
- if (ch == '.') {
- match_hard(expr, '.');
- match_hard(expr, '.');
- break;
- }
-
- put_back(expr, ch);
- }
-}
-
-static void chk_conditional(context_t *);
-
-static void chk_direct_decl(context_t *expr, decl_t type)
-{
- for (;;) {
- int ch = get_next_skip_ws(expr);
-
- if (ch == '(') {
- skip_ws(expr);
- ch = peek_next(expr);
- if (ch == '*' || ch == '(' || ch == '[')
- chk_decl(expr, type);
- else if (isalpha(ch) || ch == '_')
- chk_parm_type_list(expr);
- match_hard(expr, ')');
- } else if (ch == '[') {
- skip_ws(expr);
- ch = peek_next(expr);
- if (ch != ']')
- chk_conditional(expr);
- match_hard(expr, ']');
- } else if ((type == decl_concrete || type == decl_both) && (isalpha(ch) || ch == '_')) {
- put_back(expr, ch);
- skip_ident(expr);
- break;
- } else {
- put_back(expr, ch);
- break;
- }
- }
-}
-
-static void chk_decl(context_t *expr, decl_t type)
-{
- int ch;
- chk_pointer_opt(expr);
- skip_ws(expr);
- ch = peek_next(expr);
- if (ch == '[' || ch == '(' || ((type == decl_concrete || type == decl_both) && (isalpha(ch) || ch == '_'))) {
- chk_direct_decl(expr, type);
- }
-}
-
-static void chk_typename(context_t *expr)
-{
- chk_spec_qual_list(expr);
- chk_decl(expr, decl_abstract);
-}
-
-static void chk_primary(context_t *expr)
-{
- int ch = peek_next(expr);
-
- if (ch == 'L') {
- get_next(expr);
- ch = peek_next(expr);
-
- if (ch == '\'') {
- skip_charlit(expr);
- return;
- }
-
- if (ch == '"') {
- skip_strlit(expr);
- return;
- }
-
- put_back(expr, 'L');
- ch = 'L';
- }
-
- if (isalpha(ch) || ch == '_') {
- skip_ident(expr);
- return;
- }
-
- if (isdigit(ch) || ch == '.') {
- skip_constant(expr);
- return;
- }
-
- if (ch == '(') {
- get_next(expr);
- chk_comma(expr);
- match_hard(expr, ')');
- return;
- }
-
- if (ch == '\'') {
- skip_charlit(expr);
- return;
- }
-
- if (ch == '"') {
- skip_strlit(expr);
- return;
- }
-
- syntax_error();
-}
-
-static void chk_postfix(context_t *expr)
-{
- chk_primary(expr);
-
- for (;;) {
- int ch = get_next_skip_ws(expr);
-
- switch (ch) {
- case '[':
- chk_comma(expr);
- skip_ws(expr);
- match_hard(expr, ']');
- continue;
- case '(':
- set_effect(expr, sfx_potential);
- ch = get_next_skip_ws(expr);
-
- if (ch != ')') {
- put_back(expr, ch);
- /* clever hack: parse non-empty argument list as comma expression */
- chk_comma(expr);
- ch = get_next_skip_ws(expr);
- }
-
- if (ch != ')')
- syntax_error();
-
- continue;
- case '.':
- skip_ws(expr);
- skip_ident(expr);
- continue;
- case '-':
- ch = get_next(expr);
-
- if (ch != '-' && ch != '>') {
- put_back(expr, ch);
- put_back(expr, '-');
- break;
- }
-
- if (ch == '>') {
- skip_ws(expr);
- skip_ident(expr);
- continue;
- }
-
- set_effect(expr, sfx_certain);
- continue;
- case '+':
- ch = get_next(expr);
- if (ch != '+') {
- put_back(expr, ch);
- put_back(expr, '+');
- break;
- }
-
- set_effect(expr, sfx_certain);
- continue;
- default:
- put_back(expr, ch);
- break;
- }
- break;
- }
-}
-
-static void chk_cast(context_t *);
-
-static void chk_unary(context_t *expr)
-{
- for (;;) {
- int nscan, ch = get_next_skip_ws(expr);
-
- switch (ch) {
- case '+':
- ch = get_next(expr);
- if (ch == '+')
- set_effect(expr, sfx_certain);
- else
- put_back(expr, ch);
- chk_cast(expr);
- break;
- case '-':
- ch = get_next(expr);
- if (ch == '-')
- set_effect(expr, sfx_certain);
- else
- put_back(expr, ch);
- chk_cast(expr);
- break;
- case '&': case '*': case '~': case '!':
- chk_cast(expr);
- break;
- case 's':
- put_back(expr, ch);
- nscan = 0;
- sscanf((const char *) get_ptr(expr), "sizeof%*1[^a-z0-9_]%n", &nscan);
-
- if (nscan == 7 || strcmp((const char *) get_ptr(expr), "sizeof") == 0) {
- sfx_rating_t eff = get_effect(expr);
-
- skip_n(expr, 6);
-
- ch = get_next_skip_ws(expr);
-
- if (ch == '(') {
- context_t comma, type;
- int iscomma = speculate(chk_comma, expr, &comma, ')');
- int istype = speculate(chk_typename, expr, &type, ')');
-
- if (!iscomma && !istype)
- syntax_error();
-
- if (iscomma) {
- context_t unary;
- put_back(expr, ch);
- if (speculate(chk_unary, expr, &unary, 0)) {
- assign_context(expr, &unary);
- istype = 0;
- }
- }
-
- if (istype)
- assign_context(expr, &type);
- } else {
- put_back(expr, ch);
- chk_unary(expr);
- }
-
- reset_effect(expr);
- set_effect(expr, eff);
- break;
- }
- chk_postfix(expr);
- break;
- default:
- put_back(expr, ch);
- chk_postfix(expr);
- break;
- }
-
- break;
- }
-}
-
-static void chk_cast(context_t *expr)
-{
- enum {
- parexpr, /* parenthesized expression */
- partype, /* parenthesized type name */
- parambig, /* ambiguity between paren expr and paren type name */
- unary, /* unary expression */
- plunary, /* unary expression with leading plus or minus */
- other /* none of the above, or even end of input */
- } curr = partype, old = partype, peek = partype;
-
- /* history for backtracking: two cast expression elements back */
- context_t old_expr = { 0 }, cur_expr = { 0 };
-
- for (;;) {
- context_t type, comma, unr;
- int ch = get_next_skip_ws(expr);
-
- /*
- * Determine what the next bit of input is: parenthesized type name,
- * expression, unary expression or what? Speculative parsing is used
- * to test several hypotheses. For example, something like
- * (X)(Y) ^ 1 is seen, it will be turned, by subsequent iterations of
- * this loop, into the codes: parambig, parambig, other.
- */
-
- if (ch == '(') {
- int istype = speculate(chk_typename, expr, &type, ')');
- int iscomma = speculate(chk_comma, expr, &comma, ')');
-
- switch (istype << 1 | iscomma) {
- case 0:
- ch = get_next_skip_ws(expr);
- if (ch == ')')
- peek = other; /* empty parentheses */
- else
- syntax_error();
- break;
- case 1:
- peek = parexpr;
- break;
- case 2:
- peek = partype;
- break;
- case 3:
- peek = parambig;
- break;
- }
- put_back(expr, ch);
- } else if (ch == 0) {
- peek = other;
- } else {
- put_back(expr, ch);
- if (speculate(chk_unary, expr, &unr, 0)) {
- peek = (ch == '+' || ch == '-' || ch == '*' || ch == '&') ? plunary : unary;
- } else {
- peek = other;
- }
- }
-
- /*
- * Okay, now we have an idea what is coming in the input. We make some
- * sensible decision based on this and the thing we parsed previously.
- * Either the parsing continues to grab more parenthesized things, or
- * some decision is made to parse out the suffix material sensibly and
- * terminate. Backtracking is used up to two elements back. For
- * example in the case of (X)(Y) ^ 1 (parambig, parambig, other) it's
- * necessary, upon seeing ^ 1 (other) to go back to second to last
- * ambigous parenthesized element (X) and terminate by parsing the
- * (X)(Y) as a postfix expression. It cannot be a cast, because ^1
- * isn't an expression. Unary expressions that start with + or -
- * create an interesting ambiguity. Is (X)(Y) + 1 the addition of 1 to
- * the result of the call to function X with parameter Y? Or is it the
- * unary expression + 1 cast to type Y and X? The safer assumption is
- * to go with the function call hypothesis, since that's the
- * interpretation that may have side effects.
- */
-
- switch (curr) {
- case parexpr: /* impossible cases */
- case other:
- case unary:
- case plunary:
- assert (0);
- syntax_error();
- /* notreached */
- case partype:
- switch (peek) {
- case parexpr: /* cast in front of parenthesized expression */
- chk_postfix(expr);
- return;
- case partype: /* compounding cast: keep looping */
- break;
- case parambig: /* type or expr: keep looping */
- break;
- case unary:
- case plunary:
- chk_unary(expr);
- return;
- case other: /* cast in front of non-expression! */
- syntax_error();
- /* notreached */
- }
- break;
- case parambig:
- switch (peek) {
- case parexpr: /* function call */
- assign_context(expr, &cur_expr);
- chk_postfix(expr);
- return;
- case partype: /* compounding cast: keep looping */
- break;
- case parambig: /* type or expr: keep looping */
- break;
- case unary:
- chk_unary(expr);
- return;
- case plunary: /* treat unary expr with + or - as additive */
- case other:
- if (old == parambig) {
- /* reparse two expression-like things in a row as call */
- assign_context(expr, &old_expr);
- chk_postfix(expr);
- return;
- }
- /* reparse expression followed by non-parenthesized
- stuff as postfix expression */
- assign_context(expr, &cur_expr);
- chk_postfix(expr);
- return; /* need more context */
- }
- break;
- }
-
- old = curr;
- curr = peek;
- assign_context(&old_expr, &cur_expr);
- assign_context(&cur_expr, expr);
- assign_context(expr, &type);
- }
-}
-
-static void chk_multiplicative(context_t *expr)
-{
- for (;;) {
- int ch;
-
- chk_cast(expr);
- ch = get_next_skip_ws(expr);
-
- if ((ch != '*' && ch != '/' && ch != '%') || peek_next(expr) == '=') {
- put_back(expr, ch);
- break;
- }
- }
-}
-
-static void chk_additive(context_t *expr)
-{
- for (;;) {
- int ch;
-
- chk_multiplicative(expr);
- ch = get_next_skip_ws(expr);
-
- if ((ch != '+' && ch != '-') || peek_next(expr) == '=') {
- put_back(expr, ch);
- break;
- }
- }
-}
-
-static void chk_shift(context_t *expr)
-{
- for (;;) {
- int ch;
-
- chk_additive(expr);
- ch = get_next_skip_ws(expr);
-
- if (ch != '<' && ch != '>') {
- put_back(expr, ch);
- break;
- }
-
- if (ch == '<' && peek_next(expr) != '<') {
- put_back(expr, ch);
- break;
- }
-
- if (ch == '>' && peek_next(expr) != '>') {
- put_back(expr, ch);
- break;
- }
-
- get_next(expr);
-
- if (peek_next(expr) == '=') {
- put_back(expr, ch);
- put_back(expr, ch);
- break;
- }
- }
-}
-
-static void chk_relational(context_t *expr)
-{
- for (;;) {
- int ch;
-
- chk_shift(expr);
- ch = get_next_skip_ws(expr);
-
-
- if (ch != '<' && ch != '>') {
- put_back(expr, ch);
- break;
- }
-
- if (ch == '<' && peek_next(expr) == '<') {
- put_back(expr, ch);
- break;
- }
-
- if (ch == '>' && peek_next(expr) == '>') {
- put_back(expr, ch);
- break;
- }
-
- if (peek_next(expr) == '=')
- get_next(expr);
- }
-}
-
-static void chk_equality(context_t *expr)
-{
- for (;;) {
- int ch;
-
- chk_relational(expr);
- ch = get_next_skip_ws(expr);
-
- if ((ch != '!' && ch != '=') || peek_next(expr) != '=') {
- put_back(expr, ch);
- break;
- }
-
- match_hard(expr, '=');
- }
-}
-
-static void chk_and(context_t *expr)
-{
- for (;;) {
- int ch;
-
- chk_equality(expr);
- ch = get_next_skip_ws(expr);
-
- if (ch != '&' || peek_next(expr) == '&' || peek_next(expr) == '=') {
- put_back(expr, ch);
- break;
- }
- }
-}
-
-static void chk_exclusive_or(context_t *expr)
-{
- for (;;) {
- int ch;
-
- chk_and(expr);
- ch = get_next_skip_ws(expr);
-
- if (ch != '^' || peek_next(expr) == '=') {
- put_back(expr, ch);
- break;
- }
- }
-}
-
-static void chk_inclusive_or(context_t *expr)
-{
- for (;;) {
- int ch;
-
- chk_exclusive_or(expr);
- ch = get_next_skip_ws(expr);
-
- if (ch != '|' || peek_next(expr) == '|' || peek_next(expr) == '=') {
- put_back(expr, ch);
- break;
- }
- }
-}
-
-static void chk_logical_and(context_t *expr)
-{
- for (;;) {
- int ch;
-
- chk_inclusive_or(expr);
- ch = get_next_skip_ws(expr);
-
- if (ch != '&' || peek_next(expr) != '&') {
- put_back(expr, ch);
- break;
- }
-
- match_hard(expr, '&');
- }
-}
-
-static void chk_logical_or(context_t *expr)
-{
- for (;;) {
- int ch;
-
- chk_logical_and(expr);
- ch = get_next_skip_ws(expr);
-
- if (ch != '|' || peek_next(expr) != '|') {
- put_back(expr, ch);
- break;
- }
-
- match_hard(expr, '|');
- }
-}
-
-static void chk_conditional(context_t *expr)
-{
- for (;;) {
- int ch;
-
- chk_logical_or(expr);
- ch = get_next_skip_ws(expr);
-
- if (ch != '?') {
- put_back(expr, ch);
- break;
- }
-
- chk_comma(expr);
-
- skip_ws(expr);
- match_hard(expr, ':');
- }
-}
-
-static void chk_assignment(context_t *expr)
-{
- for (;;) {
- int ch;
-
- chk_conditional(expr);
- ch = get_next_skip_ws(expr);
-
- switch (ch) {
- case '=':
- break;
- case '*': case '/': case '%':
- case '+': case '-': case '&':
- case '^': case '|':
- match_hard(expr, '=');
- break;
- case '<':
- match_hard(expr, '<');
- match_hard(expr, '=');
- break;
- case '>':
- match_hard(expr, '>');
- match_hard(expr, '=');
- break;
- case 0:
- default:
- put_back(expr, ch);
- return;
- }
- set_effect(expr, sfx_certain);
- }
-}
-
-static void chk_comma(context_t *expr)
-{
- for (;;) {
- int ch;
-
- chk_assignment(expr);
- ch = get_next_skip_ws(expr);
-
- if (ch != ',') {
- put_back(expr, ch);
- break;
- }
- }
-}
-
-/*
- * This function returns 1 if the expression is successfully parsed,
- * or 0 if there is a syntax error.
- *
- * The object pointed to by eff is set to indicate the side effect ranking of
- * the parsed expression: sfx_none, sfx_potential and sfx_certain. These
- * rankins mean, respectively, that there are no side effects, that there are
- * potential side effects, or that there certainly are side effects.
- */
-
-int sfx_determine(const char *expr, sfx_rating_t *eff)
-{
- static const except_id_t catch[] = { { SFX_EX, XCEPT_CODE_ANY } };
- except_t *ex;
- context_t ctx;
- volatile int retval = 1;
-
- if (!except_init())
- return 0;
-
- init_context(&ctx, (const unsigned char *) expr);
-
- except_try_push(catch, 1, &ex);
-
- if (ex == 0) {
- chk_comma(&ctx);
- skip_ws(&ctx);
- if (peek_next(&ctx) != 0)
- syntax_error();
- } else {
- /* exception caught */
- retval = 0;
- }
-
- except_try_pop();
-
- *eff = ctx.eff;
-
- except_deinit();
-
- return retval;
-}
-
-
-#ifdef KAZLIB_POSIX_THREADS
-
-static pthread_once_t cache_init;
-static pthread_mutex_t cache_mutex = PTHREAD_MUTEX_INITIALIZER;
-
-#define init_once(X, Y) pthread_once(X, Y)
-#define lock_cache() pthread_mutex_lock(&cache_mutex)
-#define unlock_cache() pthread_mutex_unlock(&cache_mutex)
-
-#else
-static int cache_init;
-
-static void init_once(int *once, void (*func)(void))
-{
- if (*once == 0) {
- func();
- *once = 1;
- }
-}
-
-#define lock_cache()
-#define unlock_cache()
-#endif
-
-static hash_t *cache;
-
-extern hash_t *hash_create(hashcount_t, hash_comp_t, hash_fun_t);
-
-static void init_cache(void)
-{
- cache = hash_create(HASHCOUNT_T_MAX, 0, 0);
-}
-
-static int lookup_cache(const char *expr, sfx_rating_t *rating)
-{
- hnode_t *cache_node;
- init_once(&cache_init, init_cache);
-
- lock_cache();
-
- cache_node = hash_lookup(cache, expr);
-
- unlock_cache();
-
- if (cache_node != 0) {
- sfx_entry_t *cache_entry = hnode_get(cache_node);
- *rating = cache_entry->eff;
- return 1;
- }
-
- return 0;
-}
-
-static int cache_result(const char *expr, sfx_rating_t rating)
-{
- int result = 0;
- hnode_t *cache_node;
-
- init_once(&cache_init, init_cache);
-
- if (cache == 0)
- goto bail;
-
- lock_cache();
-
- cache_node = hash_lookup(cache, expr);
-
- if (!cache_node) {
- sfx_entry_t *cache_entry = malloc(sizeof *cache_entry);
-
- if (cache_entry == 0)
- goto bail_unlock;
-
- hnode_init(&cache_entry->node, cache_entry);
- cache_entry->expr = expr;
- cache_entry->eff = rating;
- hash_insert(cache, &cache_entry->node, expr);
- } else {
- sfx_entry_t *cache_entry = hnode_get(cache_node);
- cache_entry->eff = rating;
- result = 1;
- }
-
- result = 1;
-
-
-bail_unlock:
- unlock_cache();
-
-bail:
- return result;
-}
-
-
-void sfx_check(const char *expr, const char *file, unsigned long line)
-{
- sfx_rating_t eff;
- int success = lookup_cache(expr, &eff);
-
- if (!success) {
- success = sfx_determine(expr, &eff);
- cache_result(expr, eff);
- }
-
- if (!success) {
- fprintf(stderr, "%s:%ld: syntax error in expression \"%s\"\n",
- file, line, expr);
- } else if (eff == sfx_potential) {
- fprintf(stderr, "%s:%ld: expression \"%s\" may have side effects\n",
- file, line, expr);
- } else if (eff == sfx_certain) {
- fprintf(stderr, "%s:%ld: expression \"%s\" has side effects\n",
- file, line, expr);
- } else {
- return;
- }
-}
-
-int sfx_declare(const char *expr, sfx_rating_t eff)
-{
- return cache_result(expr, eff);
-}
-
diff --git a/libutil/kazlib/sfx.h b/libutil/kazlib/sfx.h
deleted file mode 100644
index b2a485c..0000000
--- a/libutil/kazlib/sfx.h
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * SideChk---A utility which tries to determine whether a given C expression
- * is free of side effects. This can be used for verifying that macros which
- * expand their arguments more than once are not being accidentally misused.
- *
- * Copyright (C) 1999 Kaz Kylheku <kaz at ashi.footprints.net>
- *
- * Free Software License:
- *
- * All rights are reserved by the author, with the following exceptions:
- * Permission is granted to freely reproduce and distribute this software,
- * possibly in exchange for a fee, provided that this copyright notice appears
- * intact. Permission is also granted to adapt this software to produce
- * derivative works, as long as the modified versions carry this copyright
- * notice and additional notices stating that the work has been modified.
- * This source code may be translated into executable form and incorporated
- * into proprietary software; there is no requirement for such software to
- * contain a copyright notice related to this source.
- *
- */
-
-#ifndef SFX_H
-#define SFX_H
-
-#include <assert.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-typedef enum {
- sfx_none, sfx_potential, sfx_certain
-} sfx_rating_t;
-
-int sfx_determine(const char *, sfx_rating_t *);
-int sfx_declare(const char *, sfx_rating_t);
-void sfx_check(const char *, const char *, unsigned long);
-
-#ifdef __cplusplus
-}
-#endif
-
-#define SFX_CHECK(E) (sfx_check(#E, __FILE__, __LINE__), (E))
-#define SFX_STRING(E) #E
-
-#endif
diff --git a/meryl/Make.include b/meryl/Make.include
index 494d4a2..205d1b9 100644
--- a/meryl/Make.include
+++ b/meryl/Make.include
@@ -19,24 +19,19 @@ merylsrc := $/args.C \
# meryl.H is exported only for celera-assembler.
-$/.CXX_SRCS := ${merylsrc} $/meryl.C $/mervin.C $/asmMerQC.C $/mapMers.C $/mapMers-depth.C $/maskMers.C $/compare-counts.C $/simple.C
+$/.CXX_SRCS := ${merylsrc} $/meryl.C $/simple.C $/mapMers.C $/mapMers-depth.C $/kmer-mask.C
$/.CXX_INCS := $/meryl.H
$/.CXX_LIBS := $/libmerylguts.a
-$/.CXX_EXES := $/meryl $/mervin $/simple $/asmMerQC $/mapMers $/mapMers-depth $/testPositionBias $/maskMers $/compare-counts
+$/.CXX_EXES := $/meryl $/simple $/mapMers $/mapMers-depth $/kmer-mask
$/.CLEAN := $/*.o
$/libmerylguts.a : ${merylsrc:.C=.o}
-$/meryl : $/meryl.o $/libmerylguts.a ${LIBMERYL/}libmeryl.a ${LIBSEQ/}libseq.a ${LIBBIO/}libbio.a ${LIBUTL/}libutil.a
-$/mervin : $/mervin.o ${LIBMERYL/}libmeryl.a ${LIBSEQ/}libseq.a ${LIBBIO/}libbio.a ${LIBUTL/}libutil.a
-$/simple : $/simple.o ${LIBMERYL/}libmeryl.a ${LIBSEQ/}libseq.a ${LIBBIO/}libbio.a ${LIBUTL/}libutil.a
-$/asmMerQC : $/asmMerQC.o ${LIBMERYL/}libmeryl.a ${LIBSEQ/}libseq.a ${LIBBIO/}libbio.a ${LIBUTL/}libutil.a
-$/mapMers : $/mapMers.o ${LIBKMER/}libkmer.a ${LIBMERYL/}libmeryl.a ${LIBSEQ/}libseq.a ${LIBBIO/}libbio.a ${LIBUTL/}libutil.a
-$/mapMers-depth : $/mapMers-depth.o ${LIBKMER/}libkmer.a ${LIBMERYL/}libmeryl.a ${LIBSEQ/}libseq.a ${LIBBIO/}libbio.a ${LIBUTL/}libutil.a
-$/m : $/m.o ${LIBKMER/}libkmer.a ${LIBMERYL/}libmeryl.a ${LIBSEQ/}libseq.a ${LIBBIO/}libbio.a ${LIBUTL/}libutil.a
-$/testPositionBias : $/testPositionBias.o ${LIBKMER/}libkmer.a ${LIBMERYL/}libmeryl.a ${LIBSEQ/}libseq.a ${LIBBIO/}libbio.a ${LIBUTL/}libutil.a
-$/maskMers : $/maskMers.o ${LIBKMER/}libkmer.a ${LIBMERYL/}libmeryl.a ${LIBSEQ/}libseq.a ${LIBBIO/}libbio.a ${LIBUTL/}libutil.a
-$/compare-counts : $/compare-counts.o ${LIBKMER/}libkmer.a ${LIBMERYL/}libmeryl.a ${LIBSEQ/}libseq.a ${LIBBIO/}libbio.a ${LIBUTL/}libutil.a
+$/meryl: $/meryl.o $/libmerylguts.a ${LIBMERYL/}libmeryl.a ${LIBSEQ/}libseq.a ${LIBBIO/}libbio.a ${LIBUTL/}libutil.a
+$/simple: $/simple.o ${LIBMERYL/}libmeryl.a ${LIBSEQ/}libseq.a ${LIBBIO/}libbio.a ${LIBUTL/}libutil.a
+$/mapMers: $/mapMers.o ${LIBKMER/}libkmer.a ${LIBMERYL/}libmeryl.a ${LIBSEQ/}libseq.a ${LIBBIO/}libbio.a ${LIBUTL/}libutil.a
+$/mapMers-depth: $/mapMers-depth.o ${LIBKMER/}libkmer.a ${LIBMERYL/}libmeryl.a ${LIBSEQ/}libseq.a ${LIBBIO/}libbio.a ${LIBUTL/}libutil.a
+$/kmer-mask: $/kmer-mask.o ${LIBKMER/}libkmer.a ${LIBMERYL/}libmeryl.a ${LIBSEQ/}libseq.a ${LIBBIO/}libbio.a ${LIBUTL/}libutil.a
$(eval $/%.d $/%.o: CXXFLAGS+=-I${LIBMERYL/} -I${LIBKMER/} -I${LIBBIO/} -I${LIBSEQ/} -I${LIBUTL/})
diff --git a/meryl/asmMerQC.C b/meryl/asmMerQC.C
deleted file mode 100644
index 35bdfbb..0000000
--- a/meryl/asmMerQC.C
+++ /dev/null
@@ -1,396 +0,0 @@
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "libmeryl.H"
-
-// The categories depend on the type of input (fragments or contigs):
-//
-// 0 -- no count, mer not present
-// 1 -- single copy
-// 2 -- 2 -> 10 copies (contigs) -- 2 -> 2mode copies (frags)
-// 3 -- 11 -> 100 copies (contigs) -- -> 10mode copies (frags)
-// 4 -- 101+ copies (contigs) -- -> 100mode copies (frags)
-// 5 -- -> infinity copies (frags)
-
-// You'll also need to modify compare() and output() if you change this.
-#define NUMCATEGORIES 6
-
-// The output files are global for convenience. Otherwise, we'd be passing
-// them to compare() for every single mer.
-//
-bool dumpFlag = false;
-FILE *dumpSCZF = 0L;
-FILE *dumpMCZF = 0L;
-FILE *dumpMCSF = 0L;
-FILE *dumpMCMF = 0L;
-char merstring[1024];
-
-uint32
-findMode(char *name) {
- merylStreamReader *M = new merylStreamReader(name);
- uint32 *H = new uint32 [16384];
-
- fprintf(stderr, "Finding mode of '%s'\n", name);
-
- for (uint32 i=0; i<16384; i++)
- H[i] = 0;
-
- while (M->validMer()) {
- if (M->theCount() < 16384)
- H[M->theCount()]++;
- M->nextMer();
- }
-
- uint32 mi = 2;
- for (uint32 i=2; i<16384; i++)
- if (H[i] > H[mi])
- mi = i;
-
- fprintf(stderr, "Mode of '%s' is "uint32FMT"\n", name, mi);
-
- return(mi);
-}
-
-
-void
-compare(merylStreamReader *F,
- merylStreamReader *C,
- kMer &minmer,
- uint32 mode,
- uint32 R[NUMCATEGORIES][NUMCATEGORIES]) {
- uint32 Ftype = 0;
- uint32 Ctype = 0;
- kMer Fmer = F->theFMer();
- kMer Cmer = C->theFMer();
- uint32 Fcnt = F->theCount();
- uint32 Ccnt = C->theCount();
-
- if (Fcnt == 0)
- Ftype = 0;
- else if (Fcnt == 1)
- Ftype = 1;
- else if (Fcnt <= 2*mode)
- Ftype = 2;
- else if (Fcnt <= 10*mode)
- Ftype = 3;
- else if (Fcnt <= 100*mode)
- Ftype = 4;
- else
- Ftype = 5;
-
- if (Ccnt == 0)
- Ctype = 0;
- else if (Ccnt == 1)
- Ctype = 1;
- else if (Ccnt <= 10)
- Ctype = 2;
- else if (Ccnt <= 100)
- Ctype = 3;
- else
- Ctype = 4;
-
- // If the mer isn't valid, we hit the end of the file, and the mer
- // thus (obviously) isn't in the file.
- //
- if (F->validMer() == false)
- Ftype = 0;
- if (C->validMer() == false)
- Ctype = 0;
-
- // If either type is 0, we're done, but only increment the count if
- // this mer is the minmer.
- //
- if ((Ftype == 0) || (Ctype == 0)) {
- if (((Ftype == 0) && (Cmer == minmer)) ||
- ((Ctype == 0) && (Fmer == minmer))) {
- R[Ftype][Ctype]++;
-
- // Save the mer if it's in contigs, but not fragments.
- if (dumpFlag)
- if (Ftype == 0)
- if (Ctype == 1)
- fprintf(dumpSCZF, ">"uint32FMT"\n%s\n", Ccnt, Cmer.merToString(merstring));
- else
- fprintf(dumpMCZF, ">"uint32FMT"\n%s\n", Ccnt, Cmer.merToString(merstring));
- }
- return;
- }
-
- // If the mers don't agree, we're also done. If either is the
- // minmer, note that we saw it.
- //
- if (Fmer != Cmer) {
- if (Fmer == minmer)
- R[Ftype][0]++;
- if (Cmer == minmer) {
- R[0][Ctype]++;
-
- // Again, save the mer since it's in contigs, but not fragments.
- if (dumpFlag)
- if (Ctype == 1)
- fprintf(dumpSCZF, ">"uint32FMT"\n%s\n", Ccnt, Cmer.merToString(merstring));
- else
- fprintf(dumpMCZF, ">"uint32FMT"\n%s\n", Ccnt, Cmer.merToString(merstring));
- }
-
- return;
- }
-
- // If we're not the minmer, we're done.
- if (Fmer != minmer)
- return;
-
- // Otherwise, the mers are in both inputs
- R[Ftype][Ctype]++;
-
- // Save the mer if it's in contigs "more" than if in fragments.
- if (dumpFlag) {
- if (Ftype < Ctype)
- if (Ctype == 2)
- fprintf(dumpMCSF, ">"uint32FMT"\n%s\n", Ccnt, Cmer.merToString(merstring));
- else
- fprintf(dumpMCMF, ">"uint32FMT"\n%s\n", Ccnt, Cmer.merToString(merstring));
-
- if ((Ftype == 0) && (Ctype == 1))
- fprintf(dumpSCZF, ">"uint32FMT"\n%s\n", Ccnt, Cmer.merToString(merstring));
- }
-}
-
-
-void
-output(char *title,
- uint32 mode,
- uint32 R[NUMCATEGORIES][NUMCATEGORIES]) {
-
- fprintf(stdout, "\n\n%s\n", title);
- fprintf(stdout, "(frags) | zero | one | <= 10 | <= 100 | <= inf | (contigs)\n");
-
- for (uint32 i=0; i<6; i++) {
- switch (i) {
- case 0: fprintf(stdout, "zero "); break;
- case 1: fprintf(stdout, "one "); break;
- case 2: fprintf(stdout, "<= 2mode "); break;
- case 3: fprintf(stdout, "<= 10mode "); break;
- case 4: fprintf(stdout, "<= 100mode "); break;
- case 5: fprintf(stdout, "<= inf "); break;
- default: fprintf(stdout, "????????? "); break;
- }
- for (uint32 j=0; j<5; j++)
- fprintf(stdout, uint32FMTW(12), R[i][j]);
- fprintf(stdout, "\n");
- }
-}
-
-
-
-
-int
-main(int argc, char **argv) {
- merylStreamReader *AF = 0L;
- merylStreamReader *TF = 0L;
- merylStreamReader *AC = 0L;
- merylStreamReader *DC = 0L;
- merylStreamReader *CO = 0L;
-
- uint32 AFmode = 0;
- uint32 TFmode = 0;
-
- char dumpSCZFname[1024] = {0}; // single contig, zero frags
- char dumpMCZFname[1024] = {0}; // low contig, zero frags
- char dumpMCSFname[1024] = {0}; // medium contig, low frags
- char dumpMCMFname[1024] = {0}; // everything else, contig > frags
-
- bool beVerbose = false;
-
- //fprintf(stderr, "using cached modes for testing!\n");
-
- int arg=1;
- while (arg < argc) {
- if (strcmp(argv[arg], "-af") == 0) { // All frags
- ++arg;
- AFmode = findMode(argv[arg]);
- //AFmode = 8;
- AF = new merylStreamReader(argv[arg]);
- AF->nextMer();
- } else if (strcmp(argv[arg], "-tf") == 0) { // Trimmed frags
- ++arg;
- TFmode = findMode(argv[arg]);
- //TFmode = 8;
- TF = new merylStreamReader(argv[arg]);
- TF->nextMer();
- } else if (strcmp(argv[arg], "-ac") == 0) { // All contigs
- AC = new merylStreamReader(argv[++arg]);
- AC->nextMer();
- } else if (strcmp(argv[arg], "-dc") == 0) { // Degenerate contigs
- DC = new merylStreamReader(argv[++arg]);
- DC->nextMer();
- } else if (strcmp(argv[arg], "-co") == 0) { // Contigs
- CO = new merylStreamReader(argv[++arg]);
- CO->nextMer();
- } else if (strcmp(argv[arg], "-dump") == 0) {
- arg++;
- dumpFlag = true;
- sprintf(dumpSCZFname, "%s.0.singlecontig.zerofrag.fasta", argv[arg]);
- sprintf(dumpMCZFname, "%s.1.multiplecontig.zerofrag.fasta", argv[arg]);
- sprintf(dumpMCSFname, "%s.2.multiplecontig.lowfrag.fasta", argv[arg]);
- sprintf(dumpMCMFname, "%s.3.multiplecontig.multiplefrag.fasta", argv[arg]);
- } else if (strcmp(argv[arg], "-v") == 0) {
- beVerbose = true;
- } else {
- fprintf(stderr, "unknown option '%s'\n", argv[arg]);
- }
- arg++;
- }
-
- if ((AF == 0L) && (TF == 0L) && (AC == 0L) && (DC == 0L) && (CO == 0L)) {
- fprintf(stderr, "usage: %s [opts] [-v] [-dump prefix]\n", argv[0]);
- fprintf(stderr, "At least one fragcounts and one contigcounts are needed.\n");
- fprintf(stderr, " -af | -tf fragcounts\n");
- fprintf(stderr, " -ac | -dc | -co contigcounts \n");
- fprintf(stderr, "Dumping is probably only useful with exactly one frag and\n");
- fprintf(stderr, "one contig, but I'll let you do it with any number.\n");
- exit(1);
- }
- if ((AF == 0L) && (TF == 0L)) {
- fprintf(stderr, "ERROR - need at least one of -af, -tf\n");
- exit(1);
- }
- if ((AC == 0L) && (DC == 0L) && (CO == 0L)) {
- fprintf(stderr, "ERROR - need at least one of -ac, -dc, -co\n");
- exit(1);
- }
-
- // Check mersizes.
- //
- uint32 merSize = 0;
- uint32 ms[5] = { 0 };
-
- if (AF) merSize = ms[0] = AF->merSize();
- if (TF) merSize = ms[1] = TF->merSize();
- if (AC) merSize = ms[2] = AC->merSize();
- if (DC) merSize = ms[3] = DC->merSize();
- if (CO) merSize = ms[4] = CO->merSize();
-
- bool differ = false;
-
- if ((ms[0] > 0) && (ms[0] != merSize)) differ = true;
- if ((ms[1] > 0) && (ms[1] != merSize)) differ = true;
- if ((ms[2] > 0) && (ms[2] != merSize)) differ = true;
- if ((ms[3] > 0) && (ms[3] != merSize)) differ = true;
- if ((ms[4] > 0) && (ms[4] != merSize)) differ = true;
-
- if (differ) {
- fprintf(stderr, "error: mer size differ.\n");
- fprintf(stderr, " AF - "uint32FMT"\n", ms[0]);
- fprintf(stderr, " TF - "uint32FMT"\n", ms[1]);
- fprintf(stderr, " AC - "uint32FMT"\n", ms[2]);
- fprintf(stderr, " DC - "uint32FMT"\n", ms[3]);
- fprintf(stderr, " CO - "uint32FMT"\n", ms[4]);
- exit(1);
- }
-
- if (dumpFlag) {
- errno = 0;
- dumpSCZF = fopen(dumpSCZFname, "w");
- dumpMCZF = fopen(dumpMCZFname, "w");
- dumpMCSF = fopen(dumpMCSFname, "w");
- dumpMCMF = fopen(dumpMCMFname, "w");
- if (errno)
- fprintf(stderr, "Failed to open the dump files: %s\n", strerror(errno)), exit(1);
- }
-
- uint32 AFvsAC[NUMCATEGORIES][NUMCATEGORIES];
- uint32 AFvsDC[NUMCATEGORIES][NUMCATEGORIES];
- uint32 AFvsCO[NUMCATEGORIES][NUMCATEGORIES];
- uint32 TFvsAC[NUMCATEGORIES][NUMCATEGORIES];
- uint32 TFvsDC[NUMCATEGORIES][NUMCATEGORIES];
- uint32 TFvsCO[NUMCATEGORIES][NUMCATEGORIES];
- for (uint32 i=0; i<NUMCATEGORIES; i++)
- for (uint32 j=0; j<NUMCATEGORIES; j++) {
- AFvsAC[i][j] = 0;
- AFvsDC[i][j] = 0;
- AFvsCO[i][j] = 0;
- TFvsAC[i][j] = 0;
- TFvsDC[i][j] = 0;
- TFvsCO[i][j] = 0;
- }
-
- // The default constructor for kMer sets the mer to size 0, all A.
- // We need it to be the proper size, and all T.
- kMer minmer(merSize);
-
- // Don't care what we pick, as long as it's a mer in the set.
- //
- if (AF && AF->validMer()) minmer = AF->theFMer();
- if (TF && TF->validMer()) minmer = TF->theFMer();
- if (AC && AC->validMer()) minmer = AC->theFMer();
- if (DC && DC->validMer()) minmer = DC->theFMer();
- if (CO && CO->validMer()) minmer = CO->theFMer();
-
- speedCounter *C = new speedCounter(" Examining: %7.2f Mmers -- %5.2f Mmers/second\r", 1000000.0, 0x1fffff, beVerbose);
-
- bool morestuff = true;
- while (morestuff) {
-
- // Find any mer in our set
- if (AF && AF->validMer()) minmer = AF->theFMer();
- if (TF && TF->validMer()) minmer = TF->theFMer();
- if (AC && AC->validMer()) minmer = AC->theFMer();
- if (DC && DC->validMer()) minmer = DC->theFMer();
- if (CO && CO->validMer()) minmer = CO->theFMer();
-
- // Find the smallest mer in our set
- if (AF && AF->validMer() && (AF->theFMer() < minmer)) minmer = AF->theFMer();
- if (TF && TF->validMer() && (TF->theFMer() < minmer)) minmer = TF->theFMer();
- if (AC && AC->validMer() && (AC->theFMer() < minmer)) minmer = AC->theFMer();
- if (DC && DC->validMer() && (DC->theFMer() < minmer)) minmer = DC->theFMer();
- if (CO && CO->validMer() && (CO->theFMer() < minmer)) minmer = CO->theFMer();
-
- // We need to do up to six comparisons here.
- if (AF && AC) compare(AF, AC, minmer, AFmode, AFvsAC);
- if (AF && DC) compare(AF, DC, minmer, AFmode, AFvsDC);
- if (AF && CO) compare(AF, CO, minmer, AFmode, AFvsCO);
- if (TF && AC) compare(TF, AC, minmer, TFmode, TFvsAC);
- if (TF && DC) compare(TF, DC, minmer, TFmode, TFvsDC);
- if (TF && CO) compare(TF, CO, minmer, TFmode, TFvsCO);
-
- C->tick();
-#if 0
- if (C->tick()) {
- char stringjunk[256];
- fprintf(stderr, "\nMM %s\n", minmer.merToString(stringjunk));
- if (AF) fprintf(stderr, "AF %s\n", AF->theFMer().merToString(stringjunk));
- if (TF) fprintf(stderr, "TF %s\n", TF->theFMer().merToString(stringjunk));
- if (AC) fprintf(stderr, "AC %s\n", AC->theFMer().merToString(stringjunk));
- if (DC) fprintf(stderr, "DC %s\n", DC->theFMer().merToString(stringjunk));
- if (CO) fprintf(stderr, "CO %s\n", CO->theFMer().merToString(stringjunk));
- }
-#endif
-
- // Advance to the next mer, if we were just used
- morestuff = false;
- if ((AF) && (AF->theFMer() == minmer)) morestuff |= AF->nextMer();
- if ((TF) && (TF->theFMer() == minmer)) morestuff |= TF->nextMer();
- if ((AC) && (AC->theFMer() == minmer)) morestuff |= AC->nextMer();
- if ((DC) && (DC->theFMer() == minmer)) morestuff |= DC->nextMer();
- if ((CO) && (CO->theFMer() == minmer)) morestuff |= CO->nextMer();
- }
-
- delete C;
-
- // output
-
- if ((AF) && (AC)) output("all frags vs all contigs", AFmode, AFvsAC);
- if ((AF) && (DC)) output("all frags vs deg. contigs", AFmode, AFvsDC);
- if ((AF) && (CO)) output("all frags vs non-deg. contigs", AFmode, AFvsCO);
- if ((TF) && (AC)) output("trimmed frags vs all contigs", TFmode, TFvsAC);
- if ((TF) && (DC)) output("trimmed frags vs deg. contigs", TFmode, TFvsDC);
- if ((TF) && (CO)) output("trimmed frags vs non-deg. contigs", TFmode, TFvsCO);
-
- delete AF;
- delete TF;
- delete AC;
- delete DC;
- delete CO;
-}
diff --git a/libkmer/kmer-mask.C b/meryl/kmer-mask.C
similarity index 100%
rename from libkmer/kmer-mask.C
rename to meryl/kmer-mask.C
diff --git a/meryl/m-heap.H b/meryl/m-heap.H
deleted file mode 100644
index 3202450..0000000
--- a/meryl/m-heap.H
+++ /dev/null
@@ -1,152 +0,0 @@
-#ifndef M_HEAP_H
-#define M_HEAP_H
-
-#include "util++.H"
-#include "bio++.H"
-
-//
-// This is a bit packed heap, derived from bitPackedHeap. It is
-// customized to decode a kmer from a merStream, given the location
-// of the kmer in the stream. This kmer is used for the value of the
-// data item in the heap, instead of the value stored in the heap.
-//
-
-class bitPackedMerHeap {
-public:
- bitPackedMerHeap(seqStore *SS, uint32 width, uint64 size=16) {
- _array = new bitPackedArray(width, size);
- _array->set(0, 0);
- _lastVal = 0;
- _mers = SS;
- };
-
- ~bitPackedMerHeap() {
- delete _array;
- };
-
- // Get the mer with index idx in the merStream
- //
- kMer const &getMer(uint64 idx) {
- _mers->setIterationStart(idx);
- _mers->nextMer();
- if (_mers->theRMer() < _mers->theFMer())
- return(_mers->theRMer());
- return(_mers->theFMer());
- }
-
- uint64 get(kMer &mer) {
- uint64 pos = ~uint64ZERO;
-
- if (_lastVal == 0)
- return(pos);
-
- pos = _array->get(0);
- mer = getMer(pos);
-
- if (--_lastVal == 0)
- return(pos);
-
- // Rebalance the heap
-
- uint64 tval = _array->get(_lastVal);
- kMer tmer;
-
- _array->set(0, tval);
-
- uint64 pidx = 0;
- uint64 pval = tval;
- kMer pmer = getMer(pval);
- uint64 cidx = 1;
- uint64 cval = 0; // set below
- kMer cmer;
-
- while (cidx < _lastVal) {
- // Set cval here, so we can first test if cidx is in range.
- cval = _array->get(cidx);
- cmer = getMer(cval);
-
- // Pick the smallest of the two kids
- if (cidx+1 < _lastVal) {
- tval = _array->get(cidx+1);
- tmer = getMer(tval);
-
- if (cmer > tmer) {
- cidx++;
- cval = tval;
- cmer = tmer;
- }
- }
-
- if (cmer < pmer) {
-
- // Swap p and c
- _array->set(pidx, cval);
- _array->set(cidx, pval);
-
- // Move down the tree -- pval doesn't change, we moved it into cidx!
- pidx = cidx;
-
- cidx = cidx * 2 + 1;
- } else {
- cidx = _lastVal;
- }
- }
-
- return(pos);
- };
-
- void add(uint64 value) {
- uint64 cidx = _lastVal++;
- uint64 cval = value;
- kMer cmer;
- uint64 pidx = 0;
- uint64 pval = 0;
- kMer pmer;
- bool more = true;
-
- _array->set(cidx, cval);
-
- if (cidx == 0)
- return;
-
- cmer = getMer(cval);
-
- while (more) {
- pidx = (cidx-1) / 2;
- pval = _array->get(pidx);
- pmer = getMer(pval);
-
- if (pmer > cmer) {
-
- // Swap p and c
- _array->set(cidx, pval);
- _array->set(pidx, cval);
-
- // Move up the tree -- cval doesn't change, we moved it into pidx!
- cidx = pidx;
- } else {
- more = false;
- }
- if (cidx == 0)
- more = false;
- }
- };
-
- void dump(void) {
- for (uint32 i=0; i<_lastVal; i++)
- fprintf(stderr, "HEAP["uint32FMT"]="uint64FMT"\n", i, _array->get(i));
- }
-
- void clear(void) {
- _array->clear();
- _lastVal = 0;
- };
-
-private:
- bitPackedArray *_array;
- uint64 _lastVal;
- seqStore *_mers;
-};
-
-
-#endif // M_HEAP_H
diff --git a/meryl/m.C b/meryl/m.C
deleted file mode 100644
index 1167436..0000000
--- a/meryl/m.C
+++ /dev/null
@@ -1,118 +0,0 @@
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "util++.H"
-#include "bio++.H"
-#include "meryl.H"
-
-#include "m-heap.H"
-
-int
-main(int argc, char **argv) {
- bool beVerbose = false;
- uint64 merSize = 20;
- uint64 memLimit = 768;
- char *inName = 0L;
- char *outName = 0L;
-
- int arg=1;
- while (arg < argc) {
- if (strncmp(argv[arg], "-verbose", 2) == 0) {
- beVerbose = true;
- } else if (strncmp(argv[arg], "-mersize", 4) == 0) {
- merSize = strtouint64(argv[++arg], 0L);
- } else if (strncmp(argv[arg], "-memory", 4) == 0) {
- memLimit = strtouint64(argv[++arg], 0L) * 1024 * 1024;
- } else if (strncmp(argv[arg], "-sequence", 2) == 0) {
- inName = argv[++arg];
- } else {
- fprintf(stderr, "unknown option '%s'\n", argv[arg]);
- }
- arg++;
- }
-
- if (inName == 0L) {
- fprintf(stderr, "usage: %s [-v] [-m mersize] [-memory Nmb] [-s seq.fasta]\n", argv[0]);
- exit(1);
- }
-
- outName = new char [strlen(inName) + 1];
- strcpy(outName, inName);
-
- seqStream *seqstr = new seqStream(inName);
- seqStore *seqsto = new seqStore(outName, seqstr);
-
- uint64 memUsed = seqsto->loadStoreInCore();
- uint64 numMers = seqsto->numberOfACGT();
-
-#warning needed exact number of mers here
-
- fprintf(stderr, "Found "uint64FMT" mers in file of size "uint64FMT"\n", numMers, memUsed);
-
- if (memUsed > memLimit) {
- fprintf(stderr, "ERROR: two-bit encoded sequence file is bigger than allowed memory usage.\n");
- exit(1);
- }
-
- // Allocate a heap to fill up the rest of space
-
- // Allocate a bitPackedHeap to store N merSize*2 integeers.
- // N = (memLimit - memUsed) * 8 / (merSize * 2)
- //
- // The bitPackedHeap doesn't care about the maximum size, only
- // about the block size.
- //
- uint64 pointerWidth = logBaseTwo64(numMers);
- bitPackedMerHeap *heap = new bitPackedMerHeap(seqsto, pointerWidth, 8 * 1024);
-
- speedCounter *S;
-
- uint64 N = (memLimit - memUsed) * 8 / pointerWidth;
- uint64 M = 0;
-
- fprintf(stderr, "Can store "uint64FMT" mer pointers of size "uint64FMT" in the heap.\n", N, pointerWidth);
-
- kMer mer;
-
- if (N > numMers)
- N = numMers;
-
- // Initialize the heap with some numbers
- //
- S = new speedCounter(" Loading heap: %7.2f Mmers -- %8.1f mers/second\r", 1.0, 0x1ffff, beVerbose);
- while (M < N) {
-
-#if 0
- heap->add(M);
- heap->get(mer);
- fprintf(stdout, "ADD "uint64FMT" -- %s\n", M, mer.merToString(str));
-#endif
-
- heap->add(M++);
- S->tick();
- }
- delete S;
-
- // Until we run out of mers, write things out of the heap.
- //
- S = new speedCounter(" Cycling heap: %7.2f Mmers -- %8.1f Mmers/second\r", 1.0, 0x1fff, beVerbose);
- while (M < numMers) {
- heap->add(M++);
- heap->get(mer);
- //fprintf(stdout, "GOT "uint64FMT" -- %s\n", M, mer.merToString(str));
- S->tick();
- }
- delete S;
-
- // And finally, flush the heap.
- //
- S = new speedCounter(" Dumping heap: %7.2f Mmers -- %8.1f Mmers/second\r", 1.0, 0x1fff, beVerbose);
- uint64 idx = heap->get(mer);
- while (idx != ~uint64ZERO) {
- //fprintf(stdout, "OUT "uint64FMT" -- %s\n", idx, mer.merToString(str));
- idx = heap->get(mer);
- S->tick();
- }
- delete S;
-}
diff --git a/meryl/mapMers-depth.C b/meryl/mapMers-depth.C
index 4a0b8f5..40f6e49 100644
--- a/meryl/mapMers-depth.C
+++ b/meryl/mapMers-depth.C
@@ -8,126 +8,182 @@
#include "libmeryl.H"
#include "existDB.H"
-#warning this code might not work due to intervalList changes
+// Three outputs:
+//
+// 1) Number of kmers that span this position. Count of each kmer is ignored.
+// 2) Count of the kmer that begins at this position.
+// 3) Stats of the counts of the kmers that span this position (e.g., ave, min, max, stddev).
int
main(int argc, char **argv) {
- uint32 merSize = 16;
- char *merylFile = 0L;
- char *fastaFile = 0L;
- bool beVerbose = false;
- uint32 loCount = 0;
- uint32 hiCount = ~uint32ZERO;
- uint32 windowsize = 0;
- uint32 skipsize = 0;
+ uint32 merSize = 0;
+ char *merylFile = 0L;
+
+ char *fastaFile = 0L;
+
+ bool outputCount = false;
+ bool outputDepth = false;
+ bool outputStats = false;
int arg=1;
+ int err=0;
while (arg < argc) {
- if (strcmp(argv[arg], "-m") == 0) {
+
+ if (strcmp(argv[arg], "-m") == 0)
merSize = strtouint32(argv[++arg], 0L);
- } else if (strcmp(argv[arg], "-mers") == 0) {
+
+ else if (strcmp(argv[arg], "-mers") == 0)
merylFile = argv[++arg];
- } else if (strcmp(argv[arg], "-seq") == 0) {
+
+ else if (strcmp(argv[arg], "-seq") == 0)
fastaFile = argv[++arg];
- } else if (strcmp(argv[arg], "-v") == 0) {
- beVerbose = true;
- } else if (strcmp(argv[arg], "-lo") == 0) {
- loCount = strtouint32(argv[++arg], 0L);
- } else if (strcmp(argv[arg], "-hi") == 0) {
- hiCount = strtouint32(argv[++arg], 0L);
- } else if (strcmp(argv[arg], "-w") == 0) {
- windowsize = strtouint32(argv[++arg], 0L);
- } else if (strcmp(argv[arg], "-s") == 0) {
- skipsize = strtouint32(argv[++arg], 0L);
- } else {
+
+ else if (strcmp(argv[arg], "-count") == 0)
+ outputCount = true;
+ else if (strcmp(argv[arg], "-depth") == 0)
+ outputDepth = true;
+ else if (strcmp(argv[arg], "-stats") == 0)
+ outputStats = true;
+
+ else {
fprintf(stderr, "unknown option '%s'\n", argv[arg]);
+ err++;
}
+
arg++;
}
- if ((merylFile == 0L) || (fastaFile == 0L)) {
- fprintf(stderr, "usage: %s -m mersize -mers mers -seq fasta > output\n", argv[0]);
+ if (merSize == 0)
+ err++;
+ if (fastaFile == 0L)
+ err++;
+ if (merylFile == 0L)
+ err++;
+ if (outputCount + outputDepth + outputStats != 1)
+ err++;
+
+ if (err) {
+ fprintf(stderr, "usage: %s -mers MERYL -m MERSIZE -seq IN.FASTA [-count | -depth | -stats] > output\n", argv[0]);
+ fprintf(stderr, "\n");
+ fprintf(stderr, "For sequence ordinal 's' and position in that sequence 'p':\n");
+ fprintf(stderr, "\n");
+ fprintf(stderr, " -count - report the count (c) of the single kmer that starts at position (p).\n");
+ fprintf(stderr, " Format: 's p c'\n");
+ fprintf(stderr, " -depth - report the number (n) of kmers that span position (p). Format: 's p n'\n");
+ fprintf(stderr, " -stats - report the min (m), max (M), ave (a) count of all mers that span\n");
+ fprintf(stderr, " position (p). Format: 's p m M a t n'\n");
+ fprintf(stderr, " (also reports total count (t) and number of kmers (n))\n");
+ fprintf(stderr, "\n");
+
+ if (merSize == 0)
+ fprintf(stderr, "ERROR: No mer size (-m) suppled.\n");
+ if (fastaFile == 0L)
+ fprintf(stderr, "ERROR: No fasta input (-seq) suppled.\n");
+ if (merylFile == 0L)
+ fprintf(stderr, "ERROR: No meryl database (-mers) suppled.\n");
+ if (outputCount + outputDepth + outputStats != 1)
+ fprintf(stderr, "ERROR: Exactly one of -count, -depth and -stats may be supplied.\n");
+
exit(1);
}
- existDB *E = new existDB(merylFile, merSize, existDBcounts | existDBcompressCounts | existDBcompressBuckets, loCount, hiCount);
+ // Open the input sequences
+
seqCache *F = new seqCache(fastaFile);
+ // Load kmer counts from a meryl database. existDBcompressBuckets is broken.
+
+ existDB *E = new existDB(merylFile, merSize, existDBcounts | existDBcompressCounts, 0, UINT32_MAX);
+
+ // For each sequence...
+
for (uint32 Sid=0; Sid < F->getNumberOfSequences(); Sid++) {
seqInCore *S = F->getSequenceInCore(Sid);
merStream *MS = new merStream(new kMerBuilder(merSize),
new seqStream(S->sequence(), S->sequenceLength()),
true, true);
- uint32 idlen = 0;
- intervalDepthRegions<uint64> *id = new intervalDepthRegions<uint64> [S->sequenceLength() * 2 + 2];
+ // Build a lists of the min, max and total count at each position.
- while (MS->nextMer()) {
- int32 cnt = (int32)E->count(MS->theFMer()) + (int32)E->count(MS->theRMer());
-
- // Old intervalDepth was to add 'cnt' in the first and subtract 'cnt' in the second.
- // Then to use the 'ct' field below.
- // New intervalDepth is the same, but uses the value field.
- // Count is now the number of intervals that are represented in this block.
-
- id[idlen].pos = MS->thePositionInSequence();
- id[idlen].change = cnt;
- id[idlen].open = true;
- idlen++;
-
- id[idlen].pos = MS->thePositionInSequence() + merSize;
- id[idlen].change = cnt;
- id[idlen].open = false;
- idlen++;
+ uint32 *mincount = new uint32 [S->sequenceLength() + 1];
+ uint32 *maxcount = new uint32 [S->sequenceLength() + 1];
+ uint32 *totcount = new uint32 [S->sequenceLength() + 1];
+ uint32 *numcount = new uint32 [S->sequenceLength() + 1];
+
+ for (uint32 xx=0; xx<S->sequenceLength() + 1; xx++) {
+ mincount[xx] = UINT32_MAX;
+ maxcount[xx] = 0;
+ totcount[xx] = 0;
+ numcount[xx] = 0;
}
- intervalList<uint64> ID(id, idlen);
- uint32 x = 0;
+ // Scan the sequence, find the count.
- uint32 len = S->sequenceLength();
+ while (MS->nextMer()) {
+ uint32 pos = MS->thePositionInSequence();
+ uint32 cnt = E->count(MS->theFMer()) + E->count(MS->theRMer());
+
+ if (cnt == 0)
+ // Mer doesn't exist in the database.
+ continue;
- // Default case, report un-averaged depth at every single location.
- //
- if ((windowsize == 0) && (skipsize == 0)) {
- for (uint32 i=0; i < ID.numberOfIntervals(); i++) {
- for (; x < ID.lo(i); x++)
- fprintf(stdout, uint32FMTW(7)"\t"uint32FMTW(6)"\n", x, 0);
- for (; x < ID.hi(i); x++)
- fprintf(stdout, uint32FMTW(7)"\t"uint32FMTW(6)"\n", x, ID.value(i));
- }
- for (; x < len; x++)
- fprintf(stdout, uint32FMTW(7)"\t"uint32FMTW(6)"\n", x, 0);
+ if (outputCount)
+ totcount[pos] = cnt;
- } else {
- uint32 *depth = new uint32 [len];
- for (x=0; x < len; x++)
- depth[x] = 0;
+ if (outputDepth)
+ for (uint32 xx=pos; xx<pos+merSize; xx++)
+ numcount[xx]++;
- for (uint32 i=0; i < ID.numberOfIntervals(); i++)
- for (x=ID.lo(i); x < ID.hi(i); x++)
- depth[x] = ID.count(i);
+ if (outputStats)
+ for (uint32 xx=pos; xx<pos+merSize; xx++) {
+ totcount[xx] += cnt;
+ numcount[xx]++;
- uint32 avedepth = 0;
+ if (cnt < mincount[xx])
+ mincount[xx] = cnt;
- for (x=0; x < windowsize; x++)
- avedepth += depth[x];
+ if (maxcount[xx] < cnt)
+ maxcount[xx] = cnt;
+ }
+ }
- while (x < len) {
- uint32 avepos = (x - 1) - (windowsize - 1) / 2;
- if ((avepos % skipsize) == 0)
- fprintf(stdout, uint32FMT"\t%.4f\n",
- avepos,
- (double)avedepth / (double)windowsize);
+ // If there is no coverage, the min is still set to UINT32_MAX.
- avedepth = avedepth + depth[x] - depth[x-windowsize];
+ for (uint32 x=0; x < S->sequenceLength(); x++)
+ if (numcount[x] == 0)
+ mincount[x] = 0;
- x++;
- }
+ // Report the single kmer count?
+
+ if (outputCount) {
+ for (uint32 x=0; x < S->sequenceLength(); x++)
+ fprintf(stdout, "%u\t%u\t%u\n", Sid, x, totcount[x]);
+ }
+
+ // Report the depth?
+
+ if (outputDepth) {
+ for (uint32 x=0; x < S->sequenceLength(); x++)
+ fprintf(stdout, "%u\t%u\t%u\n", Sid, x, numcount[x]);
+ }
- delete [] depth;
+ // Report the min/max/ave count?
+
+ if (outputStats) {
+ for (uint32 x=0; x < S->sequenceLength(); x++)
+ fprintf(stdout, "%u\t%u\t%u\t%u\t%u\t%u\t%u\n",
+ Sid, x,
+ mincount[x],
+ maxcount[x],
+ (numcount[x] > 0) ? totcount[x] / numcount[x] : 0,
+ totcount[x],
+ numcount[x]);
}
- delete [] id;
+ delete [] mincount;
+ delete [] maxcount;
+ delete [] totcount;
+ delete [] numcount;
delete MS;
delete S;
diff --git a/meryl/maskMers.C b/meryl/maskMers.C
index a717054..fb27172 100644
--- a/meryl/maskMers.C
+++ b/meryl/maskMers.C
@@ -11,6 +11,13 @@
#define MAX_COVERAGE 51
+// Wed May 20 02:39:41 EDT 2015
+//
+// This appears to be an analysis of the repeat/unique kmer content of a genome. From 2008.
+//
+// The 'mate rescue' appears to be measuring if mates from a library of some size would
+// anchor a repeat to a unique. I think.
+
class mateRescueData {
public:
mateRescueData() {
diff --git a/meryl/testPositionBias.C b/meryl/testPositionBias.C
deleted file mode 100644
index b49d8e2..0000000
--- a/meryl/testPositionBias.C
+++ /dev/null
@@ -1,117 +0,0 @@
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <math.h>
-
-#include "bio++.H"
-#include "libmeryl.H"
-
-
-uint32 *
-collectCounts(char *name, uint32 base) {
- merylStreamReader *A = new merylStreamReader(name);
- uint32 *C = new uint32 [4*4*4];
- char S[32];
- uint32 code = 0;
-
- for (uint32 i=0; i<4*4*4; i++)
- C[i] = 0;
-
- while (A->nextMer()) {
- A->theFMer().merToString(S);
-
- code = 0;
- code |= letterToBits[S[base]];
- code <<= 2;
- code |= letterToBits[S[base+1]];
- code <<= 2;
- code |= letterToBits[S[base+2]];
-
- C[code] += A->theCount();
-
- kMer R = A->theFMer();
- R.reverseComplement();
- R.merToString(S);
-
- code = 0;
- code |= letterToBits[S[base]];
- code <<= 2;
- code |= letterToBits[S[base+1]];
- code <<= 2;
- code |= letterToBits[S[base+2]];
-
- C[code] += A->theCount();
- }
-
- delete A;
-
- return(C);
-}
-
-
-void
-showBias(uint32 base=5) {
- uint32 *A = collectCounts("CNPT3", base);
- uint32 *B = collectCounts("25.errorless", base);
- uint32 *C = collectCounts("25.errorless.simulated", base);
-
- for (uint32 i=0; i<4*4*4; i++) {
- double bp = 0.0;
- double cp = 0.0;
-
- if (A[i] > 0) {
- bp = (double)B[i] / (double)A[i];
- cp = (double)C[i] / (double)A[i];
- }
-
- fprintf(stdout, "%c%c%c "uint32FMTW(3)" A "uint32FMTW(6)" B "uint32FMTW(6)" %.5f C "uint32FMTW(6)" %.5f\n",
- bitsToLetter[(i >> 4) & 0x00000003],
- bitsToLetter[(i >> 2) & 0x00000003],
- bitsToLetter[(i >> 0) & 0x00000003],
- i,
- A[i],
- B[i],
- bp,
- C[i],
- cp);
- }
-}
-
-
-double
-computeRMSD(uint32 base) {
- uint32 *A = collectCounts("CNPT3", base);
- uint32 *B = collectCounts("25.errorless", base);
- uint32 *C = collectCounts("25.errorless.simulated", base);
-
- double rmsd = 0;
-
- for (uint32 i=0; i<4*4*4; i++) {
- double bp = 0.0;
- double cp = 0.0;
-
- if (A[i] > 0) {
- bp = (double)B[i] / (double)A[i];
- cp = (double)C[i] / (double)A[i];
- }
-
- rmsd += (bp - cp) * (bp - cp);
- }
-
- rmsd /= 4*4*4;
-
- return(sqrt(rmsd));
-}
-
-
-
-
-
-int
-main(int argc, char **argv) {
-
- showBias(5);
-
- //for (uint32 i=0; i<23; i++)
- // fprintf(stdout, "rmsd "uint32FMTW(2)" %f\n", i, computeRMSD(i));
-}
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-med/kmer-tools.git
More information about the debian-med-commit
mailing list