changelog shortlog graph tags branches changeset files revisions annotate raw help

Mercurial > core / lisp/std/bit.lisp

changeset 543: b88bd4b0a039
parent: e2e5c4831389
child: ec1d4d544c36
author: Richard Westhaver <ellis@rwest.io>
date: Sat, 13 Jul 2024 00:03:13 -0400
permissions: -rw-r--r--
description: tweaks
1 ;;; std/bit.lisp --- Bit manipulation
2 
3 ;;; Commentary:
4 
5 ;; CMUCL doc: https://www.cs.cmu.edu/Groups/AI/html/cltl/clm/node132.html
6 
7 ;; quick primer: https://cp-algorithms.com/algebra/bit-manipulation.html
8 
9 ;;; Code:
10 (in-package :std/bit)
11 
12 ;;; Bits
13 (defun make-bits (length &rest args)
14  (apply #'make-array length (nconc (list :element-type 'bit) args)))
15 
16 ;; https://graphics.stanford.edu/~seander/bithacks.html
17 ;; http://www.azillionmonkeys.com/qed/asmexample.html
18 (defun haipart (n count)
19  (declare (fixnum n count))
20  (let ((x (abs n)))
21  (if (minusp count)
22  (ldb (byte (- count) 0) x)
23  (ldb (byte count (max 0 (- (integer-length x) count)))
24  x))))
25 
26 ;; minusp = 38 bytes
27 
28 ;; 29 bytes
29 (defun sign-bit (n)
30  "compute the sign bit of a fixnum. If N < 0 return -1 else return 0."
31  (declare (fixnum n))
32  (ash n (- 0 (integer-length n))))
33 
34 ;; 51 bytes (speed 3)
35 ;; 67 bytes (speed 1)
36 (defun different-signs-p (x y)
37  "Return non-nil iff x and y have opposite signs."
38  (declare (fixnum x y) (optimize (speed 1)))
39  (< (expt x y) 0))
40 
41 ;; TODO 2024-02-23:
42 (defun mortify-bits (x y)
43  "Interleave the bits of two numbers (Mortan numbers)."
44  (declare (fixnum x y)
45  (ignore x y))
46  ;; (loop for i across (integer-length)
47  ;; with z = 0
48  ;; ;; z |= (x & 1U << i) << i | (y & 1U << i) << (i + 1);
49  ;; do ()
50  ;; return z)
51  )
52 
53 (defun int-list-bits (n)
54  (declare (fixnum n))
55  (let ((bits '()))
56  (dotimes (position (integer-length n) bits)
57  (push (ldb (byte 1 position) n) bits))))
58 
59 (defun int-bit-vector (n)
60  (declare (fixnum n))
61  (let ((bits (make-array 0 :element-type 'bit :adjustable t :fill-pointer t)))
62  (dotimes (position (integer-length n) bits)
63  (vector-push-extend (ldb (byte 1 position) n) bits))))
64 
65 (defun aref-bit (octets idx)
66  (declare (octet-vector octets) (fixnum idx))
67  (multiple-value-bind (octet-idx bit-idx)
68  (truncate idx 8)
69  (ldb (byte 1 bit-idx)
70  (aref octets octet-idx))))
71 
72 (defun make-bit-vector (size &optional (fill 0))
73  "Make a BIT-VECTOR with SIZE and initial-element FILL which must be a
74 BIT 0|1. Note that this representation is not as useful as you might
75 think - bit-vectors don't have a direct mapping to integers/fixnums --
76 they are vectors (AKA arrays) first, and bits second. Attempting to
77 perform bitwise-ops ends up being very inefficient so whenever
78 possible, stick with fixnums and use LOG* functions."
79  (declare (bit fill))
80  (make-array size :initial-element fill :adjustable nil :element-type 'bit))
81 
82 ;; simple setter/getter for integer bits
83 (define-setf-expander logbit (index place &environment env)
84  (multiple-value-bind (temps vals stores store-form access-form)
85  (get-setf-expansion place env)
86  (let ((i (gensym))
87  (store (gensym))
88  (stemp (first stores)))
89  (values `(,i ,@temps)
90  `(,index ,@vals)
91  `(,store)
92  `(let ((,stemp (dpb ,store (byte 1 ,i) ,access-form))
93  ,@(cdr stores))
94  ,store-form
95  ,store)
96  `(logbit ,i ,access-form)))))
97 
98 (defun logbit (idx n)
99  (declare (fixnum idx n))
100  (ldb (byte 1 idx) n))
101 
102 ;;; Bitfields
103 
104 ;; see https://github.com/marcoheisig/bitfield
105 
106 ;; A bitfield is a simple, efficient mechanism for storing multiple
107 ;; discrete states into a single non-negative integer.
108 
109 (deftype bitfield ()
110  "A bitfield is a non-negative integer that efficiently encodes
111 information about some booleans, enumerations, or small integers."
112  'unsigned-byte)
113 
114 ;;; Bitfield Slots
115 (defgeneric bitfield-slot-name (bitfield-slot)
116  (:documentation
117  "Returns a symbol that is the name of the bitfield slot."))
118 
119 (defgeneric bitfield-slot-start (bitfield-slot)
120  (:documentation
121  "Returns the position of the first bit of this slot in the bitfield."))
122 
123 (defgeneric bitfield-slot-end (bitfield-slot)
124  (:documentation
125  "Returns the position right after the last bit of this slot in the bitfield."))
126 
127 (defgeneric bitfield-slot-size (bitfield-slot)
128  (:documentation
129  "Returns an unsigned byte that is the number of distinct states of the slot."))
130 
131 (defgeneric bitfield-slot-initform (bitfield-slot)
132  (:documentation
133  "Returns a form that produces the initial value for that slot."))
134 
135 (defgeneric bitfield-slot-pack (bitfield-slot value-form)
136  (:documentation
137  "Takes a form that produces a value and turns it into a form that produces
138 a non-negative integer representing that value."))
139 
140 (defgeneric bitfield-slot-unpack (bitfield-slot value-form)
141  (:documentation
142  "Take a form that produces a value that is encoded as a non-negative
143 integer (as produced by BITFIELD-SLOT-PACK), and turn it into a form that
144 produces the decoded value."))
145 
146 (defgeneric parse-atomic-bitfield-slot-specifier
147  (specifier &key initform)
148  (:documentation
149  "Parses an atomic bitfield slot specifier, i.e., a bitfield slot
150 specifier that is not a list. Returns three values:
151 
152 1. A designator for a bitfield slot class.
153 
154 2. The size of the bitfield slot.
155 
156 3. A list of additional arguments that will be supplied to MAKE-INSTANCE
157 when creating the bitfield slot instance."))
158 
159 (defgeneric parse-compound-bitfield-slot-specifier
160  (specifier arguments &key initform)
161  (:documentation
162  "Parses a compount bitfield slot specifier, i.e., a bitfield slot
163 specifier that is a list. The SPECIFIER is the CAR of that list and the
164 ARGUMENTS are the CDR of that list. Returns three values:
165 
166 1. A designator for a bitfield slot class.
167 
168 2. The size of the bitfield slot.
169 
170 3. A list of additional arguments that will be supplied to MAKE-INSTANCE
171 when creating the bitfield slot instance."))
172 
173 (defclass bitfield-slot ()
174  ((%name :initarg :name :reader bitfield-slot-name)
175  (%initform :initarg :initform :reader bitfield-slot-initform)
176  (%start :initarg :start :reader bitfield-slot-start)
177  (%end :initarg :end :reader bitfield-slot-end)
178  (%size :initarg :size :reader bitfield-slot-size)))
179 
180 ;;; Boolean Slots
181 (defclass bitfield-boolean-slot (bitfield-slot)
182  ())
183 
184 (defmethod bitfield-slot-pack ((slot bitfield-boolean-slot) value-form)
185  `(if ,value-form 1 0))
186 
187 (defmethod bitfield-slot-unpack ((slot bitfield-boolean-slot) value-form)
188  `(ecase ,value-form (0 nil) (1 t)))
189 
190 (defmethod parse-atomic-bitfield-slot-specifier
191  ((specifier (eql 'boolean)) &key (initform 'nil))
192  (values 'bitfield-boolean-slot
193  2
194  `(:initform ,initform)))
195 
196 ;;; Integer Slots
197 (defclass bitfield-integer-slot (bitfield-slot)
198  ((%offset
199  :type integer
200  :initarg :offset
201  :reader bitfield-integer-slot-offset)))
202 
203 (defmethod bitfield-slot-pack ((slot bitfield-integer-slot) value-form)
204  (let ((offset (bitfield-integer-slot-offset slot))
205  (size (bitfield-slot-size slot)))
206  `(the (integer 0 (,size))
207  (- (the (integer ,offset (,(+ offset size))) ,value-form)
208  ,offset))))
209 
210 (defmethod bitfield-slot-unpack ((slot bitfield-integer-slot) value-form)
211  (let ((offset (bitfield-integer-slot-offset slot))
212  (size (bitfield-slot-size slot)))
213  `(the (integer ,offset (,(+ offset size)))
214  (+ ,value-form ,offset))))
215 
216 (defmethod parse-atomic-bitfield-slot-specifier
217  ((specifier (eql 'bit)) &key (initform '0))
218  (values 'bitfield-unsigned-byte-slot
219  2
220  `(:offset 0 :initform ,initform)))
221 
222 (defmethod parse-compound-bitfield-slot-specifier
223  ((specifier (eql 'unsigned-byte)) arguments &key (initform '0))
224  (destructuring-bind (bits) arguments
225  (check-type bits unsigned-byte)
226  (values 'bitfield-integer-slot
227  (expt 2 bits)
228  `(:offset 0 :initform ,initform))))
229 
230 (defmethod parse-compound-bitfield-slot-specifier
231  ((specifier (eql 'signed-byte)) arguments &key (initform '0))
232  (destructuring-bind (bits) arguments
233  (check-type bits unsigned-byte)
234  (values 'bitfield-integer-slot
235  (expt 2 bits)
236  `(:offset ,(- (expt 2 (1- bits))) :initform ,initform))))
237 
238 (defmethod parse-compound-bitfield-slot-specifier
239  ((specifier (eql 'integer)) bounds &key (initform nil initform-supplied-p))
240  (flet ((fail ()
241  (error "Invalid integer bitfield slot specifier: ~S"
242  `(integer ,@bounds))))
243  (unless (typep bounds '(cons t (cons t null)))
244  (fail))
245  (destructuring-bind (lo hi) bounds
246  (let* ((start (typecase lo
247  (integer lo)
248  ((cons integer null)
249  (1+ (first lo)))
250  (otherwise (fail))))
251  (end (typecase hi
252  (integer (1+ hi))
253  ((cons integer null)
254  (first hi))
255  (otherwise (fail))))
256  (size (- end start)))
257  (unless (plusp size)
258  (fail))
259  (values 'bitfield-integer-slot
260  size
261  `(:offset ,start :initform ,(if initform-supplied-p initform start)))))))
262 
263 ;;; Member Slots
264 (defclass bitfield-member-slot (bitfield-slot)
265  ((%objects
266  :type list
267  :initarg :objects
268  :reader bitfield-member-slot-objects)))
269 
270 (defmethod bitfield-slot-pack ((slot bitfield-member-slot) value-form)
271  `(ecase ,value-form
272  ,@(loop for key in (bitfield-member-slot-objects slot)
273  for value from 0
274  collect `((,key) ,value))))
275 
276 (defmethod bitfield-slot-unpack ((slot bitfield-member-slot) value-form)
277  `(ecase ,value-form
278  ,@(loop for key from 0
279  for value in (bitfield-member-slot-objects slot)
280  collect `((,key) ',value))))
281 
282 (defmethod parse-compound-bitfield-slot-specifier
283  ((specifier (eql 'member)) objects &key (initform `',(first objects)))
284  (values 'bitfield-member-slot
285  (length objects)
286  `(:initform ,initform :objects ,objects)))
287 
288 ;;; Parsing
289 ;; The position right after the last slot that has been parsed so far.
290 (defvar *bitfield-position*)
291 
292 (defun parse-bitfield-slot (slot)
293  (destructuring-bind (slot-name slot-specifier &rest rest) slot
294  (check-type slot-name symbol)
295  (multiple-value-bind (slot-class size args)
296  (if (consp slot-specifier)
297  (apply #'parse-compound-bitfield-slot-specifier
298  (car slot-specifier)
299  (cdr slot-specifier)
300  rest)
301  (apply #'parse-atomic-bitfield-slot-specifier
302  slot-specifier
303  rest))
304  (apply #'make-instance slot-class
305  :name slot-name
306  :size size
307  :start *bitfield-position*
308  :end (incf *bitfield-position* (integer-length (1- size)))
309  args))))
310 
311 (defmacro define-bitfield (name &body slots)
312  "Defines an encoding of enumerable properties like booleans,
313 integers or finite sets as a single non-negative integer.
314 
315 For a supplied bitfield name NAME, and for some slot definitions of the
316 form (SLOT-NAME TYPE &KEY INITFORM &ALLOW-OTHER-KEYS), this macro defines
317 the following functions:
318 
319 1. A constructor named MAKE-{NAME}, that takes one keyword argument per
320  SLOT-NAME, similar to the default constructor generated by DEFSTRUCT.
321  It returns a bitfield whose entries have the values indicated by the
322  keyword arguments, or the supplied initform.
323 
324 2. A clone operation named CLONE-{NAME}, that takes an existing bitfield
325  and one keyword argument per SLOT-NAME. It returns a copy of the
326  existing bitfield, but where each supplied keyword argument supersedes
327  the value of the corresponding slot.
328 
329 3. A reader function named {NAME}-{SLOT-NAME} for each slot.
330 
331 In addition to these functions, NAME is defined as a suitable subtype of
332 UNSIGNED-BYTE.
333 
334 This macro supports boolean, integer, and member slots. It is also
335 possible to add new kinds of slots by defining new subclasses of
336 BITFIELD-SLOT and the corresponding methods on BITFIELD-SLOT-PACK,
337 BITFIELD-SLOT-UNPACK and PARSE-ATOMIC-BITFIELD-SLOT-SPECIFIER or
338 PARSE-COMPOUND-BITFIELD-SLOT-SPECIFIER.
339 
340  Example:
341 
342  (define-bitfield examplebits
343  (a boolean)
344  (b (signed-byte 2))
345  (c (unsigned-byte 3) :initform 1)
346  (d (integer -100 100))
347  (e (member foo bar baz)))
348 
349  (defun examplebits-values (examplebits)
350  (list
351  (examplebits-a examplebits)
352  (examplebits-b examplebits)
353  (examplebits-c examplebits)
354  (examplebits-d examplebits)
355  (examplebits-e examplebits)))
356 
357  (defparameter *default* (make-examplebits))
358 
359  (examplebits-values *default*)
360  ;; => (nil 0 1 -100 foo)
361 
362  (defparameter *explicit* (make-examplebits :a t :b -1 :c 7 :d 42 :e 'baz))
363 
364  (examplebits-values *explicit*)
365  ;; => (t -1 7 42 baz)
366 
367  (defparameter *clone* (clone-examplebits *explicit* :a nil :b -1 :c 2 :d -12 :e 'bar))
368 
369  (examplebits-values *clone*)
370  ;; => (nil -1 2 -12 bar)
371 "
372  (let* ((*bitfield-position* 0)
373  (package (symbol-package name))
374  (constructor
375  (intern (concatenate 'string "MAKE-" (symbol-name name)) package))
376  (cloner
377  (intern (concatenate 'string "CLONE-" (symbol-name name)) package))
378  (reader-prefix
379  (concatenate 'string ))
380  (slots
381  (mapcar #'parse-bitfield-slot slots))
382  (reader-names
383  (loop for slot in slots
384  collect
385  (intern (concatenate 'string (symbol-name name) "-" reader-prefix
386  (symbol-name (bitfield-slot-name slot)))
387  package))))
388  `(progn
389  (deftype ,name () '(unsigned-byte ,*bitfield-position*))
390  ;; Define all slot readers.
391  ,@(loop for slot in slots
392  for reader-name in reader-names
393  for start = (bitfield-slot-start slot)
394  for end = (bitfield-slot-end slot)
395  collect
396  `(declaim (inline ,reader-name))
397  collect
398  `(defun ,reader-name (,name)
399  (declare (,name ,name))
400  ,(bitfield-slot-unpack
401  slot
402  `(ldb (byte ,(- end start) ,start) ,name))))
403  ;; Define the cloner.
404  (declaim (inline ,cloner))
405  (defun ,cloner
406  (,name &key ,@(loop for slot in slots
407  for reader-name in reader-names
408  collect
409  `(,(bitfield-slot-name slot)
410  (,reader-name ,name))))
411  (declare (,name ,name))
412  (logior
413  ,@(loop for slot in slots
414  collect
415  `(ash ,(bitfield-slot-pack slot (bitfield-slot-name slot))
416  ,(bitfield-slot-start slot)))))
417  ;; Define the constructor.
418  (declaim (inline ,constructor))
419  (defun ,constructor
420  (&key ,@(loop for slot in slots
421  collect
422  `(,(bitfield-slot-name slot)
423  ,(bitfield-slot-initform slot))))
424  (logior
425  ,@(loop for slot in slots
426  collect
427  `(ash ,(bitfield-slot-pack slot (bitfield-slot-name slot))
428  ,(bitfield-slot-start slot)))))
429  ',name)))
430 
431 ;;; From bit-smasher
432 (declaim (type (simple-array (simple-bit-vector 4) (16)) *bit-map*))
433 (defvar *bit-map* #(#*0000
434  #*0001
435  #*0010
436  #*0011
437  #*0100
438  #*0101
439  #*0110
440  #*0111
441  #*1000
442  #*1001
443  #*1010
444  #*1011
445  #*1100
446  #*1101
447  #*1110
448  #*1111))
449 
450 (deftype hex-char ()
451  `(member #\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9
452  #\a #\b #\c #\d #\e #\f
453  #\A #\B #\C #\D #\E #\F))
454 
455 (declaim (ftype (function (hex-char) (integer 0 16)) hexchar->int)
456  (inline hexchar->int))
457 (defun hexchar-to-int (char)
458  "Return the bit vector associated with a hex-value character CHAR from *bit-map*."
459  (declare (optimize (speed 2) (safety 0)))
460  (cond ((char<= #\0 char #\9) (- (char-code char) #.(char-code #\0)))
461  ((char<= #\a char #\f) (- (char-code char) #.(- (char-code #\a) 10)))
462  (t (- (char-code char) #.(- (char-code #\A) 10))
463  ;; always return these results
464  #+nil (char<= #\A char #\F))))
465 
466 ;;; From Ironclad
467 (defun hex-string-to-octet-vector (string &aux (start 0) (end (length string)))
468  "Parses a substring of STRING delimited by START and END of
469 hexadecimal digits into a byte array."
470  (declare (type string string))
471  (let* ((length
472  (ash (- end start) -1)
473  #+nil (/ (- end start) 2))
474  (key (make-array length :element-type '(unsigned-byte 8))))
475  (declare (type (simple-array (unsigned-byte 8)) key))
476  (loop for i from 0
477  for j from start below end by 2
478  do (setf (aref key i)
479  (+ (* (hexchar-to-int (char string j)) 16)
480  (hexchar-to-int (char string (1+ j)))))
481  finally (return key))))
482 
483 (defun octet-vector-to-hex-string (vector)
484  "Return a string containing the hexadecimal representation of the
485 subsequence of VECTOR between START and END. ELEMENT-TYPE controls
486 the element-type of the returned string."
487  (declare (type (vector (unsigned-byte 8)) vector))
488  (let* ((length (length vector))
489  (hexdigits #.(coerce "0123456789abcdef" 'simple-base-string)))
490  (loop with string = (make-string (* length 2) :element-type 'base-char)
491  for i from 0 below length
492  for j from 0 by 2
493  do (let ((byte (aref vector i)))
494  (declare (optimize (safety 0)))
495  (setf (aref string j)
496  (aref hexdigits (ldb (byte 4 4) byte))
497  (aref string (1+ j))
498  (aref hexdigits (ldb (byte 4 0) byte))))
499  finally (return string))))
500 
501 (defun octets-to-integer (octet-vec &optional (end (length octet-vec)))
502  (declare (type (simple-array (unsigned-byte 8)) octet-vec))
503  (do ((j 0 (1+ j))
504  (sum 0))
505  ((>= j end) sum)
506  (setf sum (+ (aref octet-vec j) (ash sum 8)))))
507 
508 (defun read-little-endian (s &optional (bytes 4))
509  "Read a number in little-endian format from an byte (octet) stream S,
510 the number having BYTES octets (defaulting to 4)."
511  (loop for i from 0 below bytes
512  sum (ash (read-byte s) (* 8 i))))
513 
514 (defun write-little-endian (i s &optional (bytes 4))
515  (write-sequence (nreverse (integer-to-octets i (* 8 bytes))) s))
516 
517 (defun integer-to-octets (bignum &optional (n-bits (integer-length bignum)))
518  (let* ((n-bytes (ceiling n-bits 8))
519  (octet-vec (make-array n-bytes :element-type '(unsigned-byte 8))))
520  (declare (type (simple-array (unsigned-byte 8)) octet-vec))
521  (loop for i from (1- n-bytes) downto 0
522  for index from 0
523  do (setf (aref octet-vec index) (ldb (byte 8 (* i 8)) bignum))
524  finally (return octet-vec))))