changeset 291: |
a0dfde3cb3c4 |
child: |
e2e5c4831389 |
author: |
Richard Westhaver <ellis@rwest.io> |
date: |
Mon, 22 Apr 2024 23:14:47 -0400 |
permissions: |
-rw-r--r-- |
description: |
begin :STD refactor |
1 ;;; std/bit.lisp --- Bit manipulation 5 ;; CMUCL doc: https://www.cs.cmu.edu/Groups/AI/html/cltl/clm/node132.html 7 ;; quick primer: https://cp-algorithms.com/algebra/bit-manipulation.html 13 ;; Bytes aren't necessarily 8 bits wide in Lisp. OCTET is always 8 15 (deftype octet () '(unsigned-byte 8)) 16 (deftype octet-vector (&optional length) 17 `(simple-array octet (,length))) 20 (defun make-bits (length &rest args) 21 (apply #'make-array length (nconc (list :element-type 'bit) args))) 23 ;; https://graphics.stanford.edu/~seander/bithacks.html 24 ;; http://www.azillionmonkeys.com/qed/asmexample.html 25 (defun haipart (n count) 26 (declare (fixnum n count)) 29 (ldb (byte (- count) 0) x) 30 (ldb (byte count (max 0 (- (integer-length x) count))) 37 "compute the sign bit of a fixnum. If N < 0 return -1 else return 0." 39 (ash n (- 0 (integer-length n)))) 43 (defun different-signs-p (x y) 44 "Return non-nil iff x and y have opposite signs." 45 (declare (fixnum x y) (optimize (speed 1))) 49 (defun mortify-bits (x y) 50 "Interleave the bits of two numbers (Mortan numbers)." 53 ;; (loop for i across (integer-length) 55 ;; ;; z |= (x & 1U << i) << i | (y & 1U << i) << (i + 1); 60 (defun int-list-bits (n) 63 (dotimes (position (integer-length n) bits) 64 (push (ldb (byte 1 position) n) bits)))) 66 (defun int-bit-vector (n) 68 (let ((bits (make-array 0 :element-type 'bit :adjustable t :fill-pointer t))) 69 (dotimes (position (integer-length n) bits) 70 (vector-push-extend (ldb (byte 1 position) n) bits)))) 72 (defun aref-bit (octets idx) 73 (declare (octet-vector octets) (fixnum idx)) 74 (multiple-value-bind (octet-idx bit-idx) 77 (aref octets octet-idx)))) 79 (defun make-bit-vector (size &optional (fill 0)) 80 "Make a BIT-VECTOR with SIZE and initial-element FILL which must be a 81 BIT 0|1. Note that this representation is not as useful as you might 82 think - bit-vectors don't have a direct mapping to integers/fixnums -- 83 they are vectors (AKA arrays) first, and bits second. Attempting to 84 perform bitwise-ops ends up being very inefficient so whenever 85 possible, stick with fixnums and use LOG* functions." 87 (make-array size :initial-element fill :adjustable nil :element-type 'bit)) 89 ;; simple setter/getter for integer bits 90 (define-setf-expander logbit (index place &environment env) 91 (multiple-value-bind (temps vals stores store-form access-form) 92 (get-setf-expansion place env) 95 (stemp (first stores))) 99 `(let ((,stemp (dpb ,store (byte 1 ,i) ,access-form)) 103 `(logbit ,i ,access-form))))) 105 (defun logbit (idx n) 106 (declare (fixnum idx n)) 107 (ldb (byte 1 idx) n)) 111 ;; see https://github.com/marcoheisig/bitfield 113 ;; A bitfield is a simple, efficient mechanism for storing multiple 114 ;; discrete states into a single non-negative integer. 117 "A bitfield is a non-negative integer that efficiently encodes 118 information about some booleans, enumerations, or small integers." 122 (defgeneric bitfield-slot-name (bitfield-slot) 124 "Returns a symbol that is the name of the bitfield slot.")) 126 (defgeneric bitfield-slot-start (bitfield-slot) 128 "Returns the position of the first bit of this slot in the bitfield.")) 130 (defgeneric bitfield-slot-end (bitfield-slot) 132 "Returns the position right after the last bit of this slot in the bitfield.")) 134 (defgeneric bitfield-slot-size (bitfield-slot) 136 "Returns an unsigned byte that is the number of distinct states of the slot.")) 138 (defgeneric bitfield-slot-initform (bitfield-slot) 140 "Returns a form that produces the initial value for that slot.")) 142 (defgeneric bitfield-slot-pack (bitfield-slot value-form) 144 "Takes a form that produces a value and turns it into a form that produces 145 a non-negative integer representing that value.")) 147 (defgeneric bitfield-slot-unpack (bitfield-slot value-form) 149 "Take a form that produces a value that is encoded as a non-negative 150 integer (as produced by BITFIELD-SLOT-PACK), and turn it into a form that 151 produces the decoded value.")) 153 (defgeneric parse-atomic-bitfield-slot-specifier 154 (specifier &key initform) 156 "Parses an atomic bitfield slot specifier, i.e., a bitfield slot 157 specifier that is not a list. Returns three values: 159 1. A designator for a bitfield slot class. 161 2. The size of the bitfield slot. 163 3. A list of additional arguments that will be supplied to MAKE-INSTANCE 164 when creating the bitfield slot instance.")) 166 (defgeneric parse-compound-bitfield-slot-specifier 167 (specifier arguments &key initform) 169 "Parses a compount bitfield slot specifier, i.e., a bitfield slot 170 specifier that is a list. The SPECIFIER is the CAR of that list and the 171 ARGUMENTS are the CDR of that list. Returns three values: 173 1. A designator for a bitfield slot class. 175 2. The size of the bitfield slot. 177 3. A list of additional arguments that will be supplied to MAKE-INSTANCE 178 when creating the bitfield slot instance.")) 180 (defclass bitfield-slot () 181 ((%name :initarg :name :reader bitfield-slot-name) 182 (%initform :initarg :initform :reader bitfield-slot-initform) 183 (%start :initarg :start :reader bitfield-slot-start) 184 (%end :initarg :end :reader bitfield-slot-end) 185 (%size :initarg :size :reader bitfield-slot-size))) 188 (defclass bitfield-boolean-slot (bitfield-slot) 191 (defmethod bitfield-slot-pack ((slot bitfield-boolean-slot) value-form) 192 `(if ,value-form 1 0)) 194 (defmethod bitfield-slot-unpack ((slot bitfield-boolean-slot) value-form) 195 `(ecase ,value-form (0 nil) (1 t))) 197 (defmethod parse-atomic-bitfield-slot-specifier 198 ((specifier (eql 'boolean)) &key (initform 'nil)) 199 (values 'bitfield-boolean-slot 201 `(:initform ,initform))) 204 (defclass bitfield-integer-slot (bitfield-slot) 208 :reader bitfield-integer-slot-offset))) 210 (defmethod bitfield-slot-pack ((slot bitfield-integer-slot) value-form) 211 (let ((offset (bitfield-integer-slot-offset slot)) 212 (size (bitfield-slot-size slot))) 213 `(the (integer 0 (,size)) 214 (- (the (integer ,offset (,(+ offset size))) ,value-form) 217 (defmethod bitfield-slot-unpack ((slot bitfield-integer-slot) value-form) 218 (let ((offset (bitfield-integer-slot-offset slot)) 219 (size (bitfield-slot-size slot))) 220 `(the (integer ,offset (,(+ offset size))) 221 (+ ,value-form ,offset)))) 223 (defmethod parse-atomic-bitfield-slot-specifier 224 ((specifier (eql 'bit)) &key (initform '0)) 225 (values 'bitfield-unsigned-byte-slot 227 `(:offset 0 :initform ,initform))) 229 (defmethod parse-compound-bitfield-slot-specifier 230 ((specifier (eql 'unsigned-byte)) arguments &key (initform '0)) 231 (destructuring-bind (bits) arguments 232 (check-type bits unsigned-byte) 233 (values 'bitfield-integer-slot 235 `(:offset 0 :initform ,initform)))) 237 (defmethod parse-compound-bitfield-slot-specifier 238 ((specifier (eql 'signed-byte)) arguments &key (initform '0)) 239 (destructuring-bind (bits) arguments 240 (check-type bits unsigned-byte) 241 (values 'bitfield-integer-slot 243 `(:offset ,(- (expt 2 (1- bits))) :initform ,initform)))) 245 (defmethod parse-compound-bitfield-slot-specifier 246 ((specifier (eql 'integer)) bounds &key (initform nil initform-supplied-p)) 248 (error "Invalid integer bitfield slot specifier: ~S" 249 `(integer ,@bounds)))) 250 (unless (typep bounds '(cons t (cons t null))) 252 (destructuring-bind (lo hi) bounds 253 (let* ((start (typecase lo 263 (size (- end start))) 266 (values 'bitfield-integer-slot 268 `(:offset ,start :initform ,(if initform-supplied-p initform start))))))) 271 (defclass bitfield-member-slot (bitfield-slot) 275 :reader bitfield-member-slot-objects))) 277 (defmethod bitfield-slot-pack ((slot bitfield-member-slot) value-form) 279 ,@(loop for key in (bitfield-member-slot-objects slot) 281 collect `((,key) ,value)))) 283 (defmethod bitfield-slot-unpack ((slot bitfield-member-slot) value-form) 285 ,@(loop for key from 0 286 for value in (bitfield-member-slot-objects slot) 287 collect `((,key) ',value)))) 289 (defmethod parse-compound-bitfield-slot-specifier 290 ((specifier (eql 'member)) objects &key (initform `',(first objects))) 291 (values 'bitfield-member-slot 293 `(:initform ,initform :objects ,objects))) 296 ;; The position right after the last slot that has been parsed so far. 297 (defvar *bitfield-position*) 299 (defun parse-bitfield-slot (slot) 300 (destructuring-bind (slot-name slot-specifier &rest rest) slot 301 (check-type slot-name symbol) 302 (multiple-value-bind (slot-class size args) 303 (if (consp slot-specifier) 304 (apply #'parse-compound-bitfield-slot-specifier 308 (apply #'parse-atomic-bitfield-slot-specifier 311 (apply #'make-instance slot-class 314 :start *bitfield-position* 315 :end (incf *bitfield-position* (integer-length (1- size))) 318 (defmacro define-bitfield (name &body slots) 319 "Defines an encoding of enumerable properties like booleans, 320 integers or finite sets as a single non-negative integer. 322 For a supplied bitfield name NAME, and for some slot definitions of the 323 form (SLOT-NAME TYPE &KEY INITFORM &ALLOW-OTHER-KEYS), this macro defines 324 the following functions: 326 1. A constructor named MAKE-{NAME}, that takes one keyword argument per 327 SLOT-NAME, similar to the default constructor generated by DEFSTRUCT. 328 It returns a bitfield whose entries have the values indicated by the 329 keyword arguments, or the supplied initform. 331 2. A clone operation named CLONE-{NAME}, that takes an existing bitfield 332 and one keyword argument per SLOT-NAME. It returns a copy of the 333 existing bitfield, but where each supplied keyword argument supersedes 334 the value of the corresponding slot. 336 3. A reader function named {NAME}-{SLOT-NAME} for each slot. 338 In addition to these functions, NAME is defined as a suitable subtype of 341 This macro supports boolean, integer, and member slots. It is also 342 possible to add new kinds of slots by defining new subclasses of 343 BITFIELD-SLOT and the corresponding methods on BITFIELD-SLOT-PACK, 344 BITFIELD-SLOT-UNPACK and PARSE-ATOMIC-BITFIELD-SLOT-SPECIFIER or 345 PARSE-COMPOUND-BITFIELD-SLOT-SPECIFIER. 349 (define-bitfield examplebits 352 (c (unsigned-byte 3) :initform 1) 353 (d (integer -100 100)) 354 (e (member foo bar baz))) 356 (defun examplebits-values (examplebits) 358 (examplebits-a examplebits) 359 (examplebits-b examplebits) 360 (examplebits-c examplebits) 361 (examplebits-d examplebits) 362 (examplebits-e examplebits))) 364 (defparameter *default* (make-examplebits)) 366 (examplebits-values *default*) 367 ;; => (nil 0 1 -100 foo) 369 (defparameter *explicit* (make-examplebits :a t :b -1 :c 7 :d 42 :e 'baz)) 371 (examplebits-values *explicit*) 372 ;; => (t -1 7 42 baz) 374 (defparameter *clone* (clone-examplebits *explicit* :a nil :b -1 :c 2 :d -12 :e 'bar)) 376 (examplebits-values *clone*) 377 ;; => (nil -1 2 -12 bar) 379 (let* ((*bitfield-position* 0) 380 (package (symbol-package name)) 382 (intern (concatenate 'string "MAKE-" (symbol-name name)) package)) 384 (intern (concatenate 'string "CLONE-" (symbol-name name)) package)) 386 (concatenate 'string )) 388 (mapcar #'parse-bitfield-slot slots)) 390 (loop for slot in slots 392 (intern (concatenate 'string (symbol-name name) "-" reader-prefix 393 (symbol-name (bitfield-slot-name slot))) 396 (deftype ,name () '(unsigned-byte ,*bitfield-position*)) 397 ;; Define all slot readers. 398 ,@(loop for slot in slots 399 for reader-name in reader-names 400 for start = (bitfield-slot-start slot) 401 for end = (bitfield-slot-end slot) 403 `(declaim (inline ,reader-name)) 405 `(defun ,reader-name (,name) 406 (declare (,name ,name)) 407 ,(bitfield-slot-unpack 409 `(ldb (byte ,(- end start) ,start) ,name)))) 410 ;; Define the cloner. 411 (declaim (inline ,cloner)) 413 (,name &key ,@(loop for slot in slots 414 for reader-name in reader-names 416 `(,(bitfield-slot-name slot) 417 (,reader-name ,name)))) 418 (declare (,name ,name)) 420 ,@(loop for slot in slots 422 `(ash ,(bitfield-slot-pack slot (bitfield-slot-name slot)) 423 ,(bitfield-slot-start slot))))) 424 ;; Define the constructor. 425 (declaim (inline ,constructor)) 427 (&key ,@(loop for slot in slots 429 `(,(bitfield-slot-name slot) 430 ,(bitfield-slot-initform slot)))) 432 ,@(loop for slot in slots 434 `(ash ,(bitfield-slot-pack slot (bitfield-slot-name slot)) 435 ,(bitfield-slot-start slot))))) 439 (declaim (type (simple-array (simple-bit-vector 4) (16)) *bit-map*)) 440 (defvar *bit-map* #(#*0000 458 `(member #\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9 459 #\a #\b #\c #\d #\e #\f 460 #\A #\B #\C #\D #\E #\F)) 462 (declaim (ftype (function (hex-char) (integer 0 16)) hexchar->int) 463 (inline hexchar->int)) 464 (defun hexchar-to-int (char) 465 "Return the bit vector associated with a hex-value character CHAR from *bit-map*." 466 (declare (optimize (speed 2) (safety 0))) 467 (cond ((char<= #\0 char #\9) (- (char-code char) #.(char-code #\0))) 468 ((char<= #\a char #\f) (- (char-code char) #.(- (char-code #\a) 10))) 469 (t (- (char-code char) #.(- (char-code #\A) 10)) 470 ;; always return these results 471 #+nil (char<= #\A char #\F)))) 474 (defun hex-string-to-octet-vector (string &aux (start 0) (end (length string))) 475 "Parses a substring of STRING delimited by START and END of 476 hexadecimal digits into a byte array." 477 (declare (type string string)) 479 (ash (- end start) -1) 480 #+nil (/ (- end start) 2)) 481 (key (make-array length :element-type '(unsigned-byte 8)))) 482 (declare (type (simple-array (unsigned-byte 8)) key)) 484 for j from start below end by 2 485 do (setf (aref key i) 486 (+ (* (hexchar-to-int (char string j)) 16) 487 (hexchar-to-int (char string (1+ j))))) 488 finally (return key)))) 490 (defun octet-vector-to-hex-string (vector) 491 "Return a string containing the hexadecimal representation of the 492 subsequence of VECTOR between START and END. ELEMENT-TYPE controls 493 the element-type of the returned string." 494 (declare (type (vector (unsigned-byte 8)) vector)) 495 (let* ((length (length vector)) 496 (hexdigits #.(coerce "0123456789abcdef" 'simple-base-string))) 497 (loop with string = (make-string (* length 2) :element-type 'base-char) 498 for i from 0 below length 500 do (let ((byte (aref vector i))) 501 (declare (optimize (safety 0))) 502 (setf (aref string j) 503 (aref hexdigits (ldb (byte 4 4) byte)) 505 (aref hexdigits (ldb (byte 4 0) byte)))) 506 finally (return string)))) 508 (defun octets-to-integer (octet-vec &optional (end (length octet-vec))) 509 (declare (type (simple-array (unsigned-byte 8)) octet-vec)) 513 (setf sum (+ (aref octet-vec j) (ash sum 8))))) 515 (defun integer-to-octets (bignum &optional (n-bits (integer-length bignum))) 516 (let* ((n-bytes (ceiling n-bits 8)) 517 (octet-vec (make-array n-bytes :element-type '(unsigned-byte 8)))) 518 (declare (type (simple-array (unsigned-byte 8)) octet-vec)) 519 (loop for i from (1- n-bytes) downto 0 521 do (setf (aref octet-vec index) (ldb (byte 8 (* i 8)) bignum)) 522 finally (return octet-vec))))