Check-in [64680991bd]

Not logged in

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
SHA1 Hash:64680991bdc285b469ff012f6bebfcc498170455
Date: 2008-11-21 14:19:00
User: stephan
Comment:egg
Tags And Properties
Changes
hide diffs unified diffs patch

Added Makefile

Old () New (6c7cd75103ca0205)
> 1 #!/usr/bin/make -f
> 2 # Requires GNU Make 3.80+!
> 3 default: all
> 4
> 5 ifeq (1,$(TCC))
> 6 # If you have (want to use) tcc, try this:
> 7 CC := tcc
> 8 CXX := tcc
> 9 CPPFLAGS += -gb -bt 10 \
> 10 -Wwrite-strings \
> 11 -Wunsupported \
> 12 -Wall
> 13 else
> 14 # Assume gcc-compatible flags:
> 15 CXXFLAGS += -g
> 16 # CXXFLAGS += -Os
> 17 CFLAGS += \
> 18 -Wimplicit-function-declaration \
> 19 -Wall \
> 20 -g
> 21 endif
> 22
> 23 # CPPFLAGS ?= -g
> 24 # -Wall -Werror
> 25
> 26 PACKAGE.NAME = libwhgc
> 27 PACKAGE.VERSION := $(shell date +%Y%m%d)
> 28 ShakeNMake.DOXYGEN.GENERATE_LATEX := NO
> 29 ShakeNMake.DOXYGEN.USE_DOT := 0
> 30 include shake-n-make.make
> 31
> 32 #CFLAGS += -std=c99
> 33 #CFLAGS += -xc99=all # SUN CC
> 34
> 35 INCLUDES += -I.
> 36
> 37 ########################################################################
> 38 # Hashtable lib:
> 39 HASH_OBJ := $(patsubst %,%.o,whhash)
> 40 libwhhash.LIB.OBJECTS = $(HASH_OBJ)
> 41 $(call ShakeNMake.CALL.RULES.LIBS,libwhhash)
> 42
> 43 ########################################################################
> 44 # GC lib:
> 45 WHGC_OBJ := whgc.o whrc.o
> 46 libwhgc.LIB.OBJECTS = $(WHGC_OBJ) $(HASH_OBJ)
> 47 $(call ShakeNMake.CALL.RULES.LIBS,libwhgc)
> 48 $(libwhgc.LIB): $(libwhhash.LIB)
> 49 libs: $(libwhgc.LIB)
> 50
> 51 # test.BIN.LDFLAGS := $(libwhgc.LIB)
> 52 test.BIN.OBJECTS := test.o $(libwhgc.LIB)
> 53 $(call ShakeNMake.CALL.RULES.BINS,test)
> 54 $(test.BIN): $(libpegc.LIB)
> 55 bins: $(test.BIN)
> 56
> 57 CLEAN_FILES += *~ *.valgrind *.a
> 58
> 59 PACKAGE.DIST_FILES += $(wildcard *.c *.h)
> 60
> 61 all: libs bins

Added grindit.sh

Old () New (e8f473630fa05dc4)
> 1 #!/bin/bash
> 2 if [[ x != "x$1" ]]; then
> 3 app=./$1
> 4 shift
> 5 else
> 6 app=./test
> 7 fi
> 8 make ${app} || rm -f ${app}
> 9 test -x $app || {
> 10 echo "${app} not executable."
> 11 exit 3
> 12 }
> 13
> 14 log=${app}.valgrind
> 15 valgrind --log-fd=3 \
> 16 --leak-check=full -v \
> 17 --show-reachable=yes \
> 18 ${app} "$@" 3>${log}
> 19 x=$?
> 20 grep malloc/free ${log} && {
> 21 echo "Details are in [${log}]"
> 22 }
> 23 exit $x

Added shake-n-make.make

