-
Notifications
You must be signed in to change notification settings - Fork 0
/
binary-data.lisp
158 lines (123 loc) · 5.53 KB
/
binary-data.lisp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
(in-package :com.gigamonkeys.binary-data)
(defconstant +null+ (code-char 0))
(defun read-null-terminated-ascii (in)
(with-output-to-string (s)
(loop for char = (code-char (read-byte in))
until (char= char +null+) do (write-char char s))))
(defun write-null-terminated-ascii (string out)
(loop for char across string
do (write-byte (char-code char) out))
(write-byte (char-code +null+) out))
(defun as-keyword (sym) (intern (string sym) :keyword))
(defun slot->defclass-slot (spec)
(let ((name (first spec)))
`(,name :initarg ,(as-keyword name) :accessor ,name)))
(defmacro define-binary-class (name slots)
`(defclass ,name ()
,(mapcar #'slot->defclass-slot slots)))
(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 of the given type from the stream."))
(defgeneric read-object (object stream)
(:method-combination progn :most-specific-last)
(:documentation "Read slots of an object from stream."))
(defgeneric write-object (object stream)
(:method-combination progn :most-specific-last)
(:documentation "Write slots of the object to the stream."))
(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 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 normalize-slot-spec (spec)
(list (first spec) (mklist (second spec))))
(defun mklist (x) (if (listp x) x (list x)))
(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)
(nconc (mapcan #'all-slots superclasses) (mapcar #'first slots)))
(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)))
(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)
(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 :dispatch form."))
,@(mapcan #'slot->keyword-arg slots))))
(read-object ,objectvar ,streamvar)
,objectvar))))))
(defmacro define-binary-type (name (&rest args) &body spec)
(ecase (length spec)
(1
(with-gensyms (type stream value)
(destructuring-bind (derived-from &rest derived-args) (mklist (first spec))
`(progn
(defmethod read-value ((,type (eql ',name)) ,stream &key ,@args)
(read-value ',derived-from ,stream ,@derived-args))
(defmethod write-value ((,type (eql ',name)) ,stream ,value &key ,@args)
(write-value ',derived-from ,stream ,value ,@derived-args))))))
(2
(with-gensyms (type)
`(progn
,(destructuring-bind ((in) &body body) (rest (assoc :reader spec))
`(defmethod read-value ((,type (eql ',name)) ,in &key ,@args)
,@body))
,(destructuring-bind ((out value) &body body) (rest (assoc :writer spec))
`(defmethod write-value ((,type (eql ',name)) ,out ,value &key ,@args)
,@body)))))))
(defvar *in-progress-objects* nil)
(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)))
(defun current-binary-object () (first *in-progress-objects*))
(defun parent-of-type (type)
(find-if #'(lambda (x) (typep x type)) *in-progress-objects*))