[elephant-cvs] CVS update: elephant/src/sleepycat.lisp

blee at common-lisp.net blee at common-lisp.net
Thu Sep 16 04:22:43 UTC 2004


Update of /project/elephant/cvsroot/elephant/src
In directory common-lisp.net:/tmp/cvs-serv25936/src

Modified Files:
	sleepycat.lisp 
Log Message:
split off berkeley-db
doc-strings
buffer-streamified
cmu pointer arithmetic

Date: Thu Sep 16 06:22:41 2004
Author: blee

Index: elephant/src/sleepycat.lisp
diff -u elephant/src/sleepycat.lisp:1.9 elephant/src/sleepycat.lisp:1.10
--- elephant/src/sleepycat.lisp:1.9	Thu Sep  2 16:47:09 2004
+++ elephant/src/sleepycat.lisp	Thu Sep 16 06:22:41 2004
@@ -42,20 +42,54 @@
 
 
 (defpackage sleepycat
+  (:documentation "A low-level UFFI-based interface to
+Berkeley DB / Sleepycat, via the libsleepycat.c wrapper.
+Partly intended to be usable outside Elephant, but with some
+magic for Elephant.  In general there is a 1-1 mapping from
+functions here and functions in Sleepycat, so refer to their
+documentation for details.")
   (:use common-lisp uffi)
+  #+cmu
+  (:use alien)
+  #+sbcl
+  (:use sb-alien)
+  #+cmu
+  (:import-from :sys
+		#:sap+)
+  #+sbcl
+  (:import-from :sb-sys
+		#:sap+)  
+  #+openmcl
+  (:import-from :ccl
+		#:byte-length)
   (:export #:*current-transaction* 
-	   #:read-int #:read-uint #:read-float #:read-double 
-	   #:write-int #:write-uint #:write-float #:write-double
-	   #:offset-char-pointer #:copy-str-to-buf #:copy-bufs #:byte-length
+	   
+	   #:buffer-stream #:make-buffer-stream #:with-buffer-streams
+	   #:resize-buffer-stream #:resize-buffer-stream-no-copy 
+	   #:reset-buffer-stream #:buffer-stream-buffer
+	   #:buffer-write-byte #:buffer-write-int 
+	   #:buffer-write-uint #:buffer-write-float #:buffer-write-double 
+	   #:buffer-write-string #:buffer-read-byte #:buffer-read-fixnum 
+	   #:buffer-read-int #:buffer-read-uint #:buffer-read-float 
+	   #:buffer-read-double #:buffer-read-string #:byte-length
+	   
 	   #:pointer-int #:pointer-void #:array-or-pointer-char
+	   
 	   #:db-env-create #:db-env-close #:db-env-open #:db-env-dbremove
 	   #:db-env-dbrename #:db-env-remove 
 	   #:db-env-set-flags #:db-env-get-flags
 	   #:db-create #:db-close #:db-open 
 	   #:db-remove #:db-rename #:db-sync #:db-truncate
+	   #:db-set-flags #:db-get-flags
 	   #:db-get-key-buffered #:db-get-buffered #:db-get 
 	   #:db-put-buffered #:db-put 
 	   #:db-delete-buffered #:db-delete 
+	   #:db-cursor #:db-cursor-close #:db-cursor-delete
+	   #:db-cursor-duplicate 
+	   #:db-cursor-move-buffered #:db-cursor-set-buffered
+	   #:db-cursor-get-both-buffered 
+	   #:db-cursor-pmove-buffered #:db-cursor-pset-buffered
+	   #:db-cursor-pget-both-buffered #:db-cursor-put-buffered
 	   #:db-transaction-begin #:db-transaction-abort 
 	   #:db-transaction-commit #:with-transaction 
 	   #:db-transaction-id #:db-env-lock-id #:db-env-lock-id-free
@@ -63,6 +97,7 @@
 	   #:db-env-set-timeout #:db-env-get-timeout
 	   #:db-env-set-lock-detect #:db-env-get-lock-detect
 	   #:db-error #:db-error-errno
+	   
 	   #:+NULL-VOID+ #:+NULL-CHAR+
 	   #:DB-BTREE #:DB-HASH #:DB-QUEUE #:DB-RECNO #:DB-UNKNOWN 
 	   #:DB_KEYEMPTY #:DB_LOCK_DEADLOCK #:DB_LOCK_NOTGRANTED 
@@ -74,6 +109,10 @@
 
 (in-package "SLEEPYCAT")
 
+#+cmu
+(eval-when (:compile-toplevel)
+  (proclaim '(optimize (ext:inhibit-warnings 3))))
+
 (eval-when (:compile-toplevel :load-toplevel)
   ;; UFFI
   ;;(asdf:operate 'asdf:load-op :uffi)
@@ -98,14 +137,14 @@
        #+(or bsd freebsd)
        "/usr/local/lib/db42/libdb.so" 
        #+darwin
-       "/usr/local/BerkeleyDB.4.2/lib/libdb.dylib"
+       "/usr/local/BerkeleyDB.4.2/lib/libdb.dylib" 
        :module "sleepycat")
     (error "Couldn't load libdb (Sleepycat)!"))
 
   ;; Libsleepycat.so: edit this
   (unless
       (uffi:load-foreign-library 
-       "/usr/local/share/common-lisp/elephant-0.1/libsleepycat.so" 
+       "/usr/local/share/common-lisp/elephant-0.2/libsleepycat.so" 
        :module "libsleepycat")
     (error "Couldn't load libsleepycat!"))
 
@@ -123,16 +162,15 @@
 (declaim (inline read-int read-uint read-float read-double 
 		 write-int write-uint write-float write-double
 		 offset-char-pointer copy-str-to-buf copy-bufs
-		 %db-get-key-buffered db-get-key-buffered 
-		 %db-get-buffered db-get-buffered db-get 
-		 %db-put-buffered db-put-buffered 
-		 %db-put db-put 
-		 %db-delete db-delete-buffered db-delete 
-		 %db-txn-begin db-transaction-begin
-		 %db-txn-abort db-transaction-abort
-		 %db-txn-commit db-transaction-commit
-		 %db-transaction-id
-		 flags))
+		 ;;resize-buffer-stream 
+		 ;;buffer-stream-buffer buffer-stream-size buffer-stream-position
+		 ;;buffer-stream-length 
+		 reset-buffer-stream
+		 buffer-write-byte buffer-write-int buffer-write-uint
+		 buffer-write-float buffer-write-double buffer-write-string
+		 buffer-read-byte buffer-read-fixnum buffer-read-int
+		 buffer-read-uint buffer-read-float buffer-read-double 
+		 buffer-read-string))
 
 ;; Constants and Flags
 ;; eventually write a macro which generates a custom flag function.
@@ -169,81 +207,293 @@
 (defconstant DB_TXN_NOWAIT    #x0001000)
 (defconstant DB_TXN_SYNC      #x0002000)
 (defconstant DB_LOCK_NOWAIT   #x001)
+(defconstant DB_DUP	      #x0000002)
+(defconstant DB_DUPSORT	      #x0000004)
 
-(defconstant DB_GET_BOTH         10)
-(defconstant DB_SET_LOCK_TIMEOUT 29)
-(defconstant DB_SET_TXN_TIMEOUT  33)
-
-(defconstant DB_KEYEMPTY        -30997)
-(defconstant DB_LOCK_DEADLOCK   -30995)
-(defconstant DB_LOCK_NOTGRANTED -30994)
-(defconstant DB_NOTFOUND        -30990)
+(defconstant DB_CURRENT		      7)
+(defconstant DB_FIRST		      9)
+(defconstant DB_GET_BOTH	     10)
+(defconstant DB_GET_BOTH_RANGE	     12)
+(defconstant DB_LAST		     17)
+(defconstant DB_NEXT		     18)
+(defconstant DB_NEXT_DUP	     19)
+(defconstant DB_NEXT_NODUP	     20)
+(defconstant DB_PREV		     25)
+(defconstant DB_PREV_NODUP	     26)
+(defconstant DB_SET		     28)
+(defconstant DB_SET_RANGE	     30)
+
+(defconstant DB_AFTER		      1)
+(defconstant DB_BEFORE		      3)
+(defconstant DB_KEYFIRST	     15)
+(defconstant DB_KEYLAST		     16)
+
+(defconstant DB_NODUPDATA	     21)
+(defconstant DB_NOOVERWRITE	     22)
+(defconstant DB_NOSYNC		     23)
+
+(defconstant DB_POSITION	     24)
+
+(defconstant DB_SET_LOCK_TIMEOUT     29)
+(defconstant DB_SET_TXN_TIMEOUT      33)
+
+(defconstant DB_KEYEMPTY         -30997)
+(defconstant DB_KEYEXIST	 -30996)
+(defconstant DB_LOCK_DEADLOCK    -30995)
+(defconstant DB_LOCK_NOTGRANTED  -30994)
+(defconstant DB_NOTFOUND         -30990)
 
-(defvar +NULL-VOID+ (make-null-pointer :void))
-(defvar +NULL-CHAR+ (make-null-pointer :char))
+(defconstant DB_LOCK_DEFAULT	     1)
+(defconstant DB_LOCK_EXPIRE	     2)
+(defconstant DB_LOCK_MAXLOCKS        3)
+(defconstant DB_LOCK_MINLOCKS        4)
+(defconstant DB_LOCK_MINWRITE        5)
+(defconstant DB_LOCK_OLDEST	     6)
+(defconstant DB_LOCK_RANDOM	     7)
+(defconstant DB_LOCK_YOUNGEST        8)
 
+(defvar +NULL-VOID+ (make-null-pointer :void)
+  "A null pointer to a void type.")
+(defvar +NULL-CHAR+ (make-null-pointer :char)
+  "A null pointer to a char type.")
 
-;; Buffer management / pointer arithmetic
 
-;; Notes: on CMUCL and Allegro: with-cast-pointer +
-;; deref-array is faster than FFI + C pointer arithmetic.
-;; however pointer arithmetic is usually consing.  OpenMCL
-;; supports non-consing pointer arithmentic though.
+(def-enum DB-LOCKOP ((:DUMP 0) :GET :GET-TIMEOUT :INHERIT 
+		     :PUT :PUT-ALL :PUT-OBJ :PUT-READ
+		     :TIMEOUT :TRADE :UPGRADE-WRITE))
 
+(def-enum DB-LOCKMODE ((:NG 0) :READ :WRITE :WAIT 
+		       :IWRITE :IREAD :IWR :DIRTY :WWRITE))
 
-;; TODO: #+openmcl versions which do macptr arith.
+(def-struct DB-LOCK
+    (off :unsigned-int)
+  (ndx :unsigned-int)
+  (gen :unsigned-int)
+  (mode DB-LOCKMODE))
+
+#+openmcl
+(ccl:def-foreign-type DB-LOCK (:struct DB-LOCK))
+
+(def-struct DB-LOCKREQ
+    (op DB-LOCKOP)
+  (mode DB-LOCKMODE)
+  (timeout :unsigned-int)
+  (obj (:array :char))
+  (lock (* DB-LOCK)))
+
+#+openmcl
+(ccl:def-foreign-type DB-LOCKREQ (:struct DB-LOCKREQ))
+
+
+;; Thread local storage (special variables)
+
+(defvar *current-transaction* +NULL-VOID+
+  "The transaction which is currently in effect.")
+
+(defvar *errno-buffer* (allocate-foreign-object :int 1)
+  "Resourced space for errno return values.")
+(defvar *buffer-streams* (make-array 0 :adjustable t :fill-pointer t)
+  "Vector of buffer-streams, which you can grab / return.")
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;;
+;;; buffer-streams
+;;;
+;;; a stream-like interface for our buffers; methods are
+;;; below.  ultimately we might want a gray / simple -stream
+;;; for real, for now who cares?
+
+(defstruct buffer-stream
+  "A stream-like interface to foreign (alien) char buffers."
+  (buffer (allocate-foreign-object :char 10) :type array-or-pointer-char)
+  (size 0 :type fixnum)
+  (position 0 :type fixnum)
+  (length 10 :type fixnum))
+
+(defun grab-buffer-stream ()
+  "Grab a buffer-stream from the *buffer-streams* resource pool."
+  (declare (optimize (speed 3)))
+  (if (= (length *buffer-streams*) 0)
+      (make-buffer-stream)
+      (vector-pop *buffer-streams*)))
+
+(defun return-buffer-stream (bs)
+  "Return a buffer-stream to the *buffer-streams* resource pool."
+  (declare (optimize (speed 3)))
+  (reset-buffer-stream bs)
+  (vector-push-extend bs *buffer-streams*))
+
+(defmacro with-buffer-streams (names &body body)
+  "Grab a buffer-stream, executes forms, and returns the
+stream to the pool on exit."
+  `(let ,(loop for name in names collect (list name '(grab-buffer-stream)))
+    (unwind-protect
+	 (progn , at body)
+      (progn
+	,@(loop for name in names 
+		collect (list 'return-buffer-stream name))))))
+
+;; Buffer management / pointer arithmetic
+
+;; Notes: on Allegro: with-cast-pointer + deref-array is
+;; faster than FFI + C pointer arithmetic.  however pointer
+;; arithmetic is usually consing.  OpenMCL supports
+;; non-consing pointer arithmentic though.  Check these
+;; CMUCL / SBCL things don't cons unless necessary.
+
+;; TODO: #+openmcl versions which do macptr arith.  
+
+#+(or cmu sbcl)
+(defun read-int (buf offset)
+  "Read a 32-bit signed integer from a foreign char buffer."
+  (declare (optimize (speed 3) (safety 0))
+	   (type (alien (* char)) buf)
+	   (type fixnum offset))
+  (the (signed-byte 32)
+    (deref (cast (sap-alien (sap+ (alien-sap buf) offset) (* char))
+		 (* integer)))))
+
+#+(or cmu sbcl)
+(defun read-uint (buf offset)
+  "Read a 32-bit unsigned integer from a foreign char buffer."
+  (declare (optimize (speed 3) (safety 0))
+	   (type (alien (* char)) buf)
+	   (type fixnum offset))
+  (the (unsigned-byte 32)
+    (deref (cast (sap-alien (sap+ (alien-sap buf) offset) (* char))
+		 (* (unsigned 32))))))
+
+#+(or cmu sbcl)
+(defun read-float (buf offset)
+  "Read a single-float from a foreign char buffer."
+  (declare (optimize (speed 3) (safety 0))
+	   (type (alien (* char)) buf)
+	   (type fixnum offset))
+  (the single-float
+    (deref (cast (sap-alien (sap+ (alien-sap buf) offset) (* char))
+		 (* single-float)))))
+
+#+(or cmu sbcl)
+(defun read-double (buf offset)
+  "Read a double-float from a foreign char buffer."
+  (declare (optimize (speed 3) (safety 0))
+	   (type (alien (* char)) buf)
+	   (type fixnum offset))
+  (the double-float
+    (deref (cast (sap-alien (sap+ (alien-sap buf) offset) (* char))
+		 (* double-float)))))
+
+#+(or cmu sbcl)
+(defun write-int (buf num offset)
+  "Write a 32-bit signed integer to a foreign char buffer."
+  (declare (optimize (speed 3) (safety 0))
+	   (type (alien (* char)) buf)
+	   (type (signed-byte 32) num)
+	   (type fixnum offset))
+  (setf (deref (cast (sap-alien (sap+ (alien-sap buf) offset) (* char))
+		     (* integer))) num))
+
+#+(or cmu sbcl)
+(defun write-uint (buf num offset)
+  "Write a 32-bit unsigned integer to a foreign char buffer."
+  (declare (optimize (speed 3) (safety 0))
+	   (type (alien (* char)) buf)
+	   (type (unsigned-byte 32) num)
+	   (type fixnum offset))
+  (setf (deref (cast (sap-alien (sap+ (alien-sap buf) offset) (* char))
+		     (* (unsigned 32)))) num))
+
+#+(or cmu sbcl)
+(defun write-float (buf num offset)
+  "Write a single-float to a foreign char buffer."
+  (declare (optimize (speed 3) (safety 0))
+	   (type (alien (* char)) buf)
+	   (type single-float num)
+	   (type fixnum offset))
+  (setf (deref (cast (sap-alien (sap+ (alien-sap buf) offset) (* char))
+		     (* single-float))) num))
+
+#+(or cmu sbcl)
+(defun write-double (buf num offset)
+  "Write a double-float to a foreign char buffer."
+  (declare (optimize (speed 3) (safety 0))
+	   (type (alien (* char)) buf)
+	   (type double-float num)
+	   (type fixnum offset))
+  (setf (deref (cast (sap-alien (sap+ (alien-sap buf) offset) (* char))
+		     (* double-float))) num))
+
+#+(or cmu sbcl)
+(defun offset-char-pointer (p offset)
+  "Pointer arithmetic."
+  (declare (optimize (speed 3) (safety 0))
+	   (type (alien (* char)) p)
+	   (type fixnum offset))
+  (sap-alien (sap+ (alien-sap p) offset) (* char)))
 
+#-(or cmu sbcl)
 (def-function ("read_int" read-int)
     ((buf array-or-pointer-char)
      (offset :int))
   :returning :int)
 
+#-(or cmu sbcl)
 (def-function ("read_uint" read-uint)
     ((buf array-or-pointer-char)
      (offset :int))
   :returning :unsigned-int)
 
+#-(or cmu sbcl)
 (def-function ("read_float" read-float)
     ((buf array-or-pointer-char)
      (offset :int))
   :returning :float)
 
+#-(or cmu sbcl)
 (def-function ("read_double" read-double)
     ((buf array-or-pointer-char)
      (offset :int))
   :returning :double)
 
+#-(or cmu sbcl)
 (def-function ("write_int" write-int)
     ((buf array-or-pointer-char)
      (num :int)
      (offset :int))
   :returning :void)
 
+#-(or cmu sbcl)
 (def-function ("write_uint" write-uint)
     ((buf array-or-pointer-char)
      (num :unsigned-int)
      (offset :int))
   :returning :void)
 
+#-(or cmu sbcl)
 (def-function ("write_float" write-float)
     ((buf array-or-pointer-char)
      (num :float)
      (offset :int))
   :returning :void)
 
+#-(or cmu sbcl)
 (def-function ("write_double" write-double)
     ((buf array-or-pointer-char)
      (num :double)
      (offset :int))
   :returning :void)
 
+#-(or cmu sbcl)
 (def-function ("offset_charp" offset-char-pointer)
     ((p array-or-pointer-char)
      (offset :int))
   :returning array-or-pointer-char)
 
 ;; Allegro and Lispworks use 16-bit unicode characters
+#+(or cmu sbcl allegro lispworks)
 (defmacro byte-length (s)
+  "Return the number of bytes of the internal representation
+of a string."
   #+(or lispworks (and allegro ics))
   `(let ((l (length ,s))) (+ l l))
   #-(or lispworks (and allegro ics))
@@ -274,6 +524,7 @@
 ;; but OpenMCL can't directly pass string bytes.
 #+openmcl
 (defun copy-str-to-buf (dest dest-offset src src-offset length)
+  "Copy a string to a foreign buffer.  From Gary Byers."
   (declare (optimize (speed 3) (safety 0))
 	   (type string src)
 	   (type array-or-pointer-char dest)
@@ -287,6 +538,7 @@
 ;; Lisp version, for kicks.  this assumes 8-bit chars!
 #+(not (or cmu sbcl scl allegro openmcl lispworks))
 (defun copy-str-to-buf (dest dest-offset src src-offset length)
+  "Copy a string to a foreign buffer."
   (declare (optimize (speed 3) (safety 0))
 	   (type string src)
 	   (type array-or-pointer-char dest)
@@ -313,36 +565,240 @@
      (length :int))
   :returning :void)    
 
-;; Thread local storage (special variables)
 
-(declaim (type array-or-pointer-char *get-buffer*)
-	 (type fixnum *get-buffer-length*))
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;;
+;;; buffer-stream functions
+
+(eval-when (:compile-toplevel)
+  (defun process-struct-slot-defs (slot-defs struct)
+    (loop for def in slot-defs
+	  collect (list (first def) (list (second def) struct)))))
+
+(defmacro with-struct-slots (slot-defs struct &body body)
+  `(symbol-macrolet ,(process-struct-slot-defs slot-defs struct)
+    , at body))
+
+(defun resize-buffer-stream (bs length)
+  "Resize the underlying buffer of a buffer-stream, copying the old data."
+  (declare (optimize (speed 3) (safety 0))
+	   (type buffer-stream bs)
+	   (type fixnum length))
+  (with-struct-slots ((buf buffer-stream-buffer)
+		      (size buffer-stream-size)
+		      (len buffer-stream-length))
+    bs		      
+    (when (> length len)
+      (let ((newlen (max length (* len 2))))
+	(declare (type fixnum newlen))
+	(let ((newbuf (allocate-foreign-object :char newlen)))
+	  ;; technically we just need to copy from position to size.....
+	  (copy-bufs newbuf 0 buf 0 size)
+	  (free-foreign-object buf)
+	  (setf buf newbuf)
+	  (setf len newlen)
+	  nil)))))
+
+(defun resize-buffer-stream-no-copy (bs length)
+  "Resize the underlying buffer of a buffer-stream."
+  (declare (optimize (speed 3) (safety 0))
+	   (type buffer-stream bs)
+	   (type fixnum length))
+  (with-struct-slots ((buf buffer-stream-buffer)
+		      (size buffer-stream-size)
+		      (len buffer-stream-length))
+    bs		      
+    (when (> length len)
+      (let ((newlen (max length (* len 2))))
+	(declare (type fixnum newlen))
+	(let ((newbuf (allocate-foreign-object :char newlen)))
+	  (free-foreign-object buf)
+	  (setf buf newbuf)
+	  (setf len newlen)
+	  nil)))))
 
-(defvar *current-transaction* +NULL-VOID+)
+(defun reset-buffer-stream (bs)
+  "'Empty' the buffer-stream."
+  (declare (optimize (speed 3) (safety 0))
+	   (type buffer-stream bs))
+  (setf (buffer-stream-size bs) 0)
+  (setf (buffer-stream-position bs) 0))
 
-(defvar *errno-buffer* (allocate-foreign-object :int 1))
+(defun buffer-write-byte (b bs)
+  "Write a byte."
+  (declare (optimize (speed 3) (safety 0))
+	   (type buffer-stream bs)
+	   (type (unsigned-byte 8) b))
+  (with-struct-slots ((buf buffer-stream-buffer)
+		      (size buffer-stream-size)
+		      (len buffer-stream-length))
+    bs		      
+    (let ((needed (+ size 1)))
+      (when (> needed len)
+	(resize-buffer-stream bs needed))
+      (setf (deref-array buf '(:array :char) size) b)
+      (setf size needed))))
 
-(defvar *get-buffer* (allocate-foreign-object :char 1))
-(defvar *get-buffer-length* 0)
+(defun buffer-write-int (i bs)
+  "Write a 32-bit signed integer."
+  (declare (optimize (speed 3) (safety 0))
+	   (type buffer-stream bs)
+	   (type (signed-byte 32) i))
+  (with-struct-slots ((buf buffer-stream-buffer)
+		      (size buffer-stream-size)
+		      (len buffer-stream-length))
+    bs		      
+    (let ((needed (+ size 4)))
+      (when (> needed len)
+	(resize-buffer-stream bs needed))
+      (write-int buf i size)
+      (setf size needed)
+      nil)))
 
-(defun resize-get-buffer (length)
-  (declare (optimize (speed 3) (safety 0) (space 0))
+(defun buffer-write-uint (u bs)
+  "Write a 32-bit unsigned integer."
+  (declare (optimize (speed 3) (safety 0))
+	   (type buffer-stream bs)
+	   (type (unsigned-byte 32) u))
+  (with-struct-slots ((buf buffer-stream-buffer)
+		      (size buffer-stream-size)
+		      (len buffer-stream-length))
+    bs		      
+    (let ((needed (+ size 4)))
+      (when (> needed len)
+	(resize-buffer-stream bs needed))
+      (write-uint buf u size)
+      (setf size needed)
+      nil)))
+
+(defun buffer-write-float (d bs)
+  "Write a single-float."
+  (declare (optimize (speed 3) (safety 0))
+	   (type buffer-stream bs)
+	   (type single-float d))
+  (with-struct-slots ((buf buffer-stream-buffer)
+		      (size buffer-stream-size)
+		      (len buffer-stream-length))
+    bs		      
+    (let ((needed (+ size 4)))
+      (when (> needed len)
+	(resize-buffer-stream bs needed))
+      (write-float buf d size)
+      (setf size needed)
+      nil)))
+
+(defun buffer-write-double (d bs)
+  "Write a double-float."
+  (declare (optimize (speed 3) (safety 0))
+	   (type buffer-stream bs)
+	   (type double-float d))
+  (with-struct-slots ((buf buffer-stream-buffer)
+		      (size buffer-stream-size)
+		      (len buffer-stream-length))
+    bs		      
+    (let ((needed (+ size 8)))
+      (when (> needed len)
+	(resize-buffer-stream bs needed))
+      (write-double buf d size)
+      (setf size needed)
+      nil)))
+
+(defun buffer-write-string (s bs)
+  "Write the underlying bytes of a string.  On Unicode
+Lisps, this is a 16-bit operation."
+  (declare (optimize (speed 3) (safety 0))
+	   (type buffer-stream bs)
+	   (type string s))
+  (with-struct-slots ((buf buffer-stream-buffer)
+		      (size buffer-stream-size)
+		      (len buffer-stream-length))
+    bs		      
+    (let* ((str-bytes (byte-length s))
+	   (needed (+ size str-bytes)))
+      (declare (type fixnum str-bytes needed)
+	       (dynamic-extent str-bytes needed))
+      (when (> needed len)
+	(resize-buffer-stream bs needed))
+      (copy-str-to-buf buf size s 0 str-bytes)
+      (setf size needed)
+      nil)))
+
+(defun buffer-read-byte (bs)
+  "Read a byte."
+  (declare (optimize (speed 3) (safety 0))
+	   (type buffer-stream bs))
+  (let ((position (buffer-stream-position bs)))
+    (incf (buffer-stream-position bs))
+    (deref-array (buffer-stream-buffer bs) '(:array :char) position)))
+
+(defun buffer-read-fixnum (bs)
+  "Read a 32-bit signed integer, which is assumed to be a fixnum."
+  (declare (optimize (speed 3) (safety 0))
+	   (type buffer-stream bs))
+  (let ((position (buffer-stream-position bs)))
+    (setf (buffer-stream-position bs) (+ position 4))
+    (the fixnum (read-int (buffer-stream-buffer bs) position))))
+
+(defun buffer-read-int (bs)
+  "Read a 32-bit signed integer."
+  (declare (optimize (speed 3) (safety 0))
+	   (type buffer-stream bs))
+  (let ((position (buffer-stream-position bs)))
+    (setf (buffer-stream-position bs) (+ position 4))
+    (the (signed-byte 32) (read-int (buffer-stream-buffer bs) position))))
+
+(defun buffer-read-uint (bs)
+  "Read a 32-bit unsigned integer."
+  (declare (optimize (speed 3) (safety 0))
+	   (type buffer-stream bs))
+  (let ((position (buffer-stream-position bs)))
+    (setf (buffer-stream-position bs) (+ position 4))
+    (the (unsigned-byte 32)(read-uint (buffer-stream-buffer bs) position))))
+
+(defun buffer-read-float (bs)
+  "Read a single-float."
+  (declare (optimize (speed 3) (safety 0))
+	   (type buffer-stream bs))
+  (let ((position (buffer-stream-position bs)))
+    (setf (buffer-stream-position bs) (+ position 4))
+    (read-float (buffer-stream-buffer bs) position)))
+
+(defun buffer-read-double (bs)
+  "Read a double-float."
+  (declare (optimize (speed 3) (safety 0))
+	   (type buffer-stream bs))
+  (let ((position (buffer-stream-position bs)))
+    (setf (buffer-stream-position bs) (+ position 8))
+    (read-double (buffer-stream-buffer bs) position)))
+
+(defun buffer-read-string (bs length)
+  "Read a string.  On Unicode Lisps this is a 16-bit operation!"
+  (declare (optimize (speed 3) (safety 0))
+	   (type buffer-stream bs)
 	   (type fixnum length))
-  (if (< length *get-buffer-length*)
-      (values *get-buffer* *get-buffer-length*)
-      (let ((newlen (max length (* *get-buffer-length* 2))))
-	(declare (type fixnum newlen))
-	(setq *get-buffer-length* newlen)
-	(free-foreign-object *get-buffer*)
-	(setq *get-buffer* (allocate-foreign-object :char newlen))
-	(values *get-buffer* *get-buffer-length*))))
+  (let ((position (buffer-stream-position bs)))
+    (setf (buffer-stream-position bs) (+ position length))
+    ;; wide!!!
+    #+(and allegro ics)
+    (excl:native-to-string 
+     (offset-char-pointer (buffer-stream-buffer bs) position) 
+     :length length
+     :external-format :unicode)
+    #+lispworks
+    (fli:convert-from-foreign-string 
+     (offset-char-pointer (buffer-stream-buffer bs) position)
+     :length length :external-format :unicode :null-terminated-p nil)
+    #-(or lispworks (and allegro ics))
+    (convert-from-foreign-string 
+     (offset-char-pointer (buffer-stream-buffer bs) position) 
+     :length length :null-terminated-p nil)))
 
 ;; Wrapper macro -- handles errno return values
 ;; makes flags into keywords
 ;; makes keyword args, cstring wrappers
 
 
-(eval-when (:compile-toplevel :load-toplevel)
+(eval-when (:compile-toplevel)
   (defun make-wrapper-args (args flags keys)
     (if (or flags keys)
 	(append (remove-keys (remove 'flags args) keys)
@@ -378,6 +834,7 @@
 
 (defmacro wrap-errno (names args &key (keys nil) (flags nil)
 		      (cstrings nil) (outs 1) (declarations nil)
+		      (documentation nil)
 		      (transaction nil))
   (let ((wname (if (listp names) (first names) names))
 	(fname (if (listp names) (second names) 
@@ -388,7 +845,8 @@
     (if (> outs 1)
 	(let ((out-args (make-out-args outs)))
 	  `(defun ,wname ,wrapper-args
-	    ,@(if declarations (list declarations) (values))
+	    ,@(if documentation (list documentation) (values))
+	    ,@(if declarations (list declarations) (values))	    
 	    (with-cstrings ,(symbols-to-pairs cstrings)
 	      (multiple-value-bind ,out-args
 		  (,fname , at fun-args)
@@ -399,10 +857,11 @@
 		    ,@(if transaction
 			  (list `((or (= ,errno DB_LOCK_DEADLOCK)
 				   (= ,errno DB_LOCK_NOTGRANTED))
-				  (throw ,transaction ,transaction)))
+				  (throw 'transaction ,transaction)))
 			  (values))
 		    (t (error 'db-error :errno ,errno))))))))
 	`(defun ,wname ,wrapper-args
+	  ,@(if documentation (list documentation) (values))
 	  ,@(if declarations (list declarations) (values))
 	  (with-cstrings ,(symbols-to-pairs cstrings)
 	    (let ((,errno (,fname , at fun-args)))
@@ -412,769 +871,71 @@
 		,@(if transaction
 		      (list `((or (= ,errno DB_LOCK_DEADLOCK)
 			       (= ,errno DB_LOCK_NOTGRANTED))
-			      (throw ,transaction ,transaction)))
+			      (throw 'transaction ,transaction)))
 		      (values))
 		(t (error 'db-error :errno ,errno)))))))))
 
+(defmacro flags (&key auto-commit joinenv init-cdb init-lock init-log
+		 init-mpool init-rep init-txn recover recover-fatal lockdown
+		 private system-mem thread force dirty-read create excl nommap
+		 rdonly truncate txn-nosync txn-nowait txn-sync lock-nowait
+		 dup dup-sort current first get-both get-both-range last next
+		 next-dup next-nodup prev prev-nodup set set-range
+		 after before keyfirst keylast
+		 no-dup-data no-overwrite nosync position set-lock-timeout
+		 set-transaction-timeout)
+  (let ((flags (gensym)))
+    `(let ((,flags 0))
+      (declare (type fixnum ,flags))
+      ,@(when auto-commit `((when ,auto-commit (setq ,flags (logior ,flags DB_AUTO_COMMIT)))))
+      ,@(when joinenv `((when ,joinenv (setq ,flags (logior ,flags DB_JOINENV)))))
+      ,@(when init-cdb `((when ,init-cdb (setq ,flags (logior ,flags DB_INIT_CDB)))))
+      ,@(when init-lock `((when ,init-lock (setq ,flags (logior ,flags DB_INIT_LOCK)))))
+      ,@(when init-log `((when ,init-log (setq ,flags (logior ,flags DB_INIT_LOG)))))
+      ,@(when init-mpool `((when ,init-mpool (setq ,flags (logior ,flags DB_INIT_MPOOL)))))
+      ,@(when init-rep `((when ,init-rep (setq ,flags (logior ,flags DB_INIT_REP)))))
+      ,@(when init-txn `((when ,init-txn (setq ,flags (logior ,flags DB_INIT_TXN)))))
+      ,@(when recover `((when ,recover (setq ,flags (logior ,flags DB_RECOVER)))))
+      ,@(when recover-fatal `((when ,recover-fatal (setq ,flags (logior ,flags DB_RECOVER_FATAL)))))
+      ,@(when lockdown `((when ,lockdown (setq ,flags (logior ,flags DB_LOCKDOWN)))))
+      ,@(when private `((when ,private (setq ,flags (logior ,flags DB_PRIVATE)))))
+      ,@(when system-mem `((when ,system-mem (setq ,flags (logior ,flags DB_SYSTEM_MEM)))))
+      ,@(when thread `((when ,thread (setq ,flags (logior ,flags DB_THREAD)))))
+      ,@(when force `((when ,force (setq ,flags (logior ,flags DB_FORCE)))))
+      ,@(when dirty-read `((when ,dirty-read (setq ,flags (logior ,flags DB_DIRTY_READ)))))
+      ,@(when create `((when ,create (setq ,flags (logior ,flags DB_CREATE)))))
+      ,@(when excl `((when ,excl (setq ,flags (logior ,flags DB_EXCL)))))
+      ,@(when nommap `((when ,nommap (setq ,flags (logior ,flags DB_NOMMAP)))))
+      ,@(when rdonly `((when ,rdonly (setq ,flags (logior ,flags DB_RDONLY)))))
+      ,@(when truncate `((when ,truncate (setq ,flags (logior ,flags DB_TRUNCATE)))))
+      ,@(when txn-nosync `((when ,txn-nosync (setq ,flags (logior ,flags DB_TXN_NOSYNC)))))
+      ,@(when txn-nowait `((when ,txn-nowait (setq ,flags (logior ,flags DB_TXN_NOWAIT)))))
+      ,@(when txn-sync `((when ,txn-sync (setq ,flags (logior ,flags DB_TXN_SYNC)))))
+      ,@(when lock-nowait `((when ,lock-nowait (setq ,flags (logior ,flags DB_LOCK_NOWAIT)))))
+      ,@(when dup `((when ,dup (setq ,flags (logior ,flags DB_DUP)))))
+      ,@(when dup-sort `((when ,dup-sort (setq ,flags (logior ,flags DB_DUPSORT)))))
+      ,@(when current `((when ,current (setq ,flags (logior ,flags DB_CURRENT)))))
+      ,@(when first `((when ,first (setq ,flags (logior ,flags DB_FIRST)))))
+      ,@(when get-both `((when ,get-both (setq ,flags (logior ,flags DB_GET_BOTH)))))
+      ,@(when get-both-range `((when ,get-both-range (setq ,flags (logior ,flags DB_GET_BOTH_RANGE)))))
+      ,@(when last `((when ,last (setq ,flags (logior ,flags DB_LAST)))))
+      ,@(when next `((when ,next (setq ,flags (logior ,flags DB_NEXT)))))
+      ,@(when next-dup `((when ,next-dup (setq ,flags (logior ,flags DB_NEXT_DUP)))))
+      ,@(when next-nodup `((when ,next-nodup (setq ,flags (logior ,flags DB_NEXT_NODUP)))))
+      ,@(when prev `((when ,prev (setq ,flags (logior ,flags DB_PREV)))))
+      ,@(when prev-nodup `((when ,prev-nodup (setq ,flags (logior ,flags DB_PREV_NODUP)))))
+      ,@(when set `((when ,set (setq ,flags (logior ,flags DB_SET)))))
+      ,@(when set-range `((when ,set-range (setq ,flags (logior ,flags DB_SET_RANGE)))))
+      ,@(when after `((when ,after (setq ,flags (logior ,flags DB_AFTER)))))
+      ,@(when before `((when ,before (setq ,flags (logior ,flags DB_BEFORE)))))
+      ,@(when keyfirst `((when ,keyfirst (setq ,flags (logior ,flags DB_KEYFIRST)))))
+      ,@(when keylast `((when ,keylast (setq ,flags (logior ,flags DB_KEYLAST)))))
+      ,@(when no-dup-data `((when ,no-dup-data (setq ,flags (logior ,flags DB_NODUPDATA)))))
+      ,@(when no-overwrite `((when ,no-overwrite (setq ,flags (logior ,flags DB_NOOVERWRITE)))))
+      ,@(when nosync `((when ,nosync (setq ,flags (logior ,flags DB_NOSYNC)))))
+      ,@(when position `((when ,position (setq ,flags (logior ,flags DB_POSITION)))))    
+      ,@(when set-lock-timeout `((when ,set-lock-timeout (setq ,flags (logior ,flags DB_SET_LOCK_TIMEOUT)))))
+      ,@(when set-transaction-timeout `((when ,set-transaction-timeout (setq ,flags (logior ,flags DB_SET_TXN_TIMEOUT)))))
+      ,flags)))
 
-;; Environment
-
-(def-function ("db_env_cr" %db-env-create)
-    ((flags :unsigned-int)
-     (errno :int :out))
-  :returning :pointer-void)
-
-(defun db-env-create ()
-  (multiple-value-bind (env errno)
-      (%db-env-create 0)
-    (declare (type fixnum errno))
-    (if (= errno 0)
-	env
-	(error 'db-error :errno errno))))
-	     
-(def-function ("db_env_close" %db-env-close)
-    ((dbenvp :pointer-void)
-     (flags :unsigned-int))
-  :returning :int)
-
-(wrap-errno db-env-close (dbenvp flags))
-
-(def-function ("db_env_open" %db-env-open)
-    ((dbenvp :pointer-void)
-     (home :cstring)
-     (flags :unsigned-int)
-     (mode :int))
-  :returning :int)
-
-(wrap-errno db-env-open (dbenvp home flags mode)
-	    :flags (joinenv init-cdb init-lock init-log 
-			    init-mpool init-rep init-txn
-			    recover recover-fatal create
-			    lockdown private system-mem thread)
-	    :keys ((mode #o640))
-	    :cstrings (home))
-
-(def-function ("db_env_dbremove" %db-env-dbremove)
-    ((env :pointer-void)
-     (txn :pointer-void)
-     (file :cstring)
-     (database :cstring)
-     (flags :unsigned-int))
-  :returning :int)
-
-(wrap-errno db-env-dbremove (env transaction file database flags) 
-	    :flags (auto-commit)
-	    :keys ((transaction *current-transaction*)
-		   (database +NULL-CHAR+))
-	    :cstrings (file database)
-	    :transaction transaction)
-
-(def-function ("db_env_dbrename" %db-env-dbrename)
-    ((env :pointer-void)
-     (txn :pointer-void)
-     (file :cstring)
-     (database :cstring)
-     (newname :cstring)
-     (flags :unsigned-int))
-  :returning :int)
-
-(wrap-errno db-env-dbrename (env transaction file database newname flags) 
-	    :flags (auto-commit)
-	    :keys ((transaction *current-transaction*)
-		   (database +NULL-CHAR+))
-	    :cstrings (file database newname)
-	    :transaction transaction)
-
-(def-function ("db_env_remove" %db-env-remove)
-    ((env :pointer-void)
-     (home :cstring)
-     (flags :unsigned-int))
-  :returning :int)
-
-(wrap-errno db-env-remove (env home flags) :flags (force)
-	    :cstrings (home))
-
-(def-function ("db_env_set_flags" %db-env-set-flags)
-    ((env :pointer-void)
-     (flags :unsigned-int)
-     (onoff :int))
-  :returning :int)
-
-(wrap-errno db-env-set-flags (env flags onoff)
-	    :flags (auto-commit nommap txn-nosync))
-
-(def-function ("db_env_get_flags" %db-env-get-flags)
-    ((env :pointer-void)
-     (flags :unsigned-int :out))
-  :returning :int)
-
-(wrap-errno db-env-get-flags (env) :outs 2)
-	   
-
-;; Database
-
-(def-function ("db_cr" %db-create)
-    ((dbenv :pointer-void)
-     (flags :unsigned-int)
-     (errno :int :out))
-  :returning :pointer-void)
-	  
-(defun db-create (&optional (dbenv +NULL-VOID+))
-  (multiple-value-bind (db errno)
-      (%db-create dbenv 0)
-    (declare (type fixnum errno))
-    (if (= errno 0) 
-	db
-	(error 'db-error :errno errno))))
-
-(def-function ("db_close" %db-close)
-    ((db :pointer-void)
-     (flags :unsigned-int))
-  :returning :int)
-
-(wrap-errno db-close (db flags))
-
-(def-function ("db_open" %db-open)
-    ((db :pointer-void)
-     (txn :pointer-void)
-     (file :cstring)
-     (database :cstring)
-     (type DBTYPE)
-     (flags :unsigned-int)
-     (mode :int))
-  :returning :int)
-
-(wrap-errno db-open (db transaction file database type flags mode)
-	    :flags (auto-commit create dirty-read excl nommap 
-				rdonly thread truncate)
-	    :keys ((transaction *current-transaction*)
-		   (file +NULL-CHAR+)
-		   (database +NULL-CHAR+)
-		   (type DB-UNKNOWN)
-		   (mode #o640))
-	    :cstrings (file database)
-	    :transaction transaction)
-		
-(def-function ("db_remove" %db-remove)
-    ((db :pointer-void)
-     (file :cstring)
-     (database :cstring)
-     (flags :unsigned-int))
-  :returning :int)
-
-(wrap-errno db-remove (db file database flags)
-	    :keys ((database +NULL-CHAR+))
-	    :cstrings (file database))
-
-(def-function ("db_rename" %db-rename)
-    ((db :pointer-void)
-     (file :cstring)
-     (database :cstring)
-     (newname :cstring)
-     (flags :unsigned-int))
-  :returning :int)
-
-(wrap-errno db-rename (db file database newname flags)
-	    :keys ((database +NULL-CHAR+))
-	    :cstrings (file database newname))
-
-(def-function ("db_sync" %db-sync)
-    ((db :pointer-void)
-     (flags :unsigned-int))
-  :returning :int)
-
-(wrap-errno db-sync (db flags))
-
-(def-function ("db_truncate" %db-truncate)
-    ((db :pointer-void)
-     (txn :pointer-void)
-     (count :unsigned-int :out)
-     (flags :unsigned-int))
-  :returning :int)
-
-(wrap-errno db-truncate (db transaction flags) :flags (auto-commit) 
-	    :keys ((transaction *current-transaction*)) :outs 2
-	    :transaction transaction)
-
-;; Accessors
-
-(def-function ("db_get_raw" %db-get-key-buffered)
-    ((db :pointer-void)
-     (txn :pointer-void)
-     (key array-or-pointer-char)
-     (key-length :unsigned-int)
-     (buffer array-or-pointer-char)
-     (buffer-length :unsigned-int)
-     (flags :unsigned-int)
-     (result-length :unsigned-int :out))
-  :returning :int)
-
-(defun db-get-key-buffered (db key-buffer key-length &key
-			    (transaction *current-transaction*)
-			    auto-commit get-both dirty-read)
-  (declare (optimize (speed 3) (safety 0) (space 0))
-	   (type pointer-void db transaction)
-	   (type array-or-pointer-char key-buffer)
-	   (type fixnum key-length)
-	   (type boolean auto-commit get-both dirty-read))
-  (loop 
-   do
-   (multiple-value-bind (errno result-length)
-       (%db-get-key-buffered db transaction key-buffer key-length 
-			     *get-buffer* *get-buffer-length*
-			     (flags :auto-commit auto-commit
-				    :get-both get-both
-				    :dirty-read dirty-read))
-     (declare (type fixnum result-length errno))
-     (if (<= result-length *get-buffer-length*)
-	 (cond 
-	   ((= errno 0)
-	    (return-from db-get-key-buffered 
-	      (the (values array-or-pointer-char fixnum)
-		(values *get-buffer* result-length))))
-	   ((or (= errno DB_NOTFOUND) (= errno DB_KEYEMPTY))
-		(return-from db-get-key-buffered 
-		  (the (values null fixnum) (values nil 0))))
-	   ((or (= errno DB_LOCK_DEADLOCK) (= errno DB_LOCK_NOTGRANTED))
-	    (throw transaction transaction))
-	   (t (error 'db-error :errno errno)))
-	 (resize-get-buffer result-length)))))
-
-(def-function ("db_get_raw" %db-get-buffered)
-    ((db :pointer-void)
-     (txn :pointer-void)
-     (key :cstring)
-     (key-length :unsigned-int)
-     (buffer array-or-pointer-char)
-     (buffer-length :unsigned-int)
-     (flags :unsigned-int)
-     (result-length :unsigned-int :out))
-  :returning :int)
-
-(defun db-get-buffered (db key &key
-			(key-length (length key))
-			(transaction *current-transaction*)
-			auto-commit get-both dirty-read)
-  (declare (optimize (speed 3) (safety 0) (space 0))
-	   (type pointer-void db transaction)
-	   (type string key)
-	   (type fixnum key-length)
-	   (type boolean auto-commit get-both dirty-read))
-  (with-cstring (k key)
-    (loop 
-     do
-     (multiple-value-bind (errno result-length)
-	 (%db-get-buffered db transaction k key-length 
-			   *get-buffer* *get-buffer-length*
-			   (flags :auto-commit auto-commit
-				  :get-both get-both
-				  :dirty-read dirty-read))
-       (declare (type fixnum result-length errno))
-       (if (<= result-length *get-buffer-length*)
-	   (cond 
-	     ((= errno 0)
-	      (return-from db-get-buffered 
-		(the (values array-or-pointer-char fixnum)
-		  (values *get-buffer* result-length))))
-	     ((or (= errno DB_NOTFOUND) (= errno DB_KEYEMPTY))
-		  (return-from db-get-buffered 
-		    (the (values null fixnum) (values nil 0))))
-	     ((or (= errno DB_LOCK_DEADLOCK) (= errno DB_LOCK_NOTGRANTED))
-	      (throw transaction transaction))
-	     (t (error 'db-error :errno errno)))
-	   (resize-get-buffer result-length))))))
-
-(defun db-get (db key &key (key-length (length key))
-	       (transaction *current-transaction*)
-	       auto-commit get-both dirty-read)
-  (declare (optimize (speed 3) (safety 0) (space 0))
-	   (type pointer-void db transaction)
-	   (type string key)
-	   (type fixnum key-length)
-	   (type boolean auto-commit get-both dirty-read))
-  (with-cstring (k key)
-    (loop 
-     do
-     (multiple-value-bind (errno result-length)
-	 (%db-get-buffered db transaction k key-length 
-			   *get-buffer* *get-buffer-length*
-			   (flags :auto-commit auto-commit
-				  :get-both get-both
-				  :dirty-read dirty-read))
-       (declare (type fixnum result-length errno))
-       (if (<= result-length *get-buffer-length*)
-	   (cond
-	     ((= errno 0)
-	      (return-from db-get
-		(convert-from-foreign-string *get-buffer* 
-					     :length result-length
-					     :null-terminated-p nil)))
-	     ((or (= errno DB_NOTFOUND) (= errno DB_KEYEMPTY))
-	      (return-from db-get nil))
-	     ((or (= errno DB_LOCK_DEADLOCK) (= errno DB_LOCK_NOTGRANTED))
-	      (throw transaction transaction))
-	     (t (error 'db-error :errno errno)))
-	   (resize-get-buffer result-length))))))
-
-(def-function ("db_put_raw" %db-put-buffered)
-    ((db :pointer-void)
-     (txn :pointer-void)
-     (key array-or-pointer-char)
-     (key-length :unsigned-int)
-     (datum array-or-pointer-char)
-     (datum-length :unsigned-int)
-     (flags :unsigned-int))
-  :returning :int)
-
-(wrap-errno db-put-buffered (db transaction key key-length 
-				datum datum-length flags)
-	    :flags (auto-commit)
-	    :keys ((transaction *current-transaction*))
-	    :declarations (declare (optimize (speed 3) (safety 0) (space 0))
-				   (type pointer-void db transaction)
-				   (type array-or-pointer-char key datum)
-				   (type fixnum key-length datum-length)
-				   (type boolean auto-commit))
-	    :transaction transaction)
-
-(def-function ("db_put_raw" %db-put)
-    ((db :pointer-void)
-     (txn :pointer-void)
-     (key :cstring)
-     (key-length :unsigned-int)
-     (datum :cstring)
-     (datum-length :unsigned-int)
-     (flags :unsigned-int))
-  :returning :int)
-
-(wrap-errno db-put (db transaction key key-length datum datum-length flags)
-	    :flags (auto-commit)
-	    :keys ((key-length (length key))
-		   (datum-length (length datum))
-		   (transaction *current-transaction*))
-	    :cstrings (key datum)
-	    :declarations (declare (optimize (speed 3) (safety 0) (space 0))
-				   (type pointer-void db transaction)
-				   (type string key datum)
-				   (type fixnum key-length datum-length)
-				   (type boolean auto-commit))
-	    :transaction transaction)
-
-(def-function ("db_del" %db-delete-buffered)
-    ((db :pointer-void)
-     (txn :pointer-void)
-     (key array-or-pointer-char)
-     (key-length :unsigned-int)
-     (flags :unsigned-int))
-  :returning :int)
-
-(defun db-delete-buffered  (db key key-length &key auto-commit 
-			    (transaction *current-transaction*))
-  (declare (optimize (speed 3) (safety 0) (space 0))
-	   (type pointer-void db transaction) (type array-or-pointer-char key)
-	   (type fixnum key-length) (type boolean auto-commit))
-  (let ((errno (%db-delete-buffered db transaction
-				    key key-length 
-				    (flags :auto-commit auto-commit))))
-    (declare (type fixnum errno))
-    (cond ((= errno 0) t)
-	  ((or (= errno DB_NOTFOUND) 
-	       (= errno DB_KEYEMPTY))
-	   nil)
-	  ((or (= errno DB_LOCK_DEADLOCK)
-	       (= errno DB_LOCK_NOTGRANTED))
-	   (throw transaction transaction))
-	  (t (error 'db-error :errno errno)))))
-
-(def-function ("db_del" %db-delete)
-    ((db :pointer-void)
-     (txn :pointer-void)
-     (key :cstring)
-     (key-length :unsigned-int)
-     (flags :unsigned-int))
-  :returning :int)
-
-(defun db-delete (db key &key auto-commit (key-length (length key))
-		  (transaction *current-transaction*))
-  (declare (optimize (speed 3) (safety 0) (space 0))
-	   (type pointer-void db transaction) (type string key)
-	   (type fixnum key-length) (type boolean auto-commit))
-  (with-cstrings ((key key))
-    (let ((errno
-	   (%db-delete db transaction key
-		       key-length (flags :auto-commit auto-commit))))
-      (declare (type fixnum errno))
-      (cond ((= errno 0) nil)
-	    ((or (= errno DB_NOTFOUND) 
-		 (= errno DB_KEYEMPTY))
-	     nil)
-	    ((or (= errno DB_LOCK_DEADLOCK)
-		 (= errno DB_LOCK_NOTGRANTED))
-	     (throw transaction transaction))
-	    (t (error 'db-error :errno errno))))))
-
-;; Transactions
-
-(def-function ("db_txn_begin" %db-txn-begin)
-    ((env :pointer-void)
-     (parent :pointer-void)
-     (flags :unsigned-int)
-     (errno (* :int)))
-  :returning :pointer-void)
-
-(defun db-transaction-begin (env &key (parent *current-transaction*)
-			     dirty-read txn-nosync txn-nowait
-			     txn-sync)
-  (declare (optimize (speed 3) (safety 0) (space 0))
-	   (type pointer-void env parent)
-	   (type boolean dirty-read txn-nosync txn-nowait
-		 txn-sync)
-	   (type pointer-int *errno-buffer*))
-  (let* ((txn
-	  (%db-txn-begin env parent
-			 (flags :dirty-read dirty-read
-				:txn-nosync txn-nosync
-				:txn-nowait txn-nowait
-				:txn-sync txn-sync)
-			 *errno-buffer*))
-	 (errno (deref-array *errno-buffer* '(:array :int) 0)))
-    (declare (type pointer-void txn)
-	     (type fixnum errno))
-    (if (= errno 0) 
-	txn
-	(error 'db-error :errno errno))))
-
-(def-function ("db_txn_abort" %db-txn-abort)
-    ((txn :pointer-void))
-  :returning :int)
-
-(wrap-errno (db-transaction-abort %db-txn-abort) (transaction)
-	    :keys ((transaction *current-transaction*))
-	    :declarations (declare (optimize (speed 3) (safety 0) (space 0))
-				   (type pointer-void transaction)))
-
-(def-function ("db_txn_commit" %db-txn-commit)
-    ((txn :pointer-void)
-     (flags :unsigned-int))
-  :returning :int)
-
-(wrap-errno (db-transaction-commit %db-txn-commit) (transaction flags)
-	    :keys ((transaction *current-transaction*))
-	    :flags (txn-nosync txn-sync)
-	    :declarations (declare (optimize (speed 3) (safety 0) (space 0))
-				   (type pointer-void transaction)
-				   (type boolean txn-nosync txn-sync)))
-
-(defmacro with-transaction ((&key transaction environment
-				  (parent '*current-transaction*)
-				  (retries 100)
-				  dirty-read txn-nosync
-				  txn-nowait txn-sync)
-			    &body body)
-  (let ((txn (if transaction transaction (gensym)))
-	(count (gensym))
-	(result (gensym))
-	(success (gensym)))
-    `(loop for ,count fixnum from 1 to ,retries
-	   for ,txn of-type pointer-void = 
-	   (db-transaction-begin ,environment
-				 :parent ,parent
-				 :dirty-read ,dirty-read
-				 :txn-nosync ,txn-nosync
-				 :txn-nowait ,txn-nowait
-				 :txn-sync ,txn-sync)
-	   for ,success of-type boolean = nil
-           for ,result = 	 
-           (let ((*current-transaction* ,txn))
-	     (catch ,txn
-	       (unwind-protect
-		    (prog1 (progn , at body)
-		      (setq ,success t)
-		      (db-transaction-commit :transaction ,txn 
-					     :txn-nosync ,txn-nosync
-					     :txn-sync ,txn-sync))
-		 (unless ,success 
-		   (db-transaction-abort :transaction ,txn)))))
-           do
-	   (unless (and (eq ,result ,txn) (not ,success))
-	     (return ,result))
-           finally (error "Too many retries"))))
-
-;; this is code for a non-consing with-transaction. which
-;; doesn't work in the (globally t) case (e.g. setting
-;; *current-transaction*.)
-
-;    #+cmu
-;    `(alien:with-alien ((,txn (* t)
-;			 (%db-txn-begin 
-;			  ,environment ,parent
-;			  (flags :dirty-read ,dirty-read
-;				 :txn-nosync ,txn-nosync
-;				 :txn-nowait ,txn-nowait
-;				 :txn-sync ,txn-sync)
-;			  *errno-buffer*)))
-;      (let ((,success nil)
-;	    ,@(if globally `((*current-transaction* ,txn)) (values)))
-;	(declare (type pointer-void *current-transaction*)
-;		 (dynamic-extent *current-transaction*))
-;	(unwind-protect
-;	     (prog1 (progn , at body)
-;	       (setq ,success t)
-;	       (%db-txn-commit ,txn
-;			       (flags :txn-nosync ,txn-nosync
-;				      :txn-sync ,txn-sync)))
-;	  (unless ,success (%db-txn-abort ,txn)))))))
-	       
-
-;; Locks and timeouts
-
-
-(def-enum DB-LOCKOP ((:DUMP 0) :GET :GET-TIMEOUT :INHERIT 
-		     :PUT :PUT-ALL :PUT-OBJ :PUT-READ
-		     :TIMEOUT :TRADE :UPGRADE-WRITE))
-
-(def-enum DB-LOCKMODE ((:NG 0) :READ :WRITE :WAIT 
-		       :IWRITE :IREAD :IWR :DIRTY :WWRITE))
-
-(def-struct DB-LOCK
-    (off :unsigned-int)
-  (ndx :unsigned-int)
-  (gen :unsigned-int)
-  (mode DB-LOCKMODE))
-
-#+openmcl
-(ccl:def-foreign-type DB-LOCK (:struct DB-LOCK))
-
-(def-struct DB-LOCKREQ
-    (op DB-LOCKOP)
-  (mode DB-LOCKMODE)
-  (timeout :unsigned-int)
-  (obj (:array :char))
-  (lock (* DB-LOCK)))
-
-#+openmcl
-(ccl:def-foreign-type DB-LOCKREQ (:struct DB-LOCKREQ))
-
-(def-function ("db_txn_id" %db-transaction-id)
-    ((transaction :pointer-void))
-  :returning :unsigned-int)
-
-(defun db-transaction-id (&optional (transaction *current-transaction*))
-  (%db-transaction-id transaction))
-
-(def-function ("db_env_lock_id" %db-env-lock-id)
-    ((env :pointer-void)
-     (id :unsigned-int :out))
-  :returning :int)
-
-(wrap-errno db-env-lock-id (env) :outs 2)
-
-
-(def-function ("db_env_lock_id_free" %db-env-lock-id-free)
-    ((env :pointer-void)
-     (id :unsigned-int))
-  :returning :int)
-
-(wrap-errno db-env-lock-id-free (env id))
-
-(def-function ("db_env_lock_get" %db-env-lock-get)
-    ((env :pointer-void)
-     (locker :unsigned-int)
-     (flags :unsigned-int)
-     (object array-or-pointer-char)
-     (object-length :unsigned-int)
-     (lock-mode DB-LOCKMODE)
-     (lock (* DB-LOCK)))
-  :returning :int)
-
-(wrap-errno db-env-lock-get (env locker flags object object-length 
-				 lock-mode lock)
-	    :flags (lock-nowait))
-
-(def-function ("db_env_lock_put" %db-env-lock-put)
-    ((env :pointer-void)
-     (lock (* DB-LOCK)))
-  :returning :int)
-
-(wrap-errno db-env-lock-put (env lock))
-
-(defmacro with-lock ((env locker object object-length 
-			  &key (lock-mode DB-LOCKMODE#WRITE)
-			  lock-nowait)
-		     &body body)
-  (let ((lock (gensym))
-	(locked (gensym)))
-  `(with-foreign-object (,lock 'DB-LOCK)
-    (let ((,locked nil))
-      (unwind-protect
-	   (progn 
-	     (db-env-lock-get ,env ,locker ,object ,object-length ,lock-mode
-			      ,lock :lock-nowait ,lock-nowait)
-	     (setq ,locked T)
-	     , at body)
-	(when ,locked (db-env-lock-put ,env ,lock)))))))      
-     
-(def-function ("db_env_lock_vec" %db-env-lock-vec)
-    ((env :pointer-void)
-     (locker :unsigned-int)
-     (flags :unsigned-int)
-     (list (:array DB-LOCKREQ))
-     (nlist :int)
-     (elistp (* (* DB-LOCKREQ))))
-  :returning :int)
-
-(def-function ("db_env_set_timeout" %db-env-set-timeout)
-    ((env :pointer-void)
-     (timeout :unsigned-int)
-     (flags :unsigned-int))
-  :returning :int)
-
-(wrap-errno db-env-set-timeout (env timeout flags)
-	    :flags (set-lock-timeout set-transaction-timeout))
-
-(def-function ("db_env_get_timeout" %db-env-get-timeout)
-    ((env :pointer-void)
-     (timeout :unsigned-int :out)
-     (flags :unsigned-int))
-  :returning :int)
-
-(wrap-errno db-env-get-timeout (env flags) :outs 2
-	    :flags (set-lock-timeout set-transaction-timeout))
-
-(defconstant DB_LOCK_DEFAULT	     1)
-(defconstant DB_LOCK_EXPIRE	     2)
-(defconstant DB_LOCK_MAXLOCKS        3)
-(defconstant DB_LOCK_MINLOCKS        4)
-(defconstant DB_LOCK_MINWRITE        5)
-(defconstant DB_LOCK_OLDEST	     6)
-(defconstant DB_LOCK_RANDOM	     7)
-(defconstant DB_LOCK_YOUNGEST        8)
-
-(def-function ("db_env_set_lk_detect" %db-env-set-lock-detect)
-    ((env :pointer-void)
-     (detect :unsigned-int))
-  :returning :int)
-
-(wrap-errno db-env-set-lock-detect (env detect))
-
-(def-function ("db_env_get_lk_detect" %db-env-get-lock-detect)
-    ((env :pointer-void)
-     (detect :unsigned-int :out))
-  :returning :int)
-
-(wrap-errno db-env-get-lock-detect (env) :outs 2)
-
-(def-function ("db_env_lock_detect" %db-env-lock-detect)
-    ((env :pointer-void)
-     (flags :unsigned-int)
-     (atype :unsigned-int)
-     (aborted :int :out))
-  :returning :int)
-
-(wrap-errno db-env-lock-detect (env flags atype) :outs 2)
-
-;; Poor man's counters
-
-(def-function ("next_counter" %next-counter)
-    ((env :pointer-void)
-     (db :pointer-void)
-     (parent :pointer-void)
-     (key array-or-pointer-char)
-     (key-length :unsigned-int)
-     (lockid array-or-pointer-char)
-     (lockid-length :unsigned-int))
-  :returning :int)
 
-(defun next-counter (env db parent key key-length lockid lockid-length)
-  (let ((ret (%next-counter env db parent key key-length lockid lockid-length)))
-    (if (< ret 0)
-	(error 'db-error :errno ret)
-	ret)))
-
-;; Misc
-
-(defun flags (&key
-	      auto-commit
-	      joinenv 
-	      init-cdb
-	      init-lock
-	      init-log
-	      init-mpool
-	      init-rep
-	      init-txn
-	      recover
-	      recover-fatal
-	      lockdown
-	      private
-	      system-mem
-	      thread
-	      force
-	      get-both
-	      dirty-read
-	      create
-	      excl
-	      nommap
-	      rdonly
-	      truncate
-	      txn-nosync
-	      txn-nowait
-	      txn-sync
-	      set-lock-timeout
-	      set-transaction-timeout
-	      lock-nowait)
-  (let ((flags 0))
-    (declare (optimize (speed 3) (safety 0) (space 0))
-	     (type (unsigned-byte 32) flags)
-	     (type boolean auto-commit joinenv init-cdb init-lock
-		   init-log init-mpool init-rep init-txn
-		   recover recover-fatal lockdown private
-		   system-mem thread force get-both
-		   dirty-read create excl nommap rdonly
-		   truncate txn-nosync txn-nowait
-		   set-lock-timeout set-transaction-timeout))
-    (when auto-commit (setq flags (logior flags DB_AUTO_COMMIT)))
-    (when joinenv (setq flags (logior flags DB_JOINENV)))
-    (when init-cdb (setq flags (logior flags DB_INIT_CDB)))
-    (when init-lock (setq flags (logior flags DB_INIT_LOCK)))
-    (when init-log (setq flags (logior flags DB_INIT_LOG)))
-    (when init-mpool (setq flags (logior flags DB_INIT_MPOOL)))
-    (when init-rep (setq flags (logior flags DB_INIT_REP)))
-    (when init-txn (setq flags (logior flags DB_INIT_TXN)))
-    (when recover (setq flags (logior flags DB_RECOVER)))
-    (when recover-fatal (setq flags (logior flags DB_RECOVER_FATAL)))
-    (when lockdown (setq flags (logior flags DB_LOCKDOWN)))
-    (when private (setq flags (logior flags DB_PRIVATE)))
-    (when system-mem (setq flags (logior flags DB_SYSTEM_MEM)))
-    (when thread (setq flags (logior flags DB_THREAD)))
-    (when force (setq flags (logior flags DB_FORCE)))
-    (when get-both (setq flags (logior flags DB_GET_BOTH)))
-    (when dirty-read (setq flags (logior flags DB_DIRTY_READ)))
-    (when create (setq flags (logior flags DB_CREATE)))
-    (when excl (setq flags (logior flags DB_EXCL)))
-    (when nommap (setq flags (logior flags DB_NOMMAP)))
-    (when rdonly (setq flags (logior flags DB_RDONLY)))
-    (when truncate (setq flags (logior flags DB_TRUNCATE)))
-    (when txn-nosync (setq flags (logior flags DB_TXN_NOSYNC)))
-    (when txn-nowait (setq flags (logior flags DB_TXN_NOWAIT)))
-    (when txn-sync (setq flags (logior flags DB_TXN_SYNC)))
-    (when set-lock-timeout (setq flags (logior flags DB_SET_LOCK_TIMEOUT)))
-    (when set-transaction-timeout (setq flags (logior flags DB_SET_TXN_TIMEOUT)))
-    (when lock-nowait (setq flags (logior flags DB_LOCK_NOWAIT)))
-    flags))
-
-;; Errors
-
-(def-function ("db_strerr" %db-strerror)
-    ((error :int))
-  :returning :cstring)
-
-(defun db-strerror (errno)
-  (convert-from-cstring (%db-strerror errno)))
-
-(define-condition db-error (error) 
-  ((errno :type fixnum :initarg :errno :reader db-error-errno))
-  (:report
-   (lambda (condition stream)
-     (declare (type db-error condition) (type stream stream))
-     (format stream "Berkeley DB error: ~A"
-	     (db-strerror (db-error-errno condition))))))





More information about the Elephant-cvs mailing list