Old () New (81dbd3be3a8a77ea)
> 1 #!/usr/bin/make -f
> 2 all:
> 3 SHELL=/bin/bash
> 4 MAKE_REQUIRED_VERSION := 380# MAKE_VERSION stripped of any dots
> 5 VERSION_CHECK := \
> 6 $(shell \
> 7 test $$(echo $(MAKE_VERSION) | sed -e 's/\.//g') -ge \
> 8 "$(MAKE_REQUIRED_VERSION)" 2>/dev/null \
> 9 && echo 1 || echo 0)
> 10
> 11 ifneq (1,$(VERSION_CHECK))
> 12 $(error Your version of Make ($(MAKE_VERSION)) is too old to use this code!)
> 13 endif
> 14
> 15
> 16 ########################################################################
> 17 # This file defines a set of basic targets for single-dir C++ projects
> 18 # trees, designed for GNU make and gcc. The code in this file is
> 19 # intended to remain project-neutral. Add your project-specific stuff
> 20 # in higher-level makefiles and include this one from there.
> 21 #
> 22 # The shell code in this makefile assumes GNU Make, GNU Bash and GNU
> 23 # versions several other common system tools, like mkdir, tar, sed,
> 24 # etc.
> 25 #
> 26 ########################################################################
> 27 # Distribution policy:
> 28 #
> 29 # Public Domain, of course. No warranties at all: if it destroys your
> 30 # data or your marriage, it's your faul (or at least it's not MY fault).
> 31 #
> 32 # i ACTUALLY DO use this code in several source trees, so please send
> 33 # in improvements: http://wanderinghorse.net/computing/make/
> 34 #
> 35 ########################################################################
> 36 # Main features:
> 37 #
> 38 # - Rules for compiling C/C++ sources.
> 39 #
> 40 # - Rules for building binaries from C/C++ sources.
> 41 #
> 42 # - Rules for building shared/static libraries and binaries.
> 43 #
> 44 # - Rules for building a tarred or zipped distribution file.
> 45 #
> 46 # - Rules for installation of arbitrary file sets.
> 47 #
> 48 # - Transparent and automatic (and *fast*) dependencies generation for
> 49 # C/C++ files.
> 50 #
> 51 ########################################################################
> 52 # Ultra-quick usage overview:
> 53 #
> 54 # Include it from your Makefile like so:
> 55 #
> 56 # PACKAGE.NAME = single-token name of your project (e.g., libfoo)
> 57 # PACKAGE.VERSION = project version number, in an arbitrary format.
> 58 # include shake-n-make.mk
> 59 #
> 60 # Using spaces or special shell characters in .NAME and .VERSION may
> 61 # cause problems in this code.
> 62 #
> 63 # PACKAGE.* are used when building a distribution tarball.
> 64 #
> 65 # You can optionally define the following variables:
> 66 # CLEAN_FILES += list of files/dirs to delete during 'make clean'
> 67 # DISTCLEAN_FILES += list of files/dirs to delete during 'make distclean'
> 68 # PACKAGE.DIST_FILES += list of files/dirs to include in distribution
> 69 # tarball/zip file.
> 70 #
> 71 #
> 72 # Achtung:
> 73 # ***** Note the use of "+=" on several of those variables!!!! *****
> 74 # ***** BE CAREFUL WHEN USING [DIST]CLEAN_FILES!!!! *****
> 75 #
> 76 # The rules for bins, shared libs, etc., have to be installed by the
> 77 # user, but it's really simple. The subsections below explain how to
> 78 # use the various features.
> 79 #
> 80 # To install your package do:
> 81 #
> 82 # make install prefix=/install/prefix
> 83 #
> 84 # where $(prefix) normally defaults to /usr/local. (Setting prefix
> 85 # to $HOME is often useful.)
> 86 #
> 87 ########################################################################
> 88 # Compiling .o files from *.cpp and *.c:
> 89 #
> 90 # This file provides flexible rules for building these. See the %.o
> 91 # targets for all of the accepted flags.
> 92 #
> 93 ########################################################################
> 94 # Building a binary executable:
> 95 #
> 96 # mybin.BIN.OBJECTS = foo.o bar.o
> 97 # mybin.BIN.LDFLAGS = -ldl -lstdc++
> 98 # $(call ShakeNMake.CALL.RULES.BINS,mybin)
> 99 #
> 100 # You may pass an arbitrary number of binary names to CALL.RULES.BINS.
> 101 # See ShakeNMake.EVAL.RULES.BINS for the full list of configuration
> 102 # vars it accepts.
> 103 #
> 104 # A target named mybin.BIN is created for building the binary and
> 105 # $(mybin.BIN) holds the name of the compiled binary (typically
> 106 # "mybin").
> 107 #
> 108 ########################################################################
> 109 # Building a shared library:
> 110 #
> 111 # myDLL.DLL.OBJECTS = foo.o bar.o
> 112 # myDLL.LDFLAGS = -L/a/lib/path -lmylib
> 113 # $(call ShakeNMake.CALL.RULES.DLLS,myDLL)
> 114 #
> 115 # Like CALL.RULES.BINS, you may pass an arbitrary number of DLL names to
> 116 # CALL.RULES.DLLS. See EVAL.RULES.DLLS for the full list of configuration
> 117 # vars it accepts.
> 118 #
> 119 # A target named myDLL.DLL is created for building the library and
> 120 # $(myDLL.DLL) holds the name of the compiled DLL (typically
> 121 # "myDLL.so").
> 122 #
> 123 ########################################################################
> 124 # Building a static library is trival:
> 125 #
> 126 # myLib.LIB.OBJECTS = foo.o bar.o
> 127 # $(call ShakeNMake.CALL.RULES.LIBS,myLib)
> 128 #
> 129 # A target named myLib.LIB is created for building the library and
> 130 # $(myLib.LIB) holds the name of the compiled lib (typically
> 131 # "myLib.a").
> 132 #
> 133 ########################################################################
> 134 # Installing files:
> 135 #
> 136 # To install files you have to define "install sets", where a "set" is
> 137 # simply a collection of files which all have the same destination.
> 138 # An example:
> 139 #
> 140 # INSTALL.STUFF = $(myLib.LIB) $(myDLL.DLL) $(mybin.BIN)
> 141 # INSTALL.STUFF.DEST = $(prefix)/$(PACKAGE.NAME)
> 142 # $(call ShakeNMake.CALL.RULES.INSTALL,STUFF)
> 143 #
> 144 # That creates install rules named install-STUFF and uninstall-STUFF,
> 145 # which are called by the install and uninstall targets, respectively.
> 146 #
> 147 # You may pass an arbitrary number of install set names to
> 148 # CALL.RULES.INSTALL.
> 149 #
> 150 # Install notes:
> 151 #
> 152 # - There is nothing magical about the word STUFF: any single token can
> 153 # be used. If INSTALL.xxx.DEST is not set then [un]install-xxx will
> 154 # cause an error.
> 155 #
> 156 # - The installation rules are quite braindead, and simply use cp to
> 157 # install files.
> 158 #
> 159 ########################################################################
> 160 # Distribution tarball:
> 161 #
> 162 # To build a dist tarball:
> 163 #
> 164 # PACKAGE.DIST_FILES += list of files
> 165 #
> 166 # Then:
> 167 # make dist
> 168 #
> 169 # That builds a file named $(PACKAGE.NAME)-$(PACKAGE.VERSION).tar.gz and,
> 170 # if 'zip' is found in your $(PATH) then a zip file is created as well.
> 171 #
> 172 # Note that GNUmakefile and shake-n-make.make are added to DIST_FILES by
> 173 # default, so you don't need to add those. If shake-n-make.make is using
> 174 # mkdep.c to generate C/C++ dependencies than that file is also
> 175 # automatically included in the distribution.
> 176 #
> 177 # If you want to use a non-GNU tar or change the compression type
> 178 # (defaults to gzip) then change the ShakeNMake.TARBALL.FLAGS variable
> 179 # in this file (not in your Makefile).
> 180 # The ShakeNMake.TARBALL.XFLAGS is a special case var which has flags
> 181 # for tar which go AFTER the target filename. e.g. you can set it to
> 182 # --exclude=MySubDir to exclude files from a certain dir.
> 183 #
> 184 ########################################################################
> 185 # Eye candy:
> 186 #
> 187 # If you want the compiler/linker output to be less verbose, try:
> 188 #
> 189 # ShakeNMake.QUIET = 1
> 190 #
> 191 # BEFORE including this file.
> 192 #
> 193 ########################################################################
> 194 # Maintainer's/hacker's notes:
> 195 #
> 196 # Vars names starting with ShakeNMake are mostly internal to this
> 197 # makefile and are considered "private" unless documented otherwise.
> 198 # Notable exceptions are most of the ShakeNMake.CALL entries, which
> 199 # are $(call)able functions, and ShakeNMake.EVAL entries, which are
> 200 # $(eval)able code.
> 201 #
> 202 ########################################################################
> 203
> 204 ifneq (,$(COMSPEC))
> 205 $(warning Setting ShakeNMake.SMELLS.LIKE.WINDOWS to 1)
> 206 ShakeNMake.SMELLS.LIKE.WINDOWS := 1
> 207 ShakeNMake.EXTENSIONS.DLL = .DLL# maintenance reminder: this must stay upper-case!
> 208 ShakeNMake.EXTENSIONS.EXE = .EXE# maintenance reminder: this must stay upper-case!
> 209 else
> 210 ShakeNMake.SMELLS.LIKE.WINDOWS := 0
> 211 ShakeNMake.EXTENSIONS.DLL = .so
> 212 ShakeNMake.EXTENSIONS.EXE =# no whitespace, please
> 213 endif
> 214
> 215
> 216 ShakeNMake.MAKEFILE = shake-n-make.make
> 217 $(ShakeNMake.MAKEFILE):# avoid breaking some deps checks if someone renames this file (been there, done that)
> 218
> 219 ########################################################################
> 220 # Core information:
> 221
> 222 ifeq (,$(PACKAGE.NAME))
> 223 $(error You must set PACKAGE.NAME to a single-token name for your prject, e.g., libMyStuff)
> 224 endif
> 225
> 226 ifeq (,$(PACKAGE.VERSION))
> 227 $(error You must set PACKAGE.VERSION to a version number for your project, e.g., 1.3.5.7-beta9 or 1.0 or 2007-02-14)
> 228 endif
> 229
> 230
> 231 ########################################################################
> 232 # auto-add the makefiles to DIST_FILES, filtering out any which start
> 233 # with a dot because we use such files for temp/volitile files which
> 234 # contain Make rules (C/C++ deps, for example).
> 235 PACKAGE.MAKEFILE = $(firstword $(MAKEFILE_LIST))# normally either Makefile or GNUmakefile
> 236 $(PACKAGE.MAKEFILE):
> 237 PACKAGE.DIST_FILES += $(filter-out .%,$(MAKEFILE_LIST))
> 238
> 239
> 240 ShakeNMake.FORCE: ; @true
> 241
> 242 ########################################################################
> 243 # DESTDIR is for GNU Autotools compatibility...
> 244 DESTDIR ?=
> 245 prefix ?= /usr/local
> 246 ShakeNMake.INSTALL.DESTDIR = $(DESTDIR)
> 247 ShakeNMake.INSTALL.PREFIX = $(prefix)
> 248 ShakeNMake.INSTALL_ROOT=$(DESTDIR)$(prefix)/
> 249
> 250 ########################################################################
> 251 # ShakeNMake.CALL.FIND_BIN call()able function:
> 252 # $1 = app name
> 253 # $2 = optional path
> 254 ShakeNMake.CALL.FIND_BIN = $(firstword $(wildcard $(addsuffix /$(1),$(subst :, ,$(2) $(PATH)))))
> 255 ########################################################################
> 256
> 257 ########################################################################
> 258 # Find some common binaries...
> 259 ShakeNMake.BINS.TAR := $(call ShakeNMake.CALL.FIND_BIN,tar)
> 260 ifeq (,$(ShakeNMake.BINS.TAR))
> 261 ShakeNMake.BINS.TAR := $(call ShakeNMake.CALL.FIND_BIN,gtar)
> 262 endif
> 263 ShakeNMake.BINS.ZIP := $(call ShakeNMake.CALL.FIND_BIN,zip)
> 264 ShakeNMake.BINS.RM := $(call ShakeNMake.CALL.FIND_BIN,rm)
> 265 ShakeNMake.BINS.GCC := $(call ShakeNMake.CALL.FIND_BIN,gcc)
> 266 #
> 267 ########################################################################
> 268
> 269 ########################################################################
> 270 # An internal hack to enable "quiet" output. $(1) is a string which
> 271 # is shown ONLY if ShakeNMake.QUIET!=1
> 272 ShakeNMake.QUIET ?= 0
> 273 define ShakeNMake.CALL.SETX
> 274 if [[ x1 = "x$(ShakeNMake.QUIET)" ]]; then echo $(1); else set -x; fi
> 275 endef
> 276 ########################################################################
> 277
> 278 ########################################################################
> 279 # PACKAGE.DIST_FILES stuff...
> 280 ifeq (,$(ShakeNMake.BINS.TAR))
> 281 dist:
> 282 @echo "'tar' was not found in the PATH, so i cannot build a dist tarball :(."
> 283 else
> 284 ShakeNMake.TARBALL.FLAGS ?= czf
> 285 ShakeNMake.TARBALL.XFLAGS ?=
> 286 ShakeNMake.TARBALL.BASENAME = $(PACKAGE.NAME)-$(PACKAGE.VERSION)
> 287 ShakeNMake.TARBALL.FILE = $(ShakeNMake.TARBALL.BASENAME).tar.gz
> 288 ShakeNMake.TARBALL.ZIPFILE = $(ShakeNMake.TARBALL.BASENAME).zip
> 289 DISTCLEAN_FILES += $(ShakeNMake.TARBALL.FILE) $(ShakeNMake.TARBALL.ZIPFILE)
> 290
> 291 dist-cleanup:
> 292 @rm -fr $(ShakeNMake.TARBALL.BASENAME); true
> 293 dist-target-implementation:
> 294 @-if test -d $(ShakeNMake.TARBALL.BASENAME); then rm -fr $(ShakeNMake.TARBALL.BASENAME) || exit $$?; fi
> 295 @echo "Creating $(ShakeNMake.TARBALL.FILE)..."
> 296 @mkdir $(ShakeNMake.TARBALL.BASENAME)
> 297 @cp --parents -r $(PACKAGE.DIST_FILES) $(ShakeNMake.TARBALL.BASENAME)
> 298 @find $(ShakeNMake.TARBALL.BASENAME) -name CVS -o -name .svn -type d | xargs rm -fr; true
> 299 @$(ShakeNMake.BINS.TAR) \
> 300 $(ShakeNMake.TARBALL.FLAGS) \
> 301 $(ShakeNMake.TARBALL.FILE) \
> 302 $(ShakeNMake.TARBALL.XFLAGS) \
> 303 $(ShakeNMake.TARBALL.BASENAME)
> 304 @ls -la $(ShakeNMake.TARBALL.FILE)
> 305 ifneq (,$(ShakeNMake.BINS.ZIP))
> 306 @test -e $(ShakeNMake.TARBALL.ZIPFILE) && rm -f $(ShakeNMake.TARBALL.ZIPFILE); true
> 307 @$(ShakeNMake.BINS.ZIP) -q -r $(ShakeNMake.TARBALL.ZIPFILE) $(ShakeNMake.TARBALL.BASENAME)
> 308 @ls -la $(ShakeNMake.TARBALL.BASENAME).zip
> 309 endif
> 310 dist:
> 311 @$(MAKE) --no-print-directory dist-target-implementation; err=$$?; \
> 312 $(MAKE) --no-print-directory dist-cleanup; echo $$err
> 313 clean: dist-cleanup
> 314 endif # if $(ShakeNMake.BINS.TAR)
> 315 # end dist
> 316 ########################################################################
> 317
> 318
> 319 ########################################################################
> 320 # ShakeNMake.CALL.INSTALL: $(call)able function:
> 321 # $1 = destination dir.
> 322 # $2 = list of source files or directories
> 323 # Copies $2 to $1.
> 324 ShakeNMake.CALL.INSTALL = { \
> 325 test x = "x$(1)" && { echo "Install path is empty!"; exit 1; }; \
> 326 test -d $(1) || mkdir -p $(1) || exit; \
> 327 for x in $(2); do \
> 328 echo -e '\t--> '$$x; \
> 329 cp -rp $$x $(1) || { err=$$?; echo "Copy failed!"; exit $$?; }; \
> 330 done; \
> 331 }
> 332 # ShakeNMake.CALL.UNINSTALL: $(call)able function:
> 333 # $1 = destination dir.
> 334 # $2 = list of files (not directories!) to delete
> 335 # Removes $2 from $1. It tries to rmdir $1 when it is done, but that will
> 336 # only work if $1 is empty. Such a failure is silently ignored.
> 337 ShakeNMake.CALL.UNINSTALL = { \
> 338 test -d $(1) || exit 0; \
> 339 for x in $(2); do \
> 340 echo -e "\t<-- " $(1)/$$x; \
> 341 rm -f $(1)/$$x || exit; \
> 342 done; \
> 343 }; \
> 344 rmdir $(1) 2>/dev/null || true;
> 345 # Trivia: i actually did delete all files in my home dir
> 346 # once while testing the uninstall code. Luckily, -r wasn't
> 347 # in effect.
> 348 ########################################################################
> 349
> 350 ########################################################################
> 351 # install-% installs files listed in $(INSTALL.%) to $(INSTALL.%.DEST),
> 352 # which defaults to %. $(ShakeNMake.INSTALL.DESTDIR) is automatically
> 353 # prefixed to installation paths, for compatibility with the GNU
> 354 # Autotools and Debian-preferred install methods.
> 355 # When specifying install paths for client code, like so:
> 356 #
> 357 # INSTALL.MYSTUFF = list of files
> 358 # INSTALL.MYSTUFF.DEST = $(prefix)/$(PACKAGE.NAME)
> 359 #
> 360 # the path should always be relative to $(prefix), with the assumption that
> 361 # $(prefix) is an absolute path without a trailing backslash. The install
> 362 # path should NOT take into accont $(DESTDIR), because that is handled
> 363 # transparently at a deeper level.
> 364 ShakeNMake.CALL.GET_INSTALL_DEST = $(ShakeNMake.INSTALL.DESTDIR)$(INSTALL.$(1).DEST)
> 365 install-%:
> 366 @echo "Installing \$$(INSTALL.$(*)) files to $(call ShakeNMake.CALL.GET_INSTALL_DEST,$(*))..."; \
> 367 $(call ShakeNMake.CALL.INSTALL,$(call ShakeNMake.CALL.GET_INSTALL_DEST,$(*)),$(INSTALL.$(*)))
> 368 # Maintenance note: don't break the $(call) args onto separate lines because
> 369 # that introduces a whitespace char which can screw up the install code. :(
> 370 ########################################################################
> 371 # uninstall-% deletes INSTALL.% from INSTALL.%.DEST, which defaults to
> 372 # $(ShakeNMake.INSTALL_ROOT)%.
> 373 uninstall-%:
> 374 @echo "Uninstalling \$$(INSTALL.$(*)) files from $(call ShakeNMake.CALL.GET_INSTALL_DEST,$(*))..."; \
> 375 $(call ShakeNMake.CALL.UNINSTALL,$(call ShakeNMake.CALL.GET_INSTALL_DEST,$(*)),$(INSTALL.$(*)))
> 376 # Maintenance note: don't break the $(call) args onto separate lines because
> 377 # that introduces a whitespace char which can screw up the uninstall code. :(
> 378 ########################################################################
> 379
> 380 ########################################################################
> 381 # ShakeNMake.EVAL.RULES.INSTALL adds [un]install-$(1) as prerequisites
> 382 # of the [un]install targets, so that they get called by
> 383 # 'make [un]install'.
> 384 define ShakeNMake.EVAL.RULES.INSTALL
> 385 INSTALL.$(1).DEST ?= $(prefix)/$(1)
> 386 install: install-$(1)
> 387 uninstall: uninstall-$(1)
> 388 endef
> 389 ########################################################################
> 390 # $(call ShakeNMake.CALL.RULES.INSTALL,[list]) calls and $(eval)s
> 391 # ShakeNMake.EVAL.RULES.INSTALL one time for each item in $(1).
> 392 define ShakeNMake.CALL.RULES.INSTALL
> 393 $(foreach proggy,$(1),$(eval $(call ShakeNMake.EVAL.RULES.INSTALL,$(proggy))))
> 394 endef
> 395 # end ShakeNMake.EVAL.RULES.INSTALL
> 396 ########################################################################
> 397
> 398
> 399 ########################################################################
> 400 # builds %.o from %.c using $(CC).
> 401 # Passes on flags from these vars:
> 402 # CFLAGS, %.CFLAGS
> 403 # INCLUDES, %.OBJ.INCLUDES
> 404 # CPPFLAGS, %.OBJ.CPPFLAGS
> 405 %.o: %.c $(ShakeNMake.MAKEFILE) $(PACKAGE.MAKEFILE)
> 406 @$(call ShakeNMake.CALL.SETX,"CC [$@] ..."); \
> 407 $(CC) $(CFLAGS) $($(*).OBJ.CFLAGS) \
> 408 $(INCLUDES) $($(*).OBJ.INCLUDES) \
> 409 $(CPPFLAGS) $($(*).OBJ.CPPFLAGS) \
> 410 -c -o $@ $<
> 411 ########################################################################
> 412
> 413 ########################################################################
> 414 # build %.o from %.cpp using $(CXX).
> 415 # Passes on flags from these vars:
> 416 # CXXFLAGS, %.OBJ.CXXFLAGS
> 417 # INCLUDES, %.OBJ.INCLUDES
> 418 # CPPFLAGS, %.OBJ.CPPFLAGS
> 419 %.o: %.cpp $(ShakeNMake.MAKEFILE) $(PACKAGE.MAKEFILE)
> 420 @$(call ShakeNMake.CALL.SETX,"CXX [$@] ..."); \
> 421 $(CXX) $(CXXFLAGS) $($(*).OBJ.CXXFLAGS) \
> 422 $(INCLUDES) $($(*).OBJ.INCLUDES) \
> 423 $(CPPFLAGS) $($(*).OBJ.CPPFLAGS) \
> 424 -c -o $@ $<
> 425 # end %.o: %.cpp rules
> 426 ########################################################################
> 427
> 428
> 429 ########################################################################
> 430 # ShakeNMake.EVAL.RULES.BIN is intended to be called like so:
> 431 # $(eval $(call ShakeNMake.EVAL.RULES.BIN,MyApp))
> 432 #
> 433 # It builds a binary named $(1) by running $(CC) and passing it:
> 434 #
> 435 # INCLUDES, $(1).BIN.INCLUDES
> 436 # CFLAGS, $(1).BIN.CFLAGS
> 437 # CXXFLAGS, $(1).BIN.CXXFLAGS
> 438 # CPPFLAGS, $(1).BIN.CPPFLAGS
> 439 # LDFLAGS, $(1).BIN.LDFLAGS
> 440 # $(1).BIN.OBJECTS $(1).BIN.SOURCES
> 441 #
> 442 # Note that we have to pass both CFLAGS and CPPFLAGS because .SOURCES might
> 443 # contain either of C or C++ files.
> 444 define ShakeNMake.EVAL.RULES.BIN
> 445 $(1).BIN = $(1)$(ShakeNMake.EXTENSIONS.EXE)
> 446 $(1).BIN: $$($(1).BIN)
> 447 # Many developers feel that bins should not be cleaned by 'make
> 448 # clean', but instead by distclean, but i'm not one of those
> 449 # developers. i subscribe more to the school of thought that distclean
> 450 # is for cleaning up configure-created files. That said, shake-n-make
> 451 # isn't designed to use a configure-like process, so that is probably
> 452 # moot here and we probably (maybe?) should clean up bins only in
> 453 # distclean. As always: hack it to suit your preference:
> 454 CLEAN_FILES += $$($(1).BIN)
> 455 $$($(1).BIN): $$($(1).BIN.OBJECTS) $$($(1).BIN.SOURCES)
> 456 @test x = "x$$($(1).BIN.OBJECTS)$$($(1).BIN.SOURCES)" && { \
> 457 echo "$(1).BIN.OBJECTS and/or $(1).BIN.SOURCES is undefined!"; exit 1; }; \
> 458 $(call ShakeNMake.CALL.SETX,"CXX [$$@] ..."); \
> 459 $$(CXX) -o $$@ \
> 460 $$(INCLUDES) $$($(1).BIN.INCLUDES) \
> 461 $$(CFLAGS) $$($(1).BIN.CFLAGS) \
> 462 $$(CXXFLAGS) $$($(1).BIN.CXXFLAGS) \
> 463 $$(CPPFLAGS) $$($(1).BIN.CPPFLAGS) \
> 464 $$($(1).BIN.OBJECTS) $$($(1).BIN.SOURCES) \
> 465 $$(LDFLAGS) $$($(1).BIN.LDFLAGS)
> 466 # note about 'set -x': i do this because it normalizes backslashed
> 467 # newline, extra spaces, and other oddities of formatting.
> 468 endef
> 469 ########################################################################
> 470 # $(call ShakeNMake.CALL.RULES.BINS,[list]) calls and $(eval)s
> 471 # ShakeNMake.EVAL.RULES.BIN for each entry in $(1)
> 472 define ShakeNMake.CALL.RULES.BINS
> 473 $(foreach bin,$(1),$(eval $(call ShakeNMake.EVAL.RULES.BIN,$(bin))))
> 474 endef
> 475 # end ShakeNMake.CALL.RULES.BIN and friends
> 476 ########################################################################
> 477
> 478
> 479 ########################################################################
> 480 # ShakeNMake.EVAL.RULES.DLL builds builds $(1)$(ShakeNMake.EXTENSIONS.DLL) from object files
> 481 # defined by $(1).DLL.OBJECTS and $(1).DLL.SOURCES. Flags passed on
> 482 # to the linker include:
> 483 # LDFLAGS, $(1).DLL.LDFLAGS, LDADD, -shared -export-dynamic
> 484 # $(1).DLL.CPPFLAGS
> 485 #
> 486 # Also defines the var $(1).DLL, which expands to the filename of the DLL,
> 487 # (normally $(1)$(ShakeNMake.EXTENSIONS.DLL)).
> 488 define ShakeNMake.EVAL.RULES.DLL
> 489 $(1).DLL = $(1)$(ShakeNMake.EXTENSIONS.DLL)
> 490 ifneq (.DLL,$(ShakeNMake.EXTENSIONS.DLL))
> 491 $(1).DLL: $$($(1).DLL)
> 492 endif
> 493 CLEAN_FILES += $$($(1).DLL)
> 494 $$($(1).DLL): $$($(1).DLL.SOURCES) $$($(1).DLL.OBJECTS)
> 495 @test x = "x$$($(1).DLL.OBJECTS)$$($(1).DLL.SOURCES)" && { \
> 496 echo "$(1).DLL.OBJECTS and/or $(1).DLL.SOURCES are/is undefined!"; exit 1; }; \
> 497 $(call ShakeNMake.CALL.SETX,"CXX [$$@] ..."); \
> 498 $$(CXX) -o $$@ -shared -export-dynamic $$(LDFLAGS) \
> 499 $$($(1).DLL.LDFLAGS) $$($(1).DLL.OBJECTS) $$($(1).DLL.SOURCES) \
> 500 $$($(1).DLL.CPPFLAGS)
> 501 endef
> 502 ########################################################################
> 503 # $(call ShakeNMake.CALL.RULES.DLLS,[list]) calls and $(eval)s
> 504 # ShakeNMake.EVAL.RULES.DLL for each entry in $(1)
> 505 define ShakeNMake.CALL.RULES.DLLS
> 506 $(foreach dll,$(1),$(eval $(call ShakeNMake.EVAL.RULES.DLL,$(dll))))
> 507 endef
> 508 # end ShakeNMake.CALL.RULES.DLLS and friends
> 509 ########################################################################
> 510
> 511 ########################################################################
> 512 # ShakeNMake.EVAL.RULES.LIB creates rules to build static library
> 513 # $(1).a
> 514 define ShakeNMake.EVAL.RULES.LIB
> 515 $(1).LIB = $(1).a
> 516 $(1).LIB: $$($(1).LIB)
> 517 CLEAN_FILES += $$($(1).LIB)
> 518 $$($(1).LIB): $$($(1).LIB.OBJECTS)
> 519 @$(call ShakeNMake.CALL.SETX,"AR [$$@] ..."); \
> 520 $$(AR) crs $$@ $$($(1).LIB.OBJECTS)
> 521 endef
> 522 define ShakeNMake.CALL.RULES.LIBS
> 523 $(foreach liba,$(1),$(eval $(call ShakeNMake.EVAL.RULES.LIB,$(liba))))
> 524 endef
> 525 # end ShakeNMake.EVAL.RULES.LIB
> 526 ########################################################################
> 527
> 528 ########################################################################
> 529 # [DIST]CLEAN_FILES support...
> 530 ########################################################################
> 531 CLEAN_FILES += *.o
> 532 DISTCLEAN_FILES += *~
> 533 clean: ShakeNMake.FORCE
> 534 @fl="$(sort $(wildcard $(CLEAN_FILES)))"; \
> 535 test x = "x$$fl" && { echo "Nothing to clean!"; exit 0; }; \
> 536 $(call ShakeNMake.CALL.SETX,"Cleaning up ..."); \
> 537 $(ShakeNMake.BINS.RM) -fr $$fl
> 538 distclean: ShakeNMake.FORCE
> 539 @fl="$(sort $(wildcard $(CLEAN_FILES) $(DISTCLEAN_FILES)))"; \
> 540 test x = "x$$fl" && { echo "Nothing to clean!"; exit 0; }; \
> 541 $(call ShakeNMake.CALL.SETX,"Cleaning up ..."); \
> 542 $(ShakeNMake.BINS.RM) -fr $$fl
> 543 # end [DIST]CLEAN_FILES
> 544 ########################################################################
> 545
> 546
> 547 ########################################################################
> 548 # Automatic dependencies generation for C/C++ code...
> 549 # To disable deps generation, set ShakeNMake.USE_MKDEPS=0 *before*
> 550 # including this file.
> 551 ifeq (,$(ShakeNMake.BINS.GCC))
> 552 ShakeNMake.USE_MKDEPS ?= 0
> 553 else
> 554 ShakeNMake.USE_MKDEPS ?= 1
> 555 endif
> 556 #$(warning ShakeNMake.USE_MKDEPS=$(ShakeNMake.USE_MKDEPS));
> 557 ifeq (1,$(ShakeNMake.USE_MKDEPS))
> 558 ShakeNMake.CISH_SOURCES += $(wildcard *.cpp *.c *.c++ *.h *.hpp *.h++ *.hh)
> 559 #$(warning ShakeNMake.CISH_SOURCES=$(ShakeNMake.CISH_SOURCES))
> 560 ifneq (,$(ShakeNMake.CISH_SOURCES))
> 561 ShakeNMake.CISH_DEPS_FILE := .make.c_deps
> 562 ShakeNMake.BINS.MKDEP = gcc -E -MM $(INCLUDES)
> 563 CLEAN_FILES += $(ShakeNMake.CISH_DEPS_FILE)
> 564 $(ShakeNMake.CISH_DEPS_FILE): $(PACKAGE.MAKEFILE) $(ShakeNMake.MAKEFILE) $(ShakeNMake.CISH_SOURCES)
> 565 @$(ShakeNMake.BINS.MKDEP) $(ShakeNMake.CISH_SOURCES) 2>/dev/null > $@ || \
> 566 $(ShakeNMake.BINS.RM) -f $@ 2>/dev/null
> 567 # ^^^^ We rm -f the deps file if mkdep fails because we don't want a bad generated makefile
> 568 # to kill the build.
> 569
> 570 ifneq (,$(strip $(filter distclean clean,$(MAKECMDGOALS))))
> 571 #$(warning Skipping C/C++ deps generation.)
> 572 ABSOLUTEBOGO := $(shell $(ShakeNMake.BINS.RM) -f $(ShakeNMake.CISH_DEPS_FILE))
> 573 else
> 574 #$(warning Including C/C++ deps.)
> 575 -include $(ShakeNMake.CISH_DEPS_FILE)
> 576 endif
> 577
> 578 endif
> 579 # ^^^^ ifneq(,$(ShakeNMake.CISH_SOURCES))
> 580 endif
> 581 # ^^^^ end $(ShakeNMake.USE_MKDEPS)
> 582 ########################################################################
> 583
> 584 ########################################################################
> 585 # Doxygen
> 586 ShakeNMake.BINS.SED := $(call ShakeNMake.CALL.FIND_BIN,sed)
> 587 ShakeNMake.BINS.PERL := $(call ShakeNMake.CALL.FIND_BIN,perl)
> 588 ShakeNMake.BINS.LATEX := $(call ShakeNMake.CALL.FIND_BIN,latex)
> 589 ifneq (,$(ShakeNMake.BINS.PERL))
> 590 ShakeNMake.BINS.DOXYGEN := $(call ShakeNMake.CALL.FIND_BIN,doxygen)
> 591 ifneq (,$(ShakeNMake.BINS.DOXYGEN))
> 592 ShakeNMake.DOXYGEN.DOXYFILE_TEMPLATE = Doxyfile.at
> 593 ifneq (,$(wildcard $(ShakeNMake.DOXYGEN.DOXYFILE_TEMPLATE)))
> 594 ShakeNMake.DOXYGEN.INDEX := $(wildcard Doxygen-index.txt)
> 595 #ifneq (,$(wildcard $(ShakeNMake.DOXYGEN.INDEX)))
> 596 ########################################################################
> 597 # let's try to do doxygen stuff...
> 598 ########################################################################
> 599 # Set ShakeNMake.DOXYGEN.USE_DOT to 1 if you have 'dot' and want to
> 600 # use it in the doxygen stuff. It slows down the doc gen process
> 601 # significantly, but it looks nice.
> 602 ShakeNMake.DOXYGEN.USE_DOT ?= 0
> 603 ##################################################
> 604 PACKAGE.DIST_FILES += $(ShakeNMake.DOXYGEN.DOXYFILE_TEMPLATE) $(ShakeNMake.DOXYGEN.INDEX)
> 605
> 606 ShakeNMake.DOXYGEN.INCLUDE_DIRS = .
> 607
> 608 ShakeNMake.DOXYGEN.OUTPUT_DIR.HTML = $(PACKAGE.NAME)-$(PACKAGE.VERSION)-doxygen-html
> 609 ShakeNMake.DOXYGEN.OUTPUT_DIR.LATEX = $(PACKAGE.NAME)-$(PACKAGE.VERSION)-doxygen-latex
> 610
> 611
> 612 ifneq (,$(ShakeNMake.BINS.LATEX))
> 613 ShakeNMake.DOXYGEN.GENERATE_LATEX ?= YES
> 614 else
> 615 ShakeNMake.DOXYGEN.GENERATE_LATEX ?= NO
> 616 endif
> 617
> 618 Doxyfile: $(ShakeNMake.DOXYGEN.DOXYFILE_TEMPLATE) $(ShakeNMake.MAKEFILE) $(PACKAGE.MAKEFILE)
> 619 @$(ShakeNMake.BINS.SED) -e 's,@PACKAGE_NAME@,$(PACKAGE.NAME),' \
> 620 -e 's,@PACKAGE_VERSION@,$(PACKAGE.VERSION),' \
> 621 -e 's,@DOXYGEN_INPUT@,$(ShakeNMake.DOXYGEN.INDEX) $(ShakeNMake.DOXYGEN.INCLUDE_DIRS),' \
> 622 -e 's,@HTML_OUTPUT@,$(ShakeNMake.DOXYGEN.OUTPUT_DIR.HTML),' \
> 623 -e 's,@LATEX_OUTPUT@,$(ShakeNMake.DOXYGEN.OUTPUT_DIR.LATEX),' \
> 624 -e 's,@GENERATE_LATEX@,$(ShakeNMake.DOXYGEN.GENERATE_LATEX),' \
> 625 -e 's,@PERL@,$(ShakeNMake.BINS.PERL),' \
> 626 -e 's,@USE_DOT@,$(ShakeNMake.DOXYGEN.USE_DOT),' \
> 627 < $(ShakeNMake.DOXYGEN.DOXYFILE_TEMPLATE) > $@
> 628
> 629 doxygen-clean:
> 630 @test -d $(ShakeNMake.DOXYGEN.OUTPUT_DIR.HTML) && rm -fr $(ShakeNMake.DOXYGEN.OUTPUT_DIR.HTML); \
> 631 rm -f Doxyfile; \
> 632 true
> 633
> 634 .PHONY: $(ShakeNMake.DOXYGEN.OUTPUT_DIR.HTML)
> 635 $(ShakeNMake.DOXYGEN.OUTPUT_DIR.HTML): Doxyfile
> 636 @echo "Building docs from headers"
> 637 $(ShakeNMake.BINS.DOXYGEN)
> 638 @echo "Output should be in the directory '$@'."
> 639 ifneq (NO,$(ShakeNMake.DOXYGEN.GENERATE_LATEX))
> 640 @echo "Latex output (if any) is in '$(ShakeNMake.DOXYGEN.OUTPUT_DIR.LATEX)'."
> 641 endif
> 642
> 643 doxygen: $(ShakeNMake.DOXYGEN.OUTPUT_DIR.HTML)
> 644
> 645 doxygen-dist: doxygen
> 646 tar czf $(ShakeNMake.DOXYGEN.OUTPUT_DIR.HTML).tar.gz $(ShakeNMake.DOXYGEN.OUTPUT_DIR.HTML)
> 647 @ls -la $(ShakeNMake.DOXYGEN.OUTPUT_DIR.HTML).tar.gz
> 648
> 649 dist: doxygen-dist
> 650 CLEAN_FILES += Doxyfile $(ShakeNMake.DOXYGEN.OUTPUT_DIR.HTML) latex
> 651
> 652
> 653 INSTALL_DOXYGEN = $(ShakeNMake.DOXYGEN.OUTPUT_DIR.HTML)
> 654 INSTALL_DOXYGEN_DEST = $(prefix)/share/doc/$(PACKAGE.NAME)
> 655 install: doxygen
> 656 $(call ShakeNMake.CALL.RULES.INSTALL,DOXYGEN)
> 657
> 658 ##################################################
> 659 #endif # $(ShakeNMake.DOXYGEN.INDEX)
> 660 endif # $(ShakeNMake.DOXYGEN.DOXYFILE_TEMPLATE)
> 661 endif # $(ShakeNMake.BINS.DOXYGEN)
> 662 endif # $(ShakeNMake.BINS.PERL)
> 663 # end Doxygen
> 664 ########################################################################
> 665

