annotate bindings/sb-alien/interface.lisp @ 511:3141e51cb077

Initial checkin of WIP sb-alien bindings. Includes some analogues to libtests/ test files in tests.lisp Coverage of the C API is not complete, but I think there's enough to implement all the same functionality tests as there are in the libtests/ suite.
author mas01cr
date Wed, 21 Jan 2009 21:48:25 +0000
parents
children 633614461994
rev   line source
mas01cr@511 1 (in-package "SB-ADB")
mas01cr@511 2
mas01cr@511 3 (defclass adb ()
mas01cr@511 4 ((alien :initarg alien)))
mas01cr@511 5 (defmethod initialize-instance :after ((o adb) &key)
mas01cr@511 6 (when (and (slot-boundp o 'alien)
mas01cr@511 7 (not (null-alien (slot-value o 'alien))))
mas01cr@511 8 (let ((alien (slot-value o 'alien)))
mas01cr@511 9 (sb-ext:finalize o (lambda () (%close alien))))))
mas01cr@511 10
mas01cr@511 11 ;;; FIXME: deal with interrupt-safety / leak issues
mas01cr@511 12
mas01cr@511 13 ;;; FIXME: if-does-not-exist.
mas01cr@511 14 (defun open (path &key (direction :input) (if-exists :error) (adb-class 'adb))
mas01cr@511 15 (flet ((direction-flag (direction)
mas01cr@511 16 (ecase direction
mas01cr@511 17 ((:input :probe) sb-posix:o-rdonly)
mas01cr@511 18 ((:output :io) sb-posix:o-rdwr))))
mas01cr@511 19 (let* ((truepath (probe-file path))
mas01cr@511 20 (alien (cond
mas01cr@511 21 (truepath
mas01cr@511 22 (ecase direction
mas01cr@511 23 ((:input :probe)
mas01cr@511 24 (%%open path (direction-flag direction)))
mas01cr@511 25 ((:output :io)
mas01cr@511 26 (case if-exists
mas01cr@511 27 (:error (error "database already exists: ~S" path))
mas01cr@511 28 (:append (%open path (direction-flag direction)))
mas01cr@511 29 ;; FIXME: not the best implementation of
mas01cr@511 30 ;; :SUPERSEDE semantics ever.
mas01cr@511 31 (:supersede (delete-file path) (%create path 0 0 0))))))
mas01cr@511 32 ((eql direction :input)
mas01cr@511 33 (error "database does not exist: ~S" path))
mas01cr@511 34 (t (%create path 0 0 0)))))
mas01cr@511 35 (cond
mas01cr@511 36 ((null-alien alien)
mas01cr@511 37 (case direction
mas01cr@511 38 (:probe nil)
mas01cr@511 39 (t (error "opening database failed: ~S" path))))
mas01cr@511 40 (t (make-instance adb-class 'alien alien))))))
mas01cr@511 41 (defmethod close ((o adb))
mas01cr@511 42 (when (and (slot-boundp o 'alien)
mas01cr@511 43 (not (null-alien (slot-value o 'alien))))
mas01cr@511 44 (%close (slot-value o 'alien))
mas01cr@511 45 (sb-ext:cancel-finalization o)
mas01cr@511 46 (slot-makunbound o 'alien)))
mas01cr@511 47 (defmacro with-adb ((adb path &rest open-args &key direction adb-class if-exists)
mas01cr@511 48 &body body)
mas01cr@511 49 (declare (ignore direction adb-class if-exists))
mas01cr@511 50 `(let ((,adb (open ,path ,@open-args)))
mas01cr@511 51 (unwind-protect
mas01cr@511 52 (locally ,@body)
mas01cr@511 53 (close ,adb))))
mas01cr@511 54
mas01cr@511 55 (defgeneric l2norm (db))
mas01cr@511 56 (defmethod l2norm ((db adb))
mas01cr@511 57 (%l2norm (slot-value db 'alien)))
mas01cr@511 58
mas01cr@511 59 (defstruct (datum
mas01cr@511 60 (:constructor %make-datum)
mas01cr@511 61 (:constructor
mas01cr@511 62 make-datum
mas01cr@511 63 (key %data &key times power
mas01cr@511 64 &aux (data
mas01cr@511 65 (make-array (list (length %data) (length (elt %data 0)))
mas01cr@511 66 :element-type 'double-float
mas01cr@511 67 :initial-contents %data)))))
mas01cr@511 68 (key (error "missing argument") :type string)
mas01cr@511 69 (data (error "missing argument") :type (simple-array double-float (* *)))
mas01cr@511 70 (times nil :type (or null (simple-array double-float)))
mas01cr@511 71 (power nil :type (or null (simple-array double-float))))
mas01cr@511 72
mas01cr@511 73 (defgeneric insert (datum db))
mas01cr@511 74
mas01cr@511 75 (defmethod insert ((datum datum) (db adb))
mas01cr@511 76 (let* ((data (datum-data datum))
mas01cr@511 77 (nvectors (array-dimension data 0))
mas01cr@511 78 (dim (array-dimension data 1)))
mas01cr@511 79 (when (datum-times datum)
mas01cr@511 80 (unless (= (array-total-size (datum-times datum)) (* 2 nvectors))
mas01cr@511 81 (error "dimension mismatch for times: ~S" datum)))
mas01cr@511 82 (when (datum-power datum)
mas01cr@511 83 (unless (= (array-total-size (datum-power datum)) nvectors)
mas01cr@511 84 (error "dimension mismatch for power: ~S" datum)))
mas01cr@511 85 (with-alien ((d adb-datum-t))
mas01cr@511 86 (sb-sys:with-pinned-objects ((datum-data datum)
mas01cr@511 87 (datum-times datum)
mas01cr@511 88 (datum-power datum))
mas01cr@511 89 (setf (slot d 'dim) dim)
mas01cr@511 90 (setf (slot d 'nvectors) nvectors)
mas01cr@511 91 (setf (slot d 'key) (datum-key datum))
mas01cr@511 92 (setf (slot d 'data)
mas01cr@511 93 (sb-sys:vector-sap (sb-ext:array-storage-vector (datum-data datum))))
mas01cr@511 94 (if (datum-times datum)
mas01cr@511 95 (setf (slot d 'times) (sb-sys:vector-sap (sb-ext:array-storage-vector (datum-times datum))))
mas01cr@511 96 (setf (slot d 'times) nil))
mas01cr@511 97 (if (datum-power datum)
mas01cr@511 98 (setf (slot d 'power) (sb-sys:vector-sap (sb-ext:array-storage-vector (datum-times datum))))
mas01cr@511 99 (setf (slot d 'power) nil))
mas01cr@511 100 (sb-int:with-float-traps-masked (:invalid)
mas01cr@511 101 (%insert-datum (slot-value db 'alien) (addr d)))))))
mas01cr@511 102
mas01cr@511 103 (defstruct result
mas01cr@511 104 (key "" :type string)
mas01cr@511 105 (distance 0d0 :type double-float)
mas01cr@511 106 (qpos 0 :type (and unsigned-byte fixnum))
mas01cr@511 107 (ipos 0 :type (and unsigned-byte fixnum)))
mas01cr@511 108
mas01cr@511 109 ;;; Hrm. To copy (from the malloc heap) or not to copy? Copying
mas01cr@511 110 ;;; would make things generally easier, I guess, and we have to hope
mas01cr@511 111 ;;; that the order of magnitude is not such that the copying causes
mas01cr@511 112 ;;; pain.
mas01cr@511 113 (defclass results (sequence standard-object)
mas01cr@511 114 ())
mas01cr@511 115 (defclass copied-query-results (results)
mas01cr@511 116 ((results :initarg results :accessor %copied-results)))
mas01cr@511 117 (defmethod print-object ((o copied-query-results) s)
mas01cr@511 118 (pprint-logical-block (s nil)
mas01cr@511 119 (print-unreadable-object (o s :type t)
mas01cr@511 120 (format s "(~D results):~2I~@:_" (length o))
mas01cr@511 121 (sequence:dosequence (r o)
mas01cr@511 122 (pprint-pop)
mas01cr@511 123 (format s "~A ~6,3e ~D ~D~@:_"
mas01cr@511 124 (result-key r) (result-distance r)
mas01cr@511 125 (result-qpos r) (result-ipos r))))))
mas01cr@511 126
mas01cr@511 127 (defmethod sequence:length ((o copied-query-results))
mas01cr@511 128 (length (%copied-results o)))
mas01cr@511 129 (defmethod sequence:elt ((o copied-query-results) index)
mas01cr@511 130 (elt (%copied-results o) index))
mas01cr@511 131 (defmethod (setf sequence:elt) (new-value (o copied-query-results) index)
mas01cr@511 132 (setf (elt (%copied-results o) index) new-value))
mas01cr@511 133 (defmethod sequence:make-sequence-like
mas01cr@511 134 ((o copied-query-results) length &rest args
mas01cr@511 135 &key initial-element initial-contents)
mas01cr@511 136 (declare (ignore initial-element initial-contents))
mas01cr@511 137 (let ((vector (apply #'make-array length args)))
mas01cr@511 138 (make-instance 'copied-query-results 'results vector)))
mas01cr@511 139 (defmethod sequence:adjust-sequence
mas01cr@511 140 ((o copied-query-results) length &rest args
mas01cr@511 141 &key initial-element initial-contents)
mas01cr@511 142 (declare (ignore initial-element initial-contents))
mas01cr@511 143 (let ((results (%copied-results o)))
mas01cr@511 144 (apply #'sequence:adjust-sequence results length args))
mas01cr@511 145 o)
mas01cr@511 146
mas01cr@511 147 (defclass proxied-query-results (results)
mas01cr@511 148 ((adb :initarg adb)
mas01cr@511 149 (spec :initarg spec)
mas01cr@511 150 (results :initarg results)))
mas01cr@511 151 (defmethod initialize-instance :after ((o proxied-query-results) &key)
mas01cr@511 152 (when (and (slot-boundp o 'results)
mas01cr@511 153 (not (null-alien o))
mas01cr@511 154 (slot-boundp o 'spec))
mas01cr@511 155 (with-slots (results spec adb) o
mas01cr@511 156 (flet ((results-finalizer ()
mas01cr@511 157 (with-slots (alien) adb
mas01cr@511 158 (%free-query-results alien spec results))))
mas01cr@511 159 (sb-ext:finalize o #'results-finalizer)))))
mas01cr@511 160
mas01cr@511 161 (defgeneric query (datum db &key))
mas01cr@511 162
mas01cr@511 163 ;;; FIXME: I don't like this way of generalizing the boilerplate;
mas01cr@511 164 ;;; isn't there a nice functional way of doing this?
mas01cr@511 165 (macrolet
mas01cr@511 166 ((def (name datum-class &body qdatum-forms)
mas01cr@511 167 `(defmethod ,name ((datum ,datum-class) (db adb) &key
mas01cr@511 168 (sequence-length 1) (sequence-start 0)
mas01cr@511 169 exhaustivep accumulation distance
mas01cr@511 170 ;; FIXME: dubious historical defaults
mas01cr@511 171 (npoints 10) (ntracks 10)
mas01cr@511 172
mas01cr@511 173 (radius nil radiusp)
mas01cr@511 174 (include-keys nil include-keys-p)
mas01cr@511 175 (exclude-keys nil exclude-keys-p))
mas01cr@511 176 (unless (slot-boundp db 'alien)
mas01cr@511 177 (error "database ~S is closed" db))
mas01cr@511 178 (with-alien ((qid adb-query-id-t)
mas01cr@511 179 (qparams adb-query-parameters-t)
mas01cr@511 180 (qrefine adb-query-refine-t)
mas01cr@511 181 (qdatum adb-datum-t))
mas01cr@511 182 ,@qdatum-forms
mas01cr@511 183 (setf (slot qid 'datum) (addr qdatum))
mas01cr@511 184 (setf (slot qid 'sequence-length) sequence-length)
mas01cr@511 185 (setf (slot qid 'sequence-start) sequence-start)
mas01cr@511 186 (setf (slot qid 'flags) (if exhaustivep 1 0))
mas01cr@511 187
mas01cr@511 188 (setf (slot qparams 'accumulation)
mas01cr@511 189 (ecase accumulation
mas01cr@511 190 (:db 1)
mas01cr@511 191 (:per-track 2)
mas01cr@511 192 (:one-to-one 3)))
mas01cr@511 193 (setf (slot qparams 'distance)
mas01cr@511 194 (ecase distance
mas01cr@511 195 (:dot-product 1)
mas01cr@511 196 (:euclidean-normed 2)
mas01cr@511 197 (:euclidean 3)))
mas01cr@511 198 (setf (slot qparams 'npoints) (or npoints 0))
mas01cr@511 199 (setf (slot qparams 'ntracks) (or ntracks 0))
mas01cr@511 200
mas01cr@511 201 (let ((refine-flags 0))
mas01cr@511 202 (when radiusp
mas01cr@511 203 (setf refine-flags (logior refine-flags 4))
mas01cr@511 204 (setf (slot qrefine 'radius) (float radius 0d0)))
mas01cr@511 205 ;; FIXME: the freeing of the KEYS slot in these
mas01cr@511 206 ;; include/exclude keylists isn't interrupt-safe.
mas01cr@511 207 ;;
mas01cr@511 208 ;; FIXME: think quite hard about the behaviour of this
mas01cr@511 209 ;; when LENGTH is 0.
mas01cr@511 210 (when include-keys-p
mas01cr@511 211 (setf refine-flags (logior refine-flags 1))
mas01cr@511 212 (let ((length (length include-keys)))
mas01cr@511 213 (setf (slot (slot qrefine 'include) 'nkeys) length)
mas01cr@511 214 (let ((keys (make-alien c-string length)))
mas01cr@511 215 (setf (slot (slot qrefine 'include) 'keys) keys)
mas01cr@511 216 (loop for key being the elements of include-keys
mas01cr@511 217 for i upfrom 0
mas01cr@511 218 do (setf (deref keys i) key)))))
mas01cr@511 219 (when exclude-keys-p
mas01cr@511 220 (setf refine-flags (logior refine-flags 2))
mas01cr@511 221 (let ((length (length exclude-keys)))
mas01cr@511 222 (setf (slot (slot qrefine 'exclude) 'nkeys) length)
mas01cr@511 223 (let ((keys (make-alien c-string length)))
mas01cr@511 224 (setf (slot (slot qrefine 'exclude) 'keys) keys)
mas01cr@511 225 (loop for key being the elements of exclude-keys
mas01cr@511 226 for i upfrom 0
mas01cr@511 227 do (setf (deref keys i) key)))))
mas01cr@511 228 (setf (slot qrefine 'flags) refine-flags))
mas01cr@511 229 (setf (slot qrefine 'hopsize) 1)
mas01cr@511 230
mas01cr@511 231 ;; FIXME: hm, this possibly suggests that there's something
mas01cr@511 232 ;; a bit wrong with the C audioDB interface. The API
mas01cr@511 233 ;; currently exposed effectively requires either that all
mas01cr@511 234 ;; the processing of query results happens in the same
mas01cr@511 235 ;; dynamic extent as the call to audiodb_query_spec(), or
mas01cr@511 236 ;; that the adb_query_spec_t object is allocated on the
mas01cr@511 237 ;; heap. We need to think harder about whether the spec
mas01cr@511 238 ;; argument is really required (I think it probably isn't).
mas01cr@511 239 ;;
mas01cr@511 240 ;; meanwhile, here we're using it with dynamic extent anyway, so
mas01cr@511 241 ;; we could put it right back on the stack.
mas01cr@511 242 (let ((qspec (make-alien adb-query-spec-t)))
mas01cr@511 243 (unwind-protect
mas01cr@511 244 (progn
mas01cr@511 245 (setf (slot qspec 'qid) qid)
mas01cr@511 246 (setf (slot qspec 'params) qparams)
mas01cr@511 247 (setf (slot qspec 'refine) qrefine)
mas01cr@511 248
mas01cr@511 249 (let ((results
mas01cr@511 250 (sb-int:with-float-traps-masked (:invalid)
mas01cr@511 251 (%query (slot-value db 'alien) qspec))))
mas01cr@511 252 (flet ((collect-copied-results ()
mas01cr@511 253 (let ((nresults (slot results 'nresults))
mas01cr@511 254 (cresults (slot results 'results)))
mas01cr@511 255 (coerce
mas01cr@511 256 (loop for i below nresults
mas01cr@511 257 for r = (deref cresults i)
mas01cr@511 258 collect (make-result
mas01cr@511 259 :key (slot r 'key)
mas01cr@511 260 :distance (slot r 'dist)
mas01cr@511 261 :qpos (slot r 'qpos)
mas01cr@511 262 :ipos (slot r 'ipos)))
mas01cr@511 263 'vector))))
mas01cr@511 264 (unwind-protect
mas01cr@511 265 (make-instance 'copied-query-results
mas01cr@511 266 'results (collect-copied-results))
mas01cr@511 267 (%free-query-results (slot-value db 'alien) qspec results)))))
mas01cr@511 268 (when (logbitp 0 (slot (slot qspec 'refine) 'flags))
mas01cr@511 269 (free-alien (slot (slot (slot qspec 'refine) 'include) 'keys)))
mas01cr@511 270 (when (logbitp 1 (slot (slot qspec 'refine) 'flags))
mas01cr@511 271 (free-alien (slot (slot (slot qspec 'refine) 'exclude) 'keys)))
mas01cr@511 272 (free-alien qspec)))))))
mas01cr@511 273 (def query string (setf (slot qdatum 'key) datum))
mas01cr@511 274 (def query datum
mas01cr@511 275 (setf (slot qdatum 'key) (datum-key datum))
mas01cr@511 276 (setf (slot qdatum 'dim) (array-dimension (datum-data datum) 1))
mas01cr@511 277 (setf (slot qdatum 'nvectors) (array-dimension (datum-data datum) 0))
mas01cr@511 278 (setf (slot qdatum 'data) (sb-sys:vector-sap (sb-ext:array-storage-vector (datum-data datum))))
mas01cr@511 279 (if (datum-times datum)
mas01cr@511 280 (setf (slot qdatum 'times) (sb-sys:vector-sap (sb-ext:array-storage-vector (datum-times datum))))
mas01cr@511 281 (setf (slot qdatum 'times) nil))
mas01cr@511 282 (if (datum-power datum)
mas01cr@511 283 (setf (slot qdatum 'power) (sb-sys:vector-sap (sb-ext:array-storage-vector (datum-times datum))))
mas01cr@511 284 (setf (slot qdatum 'power) nil))))
mas01cr@511 285
mas01cr@511 286 #+test
mas01cr@511 287 (sb-adb:with-adb (db "/home/csr21/tmp/omras2-workshop/9.adb")
mas01cr@511 288 (sb-adb:query "KSA_CHARM_337" db :exhaustivep t :sequence-length 30
mas01cr@511 289 :accumulation :per-track :distance :euclidean :npoints 1 :ntracks 20))
mas01cr@511 290
mas01cr@511 291 #+test
mas01cr@511 292 (sb-adb:with-adb (db "/home/csr21/tmp/omras2-workshop/9.adb")
mas01cr@511 293 (sb-adb:query "KSA_CHARM_337" db :sequence-start 20 :sequence-length 20
mas01cr@511 294 :accumulation :per-track :distance :euclidean-normed
mas01cr@511 295 :npoints 10 :ntracks 1))
mas01cr@511 296
mas01cr@511 297 #+test
mas01cr@511 298 (sb-adb:with-adb (db "/home/csr21/tmp/omras2-workshop/9.adb")
mas01cr@511 299 (sb-adb:query "KSA_CHARM_337" db
mas01cr@511 300 :exhaustivep t :sequence-length 30
mas01cr@511 301 :accumulation :per-track :distance :euclidean-normed
mas01cr@511 302 :npoints 2 :ntracks 10))
mas01cr@511 303
mas01cr@511 304 ;;; only hacks and tests below
mas01cr@511 305 #|
mas01cr@511 306 (defun foo ()
mas01cr@511 307 (let ((db (%open "/home/csr21/tmp/omras2-workshop/9.adb" sb-posix:o-rdonly)))
mas01cr@511 308 (unless (null-alien db)
mas01cr@511 309 (unwind-protect
mas01cr@511 310 (with-alien ((status adb-status-t))
mas01cr@511 311 (%status db (addr status))
mas01cr@511 312 (print (list (slot status 'dim) (slot status 'nfiles))))
mas01cr@511 313 (%close db)))))
mas01cr@511 314
mas01cr@511 315 (defun set-up-spec (spec qid qparams qrefine)
mas01cr@511 316 (declare (type (alien adb-query-parameters-t) qparams)
mas01cr@511 317 (type (alien adb-query-refine-t) qrefine)
mas01cr@511 318 (type (alien adb-query-id-t) qid)
mas01cr@511 319 (type (alien adb-query-spec-t) spec))
mas01cr@511 320 (setf (slot spec 'refine) qrefine)
mas01cr@511 321 nil)
mas01cr@511 322
mas01cr@511 323 (defun bar ()
mas01cr@511 324 (let ((db (%open "/home/csr21/tmp/omras2-workshop/9.adb" sb-posix:o-rdonly)))
mas01cr@511 325 (unless (null-alien db)
mas01cr@511 326 (unwind-protect
mas01cr@511 327 (with-alien ((qid adb-query-id-t)
mas01cr@511 328 (qparams adb-query-parameters-t)
mas01cr@511 329 (qrefine adb-query-refine-t)
mas01cr@511 330 (qspec adb-query-spec-t)
mas01cr@511 331 (datum adb-datum-t))
mas01cr@511 332 (setf (slot datum 'key) "KSA_CHARM_337")
mas01cr@511 333 (setf (slot datum 'data) (sap-alien (sb-sys:int-sap 0) (* double)))
mas01cr@511 334
mas01cr@511 335 (setf (slot qid 'datum) (addr datum))
mas01cr@511 336 (setf (slot qid 'sequence-length) 30)
mas01cr@511 337 (setf (slot qid 'flags) 1) ; ADB_QID_FLAG_EXHAUSTIVE
mas01cr@511 338
mas01cr@511 339 (setf (slot qparams 'accumulation) 2) ; ADB_ACCUMULATION_PER_TRACK
mas01cr@511 340 (setf (slot qparams 'distance) 2) ; ADB_DISTANCE_EUCLIDEAN_NORMED
mas01cr@511 341 (setf (slot qparams 'npoints) 1)
mas01cr@511 342 (setf (slot qparams 'ntracks) 20)
mas01cr@511 343
mas01cr@511 344 (setf (slot qrefine 'flags) 0)
mas01cr@511 345 (setf (slot qrefine 'hopsize) 1)
mas01cr@511 346
mas01cr@511 347 (setf (slot qspec 'qid) qid)
mas01cr@511 348 (setf (slot qspec 'params) qparams)
mas01cr@511 349 (setf (slot qspec 'refine) qrefine)
mas01cr@511 350 (let ((results (%query db (addr qspec))))
mas01cr@511 351 (unless (null-alien results)
mas01cr@511 352 (unwind-protect
mas01cr@511 353 (flet ((print-result (n)
mas01cr@511 354 (let ((result (deref (slot results 'results) n)))
mas01cr@511 355 (format t "~&~A ~F ~D ~D~%"
mas01cr@511 356 (slot result 'key) (slot result 'dist)
mas01cr@511 357 (slot result 'qpos) (slot result 'ipos)))))
mas01cr@511 358 (dotimes (i (slot results 'nresults))
mas01cr@511 359 (print-result i)))
mas01cr@511 360 (%free-query-results db (addr qspec) results)))))
mas01cr@511 361 (%close db)))))
mas01cr@511 362 |#