d990d114cb4cf1e2aa23044697314aff92a6c0b5
[binary-types.git] / binary-types.lisp
1 ;;;;------------------------------------------------------------------
2 ;;;; 
3 ;;;;    Copyright (C) 1999-2004,
4 ;;;;    Department of Computer Science, University of Tromsoe, Norway
5 ;;;; 
6 ;;;; Filename:      binary-types.lisp
7 ;;;; Description:   Reading and writing of binary data in streams.
8 ;;;; Author:        Frode Vatvedt Fjeld <frodef@acm.org>
9 ;;;; Created at:    Fri Nov 19 18:53:57 1999
10 ;;;; Distribution:  See the accompanying file COPYING.
11 ;;;;                
12 ;;;; $Id: binary-types.lisp,v 1.3 2004/04/20 08:32:50 ffjeld Exp $
13 ;;;;                
14 ;;;;------------------------------------------------------------------
15
16 (defpackage #:binary-types
17   (:use #:common-lisp)
18   (:export #:*endian*                   ; [dynamic-var] must be bound when reading integers
19            #:endianess                  ; [deftype] The set of endian names
20            ;; built-in types
21            #:char8                      ; [type-name] 8-bit character
22            #:u8                         ; [type-name] 8-bit unsigned integer
23            #:u16                        ; [type-name] 16-bit unsigned integer
24            #:u32                        ; [type-name] 32-bit unsigned integer
25            #:s8                         ; [type-name] 8-bit signed integer
26            #:s16                        ; [type-name] 16-bit signed integer
27            #:s32                        ; [type-name] 32-bit signed integer
28                                         ; (you may define additional integer types
29                                         ; of any size yourself.)
30            ;; type defining macros
31            #:define-unsigned            ; [macro] declare an unsigned-int type
32            #:define-signed              ; [macro] declare a signed-int type
33            #:define-binary-struct       ; [macro] declare a binary defstruct type
34            #:define-binary-class        ; [macro] declare a binary defclass type
35            #:define-bitfield            ; [macro] declare a bitfield (symbolic integer) type
36            #:define-enum                ; [macro] declare an enumerated type
37            #:define-binary-string       ; [macro] declare a string type
38            #:define-null-terminated-string ; [macro] declare a null-terminated string
39            ;; readers and writers
40            #:read-binary                ; [func] reads a binary-type from a stream
41            #:read-binary-record         ; [method]
42            #:write-binary               ; [func] writes an binary object to a stream
43            #:write-binary-record        ; [method]
44            #:read-binary-string
45            ;; record handling
46            #:binary-record-slot-names   ; [func] list names of binary slots.
47            #:binary-slot-value          ; [func] get "binary" version of slot's value
48            #:binary-slot-type           ; [func] get binary slot's binary type
49            #:binary-slot-tags           ; [func] get the tags of a binary slot
50            #:slot-offset                ; [func] determine offset of slot.
51            ;; misc
52            #:find-binary-type           ; [func] accessor to binary-types namespace
53            #:sizeof                     ; [func] The size in octets of a binary type
54            #:enum-value                 ; [func] Calculate numeric version of enum value
55            #:enum-symbolic-value        ; [func] Inverse of enum-value.
56            #:with-binary-file           ; [macro] variant of with-open-file
57            #:with-binary-output-to-list ; [macro]
58            #:with-binary-output-to-vector ; [macro]
59            #:with-binary-input-from-list ; [macro]
60            #:with-binary-input-from-vector ; [macro]
61            #:*binary-write-byte*        ; [dynamic-var]
62            #:*binary-read-byte*         ; [dynamic-var]
63            #:*padding-byte*             ; [dynamic-var] The value filled in when writing paddings
64            #:split-bytes                ; [func] utility
65            #:merge-bytes                ; [func] utility
66            ))
67
68 (in-package binary-types)
69
70 (defvar *ignore-hidden-slots-for-pcl* nil
71   "Really ugly hack to allow older PCL-infested lisps to work in the
72 precense of :map-binary-read-delayed.")
73
74 (defvar *binary-write-byte* #'common-lisp:write-byte
75   "The low-level WRITE-BYTE function used by binary-types.")
76 (defvar *binary-read-byte*  #'common-lisp:read-byte
77   "The low-level READ-BYTE function used by binary-types.")
78
79 ;;; ----------------------------------------------------------------
80 ;;;                         Utilities
81 ;;; ----------------------------------------------------------------
82
83 (defun make-pairs (list)
84   "(make-pairs '(1 2 3 4)) => ((1 . 2) (3 . 4))"
85   (loop for x on list by #'cddr collect (cons (first x) (second x))))
86
87 ;;; ----------------------------------------------------------------
88 ;;; 
89 ;;; ----------------------------------------------------------------
90
91 (eval-when (:compile-toplevel :load-toplevel :execute)
92   (deftype endianess ()
93     "These are the legal declarations of endianess. The value NIL
94 means that the endianess is determined by the dynamic value of *endian*."
95     '(member nil :big-endian :little-endian)))
96
97 (defvar *endian* nil
98   "*endian* must be (dynamically) bound to either :big-endian or
99 :little-endian while reading endian-sensitive types.")
100
101 ;;; ----------------------------------------------------------------
102 ;;;                  Binary Types Namespace
103 ;;; ----------------------------------------------------------------
104
105 (defvar *binary-type-namespace* (make-hash-table :test #'eq)
106   "Maps binary type's names (which are symbols) to their binary-type class object.")
107
108 (defun find-binary-type (name &optional (errorp t))
109   (or (gethash name *binary-type-namespace*)
110       (if errorp
111           (error "Unable to find binary type named ~S." name)
112         nil)))
113
114 (defun (setf find-binary-type) (value name)
115   (check-type value binary-type)
116   (let ((old-value (find-binary-type name nil)))
117     (when (and old-value (not (eq (class-of value) (class-of old-value))))
118       (warn "Redefining binary-type ~A from ~A to ~A."
119             name (type-of old-value) (type-of value))))
120   (setf (gethash name *binary-type-namespace*) value))
121
122 (defun find-binary-type-name (type)
123   (maphash #'(lambda (key val)
124                (when (eq type val)
125                  (return-from find-binary-type-name key)))
126            *binary-type-namespace*))
127
128 ;;; ----------------------------------------------------------------
129 ;;;                  Base Binary Type (Abstract)
130 ;;; ----------------------------------------------------------------
131
132 (defgeneric sizeof (type)
133   (:documentation "Return the size in octets of the single argument TYPE,
134 or nil if TYPE is not constant-sized."))
135
136 (defmethod sizeof (obj)
137   (sizeof (find-binary-type (type-of obj))))
138   
139 (defmethod sizeof ((type symbol))
140   (sizeof (find-binary-type type)))
141
142 (defgeneric read-binary (type stream &key &allow-other-keys)
143   (:documentation "Read an object of binary TYPE from STREAM."))
144
145 (defmethod read-binary ((type symbol) stream &rest key-args)
146   (apply #'read-binary (find-binary-type type) stream key-args))
147
148 (defgeneric write-binary (type stream object &key &allow-other-keys)
149   (:documentation "Write an OBJECT of TYPE to STREAM."))
150
151 (defmethod write-binary ((type symbol) stream object &rest key-args)
152   (apply #'write-binary (find-binary-type type) stream object key-args))
153
154 (defclass binary-type ()
155   ((name
156     :initarg name
157     :initform '#:anonymous-binary-type
158     :reader binary-type-name)
159    (sizeof
160     :initarg sizeof
161     :reader sizeof))
162   (:documentation "BINARY-TYPE is the base class for binary types meta-classes."))
163
164 (defmethod print-object ((object binary-type) stream)
165   (print-unreadable-object (object stream :type 'binary-type)
166     (format stream "~A" (binary-type-name object))))
167
168 ;;; ----------------------------------------------------------------
169 ;;;                      Integer Type (Abstract)
170 ;;; ----------------------------------------------------------------
171
172 (defclass binary-integer (binary-type)
173   ((endian :type endianess
174            :reader binary-integer-endian
175            :initarg endian
176            :initform nil)))
177
178 (defmethod print-object ((type binary-integer) stream)
179   (if (not *print-readably*)
180       (print-unreadable-object (type stream :type t)
181         (format stream "~D-BIT~@[ ~A~] INTEGER TYPE: ~A"
182                 (* 8 (slot-value type 'sizeof))
183                 (slot-value type 'endian)
184                 (binary-type-name type)))    
185     (call-next-method type stream)))
186
187 ;;; WRITE-BINARY is identical for SIGNED and UNSIGNED, but READ-BINARY
188 ;;; is not.
189
190 (defmethod write-binary ((type binary-integer) stream object &key &allow-other-keys)
191   (check-type object integer)
192   (if (= 1 (sizeof type))
193       (progn (funcall *binary-write-byte* object stream) 1)
194     (ecase (or (binary-integer-endian type)
195                *endian*)
196       ((:big-endian big-endian)
197        (do ((i (* 8 (1- (sizeof type))) (- i 8)))
198            ((minusp i) (sizeof type))
199          (funcall *binary-write-byte* (ldb (byte 8 i) object) stream)))
200       ((:little-endian little-endian)
201        (dotimes (i (sizeof type))
202          (funcall *binary-write-byte* (ldb (byte 8 (* 8 i)) object) stream))
203        (sizeof type)))))
204
205 ;;; ----------------------------------------------------------------
206 ;;;                      Unsigned Integer Types
207 ;;; ----------------------------------------------------------------
208
209 (defclass binary-unsigned (binary-integer) ())
210
211 (defmacro define-unsigned (name size &optional endian)
212   (check-type size (integer 1 *))
213   (check-type endian endianess)
214   `(progn
215      (deftype ,name () '(unsigned-byte ,(* 8 size)))
216      (setf (find-binary-type ',name)
217        (make-instance 'binary-unsigned
218          'name ',name
219          'sizeof ,size
220          'endian ,endian))
221      ',name))
222
223 (define-unsigned u8 1)
224 (define-unsigned u16 2)
225 (define-unsigned u32 4)
226
227 (defmethod read-binary ((type binary-unsigned) stream &key &allow-other-keys)
228   (if (= 1 (sizeof type))
229       (values (funcall *binary-read-byte* stream)
230               1)
231     (let ((unsigned-value 0))
232       (ecase (or (binary-integer-endian type)
233                  *endian*)
234         ((:big-endian big-endian)
235          (dotimes (i (sizeof type))
236            (setf unsigned-value (+ (* unsigned-value #x100)
237                                    (funcall *binary-read-byte* stream)
238                                    ))))
239         ((:little-endian little-endian)
240          (dotimes (i (sizeof type))
241            (setf unsigned-value (+ unsigned-value
242                                    (ash (funcall *binary-read-byte* stream)
243                                         (* 8 i)))))))
244       (values unsigned-value
245               (sizeof type)))))
246     
247 ;;; ----------------------------------------------------------------
248 ;;;              Twos Complement Signed Integer Types
249 ;;; ----------------------------------------------------------------
250
251 (defclass binary-signed (binary-integer) ())
252
253 (defmacro define-signed (name size &optional (endian nil))
254   (check-type size (integer 1 *))
255   (check-type endian endianess)
256   `(progn
257      (deftype ,name () '(signed-byte ,(* 8 size)))
258      (setf (find-binary-type ',name)
259        (make-instance 'binary-signed
260          'name ',name
261          'sizeof ,size
262          'endian ,endian))
263      ',name))
264
265 (define-signed s8 1)
266 (define-signed s16 2)
267 (define-signed s32 4)
268
269 (defmethod read-binary ((type binary-signed) stream &key &allow-other-keys)
270   (let ((unsigned-value 0))
271     (if (= 1 (sizeof type))
272         (setf unsigned-value (funcall *binary-read-byte* stream))
273       (ecase (or (binary-integer-endian type)
274                  *endian*)
275         ((:big-endian big-endian)
276          (dotimes (i (sizeof type))
277            (setf unsigned-value (+ (* unsigned-value #x100)
278                                    (funcall *binary-read-byte* stream)
279                                    ))))
280         ((:little-endian little-endian)
281          (dotimes (i (sizeof type))
282            (setf unsigned-value (+ unsigned-value
283                                    (ash (funcall *binary-read-byte* stream)
284                                         (* 8 i))))))))
285     (values (if (>= unsigned-value (ash 1 (1- (* 8 (sizeof type)))))
286                 (- unsigned-value (ash 1 (* 8 (sizeof type))))
287               unsigned-value)
288             (sizeof type))))
289
290 ;;; ----------------------------------------------------------------
291 ;;;                       Character Types
292 ;;; ----------------------------------------------------------------
293
294 ;;; There are probably lots of things one _could_ do with character
295 ;;; sets etc..
296
297 (defclass binary-char8 (binary-type) ())
298
299 (setf (find-binary-type 'char8)
300   (make-instance 'binary-char8
301     'name 'char8
302     'sizeof 1))
303
304 (deftype char8 () 'character)
305
306 (defmethod read-binary ((type binary-char8) stream &key &allow-other-keys)
307   (values (code-char (read-binary 'u8 stream))
308           1))
309
310 (defmethod write-binary ((type binary-char8) stream object &key &allow-other-keys)
311   (write-binary 'u8 stream (char-code object)))
312
313 ;;; ----------------------------------------------------------------
314 ;;;     Padding Type (Implicitly defined and named by integers)
315 ;;; ----------------------------------------------------------------
316
317 ;;; The padding type of size 3 octets is named by the integer 3, and
318 ;;; so on.
319
320 (defmethod sizeof ((type integer)) type)
321
322 (defmethod read-binary ((type integer) stream &key &allow-other-keys)
323   (dotimes (i type)
324     (read-binary 'u8 stream))
325   (values nil type))
326
327 (defvar *padding-byte* #x00
328   "The value written to padding octets.")
329
330 (defmethod write-binary ((type integer) stream object &key &allow-other-keys)
331   (declare (ignore object))
332   (check-type *padding-byte* (unsigned-byte 8))
333   (dotimes (i type)
334     (write-binary 'u8 stream *padding-byte*))
335   type)
336
337 ;;; ----------------------------------------------------------------
338 ;;;                   String library functions
339 ;;; ----------------------------------------------------------------
340
341 (defun read-binary-string (stream &key size terminators)
342   "Read a string from STREAM, terminated by any member of the list TERMINATORS.
343 If SIZE is provided and non-nil, exactly SIZE octets are read, but the returned
344 string is still terminated by TERMINATORS. The string and the number of octets
345 read are returned."
346   (check-type size (or null (integer 0 *)))
347   (check-type terminators list)
348   (assert (or size terminators) (size terminators)
349     "Can't read a binary-string without a size limitation nor terminating bytes.")
350   (let (bytes-read)
351     (values (with-output-to-string (string)
352               (loop with string-terminated = nil
353                   for count upfrom 0
354                   until (if size (= count size) string-terminated)
355                   do (let ((byte (funcall *binary-read-byte* stream)))
356                        (cond
357                         ((member byte terminators :test #'=)
358                          (setf string-terminated t))
359                         ((not string-terminated)
360                          (write-char (code-char byte) string))))
361                   finally (setf bytes-read count)))
362             bytes-read)))
363
364 ;;; ----------------------------------------------------------------
365 ;;;                  String Types
366 ;;; ----------------------------------------------------------------
367
368 (defclass binary-string (binary-type)
369   ((terminators
370     :initarg terminators
371     :reader binary-string-terminators)))
372
373 (defmacro define-binary-string (type-name size &key terminators)
374   (check-type size (integer 1 *))
375   `(progn
376      (deftype ,type-name () 'string)
377      (setf (find-binary-type ',type-name)
378        (make-instance 'binary-string
379          'name ',type-name
380          'sizeof ,size
381          'terminators ,terminators))
382      ',type-name))
383
384 (defmacro define-null-terminated-string (type-name size)
385   `(define-binary-string ,type-name ,size :terminators '(0)))
386
387 (defmacro define-fixed-size-nt-string (type-name size)
388   ;; compatibility..
389   `(define-null-terminated-string ,type-name ,size))
390
391 (defmethod read-binary ((type binary-string) stream &key &allow-other-keys)
392   (read-binary-string stream
393                       :size (sizeof type)
394                       :terminators (binary-string-terminators type)))
395
396 (defmethod write-binary ((type binary-string) stream obj  &key &allow-other-keys)
397   (check-type obj string)
398   (dotimes (i (sizeof type))
399     (if (< i (length obj))
400         (funcall *binary-write-byte* (char-code (aref obj i)) stream)
401       (funcall *binary-write-byte*
402                ;; use the first member of TERMINATORS as writing terminator.
403                (or (first (binary-string-terminators type)) 0)
404                stream)))
405   (sizeof type))
406
407 ;;; ----------------------------------------------------------------
408 ;;;                    Record Types ("structs")
409 ;;; ----------------------------------------------------------------
410
411 ;;;(defstruct compound-slot
412 ;;;  name
413 ;;;  type
414 ;;;  on-write)
415
416 ;;;(defun make-record-slot (&key name type map-write)
417 ;;;  (list name type map-write map-read))
418 ;;;
419 ;;;(defun record-slot-name (s) (first s))
420 ;;;(defun record-slot-type (s) (second s))
421 ;;;(defun record-slot-on-write (s) (third s))
422
423 (eval-when (:load-toplevel :compile-toplevel)
424   (defstruct record-slot
425     name
426     type
427     map-write
428     map-read
429     map-read-delayed
430     hidden-read-slot
431     tags))                              ; for map-read-delayed, the binary value is stored here.
432
433 (defmethod make-load-form ((object record-slot) &optional environment)
434   (declare (ignore environment))
435   (with-slots (name type map-write map-read map-read-delayed hidden-read-slot)
436       object
437     `(make-record-slot :name ',name
438                        :type ',type
439                        :map-write ,map-write
440                        :map-read ,map-read
441                        :map-read-delayed ,map-read-delayed
442                        :hidden-read-slot ',hidden-read-slot)))
443
444 (defclass binary-record (binary-type)
445   ((slots
446     :initarg slots
447     :accessor binary-record-slots)
448    (offset
449     :initarg offset
450     :reader binary-record-slot-offset)))
451
452 (defclass binary-class (binary-record)
453   ;; a DEFCLASS class with binary properties
454   ((instance-class
455     :type standard-class
456     :initarg instance-class)))
457
458 (defmethod binary-record-make-instance ((type binary-class))
459   (make-instance (slot-value type 'instance-class)))
460
461 (defclass binary-struct (binary-record)
462   ;; A DEFSTRUCT type with binary properties
463   ((constructor :initarg constructor)))
464
465 (defmethod binary-record-make-instance ((type binary-struct))
466   (funcall (slot-value type 'constructor)))
467
468 (defun slot-offset (type slot-name)
469   "Return the offset (in number of octets) of SLOT-NAME in TYPE."
470   (unless (typep type 'binary-record)
471     (setf type (find-binary-type type)))
472   (check-type type binary-record)
473   (unless (find-if #'(lambda (slot)
474                        (eq slot-name (record-slot-name slot)))
475                    (binary-record-slots type))
476     (error "Slot ~S doesn't exist in type ~S."
477            slot-name type))
478   (+ (binary-record-slot-offset type)
479      (loop for slot in (binary-record-slots type)
480          until (eq slot-name (record-slot-name slot))
481          summing (sizeof (record-slot-type slot)))))
482
483 (defun binary-slot-tags (type slot-name)
484   (when (symbolp type)
485     (setf type (find-binary-type type)))
486   (let ((slot (find slot-name (binary-record-slots type) :key #'record-slot-name)))
487     (assert slot (slot-name)
488       "No slot named ~S in binary-type ~S." slot-name type)
489     (record-slot-tags slot)))
490
491 (defun binary-record-slot-names (type &key (padding-slots-p nil)
492                                            (match-tags nil))
493   "Returns a list of the slot-names of TYPE, in sequence."
494   (when (symbolp type)
495     (setf type (find-binary-type type)))
496   (when (and match-tags (atom match-tags))
497     (setf match-tags (list match-tags)))
498   (let ((slot-names (if padding-slots-p
499                         (mapcar #'record-slot-name (binary-record-slots type))
500                       (mapcan #'(lambda (slot)
501                                   (if (integerp (record-slot-type slot))
502                                       nil
503                                     (list (record-slot-name slot))))
504                               (binary-record-slots type)))))
505     (if (null match-tags)
506         slot-names
507       (loop for slot-name in slot-names
508           when (intersection (binary-slot-tags type slot-name)
509                              match-tags)
510           collect slot-name))))
511
512 (defun binary-slot-type (type slot-name)
513   (when (symbolp type)
514     (setf type (find-binary-type type)))
515   (let ((slot (find slot-name (binary-record-slots type) :key #'record-slot-name)))
516     (assert slot (slot-name)
517       "No slot named ~S in binary-type ~S." slot-name type)
518     (record-slot-type slot)))
519
520 (defun quoted-name-p (form)
521   (and (listp form)
522        (= 2 (length form))
523        (eq 'cl:quote (first form))
524        (symbolp (second form))
525        (second form)))
526
527 (defun parse-bt-spec (expr)
528   "Takes a binary-type specifier (a symbol, integer, or define-xx form),
529   and returns three values: the binary-type's name, the equivalent lisp type,
530   and any nested declaration that must be expanded separately."
531   (cond
532    ((eq :label expr) (values 0 nil))    ; a label
533    ((symbolp expr) (values expr expr))  ; a name
534    ((integerp expr) (values expr nil))  ; a padding type
535    ((quoted-name-p expr)
536     (values (second expr) (second expr))) ; a quoted name
537    ((and (listp expr)                   ; a nested declaration
538          (symbolp (first expr))
539          (eq (find-package 'binary-types)
540              (symbol-package (first expr))))
541     (values (second expr) (second expr) expr))
542    (t (error "Unknown nested binary-type specifier: ~S" expr))))
543
544 (defmacro define-binary-class (type-name supers slots &rest class-options)
545   (let (embedded-declarations)
546     (flet ((parse-slot-specifier (slot-specifier)
547              "For a class slot-specifier, return the slot-specifier to forward
548  (sans binary-type options), the binary-type of the slot (or nil),
549  and the slot's name, and map-write, map-read and map-read-delayed
550  functions if present."
551              (when (symbolp slot-specifier)
552                (setf slot-specifier (list slot-specifier)))
553              (loop for slot-options on (rest slot-specifier) by #'cddr
554                  as slot-option = (first slot-options)
555                  as slot-option-arg = (second slot-options)
556                  with bintype = nil
557                  and typetype = nil
558                  and map-write = nil
559                  and map-read = nil
560                  and map-read-delayed = nil
561                  and tags = nil
562                  unless 
563                    (case slot-option
564                      (:binary-tag
565                       (prog1 t
566                         (setf tags (if (atom slot-option-arg)
567                                        (list slot-option-arg)
568                                      slot-option-arg))))
569                      ((:bt-on-write :map-binary-write)
570                       (prog1 t
571                         (setf map-write slot-option-arg)))
572                      (:map-binary-read
573                       (prog1 t
574                         (setf map-read slot-option-arg)))
575                      (:map-binary-read-delayed
576                       (prog1 t
577                         (setf map-read-delayed slot-option-arg)))
578                      ((:bt :btt :binary-type :binary-lisp-type)
579                       (prog1 t
580                         (multiple-value-bind (bt tt nested-form)
581                             (parse-bt-spec slot-option-arg)
582                           (setf bintype bt)
583                           (when nested-form
584                             (push nested-form embedded-declarations))
585                           (when (and (symbolp tt)
586                                      (member slot-option '(:btt :binary-lisp-type)))
587                             (setf typetype tt))))))
588                  nconc (list slot-option
589                              slot-option-arg) into options
590                  finally (return (values (list* (first slot-specifier)
591                                                 (if typetype
592                                                     (list* :type typetype options)
593                                                   options))
594                                          bintype
595                                          (first slot-specifier)
596                                          map-write
597                                          map-read
598                                          map-read-delayed
599                                          tags)))))
600       (multiple-value-bind (binslot-forms binslot-types hidden-slots)
601           (loop for slot-specifier in slots with binslot-forms and binslot-types and hidden-slots
602               do (multiple-value-bind (options bintype slot-name map-write map-read map-read-delayed tags)
603                      (parse-slot-specifier slot-specifier)
604                    (declare (ignore options))
605                    (when bintype
606                      (let ((hidden-read-slot-name (when map-read-delayed
607                                                     (make-symbol (format nil "hidden-slot-~A"
608                                                                          slot-name)))))
609                        (push `(make-record-slot
610                                :name ',slot-name
611                                :type ',bintype
612                                :map-write ,map-write
613                                :map-read ,map-read
614                                :map-read-delayed ,map-read-delayed
615                                :hidden-read-slot ',hidden-read-slot-name
616                                :tags ',tags)
617                              binslot-forms)
618                        (when (and hidden-read-slot-name
619                                   (not *ignore-hidden-slots-for-pcl*))
620                          (push (list hidden-read-slot-name slot-name map-read-delayed bintype)
621                                hidden-slots))
622                        (push bintype binslot-types))))
623               finally (return (values (reverse binslot-forms)
624                                       (reverse binslot-types)
625                                       (reverse hidden-slots))))
626         (let* ((forward-class-options (loop for co in class-options
627                                           unless (member (car co)
628                                                          '(:slot-align :class-slot-offset))
629                                           collect co))
630                (class-slot-offset (or (second (assoc :class-slot-offset class-options)) 0))
631                (slot-align-slot (second (assoc :slot-align class-options)))
632                (slot-align-offset (third (assoc :slot-align class-options))))
633           `(progn
634              ,@embedded-declarations
635              (defclass ,type-name ,supers
636                ,(append (mapcar #'parse-slot-specifier slots)
637                         (mapcar #'first hidden-slots))
638                ,@forward-class-options)
639              (let ((record-size (loop for s in ',binslot-types summing (sizeof s))))
640                (setf (find-binary-type ',type-name)
641                  (make-instance 'binary-class
642                    'name ',type-name
643                    'sizeof record-size
644                    'slots (list ,@binslot-forms)
645                    'offset ,class-slot-offset
646                    'instance-class (find-class ',type-name)))
647                ,@(when slot-align-slot
648                    `((setf (slot-value (find-binary-type ',type-name) 'offset)
649                        (- ,slot-align-offset
650                           (slot-offset ',type-name ',slot-align-slot)))))
651                ,@(loop for bs in hidden-slots
652                      collect `(defmethod slot-unbound (class (instance ,type-name)
653                                                        (slot-name (eql ',(second bs))))
654                                 (if (not (slot-boundp instance ',(first bs)))
655                                     (call-next-method class instance slot-name)
656                                   (setf (slot-value instance slot-name)
657                                     (funcall ,(third bs)
658                                              (slot-value instance ',(first bs))
659                                              ',(fourth bs))))))
660                ',type-name)))))))
661   
662
663 (defmacro define-binary-struct (name-and-options dummy-options &rest doc-slot-descriptions)
664   (declare (ignore dummy-options))      ; clisp seems to require this..
665   (let (embedded-declarations)
666     (flet ((parse-slot-description (slot-description)
667              (cond
668               ((symbolp slot-description)
669                (values slot-description nil slot-description))
670               ((>= 2 (list-length slot-description))
671                (values slot-description nil (first slot-description)))
672               (t (loop for descr on (cddr slot-description) by #'cddr
673                      with bintype = nil
674                      and typetype = nil
675                      if (member (first descr)
676                                 '(:bt :btt :binary-type :binary-lisp-type))
677                      do (multiple-value-bind (bt lisp-type nested-form)
678                             (parse-bt-spec (second descr))
679                           (declare (ignore lisp-type))
680                           (setf bintype bt)
681                           (when nested-form
682                             (push nested-form embedded-declarations))
683                           (when (and (symbolp bt)
684                                      (member (first descr)
685                                              '(:btt :binary-lisp-type)))
686                             (setf typetype bintype)))
687                      else nconc
688                           (list (first descr) (second descr)) into descriptions
689                      finally
690                        (return (values (list* (first slot-description)
691                                               (second slot-description)
692                                               (if typetype
693                                                   (list* :type typetype descriptions)
694                                                 descriptions))
695                                        bintype
696                                        (first slot-description))))))))
697       (multiple-value-bind (doc slot-descriptions)
698           (if (stringp (first doc-slot-descriptions))
699               (values (list (first doc-slot-descriptions))
700                       (rest doc-slot-descriptions))
701             (values nil doc-slot-descriptions))
702         (let* ((type-name (if (consp name-and-options)
703                               (first name-and-options)
704                             name-and-options))
705                (binslots (mapcan (lambda (slot-description)
706                                    (multiple-value-bind (options bintype slot-name)
707                                        (parse-slot-description slot-description)
708                                      (declare (ignore options))
709                                      (if bintype
710                                          (list (make-record-slot :name slot-name
711                                                                  :type bintype))
712                                        nil)))
713                                  slot-descriptions))
714                (slot-types (mapcar #'record-slot-type binslots)))
715           `(progn
716              ,@embedded-declarations
717              (defstruct ,name-and-options
718                ,@doc
719                ,@(mapcar #'parse-slot-description slot-descriptions))
720              (setf (find-binary-type ',type-name)
721                (make-instance 'binary-struct
722                  'name ',type-name
723                  'sizeof (loop for s in ',slot-types sum (sizeof s))
724                  'slots ',binslots
725                  'offset 0
726                  'constructor (find-symbol (format nil "~A-~A" '#:make ',type-name))))
727              ',type-name))))))
728
729 (defmethod read-binary-record (type-name stream &key start stop &allow-other-keys)
730   (let ((type (find-binary-type type-name))
731         (start-slot 0)
732         (stop-slot nil))
733     (check-type type binary-record)
734     (when start
735       (setf start-slot (position-if #'(lambda (sp)
736                                         (eq start (record-slot-name sp)))
737                                     (binary-record-slots type)))
738       (unless start-slot
739         (error "start-slot ~S not found in type ~A"
740                start type)))
741     (when stop
742       (setf stop-slot (position-if #'(lambda (sp)
743                                        (eq stop (record-slot-name sp)))
744                                    (binary-record-slots type)))
745       (unless stop-slot
746         (error "stop-slot ~S not found in type ~A"
747                stop  type)))
748     (let ((total-read-bytes 0)
749           (slot-list (subseq (binary-record-slots type) start-slot stop-slot))
750           (object (binary-record-make-instance type)))
751       (dolist (slot slot-list)
752         (multiple-value-bind (read-slot-value read-slot-bytes)
753             (read-binary (record-slot-type slot) stream)
754           (cond
755            ((record-slot-map-read-delayed slot)
756             (setf (slot-value object (record-slot-hidden-read-slot slot))
757               read-slot-value)
758             (slot-makunbound object (record-slot-name slot)))
759            ((record-slot-map-read slot)
760             (setf (slot-value object (record-slot-name slot))
761               (funcall (record-slot-map-read slot) read-slot-value)))
762            (t (setf (slot-value object (record-slot-name slot)) read-slot-value)))
763           (incf total-read-bytes read-slot-bytes)))
764       (values object total-read-bytes))))
765   
766 (defmethod read-binary ((type binary-record) stream &key start stop &allow-other-keys)
767   (read-binary-record (binary-type-name type) stream :start start :stop stop))
768
769 (defmethod write-binary-record (object stream)
770   (write-binary (find-binary-type (type-of object)) stream object))
771
772 (defun binary-slot-value (object slot-name)
773   "Return the ``binary'' value of a slot, i.e the value mapped
774 by any MAP-ON-WRITE slot mapper function."
775   (let ((slot (find slot-name (binary-record-slots (find-binary-type (type-of object)))
776                     :key #'record-slot-name)))
777     (assert slot ()
778       "Slot-name ~A not found in ~S of type ~S."
779       slot-name object (find-binary-type (type-of object)))
780 ;;;    (warn "slot: ~S value: ~S" slot (slot-value object slot-name))
781     (cond
782      ((integerp (record-slot-type slot)) nil) ; padding
783      ((and (record-slot-map-read-delayed slot)
784            (not (slot-boundp object slot-name))
785            (slot-boundp object (record-slot-hidden-read-slot slot)))
786       (slot-value object (record-slot-hidden-read-slot slot)))
787      ((record-slot-map-write slot)
788       (funcall (record-slot-map-write slot)
789                (slot-value object slot-name)
790                (record-slot-type slot)))
791      (t (slot-value object slot-name)))))
792
793 (defmethod write-binary ((type binary-record) stream object
794                          &key start stop &allow-other-keys)
795   (let ((start-slot 0)
796         (stop-slot nil))
797     (when start
798       (setf start-slot (position-if #'(lambda (sp)
799                                         (eq start (record-slot-name sp)))
800                                     (binary-record-slots type)))
801       (unless start-slot
802         (error "start-slot ~S not found in type ~A"
803                start type)))
804     (when stop
805       (setf stop-slot (position-if #'(lambda (sp)
806                                        (eq stop (record-slot-name sp)))
807                                    (binary-record-slots type)))
808       (unless stop-slot
809         (error "stop-slot ~S not found in type ~A"
810                stop type)))
811     (let ((written-bytes 0)
812           (slot-list (subseq (binary-record-slots type) start-slot stop-slot)))
813       (dolist (slot slot-list)
814         (let* ((slot-name (record-slot-name slot))
815                (slot-type (record-slot-type slot))
816                (value (cond
817                        ((integerp slot-type) nil) ; padding
818                        ((record-slot-map-write slot)
819                         (funcall (record-slot-map-write slot)
820                                  (slot-value object slot-name)
821                                  slot-type))
822                        (t (slot-value object slot-name)))))
823           (incf written-bytes
824                 (write-binary slot-type stream value))))
825       written-bytes)))
826
827 (defun merge-binary-records (obj1 obj2)
828   "Returns a record where every non-bound slot in obj1 is replaced
829 with that slot's value from obj2."
830   (let ((class (class-of obj1)))
831     (unless (eq class (class-of obj2))
832       (error "cannot merge incompatible records ~S and ~S" obj1 obj2))
833     (let ((new-obj (make-instance class)))
834       (dolist (slot (binary-record-slots (find-binary-type (type-of obj1))))
835         (let ((slot-name (record-slot-name slot)))
836           (cond
837            ((slot-boundp obj1 slot-name)
838             (setf (slot-value new-obj slot-name)
839               (slot-value obj1 slot-name)))
840            ((slot-boundp obj2 slot-name)
841             (setf (slot-value new-obj slot-name)
842               (slot-value obj2 slot-name))))))
843       new-obj)))
844
845 (defun binary-record-alist (obj)
846   "Returns an assoc-list representation of (the slots of) a binary
847 record object."
848   (mapcan #'(lambda (slot)
849               (unless (integerp (record-slot-type slot))
850                 (list (cons (record-slot-name slot)
851                             (if (slot-boundp obj (record-slot-name slot))
852                                 (slot-value obj (record-slot-name slot))
853                               'unbound-slot)))))
854           (binary-record-slots (find-binary-type (type-of obj)))))
855
856 ;;; ----------------------------------------------------------------
857 ;;; Bitfield Types
858 ;;; ----------------------------------------------------------------
859
860 (defclass bitfield (binary-type)
861   ((storage-type
862     :type t
863     :accessor storage-type
864     :initarg storage-type)
865    (hash
866     :type hash-table
867     :initform (make-hash-table :test #'eq)
868     :accessor bitfield-hash)))
869
870 (defstruct bitfield-entry
871   value
872   bytespec)
873
874 (defmacro define-bitfield (type-name (storage-type) spec)
875   (let ((slot-list                      ; (slot-name value byte-size byte-pos)
876          (mapcan #'(lambda (set)
877                      (ecase (caar set)
878                        (:bits
879                         (mapcar #'(lambda (slot)
880                                     (list (car slot)
881                                           1
882                                           1
883                                           (cdr slot)))
884                                 (make-pairs (cdr set))))
885                        (:enum
886                         (destructuring-bind (&key byte)
887                             (rest (car set))
888                           (mapcar #'(lambda (slot)
889                                       (list (car slot)
890                                             (cdr slot)
891                                             (first byte)
892                                             (second byte)))
893                                   (make-pairs (cdr set)))))
894                        (:numeric
895                         (let ((s (car set)))
896                           (list (list (second s)
897                                       nil
898                                       (third s)
899                                       (fourth s)))))))
900                  spec)))
901     `(let ((type-obj (make-instance 'bitfield 
902                        'name ',type-name
903                        'sizeof (sizeof ',storage-type)
904                        'storage-type (find-binary-type ',storage-type))))
905        (deftype ,type-name () '(or list symbol))
906        (dolist (slot ',slot-list)
907          (setf (gethash (first slot) (bitfield-hash type-obj))
908            (make-bitfield-entry :value (second slot)
909                                 :bytespec (if (and (third slot)
910                                                    (fourth slot))
911                                               (byte (third slot)
912                                                     (fourth slot))
913                                             nil))))
914        (setf (find-binary-type ',type-name) type-obj)
915        ',type-name)))
916
917 (defmacro define-enum (type-name (storage-name &optional byte-spec) &rest spec)
918   "A simple wrapper around DEFINE-BITFIELD for simple enum types."
919   `(define-bitfield ,type-name (,storage-name)
920      (((:enum :byte ,byte-spec)
921        ,@spec))))
922
923 (defun bitfield-compute-symbolic-value (type numeric-value)
924   "Return the symbolic value of a numeric bitfield"
925   (check-type numeric-value integer)
926   (let (result)
927     (maphash #'(lambda (slot-name entry)
928                  (let ((e-value (bitfield-entry-value entry))
929                        (e-bytespec (bitfield-entry-bytespec entry)))
930                    (cond
931                     ((and e-value e-bytespec)
932                      (when (= e-value
933                               (ldb e-bytespec numeric-value))
934                        (push slot-name
935                              result)))
936                     (e-value
937                      ;; no mask => this must be the sole entry present
938                      (when (= numeric-value e-value)
939                        (setf result slot-name)))
940                     (e-bytespec
941                      ;; no value => this is a numeric sub-field
942                      (push (cons slot-name
943                                  (ldb e-bytespec numeric-value))
944                            result))
945                     (t (error "bitfield-value type ~A has NIL value and bytespec" type)))))
946              (bitfield-hash type))
947 ;;;;; Consistency check by symmetry. Uncomment for debugging.
948 ;;;    (unless (= numeric-value
949 ;;;            (bitfield-compute-numeric-value type result))
950 ;;;      (error "bitfield inconsitency with ~A: ~X => ~A => ~X."
951 ;;;          (type-of type)
952 ;;;          numeric-value
953 ;;;          result
954 ;;;          (bitfield-compute-numeric-value type result)))
955     result))
956
957 (defun enum-value (type symbolic-value)
958   "For an enum type (actually, for any bitfield-based type), ~
959    look up the numeric value of a symbol."
960   (unless (typep type 'bitfield)
961     (setf type (find-binary-type type)))
962   (bitfield-compute-numeric-value type symbolic-value))
963
964 (defun enum-symbolic-value (type binary-value)
965   "The inverse of ENUM-VALUE."
966   (unless (typep type 'bitfield)
967     (setf type (find-binary-type type)))
968   (bitfield-compute-symbolic-value type binary-value))
969
970 (defun bitfield-compute-numeric-value (type symbolic-value)
971   "Returns the numeric representation of a bitfields symbolic value."
972   (etypecase symbolic-value
973     (list
974      (let ((result 0))
975        (dolist (slot symbolic-value)
976          (etypecase slot
977            (symbol                      ; enum sub-field
978             (let ((entry (gethash slot (bitfield-hash type))))
979               (assert entry (entry) "Unknown bitfield slot ~S of ~S."
980                       slot (find-binary-type-name type))
981               (setf (ldb (bitfield-entry-bytespec entry) result)
982                 (bitfield-entry-value entry))))
983            (cons                        ; numeric sub-field
984             (let ((entry (gethash (car slot) (bitfield-hash type))))
985               (assert entry (entry) "Unknown bitfield slot ~S of ~S."
986                       (car slot) (find-binary-type-name type))
987               (setf (ldb (bitfield-entry-bytespec entry) result)
988                 (cdr slot))))))
989        result))
990     (symbol                             ; enum
991      (let ((entry (gethash symbolic-value
992                            (bitfield-hash type))))
993        (assert entry (entry) "Unknown bitfield slot ~A:~S of ~S."
994                (package-name (symbol-package symbolic-value))
995                symbolic-value
996                (find-binary-type-name type))
997        (if (bitfield-entry-bytespec entry)
998            (dpb (bitfield-entry-value entry)
999                 (bitfield-entry-bytespec entry)
1000                 0)
1001          (bitfield-entry-value entry))))))
1002   
1003 (defmethod read-binary ((type bitfield) stream &key &allow-other-keys)
1004   (multiple-value-bind (storage-obj num-octets-read)
1005       (read-binary (storage-type type) stream)
1006     (values (bitfield-compute-symbolic-value type storage-obj)
1007             num-octets-read)))
1008   
1009 (defmethod write-binary ((type bitfield) stream symbolic-value &rest key-args)
1010   (apply #'write-binary
1011          (storage-type type)
1012          stream
1013          (bitfield-compute-numeric-value type symbolic-value)
1014          key-args))
1015
1016 ;;;; Macros:
1017
1018 (defmacro with-binary-file ((stream-var path &rest key-args) &body body)
1019   "This is a thin wrapper around WITH-OPEN-FILE, that tries to set the
1020 stream's element-type to that required by READ-BINARY and WRITE-BINARY.
1021 A run-time assertion on the stream's actual element type is performed,
1022 unless you disable this feature by setting the keyword option :check-stream
1023 to nil."
1024   (let ((check-stream (getf key-args :check-stream t))
1025         (fwd-key-args (copy-list key-args)))
1026     ;; This is manual parsing of keyword arguments. We force :element-type
1027     ;; to (unsigned-byte 8), and remove :check-stream from the arguments
1028     ;; passed on to WITH-OPEN-FILE.
1029     (remf fwd-key-args :check-stream)
1030     ;; #-(and allegro-version>= (version>= 6 0))
1031     (setf (getf fwd-key-args :element-type) ''(unsigned-byte 8))
1032     `(with-open-file (,stream-var ,path ,@fwd-key-args)
1033        ,@(when check-stream
1034            `((let ((stream-type (stream-element-type ,stream-var)))
1035                (assert (and (subtypep '(unsigned-byte 8) stream-type)
1036                             (subtypep stream-type '(unsigned-byte 8)))
1037                    ()
1038                  "Failed to open ~S in 8-bit binary mode, stream element-type was ~S"
1039                  ,path stream-type))))
1040        ,@body)))
1041
1042 (defmacro with-binary-output-to-list ((stream-var) &body body)
1043   "Inside BODY, calls to WRITE-BINARY with stream STREAM-VAR will
1044 collect the individual 8-bit bytes in a list (of integers).
1045 This list is returned by the form. (There is no way to get at
1046 the return-value of BODY.)
1047 This macro depends on the binding of *BINARY-WRITE-BYTE*, which should
1048 not be shadowed."
1049   (let ((save-bwt-var (make-symbol "save-bwt"))
1050         (closure-byte-var (make-symbol "closure-byte"))
1051         (closure-stream-var (make-symbol "closure-stream")))
1052     `(let* ((,save-bwt-var *binary-write-byte*)
1053             (,stream-var (cons nil nil)) ; (head . tail)
1054             (*binary-write-byte*
1055              #'(lambda (,closure-byte-var ,closure-stream-var)
1056                  (if (eq ,stream-var ,closure-stream-var)
1057                      (if (endp (cdr ,stream-var))
1058                          (setf (cdr ,stream-var)
1059                            (setf (car ,stream-var) (list ,closure-byte-var)))
1060                        (setf (cdr ,stream-var)
1061                          (setf (cddr ,stream-var) (list ,closure-byte-var))))
1062                    (funcall ,save-bwt-var ; it's not our stream, so pass it ...
1063                             ,closure-byte-var ; along to the next function.
1064                             ,closure-stream-var)))))
1065        ,@body
1066        (car ,stream-var))))
1067
1068 (defmacro with-binary-input-from-list ((stream-var list-form) &body body)
1069   "Bind STREAM-VAR to an object that, when passed to READ-BINARY, provides
1070 8-bit bytes from LIST-FORM, which must yield a list.
1071 Binds *BINARY-READ-BYTE* appropriately. This macro will break if this
1072 binding is shadowed."
1073   (let ((save-brb-var (make-symbol "save-brb")))
1074     `(let* ((,save-brb-var *binary-read-byte*)
1075             (,stream-var (cons ,list-form nil)) ; use cell as stream id.
1076             (*binary-read-byte* #'(lambda (s)
1077                                     (if (eq s ,stream-var)
1078                                         (if (null (car s))
1079                                             (error "WITH-BINARY-INPUT-FROM-LIST reached end of list.")
1080                                           (pop (car s)))
1081                                       (funcall ,save-brb-var s)))))
1082        ,@body)))
1083
1084 (defmacro with-binary-input-from-vector
1085     ((stream-var vector-form &key (start 0)) &body body)
1086   "Bind STREAM-VAR to an object that, when passed to READ-BINARY, provides
1087 8-bit bytes from VECTOR-FORM, which must yield a vector.
1088 Binds *BINARY-READ-BYTE* appropriately. This macro will break if this
1089 binding is shadowed."
1090   (let ((save-brb-var (make-symbol "save-brb")))
1091     `(let* ((,save-brb-var *binary-read-byte*)
1092             (,stream-var (cons (1- ,start) ,vector-form))
1093             (*binary-read-byte* #'(lambda (s)
1094                                     (if (eq s ,stream-var)
1095                                         (aref (cdr s) (incf (car s)))
1096                                       (funcall ,save-brb-var s)))))
1097        ,@body)))
1098
1099 (defmacro with-binary-output-to-vector
1100     ((stream-var &optional (vector-or-size-form 0)
1101       &key (adjustable (and (integerp vector-or-size-form)
1102                             (zerop vector-or-size-form)))
1103            (fill-pointer 0)
1104            (element-type ''(unsigned-byte 8))
1105            (on-full-array :error))
1106      &body body)
1107   "Arrange for STREAM-VAR to collect octets in a vector.
1108 VECTOR-OR-SIZE-FORM is either a form that evaluates to a vector, or an
1109 integer in which case a new vector of that size is created. The vector's
1110 fill-pointer is used as the write-index. If ADJUSTABLE nil (or not provided),
1111 an error will occur if the array is too small. Otherwise, the array will
1112 be adjusted in size, using VECTOR-PUSH-EXTEND. If ADJUSTABLE is an integer,
1113 that value will be passed as the EXTENSION argument to VECTOR-PUSH-EXTEND.
1114 If VECTOR-OR-SIZE-FORM is an integer, the created vector is returned,
1115 otherwise the value of BODY."
1116   (let ((vector-form
1117          (if (integerp vector-or-size-form)
1118              `(make-array ,vector-or-size-form
1119                           :element-type ,element-type
1120                           :adjustable ,(and adjustable t)
1121                           :fill-pointer ,fill-pointer)
1122            vector-or-size-form)))
1123     (let ((save-bwb-var (make-symbol "save-bwb")))
1124       `(let* ((,save-bwb-var *binary-write-byte*)
1125               (,stream-var ,vector-form)
1126               (*binary-write-byte*
1127                #'(lambda (byte stream)
1128                    (if (eq stream ,stream-var)
1129                        ,(cond
1130                          (adjustable
1131                           `(vector-push-extend byte stream
1132                                                ,@(when (integerp adjustable)
1133                                                    (list adjustable))))
1134                          ((eq on-full-array :error)
1135                           `(assert (vector-push byte stream) (stream)
1136                              "Binary output vector is full when writing byte value ~S: ~S"
1137                              byte stream))
1138                          ((eq on-full-array :ignore)
1139                           `(vector-push byte stream))
1140                          (t (error "Unknown ON-FULL-ARRAY argument ~S, must be one of :ERROR, :IGNORE."
1141                                    on-full-array)))
1142                      (funcall ,save-bwb-var byte stream)))))
1143          ,@body
1144          ,@(when (integerp vector-or-size-form)
1145              (list stream-var))))))
1146              
1147
1148 ;;;
1149
1150 (defun split-bytes (bytes from-size to-size)
1151   "From a list of BYTES sized FROM-SIZE bits, split each byte into bytes of size TO-SIZE,
1152    according to *ENDIAN*. TO-SIZE must divide FROM-SIZE evenly. If this is not the case,
1153    you might want to apply MERGE-BYTES to the list of BYTES first."
1154   (assert (zerop (rem from-size to-size)) (from-size to-size)
1155     "TO-SIZE ~D doesn't evenly divide FROM-SIZE ~D." to-size from-size)
1156   (ecase *endian*
1157     (:little-endian
1158      (loop for byte in bytes
1159          append (loop for x from 0 below (truncate from-size to-size)
1160                     collect (ldb (byte to-size (* x to-size)) byte))))
1161     (:big-endian
1162      (loop for byte in bytes
1163          append (loop for x from (1- (truncate from-size to-size)) downto 0
1164                     collect (ldb (byte to-size (* x to-size)) byte))))))                                                                      
1165 (defun merge-bytes (bytes from-size to-size)
1166   "Combine BYTES sized FROM-SIZE bits into new bytes sized TO-SIZE bits."
1167   (assert (zerop (rem to-size from-size)))
1168   (let ((factor (truncate to-size from-size)))
1169     (ecase *endian*
1170       (:little-endian
1171        (loop for bytes on bytes by #'(lambda (x) (nthcdr factor x))
1172            collect (loop for n from 0 below factor
1173                        as sub-byte = (or (nth n bytes) 0)
1174                        summing (ash sub-byte (* n from-size)))))
1175       (:big-endian
1176        (loop for bytes on bytes by #'(lambda (x) (nthcdr factor x))
1177            collect (loop for n from 0 below factor
1178                        as sub-byte = (or (nth (- factor 1 n) bytes) 0)
1179                        summing (ash sub-byte (* n from-size))))))))