Added test.c

Old () New (c4bd24e3ba1c76f7)
> 1 #include <stdio.h>
> 2 #include <stdlib.h>
> 3 #include <string.h>
> 4 #include <ctype.h>
> 5 #include "whrc.h"
> 6
> 7 #if 1
> 8 #include <stdio.h> // only for debuggering
> 9 #define MARKER if(1) printf("MARKER: %s:%d:%s():\n",__FILE__,__LINE__,__func__); if(1) printf
> 10 #else
> 11 static void bogo_printf(char const * fmt, ...) {}
> 12 #define MARKER if(0) bogo_printf
> 13 #endif
> 14
> 15 int test_one()
> 16 {
> 17 MARKER("starting test\n");
> 18 whrc_context * cx = whrc_create_context();
> 19 #define SLEN 20
> 20 char * str = (char *)malloc(SLEN+1);
> 21 memset( str, 'x', SLEN );
> 22 str[SLEN] = 0;
> 23 #undef SLEN
> 24 whrc_register( cx, str, free );
> 25 size_t rc = whrc_refcount(cx,str); // rc == 1
> 26 MARKER("refcount=%u\n",rc);
> 27 rc = whrc_addref(cx,str); //rc == 2
> 28 MARKER("refcount=%u\n",rc);
> 29 rc = whrc_rmref(cx,str); // rc == 1
> 30 MARKER("refcount=%u\n",rc);
> 31 rc = whrc_rmref(cx,str); // rc == 0, free(str) is called
> 32 MARKER("refcount=%u\n",rc);
> 33 rc = whrc_rmref(cx,str); // rc == 0, free(str) is called
> 34 MARKER("refcount=%u\n",rc);
> 35 //whrc_destroy_context(cx,true);
> 36 MARKER("ending test\n");
> 37 return 0;
> 38 }
> 39
> 40 int main( int argc, char ** argv )
> 41 {
> 42 int rc = 0;
> 43 rc = test_one();
> 44 printf("Done rc=%d=[%s].\n",rc,
> 45 (0==rc)
> 46 ? "You win :)"
> 47 : "You lose :(");
> 48 return rc;
> 49 }

Added whgc.c

Old () New (d5dd771fe2deaaa7)
> 1 #include <stdlib.h>
> 2 #include <string.h> /* memset() */
> 3 #include "whgc.h"
> 4 #include "whhash.h"
> 5
> 6 /**
> 7 Partial list of changes changes by Stephan Beal:
> 8
> 9 - from unsigned int to unsigned long for hash keys.
> 10
> 11 - created typedef whhash_val_t.
> 12
> 13 - Added whhash_hash_cstring_{djb2,sdbm}() algorithms,
> 14 taken from: http://www.cse.yorku.ca/~oz/hash.html
> 15
> 16 - whhash_iter() now returns 0 if the whhash_table
> 17 is empty, simplifying iteration error checking a bit.
> 18
> 19 - The API is now const-safe, insofar as feasible. That is, funcs
> 20 which can get away with (void const *) instead of (void*) now use
> 21 const parameters.
> 22
> 23 - Added ability to set a custom key/value dtors for each
> 24 whhash_table.
> 25
> 26 - Slightly changed semantics of some routines to accommodate the
> 27 dtor handling.
> 28
> 29 - Default dtor for keys is now null instead of free.
> 30 */
> 31
> 32
> 33
> 34 #include <stdio.h> /* only for debuggering. */
> 35
> 36 #if defined(__cplusplus)
> 37 extern "C" {
> 38 # include <cassert>
> 39 # define ARG_UNUSED(X)
> 40 #else
> 41 # include <assert.h>
> 42 # define ARG_UNUSED(X) X
> 43 #endif /* __cplusplus */
> 44
> 45 #if 1
> 46 #define MARKER printf("**** MARKER: %s:%d:%s():\n",__FILE__,__LINE__,__func__);
> 47 #else
> 48 #define MARKER printf("**** MARKER: %s:%d:\n",__FILE__,__LINE__);
> 49 #endif
> 50
> 51 /**
> 52 Define the constants for the whgc_events object...
> 53 */
> 54 const whgc_events_t whgc_events = {
> 55 1, /* registered */
> 56 2, /* unregistered */
> 57 3, /* destructing_item */
> 58 4, /* destructing_context */
> 59 10 /* last_event_id */
> 60 };
> 61
> 62 /**
> 63 Holder for generic gc data.
> 64 */
> 65 struct whgc_gc_entry
> 66 {
> 67 void * key;
> 68 void * value;
> 69 whgc_dtor_f keyDtor;
> 70 whgc_dtor_f valueDtor;
> 71 struct whgc_gc_entry * left;
> 72 struct whgc_gc_entry * right;
> 73 };
> 74 typedef struct whgc_gc_entry whgc_gc_entry;
> 75 #define WHGC_GC_ENTRY_INIT {0,0,0,0,0,0}
> 76 static const whgc_gc_entry whgc_gc_entry_init = WHGC_GC_ENTRY_INIT;
> 77
> 78 #define WHGC_STATS_INIT {\
> 79 0, /* entry_count */ \
> 80 0, /* reg_count */ \
> 81 0, /* unreg_count */ \
> 82 0 /* alloced */ \
> 83 }
> 84 static const whgc_stats whgc_stats_init = WHGC_STATS_INIT;
> 85
> 86 struct whgc_listener
> 87 {
> 88 struct whgc_listener * next;
> 89 whgc_listener_f func;
> 90 };
> 91 typedef struct whgc_listener whgc_listener;
> 92 #define WHGC_LISTENER_INIT {0,0}
> 93 static const whgc_listener whgc_listener_init = WHGC_LISTENER_INIT;
> 94
> 95 struct whgc_context
> 96 {
> 97 void const * client;
> 98 /**
> 99 Hashtable of (void*) to (whgc_gc_entry*)
> 100 */
> 101 whhash_table * ht;
> 102 /**
> 103 Holds the right-most (most recently added) entry. A cleanup,
> 104 the list is walked leftwards to free the entries in reverse
> 105 order.
> 106 */
> 107 whgc_gc_entry * current;
> 108 /**
> 109 Event listeners.
> 110 */
> 111 whgc_listener * listeners;
> 112 /**
> 113 Informal stats.
> 114 */
> 115 whgc_stats stats;
> 116 };
> 117
> 118 #define WHGC_CONTEXT_INIT {\
> 119 0,/*client*/ \
> 120 0,/*ht*/ \
> 121 0,/*current*/ \
> 122 0,/*listeners*/ \
> 123 WHGC_STATS_INIT}
> 124 static const whgc_context whgc_context_init = WHGC_CONTEXT_INIT;
> 125
> 126 /**
> 127 A destructor for use with the hashtable API. Calls
> 128 whhash_destroy((whhash_table*)k).
> 129 static void whgc_free_hashtable( void * k )
> 130 {
> 131 //MARKER; printf("Freeing HASHTABLE @%p\n",k);
> 132 whhash_destroy( (whhash_table*)k );
> 133 }
> 134 */
> 135
> 136 /**
> 137 A logging version of free().
> 138 */
> 139 static void whgc_free( void * k )
> 140 {
> 141 //MARKER; printf("Freeing GENERIC (void*) @%p\n",k);
> 142 free(k);
> 143 }
> 144
> 145 void * whgc_alloc( whgc_context * cx, size_t size, whgc_dtor_f dtor )
> 146 {
> 147 void * ret = size ? malloc( size ) : 0;
> 148 if( ! ret ) return 0;
> 149 memset( ret, 0, size );
> 150 if( !cx ) return ret;
> 151 cx->stats.alloced += size;
> 152 whgc_add( cx, ret, dtor );
> 153 return ret;
> 154 }
> 155
> 156 void const * whgc_get_context_client(whgc_context const *cx)
> 157 {
> 158 return cx ? cx->client : 0;
> 159 }
> 160 /**
> 161 Calls all registered listeners with the given
> 162 parameters.
> 163 */
> 164 static void whgc_fire_event( whgc_context const *cx,
> 165 short ev,
> 166 void const * key,
> 167 void const * val )
> 168 {
> 169 //MARKER;printf("Firing event %d for cx @%p\n",ev,cx);
> 170 whgc_listener * l = cx ? cx->listeners : 0;
> 171 if( l )
> 172 {
> 173 whgc_event E;
> 174 E.cx = cx;
> 175 E.type = ev;
> 176 E.key = key;
> 177 E.value = val;
> 178 while( l )
> 179 {
> 180 if( l->func )
> 181 {
> 182 //MARKER;printf("Firing @%p(cx=@%p,event=%d,key=@%p,val=@%p)\n",l->func,cx,ev,key,val);
> 183 l->func( &E );
> 184 }
> 185 l = l->next;
> 186 }
> 187 }
> 188 }
> 189
> 190 /**
> 191 Destructor for use with the hashtable API. Frees
> 192 whgc_gc_entry objects by calling the assigned
> 193 dtors and then calling free(e).
> 194
> 195 The caller is expected to relink e's neighbors himself if needed
> 196 before calling this function.
> 197
> 198 The cx parameter is only used for firing a
> 199 whgc_event_destructing_item event.
> 200 */
> 201 static void whgc_free_gc_entry( whgc_context * cx,
> 202 whgc_gc_entry * e )
> 203 {
> 204 if( ! e ) return;
> 205 if( cx ) whgc_fire_event( cx, whgc_events.destructing_item, e->key, e->value );
> 206 //MARKER;printf("Freeing GC item e[@%p]: key=[%p(@%p)] val[%p(@%p)]]\n",e,e->keyDtor,e->key,e->valueDtor,e->value);
> 207 if( e->valueDtor )
> 208 {
> 209 //MARKER;printf("dtor'ing GC value %p( @%p )\n",e->valueDtor, e->value);
> 210 e->valueDtor(e->value);
> 211 }
> 212 if( e->keyDtor )
> 213 {
> 214 //MARKER;printf("dtor'ing GC key %p( @%p )\n",e->keyDtor, e->key);
> 215 e->keyDtor(e->key);
> 216 }
> 217 if( cx ) cx->stats.alloced -= sizeof(whgc_gc_entry);
> 218 whgc_free(e);
> 219 }
> 220
> 221 /**
> 222 A no-op "destructor" to assist in tracking down destructions.
> 223 */
> 224 static void whgc_free_noop( void * ARG_UNUSED(v) )
> 225 {
> 226 //MARKER;printf("dtor no-op @%p\n",v);
> 227 }
> 228
> 229 static whhash_table * whgc_hashtable( whgc_context * cx )
> 230 {
> 231 if( ! cx ) return 0;
> 232 if( cx->ht ) return cx->ht;
> 233 if( (cx->ht = whhash_create( 10, whhash_hash_void_ptr, whhash_cmp_void_ptr ) ) )
> 234 {
> 235 whhash_set_dtors( cx->ht, whgc_free_noop, whgc_free_noop );
> 236 /**
> 237 We use no-op dtors so we can log the destruction process, but cx->ht
> 238 does not own anything. Because we need predictable destruction order,
> 239 we manage a list of entries and destroy them in reverse order.
> 240 */
> 241 /* Reminder: we don't update cx->stats.alloced here. We accommodate the
> 242 Hashtable size in whgc_get_stats().
> 243 */
> 244 }
> 245 return cx->ht;
> 246 }
> 247
> 248 whgc_context * whgc_create_context( void const * clientContext )
> 249 {
> 250 whgc_context * cx = (whgc_context *)malloc(sizeof(whgc_context));
> 251 if( ! cx ) return 0;
> 252 *cx = whgc_context_init;
> 253 cx->stats.alloced += sizeof(whgc_context);
> 254 cx->client = clientContext;
> 255 return cx;
> 256 }
> 257
> 258 bool whgc_add_listener( whgc_context *cx, whgc_listener_f f )
> 259 {
> 260 if( ! cx || !f ) return false;
> 261 //MARKER;printf("Adding listener @%p() to cx @%p\n",f,cx);
> 262 whgc_listener * l = (whgc_listener *)
> 263 whgc_alloc( cx, sizeof(whgc_listener), whgc_free_noop );
> 264 //malloc(sizeof(whgc_listener));
> 265 if( ! l ) return 0;
> 266 *l = whgc_listener_init;
> 267 l->func = f;
> 268 //whgc_add( cx, l, whgc_free_noop );
> 269 whgc_listener * L = cx->listeners;
> 270 if( L )
> 271 {
> 272 while( L->next ) L = L->next;
> 273 L->next = l;
> 274 }
> 275 else
> 276 {
> 277 cx->listeners = l;
> 278 }
> 279 return true;
> 280 }
> 281
> 282 bool whgc_register( whgc_context * cx,
> 283 void * key, whgc_dtor_f keyDtor,
> 284 void * value, whgc_dtor_f valDtor )
> 285 {
> 286 if( !key || !whgc_hashtable(cx) || (0 != whhash_search(cx->ht, key)) )
> 287 {
> 288 return false;
> 289 }
> 290 whgc_gc_entry * e = (whgc_gc_entry*)whgc_alloc( 0, sizeof(whgc_gc_entry), 0 );
> 291 if( ! e ) return false;
> 292 cx->stats.alloced += sizeof(whgc_gc_entry);
> 293 *e = whgc_gc_entry_init;
> 294 e->key = key;
> 295 e->keyDtor = keyDtor;
> 296 e->value = value;
> 297 e->valueDtor = valDtor;
> 298 //MARKER;printf("Registering GC item e[@%p]: key[@%p]/%p() = val[@%p]/%p()\n",e,e->key,e->keyDtor,e->value,e->valueDtor);
> 299 whhash_insert( cx->ht, key, e );
> 300 ++(cx->stats.reg_count);
> 301 cx->stats.entry_count = whhash_count(cx->ht);
> 302 if( cx->current )
> 303 {
> 304 e->left = cx->current;
> 305 cx->current->right = e;
> 306 }
> 307 cx->current = e;
> 308 whgc_fire_event( cx, whgc_events.registered, e->key, e->value );
> 309 return true;
> 310 }
> 311
> 312 bool whgc_add( whgc_context * cx, void * key, whgc_dtor_f keyDtor )
> 313 {
> 314 return whgc_register( cx, key, keyDtor, key, 0 );
> 315 }
> 316
> 317 void * whgc_unregister( whgc_context * cx, void * key )
> 318 {
> 319 if( ! cx || !cx->ht || !key ) return 0;
> 320 whgc_gc_entry * e = (whgc_gc_entry*)whhash_take( cx->ht, key );
> 321 void * ret = e ? e->value : 0;
> 322 if( e )
> 323 {
> 324 cx->stats.entry_count = whhash_count(cx->ht);
> 325 ++(cx->stats.unreg_count);
> 326 if( e->left ) e->left->right = e->right;
> 327 if( e->right ) e->right->left = e->left;
> 328 if( cx->current == e ) cx->current = (e->right ? e->right : e->left);
> 329 /**
> 330 ^^^ this is pedantic. In theory cx->current must always be
> 331 the right-most entry, so we could do: cx->current=e->left;
> 332 */
> 333 void * k = e->key;
> 334 void * v = e->value;
> 335 whgc_free(e);
> 336 cx->stats.alloced -= sizeof(whgc_gc_entry);
> 337 whgc_fire_event( cx, whgc_events.unregistered, k, v );
> 338 }
> 339 return ret;
> 340 }
> 341
> 342 void * whgc_search( whgc_context const * cx, void const * key )
> 343 {
> 344 if( ! cx || !key || !cx->ht ) return 0;
> 345 whgc_gc_entry * e = (whgc_gc_entry*)whhash_search( cx->ht, key );
> 346 return e ? e->value : 0;
> 347 }
> 348
> 349 void whgc_clear_context( whgc_context * cx )
> 350 {
> 351 if( ! cx ) return;
> 352 //MARKER;printf("Cleaning up %u GC entries...\n",cx->stats.entry_count);
> 353 if( cx->ht )
> 354 {
> 355 //MARKER;printf("Cleaning up %u GC entries...\n",whhash_count(cx->ht));
> 356 whhash_clear( cx->ht );
> 357 }
> 358 /**
> 359 Destroy registered entries in reverse order of
> 360 their registration.
> 361 */
> 362 whgc_gc_entry * e = cx->current;
> 363 while( e )
> 364 {
> 365 whgc_fire_event( cx, whgc_events.unregistered, e->key, e->value ); /* a bit of a kludge, really. */
> 366 //MARKER;printf("Want to clean up @%p\n",e);
> 367 whgc_gc_entry * left = e->left;
> 368 whgc_free_gc_entry(cx,e);
> 369 e = left;
> 370 }
> 371 cx->stats.entry_count = 0;
> 372 cx->current = 0;
> 373 }
> 374 void whgc_destroy_context( whgc_context * cx )
> 375 {
> 376 if( ! cx ) return;
> 377 #if 0
> 378 {
> 379 whhash_stats s = whhash_get_stats( cx->ht );
> 380 MARKER;printf("GC context search collisions: %u\n",s.search_collisions);
> 381 }
> 382 #endif
> 383 whgc_fire_event( cx, whgc_events.destructing_context, 0, 0 );
> 384 whgc_clear_context( cx );
> 385 if( cx->ht )
> 386 {
> 387 whhash_destroy( cx->ht );
> 388 cx->ht = 0;
> 389 }
> 390
> 391 cx->client = 0; /* needs to stay valid until all events are fires, in case clients use it as a lookup key (i do) */
> 392 whgc_listener * L = cx->listeners;
> 393 cx->listeners = 0;
> 394 while( L )
> 395 {
> 396 whgc_listener * l = L->next;
> 397 cx->stats.alloced -= sizeof(whgc_listener);
> 398 whgc_free(L);
> 399 L = l;
> 400 }
> 401 whgc_free(cx);
> 402 }
> 403
> 404 void whgc_context_dtor( void * p )
> 405 {
> 406 whgc_destroy_context( (whgc_context*)p );
> 407 }
> 408
> 409 whgc_stats whgc_get_stats( whgc_context const * cx )
> 410 {
> 411 whgc_stats s = cx ? cx->stats : whgc_stats_init;
> 412 if( ! cx ) return s;
> 413 whhash_stats const hs = whhash_get_stats( cx->ht );
> 414 s.alloced += hs.alloced;
> 415 return s;
> 416 }
> 417
> 418 #if defined(__cplusplus)
> 419 } /* extern "C" */
> 420 #endif

