2009年11月22日日曜日

Gray Streamを使う

Common Lispのwith-output-to-stringやwith-input-from-stringは便利なのだが、文字列ストリームは名前の通り文字しか読み書きできない。

バイナリの読み書きもファイルを開かずに、このように書いてみたい。

(with-binary-stream (s)
(write-byte 2 s)
(write-byte 3 s)
(values (read-byte s) (read-byte s)))

少し調べてみたら、Gray Streamというものを使って実装している人がいた。単純なものなら結構簡単に書けそうだ。

良い機会なので、名前だけは聞いたことのあったGray Streamとやらを利用したコードを書いてみた。

(defpackage virtual-binary-stream
(:use :cl :sb-gray)
(:nicknames :vbstream)
(:export virtual-binary-stream
make-virtual-binary-stream
with-virtual-binary-stream))

(in-package virtual-binary-stream)

;;;Queueの実装:carがlistの先頭cons,cdrが最終Consを指す
;;;ANSI Common Lispより
(defun make-queue ()
(cons nil nil))
(defun enqueue (obj q)
(if (null (car q))
(setf (cdr q)
(setf (car q) (list obj)))
(setf (cdr (cdr q)) (list obj)
(cdr q) (cdr (cdr q))))
(car q))
(defun dequeue (q)
(pop (car q)))

(defclass virtual-binary-stream (fundamental-stream)
((queue :initform (make-queue) :accessor queue-of)))

(defmethod stream-element-type ((stream virtual-binary-stream))
'virtual-binary)

(defmethod close ((stream virtual-binary-stream) &key abort)
(declare (ignore abort))
(setf (queue-of stream) nil))

(defmethod stream-read-byte ((stream virtual-binary-stream))
(dequeue (queue-of stream)))

(defmethod stream-read-char ((stream virtual-binary-stream))
(code-char (stream-read-byte stream)))

(defmethod stream-write-byte ((stream virtual-binary-stream) integer)
(enqueue integer (queue-of stream)))

(defmethod stream-write-char ((stream virtual-binary-stream) character)
(enqueue (char-code character) (queue-of stream)))

(defun make-virtual-binary-stream (&optional (initial-element-list nil))
(let ((stream (make-instance 'virtual-binary-stream)))
(dolist (i initial-element-list)
(stream-write-byte stream i))
stream))

(defmacro with-virtual-binary-stream
((stream &optional initial-element-list) &body body)
`(let ((,stream (make-virtual-binary-stream ,initial-element-list)))
,@body))

キューはANSI Common Lispに書いてあったものを使った。リストでキューを表現する方法が格好良い。

これでwrite-byte,read-byteを使えるようになった。ついでにread-char,write-charも書いた。

>(vbstream:with-virtual-binary-stream (s)
(write-byte 2 s)
(write-char #\A s)
(list (read-byte s) (read-byte s)))
(2 65)

0 件のコメント:

コメントを投稿