125 lines
4.0 KiB
Common Lisp
125 lines
4.0 KiB
Common Lisp
;;;; binary.lisp
|
|
|
|
(in-package #:binary)
|
|
|
|
;;; "binary" goes here. Hacks and glory await!
|
|
|
|
|
|
(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))))
|
|
`(export (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 size)
|
|
`(progn
|
|
(defconstant ,const-name ,size)
|
|
(define-unsigned-reader ,const-name)
|
|
(define-unsigned-writer ,const-name)))
|
|
|
|
(defunsigned U32 4)
|
|
(defunsigned U16 2)
|
|
(defunsigned U8 1)
|
|
|
|
(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 size)
|
|
`(progn
|
|
(defconstant ,const-name ,size)
|
|
(define-signed-reader ,const-name)
|
|
(define-signed-writer ,const-name)))
|
|
|
|
(defsigned I32 4)
|
|
(defsigned I16 2)
|
|
(defsigned I8 1)
|