Added whgc.h

Old () New (78d0261b2af5fcdd)
> 1 #ifndef WANDERINGHORSE_NET_WHGC_H_INCLUDED
> 2 #define WANDERINGHORSE_NET_WHGC_H_INCLUDED
> 3
> 4 #include <stdarg.h>
> 5 #include <stddef.h>
> 6 #ifdef __cplusplus
> 7 extern "C" {
> 8 #endif
> 9
> 10 #ifndef __cplusplus
> 11 # ifndef WHGC_HAVE_STDBOOL
> 12 # define WHGC_HAVE_STDBOOL 0
> 13 # endif
> 14
> 15 # if defined(WHGC_HAVE_STDBOOL) && !(WHGC_HAVE_STDBOOL)
> 16 # if !defined(bool)
> 17 # define bool char
> 18 # endif
> 19 # if !defined(true)
> 20 # define true 1
> 21 # endif
> 22 # if !defined(false)
> 23 # define false 0
> 24 # endif
> 25 # else /* aha! stdbool.h! C99. */
> 26 # include <stdbool.h>
> 27 # endif /* WHGC_HAVE_STDBOOL */
> 28 #endif /* __cplusplus */
> 29
> 30 /*!
> 31 @page whgc_page_main whgc: Garbage Collection lib for C
> 32
> 33 @section whgc_sec_about_whgc About whgc
> 34
> 35 whgc (the WanderingHorse.net Garbage Collector) is a simple garbage
> 36 collector (GC) for C.
> 37
> 38 Author: Stephan Beal (http://wanderinghorse.net/home/stephan)
> 39
> 40 License: the core library is Public Domain, but some of the
> 41 borrowed utility code is released under a BSD license (see
> 42 whhash.{c,h} for details).
> 43
> 44 Home page: whgc is part of the pegc project:
> 45 http://fossil.wanderinghorse.net/repos/pegc/
> 46
> 47 whgc is a small garbage collection library for C. The library
> 48 provides context objects to which one can attach arbitrary
> 49 data (via void pointers) and arbitrary destructor functions.
> 50 When a GC context is destroyed, the destructors are called
> 51 for each attached item.
> 52
> 53 The original use case for whgc was a parser toolkit which
> 54 needed to allocate dynamic resources while generating a
> 55 grammar. By attaching the dynamic data to the parser's GC, we
> 56 eliminated a number of headaches involved with ownership of the
> 57 dynamic data. It also simplified many lookup operations when we
> 58 needed to find shared data.
> 59
> 60 @section set_features Features and Misfeatures
> 61
> 62 The main feature of whgc are:
> 63
> 64 - It allows one to bind key/value pairs to a context
> 65 object. When that object is destroyed, client-defined
> 66 destructor functions will be called on for all of the keys and
> 67 values. This greatly simplifies management and ownership of
> 68 dynamically allocated memory for some use cases.
> 69
> 70 - It supports event listeners, to assist in debugging the
> 71 lifetimes of GC'd objects.
> 72
> 73 - Aside from GC, it's sometimes useful as a general-purposes lookup
> 74 table (a hashtable of (key=void *,value=void *)).
> 75
> 76 @section whgc_sec_example Example
> 77
> 78 An exceedingly simple example of using it:
> 79
> 80 @code
> 81 whgc_context * cx = whgc_create_context( 0 );
> 82 if( ! cx ) { ... error, probably OOM ... }
> 83
> 84 int * i = (int*)malloc(sizeof(int));
> 85 *i = 42;
> 86 whgc_add( i, free );
> 87
> 88 struct mystruct * my = (struct mystruct*)malloc(sizeof(struct mystruct));
> 89 my->foo = "hi";
> 90 my->bar = 42.42;
> 91 // Assume this function exists:
> 92 // void mystruct_dtor(void*);
> 93 // and that it deallocates mystruct objects.
> 94 whgc_add( cx, my, mystruct_dtor );
> 95
> 96 ...
> 97
> 98 whgc_destroy_context( cx );
> 99 // now all items added to the context are destroyed using the
> 100 // given destructor callbacks. They are destroyed in the reverse
> 101 // order they were added (LIFO).
> 102 @endcode
> 103
> 104 Aside from whgc_add(), there is the more flexible
> 105 whgc_register(), which allows one to register key/value pairs,
> 106 and separate destructors for each key and value. The lookup key
> 107 can be used with whgc_search() to find the mapped data.
> 108
> 109 Assigning a lookup key to data is often useful when we have
> 110 some common handle type we're passing around but need to
> 111 associated dynamically-allocated objects to them. The GC approach
> 112 allows us to transfer ownership to the GC context, so that we can
> 113 map arbitrary data to an arbitrary object and not have to worry
> 114 about whether or not that memory will be deallocated later.
> 115
> 116 @section whgc_sec_threadsafety Thread safety
> 117
> 118 By default it is never legal to use the same context from
> 119 multiple threads at the same time. That said, the client may
> 120 use their own locking to serialize access. All API functions
> 121 which take a (whgc_context const *) argument require only a
> 122 read lock, whereas those taking a (whgc_context*) argument
> 123 require a exclusive (read/write) access. whgc_create_context()
> 124 is reentrant and does not need to be locked.
> 125 */
> 126
> 127 /** @struct whgc_context
> 128
> 129 whgc_context is an opaque handle for use with the whgc_xxx()
> 130 routines. They are created using whgc_create_context() and
> 131 destroyed using whgc_destroy_context().
> 132 */
> 133 struct whgc_context;
> 134 typedef struct whgc_context whgc_context;
> 135
> 136 /** @typedef void (*whgc_dtor_f)(void*)
> 137
> 138 Typedef for deallocation functions symantically compatible with
> 139 free().
> 140 */
> 141 typedef void (*whgc_dtor_f)(void*);
> 142
> 143 /**
> 144 If cx is null this function works just like malloc() except:
> 145
> 146 - It calls memset() to zero out the memory.
> 147
> 148 - If cx is not null then ownership of the memory transfers is
> 149 transfered to cx calling whgc_add(cx,theMemory,dtor). cx's memory
> 150 memory allocation telemetry (see whgc_get_stats()) is also updated.
> 151
> 152 - If the cx is null then the caller owns the returned memory and must free
> 153 it by passing it to free().
> 154
> 155 It returns 0 on an alloc error or if size is 0.
> 156 */
> 157 void * whgc_alloc( whgc_context * cx, size_t size, whgc_dtor_f dtor );
> 158
> 159 /**
> 160 Creates a gc context. The clientContext point may internally be
> 161 used as a lookup key or some such but is otherwise unused by
> 162 this API.
> 163 */
> 164 whgc_context * whgc_create_context( void const * clientContext );
> 165
> 166 /**
> 167 Returns the client-supplied pointer which was passed to
> 168 whgc_create_context() for the given context. It is sometimes
> 169 useful as a lookup key.
> 170 */
> 171 void const * whgc_get_context_client(whgc_context const *);
> 172
> 173
> 174 /**
> 175 Registers an arbitrary key and value with the given garbage
> 176 collector, such that whgc_destroy_context(st) will clean up the
> 177 resources using the given destructor functions (which may be 0,
> 178 meaning "do not destroy").
> 179
> 180 A custom hash function is supplied which hashes the address of
> 181 the key (that is, a hash value of the numeric value of the
> 182 pointer address). Thus for two keys to be equal they must have
> 183 the same address (or must have found a collision in the hash
> 184 algorithm).
> 185
> 186 If keyDtor is not 0 then during cleanup keyDtor(key) is
> 187 called. Likewise, if valDtor is not 0 then valDtor(value) is
> 188 called at cleanup time.
> 189
> 190 It is perfectly legal for the key and value to be the same
> 191 object, but only if at least one of the destructor functions is
> 192 0 or a no-op function (otherwise a double-free will happen).
> 193
> 194 It is legal for both keyDtor and valDtor to be 0, in which case
> 195 this API simply holds a reference to the data but will not destroy
> 196 it.
> 197
> 198 Returns true if the item is now registered, or false on error
> 199 (!st, !key, key was already mapped, or a memory allocation error).
> 200
> 201 It is illegal to register the same key more than once with the
> 202 same context. Doing so will cause false to be returned.
> 203
> 204 Note that the destruction order of items cleaned up using this
> 205 mechanism is in reverse order of their registration.
> 206 */
> 207 bool whgc_register( whgc_context * cx,
> 208 void * key, whgc_dtor_f keyDtor,
> 209 void * value, whgc_dtor_f valDtor );
> 210
> 211 /**
> 212 Convenience form of whgc_register(cx,key,keyDtor,key,0).
> 213 */
> 214 bool whgc_add( whgc_context *cx, void * key, whgc_dtor_f keyDtor );
> 215
> 216 /**
> 217 Removes the given key from the given context, transfering ownership
> 218 of the key and the associated value to the caller.
> 219 */
> 220 void * whgc_unregister( whgc_context * cx, void * key );
> 221
> 222 /**
> 223 Frees all resources associated with the given context.
> 224 All entries which have been added via whgc_add() are passed to
> 225 the dtor function which was assigned to them via whgc_add().
> 226
> 227 Note that the destruction order is in reverse order of the
> 228 registration (FIFO).
> 229 */
> 230 void whgc_destroy_context( whgc_context * );
> 231
> 232 /**
> 233 Clears all gc entries, calling their associated destructors.
> 234
> 235 This does not destroy cx, only its entries.
> 236 */
> 237 void whgc_clear_context( whgc_context * cx );
> 238
> 239 /**
> 240 A destructor for use with functions taking a whgc_dtor_f parameter.
> 241 It requires that its argument be a whgc_context pointer, on which
> 242 it calls whgc_destroy_context().
> 243
> 244 This can be useful if you want one gc context to manage multiple
> 245 other contexts. Simply use, e.g.:
> 246
> 247 @code
> 248 whgc_gc_add( mainCtx, subCtx, whgc_context_dtor );
> 249 @endcode
> 250
> 251 where mainCtx is the primary context and subCtx is a context which
> 252 belongs to mainCtx.
> 253 */
> 254 void whgc_context_dtor( void * );
> 255
> 256 /**
> 257 Searches the given context for the given key. Returns 0 if the
> 258 key is not found. Ownership of the returned objet is not changed.
> 259 */
> 260 void * whgc_search( whgc_context const * cx, void const * key );
> 261
> 262 /**
> 263 A type for storing some telemetry for a whgc_context.
> 264 Use whgc_get_stats() to collect the current stats of
> 265 a parser.
> 266 */
> 267 struct whgc_stats
> 268 {
> 269 /**
> 270 Number of entries in the context.
> 271 */
> 272 size_t entry_count;
> 273 /**
> 274 Number of registrations made in the context.
> 275 */
> 276 size_t reg_count;
> 277 /**
> 278 Number of items unregistered from the context.
> 279 */
> 280 size_t unreg_count;
> 281 /**
> 282 A rough *approximatation* of amount of memory allocated for
> 283 whgc-specific internal structures used by the context,
> 284 including the size of the underlying hashtable(s). A
> 285 context has no way of knowing how much memory is used by
> 286 registered items.
> 287
> 288 Don't take this number too seriously. i try to keep the
> 289 telemetry up to date for allocs, but keeping track of
> 290 deallocs is more difficult due to timing and scope issues.
> 291 */
> 292 size_t alloced;
> 293 };
> 294 typedef struct whgc_stats whgc_stats;
> 295
> 296 /**
> 297 Returns the current stats for the given context.
> 298 */
> 299 whgc_stats whgc_get_stats( whgc_context const * );
> 300
> 301 /**
> 302 The type is not intended to be instantiated directly by
> 303 clients, but instead used via the whgc_events shared object.
> 304
> 305 A helper type for simulating scoped named enums in C. The
> 306 values in this type correspond to those used by whgc_event,
> 307 which allows event listeners to distinguish between various
> 308 event types in one handler.
> 309
> 310 All members of this type must have unique values, but the
> 311 values used by the whgc_events object are unspecified (other
> 312 than that they are each unique).
> 313 */
> 314 struct whgc_events_t {
> 315 /**
> 316 Signal that a GC item has been registered.
> 317 */
> 318 short registered;
> 319
> 320 /**
> 321 Signal that a GC item has been unregistered.
> 322 */
> 323 short unregistered;
> 324
> 325 /**
> 326 Signal that a key/val pair is about to e passed through the
> 327 item's dtor functions. This event is triggered even if the dtor
> 328 functions won't actually be called (e.g. they are 0). Thus this
> 329 signals a "virtual" destruction.
> 330 */
> 331 short destructing_item;
> 332
> 333 /**
> 334 Signal that a context is about to be destroyed.
> 335 */
> 336 short destructing_context;
> 337
> 338 /**
> 339 This marks the highest reserved event ID. In theory, values
> 340 above that can be used by client code, but i can personally
> 341 see no use for doing so. Note that the IDs in this class
> 342 are not guaranteed to be sequential, so don't use this as
> 343 an end condition for a loop.
> 344 */
> 345 short last_event_id;
> 346 };
> 347 typedef struct whgc_events_t whgc_events_t;
> 348 /** @var const whgc_events_t whgc_events
> 349
> 350 This is a shared instance of whgc_events_t which holds event
> 351 type IDs. It is used instead of an enum because i find it
> 352 prettier. Sample usage, from a whgc_listener_f implementation:
> 353
> 354 @code
> 355 void my_gc_listener( whgc_event const * event )
> 356 {
> 357 if( whgc_events.destructing_context == event->type )
> 358 {
> 359 whgc_stats const st = whgc_get_stats( event->cx );
> 360 printf("Approx memory allocated by gc context: %u\n", st.alloced);
> 361 }
> 362 }
> 363 @endcode
> 364 */
> 365 extern const whgc_events_t whgc_events;
> 366
> 367 /**
> 368 whgc_event is a type for sending information regarding certain GC
> 369 context state changes to registered listeners.
> 370 */
> 371 struct whgc_event
> 372 {
> 373 /**
> 374 The context from which this event originated. This parameter
> 375 is set for all event types.
> 376 */
> 377 whgc_context const * cx;
> 378
> 379 /**
> 380 Specifies the logical type of the event, which also determines
> 381 the semantics of the event.
> 382
> 383 The type IDs are defined in the whgc_events shared object
> 384 and alert the user about which members of the event are set
> 385 and. e.g. whgc_events.destructing_context does not set the
> 386 key/value members because they are meaningless for that
> 387 event. The semantics of the various event types are:
> 388
> 389 - whgc_events.registered signals that the key/value members have
> 390 just been registered with the context.
> 391
> 392 - whgc_events.unregistered signals that the key/value members
> 393 have just been unregistered from the context.
> 394
> 395 - whgc_events.destructing_item signals that the key/value
> 396 members are about to be passed to their registered dtor
> 397 functions (if any).
> 398
> 399 - whgc_events.destructing_context signals that the cx
> 400 member is about to be destroyed (key/value will be 0).
> 401
> 402 These semantics should be respected by whgc_listener_f
> 403 implementations.
> 404 */
> 405 short type;
> 406
> 407 /**
> 408 The key associated with the event. Only set for events of type:
> 409
> 410 whgc_events.registered, whgc_events.unregistered, whgc_events.destructing_item
> 411
> 412 */
> 413 void const * key;
> 414 /**
> 415 The value associated with the event. Only set for events of type:
> 416
> 417 whgc_events.registered, whgc_events.unregistered, whgc_events.destructing_item
> 418
> 419 */
> 420 void const * value;
> 421 };
> 422 typedef struct whgc_event whgc_event;
> 423 /** @typedef void (*whgc_listener_f)( whgc_event const * ev )
> 424
> 425 A typedef for whgc context event listers. The primary use case
> 426 of a listener is to help debug the lifetimes of GC'd items. The
> 427 listener is guaranteed to not get a null pointer, so long as it
> 428 is triggered from inside of this API.
> 429 */
> 430 typedef void (*whgc_listener_f)( whgc_event const * ev );
> 431
> 432 /**
> 433 Adds an event listener to the context. The listener is called
> 434 when certain events happen within the given context. The call
> 435 order of multiple listeners is undefined. There is no way to
> 436 remove a listener.
> 437
> 438 Listeners are best reserved for debugging purposes only, as
> 439 they apply some overhead to all add/remove operations. The
> 440 exact amount of overhead is largely a function of what the
> 441 listeners do, as the context is effectively blocked until the
> 442 listeners return.
> 443 */
> 444 bool whgc_add_listener( whgc_context *, whgc_listener_f f );
> 445
> 446 #ifdef __cplusplus
> 447 } /* extern "C" */
> 448 #endif
> 449
> 450
> 451 #endif /* WANDERINGHORSE_NET_WHGC_H_INCLUDED */

