[Small-cl-src] Re: bit I/O
Zach Beane
xach at xach.com
Fri Aug 27 17:31:34 UTC 2004
(defpackage :bitstream
(:use :cl))
(in-package :bitstream)
#|
An output bitstream.
The idea here is that there are three-ish values important when
writing n bit chunks to a stream.
- a byte, acting as a buffer, accumulating the bits being written
- a counter to indicate when the byte buffer is full
- a stream to which full bytes are written
However, you can store the first two in a single unsigned 16-bit
value. Then you can store the 16-bit value and the stream in a cons.
An empty byte buffer has the value #x00FF. Bits are added to the
buffer by doing a LOGAND with a left shift. When the highest bit is
set, the byte is written out and reset.
The number of bits written to the byte buffer so far is:
(logcount (logand #xFF00 buffer))
To finish writing a partially filled buffer, you can AND the high bits
with the low bits to get the proper value:
(logand (ldb (byte 8 8) buffer) (ldb (byte 8 0) buffer))
FWIW, I don't know if this arrangement of the buffer is actually
clever or useful. I spent a lot of time staring at the disassembly, so
maybe it fried my brain.
|#
(defun make-output-bitstream (stream &key
(current-byte 0)
(bits-left 8))
(assert (output-stream-p stream))
(assert (subtypep (stream-element-type stream) '(unsigned-byte 8)))
(cons stream (logior current-byte
(ash #xFF (- 8 bits-left)))))
(declaim (inline iostream))
(defun iostream (bitstream)
(car bitstream))
(declaim (inline buffer))
(defun buffer (bitstream)
(the (unsigned-byte 16) (cdr bitstream)))
(declaim (inline (setf buffer)))
(defun (setf buffer) (new-value bitstream)
(declare (type (unsigned-byte 16) new-value))
(setf (cdr bitstream) new-value))
(declaim (inline write-bit))
(defun write-bit (bit bitstream)
"Write the single BIT to BITSTREAM."
(declare (bit bit)
(optimize (speed 3) (safety 0)))
(let ((n (buffer bitstream)))
(cond ((logtest (ash 1 15) n)
(write-byte (logand #xFF n) (iostream bitstream))
(setf (buffer bitstream) (logior (ash #x00FF 1) bit)))
(t
(setf (buffer bitstream) (logior (ash n 1) bit))))))
(defun write-bits (integer width bitstream)
"Write the leftmost WIDTH bits of INTEGER to BITSTREAM."
(declare (integer integer)
(type (unsigned-byte 29) width)
(optimize (speed 3) (safety 0)))
(when (minusp integer)
(setf integer (ldb (byte width 0) integer)))
(loop for i downfrom (1- width) to 0
do (write-bit (ldb (byte 1 i) integer) bitstream)))
(defun finish-write (bitstream)
(declare (optimize (speed 3) (safety 0)))
(let* ((n (buffer bitstream))
(final-byte (logand (ldb (byte 8 8) n)
(ldb (byte 8 0) n))))
(write-byte final-byte (iostream bitstream))
(setf (buffer bitstream) #xFF00)))
(defmacro with-output-bitstream ((bitstream stream
&key (current-byte 0) (bits-left 8))
&body body)
"Evaluate BODY with a newly-created output bitstream bound to
BITSTREAM. Pending output is flushed at the end of evaluation."
(let ((-current-byte- (gensym))
(-bits-left- (gensym)))
`(let* ((,-current-byte- ,current-byte)
(,-bits-left- ,bits-left)
(,bitstream (make-output-bitstream ,stream
:current-byte ,-current-byte-
:bits-left ,-bits-left-)))
(unwind-protect
(progn , at body)
(finish-write ,bitstream)))))
(defun test-packed (file count value width)
(with-open-file (stream file
:direction :output
:if-exists :supersede
:element-type '(unsigned-byte 8))
(with-output-bitstream (bitstream stream)
(dotimes (i count)
(write-bits value width bitstream)))))
More information about the Small-cl-src
mailing list