source: branches/home/psmith/restructure/src/buffer/buffer.lisp

Last change on this file was 100, checked in by psmith, 18 years ago

Fixed large packet problems: calculated header size correctly; use buffer-pointer to take into account buffer-position on write and moved external format to UTF-8

File size: 12.3 KB
Line 
1#|
2Copyright (c) 2006 Risto Laakso
3All rights reserved.
4
5Redistribution and use in source and binary forms, with or without
6modification, are permitted provided that the following conditions
7are met:
81. Redistributions of source code must retain the above copyright
9   notice, this list of conditions and the following disclaimer.
102. Redistributions in binary form must reproduce the above copyright
11   notice, this list of conditions and the following disclaimer in the
12   documentation and/or other materials provided with the distribution.
133. The name of the author may not be used to endorse or promote products
14   derived from this software without specific prior written permission.
15
16THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26|#
27
28(in-package :nio-buffer)
29
30
31;;NIO-buffers - to simplify operations on buffers.
32;;
33;; Will support direct(external to the vm) and non-direct buffers
34;;
35
36(declaim (optimize (debug 3) (speed 3) (space 0)))
37
38(defclass buffer ()
39  ((capacity     :initarg :capacity
40                 :initform 0
41                 :accessor buffer-capacity
42                 :documentation "Buffer capacity")
43   (limit        :initarg :limit
44                 :initform 0
45                 :accessor buffer-limit
46                 :documentation "Index of first element the should *not* be read or written 0 <= limit <= capacity")
47   (position     :initarg :position
48                 :initform 0
49                 :accessor buffer-position
50                 :documentation "Index of next element to be read/written 0<=position<=limit")
51   (mark         :initarg :position
52                 :initform 0
53                 :documentation "A marked position")
54   (buf          :initarg :buf
55                 :accessor buffer-buf)))
56
57
58;;Utils by slyrus (http://paste.lisp.org/display/11149)
59(defun hex-dump-byte (address)
60  (format nil "~2,'0X" (byte-value address)))
61
62(defun byte-value (address)
63  (sb-alien:deref
64   (sb-alien:sap-alien
65    (sb-alien::int-sap address)
66    (* (sb-alien:unsigned 8)))))
67
68(defun hex-dump-memory (start-address length)
69  (loop for i from start-address below (+ start-address length)
70     collect (format nil (hex-dump-byte i))))
71
72;;-- end utils
73
74(defun get-readable-char (char-code)
75  (if (<= char-code 32)
76      (code-char 46)
77      (if (> char-code 127)
78          (code-char 46)
79          (code-char char-code))))
80
81(defun pretty-hex-dump (start-address length)
82;  (format t "start: ~A length ~A~%" start-address length)
83  (with-output-to-string (str)
84    (let ((rows (floor (/ length 16))))
85;      (format t "rows: ~A remainder ~A~%" rows remainder)
86      (dotimes (row-index (+ 1 rows))
87        (format str "~A~%"
88                (with-output-to-string (readable)
89                  (dotimes (column-index 16)
90                    (let ((address (+ start-address (* row-index 16) column-index)))
91                                        ;           (format t "Current address : ~A~%" address)
92                      (if (>= address (+ start-address length)) 
93                          (progn 
94                            (format str (if (eql column-index 7) "   " ".. "))
95                            (format readable "."))
96                          (progn 
97                            (format str (if (eql column-index 7) "~A   " "~A ") (hex-dump-byte address))
98                            (format readable "~A" (get-readable-char (byte-value address)))))))))))))
99
100(defun make-uint8-seq (size)
101  "Make uint8 sequence."
102  (make-sequence '(vector (unsigned-byte 8)) size :initial-element 0))
103
104;;A buffer that deals with bytes
105(defclass byte-buffer (buffer)())
106
107(defun byte-buffer (capacity)
108  (make-instance 'byte-buffer :capacity capacity :limit capacity :position 0 :buf (cffi:foreign-alloc :uint8 :count capacity)))
109
110;Gets a pointer to the address in the native memory of the position index
111(defmethod buffer-pointer ((bb byte-buffer))
112  (cffi:make-pointer (+ (cffi:pointer-address (buffer-buf bb)) (buffer-position bb))))
113
114(defmethod print-object ((byte-buffer byte-buffer) stream)
115  (with-slots (capacity position limit buf) byte-buffer
116    (format stream "<byte-buffer :capacity ~A :position ~A :limit ~A :buf ~%~A>~%" capacity position limit (if buf (pretty-hex-dump (cffi:pointer-address buf) limit) nil))))
117
118(defmethod free-buffer((byte-buffer byte-buffer))
119  (with-slots (capacity position limit buf) byte-buffer
120    (cffi:foreign-free buf)
121    (setf buf NIL)
122    (setf capacity 0)
123    (setf limit 0)
124    (setf position 0)))
125
126;bytes between the position and the limit
127(defmethod remaining((byte-buffer byte-buffer))
128  (with-slots (position limit) byte-buffer
129    (- limit position)))
130
131;bytes between the current position and capacity
132(defmethod remaining-capacity((byte-buffer byte-buffer))
133  (with-slots (position capacity) byte-buffer
134    (- capacity position)))
135
136(defmethod inc-position((byte-buffer byte-buffer) num-bytes)
137  (with-slots (position limit) byte-buffer
138    (let ((new-pos (+ position num-bytes)))
139      (assert (<= new-pos limit))
140      (setf position new-pos))))
141
142(defmethod flip((byte-buffer byte-buffer))
143  :documentation "make buffer ready for relative get operation"
144  (with-slots (position limit) byte-buffer
145    (setf limit position)
146    (setf position 0)))
147
148(defmethod unflip((byte-buffer byte-buffer))
149  :documentation "make buffer ready for relative write operation. Used on partial read to reset the buffer for writing"
150  (with-slots (position limit capacity) byte-buffer
151    (setf position limit)
152    (setf limit capacity)))
153
154(defmethod clear((byte-buffer byte-buffer))
155  :documentation "Reset the position to 0 and the limit to capacity"
156  (with-slots (position limit capacity) byte-buffer
157    (setf limit capacity)
158    (setf position 0)
159    byte-buffer))
160
161(defmethod compact((byte-buffer byte-buffer))
162  :documentation "copy remaining bytes to the beginning of this buffer and set position to number of bytes copied (ready for a new put"
163  (with-slots (buf position limit capacity) byte-buffer
164    (let ((remaining (remaining byte-buffer)))
165      (%memcpy buf (cffi:make-pointer (+ (cffi:pointer-address buf) position)) remaining)
166      (setf position remaining)
167      (setf limit capacity))))
168
169(defmethod mark((bb byte-buffer))
170  :documentation "mark a position in the buffer for subsequent use with reset"
171  (with-slots (position mark) bb
172    (setf mark position)))
173
174(defmethod reset((bb byte-buffer))
175  (with-slots (position mark) bb
176    (setf position mark)))
177 
178
179
180;Used to signal either an attempt has been made to write data to a buffer that is too small using a write (overflow)
181; or an incomming packet doesn't have enough room to fit
182(define-condition buffer-too-small-error (error) 
183  ((recommended-size :initarg :recommended-size
184                     :accessor recommended-size)))
185
186(defun buffer-too-small-error(recommended-size)
187  (make-instance 'buffer-too-small-error :recommended-size recommended-size))
188
189
190
191;reads bytes from byte-buffer and returns a vector (unsigned-byte 8)
192(defmethod bytebuffer-read-vector((bb byte-buffer) &optional (num-bytes-to-read (remaining bb)))
193  (let ((vec  (make-uint8-seq num-bytes-to-read)))
194    (with-slots (buf position) bb
195      (inc-position bb (cffi:mem-read-vector vec buf :unsigned-char num-bytes-to-read position)))
196    vec))
197
198; Read bytes from bytebuffer abd return a string using the supplied decoding
199;TODO move octets-to-string into nio-compat
200(defmethod bytebuffer-read-string((bb byte-buffer) &optional (num-bytes-to-read (remaining bb)) (external-format :utf-8))
201  (sb-ext:octets-to-string (bytebuffer-read-vector bb num-bytes-to-read) :external-format external-format))
202
203; Read a byte from bytebuffer and return it incrementing the byte-buffers position
204(defmethod bytebuffer-read-8((bb byte-buffer))
205  (let ((val (cffi:mem-ref (cffi:make-pointer (+ (cffi:pointer-address (buffer-buf bb)) (buffer-position bb))) :unsigned-char )))
206    (inc-position bb 1)
207    val))
208
209; Read a 32 bit integer from bytebuffer and return it incrementing the byte-buffers position
210(defmethod bytebuffer-read-32((bb byte-buffer))
211  (let ((val (cffi:mem-ref (cffi:make-pointer (+ (cffi:pointer-address (buffer-buf bb)) (buffer-position bb))) :unsigned-int )))
212    (inc-position bb 4)
213    val))
214
215;write an 8 bit value and up date position in buffer
216(defmethod bytebuffer-write-8 ((bb byte-buffer) value)
217  (when (< (remaining bb) 1) (error 'buffer-too-small-error))
218  (setf (cffi:mem-ref (buffer-buf bb) :unsigned-char (buffer-position bb)) value)
219  (inc-position bb 1))
220
221;write a 32 bit value and up date position in buffer
222(defmethod bytebuffer-write-32 ((bb byte-buffer) value)
223  (when (< (remaining bb) 4) (error 'buffer-too-small-error))
224  (setf (cffi:mem-ref (buffer-buf bb) :unsigned-int (buffer-position bb)) value)
225  (inc-position bb 4))
226
227;insert an 8 bit value
228(defmethod bytebuffer-insert-8 ((bb byte-buffer) value byte-position)
229  (setf (cffi:mem-ref (buffer-buf bb) :unsigned-char byte-position) value))
230
231;insert a 32 bit value
232(defmethod bytebuffer-insert-32 ((bb byte-buffer) value byte-position)
233  (setf (cffi:mem-ref (buffer-buf bb) :unsigned-int byte-position) value))
234
235
236;; Write bytes from vector vec to bytebuffer
237(defmethod bytebuffer-write-vector((bb byte-buffer) vec)
238  :documentation "Returns number of bytes written to bytebuffer"
239#+nio-debug    (format t "bytebuffer-write-vector - called with ~A ~A"bb vec)
240    (when (< (remaining bb) (length vec)) (error 'buffer-too-small-error))
241    (let ((bytes-written (cffi:mem-write-vector vec (buffer-buf bb) :unsigned-char (length vec) (buffer-position bb))))
242#+nio-debug       (format t "bytebuffer-write-vector -  byteswritten: ~A~%" bytes-written)
243      (inc-position bb bytes-written)
244      bytes-written))
245
246
247;; Writes data from string str to bytebuffer using specified encoding
248;TODO move string-to-octets into nio-compat
249(defmethod bytebuffer-write-string((bb byte-buffer) str &optional (external-format :utf-8))
250  :documentation "Returns number of bytes written to bytebuffer"
251  (bytebuffer-write-vector bb (sb-ext:string-to-octets str :external-format external-format)))
252
253
254
255(defmethod copy-buffer ((old byte-buffer) (new byte-buffer))
256  (assert (<= (buffer-capacity old) (buffer-capacity new)))
257  (%memcpy (buffer-buf new) (buffer-buf old) (buffer-capacity old))
258  (setf (buffer-position new) (buffer-position old))
259  (setf (buffer-limit new) (buffer-capacity new)))
260
261;void *memcpy(void *dest, const void *src, size_t n);
262(cffi:defcfun ("memcpy" %memcpy) :pointer
263  (dest :pointer)
264  (src :pointer)
265  (len :int))
266
267;void *memset(void *s, int c, size_t n);
268(cffi:defcfun ("memset" %memset) :pointer
269  (buffer :pointer)
270  (byte :int)
271  (len :int))
272
273(defun test-buffer()
274  (let ((mybuf (byte-buffer 32)))
275    (format t "Mybuf: ~A~%" mybuf)
276    (assert (eql 32 (remaining mybuf)))
277    (inc-position mybuf 4)
278    (assert (eql 28 (remaining mybuf)))
279    (format t "Mybuf: ~A~%" mybuf)
280
281 (%memset (buffer-buf mybuf) 78 4)
282
283    (format t "Mybuf (after memset): ~A~%" mybuf)
284    (flip mybuf)
285    (format t "Mybuf (after flip): ~A~%" mybuf)
286
287
288    (format t "Remaining ~A~%" (remaining mybuf))
289
290    (format t "mybuf string ~A~%" (bytebuffer-read-string mybuf))
291
292    (format t "Mybuf (after get-string): ~A~%" mybuf)
293
294    (let ((test-copy (byte-buffer 1024)))
295      (copy-buffer mybuf test-copy)
296      (format t "new copy: ~A~%" test-copy))
297
298    (setf (buffer-position mybuf) 0)
299    (format t "bytebuffer-read-32 ~X~%" (bytebuffer-read-32 mybuf))
300
301    (format t "Mybuf (after clear): ~A~%" (clear mybuf))
302
303    ;test accessors
304    (setf (buffer-position mybuf) 11)
305    (bytebuffer-write-8 mybuf 243)
306    (assert (eql (buffer-position mybuf) 12))
307    (setf (buffer-position mybuf) 11)
308    (assert (eql (bytebuffer-read-8 mybuf) 243))
309    (format t "Mybuf (after r/w 8bit): ~A~%" mybuf)
310
311    (setf (buffer-position mybuf) 11)
312    (bytebuffer-write-32 mybuf 2147483649)
313    (assert (eql (buffer-position mybuf) 15))
314    (setf (buffer-position mybuf) 11)
315    (assert (eql (bytebuffer-read-32 mybuf) 2147483649))
316    (format t "Mybuf (after r/w 32bit): ~A~%" mybuf)
317
318    (setf (buffer-position mybuf) 11)
319
320    (compact mybuf)
321    (format t "Mybuf (after compact): ~A~%" mybuf)
322    (assert (eql (buffer-position mybuf) (- 32 11)))
323    (flip mybuf)
324    (format t "Mybuf (flip): ~A~%" mybuf)
325    (assert (eql (bytebuffer-read-32 mybuf) 2147483649))
326
327    (free-buffer mybuf)
328    (format t "Mybuf after free: ~A~%" mybuf)))
329
Note: See TracBrowser for help on using the repository browser.