Added whhash.c

Old () New (6a4c505dee2c12e5)
> 1 /* Copyright (C) 2004 Christopher Clark <firstname.lastname@cl.cam.ac.uk> */
> 2 /* Copyright (C) 2008 Stephan Beal (http://wanderinghorse.net/home/stephan/) */
> 3
> 4 #include "whhash.h"
> 5 #include <stdlib.h>
> 6 //#include <stdio.h>
> 7 #include <string.h>
> 8
> 9 #ifdef __cplusplus
> 10 extern "C" {
> 11 #endif
> 12
> 13 /*
> 14 Internal type for holding hashtable entries.
> 15 */
> 16 struct whhash_entry
> 17 {
> 18 void *k, *v;
> 19 whhash_val_t h;
> 20 struct whhash_entry *next;
> 21 };
> 22 typedef struct whhash_entry whhash_entry;
> 23
> 24 #define WHHASH_STATS_INIT {\
> 25 0,/*entries*/ \
> 26 0,/*insertions*/ \
> 27 0,/*removals*/ \
> 28 0,/*searches*/ \
> 29 0,/*alloced*/ \
> 30 0,/*expansions*/ \
> 31 0,/*search_collisions*/ \
> 32 }
> 33 static const whhash_stats whhash_stats_init = WHHASH_STATS_INIT;
> 34
> 35
> 36 struct whhash_table {
> 37 size_t tablelength;
> 38 struct whhash_entry **table;
> 39 size_t entrycount;
> 40 size_t loadlimit;
> 41 size_t primeindex;
> 42 whhash_val_t (*hashfn) (void const *k);
> 43 int (*eqfn) (void const *k1, void const *k2);
> 44 void (*freeKey)( void * );
> 45 void (*freeVal)( void * );
> 46 unsigned int flags;
> 47 whhash_stats stats;
> 48 };
> 49 static const whhash_table
> 50 whhash_init = { 0, /*tablelength*/
> 51 0, /* table */
> 52 0, /* entrycount */
> 53 0, /* loadlimit */
> 54 0, /* primeindex */
> 55 0, /* hashfn */
> 56 0, /* eqfn */
> 57 0, /* freeKey */
> 58 0, /* freeVal */
> 59 0, /* flags */
> 60 WHHASH_STATS_INIT
> 61 };
> 62 const whhash_val_t whhash_hash_val_err = (whhash_val_t)-1;
> 63
> 64 struct whhash_flags {
> 65 /**
> 66 Signals a hash for use with the whhash_st_xxx() functions.
> 67 Set only by whhash_sh_create().
> 68 */
> 69 const int SH_API;
> 70 } whhash_flags = {
> 71 0x0001 /* SH_API */
> 72 };
> 73
> 74 /**
> 75 Returns h->hashfn(k), or whhash_hash_val_err if either h or k are 0.
> 76 */
> 77 static whhash_val_t whhash_hash(whhash_table *h, void const *k)
> 78 {
> 79 if( !h || !k ) return whhash_hash_val_err;
> 80 /* Aim to protect against poor hash functions by adding logic here
> 81 * - logic taken from java 1.4 whhash_table source */
> 82 whhash_val_t i = h->hashfn(k);
> 83 i += ~(i << 9);
> 84 i ^= ((i >> 14) | (i << 18)); /* >>> */
> 85 i += (i << 4);
> 86 i ^= ((i >> 10) | (i << 22)); /* >>> */
> 87 return i;
> 88 }
> 89
> 90 /*****************************************************************************/
> 91 /* Returns (hashvalue % tablelength) */
> 92 #if 0
> 93 static size_t whhash_index(size_t tablelength, size_t hashvalue)
> 94 {
> 95 return (hashvalue % tablelength);
> 96 }
> 97 #else
> 98 #define whhash_index(LEN,HV) (HV % LEN)
> 99 #endif
> 100
> 101 /**
> 102 For internal use only: this func frees the memory associated with
> 103 key, using h->freeKey (if set). Results are undefined if key is
> 104 not a key in h and if key is used after this func is called.
> 105 */
> 106 static void whhash_free_key(whhash_table const * h, void * key )
> 107 {
> 108 if( h && key && h->freeKey ) h->freeKey( key );
> 109 }
> 110
> 111 /**
> 112 Like whhash_free_key() but applies to the value.
> 113 */
> 114 static void whhash_free_val(whhash_table const * h, void * val )
> 115 {
> 116 if( h && val && h->freeVal ) h->freeVal( val );
> 117 }
> 118
> 119
> 120 /*
> 121 Credit for primes table: Aaron Krowne
> 122 http://br.endernet.org/~akrowne/
> 123 http://planetmath.org/encyclopedia/GoodHashTablePrimes.html
> 124 */
> 125 static const whhash_val_t primes[] = {
> 126 #if 0
> 127 2,3,5,7,11,13,
> 128 #endif
> 129 #if 0
> 130 17,19,23,29,31,
> 131 #endif
> 132 #if 0
> 133 37,41,43,47,
> 134 #endif
> 135 #if 1
> 136 7,13,23,
> 137 #endif
> 138 53, 97, 193, 389,
> 139 769, 1543, 3079, 6151,
> 140 12289, 24593, 49157, 98317,
> 141 196613, 393241, 786433, 1572869,
> 142 3145739, 6291469, 12582917, 25165843,
> 143 50331653, 100663319, 201326611, 402653189,
> 144 805306457, 1610612741
> 145 };
> 146 const whhash_val_t prime_table_length = sizeof(primes)/sizeof(primes[0]);
> 147 const float max_load_factor =
> 148 /* original developer's value was 0.65. */
> 149 0.70
> 150 ;
> 151
> 152
> 153 /**
> 154 Custom implementation of ceil() to avoid a dependency on
> 155 libmath on some systems (e.g. the tcc compiler).
> 156 */
> 157 static whhash_val_t whhash_ceil( double d )
> 158 {
> 159 whhash_val_t x = (whhash_val_t)d;
> 160 return (( d - (double)x )>0.0)
> 161 ? (x+1)
> 162 : x;
> 163 }
> 164
> 165 /*****************************************************************************/
> 166 whhash_table *
> 167 whhash_create(whhash_val_t minsize,
> 168 whhash_val_t (*hashf) (void const *),
> 169 int (*eqf) (void const *,void const *))
> 170 {
> 171 whhash_table *h;
> 172 whhash_val_t pindex, size = primes[0];
> 173 /* Check requested whhash_table isn't too large */
> 174 if (minsize > (1u << 30)) return NULL;
> 175 /* Enforce size as prime */
> 176 for (pindex=0; pindex < prime_table_length; pindex++) {
> 177 if (primes[pindex] > minsize) { size = primes[pindex]; break; }
> 178 }
> 179 h = (whhash_table *)malloc(sizeof(whhash_table));
> 180 if (NULL == h) return NULL; /*oom*/
> 181 h->stats.alloced = sizeof(whhash_table);
> 182 *h = whhash_init;
> 183 h->freeKey = 0;
> 184 h->freeVal = 0;
> 185 #if 0
> 186 h->table = (whhash_entry **)malloc(sizeof(whhash_entry*) * size);
> 187 if(h->table) memset(h->table, 0, size * sizeof(whhash_entry *));
> 188 #else
> 189 h->table = (whhash_entry **)calloc(size, sizeof(whhash_entry*));
> 190 #endif
> 191 if (NULL == h->table) { free(h); return NULL; } /*oom*/
> 192 h->stats.alloced += (sizeof(whhash_entry*) * size);
> 193 h->tablelength = size;
> 194 h->primeindex = pindex;
> 195 h->entrycount = 0;
> 196 h->hashfn = hashf;
> 197 h->eqfn = eqf;
> 198 h->loadlimit = whhash_ceil(size * max_load_factor);
> 199 return h;
> 200 }
> 201 /*****************************************************************************/
> 202
> 203
> 204
> 205 static int
> 206 whhash_expand(whhash_table *h)
> 207 {
> 208 if( ! h ) return 0;
> 209 /* Jump up to the next size in the primes table to accomodate more entries */
> 210 whhash_entry **newtable;
> 211 whhash_entry *e;
> 212 whhash_entry **pE;
> 213 whhash_val_t newsize, i, index;
> 214 /* Check we're not hitting max capacity */
> 215 if (h->primeindex == (prime_table_length - 1)) return 0;
> 216 newsize = primes[++(h->primeindex)];
> 217 newtable = (whhash_entry **)malloc(sizeof(whhash_entry*) * newsize);
> 218 if (NULL != newtable)
> 219 {
> 220 memset(newtable, 0, newsize * sizeof(whhash_entry *));
> 221 /* This algorithm is not 'stable'. ie. it reverses the list
> 222 * when it transfers entries between the tables */
> 223 for (i = 0; i < h->tablelength; i++) {
> 224 while (NULL != (e = h->table[i])) {
> 225 h->table[i] = e->next;
> 226 index = whhash_index(newsize,e->h);
> 227 e->next = newtable[index];
> 228 newtable[index] = e;
> 229 }
> 230 }
> 231 free(h->table);
> 232 h->table = newtable;
> 233 }
> 234 /* Plan B: realloc instead */
> 235 else
> 236 {
> 237 newtable = (whhash_entry **)
> 238 realloc(h->table, newsize * sizeof(whhash_entry *));
> 239 if (NULL == newtable) { (h->primeindex)--; return 0; }
> 240 h->table = newtable;
> 241 memset(newtable[h->tablelength], 0, newsize - h->tablelength);
> 242 for (i = 0; i < h->tablelength; i++) {
> 243 for (pE = &(newtable[i]), e = *pE; e != NULL; e = *pE) {
> 244 index = whhash_index(newsize,e->h);
> 245 if (index == i)
> 246 {
> 247 pE = &(e->next);
> 248 }
> 249 else
> 250 {
> 251 *pE = e->next;
> 252 e->next = newtable[index];
> 253 newtable[index] = e;
> 254 }
> 255 }
> 256 }
> 257 }
> 258 ++h->stats.expansions;
> 259 h->stats.alloced += (sizeof(whhash_entry*) * newsize)
> 260 - (sizeof(whhash_entry*) * h->tablelength);
> 261 h->tablelength = newsize;
> 262 h->loadlimit = whhash_ceil(newsize * max_load_factor);
> 263 return -1;
> 264 }
> 265
> 266 size_t
> 267 whhash_count(whhash_table const * h)
> 268 {
> 269 return h ? h->entrycount : 0;
> 270 }
> 271
> 272 int
> 273 whhash_replace(whhash_table *h, void *k, void *v)
> 274 {
> 275 if( !h || !k
> 276 || !h->tablelength /* avoid a possible /-by-0 in whhash_index()*/
> 277 ) return 0;
> 278 whhash_entry *e;
> 279 whhash_val_t hashvalue, index;
> 280 hashvalue = whhash_hash(h,k);
> 281 index = whhash_index(h->tablelength,hashvalue);
> 282 e = h->table[index];
> 283 while (NULL != e)
> 284 {
> 285 /* Check hash value to short circuit heavier comparison */
> 286 if (
> 287 (k == e->k)
> 288 ||
> 289 ((hashvalue == e->h) && (h->eqfn(k, e->k)))
> 290 )
> 291 {
> 292 if( v == e->v ) return 1;
> 293 whhash_free_val( h, e->v );
> 294 e->v = v;
> 295 return -1;
> 296
> 297 }
> 298 e = e->next;
> 299 }
> 300 return 0;
> 301 }
> 302
> 303 int
> 304 whhash_insert(whhash_table *h, void *k, void *v)
> 305 {
> 306 if( ! h || !k
> 307 || !h->tablelength /* avoid a possible /-by-0 in whhash_index()*/
> 308 ) return 0;
> 309 #if 0
> 310 /* Stephan Beal, 13 Feb 2008: now simply replaces the value of existing entries.
> 311
> 312 A week or three later: profiling has shown that this nearly doubles the insertion
> 313 time. One app was spending almost 1% of its time in this code.
> 314 */
> 315 int rc = whhash_replace(h, k, v);
> 316 if( 0 != rc ) return rc;
> 317 #endif
> 318 whhash_val_t index;
> 319 whhash_entry *e;
> 320 if (++(h->entrycount) > h->loadlimit)
> 321 {
> 322 /* Ignore the return value. If expand fails, we should
> 323 * still try cramming just this value into the existing table
> 324 * -- we may not have memory for a larger table, but one more
> 325 * element may be ok. Next time we insert, we'll try expanding again.*/
> 326 whhash_expand(h);
> 327 }
> 328 e = (whhash_entry *)malloc(sizeof(whhash_entry));
> 329 if (NULL == e) { --(h->entrycount); return 0; } /*oom*/
> 330 h->stats.alloced += sizeof(whhash_entry);
> 331 e->h = whhash_hash(h,k);
> 332 index = whhash_index(h->tablelength,e->h);
> 333 e->k = k;
> 334 e->v = v;
> 335 e->next = h->table[index];
> 336 h->table[index] = e;
> 337 ++h->stats.insertions;
> 338 return 1;
> 339 }
> 340
> 341 static whhash_entry * whhash_search_entry(whhash_table *h,
> 342 void const *k)
> 343 {
> 344 if( !h || !k
> 345 || !h->tablelength /* avoid a possible /-by-0 in whhash_index()*/
> 346 ) return 0;
> 347 ++h->stats.searches;
> 348 whhash_entry * e = 0;
> 349 whhash_val_t hashvalue, index;
> 350 hashvalue = whhash_hash(h,k);
> 351 index = whhash_index(h->tablelength,hashvalue);
> 352 e = h->table[index];
> 353 while (NULL != e)
> 354 {
> 355 /* Check hash value to short circuit heavier comparison */
> 356 if ((k == e->k) || ((hashvalue == e->h) && (h->eqfn(k, e->k))))
> 357 {
> 358 return e;
> 359 }
> 360 e = e->next;
> 361 ++h->stats.search_collisions;
> 362 }
> 363 return NULL;
> 364 }
> 365
> 366 short whhash_contains(whhash_table *h, void const *k)
> 367 {
> 368 whhash_entry * e = whhash_search_entry(h,k);
> 369 return e ? 1 : 0;
> 370 }
> 371
> 372 void *
> 373 whhash_search(whhash_table *h, void const *k)
> 374 {
> 375 if( !h || !k ) return 0;
> 376 whhash_entry * e = whhash_search_entry(h,k);
> 377 return e ? e->v : 0;
> 378 }
> 379
> 380 void * whhash_take(whhash_table *h, void const *k)
> 381 {
> 382 if( !h || !k
> 383 || !h->tablelength /* avoid a possible /-by-0 in whhash_index()*/
> 384 ) return 0;
> 385
> 386 /* TODO: consider compacting the table when the load factor drops enough,
> 387 * or provide a 'compact' method. */
> 388 whhash_entry *e;
> 389 whhash_entry **pE;
> 390 void *v;
> 391 whhash_val_t hashvalue, index;
> 392 // Can we reimplement this using whhash_search_entry()?
> 393 hashvalue = whhash_hash(h,k);
> 394 index = whhash_index(h->tablelength,whhash_hash(h,k));
> 395 pE = &(h->table[index]);
> 396 e = *pE;
> 397 while (NULL != e)
> 398 {
> 399 /* Check hash value to short circuit heavier comparison */
> 400 if ( (e->k==k)
> 401 || ((hashvalue == e->h) && (h->eqfn(k, e->k)))
> 402 )
> 403 {
> 404 *pE = e->next;
> 405 --h->entrycount;
> 406 v = e->v;
> 407 h->stats.alloced -= sizeof(whhash_entry);
> 408 free(e);
> 409 ++h->stats.removals;
> 410 return v;
> 411 }
> 412 pE = &(e->next);
> 413 e = e->next;
> 414 }
> 415 return NULL;
> 416 }
> 417
> 418 short whhash_remove(whhash_table *h, void *k)
> 419 {
> 420 if( !h || !k ) return 0;
> 421 void * v = whhash_take(h,k);
> 422 whhash_free_key(h,k);
> 423 if( v )
> 424 {
> 425 whhash_free_val(h,v);
> 426 }
> 427 return NULL != v;
> 428 }
> 429
> 430 void whhash_set_val_dtor( whhash_table * h, void (*dtor)( void * ) )
> 431 {
> 432 if( h ) h->freeVal = dtor;
> 433 }
> 434 void whhash_set_key_dtor( whhash_table * h, void (*dtor)( void * ) )
> 435 {
> 436 if( h ) h->freeKey = dtor;
> 437 }
> 438 void whhash_set_dtors( whhash_table * h, void (*keyDtor)( void * ), void (*valDtor)( void * ) )
> 439 {
> 440 whhash_set_key_dtor( h, keyDtor );
> 441 whhash_set_val_dtor( h, valDtor );
> 442 }
> 443
> 444 static whhash_entry * whhash_free_entry( whhash_table * h, whhash_entry * e )
> 445 {
> 446 if( !h || !e ) return 0;
> 447 whhash_entry * next = e->next;
> 448 h->entrycount--;
> 449 whhash_free_key( h, e->k );
> 450 whhash_free_val( h, e->v );
> 451 free(e);
> 452 h->stats.alloced -= sizeof(whhash_entry);
> 453 return next;
> 454 }
> 455
> 456 void whhash_clear(whhash_table *h)
> 457 {
> 458 if( ! h || !h->table ) return;
> 459 //printf("whhash_clear(): alloced=%u\n",h->stats.alloced);
> 460 whhash_val_t i;
> 461 whhash_entry *e;
> 462 whhash_entry **table = h->table;
> 463 for (i = 0; i < h->tablelength; i++)
> 464 {
> 465 e = table[i];
> 466 while (NULL != e)
> 467 {
> 468 e = whhash_free_entry( h, e );
> 469 }
> 470 }
> 471 //printf("whhash_clear(): alloced=%u\n",h->stats.alloced);
> 472 // Which of these is correct?
> 473 h->stats.alloced -= (sizeof(whhash_entry*) * h->tablelength);
> 474 //printf("tablelen=%u sizeof... = %u\n",h->tablelength,(sizeof(whhash_entry*) * h->tablelength));
> 475 free(h->table);
> 476 h->table = 0;
> 477 h->entrycount = 0;
> 478 h->tablelength = 0;
> 479 h->loadlimit = 0;
> 480 //printf("whhash_clear(): alloced=%u\n",h->stats.alloced); // why is this 0 instead of sizeof(whhash_table)?
> 481 h->stats.alloced = sizeof(whhash_table);
> 482 //printf("whhash_clear(): alloced=%u\n",h->stats.alloced);
> 483 }
> 484 void
> 485 whhash_destroy(whhash_table *h)
> 486 {
> 487 if( h )
> 488 {
> 489 whhash_clear(h);
> 490 free(h);
> 491 }
> 492 }
> 493
> 494 whhash_stats whhash_get_stats( whhash_table const * h )
> 495 {
> 496 return h ? h->stats : whhash_stats_init;
> 497 }
> 498
> 499 int whhash_cmp_cstring( void const * k1, void const * k2 )
> 500 {
> 501 if( (k1 == k2) ) return 1;
> 502 else if( (!k1 && k2) || (k1 && !k2) ) return 0; /* protect against passing null to strcmp() */
> 503 return (0 == strcmp( (char const *) k1, (char const *) k2 ));
> 504 }
> 505
> 506 int whhash_cmp_long( void const * k1, void const * k2 )
> 507 {
> 508 return *((long const*)k1) == *((long const*)k2);
> 509 }
> 510
> 511 int whhash_cmp_void_ptr( void const * k1, void const * k2 )
> 512 {
> 513 return k1 == k2;
> 514 }
> 515
> 516 whhash_val_t whhash_hash_void_ptr( void const * k )
> 517 {
> 518 /* This next typedef must *apparently* be the same size as the platform's (void*) */
> 519 typedef long kludge_t;
> 520 /**
> 521 Originally we used the void* address as a hashvalue, until it
> 522 came to me that those doesn't distribute well within the hash
> 523 range (they tend to clump together).
> 524
> 525 So below we do a variant of the "Modified Bernstein Hash"
> 526 on the bytes of the (void*)'s numeric value. For details:
> 527
> 528 http://eternallyconfuzzled.com/tuts/algorithms/jsw_tut_hashing.aspx
> 529
> 530 Initial (but minimal) tests show the distribution to be much improved
> 531 over using the (void*) as the hash value.
> 532 */
> 533 whhash_val_t h = 0;
> 534 kludge_t const x = (kludge_t const) k;
> 535 unsigned char const * c = (unsigned char const *)&x;
> 536 size_t i = 0;
> 537 for( i = 0; i < (sizeof(kludge_t) / sizeof(char)); ++i )
> 538 {
> 539 h = 33 * h ^ c[i];
> 540 }
> 541 //MARKER; printf("hash of %p: x=%ld h=%ld, i=%d, sizeof1=%u sizeof2=%u\n",k,x,h,i,sizeof(kludge_t),sizeof(char));
> 542 return h;
> 543 }
> 544
> 545
> 546 whhash_val_t
> 547 whhash_hash_cstring_djb2( void const * vstr)
> 548 { /* "djb2" algo code taken from: http://www.cse.yorku.ca/~oz/hash.html */
> 549 if( ! vstr ) return whhash_hash_val_err;
> 550 whhash_val_t hash = 5381;
> 551 int c = 0;
> 552 char const * str = (char const *)vstr;
> 553 if( ! str ) return whhash_hash_val_err;
> 554 while( (c = *str++) )
> 555 {
> 556 hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
> 557 }
> 558 return hash;
> 559 }
> 560
> 561 whhash_val_t whhash_hash_cstring_djb2m( void const * vstr )
> 562 {
> 563 char const * str = (char const *)vstr;
> 564 if( ! str ) return whhash_hash_val_err;
> 565 whhash_val_t h = 0;
> 566 int c = 0;
> 567 while( (c = *(str++)) )
> 568 {
> 569 h = 33 * h ^ c;
> 570 }
> 571 return h;
> 572 }
> 573
> 574 #if 0
> 575 /**
> 576 Implements the Fowler/Noll/Vo (FNV) hash, as described at:
> 577
> 578 http://eternallyconfuzzled.com/tuts/algorithms/jsw_tut_hashing.aspx
> 579 */
> 580 //whhash_val_t whhash_hash_cstring_fnv( void const * str );
> 581 whhash_val_t whhash_hash_cstring_fnv( void const * vstr )
> 582 {
> 583 char const * str = (char const *)vstr;
> 584 if( ! str ) return whhash_hash_val_err;
> 585 whhash_val_t h = (unsigned long long) 2166136261L;
> 586 /**
> 587 ^^^^
> 588 gcc says: warning: this decimal constant is unsigned only in ISO C90
> 589 */
> 590 int c = 0;
> 591 while( (c = *(str++)) )
> 592 {
> 593 h = (h * 16777619L) ^ c;
> 594 }
> 595 return h;
> 596 }
> 597 #endif
> 598
> 599 whhash_val_t whhash_hash_cstring_oaat( void const * vstr )
> 600 {
> 601 char const * str = (char const *)vstr;
> 602 if( ! str ) return whhash_hash_val_err;
> 603 whhash_val_t h = 0;
> 604 int c = 0;
> 605 while( (c = *(str++)) )
> 606 {
> 607 h += c;
> 608 h += (h << 10);
> 609 h ^= (h >> 6);
> 610 }
> 611 h += ( h << 3 );
> 612 h ^= ( h >> 11 );
> 613 h += ( h << 15 );
> 614 return h;
> 615 }
> 616
> 617
> 618 whhash_val_t
> 619 whhash_hash_cstring_sdbm(void const *vstr)
> 620 { /* "sdbm" algo code taken from: http://www.cse.yorku.ca/~oz/hash.html */
> 621 char const * str = (char const *)vstr;
> 622 if( ! str ) return whhash_hash_val_err;
> 623 whhash_val_t h = 0;
> 624 int c = 0;
> 625 while( str && (c = *str++) )
> 626 {
> 627 h = c + (h << 6) + (h << 16) - h;
> 628 }
> 629 return h;
> 630 }
> 631
> 632 whhash_val_t
> 633 whhash_hash_cstring_rot( void const * vstr)
> 634 {
> 635 char const * str = (char const *)vstr;
> 636 if( ! str ) return whhash_hash_val_err;
> 637 whhash_val_t h = 0;
> 638 int c = 0;
> 639 while( (c = *(str++)) )
> 640 {
> 641 h = (h << 4) ^ (h >> 28 ) ^ c;
> 642 }
> 643 return h;
> 644 }
> 645
> 646 whhash_val_t
> 647 whhash_hash_cstring_sax( void const * vstr)
> 648 {
> 649 char const * str = (char const *)vstr;
> 650 if( ! str ) return whhash_hash_val_err;
> 651 whhash_val_t h = 0;
> 652 int c = 0;
> 653 while( (c = *(str++)) )
> 654 {
> 655 h ^= (h << 5) + (h >> 2) + c;
> 656 }
> 657 return h;
> 658 }
> 659
> 660
> 661 whhash_val_t
> 662 whhash_hash_long( void const * n )
> 663 {
> 664 return n
> 665 ? *((whhash_val_t const *)n)
> 666 : whhash_hash_val_err;
> 667 }
> 668
> 669 struct whhash_iter
> 670 {
> 671 whhash_table *h;
> 672 whhash_entry *e;
> 673 whhash_entry *parent;
> 674 unsigned int index;
> 675 };
> 676 static const whhash_iter whhash_itr_init = {0,0,0,0};
> 677
> 678 whhash_iter *
> 679 whhash_get_iter(whhash_table *h)
> 680 {
> 681 if( !whhash_count(h) ) return 0;
> 682 unsigned int i, tablelength;
> 683 whhash_iter *itr = (whhash_iter *)
> 684 malloc(sizeof(whhash_iter));
> 685 if (NULL == itr) return NULL;
> 686 h->stats.alloced += sizeof(whhash_iter);
> 687 *itr = whhash_itr_init;
> 688 itr->h = h;
> 689 itr->e = NULL;
> 690 itr->parent = NULL;
> 691 tablelength = h->tablelength;
> 692 itr->index = tablelength;
> 693 //if (0 == h->entrycount) return itr;
> 694
> 695 for (i = 0; i < tablelength; i++)
> 696 {
> 697 if (NULL != h->table[i])
> 698 {
> 699 itr->e = h->table[i];
> 700 itr->index = i;
> 701 break;
> 702 }
> 703 }
> 704 return itr;
> 705 }
> 706
> 707 void *
> 708 whhash_iter_key(whhash_iter *i)
> 709 { return i ? i->e->k : 0; }
> 710
> 711 void *
> 712 whhash_iter_value(whhash_iter *i)
> 713 { return i ? i->e->v : 0; }
> 714
> 715 int
> 716 whhash_iter_advance(whhash_iter *itr)
> 717 {
> 718 unsigned int j,tablelength;
> 719 whhash_entry **table;
> 720 whhash_entry *next;
> 721 if (!itr || (!itr->e)) return 0;
> 722
> 723 next = itr->e->next;
> 724 if (NULL != next)
> 725 {
> 726 itr->parent = itr->e;
> 727 itr->e = next;
> 728 return -1;
> 729 }
> 730 tablelength = itr->h->tablelength;
> 731 itr->parent = NULL;
> 732 if (tablelength <= (j = ++(itr->index)))
> 733 {
> 734 itr->e = NULL;
> 735 return 0;
> 736 }
> 737 table = itr->h->table;
> 738 while (NULL == (next = table[j]))
> 739 {
> 740 if (++j >= tablelength)
> 741 {
> 742 itr->index = tablelength;
> 743 itr->e = NULL;
> 744 return 0;
> 745 }
> 746 }
> 747 itr->index = j;
> 748 itr->e = next;
> 749 return -1;
> 750 }
> 751
> 752 /*****************************************************************************/
> 753 /* remove - remove the entry at the current iterator position
> 754 * and advance the iterator, if there is a successive
> 755 * element.
> 756 * The key and value of the element are freed using
> 757 * whhash_free_key/val(), which may or may not actually
> 758 * free them.
> 759 * Returns zero if end of iteration.
> 760 */
> 761
> 762 int
> 763 whhash_iter_remove(whhash_iter *itr)
> 764 {
> 765 whhash_entry *remember_e, *remember_parent;
> 766 int ret = 0;
> 767
> 768 /* Do the removal */
> 769 if (NULL == (itr->parent))
> 770 {
> 771 /* element is head of a chain */
> 772 itr->h->table[itr->index] = itr->e->next;
> 773 } else {
> 774 /* element is mid-chain */
> 775 itr->parent->next = itr->e->next;
> 776 }
> 777 /* itr->e is now outside the whhash_table */
> 778 remember_e = itr->e;
> 779 itr->h->entrycount--;
> 780 whhash_free_key( itr->h, remember_e->k );
> 781 whhash_free_val( itr->h, remember_e->v );
> 782
> 783 /* Advance the iterator, correcting the parent */
> 784 remember_parent = itr->parent;
> 785 ret = whhash_iter_advance(itr);
> 786 if (itr->parent == remember_e) { itr->parent = remember_parent; }
> 787 free(remember_e);
> 788 itr->h->stats.alloced -= sizeof(whhash_entry);
> 789 return ret;
> 790 }
> 791
> 792 int
> 793 whhash_iter_search(whhash_iter *itr,
> 794 void *k)
> 795 {
> 796 if( ! itr || !itr->h || !k
> 797 || !itr->h->tablelength /* avoid a possible /-by-0 in whhash_index()*/
> 798 ) return 0;
> 799 whhash_entry *e, *parent;
> 800 unsigned int hashvalue, index;
> 801 whhash_table *h = itr->h;
> 802 hashvalue = whhash_hash(h,k);
> 803 index = whhash_index(h->tablelength,hashvalue);
> 804 e = h->table[index];
> 805 parent = NULL;
> 806 while (NULL != e)
> 807 {
> 808 /* Check hash value to short circuit heavier comparison */
> 809 if ((e->k == k)
> 810 ||
> 811 ((hashvalue == e->h) && (h->eqfn(k, e->k)))
> 812 )
> 813 {
> 814 itr->index = index;
> 815 itr->e = e;
> 816 itr->parent = parent;
> 817 itr->h = h;
> 818 return -1;
> 819 }
> 820 parent = e;
> 821 e = e->next;
> 822 }
> 823 return 0;
> 824 }
> 825
> 826 #ifdef __cplusplus
> 827 } /* extern "C" */
> 828 #endif
> 829
> 830 /*
> 831 Copyright (c) 2002, Christopher Clark
> 832 Copyright (C) 2008 Stephan Beal (http://wanderinghorse.net/home/stephan/)
> 833 All rights reserved.
> 834
> 835 Redistribution and use in source and binary forms, with or without
> 836 modification, are permitted provided that the following conditions
> 837 are met:
> 838
> 839 * Redistributions of source code must retain the above copyright
> 840 notice, this list of conditions and the following disclaimer.
> 841
> 842 * Redistributions in binary form must reproduce the above copyright
> 843 notice, this list of conditions and the following disclaimer in the
> 844 documentation and/or other materials provided with the distribution.
> 845
> 846 * Neither the name of the original author; nor the names of any contributors
> 847 may be used to endorse or promote products derived from this software
> 848 without specific prior written permission.
> 849
> 850
> 851 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
> 852 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
> 853 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
> 854 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
> 855 OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
> 856 EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
> 857 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
> 858 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
> 859 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
> 860 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
> 861 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> 862 */

