├── makefile.mk ├── docs.ist ├── kazlib.pro ├── Android.mk ├── MUST_READ ├── Makefile.gcc ├── blast.pl ├── Makefile.vc ├── comp_trg ├── sfx.h ├── README ├── except.h ├── comp_def ├── dict.h ├── list.h ├── hash.h ├── except.c ├── CHANGES ├── list.c ├── sfx.c ├── hash.c └── dict.c /makefile.mk: -------------------------------------------------------------------------------- 1 | .INCLUDE: $(MAKE_HOME)make.cmp 2 | -------------------------------------------------------------------------------- /docs.ist: -------------------------------------------------------------------------------- 1 | preamble 2 | "\\begin{theindex}\n\\addcontentsline{toc}{section}{Index}\n" 3 | postamble 4 | "\n\\end{theindex}\n" 5 | -------------------------------------------------------------------------------- /kazlib.pro: -------------------------------------------------------------------------------- 1 | TEMPLATE = lib 2 | CONFIG += staticlib 3 | CONFIG -= qt 4 | 5 | include(../../conf.pro) 6 | 7 | SOURCES = dict.c 8 | 9 | DEFINES += NDEBUG 10 | -------------------------------------------------------------------------------- /Android.mk: -------------------------------------------------------------------------------- 1 | 2 | LOCAL_PATH := $(call my-dir) 3 | 4 | include $(CLEAR_VARS) 5 | 6 | LOCAL_MODULE := kazlib 7 | LOCAL_SRC_FILES := dict.c 8 | 9 | LOCAL_CFLAGS := $(MY_CONF_LOCAL_CFLAGS) -DNDEBUG 10 | 11 | include $(BUILD_STATIC_LIBRARY) 12 | 13 | -------------------------------------------------------------------------------- /MUST_READ: -------------------------------------------------------------------------------- 1 | Greetings, Programmer! 2 | 3 | I gather that because you are reading this, you are probably considering using 4 | the C language translation units included here in your own software. If that 5 | is the case, I would like to know who you are and urge you to contact me. 6 | 7 | Here is why: I rove over this code periodically looking for defects. In fact, 8 | I use it in my own programming projects. If I discover a defect, I will 9 | notify everyone who I know is a user of this software. If there is a serious 10 | defect in some code that you are using in your software project, wouldn't you 11 | want to be informed? In fact, there is no question that you _need_ to be 12 | informed! 13 | 14 | Here is what you do: simply send an e-mail message to kaz@ashi.footprints.net 15 | with the subject "kazlib" and the body "I am a user". Be sure that your message 16 | has a good return address. I will manually add your e-mail address to a list 17 | which I will use only for the purpose of notifications regarding Kazlib. You 18 | will receive a reply to the effect that you are added. 19 | 20 | If ever you should wish to be removed from this list, simply ask and it shall 21 | be done. 22 | 23 | Yours in earnest, 24 | 25 | Kaz Kylheku 26 | -------------------------------------------------------------------------------- /Makefile.gcc: -------------------------------------------------------------------------------- 1 | # $Id: Makefile.gcc,v 1.17 1999/11/22 03:19:24 kaz Exp $ 2 | # $Name: kazlib_1_20 $ 3 | 4 | CC = gcc 5 | #CFLAGS = -Wid-clash-15 -Wall -Wmissing-prototypes -ansi -pedantic -g 6 | CFLAGS = -Wall -Wmissing-prototypes -ansi -pedantic -O2 -DNDEBUG 7 | 8 | project: dep tdict tlist thash teh tsfx 9 | 10 | tlist: list.c list.h 11 | $(CC) $(CFLAGS) -DKAZLIB_TEST_MAIN -o tlist list.c 12 | 13 | tdict: dict.c dict.h 14 | $(CC) $(CFLAGS) -DKAZLIB_TEST_MAIN -o tdict dict.c 15 | 16 | thash: hash.c hash.h 17 | $(CC) $(CFLAGS) -DKAZLIB_TEST_MAIN -o thash hash.c 18 | 19 | teh: except.c except.h 20 | $(CC) $(CFLAGS) -DKAZLIB_TEST_MAIN -o teh except.c 21 | 22 | tsfx: sfx.c sfx.h except.o hash.o 23 | $(CC) $(CFLAGS) -DKAZLIB_TEST_MAIN -o tsfx sfx.c except.o hash.o 24 | 25 | docs: docs.dvi 26 | 27 | docs.dvi: docs.ltx docs.toc docs.ind 28 | latex docs.ltx 29 | 30 | docs.ind: docs.idx docs.ist 31 | makeindex -s docs.ist docs.idx 32 | 33 | docs.toc: docs.ltx 34 | latex docs.ltx 35 | 36 | docs.idx: docs.ltx 37 | latex docs.ltx 38 | 39 | clean: 40 | -rm tags tlist tdict thash teh tsfx except.o docs.aux docs.log docs.dvi docs.toc docs.idx docs.ind docs.ilg 41 | 42 | dep: .depend 43 | 44 | .depend: *.h 45 | gcc -MM *.c > .depend 46 | 47 | -include .depend 48 | -------------------------------------------------------------------------------- /blast.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | # 4 | # This is a program whose output can be piped to the test drivers for 5 | # hash.c and dict.c. It inserts a bunch of data and then deletes it all. 6 | # 7 | # The $modulus should be a prime number. This ensures that the $modulus - 1 8 | # generated keys are all distinct. The $factor_i and $factor_d values need not 9 | # be prime, but it should not be a multiple of $modulus (including zero), 10 | # otherwise a sequence of duplicate keys will be generated: choose numbers 11 | # in the range [1, $modulus - 1]. Choosing 1 means that 12 | # insertions (or deletions) will take place in order. 13 | # The purpose of using the prime modulus number is to generate a repeatable 14 | # sequence of unique keys that is (possibly) not in sorted order. 15 | # 16 | # $Id: blast.pl,v 1.4 1999/09/20 21:57:10 kaz Exp $ 17 | # $Name: kazlib_1_20 $ 18 | # 19 | 20 | # $modulus = 200003; 21 | # $factor_i = 100; 22 | # $factor_d = 301; 23 | 24 | $modulus = 6113; 25 | $factor_i = 1669; 26 | $factor_d = 2036; 27 | 28 | for ($i = 1; $i < $modulus; $i++) { 29 | printf("a %d %d\n", ($i * $factor_i) % $modulus, $i); 30 | } 31 | 32 | for ($i = 1; $i < $modulus; $i++) { 33 | printf("d %d\n", ($i * $factor_d) % $modulus); 34 | } 35 | 36 | print "t\nq\n" 37 | -------------------------------------------------------------------------------- /Makefile.vc: -------------------------------------------------------------------------------- 1 | # $Id: Makefile.vc,v 1.4 1999/11/09 03:34:18 kaz Exp $ 2 | # $Name: kazlib_1_20 $ 3 | 4 | CC = cl 5 | CFLAGS = /nologo /Za /W3 /Ox 6 | 7 | project: tdict.exe tlist.exe thash.exe teh.exe tsfx.exe 8 | 9 | tlist.exe: list.c list.h 10 | $(CC) $(CFLAGS) -DKAZLIB_TEST_MAIN /Fotlist.obj /Fetlist.exe list.c 11 | 12 | tdict.exe: dict.c dict.h 13 | $(CC) $(CFLAGS) -DKAZLIB_TEST_MAIN /Fotdict.obj /Fetdict.exe dict.c 14 | 15 | thash.exe: hash.c hash.h 16 | $(CC) $(CFLAGS) -DKAZLIB_TEST_MAIN /Fothash.obj /Fethash.exe hash.c 17 | 18 | teh.exe: except.c except.h 19 | $(CC) $(CFLAGS) -DKAZLIB_TEST_MAIN /Foteh.obj /Feteh.exe except.c 20 | 21 | tsfx.exe: sfx.c sfx.h except.obj hash.obj 22 | $(CC) $(CFLAGS) -DKAZLIB_TEST_MAIN /Fotsfx.obj /Fetsfx.exe sfx.c except.obj hash.obj 23 | 24 | docs: docs.dvi 25 | 26 | docs.dvi: docs.ltx docs.toc docs.ind 27 | latex docs.ltx 28 | 29 | docs.ind: docs.idx docs.ist 30 | makeindex -s docs.ist docs.idx 31 | 32 | docs.toc: docs.ltx 33 | latex docs.ltx 34 | 35 | docs.idx: docs.ltx 36 | latex docs.ltx 37 | 38 | clean: 39 | -del tags tlist.exe tdict.exe thash.exe teh.exe tsfx.exe except.obj docs.aux docs.log docs.dvi docs.toc docs.idx docs.ind docs.ilg 40 | 41 | except.obj: except.c except.h 42 | $(CC) $(CFLAGS) /c except.c 43 | 44 | hash.obj: hash.c hash.h 45 | $(CC) $(CFLAGS) /c hash.c 46 | -------------------------------------------------------------------------------- /comp_trg: -------------------------------------------------------------------------------- 1 | # 2 | # Template for comp_trg file 3 | # 4 | # $Header: /files/home/dwight/itsmake/rcs/comp_trg.tpl 1.1 1994/07/26 21:06:38 dwight Exp dwight $ 5 | 6 | # 7 | # put any special target you have for 8 | # executables and libraries that are 9 | # not built by the make system proper. 10 | # 11 | 12 | # here are some examples for building multiple targets 13 | 14 | # use this if executables have multiple object files (unix only) 15 | # 16 | #.IF $(SINGLE) == $(NULL) 17 | #FROMWAVE_MEMBERS=fromwave openwave 18 | #fromwave: 19 | # [ 20 | # $(MAKE) COMP=$(COMP) SINGLE=yes EXE=fromwave COMP_EXE_MEMBERS="$(FROMWAVE_MEMBERS)" exe 21 | # ] 22 | #.END 23 | # 24 | # 25 | 26 | # (unix or dos) 27 | # 28 | #.IF $(SINGLE) == $(NULL) 29 | #FROMWAVE_MEMBERS=fromwave openwave 30 | #fromwave: 31 | # [ 32 | #.IF $(CMD_STYLE) == DOS 33 | # set COMP_EXE_MEMBERS=$(FROMWAVE_MEMBERS) 34 | # $(MAKE) COMP=$(COMP) SINGLE=yes EXE=fromwave exe 35 | # set COMP_EXE_MEMBERS= 36 | #.ELSE 37 | # 38 | # $(MAKE) COMP=$(COMP) SINGLE=yes EXE=fromwave COMP_EXE_MEMBERS="$(FROMWAVE_MEMBERS)" exe 39 | #.END 40 | # ] 41 | #.END 42 | 43 | 44 | 45 | 46 | # use this if each executable contains one file, ie. foo.c -> foo (or foo.exe) 47 | # 48 | #.IF $(SINGLE) == $(NULL) 49 | # 50 | #$(EXES) : 51 | # $(MAKE) COMP=$(COMP) SINGLE=yes EXE=$@ COMP_EXE_MEMBERS=$(@:b) exe 52 | # 53 | # 54 | #.END 55 | -------------------------------------------------------------------------------- /sfx.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SideChk---A utility which tries to determine whether a given C expression 3 | * is free of side effects. This can be used for verifying that macros which 4 | * expand their arguments more than once are not being accidentally misused. 5 | * 6 | * Copyright (C) 1999 Kaz Kylheku 7 | * 8 | * Free Software License: 9 | * 10 | * All rights are reserved by the author, with the following exceptions: 11 | * Permission is granted to freely reproduce and distribute this software, 12 | * possibly in exchange for a fee, provided that this copyright notice appears 13 | * intact. Permission is also granted to adapt this software to produce 14 | * derivative works, as long as the modified versions carry this copyright 15 | * notice and additional notices stating that the work has been modified. 16 | * This source code may be translated into executable form and incorporated 17 | * into proprietary software; there is no requirement for such software to 18 | * contain a copyright notice related to this source. 19 | * 20 | * $Id: sfx.h,v 1.7 1999/11/07 17:01:53 kaz Exp $ 21 | * $Name: kazlib_1_20 $ 22 | */ 23 | 24 | #ifndef SFX_H 25 | #define SFX_H 26 | 27 | #include 28 | 29 | #ifdef __cplusplus 30 | extern "C" { 31 | #endif 32 | 33 | typedef enum { 34 | sfx_none, sfx_potential, sfx_certain 35 | } sfx_rating_t; 36 | 37 | int sfx_determine(const char *, sfx_rating_t *); 38 | int sfx_declare(const char *, sfx_rating_t); 39 | void sfx_check(const char *, const char *, unsigned long); 40 | 41 | #ifdef __cplusplus 42 | } 43 | #endif 44 | 45 | #define SFX_CHECK(E) (sfx_check(#E, __FILE__, __LINE__), (E)) 46 | #define SFX_STRING(E) #E 47 | 48 | #endif 49 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | This collection of data structures is maintained by 2 | Kaz Kylheku 3 | 4 | INSTRUCTIONS 5 | 6 | Simply add the necessary .c and .h files to your project. Include the 7 | appropriate .h file in any translation unit that interfaces with one or more of 8 | the kazlib modules. Then compile and link the modules together with your program. 9 | 10 | To use kazlib in a C++ project, don't compile them with a C++ compiler. 11 | Compile with a C compiler, and include the header files in 12 | your C++ translation units. Then link together the translated C and C++. 13 | As of release 1.2, the header files should work with C++. 14 | 15 | IMPORTANT NOTES 16 | 17 | 1. Self checks 18 | 19 | The modules in this collection perform extensive self-checks, some of 20 | which make the performance really poor (by actually raising the overall 21 | asymptotic complexity of an operation, for example from O(log N) to O(N). The 22 | instrumentation assertions can be disabled by compiling with the NDEBUG macro 23 | defined. 24 | 25 | You can check that your project does not violate the principles of 26 | implementation hiding in connection with its use of the kazlib modules. This 27 | is accomplished by defining the macro KAZLIB_OPAQUE_DEBUG at the beginning of 28 | any translation unit which includes the kazlib header files. Note that 29 | whereas this will detect violations, it will not result in a translation 30 | that can be linked against the kazlib. When you are done checking, turn 31 | off KAZLIB_OPAQUE_DEBUG and recompile. If your compiler has a special ``check only'' 32 | mode which enables it to perform syntax and type checking without doing 33 | an actual translation (similar to lint), it may be a time-saving idea to 34 | use it in conjunction with KAZLIB_OPAQUE_DEBUG. 35 | 36 | 2. Macros with side effects 37 | 38 | Some of the kazlib header files define macros that evaluate their arguments 39 | more than once. This means that if expressions with side effects are passed 40 | to these macros, undesirable and undefined behavior will happen. There is 41 | support in Kazlib for catching these kinds of bugs: compile with 42 | KAZLIB_SIDEEFFECT_DEBUG, and add the except.c and sfx.c modules to your 43 | object. The macros will now parse their expressions at run time to diagnose 44 | the presence of side effects and function calls. It's easy to add this support 45 | to your own code! 46 | 47 | 3. Thread support 48 | 49 | POSIX thread support is enabled by predefining KAZLIB_POSIX_THREADS. Currently 50 | only the exception-handling module has any need for this. When compiled that 51 | way, it provides thread-safe exception handling. Threads can independently 52 | throw exceptions and each thread can install its own specific catcher 53 | for unhandled exceptions. Moreover, each thread can register its own 54 | memory allocator functions. 55 | 56 | Note: this variant of the code also depends on the ability to cast between void 57 | * and function pointers, which is a common language extension. 58 | 59 | 4. CVS identification 60 | 61 | The source files contain declarations of a static char array variable called 62 | rcsid. This contains an expansion of the CVS identification of each module, 63 | making it possible to determine the ``bill of materials'' that went into an 64 | executable build. I have now wrapped the declarations of these rcsid[] arrays 65 | so they are conditional on KAZLIB_RCSID being defined. For many users, these 66 | are just a waste of space. 67 | 68 | $Id: README,v 1.13 1999/11/13 10:16:25 kaz Exp $ 69 | $Name: kazlib_1_20 $ 70 | -------------------------------------------------------------------------------- /except.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Portable Exception Handling for ANSI C. 3 | * Copyright (C) 1999 Kaz Kylheku 4 | * 5 | * Free Software License: 6 | * 7 | * All rights are reserved by the author, with the following exceptions: 8 | * Permission is granted to freely reproduce and distribute this software, 9 | * possibly in exchange for a fee, provided that this copyright notice appears 10 | * intact. Permission is also granted to adapt this software to produce 11 | * derivative works, as long as the modified versions carry this copyright 12 | * notice and additional notices stating that the work has been modified. 13 | * This source code may be translated into executable form and incorporated 14 | * into proprietary software; there is no requirement for such software to 15 | * contain a copyright notice related to this source. 16 | * 17 | * $Id: except.h,v 1.18.2.1 2001/07/25 03:37:06 kaz Exp $ 18 | * $Name: kazlib_1_20 $ 19 | */ 20 | 21 | #ifndef XCEPT_H 22 | #define XCEPT_H 23 | 24 | #include 25 | #include 26 | #include 27 | 28 | #define XCEPT_GROUP_ANY 0 29 | #define XCEPT_CODE_ANY 0 30 | #define XCEPT_BAD_ALLOC 1 31 | 32 | #ifdef __cplusplus 33 | extern "C" { 34 | #endif 35 | 36 | enum { except_no_call, except_call }; 37 | 38 | typedef struct { 39 | unsigned long except_group; 40 | unsigned long except_code; 41 | } except_id_t; 42 | 43 | typedef struct { 44 | except_id_t volatile except_id; 45 | const char *volatile except_message; 46 | void *volatile except_dyndata; 47 | } except_t; 48 | 49 | struct except_cleanup { 50 | void (*except_func)(void *); 51 | void *except_context; 52 | }; 53 | 54 | struct except_catch { 55 | const except_id_t *except_id; 56 | size_t except_size; 57 | except_t except_obj; 58 | jmp_buf except_jmp; 59 | }; 60 | 61 | enum except_stacktype { 62 | XCEPT_CLEANUP, XCEPT_CATCHER 63 | }; 64 | 65 | struct except_stacknode { 66 | struct except_stacknode *except_down; 67 | enum except_stacktype except_type; 68 | union { 69 | struct except_catch *except_catcher; 70 | struct except_cleanup *except_cleanup; 71 | } except_info; 72 | }; 73 | 74 | /* private functions made external so they can be used in macros */ 75 | void except_setup_clean(struct except_stacknode *, 76 | struct except_cleanup *, void (*)(void *), void *); 77 | void except_setup_try(struct except_stacknode *, 78 | struct except_catch *, const except_id_t [], size_t); 79 | struct except_stacknode *except_pop(void); 80 | 81 | /* public interface functions */ 82 | int except_init(void); 83 | void except_deinit(void); 84 | void except_rethrow(except_t *); 85 | void except_throw(long, long, const char *); 86 | void except_throwd(long, long, const char *, void *); 87 | void except_throwf(long, long, const char *, ...); 88 | void (*except_unhandled_catcher(void (*)(except_t *)))(except_t *); 89 | unsigned long except_code(except_t *); 90 | unsigned long except_group(except_t *); 91 | const char *except_message(except_t *); 92 | void *except_data(except_t *); 93 | void *except_take_data(except_t *); 94 | void except_set_allocator(void *(*)(size_t), void (*)(void *)); 95 | void *except_alloc(size_t); 96 | void except_free(void *); 97 | 98 | #define except_code(E) ((E)->except_id.except_code) 99 | #define except_group(E) ((E)->except_id.except_group) 100 | #define except_message(E) ((E)->except_message) 101 | #define except_data(E) ((E)->except_dyndata) 102 | 103 | #ifdef __cplusplus 104 | } 105 | #endif 106 | 107 | /* 108 | * void except_cleanup_push(void (*)(void *), void *); 109 | * void except_cleanup_pop(int); 110 | * void except_checked_cleanup_pop(void (*)(void *), int); 111 | * void except_try_push(const except_id_t [], size_t, except_t **); 112 | * void except_try_pop(void); 113 | */ 114 | 115 | #define except_cleanup_push(F, C) \ 116 | { \ 117 | struct except_stacknode except_sn; \ 118 | struct except_cleanup except_cl; \ 119 | except_setup_clean(&except_sn, &except_cl, F, C) 120 | 121 | #define except_cleanup_pop(E) \ 122 | except_pop(); \ 123 | if (E) \ 124 | except_cl.except_func(except_cl.except_context); \ 125 | } 126 | 127 | #define except_checked_cleanup_pop(F, E) \ 128 | except_pop(); \ 129 | assert (except_cl.except_func == (F)); \ 130 | if (E) \ 131 | except_cl.except_func(except_cl.except_context); \ 132 | } 133 | 134 | #define except_try_push(ID, NUM, PPE) \ 135 | { \ 136 | struct except_stacknode except_sn; \ 137 | struct except_catch except_ch; \ 138 | except_setup_try(&except_sn, &except_ch, ID, NUM); \ 139 | if (setjmp(except_ch.except_jmp)) \ 140 | *(PPE) = &except_ch.except_obj; \ 141 | else \ 142 | *(PPE) = 0 143 | 144 | #define except_try_pop() \ 145 | except_free(except_ch.except_obj.except_dyndata); \ 146 | except_pop(); \ 147 | } 148 | 149 | #endif 150 | -------------------------------------------------------------------------------- /comp_def: -------------------------------------------------------------------------------- 1 | # 2 | # template for comp_def 3 | # 4 | # $Header: /files/home/dwight/itsmake/rcs/comp_def.tpl 1.3 1994/11/08 23:57:01 dwight Exp dwight $ 5 | # 6 | 7 | COMP_NAME=kazlib 8 | 9 | .IF $(COMP) != $(NULL) 10 | .IF $(COMP) != $(COMP_NAME) 11 | # totally bogus, but need to stop the make here or weird stuff happens 12 | .ERROR: 13 | echo COMP name conflict, makefiles are hosed. 14 | 15 | COMP := __bogus 16 | .INCLUDE: __bogus 17 | 18 | .END 19 | .ELSE 20 | COMP=$(COMP_NAME) 21 | .END 22 | 23 | # 24 | # list of component level C and C++ options 25 | # 26 | COMP_CCOPTS= 27 | COMP_CPPOPTS= 28 | COMP_DFLAGS+= -DNDEBUG 29 | 30 | # The default library built by the component has a basename 31 | # that is generated from the component name. Depending on the 32 | # compiler system used to do the build, the name of the library 33 | # will be something like $(COMP).lib or $(COMP).a 34 | # The same thing is true for the default executable for the component, 35 | # execept it will be named something like $(COMP).exe or $(COMP) 36 | # You could change the default output, but this is not a good idea... 37 | # If you have other executables or libraries to generate, it is 38 | # better to add targets to comp_trg to build them (pending support 39 | # for generating multiple libs/exes in the make system). 40 | 41 | # 42 | # list of things that are put into the library for the component. 43 | # names should be without extensions, like this: 44 | # COMP_LIB_MEMBERS=file1 file2 file3 45 | # 46 | COMP_LIB_MEMBERS= dict 47 | 48 | # list of objects that should be linked into the component executable 49 | # but not put into the library. without extensions: 50 | # COMP_EXE_MEMBERS=file1 file2 file3 51 | 52 | # 53 | # list of executables to be generated by the 54 | # component. Not fully supported. You need extra 55 | # targets in comp_trg to get this to work... 56 | # 57 | #EXES_COMP=$(VER_MEMBERS:s/.c//) 58 | 59 | # list of libraries to link with the component executable. these 60 | # are libraries that are local to the component: 61 | # LIBS_COMP=lib1 lib2 62 | LIBS_COMP= 63 | 64 | # list of libraries to link with the component executable from the 65 | # configuration install directory. 66 | # COMP_LIBS_IMPORT=lib1 lib2 67 | # 68 | #COMP_LIBS_IMPORT=nrc 69 | 70 | # list of libraries to link with component executable from the 71 | # surrounding environment. all parts of the items to be linked (extensions, 72 | # paths, etc.) must be explicit, and the make system does not 73 | # change the values. 74 | # LIBS_FOREIGN=c:\djgpp\lib\libgr.a 75 | # since the make system gives you no help with the names, you may 76 | # have to twiddle with the "IF" conditions to make it work right 77 | # for all the platforms the component can compile on. 78 | # 79 | .IF $(SYSTEM_STYLE) == UNIX 80 | LIBS_FOREIGN= 81 | .ELSE 82 | LIBS_FOREIGN= 83 | .END 84 | 85 | # 86 | # list of libraries (or -l flags) to be used on the link line. 87 | # these must be complete paths or -l flags, and they are not 88 | # used in any dependency checking. 89 | # 90 | #LIBS_SYSTEM=-lm 91 | 92 | # list of files to be put into the source repository for the component. 93 | # complete file names must be specified (without leading path info). 94 | # do not put binary files into the list, since not all version control systems 95 | # can handle binaries. 96 | VER_MEMBERS= \ 97 | CHANGES \ 98 | MUST_READ \ 99 | Makefile.gcc \ 100 | Makefile.vc \ 101 | README \ 102 | blast.pl \ 103 | dict.c \ 104 | dict.h \ 105 | docs.ist \ 106 | docs.ltx \ 107 | except.c \ 108 | except.h \ 109 | hash.c \ 110 | hash.h \ 111 | list.c \ 112 | list.h \ 113 | sfx.c \ 114 | sfx.h 115 | 116 | # list of local include files to install into the configuration include dir 117 | # 118 | # INCLUDES_EXPORT=a.h b.h 119 | INCLUDES_EXPORT= dict.h 120 | 121 | # list of local libraries to install into the configuration lib dir 122 | # no extensions or path info... 123 | # if you don't set this, the default is to install the default library 124 | # for the component. 125 | # if you set it to NONE, nothing is exported. 126 | # 127 | # LIBS_COMP_EXPORT=foolib barlib 128 | #LIBS_COMP_EXPORT=NONE 129 | 130 | # list of executables to export from the component to the config install dir. 131 | # if you don't set this, default is to install the default executable. 132 | # if you set it to NONE, nothing is exported. 133 | # no extensions or paths... 134 | # 135 | # EXES_COMP_EXPORT=foo bar 136 | # 137 | EXES_COMP_EXPORT=NONE 138 | 139 | # 140 | # list of scripts to be exported to config install dir. 141 | # just list the names of the scripts, and they are 142 | # copied over... note: this is done at the same time 143 | # the executable for the component is installed. 144 | # 145 | # SCRIPTS_EXPORT=groovy 146 | 147 | # list of documentation files to export from component to config install dir. 148 | # 149 | # DOCS_EXPORT=foo.doc bar.mif 150 | 151 | # 152 | # list of miscellaneous files to install in "misc" 153 | # MISCS_EXPORT=mumble.baz 154 | -------------------------------------------------------------------------------- /dict.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Dictionary Abstract Data Type 3 | * Copyright (C) 1997 Kaz Kylheku 4 | * 5 | * Free Software License: 6 | * 7 | * All rights are reserved by the author, with the following exceptions: 8 | * Permission is granted to freely reproduce and distribute this software, 9 | * possibly in exchange for a fee, provided that this copyright notice appears 10 | * intact. Permission is also granted to adapt this software to produce 11 | * derivative works, as long as the modified versions carry this copyright 12 | * notice and additional notices stating that the work has been modified. 13 | * This source code may be translated into executable form and incorporated 14 | * into proprietary software; there is no requirement for such software to 15 | * contain a copyright notice related to this source. 16 | * 17 | * $Id: dict.h,v 1.22.2.6 2000/11/13 01:36:44 kaz Exp $ 18 | * $Name: kazlib_1_20 $ 19 | */ 20 | 21 | #ifndef DICT_H 22 | #define DICT_H 23 | 24 | #include 25 | #ifdef KAZLIB_SIDEEFFECT_DEBUG 26 | #include "sfx.h" 27 | #endif 28 | 29 | /* 30 | * Blurb for inclusion into C++ translation units 31 | */ 32 | 33 | #ifdef __cplusplus 34 | extern "C" { 35 | #endif 36 | 37 | typedef unsigned long dictcount_t; 38 | #define DICTCOUNT_T_MAX ULONG_MAX 39 | 40 | /* 41 | * The dictionary is implemented as a red-black tree 42 | */ 43 | 44 | typedef enum { dnode_red, dnode_black } dnode_color_t; 45 | 46 | typedef struct dnode_t { 47 | #if defined(DICT_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG) 48 | struct dnode_t *dict_left; 49 | struct dnode_t *dict_right; 50 | struct dnode_t *dict_parent; 51 | dnode_color_t dict_color; 52 | const void *dict_key; 53 | void *dict_data; 54 | #else 55 | int dict_dummy; 56 | #endif 57 | } dnode_t; 58 | 59 | typedef int (*dict_comp_t)(const void *, const void *); 60 | typedef dnode_t *(*dnode_alloc_t)(void *); 61 | typedef void (*dnode_free_t)(dnode_t *, void *); 62 | 63 | typedef struct dict_t { 64 | #if defined(DICT_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG) 65 | dnode_t dict_nilnode; 66 | dictcount_t dict_nodecount; 67 | dictcount_t dict_maxcount; 68 | dict_comp_t dict_compare; 69 | dnode_alloc_t dict_allocnode; 70 | dnode_free_t dict_freenode; 71 | void *dict_context; 72 | int dict_dupes; 73 | #else 74 | int dict_dummmy; 75 | #endif 76 | } dict_t; 77 | 78 | typedef void (*dnode_process_t)(dict_t *, dnode_t *, void *); 79 | 80 | typedef struct dict_load_t { 81 | #if defined(DICT_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG) 82 | dict_t *dict_dictptr; 83 | dnode_t dict_nilnode; 84 | #else 85 | int dict_dummmy; 86 | #endif 87 | } dict_load_t; 88 | 89 | extern dict_t *dict_create(dictcount_t, dict_comp_t); 90 | extern void dict_set_allocator(dict_t *, dnode_alloc_t, dnode_free_t, void *); 91 | extern void dict_destroy(dict_t *); 92 | extern void dict_free_nodes(dict_t *); 93 | extern void dict_free(dict_t *); 94 | extern dict_t *dict_init(dict_t *, dictcount_t, dict_comp_t); 95 | extern void dict_init_like(dict_t *, const dict_t *); 96 | extern int dict_verify(dict_t *); 97 | extern int dict_similar(const dict_t *, const dict_t *); 98 | extern dnode_t *dict_lookup(dict_t *, const void *); 99 | extern dnode_t *dict_lower_bound(dict_t *, const void *); 100 | extern dnode_t *dict_upper_bound(dict_t *, const void *); 101 | extern void dict_insert(dict_t *, dnode_t *, const void *); 102 | extern dnode_t *dict_delete(dict_t *, dnode_t *); 103 | extern int dict_alloc_insert(dict_t *, const void *, void *); 104 | extern void dict_delete_free(dict_t *, dnode_t *); 105 | extern dnode_t *dict_first(dict_t *); 106 | extern dnode_t *dict_last(dict_t *); 107 | extern dnode_t *dict_next(dict_t *, dnode_t *); 108 | extern dnode_t *dict_prev(dict_t *, dnode_t *); 109 | extern dictcount_t dict_count(dict_t *); 110 | extern int dict_isempty(dict_t *); 111 | extern int dict_isfull(dict_t *); 112 | extern int dict_contains(dict_t *, dnode_t *); 113 | extern void dict_allow_dupes(dict_t *); 114 | extern int dnode_is_in_a_dict(dnode_t *); 115 | extern dnode_t *dnode_create(void *); 116 | extern dnode_t *dnode_init(dnode_t *, void *); 117 | extern void dnode_destroy(dnode_t *); 118 | extern void *dnode_get(dnode_t *); 119 | extern const void *dnode_getkey(dnode_t *); 120 | extern void dnode_put(dnode_t *, void *); 121 | extern void dict_process(dict_t *, void *, dnode_process_t); 122 | extern void dict_load_begin(dict_load_t *, dict_t *); 123 | extern void dict_load_next(dict_load_t *, dnode_t *, const void *); 124 | extern void dict_load_end(dict_load_t *); 125 | extern void dict_merge(dict_t *, dict_t *); 126 | 127 | #if defined(DICT_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG) 128 | #ifdef KAZLIB_SIDEEFFECT_DEBUG 129 | #define dict_isfull(D) (SFX_CHECK(D)->dict_nodecount == (D)->dict_maxcount) 130 | #else 131 | #define dict_isfull(D) ((D)->dict_nodecount == (D)->dict_maxcount) 132 | #endif 133 | #define dict_count(D) ((D)->dict_nodecount) 134 | #define dict_isempty(D) ((D)->dict_nodecount == 0) 135 | #define dnode_get(N) ((N)->dict_data) 136 | #define dnode_getkey(N) ((N)->dict_key) 137 | #define dnode_put(N, X) ((N)->dict_data = (X)) 138 | #endif 139 | 140 | #ifdef __cplusplus 141 | } 142 | #endif 143 | 144 | #endif 145 | -------------------------------------------------------------------------------- /list.h: -------------------------------------------------------------------------------- 1 | /* 2 | * List Abstract Data Type 3 | * Copyright (C) 1997 Kaz Kylheku 4 | * 5 | * Free Software License: 6 | * 7 | * All rights are reserved by the author, with the following exceptions: 8 | * Permission is granted to freely reproduce and distribute this software, 9 | * possibly in exchange for a fee, provided that this copyright notice appears 10 | * intact. Permission is also granted to adapt this software to produce 11 | * derivative works, as long as the modified versions carry this copyright 12 | * notice and additional notices stating that the work has been modified. 13 | * This source code may be translated into executable form and incorporated 14 | * into proprietary software; there is no requirement for such software to 15 | * contain a copyright notice related to this source. 16 | * 17 | * $Id: list.h,v 1.19 1999/11/14 20:46:19 kaz Exp $ 18 | * $Name: kazlib_1_20 $ 19 | */ 20 | 21 | #ifndef LIST_H 22 | #define LIST_H 23 | 24 | #include 25 | 26 | #ifdef KAZLIB_SIDEEFFECT_DEBUG 27 | #include "sfx.h" 28 | #define LIST_SFX_CHECK(E) SFX_CHECK(E) 29 | #else 30 | #define LIST_SFX_CHECK(E) (E) 31 | #endif 32 | 33 | /* 34 | * Blurb for inclusion into C++ translation units 35 | */ 36 | 37 | #ifdef __cplusplus 38 | extern "C" { 39 | #endif 40 | 41 | typedef unsigned long listcount_t; 42 | #define LISTCOUNT_T_MAX ULONG_MAX 43 | 44 | typedef struct lnode_t { 45 | #if defined(LIST_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG) 46 | struct lnode_t *list_next; 47 | struct lnode_t *list_prev; 48 | void *list_data; 49 | #else 50 | int list_dummy; 51 | #endif 52 | } lnode_t; 53 | 54 | typedef struct lnodepool_t { 55 | #if defined(LIST_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG) 56 | struct lnode_t *list_pool; 57 | struct lnode_t *list_free; 58 | listcount_t list_size; 59 | #else 60 | int list_dummy; 61 | #endif 62 | } lnodepool_t; 63 | 64 | typedef struct list_t { 65 | #if defined(LIST_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG) 66 | lnode_t list_nilnode; 67 | listcount_t list_nodecount; 68 | listcount_t list_maxcount; 69 | #else 70 | int list_dummy; 71 | #endif 72 | } list_t; 73 | 74 | lnode_t *lnode_create(void *); 75 | lnode_t *lnode_init(lnode_t *, void *); 76 | void lnode_destroy(lnode_t *); 77 | void lnode_put(lnode_t *, void *); 78 | void *lnode_get(lnode_t *); 79 | int lnode_is_in_a_list(lnode_t *); 80 | 81 | #if defined(LIST_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG) 82 | #define lnode_put(N, D) ((N)->list_data = (D)) 83 | #define lnode_get(N) ((N)->list_data) 84 | #endif 85 | 86 | lnodepool_t *lnode_pool_init(lnodepool_t *, lnode_t *, listcount_t); 87 | lnodepool_t *lnode_pool_create(listcount_t); 88 | void lnode_pool_destroy(lnodepool_t *); 89 | lnode_t *lnode_borrow(lnodepool_t *, void *); 90 | void lnode_return(lnodepool_t *, lnode_t *); 91 | int lnode_pool_isempty(lnodepool_t *); 92 | int lnode_pool_isfrom(lnodepool_t *, lnode_t *); 93 | 94 | list_t *list_init(list_t *, listcount_t); 95 | list_t *list_create(listcount_t); 96 | void list_destroy(list_t *); 97 | void list_destroy_nodes(list_t *); 98 | void list_return_nodes(list_t *, lnodepool_t *); 99 | 100 | listcount_t list_count(list_t *); 101 | int list_isempty(list_t *); 102 | int list_isfull(list_t *); 103 | int list_contains(list_t *, lnode_t *); 104 | 105 | void list_append(list_t *, lnode_t *); 106 | void list_prepend(list_t *, lnode_t *); 107 | void list_ins_before(list_t *, lnode_t *, lnode_t *); 108 | void list_ins_after(list_t *, lnode_t *, lnode_t *); 109 | 110 | lnode_t *list_first(list_t *); 111 | lnode_t *list_last(list_t *); 112 | lnode_t *list_next(list_t *, lnode_t *); 113 | lnode_t *list_prev(list_t *, lnode_t *); 114 | 115 | lnode_t *list_del_first(list_t *); 116 | lnode_t *list_del_last(list_t *); 117 | lnode_t *list_delete(list_t *, lnode_t *); 118 | 119 | void list_process(list_t *, void *, void (*)(list_t *, lnode_t *, void *)); 120 | 121 | int list_verify(list_t *); 122 | 123 | #if defined(LIST_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG) 124 | #define lnode_pool_isempty(P) ((P)->list_free == 0) 125 | #define list_count(L) ((L)->list_nodecount) 126 | #define list_isempty(L) ((L)->list_nodecount == 0) 127 | #define list_isfull(L) (LIST_SFX_CHECK(L)->list_nodecount == (L)->list_maxcount) 128 | #define list_next(L, N) (LIST_SFX_CHECK(N)->list_next == &(L)->list_nilnode ? NULL : (N)->list_next) 129 | #define list_prev(L, N) (LIST_SFX_CHECK(N)->list_prev == &(L)->list_nilnode ? NULL : (N)->list_prev) 130 | #define list_first(L) list_next(LIST_SFX_CHECK(L), &(L)->list_nilnode) 131 | #define list_last(L) list_prev(LIST_SFX_CHECK(L), &(L)->list_nilnode) 132 | #endif 133 | 134 | #if defined(LIST_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG) 135 | #define list_append(L, N) list_ins_before(LIST_SFX_CHECK(L), N, &(L)->list_nilnode) 136 | #define list_prepend(L, N) list_ins_after(LIST_SFX_CHECK(L), N, &(L)->list_nilnode) 137 | #define list_del_first(L) list_delete(LIST_SFX_CHECK(L), list_first(L)) 138 | #define list_del_last(L) list_delete(LIST_SFX_CHECK(L), list_last(L)) 139 | #endif 140 | 141 | /* destination list on the left, source on the right */ 142 | 143 | void list_extract(list_t *, list_t *, lnode_t *, lnode_t *); 144 | void list_transfer(list_t *, list_t *, lnode_t *first); 145 | void list_merge(list_t *, list_t *, int (const void *, const void *)); 146 | void list_sort(list_t *, int (const void *, const void *)); 147 | lnode_t *list_find(list_t *, const void *, int (const void *, const void *)); 148 | int list_is_sorted(list_t *, int (const void *, const void *)); 149 | 150 | #ifdef __cplusplus 151 | } 152 | #endif 153 | 154 | #endif 155 | -------------------------------------------------------------------------------- /hash.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Hash Table Data Type 3 | * Copyright (C) 1997 Kaz Kylheku 4 | * 5 | * Free Software License: 6 | * 7 | * All rights are reserved by the author, with the following exceptions: 8 | * Permission is granted to freely reproduce and distribute this software, 9 | * possibly in exchange for a fee, provided that this copyright notice appears 10 | * intact. Permission is also granted to adapt this software to produce 11 | * derivative works, as long as the modified versions carry this copyright 12 | * notice and additional notices stating that the work has been modified. 13 | * This source code may be translated into executable form and incorporated 14 | * into proprietary software; there is no requirement for such software to 15 | * contain a copyright notice related to this source. 16 | * 17 | * $Id: hash.h,v 1.22.2.7 2000/11/13 01:36:45 kaz Exp $ 18 | * $Name: kazlib_1_20 $ 19 | */ 20 | 21 | #ifndef HASH_H 22 | #define HASH_H 23 | 24 | #include 25 | #ifdef KAZLIB_SIDEEFFECT_DEBUG 26 | #include "sfx.h" 27 | #endif 28 | 29 | /* 30 | * Blurb for inclusion into C++ translation units 31 | */ 32 | 33 | #ifdef __cplusplus 34 | extern "C" { 35 | #endif 36 | 37 | typedef unsigned long hashcount_t; 38 | #define HASHCOUNT_T_MAX ULONG_MAX 39 | 40 | typedef unsigned long hash_val_t; 41 | #define HASH_VAL_T_MAX ULONG_MAX 42 | 43 | extern int hash_val_t_bit; 44 | 45 | #ifndef HASH_VAL_T_BIT 46 | #define HASH_VAL_T_BIT ((int) hash_val_t_bit) 47 | #endif 48 | 49 | /* 50 | * Hash chain node structure. 51 | * Notes: 52 | * 1. This preprocessing directive is for debugging purposes. The effect is 53 | * that if the preprocessor symbol KAZLIB_OPAQUE_DEBUG is defined prior to the 54 | * inclusion of this header, then the structure shall be declared as having 55 | * the single member int __OPAQUE__. This way, any attempts by the 56 | * client code to violate the principles of information hiding (by accessing 57 | * the structure directly) can be diagnosed at translation time. However, 58 | * note the resulting compiled unit is not suitable for linking. 59 | * 2. This is a pointer to the next node in the chain. In the last node of a 60 | * chain, this pointer is null. 61 | * 3. The key is a pointer to some user supplied data that contains a unique 62 | * identifier for each hash node in a given table. The interpretation of 63 | * the data is up to the user. When creating or initializing a hash table, 64 | * the user must supply a pointer to a function for comparing two keys, 65 | * and a pointer to a function for hashing a key into a numeric value. 66 | * 4. The value is a user-supplied pointer to void which may refer to 67 | * any data object. It is not interpreted in any way by the hashing 68 | * module. 69 | * 5. The hashed key is stored in each node so that we don't have to rehash 70 | * each key when the table must grow or shrink. 71 | */ 72 | 73 | typedef struct hnode_t { 74 | #if defined(HASH_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG) /* 1 */ 75 | struct hnode_t *hash_next; /* 2 */ 76 | const void *hash_key; /* 3 */ 77 | void *hash_data; /* 4 */ 78 | hash_val_t hash_hkey; /* 5 */ 79 | #else 80 | int hash_dummy; 81 | #endif 82 | } hnode_t; 83 | 84 | /* 85 | * The comparison function pointer type. A comparison function takes two keys 86 | * and produces a value of -1 if the left key is less than the right key, a 87 | * value of 0 if the keys are equal, and a value of 1 if the left key is 88 | * greater than the right key. 89 | */ 90 | 91 | typedef int (*hash_comp_t)(const void *, const void *); 92 | 93 | /* 94 | * The hashing function performs some computation on a key and produces an 95 | * integral value of type hash_val_t based on that key. For best results, the 96 | * function should have a good randomness properties in *all* significant bits 97 | * over the set of keys that are being inserted into a given hash table. In 98 | * particular, the most significant bits of hash_val_t are most significant to 99 | * the hash module. Only as the hash table expands are less significant bits 100 | * examined. Thus a function that has good distribution in its upper bits but 101 | * not lower is preferrable to one that has poor distribution in the upper bits 102 | * but not the lower ones. 103 | */ 104 | 105 | typedef hash_val_t (*hash_fun_t)(const void *); 106 | 107 | /* 108 | * allocator functions 109 | */ 110 | 111 | typedef hnode_t *(*hnode_alloc_t)(void *); 112 | typedef void (*hnode_free_t)(hnode_t *, void *); 113 | 114 | /* 115 | * This is the hash table control structure. It keeps track of information 116 | * about a hash table, as well as the hash table itself. 117 | * Notes: 118 | * 1. Pointer to the hash table proper. The table is an array of pointers to 119 | * hash nodes (of type hnode_t). If the table is empty, every element of 120 | * this table is a null pointer. A non-null entry points to the first 121 | * element of a chain of nodes. 122 | * 2. This member keeps track of the size of the hash table---that is, the 123 | * number of chain pointers. 124 | * 3. The count member maintains the number of elements that are presently 125 | * in the hash table. 126 | * 4. The maximum count is the greatest number of nodes that can populate this 127 | * table. If the table contains this many nodes, no more can be inserted, 128 | * and the hash_isfull() function returns true. 129 | * 5. The high mark is a population threshold, measured as a number of nodes, 130 | * which, if exceeded, will trigger a table expansion. Only dynamic hash 131 | * tables are subject to this expansion. 132 | * 6. The low mark is a minimum population threshold, measured as a number of 133 | * nodes. If the table population drops below this value, a table shrinkage 134 | * will occur. Only dynamic tables are subject to this reduction. No table 135 | * will shrink beneath a certain absolute minimum number of nodes. 136 | * 7. This is the a pointer to the hash table's comparison function. The 137 | * function is set once at initialization or creation time. 138 | * 8. Pointer to the table's hashing function, set once at creation or 139 | * initialization time. 140 | * 9. The current hash table mask. If the size of the hash table is 2^N, 141 | * this value has its low N bits set to 1, and the others clear. It is used 142 | * to select bits from the result of the hashing function to compute an 143 | * index into the table. 144 | * 10. A flag which indicates whether the table is to be dynamically resized. It 145 | * is set to 1 in dynamically allocated tables, 0 in tables that are 146 | * statically allocated. 147 | */ 148 | 149 | typedef struct hash_t { 150 | #if defined(HASH_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG) 151 | struct hnode_t **hash_table; /* 1 */ 152 | hashcount_t hash_nchains; /* 2 */ 153 | hashcount_t hash_nodecount; /* 3 */ 154 | hashcount_t hash_maxcount; /* 4 */ 155 | hashcount_t hash_highmark; /* 5 */ 156 | hashcount_t hash_lowmark; /* 6 */ 157 | hash_comp_t hash_compare; /* 7 */ 158 | hash_fun_t hash_function; /* 8 */ 159 | hnode_alloc_t hash_allocnode; 160 | hnode_free_t hash_freenode; 161 | void *hash_context; 162 | hash_val_t hash_mask; /* 9 */ 163 | int hash_dynamic; /* 10 */ 164 | #else 165 | int hash_dummy; 166 | #endif 167 | } hash_t; 168 | 169 | /* 170 | * Hash scanner structure, used for traversals of the data structure. 171 | * Notes: 172 | * 1. Pointer to the hash table that is being traversed. 173 | * 2. Reference to the current chain in the table being traversed (the chain 174 | * that contains the next node that shall be retrieved). 175 | * 3. Pointer to the node that will be retrieved by the subsequent call to 176 | * hash_scan_next(). 177 | */ 178 | 179 | typedef struct hscan_t { 180 | #if defined(HASH_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG) 181 | hash_t *hash_table; /* 1 */ 182 | hash_val_t hash_chain; /* 2 */ 183 | hnode_t *hash_next; /* 3 */ 184 | #else 185 | int hash_dummy; 186 | #endif 187 | } hscan_t; 188 | 189 | extern hash_t *hash_create(hashcount_t, hash_comp_t, hash_fun_t); 190 | extern void hash_set_allocator(hash_t *, hnode_alloc_t, hnode_free_t, void *); 191 | extern void hash_destroy(hash_t *); 192 | extern void hash_free_nodes(hash_t *); 193 | extern void hash_free(hash_t *); 194 | extern hash_t *hash_init(hash_t *, hashcount_t, hash_comp_t, 195 | hash_fun_t, hnode_t **, hashcount_t); 196 | extern void hash_insert(hash_t *, hnode_t *, const void *); 197 | extern hnode_t *hash_lookup(hash_t *, const void *); 198 | extern hnode_t *hash_delete(hash_t *, hnode_t *); 199 | extern int hash_alloc_insert(hash_t *, const void *, void *); 200 | extern void hash_delete_free(hash_t *, hnode_t *); 201 | 202 | extern void hnode_put(hnode_t *, void *); 203 | extern void *hnode_get(hnode_t *); 204 | extern const void *hnode_getkey(hnode_t *); 205 | extern hashcount_t hash_count(hash_t *); 206 | extern hashcount_t hash_size(hash_t *); 207 | 208 | extern int hash_isfull(hash_t *); 209 | extern int hash_isempty(hash_t *); 210 | 211 | extern void hash_scan_begin(hscan_t *, hash_t *); 212 | extern hnode_t *hash_scan_next(hscan_t *); 213 | extern hnode_t *hash_scan_delete(hash_t *, hnode_t *); 214 | extern void hash_scan_delfree(hash_t *, hnode_t *); 215 | 216 | extern int hash_verify(hash_t *); 217 | 218 | extern hnode_t *hnode_create(void *); 219 | extern hnode_t *hnode_init(hnode_t *, void *); 220 | extern void hnode_destroy(hnode_t *); 221 | 222 | #if defined(HASH_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG) 223 | #ifdef KAZLIB_SIDEEFFECT_DEBUG 224 | #define hash_isfull(H) (SFX_CHECK(H)->hash_nodecount == (H)->hash_maxcount) 225 | #else 226 | #define hash_isfull(H) ((H)->hash_nodecount == (H)->hash_maxcount) 227 | #endif 228 | #define hash_isempty(H) ((H)->hash_nodecount == 0) 229 | #define hash_count(H) ((H)->hash_nodecount) 230 | #define hash_size(H) ((H)->hash_nchains) 231 | #define hnode_get(N) ((N)->hash_data) 232 | #define hnode_getkey(N) ((N)->hash_key) 233 | #define hnode_put(N, V) ((N)->hash_data = (V)) 234 | #endif 235 | 236 | #ifdef __cplusplus 237 | } 238 | #endif 239 | 240 | #endif 241 | -------------------------------------------------------------------------------- /except.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Portable Exception Handling for ANSI C. 3 | * Copyright (C) 1999 Kaz Kylheku 4 | * 5 | * Free Software License: 6 | * 7 | * All rights are reserved by the author, with the following exceptions: 8 | * Permission is granted to freely reproduce and distribute this software, 9 | * possibly in exchange for a fee, provided that this copyright notice appears 10 | * intact. Permission is also granted to adapt this software to produce 11 | * derivative works, as long as the modified versions carry this copyright 12 | * notice and additional notices stating that the work has been modified. 13 | * This source code may be translated into executable form and incorporated 14 | * into proprietary software; there is no requirement for such software to 15 | * contain a copyright notice related to this source. 16 | * $Id: except.c,v 1.27.2.2 2001/07/27 01:20:34 kaz Exp $ 17 | * $Name: kazlib_1_20 $ 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include "except.h" 26 | 27 | #define XCEPT_BUFFER_SIZE 1024 28 | 29 | #ifdef KAZLIB_RCSID 30 | static const char rcsid[] = "$Id: except.c,v 1.27.2.2 2001/07/27 01:20:34 kaz Exp $"; 31 | #endif 32 | 33 | #define group except_group 34 | #define code except_code 35 | #define id except_id 36 | #define message except_message 37 | #define dyndata except_dyndata 38 | #define func except_func 39 | #define context except_context 40 | #define id except_id 41 | #define size except_size 42 | #define obj except_obj 43 | #define jmp except_jmp 44 | #define down except_down 45 | #define type except_type 46 | #define catcher except_catcher 47 | #define cleanup except_cleanup 48 | #define info except_info 49 | 50 | #ifdef KAZLIB_POSIX_THREADS 51 | 52 | #include 53 | 54 | static pthread_mutex_t init_mtx = PTHREAD_MUTEX_INITIALIZER; 55 | static int init_counter; 56 | static pthread_key_t top_key; 57 | static pthread_key_t uh_key; 58 | static pthread_key_t alloc_key; 59 | static pthread_key_t dealloc_key; 60 | static void unhandled_catcher(except_t *); 61 | 62 | #define get_top() ((struct except_stacknode *) pthread_getspecific(top_key)) 63 | #define set_top(T) (pthread_setspecific(top_key, (T)), (void)((T) == (struct except_stacknode *) 0)) 64 | #define set_catcher(C) (pthread_setspecific(uh_key, (void *) (C)), (void)((C) == (void (*)(except_t *)) 0)) 65 | #define set_alloc(A) (pthread_setspecific(alloc_key, (void *) (A)), (void)((A) == (void *(*)(size_t)) 0)) 66 | #define set_dealloc(D) (pthread_setspecific(dealloc_key, (void *) (D)), (void)((D) == (void (*)(void *)) 0)) 67 | 68 | static void (*get_catcher(void))(except_t *) 69 | { 70 | void (*catcher)(except_t *) = (void (*)(except_t *)) pthread_getspecific(uh_key); 71 | return (catcher == 0) ? unhandled_catcher : catcher; 72 | } 73 | 74 | static void *(*get_alloc(void))(size_t) 75 | { 76 | void *(*alloc)(size_t) = (void *(*)(size_t)) pthread_getspecific(alloc_key); 77 | return (alloc == 0) ? malloc : alloc; 78 | } 79 | 80 | static void (*get_dealloc(void))(void *) 81 | { 82 | void (*dealloc)(void *) = (void (*)(void *)) pthread_getspecific(dealloc_key); 83 | return (dealloc == 0) ? free : dealloc; 84 | } 85 | 86 | int except_init(void) 87 | { 88 | int retval = 1; 89 | 90 | pthread_mutex_lock(&init_mtx); 91 | 92 | assert (init_counter < INT_MAX); 93 | 94 | if (init_counter++ == 0) { 95 | int top_ok = (pthread_key_create(&top_key, 0) == 0); 96 | int uh_ok = (pthread_key_create(&uh_key, 0) == 0); 97 | int alloc_ok = (pthread_key_create(&alloc_key, 0) == 0); 98 | int dealloc_ok = (pthread_key_create(&dealloc_key, 0) == 0); 99 | 100 | if (!top_ok || !uh_ok || !alloc_ok || !dealloc_ok) { 101 | retval = 0; 102 | init_counter = 0; 103 | if (top_ok) 104 | pthread_key_delete(top_key); 105 | if (uh_ok) 106 | pthread_key_delete(uh_key); 107 | if (alloc_ok) 108 | pthread_key_delete(alloc_key); 109 | if (dealloc_ok) 110 | pthread_key_delete(dealloc_key); 111 | } 112 | } 113 | 114 | pthread_mutex_unlock(&init_mtx); 115 | 116 | return retval; 117 | } 118 | 119 | void except_deinit(void) 120 | { 121 | pthread_mutex_lock(&init_mtx); 122 | 123 | assert (init_counter > 0); 124 | 125 | if (--init_counter == 0) { 126 | pthread_key_delete(top_key); 127 | pthread_key_delete(uh_key); 128 | pthread_key_delete(alloc_key); 129 | pthread_key_delete(dealloc_key); 130 | } 131 | 132 | pthread_mutex_unlock(&init_mtx); 133 | } 134 | 135 | #else /* no thread support */ 136 | 137 | static int init_counter; 138 | static void unhandled_catcher(except_t *); 139 | static void (*uh_catcher_ptr)(except_t *) = unhandled_catcher; 140 | static void *(*allocator)(size_t) = malloc; 141 | static void (*deallocator)(void *) = free; 142 | static struct except_stacknode *stack_top; 143 | 144 | #define get_top() (stack_top) 145 | #define set_top(T) (stack_top = (T)) 146 | #define get_catcher() (uh_catcher_ptr) 147 | #define set_catcher(C) (uh_catcher_ptr = (C)) 148 | #define get_alloc() (allocator) 149 | #define set_alloc(A) (allocator = (A)) 150 | #define get_dealloc() (deallocator) 151 | #define set_dealloc(D) (deallocator = (D)) 152 | 153 | int except_init(void) 154 | { 155 | assert (init_counter < INT_MAX); 156 | init_counter++; 157 | return 1; 158 | } 159 | 160 | void except_deinit(void) 161 | { 162 | assert (init_counter > 0); 163 | init_counter--; 164 | } 165 | 166 | #endif 167 | 168 | 169 | static int match(const volatile except_id_t *thrown, const except_id_t *caught) 170 | { 171 | int group_match = (caught->group == XCEPT_GROUP_ANY || caught->group == thrown->group); 172 | int code_match = (caught->code == XCEPT_CODE_ANY || caught->code == thrown->code); 173 | 174 | return group_match && code_match; 175 | } 176 | 177 | static void do_throw(except_t *except) 178 | { 179 | struct except_stacknode *top; 180 | 181 | assert (except->id.group != 0 && except->id.code != 0); 182 | 183 | for (top = get_top(); top != 0; top = top->down) { 184 | if (top->type == XCEPT_CLEANUP) { 185 | top->info.cleanup->func(top->info.cleanup->context); 186 | } else { 187 | struct except_catch *catcher = top->info.catcher; 188 | const except_id_t *pi = catcher->id; 189 | size_t i; 190 | 191 | assert (top->type == XCEPT_CATCHER); 192 | except_free(catcher->obj.dyndata); 193 | 194 | for (i = 0; i < catcher->size; pi++, i++) { 195 | if (match(&except->id, pi)) { 196 | catcher->obj = *except; 197 | set_top(top); 198 | longjmp(catcher->jmp, 1); 199 | } 200 | } 201 | } 202 | } 203 | 204 | set_top(top); 205 | get_catcher()(except); /* unhandled exception */ 206 | abort(); 207 | } 208 | 209 | static void unhandled_catcher(except_t *except) 210 | { 211 | fprintf(stderr, "Unhandled exception (\"%s\", group=%ld, code=%ld)\n", 212 | except->message, except->id.group, except->id.code); 213 | abort(); 214 | } 215 | 216 | static void stack_push(struct except_stacknode *node) 217 | { 218 | node->down = get_top(); 219 | set_top(node); 220 | } 221 | 222 | void except_setup_clean(struct except_stacknode *esn, 223 | struct except_cleanup *ecl, void (*cleanf)(void *), void *context) 224 | { 225 | esn->type = XCEPT_CLEANUP; 226 | ecl->func = cleanf; 227 | ecl->context = context; 228 | esn->info.cleanup = ecl; 229 | stack_push(esn); 230 | } 231 | 232 | void except_setup_try(struct except_stacknode *esn, 233 | struct except_catch *ech, const except_id_t id[], size_t size) 234 | { 235 | ech->id = id; 236 | ech->size = size; 237 | ech->obj.dyndata = 0; 238 | esn->type = XCEPT_CATCHER; 239 | esn->info.catcher = ech; 240 | stack_push(esn); 241 | } 242 | 243 | struct except_stacknode *except_pop(void) 244 | { 245 | struct except_stacknode *top = get_top(); 246 | set_top(top->down); 247 | return top; 248 | } 249 | 250 | void except_rethrow(except_t *except) 251 | { 252 | struct except_stacknode *top = get_top(); 253 | assert (top != 0); 254 | assert (top->type == XCEPT_CATCHER); 255 | assert (&top->info.catcher->obj == except); 256 | set_top(top->down); 257 | do_throw(except); 258 | } 259 | 260 | void except_throw(long group, long code, const char *msg) 261 | { 262 | except_t except; 263 | 264 | except.id.group = group; 265 | except.id.code = code; 266 | except.message = msg; 267 | except.dyndata = 0; 268 | 269 | do_throw(&except); 270 | } 271 | 272 | void except_throwd(long group, long code, const char *msg, void *data) 273 | { 274 | except_t except; 275 | 276 | except.id.group = group; 277 | except.id.code = code; 278 | except.message = msg; 279 | except.dyndata = data; 280 | 281 | do_throw(&except); 282 | } 283 | 284 | void except_throwf(long group, long code, const char *fmt, ...) 285 | { 286 | char *buf = except_alloc(XCEPT_BUFFER_SIZE); 287 | va_list vl; 288 | 289 | va_start (vl, fmt); 290 | vsprintf(buf, fmt, vl); 291 | va_end (vl); 292 | except_throwd(group, code, buf, buf); 293 | } 294 | 295 | void (*except_unhandled_catcher(void (*new_catcher)(except_t *)))(except_t *) 296 | { 297 | void (*old_catcher)(except_t *) = get_catcher(); 298 | set_catcher(new_catcher); 299 | return old_catcher; 300 | } 301 | 302 | #undef except_code 303 | #undef except_group 304 | #undef except_message 305 | #undef except_data 306 | 307 | unsigned long except_code(except_t *ex) 308 | { 309 | return ex->id.code; 310 | } 311 | 312 | unsigned long except_group(except_t *ex) 313 | { 314 | return ex->id.group; 315 | } 316 | 317 | const char *except_message(except_t *ex) 318 | { 319 | return ex->message; 320 | } 321 | 322 | void *except_data(except_t *ex) 323 | { 324 | return ex->dyndata; 325 | } 326 | 327 | void *except_take_data(except_t *ex) 328 | { 329 | void *data = ex->dyndata; 330 | ex->dyndata = 0; 331 | return data; 332 | } 333 | 334 | void except_set_allocator(void *(*alloc)(size_t), void (*dealloc)(void *)) 335 | { 336 | set_alloc(alloc); 337 | set_dealloc(dealloc); 338 | } 339 | 340 | void *except_alloc(size_t size) 341 | { 342 | void *ptr = get_alloc()(size); 343 | 344 | if (ptr == 0) 345 | except_throw(XCEPT_BAD_ALLOC, 0, "out of memory"); 346 | return ptr; 347 | } 348 | 349 | void except_free(void *ptr) 350 | { 351 | get_dealloc()(ptr); 352 | } 353 | 354 | #ifdef KAZLIB_TEST_MAIN 355 | 356 | #include 357 | #include 358 | 359 | static void cleanup(void *arg) 360 | { 361 | printf("cleanup(\"%s\") called\n", (char *) arg); 362 | } 363 | 364 | static void bottom_level(void) 365 | { 366 | char buf[256]; 367 | printf("throw exception? "); fflush(stdout); 368 | fgets(buf, sizeof buf, stdin); 369 | 370 | if (buf[0] >= 0 && toupper(buf[0]) == 'Y') 371 | except_throw(1, 1, "nasty exception"); 372 | } 373 | 374 | static void top_level(void) 375 | { 376 | except_cleanup_push(cleanup, "argument"); 377 | bottom_level(); 378 | except_cleanup_pop(0); 379 | } 380 | 381 | int main(int argc, char **argv) 382 | { 383 | static const except_id_t catch[] = { { 1, 1 }, { 1, 2 } }; 384 | except_t *ex; 385 | 386 | /* 387 | * Nested exception ``try blocks'' 388 | */ 389 | 390 | /* outer */ 391 | except_try_push(catch, 2, &ex); 392 | if (!ex) { 393 | /* inner */ 394 | except_try_push(catch, 2, &ex); 395 | if (!ex) { 396 | top_level(); 397 | } else { 398 | /* inner catch */ 399 | printf("caught exception (inner): \"%s\", s=%ld, c=%ld\n", 400 | except_message(ex), except_group(ex), except_code(ex)); 401 | except_rethrow(ex); 402 | } 403 | except_try_pop(); 404 | } else { 405 | /* outer catch */ 406 | printf("caught exception (outer): \"%s\", s=%ld, c=%ld\n", 407 | except_message(ex), except_group(ex), except_code(ex)); 408 | } 409 | except_try_pop(); 410 | except_throw(99, 99, "exception in main"); 411 | return 0; 412 | } 413 | 414 | 415 | #endif 416 | -------------------------------------------------------------------------------- /CHANGES: -------------------------------------------------------------------------------- 1 | New in 1.20 2 | 3 | 1. Bugfix in except.h. Modified non-volatile auto variables were 4 | being accessed after longjmp. 5 | 6 | New in 1.19 7 | 8 | 1. Rewrite of broken dict_free. 9 | 2. Fixed embarassing build breakages that accidentally went into 1.18 10 | 3. Function hash_scan_delete_free renamed to hash_scan_delfree to be 11 | distinct from hash_scan_delete in the first 14 characters. 12 | 4. To resolve inconsistencies between hash_free and dict_free, 13 | and a difference between the actual behavior of hash_free and 14 | the documented behavior, these two functions are marked obsolescent. 15 | The functions dict_free_nodes and hash_free_nodes are provided. 16 | The obsolescent functions continue to work as before, for now. 17 | 5. Documentation of hash_free is fixed to say that it also subjects 18 | the hash to hash_destroy, which is what the implementation does. 19 | 6. Documentation states what release it is for. 20 | 21 | New in 1.18 22 | 23 | 1. Error in assert expression in list_merge fixed. 24 | 2. Semantics of list_merge extended to allow list to be merged 25 | onto itself (which is a noop). 26 | 3. Clarified interface specification of list_transfer and list_extract; 27 | the source and destination list may be the same object. 28 | 4. New functions: 29 | dict_init_like: create a dictionary similar to another one; 30 | dict_similar: determine whether two dictionaries are similar; 31 | dict_merge: merge contents of one dictionary to another. 32 | 5. Dictionary test main can juggle multiple dictionaries, and test 33 | dict_merge. 34 | 6. If a hash node is inserted into some hash, it is a now a constraint 35 | violation to insert it again into some hash. 36 | 7. The hash_scan_delete_free function has been implemented; it is to 37 | hash_scan_delete what hash_delete_free is to hash_delete. 38 | 39 | New in 1.17 40 | 41 | Carl van Tast : 42 | 1. Removed references to ``safe malloc'' from some comments. 43 | 2. Swapped ``allowed'' and ``not allowed'' in comment to 44 | verify_bintree. 45 | 3. Fixed comment to list_next: this function never returns the 46 | sentinel. 47 | 4. lnode_pool_init: nodes[i].prev = nodes instead of nodes + 1. This 48 | saves one or two CPU cycles :-) and it gives a valid address even 49 | if we have a (somewhat pathological) pool with just one element. 50 | 51 | Kaz: 52 | 5. Dropped extra parameter from tree rotation functions in dict.c. Should 53 | shave a few cycles. 54 | 6. Fixed error in the duplicate key iteration idiom example in the 55 | documentation (see the section on dict_upper_bound). 56 | 7. Forgotten #include added to hash.c 57 | 58 | New in 1.16 59 | 60 | 1. Added an interface for loading the contents of a dictionary from an 61 | ordered sequence. This is done in O(n) time by a direct bottom-up 62 | construction of the red-black tree, making it much faster than 63 | the O(n log n) process of inserting each element. 64 | 2. Miscellaneous cleanup: missing const qualifiers were added 65 | to key pointer parameters, some incorrect comments fixed; 66 | spelling errors corrected in documentation. 67 | 68 | New in 1.15 69 | 70 | 1. Another potential exception handling memory leak fixed. This one 71 | has to do with throwing an exception from within a try-catch region 72 | in which an exception was just caught. The new exception replaces 73 | the old without the old's dynamic memory being disposed of. 74 | 2. Restrictions added on except_rethrow. 75 | 3. Exception module must now be explicitly initialized with except_init. 76 | 4. Structure members in exception header renamed to adhere to documented 77 | namespace. 78 | 5. The exwrap.[ch] source files are gone. There is support for memory 79 | allocation with exception handling in except.c, which supports user 80 | defined allocators. 81 | 6. Three bugfixes to sfx parser. First, unary operators take a cast 82 | expression, not a unary expression. Secondly, sizeof doesn't throw a syntax 83 | error anymore on things that look like casts, but maybe are not. 84 | Thirdly, empty parentheses weren't handled right in treatment of 85 | ambiguous expressions, e.g. (a)() was declared a syntax error. 86 | 7. Changed the representation of hash table chains. They are now 87 | singly linked lists, which means that the overhead of managing 88 | back pointers is gone. Only deletion is slightly more complicated 89 | now because it has to search from the beginning of the chain. 90 | [Rationale: this is okay, since chains are supposed to be short 91 | in a hash table!] 92 | 8. Rewritten test main() in list.c. It's now more like the others 93 | with a menu. Previously it was essentially a file sorting program. 94 | 9. New function: list_find. Exhaustively searches the list for a 95 | matching entry, returns pointer to node if found. 96 | 97 | New in 1.14 98 | 99 | 1. Got rid of some overbearing copyright restrictions. There is no need for 100 | executables to contain copyright notices. In fact, there are no 101 | restrictions on the use, or distribution in executable form. 102 | 2. Tiny tweak in red-black fixup code of dict_insert. 103 | 3. Keys in hash and dict are declared const void * now in all functions 104 | rather than plain void *. This means that casts are no longer 105 | necessary when calling insert or lookup functions with const 106 | data as the key. But casts of the return value of hnode_getkey 107 | or dnode_getkey may be required. 108 | 4. Fixed compile breakage of except.c when posix thread support enabled. 109 | 5. Side effect assertion interface now performs caching, to avoid 110 | parsing the same expressions over and over again. Thus debugging with 111 | KAZLIB_SIDEEFFECT_DEBUG incurs a smaller performance hit. 112 | 6. Major bugfix to sfx expression parser. The function dealing with 113 | disambiguating casts had to be rewritten to do more sophisticated 114 | lookahead and backtracking. It all started with Mark Brady discovered 115 | that (a++)+b was being incorrectly diagnosed as a syntax error. 116 | 7. Added documentation. more examples for uses of dictionaries, and 117 | exception handling. Some documentation about the internals 118 | of exception handling added. Changed document format for narrower 119 | margins, reducing page count and increasing readability. 120 | 8. Bugfix in except_rethrow. It was freeing the dynamic data of the 121 | exception even though it's not handled yet. 122 | 123 | New in 1.13 124 | 125 | 1. Fixed some potential memory leaks in except.c. 126 | 2. Finished all interface documentation. All that is left now 127 | is to flesh out the implementation notes. 128 | 3. Fixed a bug in POSIX threaded variant of except.c. Null 129 | function pointer dereference in unhandled exception case. 130 | 4. Macros beginning with E[A-Z] have been renamed to stay out 131 | of space reserved for . 132 | 5. Identifiers in exwrap.[ch] have been renamed from having 133 | ex_ prefixed to having exwrap_ prefixes. 134 | 135 | New in 1.12 136 | 137 | 1. COOL! New module for detecting side effects in C expressions. 138 | 2. Serious bugfix in hash_init(). The computation of the initial hash 139 | mask was completely botched up. Historically this code has seen little 140 | testing because hashing over a user supplied table is not extendible. 141 | Users of hash_create() are not affected. 142 | 3. Tried to make computation of hash_val_t_bit more threadsafe. It should 143 | be okay if writes to int objects are atomic, and concurrent writes of 144 | the same int value to a given object are safe. 145 | 4. Makefile renamed to Makefile.gcc. Makevile.vc added. The rename 146 | is retroactive to all prior releases. 147 | 5. OPAQUE_DEBUG becomes KAZLIB_OPAQUE_DEBUG and TEST_MAIN becomes 148 | KAZLIB_TEST_MAIN. In general, macros that affect how the modules 149 | build should be confined to a special namespace. 150 | 6. New KAZLIB_SIDEEFFECT_DEBUG feature to enable diagnosis of side 151 | effect expressions being passed to macros that evaluate their arguments 152 | more than once. 153 | 154 | New in 1.11 155 | 156 | 1. Improvements in experimental exception handling module: 157 | except_throwf has been added which takes printf-like arguments; 158 | except_checked_cleanup_pop has been added to provide a measure 159 | of safety; there is now a way to pass arbitrary data from the throw site 160 | to the catch. 161 | 2. Improvements in dict_insert. A redundant call to the comparison function 162 | has been eliminated, resulting in one fewer comparisons per insert 163 | operation! Also a redundant test has been removed from the controlling 164 | expression of the fixup loop, taking advantage of the fact that nil 165 | is always black, and hence the root node always has a black parent. 166 | 3. Small change in dict_delete. A test in the fixup loop has been eliminated 167 | by temporarily coloring the root node red. See comment and diff between 168 | dict.c revision 1.25 and 1.26. 169 | 4. Test program blast.pl deletes keys out of order; to get in order 170 | delete, initialize $factor_d to 1. 171 | 172 | New in 1.10 173 | 174 | 1. The dict_init function now correctly initializes allocator-related 175 | members of the dict structure. 176 | 2. Tiny optimization in dict_lookup---less frequent cases tested last. 177 | 3. Added list_extract, for extracting list slices (more general than 178 | list_transfer). 179 | 4. Incorporated changes from Loic Dachary: hash_free() has been 180 | added for deleting all nodes; hash and compare functions 181 | from the hash.c test code are now available to the user as 182 | defaults if null pointers are given to hash_init() or 183 | hash_create(); and hash_set_allocator restores the default 184 | allocator routines if null pointers are given to it. 185 | 5. Changes to dict analogous to hash: dict_free() added, etc. 186 | 6. New exception handling module added (experimental). 187 | 7. Much new documentation. 188 | 189 | New in 1.9 190 | 191 | 1. Third argument of list_transfer may be null, in which case no nodes 192 | are transferred. [Rationale: allows empty source list to be treated 193 | without special case testing when all nodes are being transferred.] 194 | 2. Two new functions added to dict: dict_upper_bound and dict_lower_bound. 195 | These allow for inexact and range searches. 196 | 197 | New in 1.8 198 | 199 | 1. New improved hashing function in the hash.c test code. It turns out that 200 | when I changed the hash table algorithm, the blast.pl testcase was 201 | hashing all to a single chain due to the pathologically bad hashing 202 | function. The new hashing function should be good enough for general use. 203 | It uses each nybble of the key to index a table of 16 random 32 bit integers. 204 | These integers are XOR-ed into the hash value which is rotated after each 205 | XOR. 206 | 2. Spurious semicolon removed from the #define of HASH_VAL_T_BIT. 207 | 3. I fixed some incorrect comments in hash.c which still talked about the 208 | old algorithm from release 1.5 and older. 209 | 4. The smalloc.c module is no longer supported. It's still in RCS but it's not 210 | tagged as being part of release 1.8, and is not used by any of the other 211 | sources. The standard library memory allocation functions are now used 212 | directly. [Rationale: smalloc.c is overkill and interferes with 213 | integration of the other source files into projects. Conscientious programmer 214 | already ahve their own tools for debugging allocator corruption, anyway.] 215 | 216 | New in 1.7 217 | 218 | 1. Missing #include added to smalloc.h 219 | 2. The dict_delete() functions internals have been changed to make it much 220 | more sane. This function no longer has the potential to return a node 221 | other than the one that is passed to it. 222 | 3. The changes to dict_delete() also fix a serious bug in dict_process(). 223 | The dict_process computes a pointer to a node's successor before 224 | invoking the user callback to process a node. If the user callback calls 225 | dict_delete() on the node, under the old dict_delete() semantics it was 226 | possible for the successor to get deleted instead. Thus dict_process() 227 | could end up with an invalid pointer. 228 | 4. The changes to dict_delete() also mean that key and value information will 229 | never be relocated from one node to another. User code can now rely on this 230 | convenient assumption. 231 | 232 | New in 1.6 233 | 234 | 1. The extendible hashing algorithm internals have changed. This 235 | has a potential impact on the behavior with respect to hashing functions 236 | which were written to work well specifically with the old hashing 237 | scheme. For a silly reason, in the old hashing scheme, the top N bits 238 | were always taken from the results of a hashing function, for a hash 239 | table size of 2^N chains. In the new scheme, the bottom N bits are taken 240 | instead. [Rationale: This is change makes it easier to write portable 241 | hashing functions and simplifies the functions that expand or contract 242 | the table, making them more efficient.] 243 | 2. Added const qualifiers to the rcsid[] and right[] char arrays, 244 | which shuts up the GCC compiler from complaining that these are 245 | unused statics. 246 | 247 | New in 1.5 248 | 249 | 1. First two arguments to list_prune_graft() are reversed. The leftmost 250 | argument is now the destination list. Moreover, the function has been 251 | renamed list_transfer(). [Rationale: this ordering of parameters is 252 | consistent with list_merge(), and the standard C functions 253 | also pass destination pointers on the left. Renaming the function 254 | protects against incorrect use.] 255 | 256 | 2. Red-Black tree dictionaries now support duplicate keys. [Rationale: 257 | duplicate keys could be useful in some applications.] When a dictionary 258 | is created or initialized, it does not allow duplicate keys. The 259 | function dict_allow_dupes() is used to set a flag in a dictionary to 260 | henceforth allow duplicates. Once made, the decision to allow 261 | duplicates cannot be reversed. [Rationale: toggling between allowing 262 | and disallowing duplicates does not seem useful. Once duplicates are 263 | admitted, there is no point in disallowing duplicates.] When a key is 264 | sought in tree that currently allows duplicates, the leftmost node 265 | containing that key is chosen from among the nodes that contain 266 | duplicates of the key. Then dict_next() can be used to fetch the 267 | remaining duplicates one by one. No particular order among the 268 | duplicates may be assumed. However, for what it may be worth, the order 269 | between any two duplicates is preserved for as long as they both remain 270 | in the dictionary. 271 | 272 | 3. The function prototypes in the header files have been modified to eliminate 273 | parameter names. [Rationale: parameter names in prototypes have only 274 | documentary value, and may clash with macro identifiers defined in other 275 | headers.] 276 | 277 | 4. Dictionary and hash table now has support for automatic allocation of 278 | nodes in the insert and delete operations, which means that the user 279 | can add items in one operation instead of the two operations of 280 | allocating a node and inserting it. [Rationale: ease of use.] There is 281 | support for user-defined allocators; the default allocators use the 282 | smalloc.c routines. For any instance of a dict_t or hash_t object, the 283 | user can override the allocator functions by supplying his or her 284 | own pointers to suitable functions, and a context pointer that 285 | will be passed to these functions when they are called through that 286 | particular dict_t or hash_t instance. [Rationale: flexibility, ease of 287 | use, promotes good design.] The funtion pointers can only be set when 288 | the data structure is empty. [Rationale: it is undesirable to switch to 289 | a different allocator when there are nodes in the dictionary; it might 290 | lead to the error of freeing a node with an incorrect allocator.] 291 | 292 | $Id: CHANGES,v 1.49.2.10 2001/07/25 03:37:05 kaz Exp $ 293 | $Name: kazlib_1_20 $ 294 | -------------------------------------------------------------------------------- /list.c: -------------------------------------------------------------------------------- 1 | /* 2 | * List Abstract Data Type 3 | * Copyright (C) 1997 Kaz Kylheku 4 | * 5 | * Free Software License: 6 | * 7 | * All rights are reserved by the author, with the following exceptions: 8 | * Permission is granted to freely reproduce and distribute this software, 9 | * possibly in exchange for a fee, provided that this copyright notice appears 10 | * intact. Permission is also granted to adapt this software to produce 11 | * derivative works, as long as the modified versions carry this copyright 12 | * notice and additional notices stating that the work has been modified. 13 | * This source code may be translated into executable form and incorporated 14 | * into proprietary software; there is no requirement for such software to 15 | * contain a copyright notice related to this source. 16 | * 17 | * $Id: list.c,v 1.19.2.1 2000/04/17 01:07:21 kaz Exp $ 18 | * $Name: kazlib_1_20 $ 19 | */ 20 | 21 | 22 | #include 23 | #include 24 | #include 25 | #define LIST_IMPLEMENTATION 26 | #include "list.h" 27 | 28 | #define next list_next 29 | #define prev list_prev 30 | #define data list_data 31 | 32 | #define pool list_pool 33 | #define fre list_free 34 | #define size list_size 35 | 36 | #define nilnode list_nilnode 37 | #define nodecount list_nodecount 38 | #define maxcount list_maxcount 39 | 40 | #define list_nil(L) (&(L)->nilnode) 41 | #define list_first_priv(L) ((L)->nilnode.next) 42 | #define list_last_priv(L) ((L)->nilnode.prev) 43 | #define lnode_next(N) ((N)->next) 44 | #define lnode_prev(N) ((N)->prev) 45 | 46 | #ifdef KAZLIB_RCSID 47 | static const char rcsid[] = "$Id: list.c,v 1.19.2.1 2000/04/17 01:07:21 kaz Exp $"; 48 | #endif 49 | 50 | /* 51 | * Initialize a list object supplied by the client such that it becomes a valid 52 | * empty list. If the list is to be ``unbounded'', the maxcount should be 53 | * specified as LISTCOUNT_T_MAX, or, alternately, as -1. The value zero 54 | * is not permitted. 55 | */ 56 | 57 | list_t *list_init(list_t *list, listcount_t maxcount) 58 | { 59 | assert (maxcount != 0); 60 | list->nilnode.next = &list->nilnode; 61 | list->nilnode.prev = &list->nilnode; 62 | list->nodecount = 0; 63 | list->maxcount = maxcount; 64 | return list; 65 | } 66 | 67 | /* 68 | * Dynamically allocate a list object using malloc(), and initialize it so that 69 | * it is a valid empty list. If the list is to be ``unbounded'', the maxcount 70 | * should be specified as LISTCOUNT_T_MAX, or, alternately, as -1. 71 | */ 72 | 73 | list_t *list_create(listcount_t maxcount) 74 | { 75 | list_t *new = malloc(sizeof *new); 76 | if (new) { 77 | assert (maxcount != 0); 78 | new->nilnode.next = &new->nilnode; 79 | new->nilnode.prev = &new->nilnode; 80 | new->nodecount = 0; 81 | new->maxcount = maxcount; 82 | } 83 | return new; 84 | } 85 | 86 | /* 87 | * Destroy a dynamically allocated list object. 88 | * The client must remove the nodes first. 89 | */ 90 | 91 | void list_destroy(list_t *list) 92 | { 93 | assert (list_isempty(list)); 94 | free(list); 95 | } 96 | 97 | /* 98 | * Free all of the nodes of a list. The list must contain only 99 | * dynamically allocated nodes. After this call, the list 100 | * is empty. 101 | */ 102 | 103 | void list_destroy_nodes(list_t *list) 104 | { 105 | lnode_t *lnode = list_first_priv(list), *nil = list_nil(list), *tmp; 106 | 107 | while (lnode != nil) { 108 | tmp = lnode->next; 109 | lnode->next = NULL; 110 | lnode->prev = NULL; 111 | lnode_destroy(lnode); 112 | lnode = tmp; 113 | } 114 | 115 | list_init(list, list->maxcount); 116 | } 117 | 118 | /* 119 | * Return all of the nodes of a list to a node pool. The nodes in 120 | * the list must all have come from the same pool. 121 | */ 122 | 123 | void list_return_nodes(list_t *list, lnodepool_t *pool) 124 | { 125 | lnode_t *lnode = list_first_priv(list), *tmp, *nil = list_nil(list); 126 | 127 | while (lnode != nil) { 128 | tmp = lnode->next; 129 | lnode->next = NULL; 130 | lnode->prev = NULL; 131 | lnode_return(pool, lnode); 132 | lnode = tmp; 133 | } 134 | 135 | list_init(list, list->maxcount); 136 | } 137 | 138 | /* 139 | * Insert the node ``new'' into the list immediately after ``this'' node. 140 | */ 141 | 142 | void list_ins_after(list_t *list, lnode_t *new, lnode_t *this) 143 | { 144 | lnode_t *that = this->next; 145 | 146 | assert (new != NULL); 147 | assert (!list_contains(list, new)); 148 | assert (!lnode_is_in_a_list(new)); 149 | assert (this == list_nil(list) || list_contains(list, this)); 150 | assert (list->nodecount + 1 > list->nodecount); 151 | 152 | new->prev = this; 153 | new->next = that; 154 | that->prev = new; 155 | this->next = new; 156 | list->nodecount++; 157 | 158 | assert (list->nodecount <= list->maxcount); 159 | } 160 | 161 | /* 162 | * Insert the node ``new'' into the list immediately before ``this'' node. 163 | */ 164 | 165 | void list_ins_before(list_t *list, lnode_t *new, lnode_t *this) 166 | { 167 | lnode_t *that = this->prev; 168 | 169 | assert (new != NULL); 170 | assert (!list_contains(list, new)); 171 | assert (!lnode_is_in_a_list(new)); 172 | assert (this == list_nil(list) || list_contains(list, this)); 173 | assert (list->nodecount + 1 > list->nodecount); 174 | 175 | new->next = this; 176 | new->prev = that; 177 | that->next = new; 178 | this->prev = new; 179 | list->nodecount++; 180 | 181 | assert (list->nodecount <= list->maxcount); 182 | } 183 | 184 | /* 185 | * Delete the given node from the list. 186 | */ 187 | 188 | lnode_t *list_delete(list_t *list, lnode_t *del) 189 | { 190 | lnode_t *next = del->next; 191 | lnode_t *prev = del->prev; 192 | 193 | assert (list_contains(list, del)); 194 | 195 | prev->next = next; 196 | next->prev = prev; 197 | list->nodecount--; 198 | 199 | del->next = del->prev = NULL; 200 | 201 | return del; 202 | } 203 | 204 | /* 205 | * For each node in the list, execute the given function. The list, 206 | * current node and the given context pointer are passed on each 207 | * call to the function. 208 | */ 209 | 210 | void list_process(list_t *list, void *context, 211 | void (* function)(list_t *list, lnode_t *lnode, void *context)) 212 | { 213 | lnode_t *node = list_first_priv(list), *next, *nil = list_nil(list); 214 | 215 | while (node != nil) { 216 | /* check for callback function deleting */ 217 | /* the next node from under us */ 218 | assert (list_contains(list, node)); 219 | next = node->next; 220 | function(list, node, context); 221 | node = next; 222 | } 223 | } 224 | 225 | /* 226 | * Dynamically allocate a list node and assign it the given piece of data. 227 | */ 228 | 229 | lnode_t *lnode_create(void *data) 230 | { 231 | lnode_t *new = malloc(sizeof *new); 232 | if (new) { 233 | new->data = data; 234 | new->next = NULL; 235 | new->prev = NULL; 236 | } 237 | return new; 238 | } 239 | 240 | /* 241 | * Initialize a user-supplied lnode. 242 | */ 243 | 244 | lnode_t *lnode_init(lnode_t *lnode, void *data) 245 | { 246 | lnode->data = data; 247 | lnode->next = NULL; 248 | lnode->prev = NULL; 249 | return lnode; 250 | } 251 | 252 | /* 253 | * Destroy a dynamically allocated node. 254 | */ 255 | 256 | void lnode_destroy(lnode_t *lnode) 257 | { 258 | assert (!lnode_is_in_a_list(lnode)); 259 | free(lnode); 260 | } 261 | 262 | /* 263 | * Initialize a node pool object to use a user-supplied set of nodes. 264 | * The ``nodes'' pointer refers to an array of lnode_t objects, containing 265 | * ``n'' elements. 266 | */ 267 | 268 | lnodepool_t *lnode_pool_init(lnodepool_t *pool, lnode_t *nodes, listcount_t n) 269 | { 270 | listcount_t i; 271 | 272 | assert (n != 0); 273 | 274 | pool->pool = nodes; 275 | pool->fre = nodes; 276 | pool->size = n; 277 | for (i = 0; i < n - 1; i++) { 278 | nodes[i].next = nodes + i + 1; 279 | } 280 | nodes[i].next = NULL; 281 | nodes[i].prev = nodes; /* to make sure node is marked ``on list'' */ 282 | return pool; 283 | } 284 | 285 | /* 286 | * Create a dynamically allocated pool of n nodes. 287 | */ 288 | 289 | lnodepool_t *lnode_pool_create(listcount_t n) 290 | { 291 | lnodepool_t *pool; 292 | lnode_t *nodes; 293 | 294 | assert (n != 0); 295 | 296 | pool = malloc(sizeof *pool); 297 | if (!pool) 298 | return NULL; 299 | nodes = malloc(n * sizeof *nodes); 300 | if (!nodes) { 301 | free(pool); 302 | return NULL; 303 | } 304 | lnode_pool_init(pool, nodes, n); 305 | return pool; 306 | } 307 | 308 | /* 309 | * Determine whether the given pool is from this pool. 310 | */ 311 | 312 | int lnode_pool_isfrom(lnodepool_t *pool, lnode_t *node) 313 | { 314 | listcount_t i; 315 | 316 | /* this is carefully coded this way because ANSI C forbids pointers 317 | to different objects from being subtracted or compared other 318 | than for exact equality */ 319 | 320 | for (i = 0; i < pool->size; i++) { 321 | if (pool->pool + i == node) 322 | return 1; 323 | } 324 | return 0; 325 | } 326 | 327 | /* 328 | * Destroy a dynamically allocated pool of nodes. 329 | */ 330 | 331 | void lnode_pool_destroy(lnodepool_t *p) 332 | { 333 | free(p->pool); 334 | free(p); 335 | } 336 | 337 | /* 338 | * Borrow a node from a node pool. Returns a null pointer if the pool 339 | * is exhausted. 340 | */ 341 | 342 | lnode_t *lnode_borrow(lnodepool_t *pool, void *data) 343 | { 344 | lnode_t *new = pool->fre; 345 | if (new) { 346 | pool->fre = new->next; 347 | new->data = data; 348 | new->next = NULL; 349 | new->prev = NULL; 350 | } 351 | return new; 352 | } 353 | 354 | /* 355 | * Return a node to a node pool. A node must be returned to the pool 356 | * from which it came. 357 | */ 358 | 359 | void lnode_return(lnodepool_t *pool, lnode_t *node) 360 | { 361 | assert (lnode_pool_isfrom(pool, node)); 362 | assert (!lnode_is_in_a_list(node)); 363 | 364 | node->next = pool->fre; 365 | node->prev = node; 366 | pool->fre = node; 367 | } 368 | 369 | /* 370 | * Determine whether the given list contains the given node. 371 | * According to this function, a list does not contain its nilnode. 372 | */ 373 | 374 | int list_contains(list_t *list, lnode_t *node) 375 | { 376 | lnode_t *n, *nil = list_nil(list); 377 | 378 | for (n = list_first_priv(list); n != nil; n = lnode_next(n)) { 379 | if (node == n) 380 | return 1; 381 | } 382 | 383 | return 0; 384 | } 385 | 386 | /* 387 | * A more generalized variant of list_transfer. This one removes a 388 | * ``slice'' from the source list and appends it to the destination 389 | * list. 390 | */ 391 | 392 | void list_extract(list_t *dest, list_t *source, lnode_t *first, lnode_t *last) 393 | { 394 | listcount_t moved = 1; 395 | 396 | assert (first == NULL || list_contains(source, first)); 397 | assert (last == NULL || list_contains(source, last)); 398 | 399 | if (first == NULL || last == NULL) 400 | return; 401 | 402 | /* adjust the destination list so that the slice is spliced out */ 403 | 404 | first->prev->next = last->next; 405 | last->next->prev = first->prev; 406 | 407 | /* graft the splice at the end of the dest list */ 408 | 409 | last->next = &dest->nilnode; 410 | first->prev = dest->nilnode.prev; 411 | dest->nilnode.prev->next = first; 412 | dest->nilnode.prev = last; 413 | 414 | while (first != last) { 415 | first = first->next; 416 | assert (first != list_nil(source)); /* oops, last before first! */ 417 | moved++; 418 | } 419 | 420 | /* assert no overflows */ 421 | assert (source->nodecount - moved <= source->nodecount); 422 | assert (dest->nodecount + moved >= dest->nodecount); 423 | 424 | /* assert no weirdness */ 425 | assert (moved <= source->nodecount); 426 | 427 | source->nodecount -= moved; 428 | dest->nodecount += moved; 429 | 430 | /* assert list sanity */ 431 | assert (list_verify(source)); 432 | assert (list_verify(dest)); 433 | } 434 | 435 | 436 | /* 437 | * Split off a trailing sequence of nodes from the source list and relocate 438 | * them to the tail of the destination list. The trailing sequence begins 439 | * with node ``first'' and terminates with the last node of the source 440 | * list. The nodes are added to the end of the new list in their original 441 | * order. 442 | */ 443 | 444 | void list_transfer(list_t *dest, list_t *source, lnode_t *first) 445 | { 446 | listcount_t moved = 1; 447 | lnode_t *last; 448 | 449 | assert (first == NULL || list_contains(source, first)); 450 | 451 | if (first == NULL) 452 | return; 453 | 454 | last = source->nilnode.prev; 455 | 456 | source->nilnode.prev = first->prev; 457 | first->prev->next = &source->nilnode; 458 | 459 | last->next = &dest->nilnode; 460 | first->prev = dest->nilnode.prev; 461 | dest->nilnode.prev->next = first; 462 | dest->nilnode.prev = last; 463 | 464 | while (first != last) { 465 | first = first->next; 466 | moved++; 467 | } 468 | 469 | /* assert no overflows */ 470 | assert (source->nodecount - moved <= source->nodecount); 471 | assert (dest->nodecount + moved >= dest->nodecount); 472 | 473 | /* assert no weirdness */ 474 | assert (moved <= source->nodecount); 475 | 476 | source->nodecount -= moved; 477 | dest->nodecount += moved; 478 | 479 | /* assert list sanity */ 480 | assert (list_verify(source)); 481 | assert (list_verify(dest)); 482 | } 483 | 484 | void list_merge(list_t *dest, list_t *sour, 485 | int compare (const void *, const void *)) 486 | { 487 | lnode_t *dn, *sn, *tn; 488 | lnode_t *d_nil = list_nil(dest), *s_nil = list_nil(sour); 489 | 490 | /* Nothing to do if source and destination list are the same. */ 491 | if (dest == sour) 492 | return; 493 | 494 | /* overflow check */ 495 | assert (list_count(sour) + list_count(dest) >= list_count(sour)); 496 | 497 | /* lists must be sorted */ 498 | assert (list_is_sorted(sour, compare)); 499 | assert (list_is_sorted(dest, compare)); 500 | 501 | dn = list_first_priv(dest); 502 | sn = list_first_priv(sour); 503 | 504 | while (dn != d_nil && sn != s_nil) { 505 | if (compare(lnode_get(dn), lnode_get(sn)) >= 0) { 506 | tn = lnode_next(sn); 507 | list_delete(sour, sn); 508 | list_ins_before(dest, sn, dn); 509 | sn = tn; 510 | } else { 511 | dn = lnode_next(dn); 512 | } 513 | } 514 | 515 | if (dn != d_nil) 516 | return; 517 | 518 | if (sn != s_nil) 519 | list_transfer(dest, sour, sn); 520 | } 521 | 522 | void list_sort(list_t *list, int compare(const void *, const void *)) 523 | { 524 | list_t extra; 525 | listcount_t middle; 526 | lnode_t *node; 527 | 528 | if (list_count(list) > 1) { 529 | middle = list_count(list) / 2; 530 | node = list_first_priv(list); 531 | 532 | list_init(&extra, list_count(list) - middle); 533 | 534 | while (middle--) 535 | node = lnode_next(node); 536 | 537 | list_transfer(&extra, list, node); 538 | list_sort(list, compare); 539 | list_sort(&extra, compare); 540 | list_merge(list, &extra, compare); 541 | } 542 | assert (list_is_sorted(list, compare)); 543 | } 544 | 545 | lnode_t *list_find(list_t *list, const void *key, int compare(const void *, const void *)) 546 | { 547 | lnode_t *node; 548 | 549 | for (node = list_first_priv(list); node != list_nil(list); node = node->next) { 550 | if (compare(lnode_get(node), key) == 0) 551 | return node; 552 | } 553 | 554 | return 0; 555 | } 556 | 557 | 558 | /* 559 | * Return 1 if the list is in sorted order, 0 otherwise 560 | */ 561 | 562 | int list_is_sorted(list_t *list, int compare(const void *, const void *)) 563 | { 564 | lnode_t *node, *next, *nil; 565 | 566 | next = nil = list_nil(list); 567 | node = list_first_priv(list); 568 | 569 | if (node != nil) 570 | next = lnode_next(node); 571 | 572 | for (; next != nil; node = next, next = lnode_next(next)) { 573 | if (compare(lnode_get(node), lnode_get(next)) > 0) 574 | return 0; 575 | } 576 | 577 | return 1; 578 | } 579 | 580 | /* 581 | * Get rid of macro functions definitions so they don't interfere 582 | * with the actual definitions 583 | */ 584 | 585 | #undef list_isempty 586 | #undef list_isfull 587 | #undef lnode_pool_isempty 588 | #undef list_append 589 | #undef list_prepend 590 | #undef list_first 591 | #undef list_last 592 | #undef list_next 593 | #undef list_prev 594 | #undef list_count 595 | #undef list_del_first 596 | #undef list_del_last 597 | #undef lnode_put 598 | #undef lnode_get 599 | 600 | /* 601 | * Return 1 if the list is empty, 0 otherwise 602 | */ 603 | 604 | int list_isempty(list_t *list) 605 | { 606 | return list->nodecount == 0; 607 | } 608 | 609 | /* 610 | * Return 1 if the list is full, 0 otherwise 611 | * Permitted only on bounded lists. 612 | */ 613 | 614 | int list_isfull(list_t *list) 615 | { 616 | return list->nodecount == list->maxcount; 617 | } 618 | 619 | /* 620 | * Check if the node pool is empty. 621 | */ 622 | 623 | int lnode_pool_isempty(lnodepool_t *pool) 624 | { 625 | return (pool->fre == NULL); 626 | } 627 | 628 | /* 629 | * Add the given node at the end of the list 630 | */ 631 | 632 | void list_append(list_t *list, lnode_t *node) 633 | { 634 | list_ins_before(list, node, &list->nilnode); 635 | } 636 | 637 | /* 638 | * Add the given node at the beginning of the list. 639 | */ 640 | 641 | void list_prepend(list_t *list, lnode_t *node) 642 | { 643 | list_ins_after(list, node, &list->nilnode); 644 | } 645 | 646 | /* 647 | * Retrieve the first node of the list 648 | */ 649 | 650 | lnode_t *list_first(list_t *list) 651 | { 652 | if (list->nilnode.next == &list->nilnode) 653 | return NULL; 654 | return list->nilnode.next; 655 | } 656 | 657 | /* 658 | * Retrieve the last node of the list 659 | */ 660 | 661 | lnode_t *list_last(list_t *list) 662 | { 663 | if (list->nilnode.prev == &list->nilnode) 664 | return NULL; 665 | return list->nilnode.prev; 666 | } 667 | 668 | /* 669 | * Retrieve the count of nodes in the list 670 | */ 671 | 672 | listcount_t list_count(list_t *list) 673 | { 674 | return list->nodecount; 675 | } 676 | 677 | /* 678 | * Remove the first node from the list and return it. 679 | */ 680 | 681 | lnode_t *list_del_first(list_t *list) 682 | { 683 | return list_delete(list, list->nilnode.next); 684 | } 685 | 686 | /* 687 | * Remove the last node from the list and return it. 688 | */ 689 | 690 | lnode_t *list_del_last(list_t *list) 691 | { 692 | return list_delete(list, list->nilnode.prev); 693 | } 694 | 695 | 696 | /* 697 | * Associate a data item with the given node. 698 | */ 699 | 700 | void lnode_put(lnode_t *lnode, void *data) 701 | { 702 | lnode->data = data; 703 | } 704 | 705 | /* 706 | * Retrieve the data item associated with the node. 707 | */ 708 | 709 | void *lnode_get(lnode_t *lnode) 710 | { 711 | return lnode->data; 712 | } 713 | 714 | /* 715 | * Retrieve the node's successor. If there is no successor, 716 | * NULL is returned. 717 | */ 718 | 719 | lnode_t *list_next(list_t *list, lnode_t *lnode) 720 | { 721 | assert (list_contains(list, lnode)); 722 | 723 | if (lnode->next == list_nil(list)) 724 | return NULL; 725 | return lnode->next; 726 | } 727 | 728 | /* 729 | * Retrieve the node's predecessor. See comment for lnode_next(). 730 | */ 731 | 732 | lnode_t *list_prev(list_t *list, lnode_t *lnode) 733 | { 734 | assert (list_contains(list, lnode)); 735 | 736 | if (lnode->prev == list_nil(list)) 737 | return NULL; 738 | return lnode->prev; 739 | } 740 | 741 | /* 742 | * Return 1 if the lnode is in some list, otherwise return 0. 743 | */ 744 | 745 | int lnode_is_in_a_list(lnode_t *lnode) 746 | { 747 | return (lnode->next != NULL || lnode->prev != NULL); 748 | } 749 | 750 | 751 | int list_verify(list_t *list) 752 | { 753 | lnode_t *node = list_first_priv(list), *nil = list_nil(list); 754 | listcount_t count = list_count(list); 755 | 756 | if (node->prev != nil) 757 | return 0; 758 | 759 | if (count > list->maxcount) 760 | return 0; 761 | 762 | while (node != nil && count--) { 763 | if (node->next->prev != node) 764 | return 0; 765 | node = node->next; 766 | } 767 | 768 | if (count != 0 || node != nil) 769 | return 0; 770 | 771 | return 1; 772 | } 773 | 774 | #ifdef KAZLIB_TEST_MAIN 775 | 776 | #include 777 | #include 778 | #include 779 | #include 780 | 781 | typedef char input_t[256]; 782 | 783 | static int tokenize(char *string, ...) 784 | { 785 | char **tokptr; 786 | va_list arglist; 787 | int tokcount = 0; 788 | 789 | va_start(arglist, string); 790 | tokptr = va_arg(arglist, char **); 791 | while (tokptr) { 792 | while (*string && isspace((unsigned char) *string)) 793 | string++; 794 | if (!*string) 795 | break; 796 | *tokptr = string; 797 | while (*string && !isspace((unsigned char) *string)) 798 | string++; 799 | tokptr = va_arg(arglist, char **); 800 | tokcount++; 801 | if (!*string) 802 | break; 803 | *string++ = 0; 804 | } 805 | va_end(arglist); 806 | 807 | return tokcount; 808 | } 809 | 810 | static int comparef(const void *key1, const void *key2) 811 | { 812 | return strcmp(key1, key2); 813 | } 814 | 815 | static char *dupstring(char *str) 816 | { 817 | int sz = strlen(str) + 1; 818 | char *new = malloc(sz); 819 | if (new) 820 | memcpy(new, str, sz); 821 | return new; 822 | } 823 | 824 | int main(void) 825 | { 826 | input_t in; 827 | list_t *l = list_create(LISTCOUNT_T_MAX); 828 | lnode_t *ln; 829 | char *tok1, *val; 830 | int prompt = 0; 831 | 832 | char *help = 833 | "a append value to list\n" 834 | "d delete value from list\n" 835 | "l lookup value in list\n" 836 | "s sort list\n" 837 | "c show number of entries\n" 838 | "t dump whole list\n" 839 | "p turn prompt on\n" 840 | "q quit"; 841 | 842 | if (!l) 843 | puts("list_create failed"); 844 | 845 | for (;;) { 846 | if (prompt) 847 | putchar('>'); 848 | fflush(stdout); 849 | 850 | if (!fgets(in, sizeof(input_t), stdin)) 851 | break; 852 | 853 | switch(in[0]) { 854 | case '?': 855 | puts(help); 856 | break; 857 | case 'a': 858 | if (tokenize(in+1, &tok1, (char **) 0) != 1) { 859 | puts("what?"); 860 | break; 861 | } 862 | val = dupstring(tok1); 863 | ln = lnode_create(val); 864 | 865 | if (!val || !ln) { 866 | puts("allocation failure"); 867 | if (ln) 868 | lnode_destroy(ln); 869 | free(val); 870 | break; 871 | } 872 | 873 | list_append(l, ln); 874 | break; 875 | case 'd': 876 | if (tokenize(in+1, &tok1, (char **) 0) != 1) { 877 | puts("what?"); 878 | break; 879 | } 880 | ln = list_find(l, tok1, comparef); 881 | if (!ln) { 882 | puts("list_find failed"); 883 | break; 884 | } 885 | list_delete(l, ln); 886 | val = lnode_get(ln); 887 | lnode_destroy(ln); 888 | free(val); 889 | break; 890 | case 'l': 891 | if (tokenize(in+1, &tok1, (char **) 0) != 1) { 892 | puts("what?"); 893 | break; 894 | } 895 | ln = list_find(l, tok1, comparef); 896 | if (!ln) 897 | puts("list_find failed"); 898 | else 899 | puts("found"); 900 | break; 901 | case 's': 902 | list_sort(l, comparef); 903 | break; 904 | case 'c': 905 | printf("%lu\n", (unsigned long) list_count(l)); 906 | break; 907 | case 't': 908 | for (ln = list_first(l); ln != 0; ln = list_next(l, ln)) 909 | puts(lnode_get(ln)); 910 | break; 911 | case 'q': 912 | exit(0); 913 | break; 914 | case '\0': 915 | break; 916 | case 'p': 917 | prompt = 1; 918 | break; 919 | default: 920 | putchar('?'); 921 | putchar('\n'); 922 | break; 923 | } 924 | } 925 | 926 | return 0; 927 | } 928 | 929 | #endif /* defined TEST_MAIN */ 930 | -------------------------------------------------------------------------------- /sfx.c: -------------------------------------------------------------------------------- 1 | /* 2 | * SFX---A utility which tries to determine whether a given C expression 3 | * is free of side effects. This can be used for verifying that macros which 4 | * expand their arguments more than once are not being accidentally misused. 5 | * 6 | * Copyright (C) 1999 Kaz Kylheku 7 | * 8 | * Free Software License: 9 | * 10 | * All rights are reserved by the author, with the following exceptions: 11 | * Permission is granted to freely reproduce and distribute this software, 12 | * possibly in exchange for a fee, provided that this copyright notice appears 13 | * intact. Permission is also granted to adapt this software to produce 14 | * derivative works, as long as the modified versions carry this copyright 15 | * notice and additional notices stating that the work has been modified. 16 | * This source code may be translated into executable form and incorporated 17 | * into proprietary software; there is no requirement for such software to 18 | * contain a copyright notice related to this source. 19 | * 20 | * $Id: sfx.c,v 1.30 1999/11/13 08:41:55 kaz Exp $ 21 | * $Name: kazlib_1_20 $ 22 | */ 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include "except.h" 29 | #include "sfx.h" 30 | #include "hash.h" 31 | #ifdef KAZLIB_POSIX_THREADS 32 | #include 33 | #endif 34 | 35 | #ifdef KAZLIB_RCSID 36 | static const char rcsid[] = "$Id: sfx.c,v 1.30 1999/11/13 08:41:55 kaz Exp $"; 37 | #endif 38 | 39 | /* 40 | * Exceptions 41 | */ 42 | 43 | #define SFX_EX 0x34DB9C4A 44 | #define SFX_SYNERR 1 45 | 46 | /* 47 | * Cache entry 48 | */ 49 | 50 | typedef struct { 51 | hnode_t node; 52 | const char *expr; 53 | sfx_rating_t eff; 54 | } sfx_entry_t; 55 | 56 | /* 57 | * Parsing context structure 58 | */ 59 | 60 | typedef struct { 61 | const unsigned char *start; 62 | const unsigned char *input; 63 | size_t size; 64 | sfx_rating_t eff; 65 | } context_t; 66 | 67 | /* 68 | * Declarator type: abstract, concrete or both 69 | */ 70 | 71 | typedef enum { 72 | decl_abstract, decl_concrete, decl_both 73 | } decl_t; 74 | 75 | static void init_context(context_t *ctx, const unsigned char *expr) 76 | { 77 | ctx->input = ctx->start = expr; 78 | ctx->size = strlen((const char *) expr) + 1; 79 | ctx->eff = sfx_none; 80 | } 81 | 82 | static void assign_context(context_t *copy, context_t *orig) 83 | { 84 | *copy = *orig; 85 | } 86 | 87 | static void set_effect(context_t *ctx, sfx_rating_t eff) 88 | { 89 | assert (eff == sfx_none || eff == sfx_potential || eff == sfx_certain); 90 | 91 | if (eff > ctx->eff) 92 | ctx->eff = eff; 93 | } 94 | 95 | static void reset_effect(context_t *ctx) 96 | { 97 | ctx->eff = sfx_none; 98 | } 99 | 100 | static sfx_rating_t get_effect(context_t *ctx) 101 | { 102 | return ctx->eff; 103 | } 104 | 105 | static int skip_ws(context_t *expr) 106 | { 107 | while (*expr->input != 0 && isspace(*expr->input)) 108 | expr->input++; 109 | 110 | return (*expr->input == 0); 111 | } 112 | 113 | static int get_next(context_t *expr) 114 | { 115 | int ret = *expr->input; 116 | if (ret) 117 | expr->input++; 118 | return ret; 119 | } 120 | 121 | static int get_next_skip_ws(context_t *expr) 122 | { 123 | if (!skip_ws(expr)) 124 | return *expr->input++; 125 | return 0; 126 | } 127 | 128 | static const unsigned char *get_ptr(context_t *expr) 129 | { 130 | return expr->input; 131 | } 132 | 133 | static void skip_n(context_t *ctx, size_t n) 134 | { 135 | assert ((size_t) (ctx->input - ctx->start) <= ctx->size - n); 136 | ctx->input += n; 137 | } 138 | 139 | static void put_back(context_t *expr, int ch) 140 | { 141 | if (ch) 142 | expr->input--; 143 | } 144 | 145 | static int peek_next(context_t *expr) 146 | { 147 | return *expr->input; 148 | } 149 | 150 | static void syntax_error(void) 151 | { 152 | except_throw(SFX_EX, SFX_SYNERR, "syntax_error"); 153 | } 154 | 155 | static void match_hard(context_t *expr, int match) 156 | { 157 | int ch = get_next(expr); 158 | if (ch != match) 159 | syntax_error(); 160 | } 161 | 162 | static void chk_comma(context_t *); 163 | 164 | static void skip_ident(context_t *expr) 165 | { 166 | int ch = get_next(expr); 167 | 168 | if (!isalpha(ch) && ch != '_') 169 | syntax_error(); 170 | 171 | do { 172 | ch = get_next(expr); 173 | } while (isalnum(ch) || ch == '_'); 174 | 175 | put_back(expr, ch); 176 | } 177 | 178 | static void skip_constant(context_t *expr) 179 | { 180 | int ch = get_next(expr); 181 | 182 | assert (isdigit(ch) || ch == '.'); 183 | 184 | do { 185 | ch = get_next(expr); 186 | if (ch == 'e' || ch == 'E') { 187 | ch = get_next(expr); 188 | if (ch == '+' || ch == '-') { 189 | ch = get_next(expr); 190 | if (!isdigit(ch)) 191 | syntax_error(); 192 | } 193 | } 194 | } while (ch != 0 && (isalnum(ch) || ch == '.')); 195 | 196 | put_back(expr, ch); 197 | } 198 | 199 | static void skip_strlit(context_t *expr) 200 | { 201 | int ch = get_next(expr); 202 | 203 | assert (ch == '"'); 204 | 205 | do { 206 | ch = get_next(expr); 207 | if (ch == '\\') { 208 | get_next(expr); 209 | continue; 210 | } 211 | } while (ch != 0 && ch != '"'); 212 | 213 | if (ch != '"') 214 | syntax_error(); 215 | } 216 | 217 | static void skip_charlit(context_t *expr) 218 | { 219 | int ch = get_next(expr); 220 | 221 | assert (ch == '\''); 222 | 223 | do { 224 | ch = get_next(expr); 225 | if (ch == '\\') { 226 | get_next(expr); 227 | continue; 228 | } 229 | } while (ch != 0 && ch != '\''); 230 | 231 | if (ch != '\'') 232 | syntax_error(); 233 | } 234 | 235 | static void chk_spec_qual_list(context_t *expr) 236 | { 237 | skip_ws(expr); 238 | skip_ident(expr); 239 | 240 | for (;;) { 241 | int ch; 242 | 243 | skip_ws(expr); 244 | ch = peek_next(expr); 245 | 246 | if (!isalpha(ch) && ch != '_') 247 | break; 248 | 249 | skip_ident(expr); 250 | } 251 | } 252 | 253 | static int speculate(void (*chk_func)(context_t *), context_t *expr, context_t *copy, int nextchar) 254 | { 255 | static const except_id_t catch[] = { { SFX_EX, XCEPT_CODE_ANY } }; 256 | except_t *ex; 257 | volatile int result = 0; 258 | assign_context(copy, expr); 259 | 260 | except_try_push(catch, 1, &ex); 261 | 262 | if (ex == 0) { 263 | chk_func(copy); 264 | if (nextchar) { 265 | skip_ws(copy); 266 | match_hard(copy, nextchar); 267 | } 268 | result = 1; 269 | } 270 | 271 | except_try_pop(); 272 | 273 | return result; 274 | } 275 | 276 | static void chk_pointer_opt(context_t *expr) 277 | { 278 | for (;;) { 279 | int ch = get_next_skip_ws(expr); 280 | 281 | if (ch != '*') { 282 | put_back(expr, ch); 283 | break; 284 | } 285 | 286 | skip_ws(expr); 287 | 288 | ch = peek_next(expr); 289 | 290 | if (ch == '*') 291 | continue; 292 | if (!isalpha(ch) && ch != '_') 293 | break; 294 | 295 | skip_ident(expr); 296 | } 297 | } 298 | 299 | static void chk_decl(context_t *, decl_t); 300 | 301 | static void chk_parm_decl(context_t *expr) 302 | { 303 | chk_spec_qual_list(expr); 304 | chk_decl(expr, decl_both); 305 | } 306 | 307 | static void chk_parm_type_list(context_t *expr) 308 | { 309 | for (;;) { 310 | int ch; 311 | 312 | chk_parm_decl(expr); 313 | 314 | ch = get_next_skip_ws(expr); 315 | 316 | if (ch != ',') { 317 | put_back(expr, ch); 318 | break; 319 | } 320 | 321 | ch = get_next_skip_ws(expr); 322 | 323 | if (ch == '.') { 324 | match_hard(expr, '.'); 325 | match_hard(expr, '.'); 326 | break; 327 | } 328 | 329 | put_back(expr, ch); 330 | } 331 | } 332 | 333 | static void chk_conditional(context_t *); 334 | 335 | static void chk_direct_decl(context_t *expr, decl_t type) 336 | { 337 | for (;;) { 338 | int ch = get_next_skip_ws(expr); 339 | 340 | if (ch == '(') { 341 | skip_ws(expr); 342 | ch = peek_next(expr); 343 | if (ch == '*' || ch == '(' || ch == '[') 344 | chk_decl(expr, type); 345 | else if (isalpha(ch) || ch == '_') 346 | chk_parm_type_list(expr); 347 | match_hard(expr, ')'); 348 | } else if (ch == '[') { 349 | skip_ws(expr); 350 | ch = peek_next(expr); 351 | if (ch != ']') 352 | chk_conditional(expr); 353 | match_hard(expr, ']'); 354 | } else if ((type == decl_concrete || type == decl_both) && (isalpha(ch) || ch == '_')) { 355 | put_back(expr, ch); 356 | skip_ident(expr); 357 | break; 358 | } else { 359 | put_back(expr, ch); 360 | break; 361 | } 362 | } 363 | } 364 | 365 | static void chk_decl(context_t *expr, decl_t type) 366 | { 367 | int ch; 368 | chk_pointer_opt(expr); 369 | skip_ws(expr); 370 | ch = peek_next(expr); 371 | if (ch == '[' || ch == '(' || ((type == decl_concrete || type == decl_both) && (isalpha(ch) || ch == '_'))) { 372 | chk_direct_decl(expr, type); 373 | } 374 | } 375 | 376 | static void chk_typename(context_t *expr) 377 | { 378 | chk_spec_qual_list(expr); 379 | chk_decl(expr, decl_abstract); 380 | } 381 | 382 | static void chk_primary(context_t *expr) 383 | { 384 | int ch = peek_next(expr); 385 | 386 | if (ch == 'L') { 387 | get_next(expr); 388 | ch = peek_next(expr); 389 | 390 | if (ch == '\'') { 391 | skip_charlit(expr); 392 | return; 393 | } 394 | 395 | if (ch == '"') { 396 | skip_strlit(expr); 397 | return; 398 | } 399 | 400 | put_back(expr, 'L'); 401 | ch = 'L'; 402 | } 403 | 404 | if (isalpha(ch) || ch == '_') { 405 | skip_ident(expr); 406 | return; 407 | } 408 | 409 | if (isdigit(ch) || ch == '.') { 410 | skip_constant(expr); 411 | return; 412 | } 413 | 414 | if (ch == '(') { 415 | get_next(expr); 416 | chk_comma(expr); 417 | match_hard(expr, ')'); 418 | return; 419 | } 420 | 421 | if (ch == '\'') { 422 | skip_charlit(expr); 423 | return; 424 | } 425 | 426 | if (ch == '"') { 427 | skip_strlit(expr); 428 | return; 429 | } 430 | 431 | syntax_error(); 432 | } 433 | 434 | static void chk_postfix(context_t *expr) 435 | { 436 | chk_primary(expr); 437 | 438 | for (;;) { 439 | int ch = get_next_skip_ws(expr); 440 | 441 | switch (ch) { 442 | case '[': 443 | chk_comma(expr); 444 | skip_ws(expr); 445 | match_hard(expr, ']'); 446 | continue; 447 | case '(': 448 | set_effect(expr, sfx_potential); 449 | ch = get_next_skip_ws(expr); 450 | 451 | if (ch != ')') { 452 | put_back(expr, ch); 453 | /* clever hack: parse non-empty argument list as comma expression */ 454 | chk_comma(expr); 455 | ch = get_next_skip_ws(expr); 456 | } 457 | 458 | if (ch != ')') 459 | syntax_error(); 460 | 461 | continue; 462 | case '.': 463 | skip_ws(expr); 464 | skip_ident(expr); 465 | continue; 466 | case '-': 467 | ch = get_next(expr); 468 | 469 | if (ch != '-' && ch != '>') { 470 | put_back(expr, ch); 471 | put_back(expr, '-'); 472 | break; 473 | } 474 | 475 | if (ch == '>') { 476 | skip_ws(expr); 477 | skip_ident(expr); 478 | continue; 479 | } 480 | 481 | set_effect(expr, sfx_certain); 482 | continue; 483 | case '+': 484 | ch = get_next(expr); 485 | if (ch != '+') { 486 | put_back(expr, ch); 487 | put_back(expr, '+'); 488 | break; 489 | } 490 | 491 | set_effect(expr, sfx_certain); 492 | continue; 493 | default: 494 | put_back(expr, ch); 495 | break; 496 | } 497 | break; 498 | } 499 | } 500 | 501 | static void chk_cast(context_t *); 502 | 503 | static void chk_unary(context_t *expr) 504 | { 505 | for (;;) { 506 | int nscan, ch = get_next_skip_ws(expr); 507 | 508 | switch (ch) { 509 | case '+': 510 | ch = get_next(expr); 511 | if (ch == '+') 512 | set_effect(expr, sfx_certain); 513 | else 514 | put_back(expr, ch); 515 | chk_cast(expr); 516 | break; 517 | case '-': 518 | ch = get_next(expr); 519 | if (ch == '-') 520 | set_effect(expr, sfx_certain); 521 | else 522 | put_back(expr, ch); 523 | chk_cast(expr); 524 | break; 525 | case '&': case '*': case '~': case '!': 526 | chk_cast(expr); 527 | break; 528 | case 's': 529 | put_back(expr, ch); 530 | nscan = 0; 531 | sscanf((const char *) get_ptr(expr), "sizeof%*1[^a-z0-9_]%n", &nscan); 532 | 533 | if (nscan == 7 || strcmp((const char *) get_ptr(expr), "sizeof") == 0) { 534 | sfx_rating_t eff = get_effect(expr); 535 | 536 | skip_n(expr, 6); 537 | 538 | ch = get_next_skip_ws(expr); 539 | 540 | if (ch == '(') { 541 | context_t comma, type; 542 | int iscomma = speculate(chk_comma, expr, &comma, ')'); 543 | int istype = speculate(chk_typename, expr, &type, ')'); 544 | 545 | if (!iscomma && !istype) 546 | syntax_error(); 547 | 548 | if (iscomma) { 549 | context_t unary; 550 | put_back(expr, ch); 551 | if (speculate(chk_unary, expr, &unary, 0)) { 552 | assign_context(expr, &unary); 553 | istype = 0; 554 | } 555 | } 556 | 557 | if (istype) 558 | assign_context(expr, &type); 559 | } else { 560 | put_back(expr, ch); 561 | chk_unary(expr); 562 | } 563 | 564 | reset_effect(expr); 565 | set_effect(expr, eff); 566 | break; 567 | } 568 | chk_postfix(expr); 569 | break; 570 | default: 571 | put_back(expr, ch); 572 | chk_postfix(expr); 573 | break; 574 | } 575 | 576 | break; 577 | } 578 | } 579 | 580 | static void chk_cast(context_t *expr) 581 | { 582 | enum { 583 | parexpr, /* parenthesized expression */ 584 | partype, /* parenthesized type name */ 585 | parambig, /* ambiguity between paren expr and paren type name */ 586 | unary, /* unary expression */ 587 | plunary, /* unary expression with leading plus or minus */ 588 | other /* none of the above, or even end of input */ 589 | } curr = partype, old = partype, peek = partype; 590 | 591 | /* history for backtracking: two cast expression elements back */ 592 | context_t old_expr = { 0 }, cur_expr = { 0 }; 593 | 594 | for (;;) { 595 | context_t type, comma, unr; 596 | int ch = get_next_skip_ws(expr); 597 | 598 | /* 599 | * Determine what the next bit of input is: parenthesized type name, 600 | * expression, unary expression or what? Speculative parsing is used 601 | * to test several hypotheses. For example, something like 602 | * (X)(Y) ^ 1 is seen, it will be turned, by subsequent iterations of 603 | * this loop, into the codes: parambig, parambig, other. 604 | */ 605 | 606 | if (ch == '(') { 607 | int istype = speculate(chk_typename, expr, &type, ')'); 608 | int iscomma = speculate(chk_comma, expr, &comma, ')'); 609 | 610 | switch (istype << 1 | iscomma) { 611 | case 0: 612 | ch = get_next_skip_ws(expr); 613 | if (ch == ')') 614 | peek = other; /* empty parentheses */ 615 | else 616 | syntax_error(); 617 | break; 618 | case 1: 619 | peek = parexpr; 620 | break; 621 | case 2: 622 | peek = partype; 623 | break; 624 | case 3: 625 | peek = parambig; 626 | break; 627 | } 628 | put_back(expr, ch); 629 | } else if (ch == 0) { 630 | peek = other; 631 | } else { 632 | put_back(expr, ch); 633 | if (speculate(chk_unary, expr, &unr, 0)) { 634 | peek = (ch == '+' || ch == '-' || ch == '*' || ch == '&') ? plunary : unary; 635 | } else { 636 | peek = other; 637 | } 638 | } 639 | 640 | /* 641 | * Okay, now we have an idea what is coming in the input. We make some 642 | * sensible decision based on this and the thing we parsed previously. 643 | * Either the parsing continues to grab more parenthesized things, or 644 | * some decision is made to parse out the suffix material sensibly and 645 | * terminate. Backtracking is used up to two elements back. For 646 | * example in the case of (X)(Y) ^ 1 (parambig, parambig, other) it's 647 | * necessary, upon seeing ^ 1 (other) to go back to second to last 648 | * ambigous parenthesized element (X) and terminate by parsing the 649 | * (X)(Y) as a postfix expression. It cannot be a cast, because ^1 650 | * isn't an expression. Unary expressions that start with + or - 651 | * create an interesting ambiguity. Is (X)(Y) + 1 the addition of 1 to 652 | * the result of the call to function X with parameter Y? Or is it the 653 | * unary expression + 1 cast to type Y and X? The safer assumption is 654 | * to go with the function call hypothesis, since that's the 655 | * interpretation that may have side effects. 656 | */ 657 | 658 | switch (curr) { 659 | case parexpr: /* impossible cases */ 660 | case other: 661 | case unary: 662 | case plunary: 663 | assert (0); 664 | syntax_error(); 665 | /* notreached */ 666 | case partype: 667 | switch (peek) { 668 | case parexpr: /* cast in front of parenthesized expression */ 669 | chk_postfix(expr); 670 | return; 671 | case partype: /* compounding cast: keep looping */ 672 | break; 673 | case parambig: /* type or expr: keep looping */ 674 | break; 675 | case unary: 676 | case plunary: 677 | chk_unary(expr); 678 | return; 679 | case other: /* cast in front of non-expression! */ 680 | syntax_error(); 681 | /* notreached */ 682 | } 683 | break; 684 | case parambig: 685 | switch (peek) { 686 | case parexpr: /* function call */ 687 | assign_context(expr, &cur_expr); 688 | chk_postfix(expr); 689 | return; 690 | case partype: /* compounding cast: keep looping */ 691 | break; 692 | case parambig: /* type or expr: keep looping */ 693 | break; 694 | case unary: 695 | chk_unary(expr); 696 | return; 697 | case plunary: /* treat unary expr with + or - as additive */ 698 | case other: 699 | if (old == parambig) { 700 | /* reparse two expression-like things in a row as call */ 701 | assign_context(expr, &old_expr); 702 | chk_postfix(expr); 703 | return; 704 | } 705 | /* reparse expression followed by non-parenthesized 706 | stuff as postfix expression */ 707 | assign_context(expr, &cur_expr); 708 | chk_postfix(expr); 709 | return; /* need more context */ 710 | } 711 | break; 712 | } 713 | 714 | old = curr; 715 | curr = peek; 716 | assign_context(&old_expr, &cur_expr); 717 | assign_context(&cur_expr, expr); 718 | assign_context(expr, &type); 719 | } 720 | } 721 | 722 | static void chk_multiplicative(context_t *expr) 723 | { 724 | for (;;) { 725 | int ch; 726 | 727 | chk_cast(expr); 728 | ch = get_next_skip_ws(expr); 729 | 730 | if ((ch != '*' && ch != '/' && ch != '%') || peek_next(expr) == '=') { 731 | put_back(expr, ch); 732 | break; 733 | } 734 | } 735 | } 736 | 737 | static void chk_additive(context_t *expr) 738 | { 739 | for (;;) { 740 | int ch; 741 | 742 | chk_multiplicative(expr); 743 | ch = get_next_skip_ws(expr); 744 | 745 | if ((ch != '+' && ch != '-') || peek_next(expr) == '=') { 746 | put_back(expr, ch); 747 | break; 748 | } 749 | } 750 | } 751 | 752 | static void chk_shift(context_t *expr) 753 | { 754 | for (;;) { 755 | int ch; 756 | 757 | chk_additive(expr); 758 | ch = get_next_skip_ws(expr); 759 | 760 | if (ch != '<' && ch != '>') { 761 | put_back(expr, ch); 762 | break; 763 | } 764 | 765 | if (ch == '<' && peek_next(expr) != '<') { 766 | put_back(expr, ch); 767 | break; 768 | } 769 | 770 | if (ch == '>' && peek_next(expr) != '>') { 771 | put_back(expr, ch); 772 | break; 773 | } 774 | 775 | get_next(expr); 776 | 777 | if (peek_next(expr) == '=') { 778 | put_back(expr, ch); 779 | put_back(expr, ch); 780 | break; 781 | } 782 | } 783 | } 784 | 785 | static void chk_relational(context_t *expr) 786 | { 787 | for (;;) { 788 | int ch; 789 | 790 | chk_shift(expr); 791 | ch = get_next_skip_ws(expr); 792 | 793 | 794 | if (ch != '<' && ch != '>') { 795 | put_back(expr, ch); 796 | break; 797 | } 798 | 799 | if (ch == '<' && peek_next(expr) == '<') { 800 | put_back(expr, ch); 801 | break; 802 | } 803 | 804 | if (ch == '>' && peek_next(expr) == '>') { 805 | put_back(expr, ch); 806 | break; 807 | } 808 | 809 | if (peek_next(expr) == '=') 810 | get_next(expr); 811 | } 812 | } 813 | 814 | static void chk_equality(context_t *expr) 815 | { 816 | for (;;) { 817 | int ch; 818 | 819 | chk_relational(expr); 820 | ch = get_next_skip_ws(expr); 821 | 822 | if ((ch != '!' && ch != '=') || peek_next(expr) != '=') { 823 | put_back(expr, ch); 824 | break; 825 | } 826 | 827 | match_hard(expr, '='); 828 | } 829 | } 830 | 831 | static void chk_and(context_t *expr) 832 | { 833 | for (;;) { 834 | int ch; 835 | 836 | chk_equality(expr); 837 | ch = get_next_skip_ws(expr); 838 | 839 | if (ch != '&' || peek_next(expr) == '&' || peek_next(expr) == '=') { 840 | put_back(expr, ch); 841 | break; 842 | } 843 | } 844 | } 845 | 846 | static void chk_exclusive_or(context_t *expr) 847 | { 848 | for (;;) { 849 | int ch; 850 | 851 | chk_and(expr); 852 | ch = get_next_skip_ws(expr); 853 | 854 | if (ch != '^' || peek_next(expr) == '=') { 855 | put_back(expr, ch); 856 | break; 857 | } 858 | } 859 | } 860 | 861 | static void chk_inclusive_or(context_t *expr) 862 | { 863 | for (;;) { 864 | int ch; 865 | 866 | chk_exclusive_or(expr); 867 | ch = get_next_skip_ws(expr); 868 | 869 | if (ch != '|' || peek_next(expr) == '|' || peek_next(expr) == '=') { 870 | put_back(expr, ch); 871 | break; 872 | } 873 | } 874 | } 875 | 876 | static void chk_logical_and(context_t *expr) 877 | { 878 | for (;;) { 879 | int ch; 880 | 881 | chk_inclusive_or(expr); 882 | ch = get_next_skip_ws(expr); 883 | 884 | if (ch != '&' || peek_next(expr) != '&') { 885 | put_back(expr, ch); 886 | break; 887 | } 888 | 889 | match_hard(expr, '&'); 890 | } 891 | } 892 | 893 | static void chk_logical_or(context_t *expr) 894 | { 895 | for (;;) { 896 | int ch; 897 | 898 | chk_logical_and(expr); 899 | ch = get_next_skip_ws(expr); 900 | 901 | if (ch != '|' || peek_next(expr) != '|') { 902 | put_back(expr, ch); 903 | break; 904 | } 905 | 906 | match_hard(expr, '|'); 907 | } 908 | } 909 | 910 | static void chk_conditional(context_t *expr) 911 | { 912 | for (;;) { 913 | int ch; 914 | 915 | chk_logical_or(expr); 916 | ch = get_next_skip_ws(expr); 917 | 918 | if (ch != '?') { 919 | put_back(expr, ch); 920 | break; 921 | } 922 | 923 | chk_comma(expr); 924 | 925 | skip_ws(expr); 926 | match_hard(expr, ':'); 927 | } 928 | } 929 | 930 | static void chk_assignment(context_t *expr) 931 | { 932 | for (;;) { 933 | int ch; 934 | 935 | chk_conditional(expr); 936 | ch = get_next_skip_ws(expr); 937 | 938 | switch (ch) { 939 | case '=': 940 | break; 941 | case '*': case '/': case '%': 942 | case '+': case '-': case '&': 943 | case '^': case '|': 944 | match_hard(expr, '='); 945 | break; 946 | case '<': 947 | match_hard(expr, '<'); 948 | match_hard(expr, '='); 949 | break; 950 | case '>': 951 | match_hard(expr, '>'); 952 | match_hard(expr, '='); 953 | break; 954 | case 0: 955 | default: 956 | put_back(expr, ch); 957 | return; 958 | } 959 | set_effect(expr, sfx_certain); 960 | } 961 | } 962 | 963 | static void chk_comma(context_t *expr) 964 | { 965 | for (;;) { 966 | int ch; 967 | 968 | chk_assignment(expr); 969 | ch = get_next_skip_ws(expr); 970 | 971 | if (ch != ',') { 972 | put_back(expr, ch); 973 | break; 974 | } 975 | } 976 | } 977 | 978 | /* 979 | * This function returns 1 if the expression is successfully parsed, 980 | * or 0 if there is a syntax error. 981 | * 982 | * The object pointed to by eff is set to indicate the side effect ranking of 983 | * the parsed expression: sfx_none, sfx_potential and sfx_certain. These 984 | * rankins mean, respectively, that there are no side effects, that there are 985 | * potential side effects, or that there certainly are side effects. 986 | */ 987 | 988 | int sfx_determine(const char *expr, sfx_rating_t *eff) 989 | { 990 | static const except_id_t catch[] = { { SFX_EX, XCEPT_CODE_ANY } }; 991 | except_t *ex; 992 | context_t ctx; 993 | volatile int retval = 1; 994 | 995 | if (!except_init()) 996 | return 0; 997 | 998 | init_context(&ctx, (const unsigned char *) expr); 999 | 1000 | except_try_push(catch, 1, &ex); 1001 | 1002 | if (ex == 0) { 1003 | chk_comma(&ctx); 1004 | skip_ws(&ctx); 1005 | if (peek_next(&ctx) != 0) 1006 | syntax_error(); 1007 | } else { 1008 | /* exception caught */ 1009 | retval = 0; 1010 | } 1011 | 1012 | except_try_pop(); 1013 | 1014 | *eff = ctx.eff; 1015 | 1016 | except_deinit(); 1017 | 1018 | return retval; 1019 | } 1020 | 1021 | 1022 | #ifdef KAZLIB_POSIX_THREADS 1023 | 1024 | static pthread_once_t cache_init; 1025 | static pthread_mutex_t cache_mutex = PTHREAD_MUTEX_INITIALIZER; 1026 | 1027 | #define init_once(X, Y) pthread_once(X, Y) 1028 | #define lock_cache() pthread_mutex_lock(&cache_mutex) 1029 | #define unlock_cache() pthread_mutex_unlock(&cache_mutex) 1030 | 1031 | #else 1032 | static int cache_init; 1033 | 1034 | static void init_once(int *once, void (*func)(void)) 1035 | { 1036 | if (*once == 0) { 1037 | func(); 1038 | *once = 1; 1039 | } 1040 | } 1041 | 1042 | #define lock_cache() 1043 | #define unlock_cache() 1044 | #endif 1045 | 1046 | static hash_t *cache; 1047 | 1048 | extern hash_t *hash_create(hashcount_t, hash_comp_t, hash_fun_t); 1049 | 1050 | static void init_cache(void) 1051 | { 1052 | cache = hash_create(HASHCOUNT_T_MAX, 0, 0); 1053 | } 1054 | 1055 | static int lookup_cache(const char *expr, sfx_rating_t *rating) 1056 | { 1057 | hnode_t *cache_node; 1058 | init_once(&cache_init, init_cache); 1059 | 1060 | lock_cache(); 1061 | 1062 | cache_node = hash_lookup(cache, expr); 1063 | 1064 | unlock_cache(); 1065 | 1066 | if (cache_node != 0) { 1067 | sfx_entry_t *cache_entry = hnode_get(cache_node); 1068 | *rating = cache_entry->eff; 1069 | return 1; 1070 | } 1071 | 1072 | return 0; 1073 | } 1074 | 1075 | static int cache_result(const char *expr, sfx_rating_t rating) 1076 | { 1077 | int result = 0; 1078 | hnode_t *cache_node; 1079 | 1080 | init_once(&cache_init, init_cache); 1081 | 1082 | if (cache == 0) 1083 | goto bail; 1084 | 1085 | lock_cache(); 1086 | 1087 | cache_node = hash_lookup(cache, expr); 1088 | 1089 | if (!cache_node) { 1090 | sfx_entry_t *cache_entry = malloc(sizeof *cache_entry); 1091 | 1092 | if (cache_entry == 0) 1093 | goto bail_unlock; 1094 | 1095 | hnode_init(&cache_entry->node, cache_entry); 1096 | cache_entry->expr = expr; 1097 | cache_entry->eff = rating; 1098 | hash_insert(cache, &cache_entry->node, expr); 1099 | } else { 1100 | sfx_entry_t *cache_entry = hnode_get(cache_node); 1101 | cache_entry->eff = rating; 1102 | result = 1; 1103 | } 1104 | 1105 | result = 1; 1106 | 1107 | 1108 | bail_unlock: 1109 | unlock_cache(); 1110 | 1111 | bail: 1112 | return result; 1113 | } 1114 | 1115 | 1116 | void sfx_check(const char *expr, const char *file, unsigned long line) 1117 | { 1118 | sfx_rating_t eff; 1119 | int success = lookup_cache(expr, &eff); 1120 | 1121 | if (!success) { 1122 | success = sfx_determine(expr, &eff); 1123 | cache_result(expr, eff); 1124 | } 1125 | 1126 | if (!success) { 1127 | fprintf(stderr, "%s:%ld: syntax error in expression \"%s\"\n", 1128 | file, line, expr); 1129 | } else if (eff == sfx_potential) { 1130 | fprintf(stderr, "%s:%ld: expression \"%s\" may have side effects\n", 1131 | file, line, expr); 1132 | } else if (eff == sfx_certain) { 1133 | fprintf(stderr, "%s:%ld: expression \"%s\" has side effects\n", 1134 | file, line, expr); 1135 | } else { 1136 | return; 1137 | } 1138 | } 1139 | 1140 | int sfx_declare(const char *expr, sfx_rating_t eff) 1141 | { 1142 | return cache_result(expr, eff); 1143 | } 1144 | 1145 | #ifdef KAZLIB_TEST_MAIN 1146 | 1147 | #include 1148 | 1149 | int main(int argc, char **argv) 1150 | { 1151 | char expr_buf[256]; 1152 | char *expr, *ptr; 1153 | sfx_rating_t eff; 1154 | 1155 | for (;;) { 1156 | if (argc < 2) { 1157 | expr = expr_buf; 1158 | if (fgets(expr_buf, sizeof expr_buf, stdin) == 0) 1159 | break; 1160 | if ((ptr = strchr(expr_buf, '\n')) != 0) 1161 | *ptr = 0; 1162 | } else { 1163 | expr = (argv++)[1]; 1164 | if (!expr) 1165 | break; 1166 | } 1167 | 1168 | if (!sfx_determine(expr, &eff)) { 1169 | printf("expression '%s' has a syntax error\n", expr); 1170 | return EXIT_FAILURE; 1171 | } 1172 | 1173 | switch (eff) { 1174 | case sfx_none: 1175 | printf("expression '%s' has no side effects\n", expr); 1176 | break; 1177 | case sfx_potential: 1178 | printf("expression '%s' may have side effects\n", expr); 1179 | break; 1180 | case sfx_certain: 1181 | printf("expression '%s' has side effects\n", expr); 1182 | break; 1183 | } 1184 | } 1185 | 1186 | return 0; 1187 | } 1188 | 1189 | #endif 1190 | -------------------------------------------------------------------------------- /hash.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Hash Table Data Type 3 | * Copyright (C) 1997 Kaz Kylheku 4 | * 5 | * Free Software License: 6 | * 7 | * All rights are reserved by the author, with the following exceptions: 8 | * Permission is granted to freely reproduce and distribute this software, 9 | * possibly in exchange for a fee, provided that this copyright notice appears 10 | * intact. Permission is also granted to adapt this software to produce 11 | * derivative works, as long as the modified versions carry this copyright 12 | * notice and additional notices stating that the work has been modified. 13 | * This source code may be translated into executable form and incorporated 14 | * into proprietary software; there is no requirement for such software to 15 | * contain a copyright notice related to this source. 16 | * 17 | * $Id: hash.c,v 1.36.2.11 2000/11/13 01:36:45 kaz Exp $ 18 | * $Name: kazlib_1_20 $ 19 | */ 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #define HASH_IMPLEMENTATION 26 | #include "hash.h" 27 | 28 | #ifdef KAZLIB_RCSID 29 | static const char rcsid[] = "$Id: hash.c,v 1.36.2.11 2000/11/13 01:36:45 kaz Exp $"; 30 | #endif 31 | 32 | #define INIT_BITS 6 33 | #define INIT_SIZE (1UL << (INIT_BITS)) /* must be power of two */ 34 | #define INIT_MASK ((INIT_SIZE) - 1) 35 | 36 | #define next hash_next 37 | #define key hash_key 38 | #define data hash_data 39 | #define hkey hash_hkey 40 | 41 | #define table hash_table 42 | #define nchains hash_nchains 43 | #define nodecount hash_nodecount 44 | #define maxcount hash_maxcount 45 | #define highmark hash_highmark 46 | #define lowmark hash_lowmark 47 | #define compare hash_compare 48 | #define function hash_function 49 | #define allocnode hash_allocnode 50 | #define freenode hash_freenode 51 | #define context hash_context 52 | #define mask hash_mask 53 | #define dynamic hash_dynamic 54 | 55 | #define table hash_table 56 | #define chain hash_chain 57 | 58 | static hnode_t *hnode_alloc(void *context); 59 | static void hnode_free(hnode_t *node, void *context); 60 | static hash_val_t hash_fun_default(const void *key); 61 | static int hash_comp_default(const void *key1, const void *key2); 62 | 63 | int hash_val_t_bit; 64 | 65 | /* 66 | * Compute the number of bits in the hash_val_t type. We know that hash_val_t 67 | * is an unsigned integral type. Thus the highest value it can hold is a 68 | * Mersenne number (power of two, less one). We initialize a hash_val_t 69 | * object with this value and then shift bits out one by one while counting. 70 | * Notes: 71 | * 1. HASH_VAL_T_MAX is a Mersenne number---one that is one less than a power 72 | * of two. This means that its binary representation consists of all one 73 | * bits, and hence ``val'' is initialized to all one bits. 74 | * 2. While bits remain in val, we increment the bit count and shift it to the 75 | * right, replacing the topmost bit by zero. 76 | */ 77 | 78 | static void compute_bits(void) 79 | { 80 | hash_val_t val = HASH_VAL_T_MAX; /* 1 */ 81 | int bits = 0; 82 | 83 | while (val) { /* 2 */ 84 | bits++; 85 | val >>= 1; 86 | } 87 | 88 | hash_val_t_bit = bits; 89 | } 90 | 91 | /* 92 | * Verify whether the given argument is a power of two. 93 | */ 94 | 95 | static int is_power_of_two(hash_val_t arg) 96 | { 97 | if (arg == 0) 98 | return 0; 99 | while ((arg & 1) == 0) 100 | arg >>= 1; 101 | return (arg == 1); 102 | } 103 | 104 | /* 105 | * Compute a shift amount from a given table size 106 | */ 107 | 108 | static hash_val_t compute_mask(hashcount_t size) 109 | { 110 | assert (is_power_of_two(size)); 111 | assert (size >= 2); 112 | 113 | return size - 1; 114 | } 115 | 116 | /* 117 | * Initialize the table of pointers to null. 118 | */ 119 | 120 | static void clear_table(hash_t *hash) 121 | { 122 | hash_val_t i; 123 | 124 | for (i = 0; i < hash->nchains; i++) 125 | hash->table[i] = NULL; 126 | } 127 | 128 | /* 129 | * Double the size of a dynamic table. This works as follows. Each chain splits 130 | * into two adjacent chains. The shift amount increases by one, exposing an 131 | * additional bit of each hashed key. For each node in the original chain, the 132 | * value of this newly exposed bit will decide which of the two new chains will 133 | * receive the node: if the bit is 1, the chain with the higher index will have 134 | * the node, otherwise the lower chain will receive the node. In this manner, 135 | * the hash table will continue to function exactly as before without having to 136 | * rehash any of the keys. 137 | * Notes: 138 | * 1. Overflow check. 139 | * 2. The new number of chains is twice the old number of chains. 140 | * 3. The new mask is one bit wider than the previous, revealing a 141 | * new bit in all hashed keys. 142 | * 4. Allocate a new table of chain pointers that is twice as large as the 143 | * previous one. 144 | * 5. If the reallocation was successful, we perform the rest of the growth 145 | * algorithm, otherwise we do nothing. 146 | * 6. The exposed_bit variable holds a mask with which each hashed key can be 147 | * AND-ed to test the value of its newly exposed bit. 148 | * 7. Now loop over each chain in the table and sort its nodes into two 149 | * chains based on the value of each node's newly exposed hash bit. 150 | * 8. The low chain replaces the current chain. The high chain goes 151 | * into the corresponding sister chain in the upper half of the table. 152 | * 9. We have finished dealing with the chains and nodes. We now update 153 | * the various bookeeping fields of the hash structure. 154 | */ 155 | 156 | static void grow_table(hash_t *hash) 157 | { 158 | hnode_t **newtable; 159 | 160 | assert (2 * hash->nchains > hash->nchains); /* 1 */ 161 | 162 | newtable = realloc(hash->table, 163 | sizeof *newtable * hash->nchains * 2); /* 4 */ 164 | 165 | if (newtable) { /* 5 */ 166 | hash_val_t mask = (hash->mask << 1) | 1; /* 3 */ 167 | hash_val_t exposed_bit = mask ^ hash->mask; /* 6 */ 168 | hash_val_t chain; 169 | 170 | assert (mask != hash->mask); 171 | 172 | for (chain = 0; chain < hash->nchains; chain++) { /* 7 */ 173 | hnode_t *low_chain = 0, *high_chain = 0, *hptr, *next; 174 | 175 | for (hptr = newtable[chain]; hptr != 0; hptr = next) { 176 | next = hptr->next; 177 | 178 | if (hptr->hkey & exposed_bit) { 179 | hptr->next = high_chain; 180 | high_chain = hptr; 181 | } else { 182 | hptr->next = low_chain; 183 | low_chain = hptr; 184 | } 185 | } 186 | 187 | newtable[chain] = low_chain; /* 8 */ 188 | newtable[chain + hash->nchains] = high_chain; 189 | } 190 | 191 | hash->table = newtable; /* 9 */ 192 | hash->mask = mask; 193 | hash->nchains *= 2; 194 | hash->lowmark *= 2; 195 | hash->highmark *= 2; 196 | } 197 | assert (hash_verify(hash)); 198 | } 199 | 200 | /* 201 | * Cut a table size in half. This is done by folding together adjacent chains 202 | * and populating the lower half of the table with these chains. The chains are 203 | * simply spliced together. Once this is done, the whole table is reallocated 204 | * to a smaller object. 205 | * Notes: 206 | * 1. It is illegal to have a hash table with one slot. This would mean that 207 | * hash->shift is equal to hash_val_t_bit, an illegal shift value. 208 | * Also, other things could go wrong, such as hash->lowmark becoming zero. 209 | * 2. Looping over each pair of sister chains, the low_chain is set to 210 | * point to the head node of the chain in the lower half of the table, 211 | * and high_chain points to the head node of the sister in the upper half. 212 | * 3. The intent here is to compute a pointer to the last node of the 213 | * lower chain into the low_tail variable. If this chain is empty, 214 | * low_tail ends up with a null value. 215 | * 4. If the lower chain is not empty, we simply tack the upper chain onto it. 216 | * If the upper chain is a null pointer, nothing happens. 217 | * 5. Otherwise if the lower chain is empty but the upper one is not, 218 | * If the low chain is empty, but the high chain is not, then the 219 | * high chain is simply transferred to the lower half of the table. 220 | * 6. Otherwise if both chains are empty, there is nothing to do. 221 | * 7. All the chain pointers are in the lower half of the table now, so 222 | * we reallocate it to a smaller object. This, of course, invalidates 223 | * all pointer-to-pointers which reference into the table from the 224 | * first node of each chain. 225 | * 8. Though it's unlikely, the reallocation may fail. In this case we 226 | * pretend that the table _was_ reallocated to a smaller object. 227 | * 9. Finally, update the various table parameters to reflect the new size. 228 | */ 229 | 230 | static void shrink_table(hash_t *hash) 231 | { 232 | hash_val_t chain, nchains; 233 | hnode_t **newtable, *low_tail, *low_chain, *high_chain; 234 | 235 | assert (hash->nchains >= 2); /* 1 */ 236 | nchains = hash->nchains / 2; 237 | 238 | for (chain = 0; chain < nchains; chain++) { 239 | low_chain = hash->table[chain]; /* 2 */ 240 | high_chain = hash->table[chain + nchains]; 241 | for (low_tail = low_chain; low_tail && low_tail->next; low_tail = low_tail->next) 242 | ; /* 3 */ 243 | if (low_chain != 0) /* 4 */ 244 | low_tail->next = high_chain; 245 | else if (high_chain != 0) /* 5 */ 246 | hash->table[chain] = high_chain; 247 | else 248 | assert (hash->table[chain] == NULL); /* 6 */ 249 | } 250 | newtable = realloc(hash->table, 251 | sizeof *newtable * nchains); /* 7 */ 252 | if (newtable) /* 8 */ 253 | hash->table = newtable; 254 | hash->mask >>= 1; /* 9 */ 255 | hash->nchains = nchains; 256 | hash->lowmark /= 2; 257 | hash->highmark /= 2; 258 | assert (hash_verify(hash)); 259 | } 260 | 261 | 262 | /* 263 | * Create a dynamic hash table. Both the hash table structure and the table 264 | * itself are dynamically allocated. Furthermore, the table is extendible in 265 | * that it will automatically grow as its load factor increases beyond a 266 | * certain threshold. 267 | * Notes: 268 | * 1. If the number of bits in the hash_val_t type has not been computed yet, 269 | * we do so here, because this is likely to be the first function that the 270 | * user calls. 271 | * 2. Allocate a hash table control structure. 272 | * 3. If a hash table control structure is successfully allocated, we 273 | * proceed to initialize it. Otherwise we return a null pointer. 274 | * 4. We try to allocate the table of hash chains. 275 | * 5. If we were able to allocate the hash chain table, we can finish 276 | * initializing the hash structure and the table. Otherwise, we must 277 | * backtrack by freeing the hash structure. 278 | * 6. INIT_SIZE should be a power of two. The high and low marks are always set 279 | * to be twice the table size and half the table size respectively. When the 280 | * number of nodes in the table grows beyond the high size (beyond load 281 | * factor 2), it will double in size to cut the load factor down to about 282 | * about 1. If the table shrinks down to or beneath load factor 0.5, 283 | * it will shrink, bringing the load up to about 1. However, the table 284 | * will never shrink beneath INIT_SIZE even if it's emptied. 285 | * 7. This indicates that the table is dynamically allocated and dynamically 286 | * resized on the fly. A table that has this value set to zero is 287 | * assumed to be statically allocated and will not be resized. 288 | * 8. The table of chains must be properly reset to all null pointers. 289 | */ 290 | 291 | hash_t *hash_create(hashcount_t maxcount, hash_comp_t compfun, 292 | hash_fun_t hashfun) 293 | { 294 | hash_t *hash; 295 | 296 | if (hash_val_t_bit == 0) /* 1 */ 297 | compute_bits(); 298 | 299 | hash = malloc(sizeof *hash); /* 2 */ 300 | 301 | if (hash) { /* 3 */ 302 | hash->table = malloc(sizeof *hash->table * INIT_SIZE); /* 4 */ 303 | if (hash->table) { /* 5 */ 304 | hash->nchains = INIT_SIZE; /* 6 */ 305 | hash->highmark = INIT_SIZE * 2; 306 | hash->lowmark = INIT_SIZE / 2; 307 | hash->nodecount = 0; 308 | hash->maxcount = maxcount; 309 | hash->compare = compfun ? compfun : hash_comp_default; 310 | hash->function = hashfun ? hashfun : hash_fun_default; 311 | hash->allocnode = hnode_alloc; 312 | hash->freenode = hnode_free; 313 | hash->context = NULL; 314 | hash->mask = INIT_MASK; 315 | hash->dynamic = 1; /* 7 */ 316 | clear_table(hash); /* 8 */ 317 | assert (hash_verify(hash)); 318 | return hash; 319 | } 320 | free(hash); 321 | } 322 | 323 | return NULL; 324 | } 325 | 326 | /* 327 | * Select a different set of node allocator routines. 328 | */ 329 | 330 | void hash_set_allocator(hash_t *hash, hnode_alloc_t al, 331 | hnode_free_t fr, void *context) 332 | { 333 | assert (hash_count(hash) == 0); 334 | assert ((al == 0 && fr == 0) || (al != 0 && fr != 0)); 335 | 336 | hash->allocnode = al ? al : hnode_alloc; 337 | hash->freenode = fr ? fr : hnode_free; 338 | hash->context = context; 339 | } 340 | 341 | /* 342 | * Free every node in the hash using the hash->freenode() function pointer, and 343 | * cause the hash to become empty. 344 | */ 345 | 346 | void hash_free_nodes(hash_t *hash) 347 | { 348 | hscan_t hs; 349 | hnode_t *node; 350 | hash_scan_begin(&hs, hash); 351 | while ((node = hash_scan_next(&hs))) { 352 | hash_scan_delete(hash, node); 353 | hash->freenode(node, hash->context); 354 | } 355 | hash->nodecount = 0; 356 | clear_table(hash); 357 | } 358 | 359 | /* 360 | * Obsolescent function for removing all nodes from a table, 361 | * freeing them and then freeing the table all in one step. 362 | */ 363 | 364 | void hash_free(hash_t *hash) 365 | { 366 | #ifdef KAZLIB_OBSOLESCENT_DEBUG 367 | assert ("call to obsolescent function hash_free()" && 0); 368 | #endif 369 | hash_free_nodes(hash); 370 | hash_destroy(hash); 371 | } 372 | 373 | /* 374 | * Free a dynamic hash table structure. 375 | */ 376 | 377 | void hash_destroy(hash_t *hash) 378 | { 379 | assert (hash_val_t_bit != 0); 380 | assert (hash_isempty(hash)); 381 | free(hash->table); 382 | free(hash); 383 | } 384 | 385 | /* 386 | * Initialize a user supplied hash structure. The user also supplies a table of 387 | * chains which is assigned to the hash structure. The table is static---it 388 | * will not grow or shrink. 389 | * 1. See note 1. in hash_create(). 390 | * 2. The user supplied array of pointers hopefully contains nchains nodes. 391 | * 3. See note 7. in hash_create(). 392 | * 4. We must dynamically compute the mask from the given power of two table 393 | * size. 394 | * 5. The user supplied table can't be assumed to contain null pointers, 395 | * so we reset it here. 396 | */ 397 | 398 | hash_t *hash_init(hash_t *hash, hashcount_t maxcount, 399 | hash_comp_t compfun, hash_fun_t hashfun, hnode_t **table, 400 | hashcount_t nchains) 401 | { 402 | if (hash_val_t_bit == 0) /* 1 */ 403 | compute_bits(); 404 | 405 | assert (is_power_of_two(nchains)); 406 | 407 | hash->table = table; /* 2 */ 408 | hash->nchains = nchains; 409 | hash->nodecount = 0; 410 | hash->maxcount = maxcount; 411 | hash->compare = compfun ? compfun : hash_comp_default; 412 | hash->function = hashfun ? hashfun : hash_fun_default; 413 | hash->dynamic = 0; /* 3 */ 414 | hash->mask = compute_mask(nchains); /* 4 */ 415 | clear_table(hash); /* 5 */ 416 | 417 | assert (hash_verify(hash)); 418 | 419 | return hash; 420 | } 421 | 422 | /* 423 | * Reset the hash scanner so that the next element retrieved by 424 | * hash_scan_next() shall be the first element on the first non-empty chain. 425 | * Notes: 426 | * 1. Locate the first non empty chain. 427 | * 2. If an empty chain is found, remember which one it is and set the next 428 | * pointer to refer to its first element. 429 | * 3. Otherwise if a chain is not found, set the next pointer to NULL 430 | * so that hash_scan_next() shall indicate failure. 431 | */ 432 | 433 | void hash_scan_begin(hscan_t *scan, hash_t *hash) 434 | { 435 | hash_val_t nchains = hash->nchains; 436 | hash_val_t chain; 437 | 438 | scan->table = hash; 439 | 440 | /* 1 */ 441 | 442 | for (chain = 0; chain < nchains && hash->table[chain] == 0; chain++) 443 | ; 444 | 445 | if (chain < nchains) { /* 2 */ 446 | scan->chain = chain; 447 | scan->next = hash->table[chain]; 448 | } else { /* 3 */ 449 | scan->next = NULL; 450 | } 451 | } 452 | 453 | /* 454 | * Retrieve the next node from the hash table, and update the pointer 455 | * for the next invocation of hash_scan_next(). 456 | * Notes: 457 | * 1. Remember the next pointer in a temporary value so that it can be 458 | * returned. 459 | * 2. This assertion essentially checks whether the module has been properly 460 | * initialized. The first point of interaction with the module should be 461 | * either hash_create() or hash_init(), both of which set hash_val_t_bit to 462 | * a non zero value. 463 | * 3. If the next pointer we are returning is not NULL, then the user is 464 | * allowed to call hash_scan_next() again. We prepare the new next pointer 465 | * for that call right now. That way the user is allowed to delete the node 466 | * we are about to return, since we will no longer be needing it to locate 467 | * the next node. 468 | * 4. If there is a next node in the chain (next->next), then that becomes the 469 | * new next node, otherwise ... 470 | * 5. We have exhausted the current chain, and must locate the next subsequent 471 | * non-empty chain in the table. 472 | * 6. If a non-empty chain is found, the first element of that chain becomes 473 | * the new next node. Otherwise there is no new next node and we set the 474 | * pointer to NULL so that the next time hash_scan_next() is called, a null 475 | * pointer shall be immediately returned. 476 | */ 477 | 478 | 479 | hnode_t *hash_scan_next(hscan_t *scan) 480 | { 481 | hnode_t *next = scan->next; /* 1 */ 482 | hash_t *hash = scan->table; 483 | hash_val_t chain = scan->chain + 1; 484 | hash_val_t nchains = hash->nchains; 485 | 486 | assert (hash_val_t_bit != 0); /* 2 */ 487 | 488 | if (next) { /* 3 */ 489 | if (next->next) { /* 4 */ 490 | scan->next = next->next; 491 | } else { 492 | while (chain < nchains && hash->table[chain] == 0) /* 5 */ 493 | chain++; 494 | if (chain < nchains) { /* 6 */ 495 | scan->chain = chain; 496 | scan->next = hash->table[chain]; 497 | } else { 498 | scan->next = NULL; 499 | } 500 | } 501 | } 502 | return next; 503 | } 504 | 505 | /* 506 | * Insert a node into the hash table. 507 | * Notes: 508 | * 1. It's illegal to insert more than the maximum number of nodes. The client 509 | * should verify that the hash table is not full before attempting an 510 | * insertion. 511 | * 2. The same key may not be inserted into a table twice. 512 | * 3. If the table is dynamic and the load factor is already at >= 2, 513 | * grow the table. 514 | * 4. We take the bottom N bits of the hash value to derive the chain index, 515 | * where N is the base 2 logarithm of the size of the hash table. 516 | */ 517 | 518 | void hash_insert(hash_t *hash, hnode_t *node, const void *key) 519 | { 520 | hash_val_t hkey, chain; 521 | 522 | assert (hash_val_t_bit != 0); 523 | assert (node->next == NULL); 524 | assert (hash->nodecount < hash->maxcount); /* 1 */ 525 | assert (hash_lookup(hash, key) == NULL); /* 2 */ 526 | 527 | if (hash->dynamic && hash->nodecount >= hash->highmark) /* 3 */ 528 | grow_table(hash); 529 | 530 | hkey = hash->function(key); 531 | chain = hkey & hash->mask; /* 4 */ 532 | 533 | node->key = key; 534 | node->hkey = hkey; 535 | node->next = hash->table[chain]; 536 | hash->table[chain] = node; 537 | hash->nodecount++; 538 | 539 | assert (hash_verify(hash)); 540 | } 541 | 542 | /* 543 | * Find a node in the hash table and return a pointer to it. 544 | * Notes: 545 | * 1. We hash the key and keep the entire hash value. As an optimization, when 546 | * we descend down the chain, we can compare hash values first and only if 547 | * hash values match do we perform a full key comparison. 548 | * 2. To locate the chain from among 2^N chains, we look at the lower N bits of 549 | * the hash value by anding them with the current mask. 550 | * 3. Looping through the chain, we compare the stored hash value inside each 551 | * node against our computed hash. If they match, then we do a full 552 | * comparison between the unhashed keys. If these match, we have located the 553 | * entry. 554 | */ 555 | 556 | hnode_t *hash_lookup(hash_t *hash, const void *key) 557 | { 558 | hash_val_t hkey, chain; 559 | hnode_t *nptr; 560 | 561 | hkey = hash->function(key); /* 1 */ 562 | chain = hkey & hash->mask; /* 2 */ 563 | 564 | for (nptr = hash->table[chain]; nptr; nptr = nptr->next) { /* 3 */ 565 | if (nptr->hkey == hkey && hash->compare(nptr->key, key) == 0) 566 | return nptr; 567 | } 568 | 569 | return NULL; 570 | } 571 | 572 | /* 573 | * Delete the given node from the hash table. Since the chains 574 | * are singly linked, we must locate the start of the node's chain 575 | * and traverse. 576 | * Notes: 577 | * 1. The node must belong to this hash table, and its key must not have 578 | * been tampered with. 579 | * 2. If this deletion will take the node count below the low mark, we 580 | * shrink the table now. 581 | * 3. Determine which chain the node belongs to, and fetch the pointer 582 | * to the first node in this chain. 583 | * 4. If the node being deleted is the first node in the chain, then 584 | * simply update the chain head pointer. 585 | * 5. Otherwise advance to the node's predecessor, and splice out 586 | * by updating the predecessor's next pointer. 587 | * 6. Indicate that the node is no longer in a hash table. 588 | */ 589 | 590 | hnode_t *hash_delete(hash_t *hash, hnode_t *node) 591 | { 592 | hash_val_t chain; 593 | hnode_t *hptr; 594 | 595 | assert (hash_lookup(hash, node->key) == node); /* 1 */ 596 | assert (hash_val_t_bit != 0); 597 | 598 | if (hash->dynamic && hash->nodecount <= hash->lowmark 599 | && hash->nodecount > INIT_SIZE) 600 | shrink_table(hash); /* 2 */ 601 | 602 | chain = node->hkey & hash->mask; /* 3 */ 603 | hptr = hash->table[chain]; 604 | 605 | if (hptr == node) { /* 4 */ 606 | hash->table[chain] = node->next; 607 | } else { 608 | while (hptr->next != node) { /* 5 */ 609 | assert (hptr != 0); 610 | hptr = hptr->next; 611 | } 612 | assert (hptr->next == node); 613 | hptr->next = node->next; 614 | } 615 | 616 | hash->nodecount--; 617 | assert (hash_verify(hash)); 618 | 619 | node->next = NULL; /* 6 */ 620 | return node; 621 | } 622 | 623 | int hash_alloc_insert(hash_t *hash, const void *key, void *data) 624 | { 625 | hnode_t *node = hash->allocnode(hash->context); 626 | 627 | if (node) { 628 | hnode_init(node, data); 629 | hash_insert(hash, node, key); 630 | return 1; 631 | } 632 | return 0; 633 | } 634 | 635 | void hash_delete_free(hash_t *hash, hnode_t *node) 636 | { 637 | hash_delete(hash, node); 638 | hash->freenode(node, hash->context); 639 | } 640 | 641 | /* 642 | * Exactly like hash_delete, except does not trigger table shrinkage. This is to be 643 | * used from within a hash table scan operation. See notes for hash_delete. 644 | */ 645 | 646 | hnode_t *hash_scan_delete(hash_t *hash, hnode_t *node) 647 | { 648 | hash_val_t chain; 649 | hnode_t *hptr; 650 | 651 | assert (hash_lookup(hash, node->key) == node); 652 | assert (hash_val_t_bit != 0); 653 | 654 | chain = node->hkey & hash->mask; 655 | hptr = hash->table[chain]; 656 | 657 | if (hptr == node) { 658 | hash->table[chain] = node->next; 659 | } else { 660 | while (hptr->next != node) 661 | hptr = hptr->next; 662 | hptr->next = node->next; 663 | } 664 | 665 | hash->nodecount--; 666 | assert (hash_verify(hash)); 667 | node->next = NULL; 668 | 669 | return node; 670 | } 671 | 672 | /* 673 | * Like hash_delete_free but based on hash_scan_delete. 674 | */ 675 | 676 | void hash_scan_delfree(hash_t *hash, hnode_t *node) 677 | { 678 | hash_scan_delete(hash, node); 679 | hash->freenode(node, hash->context); 680 | } 681 | 682 | /* 683 | * Verify whether the given object is a valid hash table. This means 684 | * Notes: 685 | * 1. If the hash table is dynamic, verify whether the high and 686 | * low expansion/shrinkage thresholds are powers of two. 687 | * 2. Count all nodes in the table, and test each hash value 688 | * to see whether it is correct for the node's chain. 689 | */ 690 | 691 | int hash_verify(hash_t *hash) 692 | { 693 | hashcount_t count = 0; 694 | hash_val_t chain; 695 | hnode_t *hptr; 696 | 697 | if (hash->dynamic) { /* 1 */ 698 | if (hash->lowmark >= hash->highmark) 699 | return 0; 700 | if (!is_power_of_two(hash->highmark)) 701 | return 0; 702 | if (!is_power_of_two(hash->lowmark)) 703 | return 0; 704 | } 705 | 706 | for (chain = 0; chain < hash->nchains; chain++) { /* 2 */ 707 | for (hptr = hash->table[chain]; hptr != 0; hptr = hptr->next) { 708 | if ((hptr->hkey & hash->mask) != chain) 709 | return 0; 710 | count++; 711 | } 712 | } 713 | 714 | if (count != hash->nodecount) 715 | return 0; 716 | 717 | return 1; 718 | } 719 | 720 | /* 721 | * Test whether the hash table is full and return 1 if this is true, 722 | * 0 if it is false. 723 | */ 724 | 725 | #undef hash_isfull 726 | int hash_isfull(hash_t *hash) 727 | { 728 | return hash->nodecount == hash->maxcount; 729 | } 730 | 731 | /* 732 | * Test whether the hash table is empty and return 1 if this is true, 733 | * 0 if it is false. 734 | */ 735 | 736 | #undef hash_isempty 737 | int hash_isempty(hash_t *hash) 738 | { 739 | return hash->nodecount == 0; 740 | } 741 | 742 | static hnode_t *hnode_alloc(void *context) 743 | { 744 | return malloc(sizeof *hnode_alloc(NULL)); 745 | } 746 | 747 | static void hnode_free(hnode_t *node, void *context) 748 | { 749 | free(node); 750 | } 751 | 752 | 753 | /* 754 | * Create a hash table node dynamically and assign it the given data. 755 | */ 756 | 757 | hnode_t *hnode_create(void *data) 758 | { 759 | hnode_t *node = malloc(sizeof *node); 760 | if (node) { 761 | node->data = data; 762 | node->next = NULL; 763 | } 764 | return node; 765 | } 766 | 767 | /* 768 | * Initialize a client-supplied node 769 | */ 770 | 771 | hnode_t *hnode_init(hnode_t *hnode, void *data) 772 | { 773 | hnode->data = data; 774 | hnode->next = NULL; 775 | return hnode; 776 | } 777 | 778 | /* 779 | * Destroy a dynamically allocated node. 780 | */ 781 | 782 | void hnode_destroy(hnode_t *hnode) 783 | { 784 | free(hnode); 785 | } 786 | 787 | #undef hnode_put 788 | void hnode_put(hnode_t *node, void *data) 789 | { 790 | node->data = data; 791 | } 792 | 793 | #undef hnode_get 794 | void *hnode_get(hnode_t *node) 795 | { 796 | return node->data; 797 | } 798 | 799 | #undef hnode_getkey 800 | const void *hnode_getkey(hnode_t *node) 801 | { 802 | return node->key; 803 | } 804 | 805 | #undef hash_count 806 | hashcount_t hash_count(hash_t *hash) 807 | { 808 | return hash->nodecount; 809 | } 810 | 811 | #undef hash_size 812 | hashcount_t hash_size(hash_t *hash) 813 | { 814 | return hash->nchains; 815 | } 816 | 817 | static hash_val_t hash_fun_default(const void *key) 818 | { 819 | static unsigned long randbox[] = { 820 | 0x49848f1bU, 0xe6255dbaU, 0x36da5bdcU, 0x47bf94e9U, 821 | 0x8cbcce22U, 0x559fc06aU, 0xd268f536U, 0xe10af79aU, 822 | 0xc1af4d69U, 0x1d2917b5U, 0xec4c304dU, 0x9ee5016cU, 823 | 0x69232f74U, 0xfead7bb3U, 0xe9089ab6U, 0xf012f6aeU, 824 | }; 825 | 826 | const unsigned char *str = key; 827 | hash_val_t acc = 0; 828 | 829 | while (*str) { 830 | acc ^= randbox[(*str + acc) & 0xf]; 831 | acc = (acc << 1) | (acc >> 31); 832 | acc &= 0xffffffffU; 833 | acc ^= randbox[((*str++ >> 4) + acc) & 0xf]; 834 | acc = (acc << 2) | (acc >> 30); 835 | acc &= 0xffffffffU; 836 | } 837 | return acc; 838 | } 839 | 840 | static int hash_comp_default(const void *key1, const void *key2) 841 | { 842 | return strcmp(key1, key2); 843 | } 844 | 845 | #ifdef KAZLIB_TEST_MAIN 846 | 847 | #include 848 | #include 849 | #include 850 | 851 | typedef char input_t[256]; 852 | 853 | static int tokenize(char *string, ...) 854 | { 855 | char **tokptr; 856 | va_list arglist; 857 | int tokcount = 0; 858 | 859 | va_start(arglist, string); 860 | tokptr = va_arg(arglist, char **); 861 | while (tokptr) { 862 | while (*string && isspace((unsigned char) *string)) 863 | string++; 864 | if (!*string) 865 | break; 866 | *tokptr = string; 867 | while (*string && !isspace((unsigned char) *string)) 868 | string++; 869 | tokptr = va_arg(arglist, char **); 870 | tokcount++; 871 | if (!*string) 872 | break; 873 | *string++ = 0; 874 | } 875 | va_end(arglist); 876 | 877 | return tokcount; 878 | } 879 | 880 | static char *dupstring(char *str) 881 | { 882 | int sz = strlen(str) + 1; 883 | char *new = malloc(sz); 884 | if (new) 885 | memcpy(new, str, sz); 886 | return new; 887 | } 888 | 889 | static hnode_t *new_node(void *c) 890 | { 891 | static hnode_t few[5]; 892 | static int count; 893 | 894 | if (count < 5) 895 | return few + count++; 896 | 897 | return NULL; 898 | } 899 | 900 | static void del_node(hnode_t *n, void *c) 901 | { 902 | } 903 | 904 | int main(void) 905 | { 906 | input_t in; 907 | hash_t *h = hash_create(HASHCOUNT_T_MAX, 0, 0); 908 | hnode_t *hn; 909 | hscan_t hs; 910 | char *tok1, *tok2, *val; 911 | const char *key; 912 | int prompt = 0; 913 | 914 | char *help = 915 | "a add value to hash table\n" 916 | "d delete value from hash table\n" 917 | "l lookup value in hash table\n" 918 | "n show size of hash table\n" 919 | "c show number of entries\n" 920 | "t dump whole hash table\n" 921 | "+ increase hash table (private func)\n" 922 | "- decrease hash table (private func)\n" 923 | "b print hash_t_bit value\n" 924 | "p turn prompt on\n" 925 | "s switch to non-functioning allocator\n" 926 | "q quit"; 927 | 928 | if (!h) 929 | puts("hash_create failed"); 930 | 931 | for (;;) { 932 | if (prompt) 933 | putchar('>'); 934 | fflush(stdout); 935 | 936 | if (!fgets(in, sizeof(input_t), stdin)) 937 | break; 938 | 939 | switch(in[0]) { 940 | case '?': 941 | puts(help); 942 | break; 943 | case 'b': 944 | printf("%d\n", hash_val_t_bit); 945 | break; 946 | case 'a': 947 | if (tokenize(in+1, &tok1, &tok2, (char **) 0) != 2) { 948 | puts("what?"); 949 | break; 950 | } 951 | key = dupstring(tok1); 952 | val = dupstring(tok2); 953 | 954 | if (!key || !val) { 955 | puts("out of memory"); 956 | free((void *) key); 957 | free(val); 958 | } 959 | 960 | if (!hash_alloc_insert(h, key, val)) { 961 | puts("hash_alloc_insert failed"); 962 | free((void *) key); 963 | free(val); 964 | break; 965 | } 966 | break; 967 | case 'd': 968 | if (tokenize(in+1, &tok1, (char **) 0) != 1) { 969 | puts("what?"); 970 | break; 971 | } 972 | hn = hash_lookup(h, tok1); 973 | if (!hn) { 974 | puts("hash_lookup failed"); 975 | break; 976 | } 977 | val = hnode_get(hn); 978 | key = hnode_getkey(hn); 979 | hash_scan_delfree(h, hn); 980 | free((void *) key); 981 | free(val); 982 | break; 983 | case 'l': 984 | if (tokenize(in+1, &tok1, (char **) 0) != 1) { 985 | puts("what?"); 986 | break; 987 | } 988 | hn = hash_lookup(h, tok1); 989 | if (!hn) { 990 | puts("hash_lookup failed"); 991 | break; 992 | } 993 | val = hnode_get(hn); 994 | puts(val); 995 | break; 996 | case 'n': 997 | printf("%lu\n", (unsigned long) hash_size(h)); 998 | break; 999 | case 'c': 1000 | printf("%lu\n", (unsigned long) hash_count(h)); 1001 | break; 1002 | case 't': 1003 | hash_scan_begin(&hs, h); 1004 | while ((hn = hash_scan_next(&hs))) 1005 | printf("%s\t%s\n", (char*) hnode_getkey(hn), 1006 | (char*) hnode_get(hn)); 1007 | break; 1008 | case '+': 1009 | grow_table(h); /* private function */ 1010 | break; 1011 | case '-': 1012 | shrink_table(h); /* private function */ 1013 | break; 1014 | case 'q': 1015 | exit(0); 1016 | break; 1017 | case '\0': 1018 | break; 1019 | case 'p': 1020 | prompt = 1; 1021 | break; 1022 | case 's': 1023 | hash_set_allocator(h, new_node, del_node, NULL); 1024 | break; 1025 | default: 1026 | putchar('?'); 1027 | putchar('\n'); 1028 | break; 1029 | } 1030 | } 1031 | 1032 | return 0; 1033 | } 1034 | 1035 | #endif 1036 | -------------------------------------------------------------------------------- /dict.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Dictionary Abstract Data Type 3 | * Copyright (C) 1997 Kaz Kylheku 4 | * 5 | * Free Software License: 6 | * 7 | * All rights are reserved by the author, with the following exceptions: 8 | * Permission is granted to freely reproduce and distribute this software, 9 | * possibly in exchange for a fee, provided that this copyright notice appears 10 | * intact. Permission is also granted to adapt this software to produce 11 | * derivative works, as long as the modified versions carry this copyright 12 | * notice and additional notices stating that the work has been modified. 13 | * This source code may be translated into executable form and incorporated 14 | * into proprietary software; there is no requirement for such software to 15 | * contain a copyright notice related to this source. 16 | * 17 | * $Id: dict.c,v 1.40.2.7 2000/11/13 01:36:44 kaz Exp $ 18 | * $Name: kazlib_1_20 $ 19 | */ 20 | 21 | #include 22 | #include 23 | #include 24 | #define DICT_IMPLEMENTATION 25 | #include "dict.h" 26 | 27 | #ifdef KAZLIB_RCSID 28 | static const char rcsid[] = "$Id: dict.c,v 1.40.2.7 2000/11/13 01:36:44 kaz Exp $"; 29 | #endif 30 | 31 | /* 32 | * These macros provide short convenient names for structure members, 33 | * which are embellished with dict_ prefixes so that they are 34 | * properly confined to the documented namespace. It's legal for a 35 | * program which uses dict to define, for instance, a macro called ``parent''. 36 | * Such a macro would interfere with the dnode_t struct definition. 37 | * In general, highly portable and reusable C modules which expose their 38 | * structures need to confine structure member names to well-defined spaces. 39 | * The resulting identifiers aren't necessarily convenient to use, nor 40 | * readable, in the implementation, however! 41 | */ 42 | 43 | #define left dict_left 44 | #define right dict_right 45 | #define parent dict_parent 46 | #define color dict_color 47 | #define key dict_key 48 | #define data dict_data 49 | 50 | #define nilnode dict_nilnode 51 | #define nodecount dict_nodecount 52 | #define maxcount dict_maxcount 53 | #define compare dict_compare 54 | #define allocnode dict_allocnode 55 | #define freenode dict_freenode 56 | #define context dict_context 57 | #define dupes dict_dupes 58 | 59 | #define dictptr dict_dictptr 60 | 61 | #define dict_root(D) ((D)->nilnode.left) 62 | #define dict_nil(D) (&(D)->nilnode) 63 | #define DICT_DEPTH_MAX 64 64 | 65 | static dnode_t *dnode_alloc(void *context); 66 | static void dnode_free(dnode_t *node, void *context); 67 | 68 | /* 69 | * Perform a ``left rotation'' adjustment on the tree. The given node P and 70 | * its right child C are rearranged so that the P instead becomes the left 71 | * child of C. The left subtree of C is inherited as the new right subtree 72 | * for P. The ordering of the keys within the tree is thus preserved. 73 | */ 74 | 75 | static void rotate_left(dnode_t *upper) 76 | { 77 | dnode_t *lower, *lowleft, *upparent; 78 | 79 | lower = upper->right; 80 | upper->right = lowleft = lower->left; 81 | lowleft->parent = upper; 82 | 83 | lower->parent = upparent = upper->parent; 84 | 85 | /* don't need to check for root node here because root->parent is 86 | the sentinel nil node, and root->parent->left points back to root */ 87 | 88 | if (upper == upparent->left) { 89 | upparent->left = lower; 90 | } else { 91 | assert (upper == upparent->right); 92 | upparent->right = lower; 93 | } 94 | 95 | lower->left = upper; 96 | upper->parent = lower; 97 | } 98 | 99 | /* 100 | * This operation is the ``mirror'' image of rotate_left. It is 101 | * the same procedure, but with left and right interchanged. 102 | */ 103 | 104 | static void rotate_right(dnode_t *upper) 105 | { 106 | dnode_t *lower, *lowright, *upparent; 107 | 108 | lower = upper->left; 109 | upper->left = lowright = lower->right; 110 | lowright->parent = upper; 111 | 112 | lower->parent = upparent = upper->parent; 113 | 114 | if (upper == upparent->right) { 115 | upparent->right = lower; 116 | } else { 117 | assert (upper == upparent->left); 118 | upparent->left = lower; 119 | } 120 | 121 | lower->right = upper; 122 | upper->parent = lower; 123 | } 124 | 125 | /* 126 | * Do a postorder traversal of the tree rooted at the specified 127 | * node and free everything under it. Used by dict_free(). 128 | */ 129 | 130 | static void free_nodes(dict_t *dict, dnode_t *node, dnode_t *nil) 131 | { 132 | if (node == nil) 133 | return; 134 | free_nodes(dict, node->left, nil); 135 | free_nodes(dict, node->right, nil); 136 | dict->freenode(node, dict->context); 137 | } 138 | 139 | /* 140 | * This procedure performs a verification that the given subtree is a binary 141 | * search tree. It performs an inorder traversal of the tree using the 142 | * dict_next() successor function, verifying that the key of each node is 143 | * strictly lower than that of its successor, if duplicates are not allowed, 144 | * or lower or equal if duplicates are allowed. This function is used for 145 | * debugging purposes. 146 | */ 147 | 148 | static int verify_bintree(dict_t *dict) 149 | { 150 | dnode_t *first, *next; 151 | 152 | first = dict_first(dict); 153 | 154 | if (dict->dupes) { 155 | while (first && (next = dict_next(dict, first))) { 156 | if (dict->compare(first->key, next->key) > 0) 157 | return 0; 158 | first = next; 159 | } 160 | } else { 161 | while (first && (next = dict_next(dict, first))) { 162 | if (dict->compare(first->key, next->key) >= 0) 163 | return 0; 164 | first = next; 165 | } 166 | } 167 | return 1; 168 | } 169 | 170 | 171 | /* 172 | * This function recursively verifies that the given binary subtree satisfies 173 | * three of the red black properties. It checks that every red node has only 174 | * black children. It makes sure that each node is either red or black. And it 175 | * checks that every path has the same count of black nodes from root to leaf. 176 | * It returns the blackheight of the given subtree; this allows blackheights to 177 | * be computed recursively and compared for left and right siblings for 178 | * mismatches. It does not check for every nil node being black, because there 179 | * is only one sentinel nil node. The return value of this function is the 180 | * black height of the subtree rooted at the node ``root'', or zero if the 181 | * subtree is not red-black. 182 | */ 183 | 184 | static unsigned int verify_redblack(dnode_t *nil, dnode_t *root) 185 | { 186 | unsigned height_left, height_right; 187 | 188 | if (root != nil) { 189 | height_left = verify_redblack(nil, root->left); 190 | height_right = verify_redblack(nil, root->right); 191 | if (height_left == 0 || height_right == 0) 192 | return 0; 193 | if (height_left != height_right) 194 | return 0; 195 | if (root->color == dnode_red) { 196 | if (root->left->color != dnode_black) 197 | return 0; 198 | if (root->right->color != dnode_black) 199 | return 0; 200 | return height_left; 201 | } 202 | if (root->color != dnode_black) 203 | return 0; 204 | return height_left + 1; 205 | } 206 | return 1; 207 | } 208 | 209 | /* 210 | * Compute the actual count of nodes by traversing the tree and 211 | * return it. This could be compared against the stored count to 212 | * detect a mismatch. 213 | */ 214 | 215 | static dictcount_t verify_node_count(dnode_t *nil, dnode_t *root) 216 | { 217 | if (root == nil) 218 | return 0; 219 | else 220 | return 1 + verify_node_count(nil, root->left) 221 | + verify_node_count(nil, root->right); 222 | } 223 | 224 | /* 225 | * Verify that the tree contains the given node. This is done by 226 | * traversing all of the nodes and comparing their pointers to the 227 | * given pointer. Returns 1 if the node is found, otherwise 228 | * returns zero. It is intended for debugging purposes. 229 | */ 230 | 231 | static int verify_dict_has_node(dnode_t *nil, dnode_t *root, dnode_t *node) 232 | { 233 | if (root != nil) { 234 | return root == node 235 | || verify_dict_has_node(nil, root->left, node) 236 | || verify_dict_has_node(nil, root->right, node); 237 | } 238 | return 0; 239 | } 240 | 241 | 242 | /* 243 | * Dynamically allocate and initialize a dictionary object. 244 | */ 245 | 246 | dict_t *dict_create(dictcount_t maxcount, dict_comp_t comp) 247 | { 248 | dict_t *new = malloc(sizeof *new); 249 | 250 | if (new) { 251 | new->compare = comp; 252 | new->allocnode = dnode_alloc; 253 | new->freenode = dnode_free; 254 | new->context = NULL; 255 | new->nodecount = 0; 256 | new->maxcount = maxcount; 257 | new->nilnode.left = &new->nilnode; 258 | new->nilnode.right = &new->nilnode; 259 | new->nilnode.parent = &new->nilnode; 260 | new->nilnode.color = dnode_black; 261 | new->dupes = 0; 262 | } 263 | return new; 264 | } 265 | 266 | /* 267 | * Select a different set of node allocator routines. 268 | */ 269 | 270 | void dict_set_allocator(dict_t *dict, dnode_alloc_t al, 271 | dnode_free_t fr, void *context) 272 | { 273 | assert (dict_count(dict) == 0); 274 | assert ((al == NULL && fr == NULL) || (al != NULL && fr != NULL)); 275 | 276 | dict->allocnode = al ? al : dnode_alloc; 277 | dict->freenode = fr ? fr : dnode_free; 278 | dict->context = context; 279 | } 280 | 281 | /* 282 | * Free a dynamically allocated dictionary object. Removing the nodes 283 | * from the tree before deleting it is required. 284 | */ 285 | 286 | void dict_destroy(dict_t *dict) 287 | { 288 | assert (dict_isempty(dict)); 289 | free(dict); 290 | } 291 | 292 | /* 293 | * Free all the nodes in the dictionary by using the dictionary's 294 | * installed free routine. The dictionary is emptied. 295 | */ 296 | 297 | void dict_free_nodes(dict_t *dict) 298 | { 299 | dnode_t *nil = dict_nil(dict), *root = dict_root(dict); 300 | free_nodes(dict, root, nil); 301 | dict->nodecount = 0; 302 | dict->nilnode.left = &dict->nilnode; 303 | dict->nilnode.right = &dict->nilnode; 304 | } 305 | 306 | /* 307 | * Obsolescent function, equivalent to dict_free_nodes 308 | */ 309 | 310 | void dict_free(dict_t *dict) 311 | { 312 | #ifdef KAZLIB_OBSOLESCENT_DEBUG 313 | assert ("call to obsolescent function dict_free()" && 0); 314 | #endif 315 | dict_free_nodes(dict); 316 | } 317 | 318 | /* 319 | * Initialize a user-supplied dictionary object. 320 | */ 321 | 322 | dict_t *dict_init(dict_t *dict, dictcount_t maxcount, dict_comp_t comp) 323 | { 324 | dict->compare = comp; 325 | dict->allocnode = dnode_alloc; 326 | dict->freenode = dnode_free; 327 | dict->context = NULL; 328 | dict->nodecount = 0; 329 | dict->maxcount = maxcount; 330 | dict->nilnode.left = &dict->nilnode; 331 | dict->nilnode.right = &dict->nilnode; 332 | dict->nilnode.parent = &dict->nilnode; 333 | dict->nilnode.color = dnode_black; 334 | dict->dupes = 0; 335 | return dict; 336 | } 337 | 338 | /* 339 | * Initialize a dictionary in the likeness of another dictionary 340 | */ 341 | 342 | void dict_init_like(dict_t *dict, const dict_t *template) 343 | { 344 | dict->compare = template->compare; 345 | dict->allocnode = template->allocnode; 346 | dict->freenode = template->freenode; 347 | dict->context = template->context; 348 | dict->nodecount = 0; 349 | dict->maxcount = template->maxcount; 350 | dict->nilnode.left = &dict->nilnode; 351 | dict->nilnode.right = &dict->nilnode; 352 | dict->nilnode.parent = &dict->nilnode; 353 | dict->nilnode.color = dnode_black; 354 | dict->dupes = template->dupes; 355 | 356 | assert (dict_similar(dict, template)); 357 | } 358 | 359 | /* 360 | * Remove all nodes from the dictionary (without freeing them in any way). 361 | */ 362 | 363 | static void dict_clear(dict_t *dict) 364 | { 365 | dict->nodecount = 0; 366 | dict->nilnode.left = &dict->nilnode; 367 | dict->nilnode.right = &dict->nilnode; 368 | dict->nilnode.parent = &dict->nilnode; 369 | assert (dict->nilnode.color == dnode_black); 370 | } 371 | 372 | 373 | /* 374 | * Verify the integrity of the dictionary structure. This is provided for 375 | * debugging purposes, and should be placed in assert statements. Just because 376 | * this function succeeds doesn't mean that the tree is not corrupt. Certain 377 | * corruptions in the tree may simply cause undefined behavior. 378 | */ 379 | 380 | int dict_verify(dict_t *dict) 381 | { 382 | dnode_t *nil = dict_nil(dict), *root = dict_root(dict); 383 | 384 | /* check that the sentinel node and root node are black */ 385 | if (root->color != dnode_black) 386 | return 0; 387 | if (nil->color != dnode_black) 388 | return 0; 389 | if (nil->right != nil) 390 | return 0; 391 | /* nil->left is the root node; check that its parent pointer is nil */ 392 | if (nil->left->parent != nil) 393 | return 0; 394 | /* perform a weak test that the tree is a binary search tree */ 395 | if (!verify_bintree(dict)) 396 | return 0; 397 | /* verify that the tree is a red-black tree */ 398 | if (!verify_redblack(nil, root)) 399 | return 0; 400 | if (verify_node_count(nil, root) != dict_count(dict)) 401 | return 0; 402 | return 1; 403 | } 404 | 405 | /* 406 | * Determine whether two dictionaries are similar: have the same comparison and 407 | * allocator functions, and same status as to whether duplicates are allowed. 408 | */ 409 | 410 | int dict_similar(const dict_t *left, const dict_t *right) 411 | { 412 | if (left->compare != right->compare) 413 | return 0; 414 | 415 | if (left->allocnode != right->allocnode) 416 | return 0; 417 | 418 | if (left->freenode != right->freenode) 419 | return 0; 420 | 421 | if (left->context != right->context) 422 | return 0; 423 | 424 | if (left->dupes != right->dupes) 425 | return 0; 426 | 427 | return 1; 428 | } 429 | 430 | /* 431 | * Locate a node in the dictionary having the given key. 432 | * If the node is not found, a null a pointer is returned (rather than 433 | * a pointer that dictionary's nil sentinel node), otherwise a pointer to the 434 | * located node is returned. 435 | */ 436 | 437 | dnode_t *dict_lookup(dict_t *dict, const void *key) 438 | { 439 | dnode_t *root = dict_root(dict); 440 | dnode_t *nil = dict_nil(dict); 441 | dnode_t *saved; 442 | int result; 443 | 444 | /* simple binary search adapted for trees that contain duplicate keys */ 445 | 446 | while (root != nil) { 447 | result = dict->compare(key, root->key); 448 | if (result < 0) 449 | root = root->left; 450 | else if (result > 0) 451 | root = root->right; 452 | else { 453 | if (!dict->dupes) { /* no duplicates, return match */ 454 | return root; 455 | } else { /* could be dupes, find leftmost one */ 456 | do { 457 | saved = root; 458 | root = root->left; 459 | while (root != nil && dict->compare(key, root->key)) 460 | root = root->right; 461 | } while (root != nil); 462 | return saved; 463 | } 464 | } 465 | } 466 | 467 | return NULL; 468 | } 469 | 470 | /* 471 | * Look for the node corresponding to the lowest key that is equal to or 472 | * greater than the given key. If there is no such node, return null. 473 | */ 474 | 475 | dnode_t *dict_lower_bound(dict_t *dict, const void *key) 476 | { 477 | dnode_t *root = dict_root(dict); 478 | dnode_t *nil = dict_nil(dict); 479 | dnode_t *tentative = 0; 480 | 481 | while (root != nil) { 482 | int result = dict->compare(key, root->key); 483 | 484 | if (result > 0) { 485 | root = root->right; 486 | } else if (result < 0) { 487 | tentative = root; 488 | root = root->left; 489 | } else { 490 | if (!dict->dupes) { 491 | return root; 492 | } else { 493 | tentative = root; 494 | root = root->left; 495 | } 496 | } 497 | } 498 | 499 | return tentative; 500 | } 501 | 502 | /* 503 | * Look for the node corresponding to the greatest key that is equal to or 504 | * lower than the given key. If there is no such node, return null. 505 | */ 506 | 507 | dnode_t *dict_upper_bound(dict_t *dict, const void *key) 508 | { 509 | dnode_t *root = dict_root(dict); 510 | dnode_t *nil = dict_nil(dict); 511 | dnode_t *tentative = 0; 512 | 513 | while (root != nil) { 514 | int result = dict->compare(key, root->key); 515 | 516 | if (result < 0) { 517 | root = root->left; 518 | } else if (result > 0) { 519 | tentative = root; 520 | root = root->right; 521 | } else { 522 | if (!dict->dupes) { 523 | return root; 524 | } else { 525 | tentative = root; 526 | root = root->right; 527 | } 528 | } 529 | } 530 | 531 | return tentative; 532 | } 533 | 534 | /* 535 | * Insert a node into the dictionary. The node should have been 536 | * initialized with a data field. All other fields are ignored. 537 | * The behavior is undefined if the user attempts to insert into 538 | * a dictionary that is already full (for which the dict_isfull() 539 | * function returns true). 540 | */ 541 | 542 | void dict_insert(dict_t *dict, dnode_t *node, const void *key) 543 | { 544 | dnode_t *where = dict_root(dict), *nil = dict_nil(dict); 545 | dnode_t *parent = nil, *uncle, *grandpa; 546 | int result = -1; 547 | 548 | node->key = key; 549 | 550 | assert (!dict_isfull(dict)); 551 | assert (!dict_contains(dict, node)); 552 | assert (!dnode_is_in_a_dict(node)); 553 | 554 | /* basic binary tree insert */ 555 | 556 | while (where != nil) { 557 | parent = where; 558 | result = dict->compare(key, where->key); 559 | /* trap attempts at duplicate key insertion unless it's explicitly allowed */ 560 | assert (dict->dupes || result != 0); 561 | if (result < 0) 562 | where = where->left; 563 | else 564 | where = where->right; 565 | } 566 | 567 | assert (where == nil); 568 | 569 | if (result < 0) 570 | parent->left = node; 571 | else 572 | parent->right = node; 573 | 574 | node->parent = parent; 575 | node->left = nil; 576 | node->right = nil; 577 | 578 | dict->nodecount++; 579 | 580 | /* red black adjustments */ 581 | 582 | node->color = dnode_red; 583 | 584 | while (parent->color == dnode_red) { 585 | grandpa = parent->parent; 586 | if (parent == grandpa->left) { 587 | uncle = grandpa->right; 588 | if (uncle->color == dnode_red) { /* red parent, red uncle */ 589 | parent->color = dnode_black; 590 | uncle->color = dnode_black; 591 | grandpa->color = dnode_red; 592 | node = grandpa; 593 | parent = grandpa->parent; 594 | } else { /* red parent, black uncle */ 595 | if (node == parent->right) { 596 | rotate_left(parent); 597 | parent = node; 598 | assert (grandpa == parent->parent); 599 | /* rotation between parent and child preserves grandpa */ 600 | } 601 | parent->color = dnode_black; 602 | grandpa->color = dnode_red; 603 | rotate_right(grandpa); 604 | break; 605 | } 606 | } else { /* symmetric cases: parent == parent->parent->right */ 607 | uncle = grandpa->left; 608 | if (uncle->color == dnode_red) { 609 | parent->color = dnode_black; 610 | uncle->color = dnode_black; 611 | grandpa->color = dnode_red; 612 | node = grandpa; 613 | parent = grandpa->parent; 614 | } else { 615 | if (node == parent->left) { 616 | rotate_right(parent); 617 | parent = node; 618 | assert (grandpa == parent->parent); 619 | } 620 | parent->color = dnode_black; 621 | grandpa->color = dnode_red; 622 | rotate_left(grandpa); 623 | break; 624 | } 625 | } 626 | } 627 | 628 | dict_root(dict)->color = dnode_black; 629 | 630 | assert (dict_verify(dict)); 631 | } 632 | 633 | /* 634 | * Delete the given node from the dictionary. If the given node does not belong 635 | * to the given dictionary, undefined behavior results. A pointer to the 636 | * deleted node is returned. 637 | */ 638 | 639 | dnode_t *dict_delete(dict_t *dict, dnode_t *delete) 640 | { 641 | dnode_t *nil = dict_nil(dict), *child, *delparent = delete->parent; 642 | 643 | /* basic deletion */ 644 | 645 | assert (!dict_isempty(dict)); 646 | assert (dict_contains(dict, delete)); 647 | 648 | /* 649 | * If the node being deleted has two children, then we replace it with its 650 | * successor (i.e. the leftmost node in the right subtree.) By doing this, 651 | * we avoid the traditional algorithm under which the successor's key and 652 | * value *only* move to the deleted node and the successor is spliced out 653 | * from the tree. We cannot use this approach because the user may hold 654 | * pointers to the successor, or nodes may be inextricably tied to some 655 | * other structures by way of embedding, etc. So we must splice out the 656 | * node we are given, not some other node, and must not move contents from 657 | * one node to another behind the user's back. 658 | */ 659 | 660 | if (delete->left != nil && delete->right != nil) { 661 | dnode_t *next = dict_next(dict, delete); 662 | dnode_t *nextparent = next->parent; 663 | dnode_color_t nextcolor = next->color; 664 | 665 | assert (next != nil); 666 | assert (next->parent != nil); 667 | assert (next->left == nil); 668 | 669 | /* 670 | * First, splice out the successor from the tree completely, by 671 | * moving up its right child into its place. 672 | */ 673 | 674 | child = next->right; 675 | child->parent = nextparent; 676 | 677 | if (nextparent->left == next) { 678 | nextparent->left = child; 679 | } else { 680 | assert (nextparent->right == next); 681 | nextparent->right = child; 682 | } 683 | 684 | /* 685 | * Now that the successor has been extricated from the tree, install it 686 | * in place of the node that we want deleted. 687 | */ 688 | 689 | next->parent = delparent; 690 | next->left = delete->left; 691 | next->right = delete->right; 692 | next->left->parent = next; 693 | next->right->parent = next; 694 | next->color = delete->color; 695 | delete->color = nextcolor; 696 | 697 | if (delparent->left == delete) { 698 | delparent->left = next; 699 | } else { 700 | assert (delparent->right == delete); 701 | delparent->right = next; 702 | } 703 | 704 | } else { 705 | assert (delete != nil); 706 | assert (delete->left == nil || delete->right == nil); 707 | 708 | child = (delete->left != nil) ? delete->left : delete->right; 709 | 710 | child->parent = delparent = delete->parent; 711 | 712 | if (delete == delparent->left) { 713 | delparent->left = child; 714 | } else { 715 | assert (delete == delparent->right); 716 | delparent->right = child; 717 | } 718 | } 719 | 720 | delete->parent = NULL; 721 | delete->right = NULL; 722 | delete->left = NULL; 723 | 724 | dict->nodecount--; 725 | 726 | assert (verify_bintree(dict)); 727 | 728 | /* red-black adjustments */ 729 | 730 | if (delete->color == dnode_black) { 731 | dnode_t *parent, *sister; 732 | 733 | dict_root(dict)->color = dnode_red; 734 | 735 | while (child->color == dnode_black) { 736 | parent = child->parent; 737 | if (child == parent->left) { 738 | sister = parent->right; 739 | assert (sister != nil); 740 | if (sister->color == dnode_red) { 741 | sister->color = dnode_black; 742 | parent->color = dnode_red; 743 | rotate_left(parent); 744 | sister = parent->right; 745 | assert (sister != nil); 746 | } 747 | if (sister->left->color == dnode_black 748 | && sister->right->color == dnode_black) { 749 | sister->color = dnode_red; 750 | child = parent; 751 | } else { 752 | if (sister->right->color == dnode_black) { 753 | assert (sister->left->color == dnode_red); 754 | sister->left->color = dnode_black; 755 | sister->color = dnode_red; 756 | rotate_right(sister); 757 | sister = parent->right; 758 | assert (sister != nil); 759 | } 760 | sister->color = parent->color; 761 | sister->right->color = dnode_black; 762 | parent->color = dnode_black; 763 | rotate_left(parent); 764 | break; 765 | } 766 | } else { /* symmetric case: child == child->parent->right */ 767 | assert (child == parent->right); 768 | sister = parent->left; 769 | assert (sister != nil); 770 | if (sister->color == dnode_red) { 771 | sister->color = dnode_black; 772 | parent->color = dnode_red; 773 | rotate_right(parent); 774 | sister = parent->left; 775 | assert (sister != nil); 776 | } 777 | if (sister->right->color == dnode_black 778 | && sister->left->color == dnode_black) { 779 | sister->color = dnode_red; 780 | child = parent; 781 | } else { 782 | if (sister->left->color == dnode_black) { 783 | assert (sister->right->color == dnode_red); 784 | sister->right->color = dnode_black; 785 | sister->color = dnode_red; 786 | rotate_left(sister); 787 | sister = parent->left; 788 | assert (sister != nil); 789 | } 790 | sister->color = parent->color; 791 | sister->left->color = dnode_black; 792 | parent->color = dnode_black; 793 | rotate_right(parent); 794 | break; 795 | } 796 | } 797 | } 798 | 799 | child->color = dnode_black; 800 | dict_root(dict)->color = dnode_black; 801 | } 802 | 803 | assert (dict_verify(dict)); 804 | 805 | return delete; 806 | } 807 | 808 | /* 809 | * Allocate a node using the dictionary's allocator routine, give it 810 | * the data item. 811 | */ 812 | 813 | int dict_alloc_insert(dict_t *dict, const void *key, void *data) 814 | { 815 | dnode_t *node = dict->allocnode(dict->context); 816 | 817 | if (node) { 818 | dnode_init(node, data); 819 | dict_insert(dict, node, key); 820 | return 1; 821 | } 822 | return 0; 823 | } 824 | 825 | void dict_delete_free(dict_t *dict, dnode_t *node) 826 | { 827 | dict_delete(dict, node); 828 | dict->freenode(node, dict->context); 829 | } 830 | 831 | /* 832 | * Return the node with the lowest (leftmost) key. If the dictionary is empty 833 | * (that is, dict_isempty(dict) returns 1) a null pointer is returned. 834 | */ 835 | 836 | dnode_t *dict_first(dict_t *dict) 837 | { 838 | dnode_t *nil = dict_nil(dict), *root = dict_root(dict), *left; 839 | 840 | if (root != nil) 841 | while ((left = root->left) != nil) 842 | root = left; 843 | 844 | return (root == nil) ? NULL : root; 845 | } 846 | 847 | /* 848 | * Return the node with the highest (rightmost) key. If the dictionary is empty 849 | * (that is, dict_isempty(dict) returns 1) a null pointer is returned. 850 | */ 851 | 852 | dnode_t *dict_last(dict_t *dict) 853 | { 854 | dnode_t *nil = dict_nil(dict), *root = dict_root(dict), *right; 855 | 856 | if (root != nil) 857 | while ((right = root->right) != nil) 858 | root = right; 859 | 860 | return (root == nil) ? NULL : root; 861 | } 862 | 863 | /* 864 | * Return the given node's successor node---the node which has the 865 | * next key in the the left to right ordering. If the node has 866 | * no successor, a null pointer is returned rather than a pointer to 867 | * the nil node. 868 | */ 869 | 870 | dnode_t *dict_next(dict_t *dict, dnode_t *curr) 871 | { 872 | dnode_t *nil = dict_nil(dict), *parent, *left; 873 | 874 | if (curr->right != nil) { 875 | curr = curr->right; 876 | while ((left = curr->left) != nil) 877 | curr = left; 878 | return curr; 879 | } 880 | 881 | parent = curr->parent; 882 | 883 | while (parent != nil && curr == parent->right) { 884 | curr = parent; 885 | parent = curr->parent; 886 | } 887 | 888 | return (parent == nil) ? NULL : parent; 889 | } 890 | 891 | /* 892 | * Return the given node's predecessor, in the key order. 893 | * The nil sentinel node is returned if there is no predecessor. 894 | */ 895 | 896 | dnode_t *dict_prev(dict_t *dict, dnode_t *curr) 897 | { 898 | dnode_t *nil = dict_nil(dict), *parent, *right; 899 | 900 | if (curr->left != nil) { 901 | curr = curr->left; 902 | while ((right = curr->right) != nil) 903 | curr = right; 904 | return curr; 905 | } 906 | 907 | parent = curr->parent; 908 | 909 | while (parent != nil && curr == parent->left) { 910 | curr = parent; 911 | parent = curr->parent; 912 | } 913 | 914 | return (parent == nil) ? NULL : parent; 915 | } 916 | 917 | void dict_allow_dupes(dict_t *dict) 918 | { 919 | dict->dupes = 1; 920 | } 921 | 922 | #undef dict_count 923 | #undef dict_isempty 924 | #undef dict_isfull 925 | #undef dnode_get 926 | #undef dnode_put 927 | #undef dnode_getkey 928 | 929 | dictcount_t dict_count(dict_t *dict) 930 | { 931 | return dict->nodecount; 932 | } 933 | 934 | int dict_isempty(dict_t *dict) 935 | { 936 | return dict->nodecount == 0; 937 | } 938 | 939 | int dict_isfull(dict_t *dict) 940 | { 941 | return dict->nodecount == dict->maxcount; 942 | } 943 | 944 | int dict_contains(dict_t *dict, dnode_t *node) 945 | { 946 | return verify_dict_has_node(dict_nil(dict), dict_root(dict), node); 947 | } 948 | 949 | static dnode_t *dnode_alloc(void *context) 950 | { 951 | return malloc(sizeof *dnode_alloc(NULL)); 952 | } 953 | 954 | static void dnode_free(dnode_t *node, void *context) 955 | { 956 | free(node); 957 | } 958 | 959 | dnode_t *dnode_create(void *data) 960 | { 961 | dnode_t *new = malloc(sizeof *new); 962 | if (new) { 963 | new->data = data; 964 | new->parent = NULL; 965 | new->left = NULL; 966 | new->right = NULL; 967 | } 968 | return new; 969 | } 970 | 971 | dnode_t *dnode_init(dnode_t *dnode, void *data) 972 | { 973 | dnode->data = data; 974 | dnode->parent = NULL; 975 | dnode->left = NULL; 976 | dnode->right = NULL; 977 | return dnode; 978 | } 979 | 980 | void dnode_destroy(dnode_t *dnode) 981 | { 982 | assert (!dnode_is_in_a_dict(dnode)); 983 | free(dnode); 984 | } 985 | 986 | void *dnode_get(dnode_t *dnode) 987 | { 988 | return dnode->data; 989 | } 990 | 991 | const void *dnode_getkey(dnode_t *dnode) 992 | { 993 | return dnode->key; 994 | } 995 | 996 | void dnode_put(dnode_t *dnode, void *data) 997 | { 998 | dnode->data = data; 999 | } 1000 | 1001 | int dnode_is_in_a_dict(dnode_t *dnode) 1002 | { 1003 | return (dnode->parent && dnode->left && dnode->right); 1004 | } 1005 | 1006 | void dict_process(dict_t *dict, void *context, dnode_process_t function) 1007 | { 1008 | dnode_t *node = dict_first(dict), *next; 1009 | 1010 | while (node != NULL) { 1011 | /* check for callback function deleting */ 1012 | /* the next node from under us */ 1013 | assert (dict_contains(dict, node)); 1014 | next = dict_next(dict, node); 1015 | function(dict, node, context); 1016 | node = next; 1017 | } 1018 | } 1019 | 1020 | static void load_begin_internal(dict_load_t *load, dict_t *dict) 1021 | { 1022 | load->dictptr = dict; 1023 | load->nilnode.left = &load->nilnode; 1024 | load->nilnode.right = &load->nilnode; 1025 | } 1026 | 1027 | void dict_load_begin(dict_load_t *load, dict_t *dict) 1028 | { 1029 | assert (dict_isempty(dict)); 1030 | load_begin_internal(load, dict); 1031 | } 1032 | 1033 | void dict_load_next(dict_load_t *load, dnode_t *newnode, const void *key) 1034 | { 1035 | dict_t *dict = load->dictptr; 1036 | dnode_t *nil = &load->nilnode; 1037 | 1038 | assert (!dnode_is_in_a_dict(newnode)); 1039 | assert (dict->nodecount < dict->maxcount); 1040 | 1041 | #ifndef NDEBUG 1042 | if (dict->nodecount > 0) { 1043 | if (dict->dupes) 1044 | assert (dict->compare(nil->left->key, key) <= 0); 1045 | else 1046 | assert (dict->compare(nil->left->key, key) < 0); 1047 | } 1048 | #endif 1049 | 1050 | newnode->key = key; 1051 | nil->right->left = newnode; 1052 | nil->right = newnode; 1053 | newnode->left = nil; 1054 | dict->nodecount++; 1055 | } 1056 | 1057 | void dict_load_end(dict_load_t *load) 1058 | { 1059 | dict_t *dict = load->dictptr; 1060 | dnode_t *tree[DICT_DEPTH_MAX] = { 0 }; 1061 | dnode_t *curr, *dictnil = dict_nil(dict), *loadnil = &load->nilnode, *next; 1062 | dnode_t *complete = 0; 1063 | dictcount_t fullcount = DICTCOUNT_T_MAX, nodecount = dict->nodecount; 1064 | dictcount_t botrowcount; 1065 | unsigned baselevel = 0, level = 0, i; 1066 | 1067 | assert (dnode_red == 0 && dnode_black == 1); 1068 | 1069 | while (fullcount >= nodecount && fullcount) 1070 | fullcount >>= 1; 1071 | 1072 | botrowcount = nodecount - fullcount; 1073 | 1074 | for (curr = loadnil->left; curr != loadnil; curr = next) { 1075 | next = curr->left; 1076 | 1077 | if (complete == NULL && botrowcount-- == 0) { 1078 | assert (baselevel == 0); 1079 | assert (level == 0); 1080 | baselevel = level = 1; 1081 | complete = tree[0]; 1082 | 1083 | if (complete != 0) { 1084 | tree[0] = 0; 1085 | complete->right = dictnil; 1086 | while (tree[level] != 0) { 1087 | tree[level]->right = complete; 1088 | complete->parent = tree[level]; 1089 | complete = tree[level]; 1090 | tree[level++] = 0; 1091 | } 1092 | } 1093 | } 1094 | 1095 | if (complete == NULL) { 1096 | curr->left = dictnil; 1097 | curr->right = dictnil; 1098 | curr->color = level % 2; 1099 | complete = curr; 1100 | 1101 | assert (level == baselevel); 1102 | while (tree[level] != 0) { 1103 | tree[level]->right = complete; 1104 | complete->parent = tree[level]; 1105 | complete = tree[level]; 1106 | tree[level++] = 0; 1107 | } 1108 | } else { 1109 | curr->left = complete; 1110 | curr->color = (level + 1) % 2; 1111 | complete->parent = curr; 1112 | tree[level] = curr; 1113 | complete = 0; 1114 | level = baselevel; 1115 | } 1116 | } 1117 | 1118 | if (complete == NULL) 1119 | complete = dictnil; 1120 | 1121 | for (i = 0; i < DICT_DEPTH_MAX; i++) { 1122 | if (tree[i] != 0) { 1123 | tree[i]->right = complete; 1124 | complete->parent = tree[i]; 1125 | complete = tree[i]; 1126 | } 1127 | } 1128 | 1129 | dictnil->color = dnode_black; 1130 | dictnil->right = dictnil; 1131 | complete->parent = dictnil; 1132 | complete->color = dnode_black; 1133 | dict_root(dict) = complete; 1134 | 1135 | assert (dict_verify(dict)); 1136 | } 1137 | 1138 | void dict_merge(dict_t *dest, dict_t *source) 1139 | { 1140 | dict_load_t load; 1141 | dnode_t *leftnode = dict_first(dest), *rightnode = dict_first(source); 1142 | 1143 | assert (dict_similar(dest, source)); 1144 | 1145 | if (source == dest) 1146 | return; 1147 | 1148 | dest->nodecount = 0; 1149 | load_begin_internal(&load, dest); 1150 | 1151 | for (;;) { 1152 | if (leftnode != NULL && rightnode != NULL) { 1153 | if (dest->compare(leftnode->key, rightnode->key) < 0) 1154 | goto copyleft; 1155 | else 1156 | goto copyright; 1157 | } else if (leftnode != NULL) { 1158 | goto copyleft; 1159 | } else if (rightnode != NULL) { 1160 | goto copyright; 1161 | } else { 1162 | assert (leftnode == NULL && rightnode == NULL); 1163 | break; 1164 | } 1165 | 1166 | copyleft: 1167 | { 1168 | dnode_t *next = dict_next(dest, leftnode); 1169 | #ifndef NDEBUG 1170 | leftnode->left = NULL; /* suppress assertion in dict_load_next */ 1171 | #endif 1172 | dict_load_next(&load, leftnode, leftnode->key); 1173 | leftnode = next; 1174 | continue; 1175 | } 1176 | 1177 | copyright: 1178 | { 1179 | dnode_t *next = dict_next(source, rightnode); 1180 | #ifndef NDEBUG 1181 | rightnode->left = NULL; 1182 | #endif 1183 | dict_load_next(&load, rightnode, rightnode->key); 1184 | rightnode = next; 1185 | continue; 1186 | } 1187 | } 1188 | 1189 | dict_clear(source); 1190 | dict_load_end(&load); 1191 | } 1192 | 1193 | #ifdef KAZLIB_TEST_MAIN 1194 | 1195 | #include 1196 | #include 1197 | #include 1198 | #include 1199 | 1200 | typedef char input_t[256]; 1201 | 1202 | static int tokenize(char *string, ...) 1203 | { 1204 | char **tokptr; 1205 | va_list arglist; 1206 | int tokcount = 0; 1207 | 1208 | va_start(arglist, string); 1209 | tokptr = va_arg(arglist, char **); 1210 | while (tokptr) { 1211 | while (*string && isspace((unsigned char) *string)) 1212 | string++; 1213 | if (!*string) 1214 | break; 1215 | *tokptr = string; 1216 | while (*string && !isspace((unsigned char) *string)) 1217 | string++; 1218 | tokptr = va_arg(arglist, char **); 1219 | tokcount++; 1220 | if (!*string) 1221 | break; 1222 | *string++ = 0; 1223 | } 1224 | va_end(arglist); 1225 | 1226 | return tokcount; 1227 | } 1228 | 1229 | static int comparef(const void *key1, const void *key2) 1230 | { 1231 | return strcmp(key1, key2); 1232 | } 1233 | 1234 | static char *dupstring(char *str) 1235 | { 1236 | int sz = strlen(str) + 1; 1237 | char *new = malloc(sz); 1238 | if (new) 1239 | memcpy(new, str, sz); 1240 | return new; 1241 | } 1242 | 1243 | static dnode_t *new_node(void *c) 1244 | { 1245 | static dnode_t few[5]; 1246 | static int count; 1247 | 1248 | if (count < 5) 1249 | return few + count++; 1250 | 1251 | return NULL; 1252 | } 1253 | 1254 | static void del_node(dnode_t *n, void *c) 1255 | { 1256 | } 1257 | 1258 | static int prompt = 0; 1259 | 1260 | static void construct(dict_t *d) 1261 | { 1262 | input_t in; 1263 | int done = 0; 1264 | dict_load_t dl; 1265 | dnode_t *dn; 1266 | char *tok1, *tok2, *val; 1267 | const char *key; 1268 | char *help = 1269 | "p turn prompt on\n" 1270 | "q finish construction\n" 1271 | "a add new entry\n"; 1272 | 1273 | if (!dict_isempty(d)) 1274 | puts("warning: dictionary not empty!"); 1275 | 1276 | dict_load_begin(&dl, d); 1277 | 1278 | while (!done) { 1279 | if (prompt) 1280 | putchar('>'); 1281 | fflush(stdout); 1282 | 1283 | if (!fgets(in, sizeof(input_t), stdin)) 1284 | break; 1285 | 1286 | switch (in[0]) { 1287 | case '?': 1288 | puts(help); 1289 | break; 1290 | case 'p': 1291 | prompt = 1; 1292 | break; 1293 | case 'q': 1294 | done = 1; 1295 | break; 1296 | case 'a': 1297 | if (tokenize(in+1, &tok1, &tok2, (char **) 0) != 2) { 1298 | puts("what?"); 1299 | break; 1300 | } 1301 | key = dupstring(tok1); 1302 | val = dupstring(tok2); 1303 | dn = dnode_create(val); 1304 | 1305 | if (!key || !val || !dn) { 1306 | puts("out of memory"); 1307 | free((void *) key); 1308 | free(val); 1309 | if (dn) 1310 | dnode_destroy(dn); 1311 | } 1312 | 1313 | dict_load_next(&dl, dn, key); 1314 | break; 1315 | default: 1316 | putchar('?'); 1317 | putchar('\n'); 1318 | break; 1319 | } 1320 | } 1321 | 1322 | dict_load_end(&dl); 1323 | } 1324 | 1325 | int main(void) 1326 | { 1327 | input_t in; 1328 | dict_t darray[10]; 1329 | dict_t *d = &darray[0]; 1330 | dnode_t *dn; 1331 | int i; 1332 | char *tok1, *tok2, *val; 1333 | const char *key; 1334 | 1335 | char *help = 1336 | "a add value to dictionary\n" 1337 | "d delete value from dictionary\n" 1338 | "l lookup value in dictionary\n" 1339 | "( lookup lower bound\n" 1340 | ") lookup upper bound\n" 1341 | "# switch to alternate dictionary (0-9)\n" 1342 | "j merge two dictionaries\n" 1343 | "f free the whole dictionary\n" 1344 | "k allow duplicate keys\n" 1345 | "c show number of entries\n" 1346 | "t dump whole dictionary in sort order\n" 1347 | "m make dictionary out of sorted items\n" 1348 | "p turn prompt on\n" 1349 | "s switch to non-functioning allocator\n" 1350 | "q quit"; 1351 | 1352 | for (i = 0; i < sizeof darray / sizeof *darray; i++) 1353 | dict_init(&darray[i], DICTCOUNT_T_MAX, comparef); 1354 | 1355 | for (;;) { 1356 | if (prompt) 1357 | putchar('>'); 1358 | fflush(stdout); 1359 | 1360 | if (!fgets(in, sizeof(input_t), stdin)) 1361 | break; 1362 | 1363 | switch(in[0]) { 1364 | case '?': 1365 | puts(help); 1366 | break; 1367 | case 'a': 1368 | if (tokenize(in+1, &tok1, &tok2, (char **) 0) != 2) { 1369 | puts("what?"); 1370 | break; 1371 | } 1372 | key = dupstring(tok1); 1373 | val = dupstring(tok2); 1374 | 1375 | if (!key || !val) { 1376 | puts("out of memory"); 1377 | free((void *) key); 1378 | free(val); 1379 | } 1380 | 1381 | if (!dict_alloc_insert(d, key, val)) { 1382 | puts("dict_alloc_insert failed"); 1383 | free((void *) key); 1384 | free(val); 1385 | break; 1386 | } 1387 | break; 1388 | case 'd': 1389 | if (tokenize(in+1, &tok1, (char **) 0) != 1) { 1390 | puts("what?"); 1391 | break; 1392 | } 1393 | dn = dict_lookup(d, tok1); 1394 | if (!dn) { 1395 | puts("dict_lookup failed"); 1396 | break; 1397 | } 1398 | val = dnode_get(dn); 1399 | key = dnode_getkey(dn); 1400 | dict_delete_free(d, dn); 1401 | 1402 | free(val); 1403 | free((void *) key); 1404 | break; 1405 | case 'f': 1406 | dict_free(d); 1407 | break; 1408 | case 'l': 1409 | case '(': 1410 | case ')': 1411 | if (tokenize(in+1, &tok1, (char **) 0) != 1) { 1412 | puts("what?"); 1413 | break; 1414 | } 1415 | dn = 0; 1416 | switch (in[0]) { 1417 | case 'l': 1418 | dn = dict_lookup(d, tok1); 1419 | break; 1420 | case '(': 1421 | dn = dict_lower_bound(d, tok1); 1422 | break; 1423 | case ')': 1424 | dn = dict_upper_bound(d, tok1); 1425 | break; 1426 | } 1427 | if (!dn) { 1428 | puts("lookup failed"); 1429 | break; 1430 | } 1431 | val = dnode_get(dn); 1432 | puts(val); 1433 | break; 1434 | case 'm': 1435 | construct(d); 1436 | break; 1437 | case 'k': 1438 | dict_allow_dupes(d); 1439 | break; 1440 | case 'c': 1441 | printf("%lu\n", (unsigned long) dict_count(d)); 1442 | break; 1443 | case 't': 1444 | for (dn = dict_first(d); dn; dn = dict_next(d, dn)) { 1445 | printf("%s\t%s\n", (char *) dnode_getkey(dn), 1446 | (char *) dnode_get(dn)); 1447 | } 1448 | break; 1449 | case 'q': 1450 | exit(0); 1451 | break; 1452 | case '\0': 1453 | break; 1454 | case 'p': 1455 | prompt = 1; 1456 | break; 1457 | case 's': 1458 | dict_set_allocator(d, new_node, del_node, NULL); 1459 | break; 1460 | case '#': 1461 | if (tokenize(in+1, &tok1, (char **) 0) != 1) { 1462 | puts("what?"); 1463 | break; 1464 | } else { 1465 | int dictnum = atoi(tok1); 1466 | if (dictnum < 0 || dictnum > 9) { 1467 | puts("invalid number"); 1468 | break; 1469 | } 1470 | d = &darray[dictnum]; 1471 | } 1472 | break; 1473 | case 'j': 1474 | if (tokenize(in+1, &tok1, &tok2, (char **) 0) != 2) { 1475 | puts("what?"); 1476 | break; 1477 | } else { 1478 | int dict1 = atoi(tok1), dict2 = atoi(tok2); 1479 | if (dict1 < 0 || dict1 > 9 || dict2 < 0 || dict2 > 9) { 1480 | puts("invalid number"); 1481 | break; 1482 | } 1483 | dict_merge(&darray[dict1], &darray[dict2]); 1484 | } 1485 | break; 1486 | default: 1487 | putchar('?'); 1488 | putchar('\n'); 1489 | break; 1490 | } 1491 | } 1492 | 1493 | return 0; 1494 | } 1495 | 1496 | #endif 1497 | --------------------------------------------------------------------------------