binary/binary.lisp

130 lines
4.0 KiB
Common Lisp

;;;; binary.lisp
(in-package #:binary)
;;; "binary" goes here. Hacks and glory await!
(defconstant U32 4)
(defconstant I32 4)
(defconstant U16 2)
(defconstant I16 2)
(defconstant U8 1)
(defconstant I8 1)
(defun octets (stream length)
"Read length bytes from the stream."
(let ((bin (make-array length :element-type '(unsigned-byte 8))))
(read-sequence bin stream)
bin))
(defun uint-from-bytes (bin &key (endian :little))
"Produce an unsigned integer from the binary array input."
(let ((bin (cond
((eql endian :little) bin)
((eql endian :big) (reverse bin))
(t (error "Invalid endian specification."))))
(n 0))
(dotimes (i (length bin))
(setf n (+ n (ash (aref bin i) (* i 8)))))
n))
(defun uint-to-bytes (n size &key (endian :little))
"Produce a binary array of size bytes from the unsigned integer
provided."
(let ((bin (make-array size :element-type '(unsigned-byte 8))))
(dotimes (i size)
(setf (aref bin i)
(logand 255 (ash n (- 0 (* i 8))))))
(cond
((eql endian :little) bin)
((eql endian :big) (nreverse bin))
(t (error "Invalid endian specification.")))))
(defun read-uint (stream size &key (endian :little))
(uint-from-bytes (octets stream size) :endian endian))
(defun write-uint (stream n size &key (endian :little))
(write-sequence (uint-to-bytes n size :endian endian) stream))
(defmacro define-unsigned-reader (const-name)
(let ((docstring
(format nil
"Read an unsigned ~A-bit integer from a stream."
(subseq (format nil "~A" const-name) 1))))
`(defun ,(intern (format nil "READ-~A" const-name))
(stream &key (endian :little))
,docstring
(read-uint stream ,const-name :endian endian))))
(defmacro define-unsigned-writer (const-name)
(let ((docstring
(format nil
"Write an unsigned ~A-bit integer to a stream."
(subseq (format nil "~A" const-name) 1))))
`(defun ,(intern (format nil "WRITE-~A" const-name))
(stream n &key (endian :little))
,docstring
(write-uint stream n ,const-name :endian endian))))
(defmacro defunsigned (const-name)
`(progn
(define-unsigned-reader ,const-name)
(define-unsigned-writer ,const-name)))
(defunsigned U32)
(defunsigned U16)
(defunsigned U8)
(defun twos-complement (n size)
(if (zerop (logand (ash 1 (* (- size 1) 8)) n))
n
(- n (ash 1 (* size 8)))))
(defun int-from-bytes (bin &key (endian :little))
"Produce a signed integer from the binary array input."
(twos-complement (uint-from-bytes bin :endian endian)
(length bin)))
(defun int-to-bytes (n size &key (endian :little))
"Produce a binary array of size bytes from the provided signed
integer."
(uint-to-bytes (twos-complement n size) size :endian endian))
(defun read-int (stream size &key (endian :little))
(int-from-bytes (octets stream size)
:endian endian))
(defun write-int (stream n size &key (endian :little))
(write-sequence (int-to-bytes n size :endian endian)
stream))
(defmacro define-signed-reader (const-name)
(let ((docstring
(format nil
"Read a signed ~A-bit integer from a stream."
(subseq (format nil "~A" const-name) 1))))
`(defun ,(intern (format nil "READ-~A" const-name))
(stream &key (endian :little))
,docstring
(read-int stream ,const-name :endian endian))))
(defmacro define-signed-writer (const-name)
(let ((docstring
(format nil
"Write a signed ~A-bit integer to a stream."
(subseq (format nil "~A" const-name) 1))))
`(defun ,(intern (format nil "WRITE-~A" const-name))
(stream n &key (endian :little))
,docstring
(write-int stream n ,const-name :endian endian))))
(defmacro defsigned (const-name)
`(progn
(define-signed-reader ,const-name)
(define-signed-writer ,const-name)))
(defsigned I32)
(defsigned I16)
(defsigned I8)