Added whhash.h

Old () New (b966a1a75384615d)
> 1 /* Copyright (C) 2002 Christopher Clark <firstname.lastname@cl.cam.ac.uk> */
> 2 /* Copyright (C) 2008 Stephan Beal (http://wanderinghorse.net/home/stephan/) */
> 3 /* Code originally taken from: http://www.cl.cam.ac.uk/~cwc22/hashtable/ */
> 4 #ifndef WANDERINGHORSE_NET_WHHASH_H_INCLUDED
> 5 #define WANDERINGHORSE_NET_WHHASH_H_INCLUDED
> 6 #include <stddef.h> /* size_t */
> 7 #ifdef __cplusplus
> 8 extern "C" {
> 9 #endif
> 10 /**
> 11 @page whhash_page_main whhash: hashtable API
> 12
> 13 The functions and types named whhash* are part of the
> 14 WanderingHorse.net hashtable library. It is a hashtable
> 15 implementation based on code by Christopher Clark,
> 16 adopted, extended, and changed somewhat by yours truly.
> 17
> 18 License: New BSD License
> 19
> 20 Maintainer: Stephan Beal (http://wanderinghorse.net/home/stephan)
> 21
> 22 The hashtables described here map (void*) to (void*) by using a
> 23 client-supplied hash algorithm on the key pointers. The hashtable
> 24 can optionally take over ownership of its keys or values, via
> 25 whhash_set_key_dtor() and whhash_set_key_dtor(). The ownership
> 26 management option makes this type useful as a simple garbage
> 27 collector.
> 28
> 29 @section whhash_sec_example Example
> 30
> 31 @code
> 32 whhash_table *h;
> 33 struct some_key *k;
> 34 struct some_value *v;
> 35
> 36 // Assume the following functions which can hash and compare the
> 37 // some_key and some_val structs:
> 38 static whhash_val_t hash_some_key( void *k );
> 39 static int cmp_key_val ( void *key1, void *key2 );
> 40
> 41 h = whhash_create(16, hash_some_key, cmp_key_val);
> 42 k = (struct some_key *) malloc(sizeof(struct some_key));
> 43 v = (struct some_value *) malloc(sizeof(struct some_value));
> 44
> 45 ...initialise k and v to suitable values...
> 46
> 47 if (! whhash_insert(h,k,v) )
> 48 {
> 49 ...error... probably OOM or one of the args was null...;
> 50 }
> 51
> 52 if (NULL == (found = whhash_search(h,k) ))
> 53 { printf("not found!"); }
> 54 if (NULL == (found = whhash_take(h,k) ))
> 55 { printf("Not found\n"); }
> 56
> 57 whhash_destroy( h );
> 58 @endcode
> 59
> 60 @section whhash_sec_ownership Memory ownership
> 61
> 62 By default a hashtable does no memory management of its keys or
> 63 values. However, whhash_set_key_dtor() and whhash_set_val_dtor()
> 64 can be used to set a cleanup function. If a cleanup function is
> 65 set, it is called (and passed the appropriate key or value) when an
> 66 item is removed from the hashtable (e.g. when the hashtable is
> 67 destroyed). It is possible to transfer ownership back to the caller
> 68 by using whhash_take().
> 69
> 70 Example:
> 71
> 72 @code
> 73 whhash_table *h;
> 74 h = whhash_create(16, whhash_hash_cstring_djb2, whhash_cmp_cstring );
> 75 whhash_set_dtors(h, free, free);
> 76 char * str = 0;
> 77 char * key = 0;
> 78 int i = 0;
> 79 for( int i = 0; i < 10; ++i )
> 80 {
> 81 // Assume mnprintf() is a printf-like func which allocates new strings:
> 82 key = mnprintf("key_%d",i);
> 83 str = mnprintf("...%d...",i);
> 84 whhash_insert(h, key, str ); // transfers ownership of key/str to h
> 85 }
> 86 whhash_destroy( h ); // Calls free() on each inserted copy of key and str.
> 87 @endcode
> 88
> 89
> 90 @section whhash_sec_macros Macros
> 91
> 92 Macros may be used to define type-safe(r) whhash_table access functions, with
> 93 methods specialized to take known key and value types as parameters.
> 94
> 95 Example:
> 96
> 97 Insert this at the start of your file:
> 98
> 99 @code
> 100 DEFINE_WHHASH_INSERT(insert_some, struct some_key, struct some_value);
> 101 DEFINE_WHHASH_SEARCH(search_some, struct some_key, struct some_value);
> 102 DEFINE_WHHASH_TAKE(take_some, struct some_key);
> 103 DEFINE_WHHASH_REMOVE(remove_some, struct some_key);
> 104 @endcode
> 105
> 106 This defines the functions 'insert_some', 'search_some',
> 107 'take_some', and 'remove_some'. These operate just like
> 108 whhash_insert() etc., with the same parameters, but their function
> 109 signatures have 'struct some_key *' rather than 'void *', and
> 110 hence can generate compile time errors if your program is
> 111 supplying incorrect data as a key (and similarly for value).
> 112
> 113 Note that the hash and key equality functions passed to
> 114 whhash_create still take 'void *' parameters instead of 'some key
> 115 *'. This shouldn't be a difficult issue as they're only defined
> 116 and passed once, and the other functions will ensure that only
> 117 valid keys are supplied to them.
> 118
> 119 The cost for this checking is increased code size and runtime
> 120 overhead - if performance is important, it may be worth switching
> 121 back to the unsafe methods once your program has been debugged
> 122 with the safe methods. This just requires switching to some
> 123 simple alternative defines - eg:
> 124
> 125 @code
> 126 #define insert_some whhash_insert
> 127 @endcode
> 128
> 129 @section whhash_sec_threadsafety Thread safety
> 130
> 131 By default it is never legal to use the same hashtable from
> 132 multiple threads at the same time. That said, the client may use
> 133 their own locking to serialize access. All API functions which
> 134 take a (whhash_table const *) argument require only a read lock,
> 135 whereas those taking a (whhash_table*) argument require a
> 136 exclusive (read/write) access. whhash_create() is reentrant and
> 137 does not need to be locked.
> 138
> 139 Special consideration is also needed considering locking of stored
> 140 keys/values. If a referenced item is used by multiple threads,
> 141 this code has no way of knowing about it. When in doubt, don't
> 142 store items which are shared across threads in a hashtable unless
> 143 you know that lifetime and ownership issues can be mitigated. If,
> 144 e.g., a hashtable does not own its entries, one needs to make sure
> 145 that if the entry is deleted from somewhere else, that it's
> 146 removed from the hashtable (or ensure that that entry cannot be
> 147 references again, otherwise you'll get a dangling pointer back).
> 148
> 149
> 150 @section whhash_sec_links Other resources
> 151
> 152 Some other resources:
> 153
> 154 - The original implementation off of which whhash is based can be found
> 155 at: http://www.cl.cam.ac.uk/~cwc22/hashtable/
> 156
> 157 - A good intro to hashtables:
> 158 http://en.wikipedia.org/wiki/Hash_table
> 159
> 160 - Brief analysis and implementations of of several well-known hash routines:
> 161 http://eternallyconfuzzled.com/tuts/algorithms/jsw_tut_hashing.aspx
> 162 */
> 163
> 164 /** @typedef whhash_val_t
> 165
> 166 The hash key value type by the whhash API.
> 167 */
> 168 typedef unsigned long whhash_val_t;
> 169
> 170 /**
> 171 @var const whhash_val_t whhash_hash_val_err
> 172
> 173 whhash_hash_val_err is ((whhash_val_t)-1). It is used to report
> 174 hash value errors. Hash routines which want to report
> 175 an error may do so by returning this value.
> 176 */
> 177 extern const whhash_val_t whhash_hash_val_err;
> 178 /** @struct whhash_table
> 179
> 180 whhash_table is an opaque handle to hashtable data. They are
> 181 created using whhash_create() and destroyed using
> 182 whhash_destroy().
> 183 */
> 184 struct whhash_table;
> 185 typedef struct whhash_table whhash_table;
> 186
> 187
> 188 /**
> 189 whhash_create() allocates a new hashtable with the given minimum
> 190 starting size (the actual size may, and probably will, be
> 191 larger). The given hash function and comparison function are used
> 192 for all hashing and comparisons, respectively.
> 193
> 194 The comparison function must return 0 if no match is made, else
> 195 non-zero. The hash function must create a hash value for the given
> 196 object, the more randomly distributed the better.
> 197
> 198 minsize specifies the minimum initial size of the hashtable. It may
> 199 (and probably will) allocate more than this, however.
> 200 */
> 201 whhash_table * whhash_create(whhash_val_t minsize,
> 202 whhash_val_t (*hashfunction) (void const *),
> 203 int (*key_eq_fn) (void const *,void const *));
> 204
> 205
> 206 /**
> 207 Sets the destructor function for h's keys, which are cleaned up
> 208 when items are removed or the whhash_table is destroyed. By default
> 209 a whhash_table owns its keys and will call free() to release
> 210 them. If you use keys from managed memory and don't want them
> 211 destroyed by the whhash_table, simply pass 0 as the dtor argument.
> 212 */
> 213 void whhash_set_key_dtor( whhash_table * h, void (*dtor)( void * ) );
> 214
> 215 /**
> 216 This is similar to whhash_set_key_dtor(), but applies to
> 217 whhash_table values instead of keys. By default a whhash_table does
> 218 not own its values and will not delete them under any
> 219 circumstances. To make the whhash_table take ownership, simply pass
> 220 'free' (or suitable function for your type, e.g. many structs may
> 221 need a custom destructor) to this function.
> 222 */
> 223 void whhash_set_val_dtor( whhash_table * h, void (*dtor)( void * ) );
> 224
> 225 /**
> 226 Equivalent to whhash_set_key_dtor(h,keyDtor) and
> 227 whhash_set_val_dtor(h,valDtor).
> 228 */
> 229 void whhash_set_dtors( whhash_table * h, void (*keyDtor)( void * ), void (*valDtor)( void * ) );
> 230
> 231
> 232 /**
> 233 whhash_insert() adds new entries to a hashtable.
> 234
> 235 Neither of the h or k parameters may be 0. A value of 0 is okay but
> 236 in practice it means a client cannot differentiate between "not
> 237 found" and "found value of 0". The objects pointed to by the
> 238 parameters must outlive this hashtable (or be managed/owned by it by
> 239 assigning destructor functions, e.g. via whhash_set_dtors()).
> 240
> 241 When a key is re-inserted (already mapped to something) then results
> 242 are undefined. When in doubt you should use whhash_take() to remove
> 243 the entry before re-adding it (but then freeing the old value becomes
> 244 your responsibility). (Historical note: some versions of this code
> 245 automatically replaced existing items, but profiling shows it to
> 246 nearly double the insertion costs so this feature was removed.)
> 247
> 248 This function will cause the table to expand if the insertion would
> 249 take the ratio of entries to table size over the maximum load factor.
> 250
> 251 Key/value ownership: By default a hashtable does not own its keys nor
> 252 its values. You may set the ownership policy by using
> 253 whhash_set_key_dtor() and whhash_set_val_dtor(). The destructors are
> 254 called whenever items are removed from the whhash_table or the
> 255 whhash_table is destroyed.
> 256
> 257 The key object must not change in a way which affects its hash value
> 258 after it is inserted. To do so invokes undefined behaviour.
> 259
> 260 That the key is not a const parameter is unfortunate, but it's the
> 261 only way we can properly apply ownership management without violating
> 262 constness. The original intention of this type was a hash which owned its
> 263 keys, which is why the parameter has historically not been const. Current
> 264 usage of this class ranges from general purpose lookup (without memory
> 265 management) to garbage collector, and some of those uses cannot be achieved
> 266 with a const key.
> 267 */
> 268 int
> 269 whhash_insert(whhash_table *h, void *k, void *v);
> 270
> 271 /**
> 272 whhash_replace() changes the value associated with a key, where there
> 273 already exists a value bound to the key in the whhash_table. If (v ==
> 274 existingValue) then this routine has no side effects, otherwise this
> 275 function calls whhash_free_val() for any existing value tied to
> 276 k, so it may (or may not) deallocate an object.
> 277
> 278 Source by Holger Schemel. Modified by Stephan Beal to use
> 279 whhash_free_value().
> 280
> 281 Returns 0 if no match is found, -1 if a match is made and replaced,
> 282 and 1 if (v == existingValue).
> 283
> 284 Neither the h nor k parameters may be 0.
> 285 */
> 286 int
> 287 whhash_replace(whhash_table *h, void *k, void *v);
> 288
> 289 #define DEFINE_WHHASH_INSERT(fnname, keytype, valuetype) \
> 290 int fnname (whhash_table *h, keytype *k, valuetype *v) \
> 291 { \
> 292 return whhash_insert(h,k,v); \
> 293 }
> 294
> 295 /**
> 296 whhash_search() searches for the given key and returns the
> 297 associated value (if found) or 0 (if not found). Ownership of the
> 298 returned value is unchanged.
> 299 */
> 300 void *
> 301 whhash_search(whhash_table *h, void const * k);
> 302
> 303 /**
> 304 Works like whhash_search() but can differentiate between a found
> 305 value of 0 and no-such-element.
> 306
> 307 Returns 0 for if wh does not contain the given key and non-zero for
> 308 if wh does contain the key.
> 309
> 310 Maintainer's note: the wh parameter should be const, but its not
> 311 for internal reasons.
> 312 */
> 313 short whhash_contains(whhash_table *wh, void const * k);
> 314
> 315
> 316 #define DEFINE_WHHASH_SEARCH(fnname, keytype, valuetype) \
> 317 valuetype * fnname (whhash_table *h, keytype const *k) \
> 318 { \
> 319 return (valuetype *) (whhash_search(h,k)); \
> 320 }
> 321
> 322 /**
> 323 whhash_take() removes the given key from the whhash_table and
> 324 returns the value to the caller. The key/value destructors (if any)
> 325 set via whhash_set_key_dtor() and whhash_set_val_dtor() WILL
> 326 NOT be called! If you want to remove an item and call the dtors for
> 327 its key and value, use whhash_remove().
> 328
> 329 If (!h), (!k), or no match is found, then 0 is returned and ownership
> 330 of k is not changed. If a match is found which is 0, 0 is
> 331 still returned but ownership of k is returned to the caller. There
> 332 is unfortunately no way for a caller to differentiate between these
> 333 two cases.
> 334
> 335 */
> 336 void * whhash_take(whhash_table *h, void const *k);
> 337
> 338 /**
> 339 Works like whhash_take(h,k), but also calls the dtors registered
> 340 for the key and value, which may (or may not) deallocate the
> 341 objects. Since the destructor may (or may not) destroy the key, k
> 342 may not (or may) be valid after this function returns.
> 343
> 344 Returns 0 if it finds no entry to remove, else non-zero.
> 345 */
> 346 short whhash_remove(whhash_table *h, void *k);
> 347
> 348 #define DEFINE_WHHASH_TAKE(fnname, keytype, valuetype) \
> 349 valuetype * fnname (whhash_table *h, keytype const *k) \
> 350 { \
> 351 return (valuetype *) (whhash_take(h,k)); \
> 352 }
> 353
> 354 #define DEFINE_WHHASH_REMOVE(fnname, keytype) \
> 355 short fnname (whhash_table *h, keytype const *k) { return whhash_remove(h,k);}
> 356
> 357
> 358 /*!
> 359 whhash_count() returns the number of items in the hashtable.
> 360 This is a constant-time operation.
> 361 */
> 362 size_t
> 363 whhash_count(whhash_table const * h);
> 364
> 365
> 366 /**
> 367 whhash_destroy() cleans up resources allocated by a whhash_table and calls
> 368 the configured destructors for each key and value. After
> 369 this call, h is invalid.
> 370 */
> 371 void whhash_destroy(whhash_table *h);
> 372
> 373 /**
> 374 Similar to whhash_destroy(), but only cleans up the internal
> 375 contents, and does not actualy delete the h pointer. Thus h
> 376 can be used for further inserts. The next insert will force
> 377 the hashtable to re-allocate internal storage. This routine
> 378 is the only way to get a whhash_table to reduce its size.
> 379
> 380 This routine will force any registered dtors to be called on
> 381 each key/value in the hashtable.
> 382 */
> 383 void whhash_clear(whhash_table *h);
> 384
> 385 /**
> 386 A comparison function for use with whhash_create(). If (k1==k2) it
> 387 returns non-0 (true), otherwise it performs strcmp(k1,k2) and
> 388 returns true if they match.
> 389
> 390 This function accepts null pointers. Two null pointers will compare
> 391 equal, otherwise a single null pointer in the pair will evaluate
> 392 to false.
> 393 */
> 394 int whhash_cmp_cstring( void const * k1, void const * k2 );
> 395
> 396
> 397 /**
> 398 A comparison function for use with whhash_create().
> 399 It essentially performs (*((long*)k1) == (*((long*)k2))).
> 400
> 401 Trying to compare numbers of different-sized types (e.g. long and
> 402 short) won't work (well, probably won't). Results are undefined.
> 403 */
> 404 int whhash_cmp_long( void const * k1, void const * k2 );
> 405
> 406 /**
> 407 An int/long hashing function for use with whhash_create(). It
> 408 requires that n point to a long integer, and it simply returns the
> 409 value of n, or whhash_hash_val_err on error (n is NULL).
> 410 */
> 411 whhash_val_t whhash_hash_long( void const * n );
> 412
> 413 /**
> 414 This is a hash routine for generic void pointers. To avoid clustering
> 415 of hashvalues, it performs the following:
> 416
> 417 - Casts k to its numeric value (as a long, or some other platform-specific
> 418 numeric type).
> 419 - Performs a Berstein Hash on the first (sizeof(long)/sizeof(char)) bytes
> 420 of that numeric value, treating each byte as a separate input character.
> 421
> 422 That greatly improves the distribution range of the hash values over using
> 423 the address of k as the hash value.
> 424 */
> 425 whhash_val_t whhash_hash_void_ptr( void const * k );
> 426
> 427 /**
> 428 A hashtable comparison function which simply returns (k1 == k2).
> 429 */
> 430 int whhash_cmp_void_ptr( void const * k1, void const * k2 );
> 431
> 432 /**
> 433 A C-string hashing function for use with whhash_create(). Uses the
> 434 so-called "djb2" algorithm. Returns whhash_hash_val_err if (!str).
> 435
> 436 For notes on the hash algorithm see:
> 437
> 438 http://www.cse.yorku.ca/~oz/hash.html
> 439
> 440 */
> 441 whhash_val_t whhash_hash_cstring_djb2( void const * str );
> 442
> 443 /**
> 444 Implements the "Modified Bernstein Hash", as described at:
> 445
> 446 http://eternallyconfuzzled.com/tuts/algorithms/jsw_tut_hashing.aspx
> 447
> 448 */
> 449 whhash_val_t whhash_hash_cstring_djb2m( void const * str );
> 450
> 451
> 452 /**
> 453 Implements the "Shift-Add-XOR" (SAX) hash, as described at:
> 454
> 455 http://eternallyconfuzzled.com/tuts/algorithms/jsw_tut_hashing.aspx
> 456 */
> 457 whhash_val_t whhash_hash_cstring_djb2m( void const * str );
> 458
> 459
> 460 /**
> 461 Implements the One-at-a-time hash, as described at:
> 462
> 463 http://eternallyconfuzzled.com/tuts/algorithms/jsw_tut_hashing.aspx
> 464 */
> 465 whhash_val_t whhash_hash_cstring_oaat( void const * str );
> 466
> 467 /**
> 468 The "rotating hash", as described at:
> 469
> 470 http://eternallyconfuzzled.com/tuts/algorithms/jsw_tut_hashing.aspx
> 471
> 472 To quote that page:
> 473
> 474 "Much of the time, the rotating hash is sufficient, and can be
> 475 considered the minimal acceptable algorithm."
> 476 */
> 477 whhash_val_t whhash_hash_cstring_rot( void const * str );
> 478
> 479 /**
> 480 A C-string hashing function for use with whhash_create(). Uses the
> 481 so-called "sdbm" algorithm. Returns whhash_hash_val_err if (!str).
> 482
> 483 For notes on the hash algorithm see:
> 484
> 485 http://www.cse.yorku.ca/~oz/hash.html
> 486 */
> 487 whhash_val_t whhash_hash_cstring_sdbm( void const * str );
> 488
> 489
> 490 /*! @struct whhash_iter
> 491
> 492 Opaque handle for whhash_table interators.
> 493 */
> 494 struct whhash_iter;
> 495 typedef struct whhash_iter whhash_iter;
> 496
> 497
> 498 /**
> 499 whhash_get_iter() creates a new iterator for the given hashtable
> 500 and returns it. The caller must call free() on the object when he
> 501 is done with it. If (!whhash_count(h)) then this function returns
> 502 0.
> 503 */
> 504 whhash_iter * whhash_get_iter(whhash_table *h);
> 505
> 506 /**
> 507 Returns the key of the (key,value) pair at the current position,
> 508 or 0 if !i.
> 509 */
> 510 void * whhash_iter_key(whhash_iter *i);
> 511
> 512 /**
> 513 Returns the value of the (key,value) pair at the current position,
> 514 or 0 if !i.
> 515 */
> 516 void * whhash_iter_value(whhash_iter *i);
> 517
> 518 /**
> 519 Advance the iterator to the next element. Returns zero if advanced to
> 520 end of table or if (!itr).
> 521 */
> 522 int whhash_iter_advance(whhash_iter *itr);
> 523
> 524 /**
> 525 Removes current element and advance the iterator to the
> 526 next element the associated destructors to free (or not) the
> 527 pointed-to key and value, so this call may (or may not) deallocate
> 528 those objects.
> 529 */
> 530 int whhash_iter_remove(whhash_iter *itr);
> 531
> 532 /**
> 533 Searches for the given key in itr's associated hashtable.
> 534 If found, itr is modified to point to the found item and
> 535 a true value is returned. If no item is found or if (!itr||!k)
> 536 then false (0) is returned.
> 537 */
> 538 int whhash_iter_search(whhash_iter *itr, void *k);
> 539
> 540 #define DEFINE_WHHASH_ITERATOR_SEARCH(fnname, keytype) \
> 541 int fnname (whhash_iter *i, keytype *k) \
> 542 { \
> 543 return (whhash_iter_search(i,k)); \
> 544 }
> 545
> 546 /**
> 547 The whhash_stats struct is used for telemetry collection for
> 548 whhash_table objects. These objects should be created by calling
> 549 whhash_get_stats().
> 550 */
> 551 struct whhash_stats
> 552 {
> 553 /**
> 554 Number of entries in the context.
> 555 */
> 556 size_t entries;
> 557
> 558 /**
> 559 Number of registrations made in the context.
> 560 */
> 561 size_t insertions;
> 562
> 563 /**
> 564 Number of whhash_take() calls made in the context.
> 565 */
> 566 size_t removals;
> 567
> 568 /**
> 569 The number of searches made on the hashtable.
> 570 */
> 571 size_t searches;
> 572
> 573 /**
> 574 A rough *approximatation* of amount of memory allocated for
> 575 whgc-specific internal structures used by the context,
> 576 including the size of the underlying hashtable(s). A
> 577 context has no way of knowing how much memory is used by
> 578 registered items.
> 579
> 580 Don't take this number too seriously. i try to keep the
> 581 telemetry up to date for allocs, but keeping track of
> 582 deallocs is more difficult due to timing and scope issues.
> 583 */
> 584 size_t alloced;
> 585 /**
> 586 The number of times the size of the hashtable has been
> 587 increased beyond the initial value.
> 588 */
> 589 size_t expansions;
> 590
> 591 /**
> 592 This is incremented each time a search operation fails
> 593 to find the proper entry on the first try. If this
> 594 number is large, the hash function may need to be
> 595 changed.
> 596 */
> 597 size_t search_collisions;
> 598 };
> 599 typedef struct whhash_stats whhash_stats;
> 600
> 601 /**
> 602 Returns the current statistics for h, or an object
> 603 with all 0 values if (!h).
> 604 */
> 605 whhash_stats whhash_get_stats( whhash_table const * h );
> 606
> 607 #ifdef __cplusplus
> 608 } /* extern "C" */
> 609 #endif
> 610 #endif /* WANDERINGHORSE_NET_WHHASH_H_INCLUDED */
> 611
> 612 /*
> 613 Copyright (c) 2002, Christopher Clark
> 614 Copyright (C) 2008 Stephan Beal (http://wanderinghorse.net/home/stephan/)
> 615 All rights reserved.
> 616
> 617 Redistribution and use in source and binary forms, with or without
> 618 modification, are permitted provided that the following conditions
> 619 are met:
> 620
> 621 * Redistributions of source code must retain the above copyright
> 622 notice, this list of conditions and the following disclaimer.
> 623
> 624 * Redistributions in binary form must reproduce the above copyright
> 625 notice, this list of conditions and the following disclaimer in the
> 626 documentation and/or other materials provided with the distribution.
> 627
> 628 * Neither the name of the original author; nor the names of any contributors
> 629 may be used to endorse or promote products derived from this software
> 630 without specific prior written permission.
> 631
> 632 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
> 633 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
> 634 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
> 635 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
> 636 OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
> 637 EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
> 638 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
> 639 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
> 640 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
> 641 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
> 642 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> 643 */

