Update of /project/elephant/cvsroot/elephant/src/contrib/eslick/db-lisp
In directory clnet:/tmp/cvs-serv8664/src/contrib/eslick/db-lisp
Added Files:
TODO binary-data.lisp binary-types.lisp btree.lisp
buffers.lisp file.lisp lisp-types.lisp octet-stream.lisp
package.lisp
Log Message:
Cleaning up source directory, moving partial projects to contrib
--- /project/elephant/cvsroot/elephant/src/contrib/eslick/db-lisp/TODO 2007/02/04 10:17:21 NONE
+++ /project/elephant/cvsroot/elephant/src/contrib/eslick/db-lisp/TODO 2007/02/04 10:17:21 1.1
A lisp backend will need:
- read/write binary sequences
- move/cache binary pages to/from disk
- locking of structures/pages for multi-threaded use
- transaction logging (context + primitives)
- checkpointing, backup and log removal
Want to build other data structures on this basic substrate:
- large sets / persistent arrays
- linear records
- inverse index
Can we store odd types?
- Classes?
- Closures?
- Functions?
(probably only with source access)
Some constraints:
- A binary file will allow for multiple internal btrees, will lock the root indices
- Want to enable multiple possible allocation, layout and update algorithms; so each
file should describe its type?
- Would like to allow multiple processes to have open file handles
Perhaps have a per-thread file handle?
- Mixing types is possible if based on underlying page size, but performance will suffer
;; read/write fields in an array
;; parse/unparse from a stream or array
;; instantiate a lisp version of the binary type
;; associate a type with an array
;; need binary versions of native lisp types
;; (defgeneric parse-binary-value (type in &key)
;; (:documentation "Read a binary value from an array or stream"))
;; (defmethod parse-binary-value ((type (eql 'u8)) (in stream) &key)
;; (read-byte in))
;; (defmethod parse-binary-value ((type (eql 'u8)) (in array) &key (offset 0))
;; (declare (type fixnum offset))
;; (assert (subtypep (type-of in) '(array (unsigned 8) *)))
;; (aref in offset))
;; (defmethod unparse-binary-value ((type (eql 'u8)) (out stream) (value (unsigned 8)) &key)
;; (write-byte value out))
;; (defmethod unparse-binary-value ((type (eql 'u8)) (out array) (value (unsigned 8)) &key (offset 0))
;; (--- /project/elephant/cvsroot/elephant/src/contrib/eslick/db-lisp/binary-data.lisp 2007/02/04 10:17:21 NONE
+++ /project/elephant/cvsroot/elephant/src/contrib/eslick/db-lisp/binary-data.lisp 2007/02/04 10:17:21 1.1
(in-package :db-lisp)
;;
;; Macros
;;
(defmacro with-gensyms ((&rest names) &body body)
`(let ,(loop for n in names collect `(,n (make-symbol ,(string n))))
,@body))
;;
;; Binary types
;;
;; NOTE: Needs to be made MP safe
(defvar *in-progress-objects* nil)
(defconstant +null+ (code-char 0))
(defgeneric read-value (type stream &key)
(:documentation "Read a value of the given type from the stream."))
(defgeneric write-value (type stream value &key)
(:documentation "Write a value as the given type to the stream."))
(defgeneric read-object (object stream)
(:method-combination progn :most-specific-last)
(:documentation "Fill in the slots of object from stream."))
(defgeneric write-object (object stream)
(:method-combination progn :most-specific-last)
(:documentation "Write out the slots of object to the stream."))
;; These may not be needed; design your compound objects so that
;; you can read offsets and parse compound objects
;;(defgeneric read-field-value (type stream &optional base-pos)
;; (:documentation "Index directly to a subfield of a complex type to read
;; from a random underlying stream"))
;;
;;(defgeneric write-field-value (type stream value &optional base-pos)
;; (:documentation "Write an object directly to the subfield of a complex
;; type in the provided field"))
;;
;; Defaults for read-value of binary-object types
(defmethod read-value ((type symbol) stream &key)
(let ((object (make-instance type)))
(read-object object stream)
object))
(defmethod write-value ((type symbol) stream value &key)
(assert (typep value type))
(write-object value stream))
(defun read-value-at (type stream pos)
"Ensure a stream is at a particular offset before reading"
(file-position stream pos)
(read-value type stream))
(defun write-value-at (type stream pos value)
"Ensure a stream is at a particular offset before writing"
(file-position stream pos)
(write-value type stream value))
;;; Binary types
(defmacro define-binary-type (name (&rest args) &body spec)
(with-gensyms (type stream value)
`(progn
(defmethod read-value ((,type (eql ',name)) ,stream &key ,@args)
(declare (ignorable ,@args))
,(type-reader-body spec stream))
(defmethod write-value ((,type (eql ',name)) ,stream ,value &key ,@args)
(declare (ignorable ,@args))
,(type-writer-body spec stream value)))))
(defun type-reader-body (spec stream)
(ecase (length spec)
(1 (destructuring-bind (type &rest args) (mklist (first spec))
`(read-value ',type ,stream ,@args)))
(2 (destructuring-bind ((in) &body body) (cdr (assoc :reader spec))
`(let ((,in ,stream)) ,@body)))))
(defun type-writer-body (spec stream value)
(ecase (length spec)
(1 (destructuring-bind (type &rest args) (mklist (first spec))
`(write-value ',type ,stream ,value ,@args)))
(2 (destructuring-bind ((out v) &body body) (cdr (assoc :writer spec))
`(let ((,out ,stream) (,v ,value)) ,@body)))))
;;; Binary classes
(defmacro define-generic-binary-class (name (&rest superclasses) slots read-method)
(with-gensyms (objectvar streamvar)
`(progn
(eval-when (:compile-toplevel :load-toplevel :execute)
(setf (get ',name 'slots) ',(mapcar #'first slots))
(setf (get ',name 'superclasses) ',superclasses))
(defclass ,name ,superclasses
,(mapcar #'slot->defclass-slot slots))
,read-method
(defmethod write-object progn ((,objectvar ,name) ,streamvar)
(declare (ignorable ,streamvar))
(with-slots ,(new-class-all-slots slots superclasses) ,objectvar
,@(mapcar #'(lambda (x) (slot->write-value x streamvar)) slots))))))
(defmacro define-binary-class (name (&rest superclasses) slots)
(with-gensyms (objectvar streamvar)
`(define-generic-binary-class ,name ,superclasses ,slots
(defmethod read-object progn ((,objectvar ,name) ,streamvar)
(declare (ignorable ,streamvar))
(with-slots ,(new-class-all-slots slots superclasses) ,objectvar
,@(mapcar #'(lambda (x) (slot->read-value x streamvar)) slots))))))
(defmacro define-tagged-binary-class (name (&rest superclasses) slots &rest options)
(with-gensyms (typevar objectvar streamvar)
`(define-generic-binary-class ,name ,superclasses ,slots
(defmethod read-value ((,typevar (eql ',name)) ,streamvar &key)
(let* ,(mapcar #'(lambda (x) (slot->binding x streamvar)) slots)
(let ((,objectvar
(make-instance
,@(or (cdr (assoc :dispatch options))
(error "Must supply :disptach form."))
,@(mapcan #'slot->keyword-arg slots))))
(read-object ,objectvar ,streamvar)
,objectvar))))))
(defun as-keyword (sym) (intern (string sym) :keyword))
(defun normalize-slot-spec (spec)
(list (first spec) (mklist (second spec))))
(defun mklist (x) (if (listp x) x (list x)))
(defun slot->defclass-slot (spec)
(let ((name (first spec)))
`(,name :initarg ,(as-keyword name) :accessor ,name)))
(defun slot->read-value (spec stream)
(destructuring-bind (name (type &rest args)) (normalize-slot-spec spec)
`(setf ,name (read-value ',type ,stream ,@args))))
(defun slot->write-value (spec stream)
(destructuring-bind (name (type &rest args)) (normalize-slot-spec spec)
`(write-value ',type ,stream ,name ,@args)))
(defun slot->binding (spec stream)
(destructuring-bind (name (type &rest args)) (normalize-slot-spec spec)
`(,name (read-value ',type ,stream ,@args))))
(defun slot->keyword-arg (spec)
(let ((name (first spec)))
`(,(as-keyword name) ,name)))
;;; Keeping track of inherited slots
(defun direct-slots (name)
(copy-list (get name 'slots)))
(defun inherited-slots (name)
(loop for super in (get name 'superclasses)
nconc (direct-slots super)
nconc (inherited-slots super)))
(defun all-slots (name)
(nconc (direct-slots name) (inherited-slots name)))
(defun new-class-all-slots (slots superclasses)
"Like all slots but works while compiling a new class before slots
and superclasses have been saved."
(nconc (mapcan #'all-slots superclasses) (mapcar #'first slots)))
;;; In progress Object stack
(defun current-binary-object ()
(first *in-progress-objects*))
(defun parent-of-type (type)
(find-if #'(lambda (x) (typep x type)) *in-progress-objects*))
(defmethod read-object :around (object stream)
(declare (ignore stream))
(let ((*in-progress-objects* (cons object *in-progress-objects*)))
(call-next-method)))
(defmethod write-object :around (object stream)
(declare (ignore stream))
(let ((*in-progress-objects* (cons object *in-progress-objects*)))
(call-next-method)))
;; Copyright (c) 2005, Peter Seibel All rights reserved.
;; Redistribution and use in source and binary forms, with or without
;; modification, are permitted provided that the following conditions are
;; met:
;; * Redistributions of source code must retain the above copyright
;; notice, this list of conditions and the following disclaimer.
;; * 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.
;; * Neither the name of the Peter Seibel nor the names of its
;; contributors may be used to endorse or promote products derived
;; from this software without specific prior written permission.
;; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
;; "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 THE COPYRIGHT
;; OWNER 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.
--- /project/elephant/cvsroot/elephant/src/contrib/eslick/db-lisp/binary-types.lisp 2007/02/04 10:17:21 NONE
+++ /project/elephant/cvsroot/elephant/src/contrib/eslick/db-lisp/binary-types.lisp 2007/02/04 10:17:21 1.1
(in-package :db-lisp)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; A few basic types
(define-binary-type unsigned-integer (bytes)
(:reader (in)
(loop with value = 0
for shift downfrom (* bytes 8) to 0 by 8 do
(setf value (logior (ash (read-byte in) shift) value))
finally (return value)))
(:writer (out value)
(loop for shift downfrom (* bytes 8) to 0 by 8 do
(write-byte (logand (ash value (- shift)) #xFF) out))))
(define-binary-type unsigned-integer-cplx (bytes bits-per-byte)
(:reader (in)
(loop with value = 0
for low-bit downfrom (* bits-per-byte (1- bytes)) to 0 by bits-per-byte do
(setf (ldb (byte bits-per-byte low-bit) value) (read-byte in))
finally (return value)))
(:writer (out value)
(loop for low-bit downfrom (* bits-per-byte (1- bytes)) to 0 by bits-per-byte
do (write-byte (ldb (byte bits-per-byte low-bit) value) out))))
(define-binary-type u8 () (unsigned-integer :bytes 1))
(define-binary-type u16 () (unsigned-integer :bytes 2))
(define-binary-type u24 () (unsigned-integer :bytes 3))
(define-binary-type u32 () (unsigned-integer :bytes 4))
(define-binary-type u64 () (unsigned-integer :bytes 8))
;;; Strings
(define-binary-type generic-string (length character-type)
(:reader (in)
(let ((string (make-string length)))
(dotimes (i length)
(setf (char string i) (read-value character-type in)))
string))
(:writer (out string)
(dotimes (i length)
(write-value character-type out (char string i)))))
(define-binary-type generic-terminated-string (terminator character-type)
(:reader (in)
(with-output-to-string (s)
(loop for char = (read-value character-type in)
until (char= char terminator) do (write-char char s))))
(:writer (out string)
(loop for char across string
do (write-value character-type out char)
finally (write-value character-type out terminator))))
;;; ISO-8859-1 strings
(define-binary-type iso-8859-1-char ()
(:reader (in)
(let ((code (read-byte in)))
(or (code-char code)
(error "Character code ~d not supported" code))))
(:writer (out char)
(let ((code (char-code char)))
(if (<= 0 code #xff)
(write-byte code out)
(error "Illegal character for iso-8859-1 encoding: character: ~c with code: ~d" char code)))))
(define-binary-type iso-8859-1-string (length)
(generic-string :length length :character-type 'iso-8859-1-char))
(define-binary-type iso-8859-1-terminated-string (terminator)
(generic-terminated-string :terminator terminator :character-type 'iso-8859-1-char))
;;; UCS-2 (Unicode) strings (i.e. UTF-16 without surrogate pairs, phew.)
;;; Define a binary type for reading a UCS-2 character relative to a
;;; particular byte ordering as indicated by the BOM value.
;; v2.3 specifies that the BOM should be present. v2.2 is silent
;; though it is arguably inherent in the definition of UCS-2) Length
;; is in bytes. On the write side, since we don't have any way of
;; knowing what BOM was used to read the string we just pick one.
;; This does mean roundtrip transparency could be broken.
(define-binary-type ucs-2-char (swap)
(:reader (in)
(let ((code (read-value 'u2 in)))
(when swap (setf code (swap-bytes code)))
(or (code-char code) (error "Character code ~d not supported" code))))
(:writer (out char)
(let ((code (char-code char)))
(unless (<= 0 code #xffff)
(error "Illegal character for ucs-2 encoding: ~c with char-code: ~d" char code))
(when swap (setf code (swap-bytes code)))
(write-value 'u2 out code))))
(defun swap-bytes (code)
(assert (<= code #xffff))
(rotatef (ldb (byte 8 0) code) (ldb (byte 8 8) code))
code)
(define-binary-type ucs-2-char-big-endian () (ucs-2-char :swap nil))
(define-binary-type ucs-2-char-little-endian () (ucs-2-char :swap t))
(defun ucs-2-char-type (byte-order-mark)
(ecase byte-order-mark
(#xfeff 'ucs-2-char-big-endian)
(#xfffe 'ucs-2-char-little-endian)))
(define-binary-type ucs-2-string (length)
(:reader (in)
(let ((byte-order-mark (read-value 'u2 in))
(characters (1- (/ length 2))))
(read-value
'generic-string in
:length characters
:character-type (ucs-2-char-type byte-order-mark))))
(:writer (out string)
(write-value 'u2 out #xfeff)
(write-value
'generic-string out string
:length (length string)
:character-type (ucs-2-char-type #xfeff))))
[43 lines skipped]
--- /project/elephant/cvsroot/elephant/src/contrib/eslick/db-lisp/btree.lisp 2007/02/04 10:17:21 NONE
+++ /project/elephant/cvsroot/elephant/src/contrib/eslick/db-lisp/btree.lisp 2007/02/04 10:17:21 1.1
[45 lines skipped]
--- /project/elephant/cvsroot/elephant/src/contrib/eslick/db-lisp/buffers.lisp 2007/02/04 10:17:21 NONE
+++ /project/elephant/cvsroot/elephant/src/contrib/eslick/db-lisp/buffers.lisp 2007/02/04 10:17:21 1.1
[209 lines skipped]
--- /project/elephant/cvsroot/elephant/src/contrib/eslick/db-lisp/file.lisp 2007/02/04 10:17:21 NONE
+++ /project/elephant/cvsroot/elephant/src/contrib/eslick/db-lisp/file.lisp 2007/02/04 10:17:21 1.1
[227 lines skipped]
--- /project/elephant/cvsroot/elephant/src/contrib/eslick/db-lisp/lisp-types.lisp 2007/02/04 10:17:21 NONE
+++ /project/elephant/cvsroot/elephant/src/contrib/eslick/db-lisp/lisp-types.lisp 2007/02/04 10:17:21 1.1
[242 lines skipped]
--- /project/elephant/cvsroot/elephant/src/contrib/eslick/db-lisp/octet-stream.lisp 2007/02/04 10:17:21 NONE
+++ /project/elephant/cvsroot/elephant/src/contrib/eslick/db-lisp/octet-stream.lisp 2007/02/04 10:17:21 1.1
[483 lines skipped]
--- /project/elephant/cvsroot/elephant/src/contrib/eslick/db-lisp/package.lisp 2007/02/04 10:17:21 NONE
+++ /project/elephant/cvsroot/elephant/src/contrib/eslick/db-lisp/package.lisp 2007/02/04 10:17:21 1.1
[555 lines skipped]