diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.am | 16 | ||||
-rw-r--r-- | src/Makefile.in | 554 | ||||
-rw-r--r-- | src/itzam.h | 709 | ||||
-rw-r--r-- | src/itzam_btree.c | 1993 | ||||
-rw-r--r-- | src/itzam_data.c | 1573 | ||||
-rw-r--r-- | src/itzam_util.c | 308 |
6 files changed, 5153 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..181ad4f --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,16 @@ +INCLUDES = -I$(top_srcdir) +CFLAGS = @CFLAGS@ -std=gnu99 + +h_sources = itzam.h + +cpp_sources = itzam_util.c itzam_data.c itzam_btree.c + +lib_LTLIBRARIES = libitzam.la + +libitzam_la_SOURCES = $(h_sources) $(cpp_sources) +libitzam_la_LDFLAGS= -version-info $(GENERIC_LIBRARY_VERSION) -release $(GENERIC_RELEASE) + +library_includedir=$(includedir)/$(GENERIC_LIBRARY_NAME) +library_include_HEADERS = $(h_sources) + +DEFS = -I. -I$(srcdir) diff --git a/src/Makefile.in b/src/Makefile.in new file mode 100644 index 0000000..7bbf01c --- /dev/null +++ b/src/Makefile.in @@ -0,0 +1,554 @@ +# Makefile.in generated by automake 1.11.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, +# Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + + +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = src +DIST_COMMON = $(library_include_HEADERS) $(srcdir)/Makefile.am \ + $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__installdirs = "$(DESTDIR)$(libdir)" \ + "$(DESTDIR)$(library_includedir)" +LTLIBRARIES = $(lib_LTLIBRARIES) +libitzam_la_LIBADD = +am__objects_1 = +am__objects_2 = itzam_util.lo itzam_data.lo itzam_btree.lo +am_libitzam_la_OBJECTS = $(am__objects_1) $(am__objects_2) +libitzam_la_OBJECTS = $(am_libitzam_la_OBJECTS) +libitzam_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(libitzam_la_LDFLAGS) $(LDFLAGS) -o $@ +DEFAULT_INCLUDES = -I.@am__isrc@ +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \ + $(LDFLAGS) -o $@ +SOURCES = $(libitzam_la_SOURCES) +DIST_SOURCES = $(libitzam_la_SOURCES) +HEADERS = $(library_include_HEADERS) +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ -std=gnu99 +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = -I. -I$(srcdir) +DEPDIR = @DEPDIR@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GENERIC_LIBRARY_NAME = @GENERIC_LIBRARY_NAME@ +GENERIC_LIBRARY_VERSION = @GENERIC_LIBRARY_VERSION@ +GENERIC_RELEASE = @GENERIC_RELEASE@ +GENERIC_VERSION = @GENERIC_VERSION@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAKEINFO = @MAKEINFO@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +lt_ECHO = @lt_ECHO@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +INCLUDES = -I$(top_srcdir) +h_sources = itzam.h +cpp_sources = itzam_util.c itzam_data.c itzam_btree.c +lib_LTLIBRARIES = libitzam.la +libitzam_la_SOURCES = $(h_sources) $(cpp_sources) +libitzam_la_LDFLAGS = -version-info $(GENERIC_LIBRARY_VERSION) -release $(GENERIC_RELEASE) +library_includedir = $(includedir)/$(GENERIC_LIBRARY_NAME) +library_include_HEADERS = $(h_sources) +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): +install-libLTLIBRARIES: $(lib_LTLIBRARIES) + @$(NORMAL_INSTALL) + test -z "$(libdir)" || $(MKDIR_P) "$(DESTDIR)$(libdir)" + @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ + list2=; for p in $$list; do \ + if test -f $$p; then \ + list2="$$list2 $$p"; \ + else :; fi; \ + done; \ + test -z "$$list2" || { \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \ + } + +uninstall-libLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ + for p in $$list; do \ + $(am__strip_dir) \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \ + done + +clean-libLTLIBRARIES: + -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES) + @list='$(lib_LTLIBRARIES)'; for p in $$list; do \ + dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \ + test "$$dir" != "$$p" || dir=.; \ + echo "rm -f \"$${dir}/so_locations\""; \ + rm -f "$${dir}/so_locations"; \ + done +libitzam.la: $(libitzam_la_OBJECTS) $(libitzam_la_DEPENDENCIES) + $(libitzam_la_LINK) -rpath $(libdir) $(libitzam_la_OBJECTS) $(libitzam_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/itzam_btree.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/itzam_data.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/itzam_util.Plo@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c $< + +.c.obj: +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +install-library_includeHEADERS: $(library_include_HEADERS) + @$(NORMAL_INSTALL) + test -z "$(library_includedir)" || $(MKDIR_P) "$(DESTDIR)$(library_includedir)" + @list='$(library_include_HEADERS)'; test -n "$(library_includedir)" || list=; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(library_includedir)'"; \ + $(INSTALL_HEADER) $$files "$(DESTDIR)$(library_includedir)" || exit $$?; \ + done + +uninstall-library_includeHEADERS: + @$(NORMAL_UNINSTALL) + @list='$(library_include_HEADERS)'; test -n "$(library_includedir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + test -n "$$files" || exit 0; \ + echo " ( cd '$(DESTDIR)$(library_includedir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(library_includedir)" && rm -f $$files + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + set x; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: CTAGS +CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LTLIBRARIES) $(HEADERS) +installdirs: + for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(library_includedir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \ + mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: install-library_includeHEADERS + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: install-libLTLIBRARIES + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-libLTLIBRARIES \ + uninstall-library_includeHEADERS + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \ + clean-libLTLIBRARIES clean-libtool ctags distclean \ + distclean-compile distclean-generic distclean-libtool \ + distclean-tags distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am \ + install-libLTLIBRARIES install-library_includeHEADERS \ + install-man install-pdf install-pdf-am install-ps \ + install-ps-am install-strip installcheck installcheck-am \ + installdirs maintainer-clean maintainer-clean-generic \ + mostlyclean mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool pdf pdf-am ps ps-am tags uninstall \ + uninstall-am uninstall-libLTLIBRARIES \ + uninstall-library_includeHEADERS + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/src/itzam.h b/src/itzam.h new file mode 100644 index 0000000..e171a1d --- /dev/null +++ b/src/itzam.h @@ -0,0 +1,709 @@ +/*
+ Itzam/C (version 6.0) is an embedded database engine written in Standard C.
+
+ Copyright 2011 Scott Robert Ladd. All rights reserved.
+
+ Older versions of Itzam/C are:
+ Copyright 2002, 2004, 2006, 2008 Scott Robert Ladd. All rights reserved.
+
+ Ancestral code, from Java and C++ books by the author, is:
+ Copyright 1992, 1994, 1996, 2001 Scott Robert Ladd. All rights reserved.
+
+ Itzam/C is user-supported open source software. It's continued development is dependent on
+ financial support from the community. You can provide funding by visiting the Itzam/C
+ website at:
+
+ http://www.coyotegulch.com
+
+ You may license Itzam/C in one of two fashions:
+
+ 1) Simplified BSD License (FreeBSD License)
+
+ Redistribution and use in source and binary forms, with or without modification, are
+ permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice, this list of
+ conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ of conditions and the following disclaimer in the documentation and/or other materials
+ provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY SCOTT ROBERT LADD ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SCOTT ROBERT LADD OR
+ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ The views and conclusions contained in the software and documentation are those of the
+ authors and should not be interpreted as representing official policies, either expressed
+ or implied, of Scott Robert Ladd.
+
+ 2) Closed-Source Proprietary License
+
+ If your project is a closed-source or proprietary project, the Simplified BSD License may
+ not be appropriate or desirable. In such cases, contact the Itzam copyright holder to
+ arrange your purchase of an appropriate license.
+
+ The author can be contacted at:
+
+ scott.ladd@coyotegulch.com
+ scott.ladd@gmail.com
+ http:www.coyotegulch.com
+*/
+
+#if !defined(LIBITZAM_ITZAM_H)
+#define LIBITZAM_ITZAM_H
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+#include <string.h>
+#include <stdio.h>
+
+/*-----------------------------------------------------------------------------
+ Operating system identification and settings
+ */
+#if !defined(ITZAM32) && !defined(ITZAM64)
+ #if defined(__LP64__) || defined(_M_X64)
+ #undef ITZAM32
+ #define ITZAM64 1
+ #define ITZAM_MAX_FILE_SIZE 9223372036854775808
+ #define REF_BYTES 8
+ #else
+ #undef ITZAM64
+ #define ITZAM32 1
+ #define ITZAM_MAX_FILE_SIZE 2147483648
+ #define REF_BYTES 4
+ #endif
+#endif
+
+#if defined(_WIN32) || defined(_WIN64) || defined (_MSC_VER)
+ #define ITZAM_WINDOWS
+ #include <io.h>
+ #include <fcntl.h>
+ #include <share.h>
+ #include <windows.h>
+ #include <process.h>
+ #if _MSC_VER >= 0x0A00
+ #include <stdint.h>
+ #else
+ typedef unsigned __int64 uint64_t;
+ typedef __int64 int64_t;
+ typedef unsigned long uint32_t;
+ typedef long int32_t;
+ typedef unsigned short uint16_t;
+ typedef short int16_t;
+ typedef unsigned char uint8_t;
+ typedef signed char int8_t;
+ #endif
+ #define ITZAM_SHMEM_TYPE HANDLE
+ #define ITZAM_FILE_TYPE HANDLE
+ #define ITZAM_GOOD_FILE(file) (file != INVALID_HANDLE_VALUE)
+ #define ITZAM_SEEK_BEGIN FILE_BEGIN
+ #define ITZAM_SEEK_END FILE_END
+ #define ITZAM_SEEK_CURRENT FILE_CURRENT
+ #define DLL_EXPORT
+ #pragma warning (disable : 4786 4244 4267 4101 4996 4761 4127)
+#elif defined(__unix__) || defined(unix)
+ #if defined(__linux__) || defined(__linux)
+ #define ITZAM_LINUX
+ #endif
+ #define ITZAM_UNIX
+ #include <unistd.h>
+ #include <pthread.h>
+ #include <stdbool.h>
+ #include <stdint.h>
+ #include <fcntl.h>
+ #define ITZAM_SHMEM_TYPE int
+ #define ITZAM_FILE_TYPE int
+ #define ITZAM_SEEK_BEGIN SEEK_SET
+ #define ITZAM_SEEK_END SEEK_END
+ #define ITZAM_SEEK_CURRENT SEEK_CUR
+ #define ITZAM_GOOD_FILE(file) (file != -1)
+#else
+ #error "Itzam is designed for 32- and 64-bit Windows and POSIX"
+#endif
+
+#if defined(ITZAM_UNIX) || defined(ITZAM_LINUX)
+ #include <unistd.h>
+ #if _POSIX_VERSION < 200809L
+ #error "Itzam requires POSIX.1-2008 or later"
+ #endif
+#endif
+
+/*-----------------------------------------------------------------------------
+ * simple types and values
+ */
+
+#pragma pack(push)
+#pragma pack(8)
+
+/* create boolean type to ensure exact size on all platforms */
+typedef int16_t itzam_bool;
+static const int16_t itzam_true = -1;
+static const int16_t itzam_false = 0;
+
+typedef uint8_t itzam_byte;
+
+#if defined(ITZAM64)
+typedef int64_t itzam_ref;
+typedef int64_t itzam_int;
+#else
+typedef int32_t itzam_ref;
+typedef int32_t itzam_int;
+#endif
+
+static const itzam_ref ITZAM_NULL_REF = -1;
+static const itzam_ref ITZAM_NULL_KEY = -1;
+
+typedef enum
+{
+ ITZAM_ERROR_SIGNATURE, // 00
+ ITZAM_ERROR_VERSION,
+ ITZAM_ERROR_64BIT_DB,
+ ITZAM_ERROR_WRITE_FAILED,
+ ITZAM_ERROR_OPEN_FAILED,
+ ITZAM_ERROR_READ_FAILED,
+ ITZAM_ERROR_CLOSE_FAILED,
+ ITZAM_ERROR_SEEK_FAILED,
+ ITZAM_ERROR_TELL_FAILED,
+ ITZAM_ERROR_DUPE_REMOVE,
+ ITZAM_ERROR_FLUSH_FAILED, // 10
+ ITZAM_ERROR_TOO_SMALL,
+ ITZAM_ERROR_PAGE_NOT_FOUND,
+ ITZAM_ERROR_LOST_KEY,
+ ITZAM_ERROR_KEY_NOT_WRITTEN,
+ ITZAM_ERROR_KEY_SEEK_FAILED,
+ ITZAM_ERROR_KEY_REMOVE_FAILED,
+ ITZAM_ERROR_REC_SEEK_FAILED,
+ ITZAM_ERROR_REC_REMOVE_FAILED,
+ ITZAM_ERROR_DELLIST_NOT_READ,
+ ITZAM_ERROR_DELLIST_NOT_WRITTEN, // 20
+ ITZAM_ERROR_ITERATOR_COUNT,
+ ITZAM_ERROR_REWRITE_DELETED,
+ ITZAM_ERROR_INVALID_COL,
+ ITZAM_ERROR_INVALID_ROW,
+ ITZAM_ERROR_INVALID_HASH,
+ ITZAM_ERROR_MALLOC,
+ ITZAM_ERROR_READ_DELETED,
+ ITZAM_ERROR_INVALID_RECORD,
+ ITZAM_ERROR_INVALID_FILE_LOCK_MODE,
+ ITZAM_ERROR_FILE_LOCK_FAILED, // 30
+ ITZAM_ERROR_UNLOCK_FAILED,
+ ITZAM_ERROR_REC_SIZE,
+ ITZAM_ERROR_TRANSACTION_OVERLAP,
+ ITZAM_ERROR_NO_TRANSACTION,
+ ITZAM_ERROR_CURSOR_COUNT,
+ ITZAM_ERROR_INVALID_DATAFILE_OBJECT,
+ ITZAM_ERROR_SIZE_T,
+ ITZAM_ERROR_FILE_CREATE,
+ ITZAM_ERROR_SHMEM_PRIVILEGE,
+ ITZAM_ERROR_SHMEM, // 40
+ ITZAM_ERROR_ALREADY_CREATED,
+ ITZAM_ERROR_READ_ONLY,
+ ITZAM_ERROR_TOO_LONG
+} itzam_error;
+
+typedef enum
+{
+ ITZAM_OKAY,
+ ITZAM_FAILED,
+ ITZAM_VERSION_ERROR,
+ ITZAM_AT_END,
+ ITZAM_AT_BEGIN,
+ ITZAM_NOT_FOUND,
+ ITZAM_DUPLICATE,
+ ITZAM_32BIT_OVERFLOW,
+ ITZAM_DATA_WRITE_FAILED,
+ ITZAM_REF_SIZE_MISMATCH,
+ ITZAM_READ_ONLY,
+ ITZAM_OVERWRITE_TOO_LONG
+} itzam_state;
+
+/*-----------------------------------------------------------------------------
+ * shared memory
+ */
+
+ITZAM_SHMEM_TYPE itzam_shmem_obtain(const char * name, size_t len, itzam_bool * creator);
+
+void itzam_shmem_close(ITZAM_SHMEM_TYPE shmem, const char * name);
+
+void * itzam_shmem_getptr(ITZAM_SHMEM_TYPE shmem, size_t len);
+
+itzam_bool itzam_shmem_commit(void * shmem_ptr);
+
+itzam_bool itzam_shmem_freeptr(void * shmem_ptr, size_t len);
+
+/*-----------------------------------------------------------------------------
+ * generalized file operations
+ */
+
+ITZAM_FILE_TYPE itzam_file_create(const char * filename);
+
+ITZAM_FILE_TYPE itzam_file_open(const char * filename);
+
+itzam_bool itzam_file_close(ITZAM_FILE_TYPE datafile);
+
+itzam_bool itzam_file_read(ITZAM_FILE_TYPE datafile, void * data, size_t len);
+
+itzam_bool itzam_file_write(ITZAM_FILE_TYPE datafile, const void * data, size_t len);
+
+itzam_ref itzam_file_seek(ITZAM_FILE_TYPE datafile, itzam_ref pos, int mode);
+
+itzam_ref itzam_file_tell(ITZAM_FILE_TYPE datafile);
+
+itzam_bool itzam_file_commit(ITZAM_FILE_TYPE datafile);
+
+itzam_bool itzam_file_remove(const char * filename);
+
+itzam_bool itzam_file_lock(ITZAM_FILE_TYPE datafile);
+
+itzam_bool itzam_file_unlock(ITZAM_FILE_TYPE datafile);
+
+/*-----------------------------------------------------------------------------
+ * general function types
+ */
+
+typedef void itzam_error_handler(const char * function_name, itzam_error error);
+
+extern itzam_error_handler * default_error_handler;
+
+void itzam_set_default_error_handler(itzam_error_handler * handler);
+
+/*-----------------------------------------------------------------------------
+ * utility function for normalizing system objects' names (mutex, shm, ...)
+ */
+
+void itzam_build_normalized_name(char * buffer, const char * format, const char * basename);
+
+/* functions of this type must return:
+ * < 0 key1 is before key2
+ * 0 keys are equal
+ * > 0 key1 is after key2
+ */
+typedef int itzam_key_comparator(const void * key1, const void * key2);
+
+/* function to select keys to be included in a subset
+ * itzam_true include key
+ * itzam_false don't include this key
+ */
+typedef itzam_bool itzam_key_selector(const void * key);
+
+/* built-in key comparisons
+ */
+int itzam_comparator_int32(const void * key1, const void * key2);
+int itzam_comparator_uint32(const void * key1, const void * key2);
+int itzam_comparator_string(const void * key1, const void * key2);
+
+/* a callback function used to retrieve whatever data is associated with a reference
+ */
+typedef itzam_bool itzam_export_callback(itzam_ref ref, void ** record, itzam_int * rec_len);
+
+/*-----------------------------------------------------------------------------
+ * datafile structures
+ */
+
+/* data file header, prefix for entire data file
+ */
+static const uint32_t ITZAM_DATAFILE_SIGNATURE = 0x4D5A5449; /* ITZM */
+static const uint32_t ITZAM_RECORD_SIGNATURE = 0x525A5449; /* ITZR */
+
+#if defined(ITZAM64)
+static const uint32_t ITZAM_DATAFILE_VERSION = 0x40050100; /* 64.MM.MM.MM */
+#else
+static const uint32_t ITZAM_DATAFILE_VERSION = 0x20050100; /* 32.MM.MM.MM */
+#endif
+
+static const itzam_int ITZAM_DELLIST_BLOCK_SIZE = 256;
+
+/* flags and masks for record identification
+ */
+static const int32_t ITZAM_RECORD_FLAGS_GENERAL = 0x000000ff;
+static const int32_t ITZAM_RECORD_IN_USE = 0x00000001;
+static const int32_t ITZAM_RECORD_DELLIST = 0x00000002;
+static const int32_t ITZAM_RECORD_SCHEMA = 0x00000004;
+static const int32_t ITZAM_RECORD_TRAN_HEADER = 0x00000010;
+static const int32_t ITZAM_RECORD_TRAN_RECORD = 0x00000020;
+
+static const int32_t ITZAM_RECORD_FLAGS_BTREE = 0x00000f00;
+static const int32_t ITZAM_RECORD_BTREE_HEADER = 0x00000100;
+static const int32_t ITZAM_RECORD_BTREE_PAGE = 0x00000200;
+static const int32_t ITZAM_RECORD_BTREE_KEY = 0x00000400;
+
+static const int32_t ITZAM_RECORD_FLAGS_MATRIX = 0x0000f000;
+static const int32_t ITZAM_RECORD_MATRIX_HEADER = 0x00001000;
+static const int32_t ITZAM_RECORD_MATRIX_TABLE = 0x00002000;
+
+static const int32_t ITZAM_RECORD_FLAGS_HASH = 0x000f0000;
+static const int32_t ITZAM_RECORD_HASH_HEADER = 0x00010000;
+static const int32_t ITZAM_RECORD_HASH_TABLE = 0x00020000;
+static const int32_t ITZAM_RECORD_HASH_KEY = 0x00040000;
+static const int32_t ITZAM_RECORD_HASH_LIST_ENTRY = 0x00080000;
+
+/* record header, prefix for every record in a datafile
+ */
+typedef struct t_itzam_record_header
+{
+ uint32_t m_signature; /* four bytes that identify this as an itzam record */
+ uint32_t m_flags; /* a set of ITZAM_RECORD_* bit settings */
+ itzam_int m_length; /* record length */
+ itzam_int m_rec_len; /* number of bytes in use by actual data */
+}
+itzam_record_header;
+
+/* structures for list of deleted records
+ */
+typedef struct t_itzam_dellist_header
+{
+ itzam_int m_table_size; /* number of entries in the table */
+}
+itzam_dellist_header;
+
+typedef struct t_itzam_dellist_entry
+{
+ itzam_ref m_where; /* reference for this deleted record */
+ itzam_int m_length; /* length of the deleted record */
+}
+itzam_dellist_entry;
+
+/* transaction definitions
+ */
+typedef enum
+{
+ ITZAM_TRAN_OP_WRITE,
+ ITZAM_TRAN_OP_REMOVE,
+ ITZAM_TRAN_OP_OVERWRITE
+}
+itzam_op_type;
+
+/* data file header
+ */
+typedef struct t_itzam_datafile_header
+{
+ uint32_t m_signature; /* file type signature */
+ uint32_t m_version; /* version of this file type */
+ itzam_ref m_dellist_ref; /* position of deleted list in file */
+ itzam_ref m_schema_ref; /* position of XML schema that describes this file */
+ itzam_ref m_index_list_ref; /* position of index reference list */
+ itzam_ref m_transaction_tail; /* last item in the current transaction */
+}
+itzam_datafile_header;
+
+/* transaction operation header
+ */
+typedef struct t_itzam_op_header
+{
+ itzam_op_type m_type;
+ itzam_ref m_where;
+ itzam_ref m_prev_tran;
+ itzam_record_header m_record_header;
+ itzam_ref m_record_where;
+}
+itzam_op_header;
+
+/* definition of shared memory used by all isnatnces of a datafile
+ */
+typedef struct t_itzam_datafile_shared
+{
+ int m_count; /* header information */
+ itzam_datafile_header m_header; /* header information */
+#if defined(ITZAM_UNIX)
+ pthread_mutex_t m_mutex; /* shared mutex */
+#endif
+}
+itzam_datafile_shared;
+
+/* working storage for a loaded datafile
+ */
+typedef struct t_itzam_datafile
+{
+ /* OS file information */
+ ITZAM_FILE_TYPE m_file; /* file associated with this datafile */
+ char * m_filename; /* filename for this datafile */
+
+ /* list of deleted records */
+ itzam_dellist_header m_dellist_header; /* header for current deleted record list */
+ itzam_dellist_entry * m_dellist; /* pointer to list of deleted records */
+
+ /* transaction tracking file information */
+ char * m_tran_file_name; /* transaction filename */
+ struct t_itzam_datafile * m_tran_file; /* transaction file */
+
+#if defined(ITZAM_WINDOWS)
+ /* shared (named) mutex */
+ HANDLE m_mutex; /* mutex information */
+#endif
+
+ /* shared memory, used to communicate between threads and processes */
+ ITZAM_SHMEM_TYPE m_shmem; /* system link to shared memory */
+ char * m_shmem_name; /* name of shared memory block */
+ itzam_datafile_shared * m_shared; /* items shared among all instances */
+
+ /* error handling */
+ itzam_error_handler * m_error_handler; /* function to handle errors that occur */
+
+ /* flags */
+ itzam_bool m_is_open; /* is the file currently open? */
+ itzam_bool m_read_only; /* is the file open read-only? */
+ itzam_bool m_file_locked; /* is the file currently locked? */
+ itzam_bool m_in_transaction; /* are we journalizing a transaction? */
+ itzam_bool m_tran_replacing; /* set when a write replaces a record during a write */
+
+ /* file locking */
+#if defined(ITZAM_UNIX)
+ struct flock m_file_lock; /* fcntl lock */
+#endif
+}
+itzam_datafile;
+
+/*-----------------------------------------------------------------------------
+ * prototypes for variable-length reference data file
+ */
+
+itzam_bool itzam_datafile_exists(const char * filename);
+
+itzam_state itzam_datafile_create(itzam_datafile * datafile,
+ const char * filename);
+
+itzam_state itzam_datafile_open(itzam_datafile * datafile,
+ const char * filename,
+ itzam_bool recover,
+ itzam_bool read_only);
+
+itzam_state itzam_datafile_close(itzam_datafile * datafile);
+
+void itzam_datafile_mutex_lock(itzam_datafile * datafile);
+
+void itzam_datafile_mutex_unlock(itzam_datafile * datafile);
+
+itzam_bool itzam_datafile_file_lock(itzam_datafile * datafile);
+
+itzam_bool itzam_datafile_file_unlock(itzam_datafile * datafile);
+
+itzam_bool itzam_datafile_is_open(itzam_datafile * datafile);
+
+float itzam_datafile_get_version(itzam_datafile * datafile);
+
+void itzam_datafile_set_error_handler(itzam_datafile * datafile,
+ itzam_error_handler * error_handler);
+
+itzam_ref itzam_datafile_tell(itzam_datafile * datafile);
+
+itzam_state itzam_datafile_seek(itzam_datafile * datafile,
+ itzam_ref pos);
+
+itzam_state itzam_datafile_rewind(itzam_datafile * datafile);
+
+/* INTERNAL FUNCTION -- DO NOT USE EXPLICITLY */
+itzam_ref itzam_datafile_get_next_open(itzam_datafile * datafile,
+ itzam_int length);
+
+itzam_ref itzam_datafile_write_flags(itzam_datafile * datafile,
+ const void * data,
+ itzam_int length,
+ itzam_ref where,
+ int32_t flags);
+
+itzam_ref itzam_datafile_write(itzam_datafile * datafile,
+ const void * data,
+ itzam_int length,
+ itzam_ref where);
+
+itzam_state itzam_datafile_overwrite(itzam_datafile * datafile,
+ const void * data,
+ itzam_int length,
+ itzam_ref where,
+ itzam_int offset);
+
+itzam_state itzam_datafile_read(itzam_datafile * datafile,
+ void * data,
+ itzam_int max_length);
+
+itzam_state itzam_datafile_read_alloc(itzam_datafile * datafile,
+ void ** data,
+ itzam_int * length);
+
+itzam_state itzam_datafile_remove(itzam_datafile * datafile);
+
+itzam_state itzam_datafile_transaction_start(itzam_datafile * datafile);
+
+itzam_state itzam_datafile_transaction_commit(itzam_datafile * datafile);
+
+itzam_state itzam_datafile_transaction_rollback(itzam_datafile * datafile);
+
+/*-----------------------------------------------------------------------------
+ * B-tree types data structures
+ */
+
+/* page header, prefix for pages
+ */
+typedef struct t_itzam_btree_page_header
+{
+ itzam_ref m_where; /* location in page file */
+ itzam_ref m_parent; /* parent in page file */
+ uint16_t m_key_count; /* actual # of keys */
+}
+itzam_btree_page_header;
+
+/* a page in the B-tree
+ */
+typedef struct t_itzam_btree_page
+{
+ /* the actual data for this reference
+ */
+ itzam_byte * m_data;
+
+ /* header information
+ */
+ itzam_btree_page_header * m_header;
+
+ /* these elements are pointers into m_data
+ */
+ itzam_byte * m_keys; /* array of key/data objects */
+ itzam_ref * m_links; /* links to other pages */
+}
+itzam_btree_page;
+
+/* B-tree constants
+ */
+static const uint32_t ITZAM_BTREE_VERSION = 0x00040000;
+static const uint16_t ITZAM_BTREE_ORDER_MINIMUM = 4;
+static const uint16_t ITZAM_BTREE_ORDER_DEFAULT = 25;
+
+/* B-tree header
+ */
+typedef struct t_itzam_btree_header
+{
+ uint32_t m_version; /* version of this file structure */
+ uint32_t m_sizeof_page; /* sizeof(itzam_btree_page) used in creating this btreefile */
+ uint32_t m_sizeof_key; /* size of data being stored in pages */
+ uint16_t m_order; /* number of keys per page */
+ uint64_t m_count; /* counts number of active records */
+ uint64_t m_ticker; /* counts the total number of new records added over the life of this btree datafile*/
+ itzam_ref m_where; /* pointer to location of this header */
+ itzam_ref m_root_where; /* pointer to location of root reference in file */
+ itzam_ref m_schema_ref; /* links to a copy of the XML schema for this table (future use) */
+}
+itzam_btree_header;
+
+/* working storage for a loaded B-tree
+ */
+typedef struct t_itzam_btree
+{
+ itzam_datafile * m_datafile; /* file associated with this btree file */
+ ITZAM_SHMEM_TYPE m_shmem_header; /* memory map for the header */
+ char * m_shmem_header_name; /* name associated with memory map */
+ itzam_btree_header * m_header; /* header information */
+ ITZAM_SHMEM_TYPE m_shmem_root; /* memory map for the header */
+ char * m_shmem_root_name; /* name associated with memory map */
+ itzam_byte * m_root_data; /* actual root data */
+ itzam_btree_page m_root; /* root structure */
+ itzam_bool m_free_datafile; /* free the datafile object when closed */
+ uint16_t m_links_size; /* number of links per page; order + 1; calculated at creation time */
+ uint16_t m_min_keys; /* minimum # of keys; order / 2; calculated at creation time */
+ uint16_t m_cursor_count; /* Number of active cursors */
+ itzam_key_comparator * m_key_comparator; /* function to compare keys */
+ itzam_ref m_saved_header; /* temporary header saved during transaction, for use in a rollback */
+}
+itzam_btree;
+
+/*-----------------------------------------------------------------------------
+ * prototypes for B-tree file
+ */
+
+itzam_state itzam_btree_create(itzam_btree * btree,
+ const char * filename,
+ uint16_t order,
+ itzam_int key_size,
+ itzam_key_comparator * key_comparator,
+ itzam_error_handler * error_handler);
+
+itzam_state itzam_btree_open(itzam_btree * btree,
+ const char * filename,
+ itzam_key_comparator * key_comparator,
+ itzam_error_handler * error_handler,
+ itzam_bool recover,
+ itzam_bool read_only);
+
+itzam_state itzam_btree_close(itzam_btree * btree);
+
+uint64_t itzam_btree_count(itzam_btree * btree);
+
+uint64_t itzam_btree_ticker(itzam_btree * btree);
+
+void itzam_btree_mutex_lock(itzam_btree * btree);
+
+void itzam_btree_mutex_unlock(itzam_btree * btree);
+
+itzam_bool itzam_btree_file_lock(itzam_btree * btree);
+
+itzam_bool itzam_btree_file_unlock(itzam_btree * btree);
+
+itzam_bool itzam_btree_is_open(itzam_btree * btree);
+
+void itzam_btree_set_error_handler(itzam_btree * btree,
+ itzam_error_handler * error_handler);
+
+itzam_state itzam_btree_insert(itzam_btree * btree, const void * key);
+
+itzam_bool itzam_btree_find(itzam_btree * btree, const void * search_key, void * result);
+
+itzam_state itzam_btree_remove(itzam_btree * btree, const void * key);
+
+uint16_t itzam_btree_cursor_count(itzam_btree * btree);
+
+itzam_state itzam_btree_transaction_start(itzam_btree * btree);
+
+itzam_state itzam_btree_transaction_commit(itzam_btree * btree);
+
+itzam_state itzam_btree_transaction_rollback(itzam_btree * btree);
+
+/*-----------------------------------------------------------------------------
+ * B-tree cursor structures
+ */
+
+typedef struct t_itzam_btree_cursor_memory
+{
+ struct t_itzam_btree_cursor_memory * m_prev;
+ itzam_bool m_done;
+ size_t m_index;
+}
+itzam_btree_cursor_memory;
+
+typedef struct t_itzam_btree_cursor
+{
+ itzam_btree * m_btree;
+ itzam_btree_page * m_page;
+ size_t m_index;
+ itzam_btree_cursor_memory * m_parent_memory;
+}
+itzam_btree_cursor;
+
+/* B-tree cursor functions
+ */
+
+itzam_state itzam_btree_cursor_create(itzam_btree_cursor * cursor, itzam_btree * btree);
+
+itzam_bool itzam_btree_cursor_valid(itzam_btree_cursor * cursor);
+
+itzam_state itzam_btree_cursor_free(itzam_btree_cursor * cursor);
+
+itzam_bool itzam_btree_cursor_next(itzam_btree_cursor * cursor);
+
+itzam_bool itzam_btree_cursor_reset(itzam_btree_cursor * cursor);
+
+itzam_state itzam_btree_cursor_read(itzam_btree_cursor * cursor, void * returned_key);
+
+#pragma pack(pop)
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
diff --git a/src/itzam_btree.c b/src/itzam_btree.c new file mode 100644 index 0000000..6b21ee6 --- /dev/null +++ b/src/itzam_btree.c @@ -0,0 +1,1993 @@ +/*
+ Itzam/C (version 6.0) is an embedded database engine written in Standard C.
+
+ Copyright 2011 Scott Robert Ladd. All rights reserved.
+
+ Older versions of Itzam/C are:
+ Copyright 2002, 2004, 2006, 2008 Scott Robert Ladd. All rights reserved.
+
+ Ancestral code, from Java and C++ books by the author, is:
+ Copyright 1992, 1994, 1996, 2001 Scott Robert Ladd. All rights reserved.
+
+ Itzam/C is user-supported open source software. It's continued development is dependent on
+ financial support from the community. You can provide funding by visiting the Itzam/C
+ website at:
+
+ http://www.coyotegulch.com
+
+ You may license Itzam/C in one of two fashions:
+
+ 1) Simplified BSD License (FreeBSD License)
+
+ Redistribution and use in source and binary forms, with or without modification, are
+ permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice, this list of
+ conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ of conditions and the following disclaimer in the documentation and/or other materials
+ provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY SCOTT ROBERT LADD ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SCOTT ROBERT LADD OR
+ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ The views and conclusions contained in the software and documentation are those of the
+ authors and should not be interpreted as representing official policies, either expressed
+ or implied, of Scott Robert Ladd.
+
+ 2) Closed-Source Proprietary License
+
+ If your project is a closed-source or proprietary project, the Simplified BSD License may
+ not be appropriate or desirable. In such cases, contact the Itzam copyright holder to
+ arrange your purchase of an appropriate license.
+
+ The author can be contacted at:
+
+ scott.ladd@coyotegulch.com
+ scott.ladd@gmail.com
+ http:www.coyotegulch.com
+*/
+
+#include "itzam.h"
+
+#include <stdlib.h>
+#include <ctype.h>
+
+static pthread_mutex_t global_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+static itzam_state update_header(itzam_btree * btree)
+{
+ itzam_state result = ITZAM_FAILED;
+
+ /* rewrite file header
+ */
+ itzam_ref where = itzam_datafile_write_flags(btree->m_datafile,
+ btree->m_header,
+ sizeof(itzam_btree_header),
+ btree->m_header->m_where,
+ ITZAM_RECORD_BTREE_HEADER);
+
+ if (where == btree->m_header->m_where)
+ result = ITZAM_OKAY;
+
+ return result;
+}
+
+static itzam_btree_page * dupe_page(const itzam_btree * btree, const itzam_btree_page * source_page)
+{
+ itzam_btree_page * page = NULL;
+
+ /* allocate page
+ */
+ page = (itzam_btree_page *)malloc(sizeof(itzam_btree_page));
+
+ if (page != NULL)
+ {
+ /* allocate data space
+ */
+ page->m_data = (itzam_byte *)malloc(btree->m_header->m_sizeof_page);
+
+ if (page->m_data != NULL)
+ {
+ page->m_header = (itzam_btree_page_header *)page->m_data;
+ page->m_keys = (itzam_byte *)(page->m_data + sizeof(itzam_btree_page_header));
+ page->m_links = (itzam_ref *)(page->m_data + sizeof(itzam_btree_page_header) + btree->m_header->m_sizeof_key * btree->m_header->m_order);
+ memcpy(page->m_data, source_page->m_data, btree->m_header->m_sizeof_page);
+ }
+ else
+ {
+ free(page);
+ page = NULL;
+ }
+ }
+
+ return page;
+}
+
+static void set_page_pointers(const itzam_btree * btree, itzam_btree_page * page)
+{
+ page->m_header = (itzam_btree_page_header *)page->m_data;
+ page->m_keys = (itzam_byte *)(page->m_data + sizeof(itzam_btree_page_header));
+ page->m_links = (itzam_ref *)(page->m_data + sizeof(itzam_btree_page_header) + btree->m_header->m_sizeof_key * btree->m_header->m_order);
+}
+
+static void init_page(const itzam_btree * btree, itzam_btree_page * page)
+{
+ int n;
+
+ page->m_header->m_where = ITZAM_NULL_REF;
+ page->m_header->m_parent = ITZAM_NULL_REF;
+ page->m_header->m_key_count = 0;
+
+ memset(page->m_keys, 0, btree->m_header->m_sizeof_key * btree->m_header->m_order);
+
+ for (n = 0; n < btree->m_header->m_order + 1; ++n)
+ page->m_links[n] = ITZAM_NULL_REF;
+}
+
+static void set_page(const itzam_btree * btree, itzam_btree_page * page, itzam_byte * memory)
+{
+ if ((page != NULL) && (memory != NULL))
+ {
+ page->m_data = memory;
+ set_page_pointers(btree,page);
+ }
+}
+
+static itzam_btree_page * alloc_page(const itzam_btree * btree)
+{
+ itzam_btree_page * page = NULL;
+
+ /* allocate page
+ */
+ page = (itzam_btree_page *)malloc(sizeof(itzam_btree_page));
+
+ if (page != NULL)
+ {
+ /* allocate data space
+ */
+ page->m_data = (itzam_byte *)malloc(btree->m_header->m_sizeof_page);
+
+ if (page->m_data != NULL)
+ {
+ set_page_pointers(btree,page);
+ init_page(btree,page);
+ }
+ else
+ {
+ /* error; clean up and return NULL
+ */
+ free(page);
+ page = NULL;
+ }
+ }
+
+ return page;
+}
+
+static void free_page(itzam_btree_page * page)
+{
+ if ((page != NULL) && (page->m_header->m_parent != ITZAM_NULL_REF))
+ {
+ if (page->m_data != NULL)
+ free(page->m_data);
+
+ free(page);
+ }
+}
+
+static void set_root(itzam_btree * btree, itzam_btree_page * new_root)
+{
+ memcpy(btree->m_root_data, new_root->m_data, btree->m_header->m_sizeof_page);
+ btree->m_header->m_root_where = new_root->m_header->m_where;
+ update_header(btree);
+ new_root->m_data = NULL;
+}
+
+/* can't be static because it's useful for debug/analysis routines
+ */
+itzam_btree_page * read_page(itzam_btree * btree, itzam_ref where)
+{
+ itzam_btree_page * page = NULL;
+
+ /* position datafile
+ */
+ if (ITZAM_OKAY == itzam_datafile_seek(btree->m_datafile,where))
+ {
+ /* allocate a buffer
+ */
+ page = alloc_page(btree);
+
+ if (page != NULL)
+ {
+ /* read the ref
+ */
+ if (ITZAM_OKAY != itzam_datafile_read(btree->m_datafile,page->m_data,btree->m_header->m_sizeof_page))
+ {
+ free_page(page);
+ page = NULL;
+ }
+ }
+ }
+
+ return page;
+}
+
+static itzam_ref write_page(itzam_btree * btree, itzam_btree_page * page)
+{
+ itzam_ref where;
+
+ /* does this page have a location, i.e., is it new?
+ */
+ if (page->m_header->m_where == ITZAM_NULL_REF)
+ page->m_header->m_where = itzam_datafile_get_next_open(btree->m_datafile, btree->m_header->m_sizeof_page);
+
+ where = itzam_datafile_write_flags(btree->m_datafile,
+ page->m_data,
+ btree->m_header->m_sizeof_page,
+ page->m_header->m_where,
+ ITZAM_RECORD_BTREE_PAGE);
+
+ /* make sure things got put where we thought they did
+ */
+ if (where != page->m_header->m_where)
+ where = ITZAM_NULL_REF;
+
+ return where;
+}
+
+int itzam_comparator_int32(const void * key1, const void * key2)
+{
+ int result = 0;
+
+ int32_t * k1 = (int32_t *)key1;
+ int32_t * k2 = (int32_t *)key2;
+
+ if (*k1 < *k2)
+ result = -1;
+ else if (*k1 > *k2)
+ result = 1;
+
+ return result;
+}
+
+int itzam_comparator_uint32(const void * key1, const void * key2)
+{
+ int result = 0;
+
+ uint32_t * k1 = (uint32_t *)key1;
+ uint32_t * k2 = (uint32_t *)key2;
+
+ if (*k1 < *k2)
+ result = -1;
+ else if (*k1 > *k2)
+ result = 1;
+
+ return result;
+}
+
+int itzam_comparator_string(const void * key1, const void * key2)
+{
+ return strcmp((const char *)key1,(const char *)key2);
+}
+
+static char * get_shared_name(const char * fmt, const char * filename)
+{
+ char * result = (char *)malloc(strlen(fmt) + strlen(filename) + 1);
+
+ char * norm = strdup(filename);
+ char * c = norm;
+
+ while (*c)
+ {
+ if (!isalnum(*c))
+ *c = '_';
+ else
+ if (isalpha(*c))
+ *c = tolower(*c);
+
+ ++c;
+ }
+
+ sprintf(result, fmt, norm);
+
+ free(norm);
+
+ return result;
+}
+
+#if defined(ITZAM_UNIX)
+static const char * HDR_NAME_MASK = "/%s-ItzamBTreeHeader";
+static const char * ROOT_NAME_MASK = "/%s-ItzamBTreeRoot";
+#else
+static const char * HDR_NAME_MASK = "Global\\%s-ItzamBTreeHeader";
+static const char * ROOT_NAME_MASK = "Global\\%s-ItzamBTreeRoot";
+#endif
+
+#define MAKE_ITZAM_BHNAME(basename) get_shared_name(HDR_NAME_MASK,basename)
+#define MAKE_ITZAM_ROOT_NAME(basename) get_shared_name(ROOT_NAME_MASK,basename)
+
+itzam_state itzam_btree_create(itzam_btree * btree,
+ const char * filename,
+ uint16_t order,
+ itzam_int key_size,
+ itzam_key_comparator * key_comparator,
+ itzam_error_handler * error_handler)
+{
+ itzam_state result = ITZAM_FAILED;
+ itzam_bool creator;
+
+ pthread_mutex_lock(&global_mutex);
+
+ /* make sure the arguments make sense
+ */
+ if ((btree != NULL) && (filename != NULL) && (key_size > 0) && (key_comparator != NULL))
+ {
+ /* allocate datafile
+ */
+ btree->m_datafile = (itzam_datafile *)malloc(sizeof(itzam_datafile));
+
+ if (btree->m_datafile != NULL)
+ {
+ /* create data file
+ */
+ result = itzam_datafile_create(btree->m_datafile,filename);
+
+ if (ITZAM_OKAY == result)
+ {
+ /* If an error handler was provided, assign it to the datafile
+ */
+ if (error_handler != NULL)
+ itzam_datafile_set_error_handler(btree->m_datafile,error_handler);
+
+ /* allocate memory for shared header
+ */
+ btree->m_shmem_header_name = MAKE_ITZAM_BHNAME(filename);
+
+ btree->m_shmem_header = itzam_shmem_obtain(btree->m_shmem_header_name, sizeof(itzam_btree_header),&creator);
+ btree->m_header = (itzam_btree_header *)itzam_shmem_getptr(btree->m_shmem_header, sizeof(itzam_btree_header));
+
+ /* fill in structure
+ */
+ if (order < ITZAM_BTREE_ORDER_MINIMUM)
+ order = ITZAM_BTREE_ORDER_MINIMUM;
+
+ btree->m_header->m_version = ITZAM_BTREE_VERSION;
+ btree->m_header->m_order = order;
+ btree->m_header->m_count = 0;
+ btree->m_header->m_ticker = 0;
+ btree->m_header->m_schema_ref = ITZAM_NULL_REF;
+
+ btree->m_free_datafile = itzam_true;
+ btree->m_links_size = btree->m_header->m_order + 1;
+ btree->m_min_keys = btree->m_header->m_order / 2;
+ btree->m_key_comparator = key_comparator;
+ btree->m_cursor_count = 0;
+
+ btree->m_header->m_where = itzam_datafile_get_next_open(btree->m_datafile,sizeof(itzam_btree_header));
+ btree->m_header->m_root_where = 0;
+ btree->m_header->m_sizeof_key = key_size;
+ btree->m_header->m_sizeof_page = sizeof(itzam_btree_page_header)
+ + btree->m_header->m_sizeof_key * btree->m_header->m_order
+ + sizeof(itzam_ref) * btree->m_links_size;
+
+ /* write header for first time (lacks root pointer info, but needs to occupy space in the file)
+ */
+ if (btree->m_header->m_where == itzam_datafile_write_flags(btree->m_datafile, btree->m_header, sizeof(itzam_btree_header), btree->m_header->m_where, ITZAM_RECORD_BTREE_HEADER))
+ {
+ /* allocate memory for shared header
+ */
+ btree->m_shmem_root_name = MAKE_ITZAM_ROOT_NAME(filename);
+ btree->m_shmem_root = itzam_shmem_obtain(btree->m_shmem_root_name, btree->m_header->m_sizeof_page, &creator);
+ btree->m_root_data = (itzam_byte *)itzam_shmem_getptr(btree->m_shmem_root, btree->m_header->m_sizeof_page);
+ set_page(btree, &btree->m_root, btree->m_root_data);
+ init_page(btree, &btree->m_root);
+
+ /* assign root a file position
+ */
+ btree->m_root.m_header->m_where = itzam_datafile_get_next_open(btree->m_datafile, btree->m_header->m_sizeof_page);
+
+ if (btree->m_root.m_header->m_where != ITZAM_NULL_REF)
+ {
+ /* write root page
+ */
+ btree->m_header->m_root_where = itzam_datafile_write_flags(btree->m_datafile,
+ btree->m_root.m_data,
+ btree->m_header->m_sizeof_page,
+ btree->m_root.m_header->m_where,
+ ITZAM_RECORD_BTREE_PAGE);
+
+
+ /* make certain that the root was written where we expect it to be
+ */
+ if ((btree->m_header->m_root_where == btree->m_root.m_header->m_where) && (btree->m_header->m_root_where != ITZAM_NULL_REF))
+ result = update_header(btree);
+
+ /* make certain root was written
+ */
+ itzam_file_commit(btree->m_datafile->m_file);
+ }
+ }
+
+ /* remember to deallocate this datafile at closing
+ */
+ btree->m_free_datafile = itzam_true;
+
+ result = ITZAM_OKAY;
+ }
+ }
+ }
+ else
+ default_error_handler("itzam_btree_create",ITZAM_ERROR_INVALID_DATAFILE_OBJECT);
+
+ pthread_mutex_unlock(&global_mutex);
+
+ return result;
+}
+
+itzam_state itzam_btree_open(itzam_btree * btree,
+ const char * filename,
+ itzam_key_comparator * key_comparator,
+ itzam_error_handler * error_handler,
+ itzam_bool recover,
+ itzam_bool read_only)
+{
+ /* what we return
+ */
+ itzam_state result = ITZAM_FAILED;
+ itzam_bool creator;
+
+ pthread_mutex_lock(&global_mutex);
+
+ /* make sure the arguments make sense
+ */
+ if ((btree != NULL) && (filename != NULL) && (key_comparator != NULL))
+ {
+ /* allocate datafile
+ */
+ btree->m_datafile = (itzam_datafile *)malloc(sizeof(itzam_datafile));
+
+ if (btree->m_datafile != NULL)
+ {
+ /* create data file
+ */
+ result = itzam_datafile_open(btree->m_datafile,filename,recover,read_only);
+
+ if (ITZAM_OKAY == result)
+ {
+ /* If an error handler was provided, assign it to the datafile
+ */
+ if (error_handler != NULL)
+ itzam_datafile_set_error_handler(btree->m_datafile,error_handler);
+
+ /* do the actual create
+ */
+ btree->m_free_datafile = itzam_true;
+ btree->m_key_comparator = key_comparator;
+ btree->m_cursor_count = 0;
+
+ /* allocate memory for embedded header
+ */
+ btree->m_shmem_header_name = MAKE_ITZAM_BHNAME(filename);
+ btree->m_shmem_header = itzam_shmem_obtain(btree->m_shmem_header_name, sizeof(itzam_btree_header),&creator);
+ btree->m_header = (itzam_btree_header *)itzam_shmem_getptr(btree->m_shmem_header, sizeof(itzam_btree_header));
+
+ /* assumes first record is header
+ */
+ if (ITZAM_OKAY == itzam_datafile_rewind(btree->m_datafile))
+ {
+ if (creator)
+ result = itzam_datafile_read(btree->m_datafile,btree->m_header,sizeof(itzam_btree_header));
+
+ if (ITZAM_OKAY == result)
+ {
+ /* verify version
+ */
+ if (btree->m_header->m_version == ITZAM_BTREE_VERSION)
+ {
+ /* finish intializing btree
+ */
+ btree->m_links_size = btree->m_header->m_order + 1;
+ btree->m_min_keys = btree->m_header->m_order / 2;
+
+ /* allocate memory for shared header
+ */
+ btree->m_shmem_root_name = MAKE_ITZAM_ROOT_NAME(filename);
+ btree->m_shmem_root = itzam_shmem_obtain(btree->m_shmem_root_name, btree->m_header->m_sizeof_page,&creator);
+ btree->m_root_data = (itzam_byte *)itzam_shmem_getptr(btree->m_shmem_root, btree->m_header->m_sizeof_page);
+ set_page(btree, &btree->m_root, btree->m_root_data);
+
+ /* read root
+ */
+ if (creator)
+ {
+ itzam_datafile_seek(btree->m_datafile, btree->m_header->m_root_where);
+ itzam_datafile_read(btree->m_datafile, btree->m_root_data, btree->m_header->m_sizeof_page);
+ }
+
+ result = ITZAM_OKAY;
+ }
+ else
+ result = ITZAM_VERSION_ERROR;
+ }
+ }
+
+ result = ITZAM_OKAY;
+ }
+ }
+ }
+ else
+ default_error_handler("itzam_btree_open",ITZAM_ERROR_INVALID_DATAFILE_OBJECT);
+
+ pthread_mutex_unlock(&global_mutex);
+
+ return result;
+}
+
+/* return number of active records in database
+ */
+uint64_t itzam_btree_count(itzam_btree * btree)
+{
+ uint64_t result = 0;
+
+ if (btree != NULL)
+ {
+ itzam_datafile_mutex_lock(btree->m_datafile);
+
+ if (ITZAM_OKAY == result)
+ result = btree->m_header->m_count;
+
+ itzam_datafile_mutex_unlock(btree->m_datafile);
+ }
+
+ return result;
+}
+
+/* return ticker, which is a count of every record ever added to the database
+ */
+uint64_t itzam_btree_ticker(itzam_btree * btree)
+{
+ uint64_t result = 0;
+
+ if (btree != NULL)
+ {
+ itzam_datafile_mutex_lock(btree->m_datafile);
+
+ if (ITZAM_OKAY == result)
+ result = btree->m_header->m_ticker;
+
+ itzam_datafile_mutex_unlock(btree->m_datafile);
+ }
+ else
+ {
+ default_error_handler("itzam_btree_ticker",ITZAM_ERROR_INVALID_DATAFILE_OBJECT);
+ }
+
+ return result;
+}
+
+/* close file
+ */
+itzam_state itzam_btree_close(itzam_btree * btree)
+{
+ itzam_state result = ITZAM_FAILED;
+
+ pthread_mutex_lock(&global_mutex);
+
+ /* make sure the arguments make sense
+ */
+ if ((btree != NULL) && (btree->m_cursor_count == 0))
+ {
+ if (!btree->m_datafile->m_read_only)
+ {
+ itzam_datafile_mutex_unlock(btree->m_datafile);
+ update_header(btree);
+ itzam_datafile_mutex_unlock(btree->m_datafile);
+ }
+
+ if (result == ITZAM_OKAY)
+ {
+ free(btree->m_datafile);
+ btree->m_datafile = NULL;
+ }
+
+ itzam_shmem_freeptr(btree->m_root_data, btree->m_header->m_sizeof_page);
+ itzam_shmem_close(btree->m_shmem_root, btree->m_shmem_root_name);
+ free(btree->m_shmem_root_name);
+
+ itzam_shmem_freeptr(btree->m_header, sizeof(itzam_btree_header));
+ itzam_shmem_close(btree->m_shmem_header,btree->m_shmem_header_name);
+ free(btree->m_shmem_header_name);
+
+ result = ITZAM_OKAY;
+ }
+ else
+ default_error_handler("itzam_btree_close",ITZAM_ERROR_INVALID_DATAFILE_OBJECT);
+
+ pthread_mutex_unlock(&global_mutex);
+
+ return result;
+}
+
+void itzam_btree_mutex_lock(itzam_btree * btree)
+{
+ if (btree != NULL)
+ itzam_datafile_mutex_lock(btree->m_datafile);
+ else
+ default_error_handler("itzam_btree_lock",ITZAM_ERROR_INVALID_DATAFILE_OBJECT);
+}
+
+void itzam_btree_mutex_unlock(itzam_btree * btree)
+{
+ if (btree != NULL)
+ itzam_datafile_mutex_unlock(btree->m_datafile);
+ else
+ default_error_handler("itzam_btree_unlock",ITZAM_ERROR_INVALID_DATAFILE_OBJECT);
+}
+
+/* lock btree file
+ */
+itzam_bool itzam_btree_file_lock(itzam_btree * btree)
+{
+ itzam_bool result = itzam_false;
+
+ if (btree != NULL)
+ {
+ itzam_datafile_mutex_lock(btree->m_datafile);
+ result = itzam_datafile_file_lock(btree->m_datafile);
+ itzam_datafile_mutex_unlock(btree->m_datafile);
+ }
+ else
+ default_error_handler("itzam_btree_lock",ITZAM_ERROR_INVALID_DATAFILE_OBJECT);
+
+ return result;
+}
+
+/* unlock btree file
+ */
+itzam_bool itzam_btree_file_unlock(itzam_btree * btree)
+{
+ itzam_bool result = itzam_false;
+
+ if (btree != NULL)
+ {
+ itzam_datafile_mutex_lock(btree->m_datafile);
+ result = itzam_datafile_file_unlock(btree->m_datafile);
+ itzam_datafile_mutex_unlock(btree->m_datafile);
+ }
+ else
+ default_error_handler("itzam_btree_unlock",ITZAM_ERROR_INVALID_DATAFILE_OBJECT);
+
+ return result;
+}
+
+itzam_bool itzam_btree_is_open(itzam_btree * btree)
+{
+ itzam_bool result = itzam_false;
+
+ if (btree != NULL)
+ {
+ itzam_datafile_mutex_lock(btree->m_datafile);
+ result = itzam_datafile_is_open(btree->m_datafile);
+ itzam_datafile_mutex_unlock(btree->m_datafile);
+ }
+ else
+ {
+ default_error_handler("itzam_btree_is_open",ITZAM_ERROR_INVALID_DATAFILE_OBJECT);
+ }
+
+ return result;
+}
+
+void itzam_btree_set_error_handler(itzam_btree * btree,
+ itzam_error_handler * error_handler)
+{
+ if (btree != NULL)
+ {
+ itzam_datafile_mutex_lock(btree->m_datafile);
+ itzam_datafile_set_error_handler(btree->m_datafile,error_handler);
+ itzam_datafile_mutex_unlock(btree->m_datafile);
+ }
+ else
+ {
+ default_error_handler("itzam_btree_set_error_handler",ITZAM_ERROR_INVALID_DATAFILE_OBJECT);
+ }
+}
+
+/* structure used to return search information
+ */
+typedef struct
+{
+ itzam_btree_page * m_page;
+ int m_index;
+ itzam_bool m_found;
+} search_result;
+
+static void search(itzam_btree * btree, const void * key, search_result * result)
+{
+ int index;
+
+ /* duplicate root
+ */
+ itzam_btree_page * page = &btree->m_root;
+
+ while (itzam_true)
+ {
+ index = 0;
+
+ /* if page is empty, we didn't find the key
+ */
+ if ((page == NULL) || (page->m_header->m_key_count == 0))
+ {
+ result->m_page = page;
+ result->m_index = index;
+ result->m_found = itzam_false;
+ return;
+ }
+ else
+ {
+ /* loop through keys
+ */
+ while (index < page->m_header->m_key_count)
+ {
+ int comp = btree->m_key_comparator(key,(const void *)(page->m_keys + index * btree->m_header->m_sizeof_key));
+
+ /* do we move on, or have we found it?
+ */
+ if (comp > 0)
+ ++index;
+ else
+ {
+ if (comp == 0)
+ {
+ result->m_page = page;
+ result->m_index = index;
+ result->m_found = itzam_true;
+ return;
+ }
+
+ break;
+ }
+ }
+
+ /* if we're in a leaf, the key hasn't been found
+ */
+ if (page->m_links[index] == ITZAM_NULL_REF)
+ {
+ result->m_page = page;
+ result->m_index = index;
+ result->m_found = itzam_false;
+ return;
+ }
+ else
+ {
+ /* read and search next page
+ */
+ itzam_btree_page * next_page = read_page(btree,page->m_links[index]);
+
+ if (page->m_header->m_parent != ITZAM_NULL_REF)
+ free_page(page);
+
+ page = next_page;
+ }
+ }
+ }
+}
+
+itzam_bool itzam_btree_find(itzam_btree * btree, const void * key, void * returned_key)
+{
+ search_result s;
+
+ s.m_found = itzam_false;
+ s.m_index = 0;
+ s.m_page = NULL;
+
+ if ((btree != NULL) && (key != NULL))
+ {
+ itzam_datafile_mutex_lock(btree->m_datafile);
+
+ search(btree,key,&s);
+
+ if ((s.m_found) && (returned_key != NULL))
+ memcpy(returned_key,s.m_page->m_keys + s.m_index * btree->m_header->m_sizeof_key, btree->m_header->m_sizeof_key);
+
+ if (s.m_page->m_header->m_parent != ITZAM_NULL_REF)
+ free_page(s.m_page);
+
+ itzam_datafile_mutex_unlock(btree->m_datafile);
+ }
+ else
+ {
+ default_error_handler("itzam_btree_find",ITZAM_ERROR_INVALID_DATAFILE_OBJECT);
+ }
+
+ return s.m_found;
+}
+
+/* promote key by creating new root
+ */
+static void promote_root(itzam_btree * btree,
+ itzam_byte * key,
+ itzam_btree_page * page_after)
+{
+ /* create a new root page
+ */
+ itzam_btree_page * new_root = alloc_page(btree);
+ itzam_btree_page * new_before = dupe_page(btree, &btree->m_root);
+
+ /* add key and links to root
+ */
+ memcpy(new_root->m_keys,key,btree->m_header->m_sizeof_key);
+ new_root->m_links[0] = new_before->m_header->m_where;
+ new_root->m_links[1] = page_after->m_header->m_where;
+ new_root->m_header->m_key_count = 1;
+
+ /* write new root to tree, and make it actual root internally
+ */
+ write_page(btree,new_root);
+ set_root(btree,new_root);
+
+ /* update children
+ */
+ new_before->m_header->m_parent = new_root->m_header->m_where;
+ page_after->m_header->m_parent = new_root->m_header->m_where;
+
+ write_page(btree,new_before);
+ write_page(btree,page_after);
+
+ free_page(new_before);
+ free_page(new_root);
+}
+
+/* promote key into parent
+ */
+static void promote_internal(itzam_btree * btree,
+ itzam_btree_page * page_insert,
+ itzam_byte * key,
+ itzam_ref link)
+{
+ if (page_insert->m_header->m_key_count == btree->m_header->m_order)
+ {
+ int nt = 0;
+ int ni = 0;
+ int insert_index = 0;
+
+ itzam_btree_page * page_sibling;
+ itzam_btree_page * child;
+
+ /* temporary array
+ */
+ itzam_byte * temp_keys = (itzam_byte *)malloc(btree->m_header->m_sizeof_key * (btree->m_header->m_order + 1));
+ itzam_ref * temp_links = (itzam_ref *)malloc(sizeof(itzam_ref) * (btree->m_header->m_order + 2));
+
+ if ((temp_keys == NULL) || (temp_links == NULL))
+ btree->m_datafile->m_error_handler("promote_internal", ITZAM_ERROR_MALLOC);
+
+ temp_links[0] = page_insert->m_links[0];
+
+ /* find insertion point
+ */
+ while ((insert_index < page_insert->m_header->m_key_count) && (btree->m_key_comparator(key,(const void *)(page_insert->m_keys + insert_index * btree->m_header->m_sizeof_key)) >= 0))
+ ++insert_index;
+
+ /* store new info
+ */
+ memcpy(temp_keys + insert_index * btree->m_header->m_sizeof_key, key, btree->m_header->m_sizeof_key);
+ temp_links[insert_index + 1] = link;
+
+ /* copy existing keys
+ */
+ while (ni < btree->m_header->m_order)
+ {
+ if (ni == insert_index)
+ ++nt;
+
+ memcpy(temp_keys + nt * btree->m_header->m_sizeof_key, page_insert->m_keys + ni * btree->m_header->m_sizeof_key, btree->m_header->m_sizeof_key);
+ temp_links[nt + 1] = page_insert->m_links[ni + 1];
+
+ ++ni;
+ ++nt;
+ }
+
+ /* generate a new leaf node
+ */
+ page_sibling = alloc_page(btree);
+ page_sibling->m_header->m_parent = page_insert->m_header->m_parent;
+
+ /* clear key counts
+ */
+ page_insert->m_header->m_key_count = 0;
+ page_sibling->m_header->m_key_count = 0;
+ page_insert->m_links[0] = temp_links[0];
+
+ /* copy keys from temp to pages
+ */
+ for (ni = 0; ni < btree->m_min_keys; ++ni)
+ {
+ memcpy(page_insert->m_keys + ni * btree->m_header->m_sizeof_key, temp_keys + ni * btree->m_header->m_sizeof_key, btree->m_header->m_sizeof_key);
+ page_insert->m_links[ni + 1] = temp_links[ni + 1];
+ ++page_insert->m_header->m_key_count;
+ }
+
+ page_sibling->m_links[0] = temp_links[btree->m_min_keys + 1];
+
+ for (ni = btree->m_min_keys + 1; ni <= btree->m_header->m_order; ++ni)
+ {
+ memcpy(page_sibling->m_keys + (ni - 1 - btree->m_min_keys) * btree->m_header->m_sizeof_key, temp_keys + ni * btree->m_header->m_sizeof_key, btree->m_header->m_sizeof_key);
+ page_sibling->m_links[ni - btree->m_min_keys] = temp_links[ni + 1];
+ ++page_sibling->m_header->m_key_count;
+ }
+
+ /* replace unused entries with null
+ */
+ for (ni = btree->m_min_keys; ni < btree->m_header->m_order; ++ni)
+ {
+ memset(page_insert->m_keys + ni * btree->m_header->m_sizeof_key, 0, btree->m_header->m_sizeof_key);
+ page_insert->m_links[ni + 1] = ITZAM_NULL_REF;
+ }
+
+ /* write pages
+ */
+ write_page(btree,page_insert);
+ write_page(btree,page_sibling);
+
+ if (page_insert->m_header->m_parent == ITZAM_NULL_REF)
+ set_root(btree,page_insert);
+
+ /* update parent links in child nodes
+ */
+ for (ni = 0; ni <= page_sibling->m_header->m_key_count; ++ni)
+ {
+ child = read_page(btree,page_sibling->m_links[ni]);
+
+ if (child != NULL)
+ {
+ child->m_header->m_parent = page_sibling->m_header->m_where;
+ write_page(btree,child);
+ }
+ else
+ btree->m_datafile->m_error_handler("promote_internal",ITZAM_ERROR_PAGE_NOT_FOUND);
+
+ free_page(child);
+ }
+
+ /* promote key and pointer
+ */
+ if (page_insert->m_header->m_parent == ITZAM_NULL_REF)
+ {
+ /* create a new root
+ */
+ promote_root(btree,
+ temp_keys + btree->m_min_keys * btree->m_header->m_sizeof_key,
+ page_sibling);
+ }
+ else
+ {
+ /* read parent and promote key
+ */
+ itzam_btree_page * parent_page = read_page(btree,page_insert->m_header->m_parent);
+
+ promote_internal(btree,
+ parent_page,
+ temp_keys + btree->m_min_keys * btree->m_header->m_sizeof_key,
+ page_sibling->m_header->m_where);
+
+ free_page(parent_page);
+ }
+
+ /* release resources
+ */
+ free_page(page_sibling);
+ free(temp_keys);
+ free(temp_links);
+ }
+ else
+ {
+ int n, insert_index = 0;
+
+ /* find insertion point
+ */
+ while ((insert_index < page_insert->m_header->m_key_count)
+ && (btree->m_key_comparator(key,(void *)(page_insert->m_keys + insert_index * btree->m_header->m_sizeof_key)) >= 0))
+ {
+ ++insert_index;
+ }
+
+ /* shift keys right
+ */
+ for (n = page_insert->m_header->m_key_count; n > insert_index; --n)
+ {
+ memcpy(page_insert->m_keys + n * btree->m_header->m_sizeof_key, page_insert->m_keys + (n - 1) * btree->m_header->m_sizeof_key, btree->m_header->m_sizeof_key);
+ page_insert->m_links[n + 1] = page_insert->m_links[n];
+ }
+
+ memcpy(page_insert->m_keys + insert_index * btree->m_header->m_sizeof_key, key, btree->m_header->m_sizeof_key);
+ page_insert->m_links[insert_index + 1] = link;
+
+ ++page_insert->m_header->m_key_count;
+
+ /* write updated page
+ */
+ write_page(btree,page_insert);
+
+ if (page_insert->m_header->m_parent == ITZAM_NULL_REF)
+ set_root(btree, page_insert);
+ }
+}
+
+static void write_key(itzam_btree * btree,
+ search_result * insert_info,
+ const itzam_byte * key)
+{
+ itzam_btree_page * page_sibling;
+ itzam_btree_page * page_parent;
+
+ /* check to see if page is full
+ */
+ if (insert_info->m_page->m_header->m_key_count == btree->m_header->m_order)
+ {
+ int nt, ni;
+
+ /* temporary array to store new items
+ */
+ itzam_byte * temp_keys = (itzam_byte *)malloc(btree->m_header->m_sizeof_key * (btree->m_header->m_order + 1));
+ memcpy(temp_keys + insert_info->m_index * btree->m_header->m_sizeof_key, key, btree->m_header->m_sizeof_key);
+
+ /* copy entries from insertion page to temps
+ */
+ nt = 0;
+ ni = 0;
+
+ while (ni < btree->m_header->m_order)
+ {
+ /* skip over inserted data
+ */
+ if (ni == insert_info->m_index)
+ ++nt;
+
+ /* copy data
+ */
+ memcpy(temp_keys + nt * btree->m_header->m_sizeof_key, insert_info->m_page->m_keys + ni * btree->m_header->m_sizeof_key, btree->m_header->m_sizeof_key);
+
+ /* next one
+ */
+ ++ni;
+ ++nt;
+ }
+
+ /* create a new leaf
+ */
+ page_sibling = alloc_page(btree);
+ page_sibling->m_header->m_parent = insert_info->m_page->m_header->m_parent;
+
+ /* clear key counts
+ */
+ insert_info->m_page->m_header->m_key_count = 0;
+ page_sibling->m_header->m_key_count = 0;
+
+ /* copy keys from temp to pages
+ */
+ for (ni = 0; ni < btree->m_min_keys; ++ni)
+ {
+ memcpy(insert_info->m_page->m_keys + ni * btree->m_header->m_sizeof_key, temp_keys + ni * btree->m_header->m_sizeof_key, btree->m_header->m_sizeof_key);
+ ++insert_info->m_page->m_header->m_key_count;
+ }
+
+ for (ni = btree->m_min_keys + 1; ni <= btree->m_header->m_order; ++ni)
+ {
+ memcpy(page_sibling->m_keys + (ni - 1 - btree->m_min_keys) * btree->m_header->m_sizeof_key, temp_keys + ni * btree->m_header->m_sizeof_key, btree->m_header->m_sizeof_key);
+ ++page_sibling->m_header->m_key_count;
+ }
+
+ /* replace remaining entries with null
+ */
+ for (ni = btree->m_min_keys; ni < btree->m_header->m_order; ++ni)
+ memset(insert_info->m_page->m_keys + ni * btree->m_header->m_sizeof_key,0,btree->m_header->m_sizeof_key);
+
+ /* write pages
+ */
+ write_page(btree,insert_info->m_page);
+ write_page(btree,page_sibling);
+
+ /* promote key and its pointer
+ */
+ if (insert_info->m_page->m_header->m_parent == ITZAM_NULL_REF)
+ {
+ /* creating a new root page
+ */
+ promote_root(btree,
+ temp_keys + btree->m_min_keys * btree->m_header->m_sizeof_key,
+ page_sibling);
+ }
+ else
+ {
+ /* read parent
+ */
+ page_parent = read_page(btree,insert_info->m_page->m_header->m_parent);
+
+ /* promote key into parent page
+ */
+ promote_internal(btree,
+ page_parent,
+ temp_keys + btree->m_min_keys * btree->m_header->m_sizeof_key,
+ page_sibling->m_header->m_where);
+
+ free_page(page_parent);
+ }
+
+ /* release sibling page
+ */
+ if (page_sibling->m_header->m_parent != ITZAM_NULL_REF)
+ free_page(page_sibling);
+
+ free(temp_keys);
+ }
+ else
+ {
+ int n;
+
+ /* move keys to make room for new one
+ */
+ for (n = insert_info->m_page->m_header->m_key_count; n > insert_info->m_index; --n)
+ memcpy(insert_info->m_page->m_keys + n * btree->m_header->m_sizeof_key, insert_info->m_page->m_keys + (n - 1) * btree->m_header->m_sizeof_key, btree->m_header->m_sizeof_key);
+
+ memcpy(insert_info->m_page->m_keys + insert_info->m_index * btree->m_header->m_sizeof_key, key, btree->m_header->m_sizeof_key);
+
+ ++insert_info->m_page->m_header->m_key_count;
+
+ /* write updated page
+ */
+ write_page(btree,insert_info->m_page);
+ }
+}
+
+itzam_state itzam_btree_insert(itzam_btree * btree,
+ const void * key)
+{
+ itzam_state result = ITZAM_FAILED;
+ search_result insert_info;
+
+ if ((btree != NULL) && (key != NULL) && (btree->m_cursor_count == 0))
+ {
+ itzam_datafile_mutex_lock(btree->m_datafile);
+
+ if (!btree->m_datafile->m_read_only)
+ {
+ search(btree,key,&insert_info);
+
+ if (!insert_info.m_found)
+ {
+ write_key(btree,&insert_info,(const itzam_byte *)key);
+ ++btree->m_header->m_count;
+ ++btree->m_header->m_ticker;
+
+ result = update_header(btree);
+ }
+ else
+ {
+ result = ITZAM_DUPLICATE;
+ }
+
+ if (insert_info.m_page->m_header->m_parent != ITZAM_NULL_REF)
+ free_page(insert_info.m_page);
+ }
+ else
+ result = ITZAM_READ_ONLY;
+
+ itzam_datafile_mutex_unlock(btree->m_datafile);
+ }
+
+ //return result;
+ return result;
+}
+
+static void redistribute(itzam_btree * btree,
+ int index,
+ itzam_btree_page * page_before,
+ itzam_btree_page * page_parent,
+ itzam_btree_page * page_after)
+{
+ if ((btree != NULL) && (page_before != NULL) && (page_parent != NULL) && (page_after != NULL))
+ {
+ /* check for leaf page
+ */
+ if (page_before->m_links[0] == ITZAM_NULL_REF)
+ {
+ if (page_before->m_header->m_key_count > page_after->m_header->m_key_count)
+ {
+ int n;
+
+ /* move a key from page_before to page_after
+ */
+ for (n = page_after->m_header->m_key_count; n > 0; --n)
+ memcpy(page_after->m_keys + n * btree->m_header->m_sizeof_key, page_after->m_keys + (n - 1) * btree->m_header->m_sizeof_key, btree->m_header->m_sizeof_key);
+
+ /* store parent separator in page_after
+ */
+ memcpy(page_after->m_keys, page_parent->m_keys + index * btree->m_header->m_sizeof_key, btree->m_header->m_sizeof_key);
+
+ /* increment page_after key count
+ */
+ ++page_after->m_header->m_key_count;
+
+ /* decrement page_before key count
+ */
+ --page_before->m_header->m_key_count;
+
+ /* move last key in page_before to page_parent as separator
+ */
+ memcpy(page_parent->m_keys + index * btree->m_header->m_sizeof_key, page_before->m_keys + page_before->m_header->m_key_count * btree->m_header->m_sizeof_key, btree->m_header->m_sizeof_key);
+
+ /* clear last key in page_before
+ */
+ memset(page_before->m_keys + page_before->m_header->m_key_count * btree->m_header->m_sizeof_key, 0, btree->m_header->m_sizeof_key);
+ }
+ else
+ {
+ int n;
+
+ /* add parent key to lesser page
+ */
+ memcpy(page_before->m_keys + page_before->m_header->m_key_count * btree->m_header->m_sizeof_key, page_parent->m_keys + index * btree->m_header->m_sizeof_key, btree->m_header->m_sizeof_key);
+
+ /* increment page_before key count
+ */
+ ++page_before->m_header->m_key_count;
+
+ /* move first key in page_after to page_parent as separator
+ */
+ memcpy(page_parent->m_keys + index * btree->m_header->m_sizeof_key, page_after->m_keys, btree->m_header->m_sizeof_key);
+
+ /* decrement page_after key count
+ */
+ --page_after->m_header->m_key_count;
+
+ /* move a key from page_after to page_before
+ */
+ for (n = 0; n < page_after->m_header->m_key_count; ++n)
+ memcpy(page_after->m_keys + n * btree->m_header->m_sizeof_key, page_after->m_keys + (n + 1) * btree->m_header->m_sizeof_key, btree->m_header->m_sizeof_key);
+
+ /* clear last key in page_after
+ */
+ memset(page_after->m_keys + n * btree->m_header->m_sizeof_key, 0, btree->m_header->m_sizeof_key);
+ }
+ }
+ else
+ {
+ itzam_btree_page * page_child;
+
+ if (page_before->m_header->m_key_count > page_after->m_header->m_key_count)
+ {
+ int n;
+
+ /* move a key from page_before to page_after
+ */
+ for (n = page_after->m_header->m_key_count; n > 0; --n)
+ {
+ memcpy(page_after->m_keys + n * btree->m_header->m_sizeof_key, page_after->m_keys + (n - 1) * btree->m_header->m_sizeof_key, btree->m_header->m_sizeof_key);
+ page_after->m_links[n + 1] = page_after->m_links[n];
+ }
+
+ page_after->m_links[1] = page_after->m_links[0];
+
+ /* store page_parent separator key in page_after
+ */
+ memcpy(page_after->m_keys, page_parent->m_keys + index * btree->m_header->m_sizeof_key, btree->m_header->m_sizeof_key);
+ page_after->m_links[0] = page_before->m_links[page_before->m_header->m_key_count];
+
+ /* update child link
+ */
+ page_child = read_page(btree,page_after->m_links[0]);
+
+ if (page_child != NULL)
+ {
+ page_child->m_header->m_parent = page_after->m_header->m_where;
+ write_page(btree,page_child);
+ free_page(page_child);
+ }
+ else
+ btree->m_datafile->m_error_handler("redistribute",ITZAM_ERROR_PAGE_NOT_FOUND);
+
+ /* increment page_after key count
+ */
+ ++page_after->m_header->m_key_count;
+
+ /* decrement page_before key count
+ */
+ --page_before->m_header->m_key_count;
+
+ /* move last key in page_before to page_parent as separator
+ */
+ memcpy(page_parent->m_keys + index * btree->m_header->m_sizeof_key, page_before->m_keys + page_before->m_header->m_key_count * btree->m_header->m_sizeof_key, btree->m_header->m_sizeof_key);
+
+ /* clear last key in page_before
+ */
+ memset(page_before->m_keys + page_before->m_header->m_key_count * btree->m_header->m_sizeof_key, 0, btree->m_header->m_sizeof_key);
+ page_before->m_links[page_before->m_header->m_key_count + 1] = ITZAM_NULL_REF;
+ }
+ else
+ {
+ int n;
+
+ /* store page_parent separator key in page_before
+ */
+ memcpy(page_before->m_keys + page_before->m_header->m_key_count * btree->m_header->m_sizeof_key, page_parent->m_keys + index * btree->m_header->m_sizeof_key, btree->m_header->m_sizeof_key);
+ page_before->m_links[page_before->m_header->m_key_count + 1] = page_after->m_links[0];
+
+ /* update child link
+ */
+ page_child = read_page(btree,page_after->m_links[0]);
+
+ if (page_child != NULL)
+ {
+ page_child->m_header->m_parent = page_before->m_header->m_where;
+ write_page(btree,page_child);
+ free_page(page_child);
+ }
+ else
+ btree->m_datafile->m_error_handler("redistribute",ITZAM_ERROR_PAGE_NOT_FOUND);
+
+ /* increment page_before key count
+ */
+ ++page_before->m_header->m_key_count;
+
+ /* move last key in page_after to page_parent as separator
+ */
+ memcpy(page_parent->m_keys + index * btree->m_header->m_sizeof_key, page_after->m_keys, btree->m_header->m_sizeof_key);
+
+ /* decrement page_after key count
+ */
+ --page_after->m_header->m_key_count;
+
+ /* move a key from page_after to page_before
+ */
+ for (n = 0; n < page_after->m_header->m_key_count; ++n)
+ {
+ memcpy(page_after->m_keys + n * btree->m_header->m_sizeof_key, page_after->m_keys + (n + 1) * btree->m_header->m_sizeof_key, btree->m_header->m_sizeof_key);
+ page_after->m_links[n] = page_after->m_links[n + 1];
+ }
+
+ page_after->m_links[n] = page_after->m_links[n + 1];
+
+ /* clear last key in page_after
+ */
+ memset(page_after->m_keys + n * btree->m_header->m_sizeof_key, 0, btree->m_header->m_sizeof_key);
+ page_after->m_links[n + 1] = ITZAM_NULL_REF;
+ }
+ }
+
+ /* write updated pages
+ */
+ write_page(btree,page_before);
+ write_page(btree,page_after);
+ write_page(btree,page_parent);
+
+ if(page_parent->m_header->m_parent == ITZAM_NULL_REF)
+ set_root(btree, page_parent);
+ }
+}
+
+static void adjust_tree(itzam_btree * btree, itzam_btree_page * page);
+
+static void concatenate(itzam_btree * btree, int index, itzam_btree_page * page_before, itzam_btree_page * page_parent, itzam_btree_page * page_after)
+{
+ int n, n2;
+
+ /* move separator key from page_parent into page_before
+ */
+ memcpy(page_before->m_keys + page_before->m_header->m_key_count * btree->m_header->m_sizeof_key, page_parent->m_keys + index * btree->m_header->m_sizeof_key, btree->m_header->m_sizeof_key);
+ page_before->m_links[page_before->m_header->m_key_count + 1] = page_after->m_links[0];
+
+ /* increment page_before key count
+ */
+ ++page_before->m_header->m_key_count;
+
+ /* delete separator from page_parent
+ */
+ --page_parent->m_header->m_key_count;
+
+ for (n = index; n < page_parent->m_header->m_key_count; ++n)
+ {
+ memcpy(page_parent->m_keys + n * btree->m_header->m_sizeof_key, page_parent->m_keys + (n + 1) * btree->m_header->m_sizeof_key, btree->m_header->m_sizeof_key);
+ page_parent->m_links[n + 1] = page_parent->m_links[n + 2];
+ }
+
+ /* clear unused key from parent
+ */
+ memset(page_parent->m_keys + n * btree->m_header->m_sizeof_key, 0, btree->m_header->m_sizeof_key);
+ page_parent->m_links[n + 1] = ITZAM_NULL_REF;
+
+ /* copy keys from page_after to page_before
+ */
+ n2 = 0;
+ n = page_before->m_header->m_key_count;
+
+ /* combine pages
+ */
+ while (n2 < page_after->m_header->m_key_count)
+ {
+ ++page_before->m_header->m_key_count;
+ memcpy(page_before->m_keys + n * btree->m_header->m_sizeof_key, page_after->m_keys + n2 * btree->m_header->m_sizeof_key, btree->m_header->m_sizeof_key);
+ page_before->m_links[n + 1] = page_after->m_links[n2 + 1];
+ ++n2;
+ ++n;
+ }
+
+ /* delete page_after
+ */
+ itzam_datafile_seek(btree->m_datafile,page_after->m_header->m_where);
+ itzam_datafile_remove(btree->m_datafile);
+
+ /* is this an inner page?
+ */
+ if (page_before->m_links[0] != ITZAM_NULL_REF)
+ {
+ /* adjust child pointers
+ */
+ itzam_btree_page * page_child;
+
+ for (n = 0; n <= page_before->m_header->m_key_count; ++n)
+ {
+ /* read child
+ */
+ page_child = read_page(btree,page_before->m_links[n]);
+
+ /* make sure the child was actually read
+ */
+ if (page_child != NULL)
+ {
+ page_child->m_header->m_parent = page_before->m_header->m_where;
+ write_page(btree,page_child);
+ free_page(page_child);
+ }
+ else
+ btree->m_datafile->m_error_handler("concatenate",ITZAM_ERROR_PAGE_NOT_FOUND);
+ }
+ }
+
+ /* write page_before and parent
+ */
+ if (page_parent->m_header->m_key_count == 0)
+ {
+ /* update before page with old parent's parent
+ */
+ page_before->m_header->m_parent = page_parent->m_header->m_parent;
+ write_page(btree,page_before);
+
+ if (page_before->m_header->m_parent == ITZAM_NULL_REF)
+ {
+ /* update the header if this is the new root
+ */
+ set_root(btree,page_before);
+ }
+ else
+ {
+ /* read parents's parent
+ */
+ itzam_btree_page * parents_parent = read_page(btree,page_parent->m_header->m_parent);
+
+ /* find parent reference and replace with before page
+ */
+ if (parents_parent != NULL)
+ {
+ for (n = 0; n < btree->m_links_size; ++n)
+ {
+ if (parents_parent->m_links[n] == page_parent->m_header->m_where)
+ {
+ parents_parent->m_links[n] = page_before->m_header->m_where;
+ write_page(btree,parents_parent);
+ }
+ }
+
+ free_page(parents_parent);
+ }
+ }
+
+ /* remove empty parent page
+ */
+ itzam_datafile_seek(btree->m_datafile,page_parent->m_header->m_where);
+ itzam_datafile_remove(btree->m_datafile);
+ }
+ else
+ {
+ /* write pages
+ */
+ write_page(btree,page_parent);
+ write_page(btree,page_before);
+
+ /* reset root page if needed
+ */
+ if (page_parent->m_header->m_parent == ITZAM_NULL_REF)
+ set_root(btree,page_parent);
+
+ /* if parent is too small, adjust
+ */
+ if (page_parent->m_header->m_key_count < btree->m_min_keys)
+ adjust_tree(btree,page_parent);
+ }
+}
+
+static void adjust_tree(itzam_btree * btree, itzam_btree_page * page)
+{
+ if ((btree != NULL) && (page != NULL) && (page->m_header->m_parent != ITZAM_NULL_REF))
+ {
+ /* get parent page
+ */
+ itzam_btree_page * page_parent = read_page(btree,page->m_header->m_parent);
+
+ if (page_parent != NULL)
+ {
+ itzam_btree_page * page_sibling_after = NULL;
+ itzam_btree_page * page_sibling_before = NULL;
+
+ /* find pointer to page
+ */
+ int n = 0;
+
+ while (page_parent->m_links[n] != page->m_header->m_where)
+ ++n;
+
+ /* read sibling pages
+ */
+ if (n < page_parent->m_header->m_key_count)
+ page_sibling_after = read_page(btree,page_parent->m_links[n+1]);
+
+ if (n > 0)
+ page_sibling_before = read_page(btree,page_parent->m_links[n-1]);
+
+ /* figure out what to do
+ */
+ if (page_sibling_before != NULL)
+ {
+ --n;
+
+ if (page_sibling_before->m_header->m_key_count > btree->m_min_keys)
+ redistribute(btree,n,page_sibling_before,page_parent,page);
+ else
+ concatenate(btree,n,page_sibling_before,page_parent,page);
+ }
+ else
+ {
+ if (page_sibling_after != NULL)
+ {
+ if (page_sibling_after->m_header->m_key_count > btree->m_min_keys)
+ redistribute(btree,n,page,page_parent,page_sibling_after);
+ else
+ concatenate(btree,n,page,page_parent,page_sibling_after);
+ }
+ }
+
+ if (page_sibling_before != NULL)
+ free_page(page_sibling_before);
+
+ if (page_sibling_after != NULL)
+ free_page(page_sibling_after);
+
+ if (page_parent->m_header->m_parent != ITZAM_NULL_REF)
+ free_page(page_parent);
+ }
+ }
+}
+
+itzam_state itzam_btree_remove(itzam_btree * btree, const void * key)
+{
+ itzam_state result = ITZAM_FAILED;
+ search_result remove_info;
+
+ if ((btree != NULL) && (key != NULL) && (btree->m_cursor_count == 0))
+ {
+ itzam_datafile_mutex_lock(btree->m_datafile);
+
+ if (btree->m_datafile->m_read_only)
+ result = ITZAM_READ_ONLY;
+ else
+ {
+ search(btree,key,&remove_info);
+
+ if (remove_info.m_found)
+ {
+ /* is this a leaf node?
+ */
+ if (remove_info.m_page->m_links[0] == ITZAM_NULL_REF)
+ {
+ int n;
+
+ /* removing key from leaf
+ */
+ --remove_info.m_page->m_header->m_key_count;
+
+ /* slide keys left over removed one
+ */
+ for (n = remove_info.m_index; n < remove_info.m_page->m_header->m_key_count; ++n)
+ memcpy(remove_info.m_page->m_keys + n * btree->m_header->m_sizeof_key, remove_info.m_page->m_keys + (n + 1) * btree->m_header->m_sizeof_key, btree->m_header->m_sizeof_key);
+
+ memset( remove_info.m_page->m_keys + remove_info.m_page->m_header->m_key_count * btree->m_header->m_sizeof_key, 0, btree->m_header->m_sizeof_key);
+
+ /* save page
+ */
+ write_page(btree,remove_info.m_page);
+
+ /* adjust the tree, if needed
+ */
+ if (remove_info.m_page->m_header->m_key_count < btree->m_min_keys)
+ adjust_tree(btree,remove_info.m_page);
+
+ result = ITZAM_OKAY;
+ }
+ else /* removing from an internal page */
+ {
+ /* get the successor page
+ */
+ itzam_btree_page * page_successor = read_page(btree,remove_info.m_page->m_links[remove_info.m_index + 1]);
+
+ if (page_successor != NULL)
+ {
+ int n;
+
+ while (page_successor->m_links[0] != ITZAM_NULL_REF)
+ {
+ itzam_btree_page * next_successor = read_page(btree,page_successor->m_links[0]);
+
+ /* check for null page in case of corrupted index
+ */
+ if (next_successor != NULL)
+ {
+ free_page(page_successor);
+ page_successor = next_successor;
+ }
+ else
+ btree->m_datafile->m_error_handler("itzam_btree_remove",ITZAM_ERROR_PAGE_NOT_FOUND);
+ }
+
+ /* first key is the "swappee"
+ */
+ memcpy(remove_info.m_page->m_keys + remove_info.m_index * btree->m_header->m_sizeof_key, page_successor->m_keys, btree->m_header->m_sizeof_key);
+
+ /* remove swapped key from successor page
+ */
+ --page_successor->m_header->m_key_count;
+
+ for (n = 0; n < page_successor->m_header->m_key_count; ++n)
+ memcpy(page_successor->m_keys + n * btree->m_header->m_sizeof_key, page_successor->m_keys + (n + 1) * btree->m_header->m_sizeof_key, btree->m_header->m_sizeof_key);
+
+ memset(page_successor->m_keys + page_successor->m_header->m_key_count * btree->m_header->m_sizeof_key, 0, btree->m_header->m_sizeof_key);
+
+ /* write modified pages
+ */
+ write_page(btree,remove_info.m_page);
+ write_page(btree,page_successor);
+
+ /* adjust tree for leaf node
+ */
+ if (page_successor->m_header->m_key_count < btree->m_min_keys)
+ adjust_tree(btree,page_successor);
+
+ result = ITZAM_OKAY;
+
+ free_page(page_successor);
+ }
+ }
+
+ /* decrement number of records in file
+ */
+ --btree->m_header->m_count;
+ update_header(btree);
+ }
+ else
+ {
+ result = ITZAM_NOT_FOUND;
+ }
+
+ if (remove_info.m_page->m_header->m_parent != ITZAM_NULL_REF)
+ free_page(remove_info.m_page);
+ }
+
+ itzam_datafile_mutex_unlock(btree->m_datafile);
+ }
+
+ return result;
+}
+
+uint16_t itzam_btree_cursor_count(itzam_btree * btree)
+{
+ uint16_t result = 0;
+
+ if (btree != NULL)
+ {
+ itzam_datafile_mutex_lock(btree->m_datafile);
+ result = btree->m_cursor_count;
+ itzam_datafile_mutex_unlock(btree->m_datafile);
+ }
+
+ return result;
+}
+
+itzam_state itzam_btree_transaction_start(itzam_btree * btree)
+{
+ itzam_state result = ITZAM_FAILED;
+
+ if (btree != NULL)
+ {
+ itzam_datafile_mutex_lock(btree->m_datafile);
+ result = itzam_datafile_transaction_start(btree->m_datafile);
+
+ if (result == ITZAM_OKAY)
+ btree->m_saved_header = itzam_datafile_write(btree->m_datafile->m_tran_file, btree->m_header, sizeof(itzam_btree_header), ITZAM_NULL_REF);
+ }
+
+ return result;
+}
+
+itzam_state itzam_btree_transaction_commit(itzam_btree * btree)
+{
+ itzam_state result = ITZAM_FAILED;
+
+ if (btree != NULL)
+ {
+ result = itzam_datafile_transaction_commit(btree->m_datafile);
+ itzam_datafile_mutex_unlock(btree->m_datafile);
+ }
+
+ return result;
+}
+
+itzam_state itzam_btree_transaction_rollback(itzam_btree * btree)
+{
+ itzam_state result = ITZAM_FAILED;
+ itzam_btree_page * old_root;
+
+ if (btree != NULL)
+ {
+ /* turn off transaction processing so we can restore
+ */
+ btree->m_datafile->m_in_transaction = itzam_false;
+ itzam_datafile_seek(btree->m_datafile->m_tran_file, btree->m_saved_header);
+ itzam_datafile_read(btree->m_datafile->m_tran_file, btree->m_header, sizeof(itzam_btree_header));
+ update_header(btree);
+
+ /* turn transaction processing on again
+ */
+ btree->m_datafile->m_in_transaction = itzam_true;
+ result = itzam_datafile_transaction_rollback(btree->m_datafile);
+
+ /* restore the root
+ */
+ old_root = read_page(btree,btree->m_header->m_root_where);
+ //free(btree->m_root.m_data);
+ memcpy(btree->m_root_data, old_root->m_data, btree->m_header->m_sizeof_page);
+
+ /* done here
+ */
+ itzam_datafile_mutex_unlock(btree->m_datafile);
+ }
+
+ return result;
+}
+
+/**
+ *------------------------------------------------------------
+ * B-tree cursor functions
+ */
+
+static itzam_bool reset_cursor(itzam_btree_cursor * cursor)
+{
+ itzam_bool result = itzam_false;
+ itzam_bool looking = itzam_true;
+
+ /* read root */
+ itzam_btree_page * next_page = NULL;
+ itzam_btree_cursor_memory * next_memory = NULL;
+ itzam_btree_page * page = &cursor->m_btree->m_root;
+
+ /* follow the tree to the first key in the sequence */
+ cursor->m_parent_memory = NULL;
+ cursor->m_page = NULL;
+ cursor->m_index = 0;
+
+ while (looking && (page != NULL))
+ {
+ if (page->m_header->m_key_count > 0)
+ {
+ if (page->m_links[0] == ITZAM_NULL_REF)
+ {
+ /* found the first key */
+ cursor->m_page = page;
+ cursor->m_index = 0;
+ result = itzam_true;
+ looking = itzam_false;
+ }
+ else
+ {
+ /* remember position in parent page */
+ next_memory = (itzam_btree_cursor_memory *)malloc(sizeof(itzam_btree_cursor_memory));
+
+ if (next_memory == NULL)
+ cursor->m_btree->m_datafile->m_error_handler("reset_sursor", ITZAM_ERROR_MALLOC);
+
+ next_memory->m_prev = cursor->m_parent_memory;
+ next_memory->m_index = 0;
+ cursor->m_parent_memory = next_memory;
+
+ /* move to next page */
+ next_page = read_page(cursor->m_btree,page->m_links[0]);
+
+ if (page->m_header->m_parent != ITZAM_NULL_REF)
+ free_page(page);
+
+ page = next_page;
+ }
+ }
+ else
+ {
+ free_page(page);
+ cursor->m_page = NULL;
+ cursor->m_index = 0;
+ cursor->m_parent_memory = NULL;
+ looking = itzam_false;
+ }
+ }
+
+ return result;
+}
+
+itzam_state itzam_btree_cursor_create(itzam_btree_cursor * cursor, itzam_btree * btree)
+{
+ itzam_state result = ITZAM_FAILED;
+
+ if ((cursor != NULL) && (btree != NULL))
+ {
+ /* keep reference to target tree */
+ cursor->m_btree = btree;
+
+ /* set cursor to first index key */
+ if (reset_cursor(cursor))
+ {
+ /* increment count of cursors in btree */
+ ++cursor->m_btree->m_cursor_count;
+ result = ITZAM_OKAY;
+ }
+ }
+
+ return result;
+}
+
+itzam_bool itzam_btree_cursor_valid(itzam_btree_cursor * cursor)
+{
+ return (cursor->m_page != NULL);
+}
+
+itzam_state itzam_btree_cursor_free(itzam_btree_cursor * cursor)
+{
+ itzam_state result = ITZAM_FAILED;
+
+ if ((cursor != NULL) && (cursor->m_page != NULL))
+ {
+ /* decrement btree cursor count */
+ if (cursor->m_btree->m_cursor_count > 0)
+ {
+ --cursor->m_btree->m_cursor_count;
+ result = ITZAM_OKAY;
+ }
+ else
+ cursor->m_btree->m_datafile->m_error_handler("itzam_btree_cursor_free",ITZAM_ERROR_CURSOR_COUNT);
+ }
+
+ return result;
+}
+
+itzam_bool itzam_btree_cursor_next(itzam_btree_cursor * cursor)
+{
+ itzam_bool result = itzam_false;
+ itzam_bool looking = itzam_true;
+ itzam_btree_page * next_page = NULL;
+ itzam_btree_cursor_memory * temp_memory = NULL;
+
+ if ((cursor != NULL) && (cursor->m_page != NULL))
+ {
+ ++cursor->m_index;
+
+ /* is this a leaf page? */
+ if (cursor->m_page->m_links[cursor->m_index] == ITZAM_NULL_REF)
+ {
+ while (looking)
+ {
+ /* are we at the end of the leaf page? */
+ if (cursor->m_index == cursor->m_page->m_header->m_key_count)
+ {
+ /* do we have a oarent to go back to? */
+ if (cursor->m_page->m_header->m_parent == ITZAM_NULL_REF)
+ {
+ /* at end of keys */
+ result = itzam_false;
+ looking = itzam_false;
+ }
+ else
+ {
+ /* return to parent */
+ next_page = read_page(cursor->m_btree,cursor->m_page->m_header->m_parent);
+
+ /* make new page our current page */
+ if (cursor->m_page->m_header->m_parent != ITZAM_NULL_REF)
+ free_page(cursor->m_page);
+
+ cursor->m_page = next_page;
+
+ /* restore parent page position, remove old parent memory */
+ temp_memory = cursor->m_parent_memory;
+ cursor->m_index = cursor->m_parent_memory->m_index;
+ cursor->m_parent_memory = cursor->m_parent_memory->m_prev;
+ free(temp_memory);
+
+ result = itzam_true;
+ looking = itzam_true;
+ }
+ }
+ else
+ {
+ /* we're on the next key */
+ result = itzam_true;
+ looking = itzam_false;
+ }
+ }
+ }
+ else /* inner page */
+ {
+ while (itzam_true)
+ {
+ /* read next page */
+ next_page = read_page(cursor->m_btree,cursor->m_page->m_links[cursor->m_index]);
+
+ /* remember position in parent page */
+ temp_memory = (itzam_btree_cursor_memory *)malloc(sizeof(itzam_btree_cursor_memory));
+ temp_memory->m_prev = cursor->m_parent_memory;
+ temp_memory->m_index = cursor->m_index;
+ cursor->m_parent_memory = temp_memory;
+
+ /* start at beginning of new page */
+ cursor->m_index = 0;
+
+ /* make new page our current page */
+ if (cursor->m_page->m_header->m_parent != ITZAM_NULL_REF)
+ free_page(cursor->m_page);
+
+ cursor->m_page = next_page;
+
+ /* do we need to push and move up again? */
+ if ((cursor->m_index == 0) && (cursor->m_page->m_header->m_parent != ITZAM_NULL_REF) && (cursor->m_page->m_links[0] != ITZAM_NULL_REF))
+ continue;
+ else
+ break;
+ }
+
+ result = itzam_true;
+ }
+ }
+
+ return result;
+}
+
+itzam_bool itzam_btree_cursor_reset(itzam_btree_cursor * cursor)
+{
+ return reset_cursor(cursor);
+}
+
+itzam_state itzam_btree_cursor_read(itzam_btree_cursor * cursor, void * returned_key)
+{
+ itzam_state result = ITZAM_NOT_FOUND;
+
+ if ((cursor != NULL) && (returned_key != NULL) && (cursor->m_page != NULL))
+ {
+ memcpy(returned_key, cursor->m_page->m_keys + cursor->m_index * cursor->m_btree->m_header->m_sizeof_key, cursor->m_btree->m_header->m_sizeof_key);
+ result = ITZAM_OKAY;
+ }
+
+ return result;
+}
diff --git a/src/itzam_data.c b/src/itzam_data.c new file mode 100644 index 0000000..b4b31d8 --- /dev/null +++ b/src/itzam_data.c @@ -0,0 +1,1573 @@ +/*
+ Itzam/C (version 6.0) is an embedded database engine written in Standard C.
+
+ Copyright 2011 Scott Robert Ladd. All rights reserved.
+
+ Older versions of Itzam/C are:
+ Copyright 2002, 2004, 2006, 2008 Scott Robert Ladd. All rights reserved.
+
+ Ancestral code, from Java and C++ books by the author, is:
+ Copyright 1992, 1994, 1996, 2001 Scott Robert Ladd. All rights reserved.
+
+ Itzam/C is user-supported open source software. It's continued development is dependent on
+ financial support from the community. You can provide funding by visiting the Itzam/C
+ website at:
+
+ http://www.coyotegulch.com
+
+ You may license Itzam/C in one of two fashions:
+
+ 1) Simplified BSD License (FreeBSD License)
+
+ Redistribution and use in source and binary forms, with or without modification, are
+ permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice, this list of
+ conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ of conditions and the following disclaimer in the documentation and/or other materials
+ provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY SCOTT ROBERT LADD ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SCOTT ROBERT LADD OR
+ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ The views and conclusions contained in the software and documentation are those of the
+ authors and should not be interpreted as representing official policies, either expressed
+ or implied, of Scott Robert Ladd.
+
+ 2) Closed-Source Proprietary License
+
+ If your project is a closed-source or proprietary project, the Simplified BSD License may
+ not be appropriate or desirable. In such cases, contact the Itzam copyright holder to
+ arrange your purchase of an appropriate license.
+
+ The author can be contacted at:
+
+ scott.ladd@coyotegulch.com
+ scott.ladd@gmail.com
+ http:www.coyotegulch.com
+*/
+
+#include "itzam.h"
+
+#include <stdlib.h>
+#include <errno.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+static pthread_mutex_t global_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+/*-----------------------------------------------------------------------------
+ * deleted record list management
+ */
+static itzam_state read_dellist(itzam_datafile * datafile)
+{
+ itzam_state result = ITZAM_FAILED;
+
+ if (datafile->m_shared->m_header.m_dellist_ref != ITZAM_NULL_REF)
+ {
+ if (-1 != itzam_file_seek(datafile->m_file,datafile->m_shared->m_header.m_dellist_ref + sizeof(itzam_record_header),ITZAM_SEEK_BEGIN))
+ {
+ /* read the header
+ */
+ if (itzam_file_read(datafile->m_file,&datafile->m_dellist_header,sizeof(itzam_dellist_header)))
+ {
+ itzam_int size = sizeof(itzam_dellist_entry) * datafile->m_dellist_header.m_table_size;
+
+ if (datafile->m_dellist != NULL)
+ free(datafile->m_dellist);
+
+ datafile->m_dellist = (itzam_dellist_entry *)malloc(size);
+
+ if (datafile->m_dellist != NULL)
+ {
+ if (itzam_file_read(datafile->m_file,datafile->m_dellist,size))
+ result = ITZAM_OKAY;
+ }
+ else
+ datafile->m_error_handler("read_dellist",ITZAM_ERROR_MALLOC);
+ }
+ }
+
+ if (result != ITZAM_OKAY)
+ datafile->m_error_handler("read_dellist",ITZAM_ERROR_DELLIST_NOT_READ);
+ }
+ else
+ datafile->m_dellist = NULL;
+
+ return result;
+}
+
+static itzam_state write_dellist(itzam_datafile * datafile, itzam_bool has_grown)
+{
+ itzam_int size = sizeof(itzam_dellist_entry) * datafile->m_dellist_header.m_table_size;
+ itzam_state result = ITZAM_FAILED;
+ itzam_record_header header;
+
+ if (has_grown)
+ {
+ /* remove old record if there was one
+ */
+ if (datafile->m_shared->m_header.m_dellist_ref != ITZAM_NULL_REF)
+ {
+ if (-1 != itzam_file_seek(datafile->m_file,datafile->m_shared->m_header.m_dellist_ref,ITZAM_SEEK_BEGIN))
+ {
+ /* read the header
+ */
+ if (itzam_file_read(datafile->m_file,&header,sizeof(header)))
+ {
+ /* change record header; make this record the head of the deleted list
+ */
+ header.m_flags &= ~ITZAM_RECORD_IN_USE;
+ header.m_flags &= ~ITZAM_RECORD_DELLIST;
+
+ /* move to beginning of record header again and rewrite it
+ */
+ itzam_file_seek(datafile->m_file,datafile->m_shared->m_header.m_dellist_ref,ITZAM_SEEK_BEGIN);
+
+ if (itzam_file_write(datafile->m_file,&header,sizeof(header)))
+ result = ITZAM_OKAY;
+ }
+ }
+ }
+
+ /* explicitly append; we can't use write because it might try to change the
+ * deleted list while we're saving it
+ */
+ if (-1 != itzam_file_seek(datafile->m_file,0,ITZAM_SEEK_END))
+ {
+ datafile->m_shared->m_header.m_dellist_ref = itzam_file_tell(datafile->m_file);
+
+ if (datafile->m_shared->m_header.m_dellist_ref > 0)
+ {
+ /* write new record header
+ */
+ itzam_record_header rec_head;
+
+ rec_head.m_signature = ITZAM_RECORD_SIGNATURE;
+ rec_head.m_flags = ITZAM_RECORD_IN_USE | ITZAM_RECORD_DELLIST;
+ rec_head.m_length = sizeof(itzam_dellist_header) + size;
+ rec_head.m_rec_len = rec_head.m_length;
+
+ if (itzam_file_write(datafile->m_file,&rec_head,sizeof(itzam_record_header)))
+ {
+ /* write the list header
+ */
+ if (itzam_file_write(datafile->m_file,&datafile->m_dellist_header,sizeof(itzam_dellist_header)))
+ {
+ /* write the list
+ */
+ if (itzam_file_write(datafile->m_file,datafile->m_dellist,size))
+ {
+ /* update the header with the pointer to the new deleted record list
+ */
+ if (-1 != itzam_file_seek(datafile->m_file,0,ITZAM_SEEK_BEGIN))
+ {
+ if (itzam_file_write(datafile->m_file,&datafile->m_shared->m_header,sizeof(itzam_datafile_header)))
+ result = ITZAM_OKAY;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ if (-1 != itzam_file_seek(datafile->m_file,datafile->m_shared->m_header.m_dellist_ref + sizeof(itzam_record_header),ITZAM_SEEK_BEGIN))
+ {
+ /* write the header
+ */
+ if (itzam_file_write(datafile->m_file,&datafile->m_dellist_header,sizeof(itzam_dellist_header)))
+ {
+ /* write the list
+ */
+ if (itzam_file_write(datafile->m_file,datafile->m_dellist,size))
+ result = ITZAM_OKAY;
+ }
+ }
+ }
+
+ if (result != ITZAM_OKAY)
+ datafile->m_error_handler("write_dellist",ITZAM_ERROR_DELLIST_NOT_WRITTEN);
+
+ return result;
+}
+
+/*-----------------------------------------------------------------------------
+ * utilities
+ */
+static char * get_tranfile_name(const char * filename)
+{
+ static const char * tran_name_mask = "%s.itzamtran";
+ char * result = (char *)malloc(strlen(tran_name_mask) + strlen(filename) + 1);
+ sprintf(result,tran_name_mask,filename);
+ return result;
+}
+
+itzam_bool itzam_datafile_exists(const char * filename)
+{
+ struct stat info;
+ return (0 == stat(filename, &info));
+}
+
+static char * get_shared_name(const char * mask, const char * filename)
+{
+ char * result = (char *)malloc(strlen(mask) + strlen(filename) + 1);
+
+ char * norm = strdup(filename);
+ char * c = norm;
+
+ while (*c)
+ {
+ if (!isalnum(*c))
+ *c = '_';
+ else
+ if (isalpha(*c))
+ *c = tolower(*c);
+
+ ++c;
+ }
+
+ sprintf(result, mask, norm);
+
+ free(norm);
+
+ return result;
+}
+
+#if defined(ITZAM_UNIX)
+static const char * shared_mask = "/%s_ItzamSharedDatafile";
+#else
+static const char * shared_mask = "Global\\%s_ItzamSharedDatafile";
+static const char * mutex_mask = "Global\\%s_ItzamMutex";
+#endif
+
+/*-----------------------------------------------------------------------------
+ * datafile functions
+ */
+itzam_state itzam_datafile_create(itzam_datafile * datafile, const char * filename)
+{
+ itzam_datafile_header header;
+#if defined(ITZAM_UNIX)
+ pthread_mutexattr_t attr;
+#else
+ char * mutex_name;
+#endif
+ itzam_state result = ITZAM_FAILED;
+ itzam_bool creator;
+
+ pthread_mutex_lock(&global_mutex);
+
+ /* verify arguments before proceeding
+ */
+ if (datafile != NULL)
+ {
+ /* fill header
+ */
+ header.m_signature = ITZAM_DATAFILE_SIGNATURE;
+ header.m_version = ITZAM_DATAFILE_VERSION;
+ header.m_dellist_ref = ITZAM_NULL_REF;
+ header.m_schema_ref = ITZAM_NULL_REF;
+ header.m_index_list_ref = ITZAM_NULL_REF;
+ header.m_transaction_tail = ITZAM_NULL_REF;
+
+ /* fill remaining datafile members
+ */
+#if defined(ITZAM_UNIX)
+ datafile->m_file = -1;
+ datafile->m_shmem = -1;
+#else
+ datafile->m_file = NULL;
+ datafile->m_shmem = NULL;
+#endif
+ datafile->m_filename = strdup(filename);
+ datafile->m_dellist = NULL;
+ datafile->m_tran_file = NULL;
+ datafile->m_tran_replacing = itzam_false;
+ datafile->m_shared = NULL;
+ datafile->m_is_open = itzam_false;
+ datafile->m_file_locked = itzam_false;
+ datafile->m_in_transaction = itzam_false;
+ datafile->m_error_handler = default_error_handler;
+
+#if defined(ITZAM_UNIX)
+ memset(&datafile->m_file_lock,0,sizeof(struct flock));
+#endif
+
+ /* generate transaction file name
+ */
+ datafile->m_tran_file_name = get_tranfile_name(filename);;
+
+ /* generate shared memory for header
+ */
+ datafile->m_shmem_name = get_shared_name(shared_mask, filename);
+
+ /* create OS-level file
+ */
+ datafile->m_file = itzam_file_create(filename);
+
+ if (ITZAM_GOOD_FILE(datafile->m_file))
+ {
+ /* write header
+ */
+ if (itzam_file_write(datafile->m_file,&header,sizeof(itzam_datafile_header)))
+ {
+ itzam_file_commit(datafile->m_file);
+
+ /* generate shared memory and fill it
+ */
+ datafile->m_shmem = itzam_shmem_obtain(datafile->m_shmem_name, sizeof(itzam_datafile_shared), &creator);
+ datafile->m_shared = (itzam_datafile_shared *)itzam_shmem_getptr(datafile->m_shmem, sizeof(itzam_datafile_shared));
+ datafile->m_shared->m_count = 1;
+
+ /* obtain mutex
+ */
+#if defined(ITZAM_UNIX)
+ pthread_mutexattr_init(&attr);
+ pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
+ pthread_mutex_init(&datafile->m_shared->m_mutex, &attr);
+#else
+ mutex_name = get_shared_name(mutex_mask,filename);
+
+ datafile->m_mutex = OpenMutex(MUTEX_ALL_ACCESS, FALSE, (LPCSTR)name);
+
+ if (datafile->m_mutex == NULL)
+ datafile->m_mutex = CreateMutex(NULL, FALSE, (LPCSTR)name);
+
+ free(mutex_name);
+#endif
+ datafile->m_read_only = itzam_false; /* can't be read only durign creation */
+ memcpy(&datafile->m_shared->m_header, &header, sizeof(itzam_datafile_header));
+
+ result = ITZAM_OKAY;
+ }
+ else
+ default_error_handler("itzam_datafile_create",ITZAM_ERROR_WRITE_FAILED);
+ }
+ else
+ default_error_handler("itzam_datafile_create",ITZAM_ERROR_FILE_CREATE);
+
+ datafile->m_is_open = (result == ITZAM_OKAY);
+ }
+ else
+ default_error_handler("itzam_datafile_create",ITZAM_ERROR_INVALID_DATAFILE_OBJECT);
+
+ pthread_mutex_unlock(&global_mutex);
+
+ return result;
+}
+
+itzam_state itzam_datafile_open(itzam_datafile * datafile,
+ const char * filename,
+ itzam_bool recover,
+ itzam_bool read_only)
+{
+ itzam_bool have_header = itzam_false;
+ itzam_bool creator = itzam_false;
+#if defined(ITZAM_UNIX)
+ pthread_mutexattr_t attr;
+#else
+ char * mutex_name;
+#endif
+ itzam_state result = ITZAM_FAILED;
+
+ pthread_mutex_lock(&global_mutex);
+
+ /* verify arguments before proceeding
+ */
+ if (datafile != NULL)
+ {
+ /* set default error handler
+ */
+ datafile->m_error_handler = default_error_handler;
+ datafile->m_tran_file = NULL;
+ datafile->m_tran_replacing = itzam_false;
+ datafile->m_file_locked = itzam_false;
+ datafile->m_is_open = itzam_false;
+ datafile->m_dellist = NULL;
+ datafile->m_in_transaction = itzam_false;
+
+#if defined(ITZAM_UNIX)
+ memset(&datafile->m_file_lock,0,sizeof(struct flock));
+#endif
+
+ /* assumes that datafile is a clean structure (i.e., it isn't open already)
+ */
+ datafile->m_file = itzam_file_open(filename);
+
+ if (ITZAM_GOOD_FILE(datafile->m_file))
+ {
+ datafile->m_is_open = itzam_true;
+
+ //itzam_datafile_file_lock(datafile);
+
+ /* get tranfile name
+ */
+ datafile->m_tran_file_name = get_tranfile_name(filename);;
+
+ /* get shared memory
+ */
+ datafile->m_shmem_name = get_shared_name(shared_mask, filename);
+ datafile->m_shmem = itzam_shmem_obtain(datafile->m_shmem_name, sizeof(itzam_datafile_shared), &creator);
+ datafile->m_shared = (itzam_datafile_shared *)itzam_shmem_getptr(datafile->m_shmem, sizeof(itzam_datafile_shared));
+
+#if defined(ITZAM_UNIX)
+ if (creator)
+ {
+ pthread_mutexattr_init(&attr);
+ pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
+ pthread_mutex_init(&datafile->m_shared->m_mutex, &attr);
+ }
+#else
+ mutex_name = get_shared_name(mutex_mask,filename);
+
+ datafile->m_mutex = OpenMutex(MUTEX_ALL_ACCESS, FALSE, (LPCSTR)name);
+
+ if (datafile->m_mutex == NULL)
+ datafile->m_mutex = CreateMutex(NULL, FALSE, (LPCSTR)name);
+
+ free(mutex_name);
+#endif
+
+ datafile->m_read_only = read_only;
+
+ if (creator)
+ datafile->m_shared->m_count = 1;
+ else
+ datafile->m_shared->m_count += 1;
+
+ /* read the header
+ */
+ if (creator) // (datafile->m_shared->m_header.m_signature != ITZAM_DATAFILE_SIGNATURE) && (creator))
+ have_header = itzam_file_read(datafile->m_file, &datafile->m_shared->m_header, sizeof(itzam_datafile_header));
+ else
+ have_header = itzam_true;
+
+ if (have_header)
+ {
+ /* verify signature and version
+ */
+ if (datafile->m_shared->m_header.m_signature == ITZAM_DATAFILE_SIGNATURE)
+ {
+ if (datafile->m_shared->m_header.m_version == ITZAM_DATAFILE_VERSION)
+ {
+ /* read deleted list, if any
+ */
+ if (datafile->m_shared->m_header.m_dellist_ref != ITZAM_NULL_REF)
+ result = read_dellist(datafile);
+ else
+ result = ITZAM_OKAY;
+
+ /* if we have a dangling transaction, roll it back
+ */
+ if (recover && (datafile->m_shared->m_header.m_transaction_tail != ITZAM_NULL_REF))
+ itzam_datafile_transaction_rollback(datafile);
+ }
+ else
+ datafile->m_error_handler("itzam_datafile_open",ITZAM_ERROR_VERSION);
+ }
+ else
+ datafile->m_error_handler("itzam_datafile_open",ITZAM_ERROR_SIGNATURE);
+ }
+ else
+ datafile->m_error_handler("itzam_datafile_open",ITZAM_ERROR_OPEN_FAILED);
+ }
+
+ /* can size_t hold the types of references used by this file?
+ */
+ if (result == ITZAM_OKAY)
+ {
+ size_t refbytes = (int)((datafile->m_shared->m_header.m_version & 0xFF000000) >> 24) / 8;
+
+ if (REF_BYTES != refbytes)
+ result = ITZAM_REF_SIZE_MISMATCH;
+ }
+
+ //itzam_datafile_file_unlock(datafile);
+ }
+ else
+ default_error_handler("itzam_datafile_open",ITZAM_ERROR_INVALID_DATAFILE_OBJECT);
+
+ pthread_mutex_unlock(&global_mutex);
+
+ return result;
+}
+
+itzam_state itzam_datafile_close(itzam_datafile * datafile)
+{
+ itzam_state result = ITZAM_FAILED;
+ itzam_bool last_owner = itzam_false;
+
+ pthread_mutex_lock(&global_mutex);
+
+ if (datafile != NULL)
+ {
+ itzam_datafile_mutex_lock(datafile);
+ datafile->m_shared->m_count -= 1;
+ itzam_datafile_mutex_unlock(datafile);
+
+ last_owner = (datafile->m_shared->m_count <= 0) ? itzam_true : itzam_false;
+
+ if (last_owner)
+ #if defined(ITZAM_UNIX)
+ pthread_mutex_destroy(&datafile->m_shared->m_mutex);
+ #else
+ CloseHandle(datafile->m_mutex);
+ #endif
+
+ if (datafile->m_dellist != NULL)
+ {
+ free(datafile->m_dellist);
+ datafile->m_dellist = NULL;
+ }
+
+ free(datafile->m_tran_file_name);
+
+ itzam_shmem_freeptr(datafile->m_shared, sizeof(itzam_datafile_shared));
+
+ if (last_owner)
+ itzam_shmem_close(datafile->m_shmem, datafile->m_shmem_name);
+
+ free(datafile->m_shmem_name);
+
+ if (itzam_file_close(datafile->m_file))
+ {
+ datafile->m_is_open = itzam_false;
+ result = ITZAM_OKAY;
+ }
+ else
+ datafile->m_error_handler("itzam_datafile_close",ITZAM_ERROR_CLOSE_FAILED);
+ }
+ else
+ default_error_handler("itzam_datafile_close",ITZAM_ERROR_INVALID_DATAFILE_OBJECT);
+
+ pthread_mutex_unlock(&global_mutex);
+
+ return result;
+}
+
+void itzam_datafile_mutex_lock(itzam_datafile * datafile)
+{
+#if defined(ITZAM_UNIX)
+ pthread_mutex_lock(&datafile->m_shared->m_mutex);
+#else
+ WaitForSingleObject(datafile->m_mutex, INFINITE);
+#endif
+}
+
+void itzam_datafile_mutex_unlock(itzam_datafile * datafile)
+{
+#if defined(ITZAM_UNIX)
+ pthread_mutex_unlock(&datafile->m_shared->m_mutex);
+#else
+ ReleaseMutex(datafile->m_mutex);
+#endif
+}
+
+itzam_bool itzam_datafile_file_lock(itzam_datafile * datafile)
+{
+ itzam_bool result = itzam_false;
+
+ if ((datafile != NULL) && (datafile->m_is_open))
+ {
+ if (datafile->m_file_locked)
+ result = itzam_true;
+ else
+ {
+#if defined(ITZAM_UNIX)
+ memset(&datafile->m_file_lock,0,sizeof(struct flock));
+ datafile->m_file_lock.l_type = F_WRLCK;
+ result = (itzam_bool)fcntl(datafile->m_file,F_SETLKW,&datafile->m_file_lock);
+#else
+ result = (itzam_bool)LockFile(datafile->m_file, 0, 0, 0xFFFFFFFF, 0xFFFFFFFF);
+#endif
+ }
+
+ datafile->m_file_locked = result;
+ }
+ else
+ {
+ default_error_handler("itzam_datafile_file_lock",ITZAM_ERROR_INVALID_DATAFILE_OBJECT);
+ }
+
+ return result;
+}
+
+itzam_bool itzam_datafile_file_unlock(itzam_datafile * datafile)
+{
+ itzam_bool result = itzam_false;
+
+ if ((datafile != NULL) && (datafile->m_is_open))
+ {
+ if (datafile->m_file_locked)
+ {
+#if defined(ITZAM_UNIX)
+ fsync(datafile->m_file);
+ datafile->m_file_lock.l_type = F_UNLCK;
+ result = (itzam_bool)fcntl(datafile->m_file,F_SETLKW,&datafile->m_file_lock);
+#else
+ result = (itzam_bool)UnlockFile(datafile->m_file, 0, 0, 0xFFFFFFFF, 0xFFFFFFFF);
+#endif
+ }
+ else
+ result = itzam_true; /* file wasn't locked, so it *is* unlocked... */
+
+ if (result == itzam_true)
+ datafile->m_file_locked = itzam_false;
+ }
+ else
+ default_error_handler("itzam_datafile_file_unlock",ITZAM_ERROR_INVALID_DATAFILE_OBJECT);
+
+ return result;
+}
+
+itzam_bool itzam_datafile_is_open(itzam_datafile * datafile)
+{
+ itzam_bool result = itzam_false;
+
+ if (datafile != NULL)
+ {
+ itzam_datafile_mutex_lock(datafile);
+ result = datafile->m_is_open;
+ itzam_datafile_mutex_unlock(datafile);
+ }
+ else
+ {
+ default_error_handler("itzam_datafile_is_open",ITZAM_ERROR_INVALID_DATAFILE_OBJECT);
+ }
+
+ return result;
+}
+
+float itzam_datafile_get_version(itzam_datafile * datafile)
+{
+ float result = 0.0F;
+
+ if (datafile != NULL)
+ {
+ itzam_datafile_mutex_lock(datafile);
+
+ result = (double)((datafile->m_shared->m_header.m_version & 0x00FF0000) >> 16)
+ + ((datafile->m_shared->m_header.m_version & 0x0000FF00) >> 8) / 100.0
+ + ((datafile->m_shared->m_header.m_version & 0x000000FF)) / 10000.0;
+
+ itzam_datafile_mutex_unlock(datafile);
+ }
+ else
+ {
+ default_error_handler("itzam_datafile_get_version",ITZAM_ERROR_INVALID_DATAFILE_OBJECT);
+ }
+
+ return result;
+}
+
+void itzam_datafile_set_error_handler(itzam_datafile * datafile,
+ itzam_error_handler * error_handler)
+{
+ if (datafile != NULL)
+ {
+ itzam_datafile_mutex_lock(datafile);
+
+ if (error_handler == NULL)
+ datafile->m_error_handler = default_error_handler;
+ else
+ datafile->m_error_handler = error_handler;
+
+ itzam_datafile_mutex_unlock(datafile);
+ }
+ else
+ {
+ default_error_handler("itzam_datafile_set_error_handler",ITZAM_ERROR_INVALID_DATAFILE_OBJECT);
+ }
+}
+
+itzam_ref itzam_datafile_tell(itzam_datafile * datafile)
+{
+ itzam_ref where = -1;
+
+ if ((datafile != NULL) && (datafile->m_is_open))
+ {
+ itzam_datafile_mutex_lock(datafile);
+
+ where = (itzam_ref)itzam_file_tell(datafile->m_file);
+
+ itzam_datafile_mutex_unlock(datafile);
+ }
+ else
+ default_error_handler("itzam_datafile_tell",ITZAM_ERROR_INVALID_DATAFILE_OBJECT);
+
+ return where;
+}
+
+itzam_state itzam_datafile_seek(itzam_datafile * datafile, itzam_ref pos)
+{
+ itzam_state result = ITZAM_FAILED;
+
+ if ((datafile != NULL) && (datafile->m_is_open))
+ {
+ itzam_datafile_mutex_lock(datafile);
+
+ /* seek to requested position
+ */
+ if (-1 != itzam_file_seek(datafile->m_file,(long)pos,ITZAM_SEEK_BEGIN))
+ result = ITZAM_OKAY;
+ else
+ datafile->m_error_handler("itzam_datafile_seek",ITZAM_ERROR_SEEK_FAILED);
+
+ itzam_datafile_mutex_unlock(datafile);
+ }
+ else
+ default_error_handler("itzam_datafile_seek",ITZAM_ERROR_INVALID_DATAFILE_OBJECT);
+
+ return result;
+}
+
+itzam_state itzam_datafile_rewind(itzam_datafile * datafile)
+{
+ itzam_state result = ITZAM_FAILED;
+
+ if ((datafile != NULL) && (datafile->m_is_open))
+ {
+ itzam_datafile_mutex_lock(datafile);
+
+ /* seek to byte after first deleted marker
+ */
+ if (-1 != itzam_file_seek(datafile->m_file,(long)sizeof(itzam_datafile_header),ITZAM_SEEK_BEGIN))
+ result = ITZAM_OKAY;
+ else
+ datafile->m_error_handler("itzam_datafile_rewind",ITZAM_ERROR_SEEK_FAILED);
+
+ itzam_datafile_mutex_unlock(datafile);
+ }
+ else
+ default_error_handler("itzam_datafile_rewind",ITZAM_ERROR_INVALID_DATAFILE_OBJECT);
+
+ return result;
+}
+
+/* This function should NEVER be called by user code; it is an internal function used by indexes.
+ * It assumes that a returned deleted record will be used by the calling function.
+ */
+itzam_ref itzam_datafile_get_next_open(itzam_datafile * datafile, itzam_int length)
+{
+ itzam_int n;
+ itzam_ref where = ITZAM_NULL_REF;
+
+ if ((datafile != NULL) && (datafile->m_is_open))
+ {
+ if (ITZAM_OKAY == read_dellist(datafile))
+ {
+ for (n = 0; n < datafile->m_dellist_header.m_table_size; ++n)
+ {
+ if (datafile->m_dellist[n].m_where != ITZAM_NULL_REF)
+ {
+ if (datafile->m_dellist[n].m_length == length)
+ {
+ /* save the location of the deleted record we're replacing
+ */
+ where = datafile->m_dellist[n].m_where;
+
+ /* remove this entry from the table
+ */
+ datafile->m_dellist[n].m_where = ITZAM_NULL_REF;
+ datafile->m_dellist[n].m_length = 0;
+ write_dellist(datafile,itzam_false);
+ break;
+ }
+ }
+ }
+ }
+
+ if (where == ITZAM_NULL_REF)
+ {
+ /* no deleted records, so append
+ */
+ if (-1 != itzam_file_seek(datafile->m_file,0,ITZAM_SEEK_END))
+ {
+ where = itzam_file_tell(datafile->m_file);
+
+ if (where < 0)
+ where = ITZAM_NULL_REF;
+ }
+ else
+ where = ITZAM_NULL_REF;
+ }
+ }
+ else
+ default_error_handler("itzam_datafile_get_next_open",ITZAM_ERROR_INVALID_DATAFILE_OBJECT);
+
+ return where;
+}
+
+/* internal function to add an operation to the transaction list
+ */
+static itzam_bool add_tran_op(itzam_datafile * datafile, itzam_int where, itzam_record_header * header, const void * data, itzam_op_type op_type)
+{
+ itzam_bool result = itzam_false;
+ itzam_op_header op_header;
+ itzam_int op_where;
+
+ if (datafile->m_in_transaction)
+ {
+ /* initialize operation header and copy data in
+ */
+ op_header.m_type = op_type;
+ op_header.m_where = where;
+ op_header.m_prev_tran = datafile->m_shared->m_header.m_transaction_tail;
+ memcpy(&op_header.m_record_header,header,sizeof(itzam_record_header));
+ op_header.m_record_header.m_flags |= ITZAM_RECORD_TRAN_RECORD;
+ op_header.m_record_where = ITZAM_NULL_REF;
+
+ /* turn off transactions for a moment
+ */
+ datafile->m_in_transaction = itzam_false;
+
+ /* write op data to the file
+ */
+ if (op_type != ITZAM_TRAN_OP_WRITE)
+ {
+ op_header.m_record_where = itzam_datafile_get_next_open(datafile->m_tran_file,op_header.m_record_header.m_length);
+ itzam_datafile_write_flags(datafile->m_tran_file,data,op_header.m_record_header.m_length,op_header.m_record_where,ITZAM_RECORD_TRAN_RECORD);
+ }
+
+ /* write op header
+ */
+ op_where = itzam_datafile_get_next_open(datafile->m_tran_file,sizeof(itzam_op_header));
+ itzam_datafile_write_flags(datafile->m_tran_file,&op_header,sizeof(op_header),op_where,ITZAM_RECORD_TRAN_HEADER);
+
+ /* turn transactions on again
+ */
+ datafile->m_in_transaction = itzam_true;
+
+ /* rewrite datafile header
+ */
+ datafile->m_shared->m_header.m_transaction_tail = op_where;
+
+ if (-1 != itzam_file_seek(datafile->m_file,0,ITZAM_SEEK_BEGIN))
+ {
+ if (itzam_file_write(datafile->m_file,&datafile->m_shared->m_header,sizeof(itzam_datafile_header)))
+ result = itzam_true;
+ }
+ }
+
+ return result;
+}
+
+itzam_ref itzam_datafile_write_flags(itzam_datafile * datafile, const void * data, itzam_int length, itzam_ref where, int32_t flags)
+{
+ itzam_record_header rec_header;
+
+ /* make sure the arguments make sense
+ */
+ if ((datafile != NULL) && (data != NULL) && (length > 0) && (datafile->m_is_open))
+ {
+ itzam_datafile_mutex_lock(datafile);
+
+ if (datafile->m_read_only)
+ return ITZAM_NULL_REF;
+ else
+ {
+ /* if we aren't told where to put the record, find a place
+ */
+ if (where == ITZAM_NULL_REF)
+ where = itzam_datafile_get_next_open(datafile,length);
+ else
+ {
+ /* are we in a transaction?
+ */
+ if (datafile->m_in_transaction)
+ {
+ /* read the old record rec_header
+ */
+ if (-1 != itzam_file_seek(datafile->m_file,where,ITZAM_SEEK_BEGIN))
+ {
+ if (itzam_file_read(datafile->m_file,&rec_header,sizeof(itzam_record_header)))
+ {
+ /* if the record is active, then we need to save it
+ */
+ if (rec_header.m_flags & ITZAM_RECORD_IN_USE)
+ {
+ /* create a temporary buffer
+ */
+ void * op_data = malloc(rec_header.m_rec_len);
+
+ /* read data
+ */
+ if (op_data != NULL)
+ {
+ /* now save record information
+ */
+ if (itzam_file_read(datafile->m_file,op_data,rec_header.m_length))
+ {
+ add_tran_op(datafile,where,&rec_header,op_data,ITZAM_TRAN_OP_REMOVE);
+ free(op_data);
+ }
+ else
+ datafile->m_error_handler("itzam_datafile_write_flags (1)",ITZAM_ERROR_READ_FAILED);
+ }
+ else
+ datafile->m_error_handler("itzam_datafile_write_flags (2)",ITZAM_ERROR_MALLOC);
+ }
+ }
+ }
+ }
+ }
+
+ rec_header.m_signature = ITZAM_RECORD_SIGNATURE;
+ rec_header.m_flags = ITZAM_RECORD_IN_USE | flags;
+ rec_header.m_length = 0;
+ rec_header.m_rec_len = 0;
+
+ /* do we have a good place to write?
+ */
+ if (where != ITZAM_NULL_REF)
+ {
+ /* modify record rec_header
+ */
+ rec_header.m_rec_len = length;
+ rec_header.m_length = length;
+
+ /* ITZAM_SEEK to beginning of new record
+ */
+ if (-1 != itzam_file_seek(datafile->m_file,where,ITZAM_SEEK_BEGIN))
+ {
+ /* write the record rec_header
+ */
+ if (itzam_file_write(datafile->m_file,&rec_header,sizeof(rec_header)))
+ {
+ /* write the record
+ */
+ if (!itzam_file_write(datafile->m_file,data,length))
+ {
+ datafile->m_error_handler("itzam_datafile_write_flags (4)",ITZAM_ERROR_WRITE_FAILED);
+ where = ITZAM_NULL_REF;
+ }
+ }
+ else
+ {
+ datafile->m_error_handler("itzam_datafile_write_flags (5)", ITZAM_ERROR_WRITE_FAILED);
+ where = ITZAM_NULL_REF;
+ }
+ }
+ else
+ {
+ datafile->m_error_handler("itzam_datafile_write_flags (6)",ITZAM_ERROR_SEEK_FAILED);
+ where = ITZAM_NULL_REF;
+ }
+
+ if ((where != ITZAM_NULL_REF) && (datafile->m_in_transaction))
+ add_tran_op(datafile,where,&rec_header,data,ITZAM_TRAN_OP_WRITE);
+ }
+ }
+
+ itzam_datafile_mutex_unlock(datafile);
+ }
+ else
+ default_error_handler("itzam_datafile_write_flags (7)",ITZAM_ERROR_INVALID_DATAFILE_OBJECT);
+
+ return where;
+}
+
+itzam_ref itzam_datafile_write(itzam_datafile * datafile, const void * data, itzam_int length, itzam_ref where)
+{
+ return itzam_datafile_write_flags(datafile, data, length, where, 0);
+}
+
+// dangerous! this function could overwrite critical file data structures
+itzam_state itzam_datafile_overwrite(itzam_datafile * datafile, const void * data, itzam_int length, itzam_ref where, itzam_int offset)
+{
+ itzam_state result = ITZAM_FAILED;
+ itzam_record_header rec_header;
+
+ if ((datafile != NULL) && (data != NULL) && (length > 0) && (datafile->m_is_open) && (where != ITZAM_NULL_REF))
+ {
+ if (datafile->m_read_only)
+ {
+ datafile->m_error_handler("itzam_datafile_write_flags", ITZAM_ERROR_READ_ONLY);
+ return ITZAM_FAILED;
+ }
+
+ itzam_datafile_mutex_lock(datafile);
+
+ /* read header file
+ */
+ if (-1 != itzam_file_seek(datafile->m_file,where,ITZAM_SEEK_BEGIN))
+ {
+ if (!itzam_file_read(datafile->m_file,&rec_header,sizeof(itzam_record_header)))
+ datafile->m_error_handler("itzam_datafile_explicit_write (1)",ITZAM_ERROR_READ_FAILED);
+
+ if ((0 == (rec_header.m_flags & ITZAM_RECORD_IN_USE)) || (rec_header.m_signature != ITZAM_RECORD_SIGNATURE))
+ datafile->m_error_handler("itzam_datafile_explicit_write (2)",ITZAM_ERROR_INVALID_RECORD);
+ }
+
+ /* make sure we fit inside the record
+ */
+ if (rec_header.m_length < (length + offset))
+ return ITZAM_OVERWRITE_TOO_LONG;
+
+ /* are we in a transaction? if so, save the rec we're changing
+ */
+ if (datafile->m_in_transaction)
+ {
+ /* create a temporary buffer
+ */
+ void * op_data = malloc(rec_header.m_rec_len);
+
+ /* read data
+ */
+ if (op_data != NULL)
+ {
+ /* now save record information
+ */
+ if (itzam_file_read(datafile->m_file,op_data,rec_header.m_length))
+ {
+ add_tran_op(datafile, where, &rec_header, op_data, ITZAM_TRAN_OP_OVERWRITE);
+ free(op_data);
+ }
+ else
+ datafile->m_error_handler("itzam_datafile_explicit_write (2)",ITZAM_ERROR_READ_FAILED);
+ }
+ else
+ datafile->m_error_handler("itzam_datafile_explicit_write (3)",ITZAM_ERROR_MALLOC);
+ }
+
+ /* modify record at given offset
+ */
+ if (-1 != itzam_file_seek(datafile->m_file, where + sizeof(itzam_record_header) + offset, ITZAM_SEEK_BEGIN))
+ {
+ if (!itzam_file_write(datafile->m_file,data,length))
+ {
+ datafile->m_error_handler("itzam_datafile_explicit_write (4)",ITZAM_ERROR_WRITE_FAILED);
+ where = ITZAM_NULL_REF;
+ }
+ }
+ else
+ {
+ datafile->m_error_handler("itzam_datafile_explicit_write (5)",ITZAM_ERROR_SEEK_FAILED);
+ where = ITZAM_NULL_REF;
+ }
+
+ itzam_datafile_mutex_unlock(datafile);
+
+ result = ITZAM_OKAY;
+ }
+ else
+ default_error_handler("itzam_datafile_write_flags (5)",ITZAM_ERROR_INVALID_DATAFILE_OBJECT);
+
+ return result;
+}
+
+itzam_state itzam_datafile_read(itzam_datafile * datafile, void * record, itzam_int max_length)
+{
+ itzam_state result = ITZAM_FAILED;
+ itzam_record_header header = { 0, 0, 0, 0 };
+ itzam_bool error = itzam_false;
+
+ if ((datafile != NULL) && (record != NULL) && (max_length > 0) && (datafile->m_is_open))
+ {
+ itzam_datafile_mutex_lock(datafile);
+
+ while (!error)
+ {
+ /* read the record header
+ */
+ if (itzam_file_read(datafile->m_file,&header,sizeof(itzam_record_header)))
+ {
+ /* if the record is active, we can read it
+ */
+ if ((header.m_flags & ITZAM_RECORD_IN_USE) && !(header.m_flags & ITZAM_RECORD_DELLIST))
+ break;
+
+ /* if the record signature is invalid, we have an error
+ */
+ if (header.m_signature != ITZAM_RECORD_SIGNATURE)
+ {
+ datafile->m_error_handler("itzam_datafile_read",ITZAM_ERROR_INVALID_RECORD);
+ error = itzam_true;
+ }
+
+ /* move to the next record
+ */
+ if (-1 == itzam_file_seek(datafile->m_file,header.m_length,ITZAM_SEEK_CURRENT))
+ {
+ datafile->m_error_handler("itzam_datafile_read",ITZAM_ERROR_SEEK_FAILED);
+ error = itzam_true;
+ }
+ }
+ else
+ {
+ datafile->m_error_handler("itzam_datafile_read",ITZAM_ERROR_READ_FAILED);
+ error = itzam_true;
+ }
+ }
+
+ /* only read actual data if no errors
+ */
+ if (!error)
+ {
+ itzam_int read_len = (max_length < header.m_rec_len) ? max_length : header.m_rec_len;
+
+ if (itzam_file_read(datafile->m_file, record, read_len))
+ {
+ /* skip any "padding" between record size and record buffer length
+ */
+ if (read_len < header.m_length)
+ {
+ if (-1 == itzam_file_seek(datafile->m_file,header.m_length - read_len,ITZAM_SEEK_CURRENT))
+ {
+ datafile->m_error_handler("itzam_datafile_read",ITZAM_ERROR_SEEK_FAILED);
+ error = itzam_true;
+ }
+ }
+
+ result = ITZAM_OKAY;
+ }
+ else
+ {
+ datafile->m_error_handler("itzam_datafile_read",ITZAM_ERROR_READ_FAILED);
+ error = itzam_true;
+ }
+ }
+
+ itzam_datafile_mutex_unlock(datafile);
+ }
+ else
+ default_error_handler("itzam_datafile_read",ITZAM_ERROR_INVALID_DATAFILE_OBJECT);
+
+ return result;
+}
+
+itzam_state itzam_datafile_read_alloc(itzam_datafile * datafile, void ** data, itzam_int * length)
+{
+ itzam_state result = ITZAM_FAILED;
+ itzam_record_header header;
+
+ if ((datafile != NULL) && (data != NULL) && (datafile->m_is_open))
+ {
+ itzam_datafile_mutex_lock(datafile);
+
+ if (itzam_file_read(datafile->m_file,&header,sizeof(header)))
+ {
+ /* if the record is active, we can read it
+ */
+ if (header.m_flags & ITZAM_RECORD_IN_USE)
+ {
+ /* create a temporary buffer
+ */
+ if (length != NULL)
+ *length = header.m_rec_len;
+
+ *data = malloc(header.m_rec_len);
+
+ /* read data
+ */
+ if (*data != NULL)
+ {
+ if (itzam_file_read(datafile->m_file,*data,header.m_length))
+ {
+ /* skip any "padding" between record size and record buffer length
+ */
+ if (header.m_rec_len < header.m_length)
+ itzam_file_seek(datafile->m_file,header.m_length - header.m_rec_len,ITZAM_SEEK_CURRENT);
+
+ result = ITZAM_OKAY;
+ }
+ else
+ datafile->m_error_handler("1) itzam_datafile_read_alloc",ITZAM_ERROR_READ_FAILED);
+ }
+ else
+ datafile->m_error_handler("itzam_datafile_read_alloc",ITZAM_ERROR_MALLOC);
+ }
+ else
+ datafile->m_error_handler("2) itzam_datafile_read_alloc",ITZAM_ERROR_READ_FAILED);
+ }
+ else
+ datafile->m_error_handler("3) itzam_datafile_read_alloc",ITZAM_ERROR_READ_FAILED);
+
+ itzam_datafile_mutex_unlock(datafile);
+ }
+ else
+ default_error_handler("itzam_datafile_read_alloc",ITZAM_ERROR_INVALID_DATAFILE_OBJECT);
+
+ return result;
+}
+
+itzam_state itzam_datafile_remove(itzam_datafile * datafile)
+{
+ itzam_state result = ITZAM_FAILED;
+ int n;
+ itzam_ref where;
+
+ if ((datafile != NULL) && (datafile->m_is_open))
+ {
+ itzam_datafile_mutex_lock(datafile);
+
+ if (datafile->m_read_only)
+ result = ITZAM_READ_ONLY;
+ else
+ {
+ /* get our position
+ */
+ where = itzam_file_tell(datafile->m_file);
+
+ /* make sure we're not in the file header
+ */
+ if (where >= sizeof(itzam_ref))
+ {
+ itzam_record_header header;
+
+ /* read the header
+ */
+ if (itzam_file_read(datafile->m_file,&header,sizeof(header)))
+ {
+ /* only delete if it isn't already deleted
+ */
+ if (header.m_flags & ITZAM_RECORD_IN_USE)
+ {
+ /* save this record if we're in a transaction
+ */
+ if (datafile->m_in_transaction)
+ {
+ /**
+ * create a temporary buffer
+ */
+ void * op_data = malloc(header.m_rec_len);
+
+ /* read data
+ */
+ if (op_data != NULL)
+ {
+ /* now save record information
+ */
+ if (itzam_file_read(datafile->m_file,op_data,header.m_length))
+ {
+ add_tran_op(datafile,where,&header,op_data,ITZAM_TRAN_OP_REMOVE);
+ free(op_data);
+ }
+ else
+ datafile->m_error_handler("itzam_datafile_remove",ITZAM_ERROR_READ_FAILED);
+ }
+ else
+ datafile->m_error_handler("itzam_datafile_remove",ITZAM_ERROR_MALLOC);
+ }
+
+ /* change record header; make this record the head of the deleted list
+ */
+ header.m_flags &= ~ITZAM_RECORD_IN_USE;
+
+ /* move to beginning of record header
+ */
+ if (-1 != itzam_file_seek(datafile->m_file,where,ITZAM_SEEK_BEGIN))
+ {
+ /* write revised record header
+ */
+ if (itzam_file_write(datafile->m_file,&header,sizeof(header)))
+ {
+ /* update deleted list
+ */
+ if (ITZAM_FAILED == read_dellist(datafile))
+ {
+ datafile->m_dellist_header.m_table_size = ITZAM_DELLIST_BLOCK_SIZE;
+ datafile->m_dellist = (itzam_dellist_entry *)malloc(sizeof(itzam_dellist_entry) * datafile->m_dellist_header.m_table_size);
+
+ if (datafile->m_dellist != NULL)
+ {
+ datafile->m_dellist[0].m_where = where;
+ datafile->m_dellist[0].m_length = header.m_length;
+
+ for (n = 1; n < datafile->m_dellist_header.m_table_size; ++n)
+ {
+ datafile->m_dellist[n].m_where = ITZAM_NULL_REF;
+ datafile->m_dellist[n].m_length = 0;
+ }
+
+ result = write_dellist(datafile,itzam_true);
+ }
+ else
+ datafile->m_error_handler("itzam_datafile_remove",ITZAM_ERROR_MALLOC);
+ }
+ else
+ {
+ for (n = 0; n < datafile->m_dellist_header.m_table_size; ++n)
+ {
+ if (datafile->m_dellist[n].m_where == ITZAM_NULL_REF)
+ {
+ datafile->m_dellist[n].m_where = where;
+ datafile->m_dellist[n].m_length = header.m_length;
+ result = write_dellist(datafile,itzam_false);
+ break;
+ }
+ }
+
+ /* if the entry didn't fit, we have to expand it
+ */
+ if (n == datafile->m_dellist_header.m_table_size)
+ {
+ /* create new table
+ */
+ itzam_int newsize = datafile->m_dellist_header.m_table_size + ITZAM_DELLIST_BLOCK_SIZE;
+ itzam_dellist_entry * newlist = (itzam_dellist_entry *)malloc(sizeof(itzam_dellist_entry) * newsize);
+
+ if (newlist != NULL)
+ {
+ /* copy old entries; could (?should) use memcpy here I suppose
+ */
+ for (n = 0; n < datafile->m_dellist_header.m_table_size; ++n)
+ {
+ newlist[n].m_where = datafile->m_dellist[n].m_where;
+ newlist[n].m_length = datafile->m_dellist[n].m_length;
+ }
+
+ /* add the newly-deleted record
+ */
+ newlist[n].m_where = where;
+ newlist[n].m_length = header.m_length;
+
+ /* add the old deleted list record
+ */
+ newlist[n+1].m_where = datafile->m_shared->m_header.m_dellist_ref;
+ newlist[n+1].m_length = datafile->m_dellist_header.m_table_size;
+
+ /* initialize remaining unused new entries
+ */
+ for (n += 2 ; n < newsize; ++n)
+ {
+ newlist[n].m_where = ITZAM_NULL_REF;
+ newlist[n].m_length = 0;
+ }
+
+ /* free space used by old list
+ */
+ free(datafile->m_dellist);
+
+ /* exchange new list for old
+ */
+ datafile->m_dellist = newlist;
+ datafile->m_dellist_header.m_table_size = newsize;
+
+ /* write it
+ */
+ result = write_dellist(datafile,itzam_true);
+ }
+ else
+ datafile->m_error_handler("itzam_datafile_read_alloc",ITZAM_ERROR_MALLOC);
+ }
+ }
+ }
+ else
+ datafile->m_error_handler("itzam_datafile_remove",ITZAM_ERROR_WRITE_FAILED);
+ }
+ else
+ datafile->m_error_handler("itzam_datafile_remove",ITZAM_ERROR_SEEK_FAILED);
+ }
+ else
+ datafile->m_error_handler("itzam_datafile_remove",ITZAM_ERROR_DUPE_REMOVE);
+ }
+ else
+ datafile->m_error_handler("itzam_datafile_remove",ITZAM_ERROR_READ_FAILED);
+ }
+ else
+ datafile->m_error_handler("itzam_datafile_remove",ITZAM_ERROR_TELL_FAILED);
+ }
+
+ itzam_datafile_mutex_unlock(datafile);
+ }
+ else
+ default_error_handler("itzam_datafile_remove",ITZAM_ERROR_INVALID_DATAFILE_OBJECT);
+
+ return result;
+}
+
+itzam_state itzam_datafile_transaction_start(itzam_datafile * datafile)
+{
+ itzam_state result = ITZAM_FAILED;
+
+ /* only start a transaction is one isn't already in progress
+ */
+ if ((datafile != NULL) && (datafile->m_is_open) && (!datafile->m_in_transaction))
+ {
+ if (datafile->m_read_only)
+ {
+ datafile->m_error_handler("itzam_datafile_transactions_start", ITZAM_ERROR_READ_ONLY);
+ return ITZAM_READ_ONLY;
+ }
+
+ itzam_datafile_mutex_lock(datafile);
+
+ datafile->m_tran_file = (itzam_datafile *)malloc(sizeof(itzam_datafile));
+
+ if (datafile->m_tran_file != NULL)
+ {
+ /* create a datafile to contain the transaction operations
+ */
+ result = itzam_datafile_create(datafile->m_tran_file,datafile->m_tran_file_name);
+
+ if (ITZAM_OKAY == result)
+ datafile->m_in_transaction = itzam_true;
+ else
+ default_error_handler("itzam_datafile_transaction_start",ITZAM_ERROR_INVALID_DATAFILE_OBJECT);
+ }
+ }
+ else
+ default_error_handler("itzam_datafile_transaction_start",ITZAM_ERROR_INVALID_DATAFILE_OBJECT);
+
+ return result;
+}
+
+static void transaction_cleanup(itzam_datafile * datafile, itzam_bool rollback)
+{
+ itzam_op_header * op_header;
+ itzam_int dont_care;
+ void * op_record;
+ itzam_int data_len;
+ itzam_int n;
+ itzam_bool dummy;
+
+ if (rollback)
+ {
+ /* start at the beginning
+ */
+ itzam_ref op_where = datafile->m_shared->m_header.m_transaction_tail;
+
+ /* while we have something to process
+ */
+ while (op_where != ITZAM_NULL_REF)
+ {
+ /* read the operation header
+ */
+ itzam_datafile_seek(datafile->m_tran_file,op_where);
+
+ /* read the rolled-back record
+ */
+ itzam_datafile_read_alloc(datafile->m_tran_file,(void **)(void*)&op_header,&dont_care);
+
+ /* act upon the op_record....
+ */
+ switch (op_header->m_type)
+ {
+ /* remove a record that was written
+ */
+ case ITZAM_TRAN_OP_WRITE:
+ itzam_datafile_seek(datafile,op_header->m_where);
+ itzam_datafile_remove(datafile);
+ break;
+
+ /* replace a record that was removed
+ */
+ case ITZAM_TRAN_OP_REMOVE:
+ itzam_datafile_seek(datafile->m_tran_file,op_header->m_record_where);
+ itzam_datafile_read_alloc(datafile->m_tran_file,(void **)&op_record,&data_len);
+ itzam_datafile_write(datafile,op_record,op_header->m_record_header.m_length,op_header->m_where);
+ free(op_record);
+
+ if (ITZAM_OKAY == read_dellist(datafile))
+ {
+ for (n = 0; n < datafile->m_dellist_header.m_table_size; ++n)
+ {
+ if (datafile->m_dellist[n].m_where == op_header->m_where)
+ {
+ /* remove this entry from the table
+ */
+ datafile->m_dellist[n].m_where = ITZAM_NULL_REF;
+ datafile->m_dellist[n].m_length = 0;
+ break;
+ }
+ }
+ }
+
+ write_dellist(datafile,itzam_false);
+
+ break;
+
+ /* restore a record that was over-written
+ */
+ case ITZAM_TRAN_OP_OVERWRITE:
+ itzam_datafile_seek(datafile->m_tran_file,op_header->m_record_where);
+ itzam_datafile_read_alloc(datafile->m_tran_file,(void **)&op_record,&data_len);
+ itzam_datafile_write(datafile, op_record, op_header->m_record_header.m_length, op_header->m_where);
+ free(op_record);
+
+ break;
+ }
+
+ /* save next operation
+ */
+ op_where = op_header->m_prev_tran;
+
+ /* release memory
+ */
+ free(op_header);
+ }
+ }
+
+ /* update the header
+ */
+ datafile->m_shared->m_header.m_transaction_tail = ITZAM_NULL_REF;
+ itzam_file_seek(datafile->m_file,0,ITZAM_SEEK_BEGIN);
+ dummy = itzam_file_write(datafile->m_file,&datafile->m_shared->m_header,sizeof(itzam_datafile_header));
+
+ /* close and remove transaction file
+ */
+ itzam_datafile_close(datafile->m_tran_file);
+ itzam_file_remove(datafile->m_tran_file_name);
+ free(datafile->m_tran_file);
+ datafile->m_tran_file = NULL;
+
+ itzam_datafile_mutex_unlock(datafile);
+}
+
+itzam_state itzam_datafile_transaction_commit(itzam_datafile * datafile)
+{
+ itzam_state result = ITZAM_FAILED;
+
+ /* only commit if a transaction is active
+ */
+ if ((datafile != NULL) && (datafile->m_is_open) && (datafile->m_in_transaction))
+ {
+ /* we're no longer in a transaction
+ */
+ datafile->m_in_transaction = itzam_false;
+
+ /* clean up transactioon data; it's no longer needed
+ */
+ transaction_cleanup(datafile,itzam_false);
+ result = ITZAM_OKAY;
+ }
+ else
+ default_error_handler("itzam_datafile_transaction_commit",ITZAM_ERROR_INVALID_DATAFILE_OBJECT);
+
+ return result;
+}
+
+itzam_state itzam_datafile_transaction_rollback(itzam_datafile * datafile)
+{
+ itzam_state result = ITZAM_FAILED;
+
+ /* only rollback if a transaction is active
+ */
+ if ((datafile != NULL) && (datafile->m_is_open) && (datafile->m_in_transaction))
+ {
+ /* we're no longer in a transaction
+ */
+ datafile->m_in_transaction = itzam_false;
+
+ /* clean up transaction data; it's no longer needed
+ */
+ transaction_cleanup(datafile,itzam_true);
+ result = ITZAM_OKAY;
+ }
+ else
+ default_error_handler("itzam_datafile_transaction_rollback",ITZAM_ERROR_INVALID_DATAFILE_OBJECT);
+
+ return result;
+}
diff --git a/src/itzam_util.c b/src/itzam_util.c new file mode 100644 index 0000000..cdfbc66 --- /dev/null +++ b/src/itzam_util.c @@ -0,0 +1,308 @@ +/*
+ Itzam/C (version 6.0) is an embedded database engine written in Standard C.
+
+ Copyright 2011 Scott Robert Ladd. All rights reserved.
+
+ Older versions of Itzam/C are:
+ Copyright 2002, 2004, 2006, 2008 Scott Robert Ladd. All rights reserved.
+
+ Ancestral code, from Java and C++ books by the author, is:
+ Copyright 1992, 1994, 1996, 2001 Scott Robert Ladd. All rights reserved.
+
+ Itzam/C is user-supported open source software. It's continued development is dependent on
+ financial support from the community. You can provide funding by visiting the Itzam/C
+ website at:
+
+ http://www.coyotegulch.com
+
+ You may license Itzam/C in one of two fashions:
+
+ 1) Simplified BSD License (FreeBSD License)
+
+ Redistribution and use in source and binary forms, with or without modification, are
+ permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice, this list of
+ conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ of conditions and the following disclaimer in the documentation and/or other materials
+ provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY SCOTT ROBERT LADD ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SCOTT ROBERT LADD OR
+ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ The views and conclusions contained in the software and documentation are those of the
+ authors and should not be interpreted as representing official policies, either expressed
+ or implied, of Scott Robert Ladd.
+
+ 2) Closed-Source Proprietary License
+
+ If your project is a closed-source or proprietary project, the Simplified BSD License may
+ not be appropriate or desirable. In such cases, contact the Itzam copyright holder to
+ arrange your purchase of an appropriate license.
+
+ The author can be contacted at:
+
+ scott.ladd@coyotegulch.com
+ scott.ladd@gmail.com
+ http:www.coyotegulch.com
+*/
+
+#include "itzam.h"
+
+#include <stdlib.h>
+#include <errno.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#if defined(ITZAM_UNIX)
+#include <sys/mman.h>
+#endif
+
+/*-----------------------------------------------------------------------------
+ * default error handler, for errors outside the scope of valid datafile objects
+ */
+void default_default_error_handler(const char * function_name, itzam_error error)
+{
+ /* default error handler displays a simple message and exits program
+ */
+ fprintf(stderr,"Itzam internal error in %s: %d\n",function_name,error);
+ exit(1);
+}
+
+itzam_error_handler * default_error_handler = default_default_error_handler;
+
+void itzam_set_default_error_handler(itzam_error_handler * handler)
+{
+ if (handler != NULL)
+ default_error_handler = handler;
+}
+
+/*-----------------------------------------------------------------------------
+ * shared memory
+ */
+
+ITZAM_SHMEM_TYPE itzam_shmem_obtain(const char * name, size_t len, itzam_bool * creator)
+{
+#if defined(ITZAM_UNIX)
+ int result;
+
+ result = shm_open(name, O_CREAT | O_EXCL | O_RDWR, S_IRUSR | S_IWUSR);
+
+ if (result < 0)
+ {
+ if (errno == EEXIST)
+ {
+ *creator = itzam_false;
+
+ result = shm_open(name, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
+
+ if (result < 0)
+ default_error_handler("itzam_shmem_obtain",ITZAM_ERROR_SHMEM);
+ }
+ }
+ else
+ {
+ *creator = itzam_true;
+
+ if (ftruncate(result, len))
+ default_error_handler("itzam_shmem_obtain",ITZAM_ERROR_SHMEM);
+ }
+
+ return result;
+#else
+ HANDLE result;
+
+ result = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, (LPCSTR)name);
+
+ if ((result == NULL) && (len > 0))
+ {
+ *created = itzam_true;
+ result = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, (DWORD)len, (LPCTSTR)name);
+ }
+ else
+ *created = itzam_false;
+
+ if (result == NULL)
+ {
+ if (ERROR_ACCESS_DENIED == GetLastError())
+ default_error_handler("itzam_shmem_obtain",ITZAM_ERROR_SHMEM_PRIVILEGE);
+ else
+ default_error_handler("itzam_shmem_obtain",ITZAM_ERROR_SHMEM);
+ }
+
+ return result;
+#endif
+}
+
+void itzam_shmem_close(ITZAM_SHMEM_TYPE shmem, const char * name)
+{
+#if defined(ITZAM_UNIX)
+ close(shmem);
+ shm_unlink(name);
+#else
+ if (shmem != NULL)
+ CloseHandle(shmem);
+#endif
+}
+
+void * itzam_shmem_getptr(ITZAM_SHMEM_TYPE shmem, size_t len)
+{
+#if defined(ITZAM_UNIX)
+ void * result = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, shmem, 0);
+
+ if (result == MAP_FAILED)
+ default_error_handler("itzam_shmem_obtain",ITZAM_ERROR_SHMEM);
+
+ return result;
+#else
+ void * result = NULL;
+
+ result = (void *)MapViewOfFile(shmem, FILE_MAP_ALL_ACCESS, 0, 0, len);
+
+ if (result == NULL)
+ {
+ if (ERROR_ACCESS_DENIED == GetLastError())
+ default_error_handler("itzam_shmem_obtain",ITZAM_ERROR_SHMEM_PRIVILEGE);
+ else
+ default_error_handler("itzam_shmem_obtain",ITZAM_ERROR_SHMEM);
+ }
+
+ return result;
+#endif
+}
+
+itzam_bool itzam_shmem_freeptr(void * shmem_ptr, size_t len)
+{
+#if defined(ITZAM_UNIX)
+ return (itzam_bool)(0 == munmap(shmem_ptr,len));
+#else
+ return (itzam_bool)UnmapViewOfFile(shmem_ptr);
+#endif
+}
+
+/*-----------------------------------------------------------------------------
+ * file I/O functions
+ */
+
+ITZAM_FILE_TYPE itzam_file_create(const char * filename)
+{
+#if defined(ITZAM_UNIX)
+ return open(filename, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH );
+#else
+ HANDLE result = NULL;
+
+ do
+ {
+ result = CreateFile((LPCSTR)filename,
+ GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL,
+ CREATE_ALWAYS,
+ FILE_FLAG_RANDOM_ACCESS, // | FILE_FLAG_WRITE_THROUGH,
+ NULL);
+
+ if ((result != NULL) && (result != INVALID_HANDLE_VALUE))
+ break;
+
+ if ((result == INVALID_HANDLE_VALUE) && (GetLastError() == ERROR_ACCESS_DENIED))
+ Sleep(250);
+ else
+ break;
+ }
+ while (1);
+
+ return result;
+
+#endif
+}
+
+ITZAM_FILE_TYPE itzam_file_open(const char * filename)
+{
+#if defined(ITZAM_UNIX)
+ return open(filename, O_RDWR);
+#else
+ return CreateFile((LPCSTR)filename,
+ GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL,
+ OPEN_EXISTING,
+ FILE_FLAG_RANDOM_ACCESS, // | FILE_FLAG_WRITE_THROUGH,
+ NULL);
+
+#endif
+}
+
+itzam_bool itzam_file_close(ITZAM_FILE_TYPE file)
+{
+#if defined(ITZAM_UNIX)
+ return (itzam_bool)(0 == close(file));
+#else
+ return (itzam_bool)CloseHandle(file);
+#endif
+}
+
+itzam_bool itzam_file_read(ITZAM_FILE_TYPE file, void * data, size_t len)
+{
+#if defined(ITZAM_UNIX)
+ return (itzam_bool)(len == read(file,data,len));
+#else
+ DWORD count;
+ return (itzam_bool)ReadFile(file, (LPVOID)data, (DWORD)len, &count, NULL);
+#endif
+}
+
+itzam_bool itzam_file_write(ITZAM_FILE_TYPE file, const void * data, size_t len)
+{
+#if defined(ITZAM_UNIX)
+ return (itzam_bool)(len == write(file,data,len));
+#else
+ DWORD count;
+ return (itzam_bool)WriteFile(file,(LPVOID)data, (DWORD)len, &count, NULL);
+#endif
+}
+
+itzam_ref itzam_file_seek(ITZAM_FILE_TYPE file, itzam_ref pos, int mode)
+{
+#if defined(ITZAM_UNIX)
+ return (itzam_ref)lseek(file,pos,mode);
+#else
+ return (itzam_ref)SetFilePointer(file, (LONG)pos, NULL, mode);
+#endif
+}
+
+itzam_ref itzam_file_tell(ITZAM_FILE_TYPE file)
+{
+#if defined(ITZAM_UNIX)
+ return (itzam_ref)lseek(file,0,SEEK_CUR);
+#else
+ return (itzam_ref)SetFilePointer(file, 0, NULL, FILE_CURRENT);
+#endif
+}
+
+itzam_bool itzam_file_commit(ITZAM_FILE_TYPE file)
+{
+#if defined(ITZAM_UNIX)
+ return true; // (itzam_bool)commit(file);
+#else
+ return (itzam_bool)FlushFileBuffers(file);
+#endif
+}
+
+itzam_bool itzam_file_remove(const char * filename)
+{
+#if defined(ITZAM_UNIX)
+ return (itzam_bool)remove(filename);
+#else
+ return (itzam_bool)DeleteFile((LPCSTR)filename);
+#endif
+}
|