Added whrc.c

Old () New (2e21d9d2ad366f20)
> 1 #include <stdlib.h> /* malloc/free */
> 2 #include <assert.h>
> 3
> 4 #include "whrc.h"
> 5 #include "whhash.h"
> 6 #ifdef __cplusplus
> 7 extern "C" {
> 8 #endif
> 9
> 10 #if 1
> 11 #include <stdio.h> // only for debuggering
> 12 #define MARKER if(1) printf("MARKER: %s:%d:%s():\n",__FILE__,__LINE__,__func__); if(1) printf
> 13 #else
> 14 static void bogo_printf(char const * fmt, ...) {}
> 15 #define MARKER if(0) bogo_printf
> 16 #endif
> 17
> 18 const size_t whrc_ref_err_val = ((size_t)-1);
> 19 struct whrc_context
> 20 {
> 21 whhash_table * ht;
> 22 };
> 23
> 24 static const whrc_context whrc_context_init =
> 25 {
> 26 0 /* ht */
> 27 };
> 28
> 29 struct whrc_entry
> 30 {
> 31 whrc_dtor_f dtor;
> 32 size_t refcount;
> 33 };
> 34 typedef struct whrc_entry whrc_entry;
> 35 static const whrc_entry whrc_entry_init = {0,0};
> 36
> 37 whrc_context * whrc_create_context()
> 38 {
> 39 whrc_context * c = (whrc_context *)malloc(sizeof(whrc_context));
> 40 if( ! c ) return 0;
> 41 *c = whrc_context_init;
> 42 if( (c->ht = whhash_create( 10, whhash_hash_void_ptr, whhash_cmp_void_ptr ) ) )
> 43 {
> 44 whhash_set_dtors( c->ht, 0, free );
> 45 }
> 46 if( ! c->ht )
> 47 {
> 48 free(c);
> 49 return 0;
> 50 }
> 51 return c;
> 52 }
> 53
> 54 void whrc_clear_context( whrc_context * cx )
> 55 {
> 56 whhash_iter * it = cx ? whhash_get_iter(cx->ht) : 0;
> 57 //MARKER;
> 58 if( ! it ) return;
> 59 //MARKER;
> 60 do
> 61 {
> 62 //MARKER;
> 63 whrc_entry * e = (whrc_entry *)whhash_iter_value(it);
> 64 if( ! e ) continue; /* shouldn't happen! */
> 65 void * item = whhash_iter_key(it);
> 66 if( item && e->dtor )
> 67 {
> 68 //MARKER;
> 69 e->dtor( item );
> 70 }
> 71 }
> 72 while( whhash_iter_advance(it) );
> 73 free(it);
> 74 whhash_clear(cx->ht);
> 75 }
> 76
> 77 void whrc_destroy_context( whrc_context * cx, bool freeItems )
> 78 {
> 79 if( ! cx ) return;
> 80 if( freeItems ) whrc_clear_context(cx);
> 81 whhash_destroy( cx->ht );
> 82 free(cx);
> 83 }
> 84
> 85 bool whrc_register( whrc_context * cx, void * item, whrc_dtor_f dtor )
> 86 {
> 87 if( ! cx || !item ) return false;
> 88 if( whrc_is_registered(cx,item) ) return false;
> 89 whrc_entry * e = (whrc_entry*)malloc(sizeof(whrc_entry));
> 90 if( ! e ) return false;
> 91 *e = whrc_entry_init;
> 92 e->refcount = 1;
> 93 e->dtor = dtor;
> 94 if( ! whhash_insert( cx->ht, item, e ) )
> 95 {
> 96 free(e);
> 97 e = 0;
> 98 }
> 99 return 0 != e;
> 100 }
> 101
> 102 static whrc_entry * whrc_search_entry( whrc_context * cx, void const * item )
> 103 {
> 104 return (cx && item)
> 105 ? (whrc_entry *) whhash_search(cx->ht, item)
> 106 : 0;
> 107 }
> 108
> 109 #if 0
> 110 /**
> 111 This searches the context for the given item and returns it (or 0 if
> 112 no match is found (i.e. the item is not registered)).
> 113
> 114 Why use an item to look up itself? The first reason is, this routine
> 115 can be used in place to whrc_is_registered() (though that one is
> 116 slightly more efficient).
> 117
> 118 Secondly it can sometimes be used to legally get a non-const pointer
> 119 to an object which is otherwise const (that is, without casting away
> 120 the constness). For example, one routine may register the item, then
> 121 downstream the item is passed as a const pointer to another
> 122 routine. That routine can (assuming it has the whrc_context handle)
> 123 then use whrc_search() to get a non-const pointer to the item.
> 124 Admittedly only rarely useful, but this approach has come in handy a
> 125 time or two.
> 126 */
> 127 //void * whrc_search( whrc_context * cx, void const * item );
> 128 static void * whrc_search( whrc_context * cx, void const * item )
> 129 {
> 130 whrc_entry * e = whrc_search_entry(cx,item);
> 131 return e
> 132 ? e->item
> 133 : 0;
> 134 }
> 135 #endif
> 136
> 137
> 138 size_t whrc_refcount( whrc_context * cx, void const * item )
> 139 {
> 140 whrc_entry const * e = whrc_search_entry(cx, item);
> 141 if( ! e ) return whrc_ref_err_val;
> 142 return e->refcount;
> 143 }
> 144
> 145
> 146 bool whrc_is_registered( whrc_context * cx, void const * item )
> 147 {
> 148 return 0 != whrc_search_entry(cx,item);
> 149 }
> 150
> 151 static whrc_entry * whrc_take_entry( whrc_context * cx, void * item )
> 152 {
> 153 return cx
> 154 ? (whrc_entry *) whhash_take(cx->ht, item)
> 155 : 0;
> 156 }
> 157
> 158 size_t whrc_addref( whrc_context * cx, void * item )
> 159 {
> 160 whrc_entry * e = whrc_search_entry(cx,item);
> 161 if( ! e ) return whrc_ref_err_val;
> 162 return ++e->refcount;
> 163 }
> 164
> 165 size_t whrc_rmref( whrc_context * cx, void * item )
> 166 {
> 167 whrc_entry * e = whrc_search_entry(cx,item);
> 168 //MARKER("e=@%p, rc=%u\n",e,e?e->refcount:whrc_ref_err_val);
> 169 if( ! e ) return whrc_ref_err_val;
> 170 if( 0 == --e->refcount )
> 171 {
> 172 whrc_entry * check = whrc_take_entry(cx, item);
> 173 assert( (check == e) && "Internal state error - it seems another thread has trashed our hashtable!");
> 174 /**
> 175 We do this copy/free thing for the case that the dtor
> 176 throws a C++ exception or otherwise doesn't return (e.g. it
> 177 jumps). i know it's not likely, but it's easy enough to
> 178 avoid the leak here, so i do...
> 179 */
> 180 whrc_entry E = *check;
> 181 free(check);
> 182 if( E.dtor )
> 183 {
> 184 E.dtor( item );
> 185 }
> 186 return 0;
> 187 }
> 188 return e->refcount;
> 189 }
> 190
> 191 #ifdef __cplusplus
> 192 } /* extern "C" */
> 193 #endif

Added whrc.h

Old () New (a16dc9ab135125fb)
> 1 #ifndef WANDERINGHORSE_NET_WHRC_H_INCLUDED
> 2 #define WANDERINGHORSE_NET_WHRC_H_INCLUDED 1
> 3 /** @page whrc_page_main whrc: reference counting library for C
> 4
> 5 whrc (the WanderingHorse.net Reference Counter) is a C library for
> 6 adding reference counts to arbitrary void pointers. It allows one to
> 7 register a destructor function which will be called when the reference
> 8 count goes to 0. It is similar to the whgc garbage collector
> 9 (see: @ref whgc_page_main)
> 10 but is lighter-weight and does not manage key/value
> 11 pairs as that library does.
> 12
> 13 It internally uses a hashtable which uses a hash code based on the
> 14 (void*) address of an item. In theory the hashtable can average 0(1)
> 15 speeds, so there is very little performance hit.
> 16
> 17 Example:
> 18 @code
> 19 whrc_context * cx = whrc_create_context();
> 20 char * str = (char *)malloc(...);
> 21 whrc_register( cx, str, free );
> 22 size_t rc = whrc_refcount(cx,str); // rc == 1
> 23 rc = whrc_addref(cx,str); //rc == 2
> 24 rc = whrc_rmref(cx,str); // rc == 1
> 25 rc = whrc_rmref(cx,str); // rc == 0, free(str) is called
> 26 whrc_destroy_context(cx,true);
> 27 @endcode
> 28
> 29 (The bool argument to whrc_destroy_context() says whether or not to
> 30 free up any "dangling" items (those with references) in the context.)
> 31
> 32 That demonstrates the majority of the API. There are only a handful of
> 33 functions to learn.
> 34
> 35 @section whrc_sec_threadsafety Thread safety
> 36
> 37 By default it is never legal to use the same @ref whrc_context from
> 38 multiple threads at the same time. That said, the client may use
> 39 their own locking to serialize access. All API functions which
> 40 take a (@ref whrc_context const *) argument require only a read lock,
> 41 whereas those taking a (@ref whrc_context *) argument require a
> 42 exclusive (read/write) access. whrc_create_context() is reentrant
> 43 and does not need to be locked.
> 44
> 45 Special consideration is also needed considering locking of stored
> 46 items. If a referenced item is used by multiple threads, this code
> 47 has no way of knowing about it. When in doubt, don't store items
> 48 which are shared across threads unless you know that lifetime and
> 49 ownership issues can be mitigated.
> 50 */
> 51
> 52 #include <stddef.h> /* size_t */
> 53
> 54 #ifdef __cplusplus
> 55 extern "C" {
> 56 #endif
> 57
> 58 #ifndef __cplusplus
> 59 # ifndef WHGC_HAVE_STDBOOL
> 60 # define WHGC_HAVE_STDBOOL 0
> 61 # endif
> 62 # if defined(WHRC_HAVE_STDBOOL) && !(WHRC_HAVE_STDBOOL)
> 63 # if !defined(bool)
> 64 # define bool char
> 65 # endif
> 66 # if !defined(true)
> 67 # define true 1
> 68 # endif
> 69 # if !defined(false)
> 70 # define false 0
> 71 # endif
> 72 # else /* aha! stdbool.h! C99. */
> 73 # include <stdbool.h>
> 74 # endif /* WHRC_HAVE_STDBOOL */
> 75 #endif /* __cplusplus */
> 76
> 77 /** @typedef void (*whrc_dtor_f)(void*)
> 78
> 79 A destructor function semantically compatible with free().
> 80 */
> 81 typedef void (*whrc_dtor_f)(void*);
> 82
> 83 /**
> 84 Defined to ((size_t)-1), and returned from some functions
> 85 which need to report an error.
> 86 */
> 87 extern const size_t whrc_ref_err_val;
> 88
> 89 /**@typedef struct whrc_context whrc_context
> 90
> 91 Opaque handle type for reference count operations.
> 92 */
> 93 struct whrc_context;
> 94 typedef struct whrc_context whrc_context;
> 95
> 96 /**
> 97 Creates a new whrc_context object and transfers ownership to the
> 98 caller. The caller must free it with whrc_destroy_context().
> 99 */
> 100 whrc_context * whrc_create_context();
> 101
> 102 /**
> 103 Clears all items in a context, unconditionally passing them to
> 104 their registered destructors.
> 105 */
> 106 void whrc_clear_context( whrc_context * );
> 107
> 108 /**
> 109 Frees all resources allocated by the context. If freeItems is true
> 110 then whrc_clear_context() is called, otherwise any remaining items
> 111 in the cache are left untouched (and will leak unless ownership of
> 112 the items has been explicitly transfered).
> 113 */
> 114 void whrc_destroy_context( whrc_context * cx, bool freeItems );
> 115
> 116 /**
> 117 Registers item with cx and sets its reference count to 1.
> 118 It is not legal to register the same item more than once.
> 119
> 120 Returns true if the item is now registered (and therefore
> 121 owned by this object) or false if !cx, !item, or if an entry
> 122 is already found for the given key.
> 123
> 124 Note that it is legal to set a 0 dtor, in which case this
> 125 object does not own the item but will still maintain a reference
> 126 count. In any case, item must outlive the context or be destroyed
> 127 by the context.
> 128 */
> 129 bool whrc_register( whrc_context * cx, void * item, whrc_dtor_f );
> 130
> 131 /**
> 132 Returns true only if item is registered in the given context.
> 133 */
> 134 bool whrc_is_registered( whrc_context * cx, void const * item );
> 135
> 136
> 137 /**
> 138 Adds one to the reference count of item and returns the new
> 139 reference count, or whrc_ref_err_val if (!cx), (!item), or
> 140 if no entry is found.
> 141 */
> 142 size_t whrc_addref( whrc_context * cx, void * item );
> 143
> 144 /**
> 145 Subtracts one to the reference count of item and returns the new
> 146 reference count, or whrc_ref_err_val if (!cx) or (!item), or
> 147 if no entry is found. If 0 is returned then the dtor registered
> 148 via whrc_register() is called and passed item.
> 149 */
> 150 size_t whrc_rmref( whrc_context * cx, void * item );
> 151
> 152 /**
> 153 Returns the current refcount for the given item, or whrc_ref_err_val
> 154 if no such item is found or if cx or item are 0.
> 155 */
> 156 size_t whrc_refcount( whrc_context * cx, void const * item );
> 157
> 158
> 159 #ifdef __cplusplus
> 160 } /* extern "C" */
> 161 #endif
> 162
> 163
> 164 #endif /* WANDERINGHORSE_NET_WHRC_H_INCLUDED */