Coverage report: /home/ellis/comp/core/std/bit.lisp
Kind | Covered | All | % |
expression | 70 | 596 | 11.7 |
branch | 2 | 16 | 12.5 |
Key
Not instrumented
Conditionalized out
Executed
Not executed
Both branches taken
One branch taken
Neither branch taken
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
(defun make-bits (length &rest args)
14
"Make an array of bits with dimensions LENGTH and keyword arguments ARGS."
15
(apply #'make-array length (nconc (list :element-type 'bit) args)))
17
;; https://graphics.stanford.edu/~seander/bithacks.html
18
;; http://www.azillionmonkeys.com/qed/asmexample.html
19
(defun haipart (n count)
20
(declare (fixnum n count))
23
(ldb (byte (- count) 0) x)
24
(ldb (byte count (max 0 (- (integer-length x) count)))
31
"compute the sign bit of a fixnum. If N < 0 return -1 else return 0."
33
(ash n (- 0 (integer-length n))))
37
(defun different-signs-p (x y)
38
"Return non-nil iff x and y have opposite signs."
39
(declare (fixnum x y) (optimize (speed 1)))
43
(defun mortify-bits (x y)
44
"Interleave the bits of two numbers (Mortan numbers)."
47
;; (loop for i across (integer-length)
49
;; ;; z |= (x & 1U << i) << i | (y & 1U << i) << (i + 1);
54
(defun int-list-bits (n)
55
"Return the list of bits which compose the fixnum N."
58
(dotimes (position (integer-length n) bits)
59
(push (ldb (byte 1 position) n) bits))))
61
(defun int-bit-vector (n)
62
"Return the bit representation of N as a vector of bits."
64
(let ((bits (make-array 0 :element-type 'bit :adjustable t :fill-pointer t)))
65
(dotimes (position (integer-length n) bits)
66
(vector-push-extend (ldb (byte 1 position) n) bits))))
68
(defun aref-bit (octets idx)
69
(declare (octet-vector octets) (fixnum idx))
70
(multiple-value-bind (octet-idx bit-idx)
73
(aref octets octet-idx))))
75
(defun make-bit-vector (size &optional (fill 0))
76
"Make a BIT-VECTOR with SIZE and initial-element FILL which must be a
77
BIT 0|1. Note that this representation is not as useful as you might
78
think - bit-vectors don't have a direct mapping to integers/fixnums --
79
they are vectors (AKA arrays) first, and bits second. Attempting to
80
perform bitwise-ops ends up being very inefficient so whenever
81
possible, stick with fixnums and use LOG* functions."
83
(make-array size :initial-element fill :adjustable nil :element-type 'bit))
85
;; simple setter/getter for integer bits
86
(define-setf-expander logbit (index place &environment env)
87
(multiple-value-bind (temps vals stores store-form access-form)
88
(get-setf-expansion place env)
91
(stemp (first stores)))
95
`(let ((,stemp (dpb ,store (byte 1 ,i) ,access-form))
99
`(logbit ,i ,access-form)))))
101
(defun logbit (idx n)
102
(declare (fixnum idx n))
103
(ldb (byte 1 idx) n))
105
;; Hacker's Delight ch 3-1 - petalisp
107
"Round the unsigned integer N down to the next smaller power of two."
111
(declare (type (and fixnum unsigned-byte) x))
112
(setf x (logior x (ash x -1)))
113
(setf x (logior x (ash x -2)))
114
(setf x (logior x (ash x -4)))
115
(setf x (logior x (ash x -8)))
116
(setf x (logior x (ash x -16)))
117
(setf x (logior x (ash x -32)))
120
(ash 1 (1- (integer-length n))))))
122
(deftype clp2-fixnum ()
123
`(integer 0 ,(expt 2 (1- (integer-length most-positive-fixnum)))))
126
"Round the unsigned integer N up to the next larger power of two."
130
(return-from clp2 0))
132
(declare (type clp2-fixnum x))
133
(setf x (logior x (ash x -1)))
134
(setf x (logior x (ash x -2)))
135
(setf x (logior x (ash x -4)))
136
(setf x (logior x (ash x -8)))
137
(setf x (logior x (ash x -16)))
138
(setf x (logior x (ash x -32)))
141
(ash 1 (integer-length (1- n))))))
145
;; see https://github.com/marcoheisig/bitfield
147
;; A bitfield is a simple, efficient mechanism for storing multiple
148
;; discrete states into a single non-negative integer.
151
"A bitfield is a non-negative integer that efficiently encodes
152
information about some booleans, enumerations, or small integers."
156
(defgeneric bitfield-slot-name (bitfield-slot)
158
"Returns a symbol that is the name of the bitfield slot."))
160
(defgeneric bitfield-slot-start (bitfield-slot)
162
"Returns the position of the first bit of this slot in the bitfield."))
164
(defgeneric bitfield-slot-end (bitfield-slot)
166
"Returns the position right after the last bit of this slot in the bitfield."))
168
(defgeneric bitfield-slot-size (bitfield-slot)
170
"Returns an unsigned byte that is the number of distinct states of the slot."))
172
(defgeneric bitfield-slot-initform (bitfield-slot)
174
"Returns a form that produces the initial value for that slot."))
176
(defgeneric bitfield-slot-pack (bitfield-slot value-form)
178
"Takes a form that produces a value and turns it into a form that produces
179
a non-negative integer representing that value."))
181
(defgeneric bitfield-slot-unpack (bitfield-slot value-form)
183
"Take a form that produces a value that is encoded as a non-negative
184
integer (as produced by BITFIELD-SLOT-PACK), and turn it into a form that
185
produces the decoded value."))
187
(defgeneric parse-atomic-bitfield-slot-specifier
188
(specifier &key initform)
190
"Parses an atomic bitfield slot specifier, i.e., a bitfield slot
191
specifier that is not a list. Returns three values:
193
1. A designator for a bitfield slot class.
195
2. The size of the bitfield slot.
197
3. A list of additional arguments that will be supplied to MAKE-INSTANCE
198
when creating the bitfield slot instance."))
200
(defgeneric parse-compound-bitfield-slot-specifier
201
(specifier arguments &key initform)
203
"Parses a compount bitfield slot specifier, i.e., a bitfield slot
204
specifier that is a list. The SPECIFIER is the CAR of that list and the
205
ARGUMENTS are the CDR of that list. Returns three values:
207
1. A designator for a bitfield slot class.
209
2. The size of the bitfield slot.
211
3. A list of additional arguments that will be supplied to MAKE-INSTANCE
212
when creating the bitfield slot instance."))
214
(defclass bitfield-slot ()
215
((%name :initarg :name :reader bitfield-slot-name)
216
(%initform :initarg :initform :reader bitfield-slot-initform)
217
(%start :initarg :start :reader bitfield-slot-start)
218
(%end :initarg :end :reader bitfield-slot-end)
219
(%size :initarg :size :reader bitfield-slot-size))
220
(:documentation "Superclass for slot objects of a BITFIELD class."))
223
(defclass bitfield-boolean-slot (bitfield-slot)
225
(:documentation "Boolean bitfield slots."))
227
(defmethod bitfield-slot-pack ((slot bitfield-boolean-slot) value-form)
228
`(if ,value-form 1 0))
230
(defmethod bitfield-slot-unpack ((slot bitfield-boolean-slot) value-form)
231
`(ecase ,value-form (0 nil) (1 t)))
233
(defmethod parse-atomic-bitfield-slot-specifier
234
((specifier (eql 'boolean)) &key (initform 'nil))
235
(values 'bitfield-boolean-slot
237
`(:initform ,initform)))
240
(defclass bitfield-integer-slot (bitfield-slot)
244
:reader bitfield-integer-slot-offset))
245
(:documentation "Integer bitfield slots."))
247
(defmethod bitfield-slot-pack ((slot bitfield-integer-slot) value-form)
248
(let ((offset (bitfield-integer-slot-offset slot))
249
(size (bitfield-slot-size slot)))
250
`(the (integer 0 (,size))
251
(- (the (integer ,offset (,(+ offset size))) ,value-form)
254
(defmethod bitfield-slot-unpack ((slot bitfield-integer-slot) value-form)
255
(let ((offset (bitfield-integer-slot-offset slot))
256
(size (bitfield-slot-size slot)))
257
`(the (integer ,offset (,(+ offset size)))
258
(+ ,value-form ,offset))))
260
(defmethod parse-atomic-bitfield-slot-specifier
261
((specifier (eql 'bit)) &key (initform '0))
262
(values 'bitfield-unsigned-byte-slot
264
`(:offset 0 :initform ,initform)))
266
(defmethod parse-compound-bitfield-slot-specifier
267
((specifier (eql 'unsigned-byte)) arguments &key (initform '0))
268
(destructuring-bind (bits) arguments
269
(check-type bits unsigned-byte)
270
(values 'bitfield-integer-slot
272
`(:offset 0 :initform ,initform))))
274
(defmethod parse-compound-bitfield-slot-specifier
275
((specifier (eql 'signed-byte)) arguments &key (initform '0))
276
(destructuring-bind (bits) arguments
277
(check-type bits unsigned-byte)
278
(values 'bitfield-integer-slot
280
`(:offset ,(- (expt 2 (1- bits))) :initform ,initform))))
282
(defmethod parse-compound-bitfield-slot-specifier
283
((specifier (eql 'integer)) bounds &key (initform nil initform-supplied-p))
285
(error "Invalid integer bitfield slot specifier: ~S"
286
`(integer ,@bounds))))
287
(unless (typep bounds '(cons t (cons t null)))
289
(destructuring-bind (lo hi) bounds
290
(let* ((start (typecase lo
300
(size (- end start)))
303
(values 'bitfield-integer-slot
305
`(:offset ,start :initform ,(if initform-supplied-p initform start)))))))
308
(defclass bitfield-member-slot (bitfield-slot)
312
:reader bitfield-member-slot-objects))
313
(:documentation "Bitfield slots containing a value from a mutually-exclusive list of options."))
315
(defmethod bitfield-slot-pack ((slot bitfield-member-slot) value-form)
317
,@(loop for key in (bitfield-member-slot-objects slot)
319
collect `((,key) ,value))))
321
(defmethod bitfield-slot-unpack ((slot bitfield-member-slot) value-form)
323
,@(loop for key from 0
324
for value in (bitfield-member-slot-objects slot)
325
collect `((,key) ',value))))
327
(defmethod parse-compound-bitfield-slot-specifier
328
((specifier (eql 'member)) objects &key (initform `',(first objects)))
329
(values 'bitfield-member-slot
331
`(:initform ,initform :objects ,objects)))
334
;; The position right after the last slot that has been parsed so far.
335
(defvar *bitfield-position*)
337
(defun parse-bitfield-slot (slot)
338
(destructuring-bind (slot-name slot-specifier &rest rest) slot
339
(check-type slot-name symbol)
340
(multiple-value-bind (slot-class size args)
341
(if (consp slot-specifier)
342
(apply #'parse-compound-bitfield-slot-specifier
346
(apply #'parse-atomic-bitfield-slot-specifier
349
(apply #'make-instance slot-class
352
:start *bitfield-position*
353
:end (incf *bitfield-position* (integer-length (1- size)))
356
(defmacro define-bitfield (name &body slots)
357
"Defines an encoding of enumerable properties like booleans,
358
integers or finite sets as a single non-negative integer.
360
For a supplied bitfield name NAME, and for some slot definitions of the
361
form (SLOT-NAME TYPE &KEY INITFORM &ALLOW-OTHER-KEYS), this macro defines
362
the following functions:
364
1. A constructor named MAKE-{NAME}, that takes one keyword argument per
365
SLOT-NAME, similar to the default constructor generated by DEFSTRUCT.
366
It returns a bitfield whose entries have the values indicated by the
367
keyword arguments, or the supplied initform.
369
2. A clone operation named CLONE-{NAME}, that takes an existing bitfield
370
and one keyword argument per SLOT-NAME. It returns a copy of the
371
existing bitfield, but where each supplied keyword argument supersedes
372
the value of the corresponding slot.
374
3. A reader function named {NAME}-{SLOT-NAME} for each slot.
376
In addition to these functions, NAME is defined as a suitable subtype of
379
This macro supports boolean, integer, and member slots. It is also
380
possible to add new kinds of slots by defining new subclasses of
381
BITFIELD-SLOT and the corresponding methods on BITFIELD-SLOT-PACK,
382
BITFIELD-SLOT-UNPACK and PARSE-ATOMIC-BITFIELD-SLOT-SPECIFIER or
383
PARSE-COMPOUND-BITFIELD-SLOT-SPECIFIER.
387
(define-bitfield examplebits
390
(c (unsigned-byte 3) :initform 1)
391
(d (integer -100 100))
392
(e (member foo bar baz)))
394
(defun examplebits-values (examplebits)
396
(examplebits-a examplebits)
397
(examplebits-b examplebits)
398
(examplebits-c examplebits)
399
(examplebits-d examplebits)
400
(examplebits-e examplebits)))
402
(defparameter *default* (make-examplebits))
404
(examplebits-values *default*)
405
;; => (nil 0 1 -100 foo)
407
(defparameter *explicit* (make-examplebits :a t :b -1 :c 7 :d 42 :e 'baz))
409
(examplebits-values *explicit*)
410
;; => (t -1 7 42 baz)
412
(defparameter *clone* (clone-examplebits *explicit* :a nil :b -1 :c 2 :d -12 :e 'bar))
414
(examplebits-values *clone*)
415
;; => (nil -1 2 -12 bar)
417
(let* ((*bitfield-position* 0)
418
(package (symbol-package name))
420
(intern (concatenate 'string "MAKE-" (symbol-name name)) package))
422
(intern (concatenate 'string "CLONE-" (symbol-name name)) package))
424
(concatenate 'string ))
426
(mapcar #'parse-bitfield-slot slots))
428
(loop for slot in slots
430
(intern (concatenate 'string (symbol-name name) "-" reader-prefix
431
(symbol-name (bitfield-slot-name slot)))
434
(deftype ,name () '(unsigned-byte ,*bitfield-position*))
435
;; Define all slot readers.
436
,@(loop for slot in slots
437
for reader-name in reader-names
438
for start = (bitfield-slot-start slot)
439
for end = (bitfield-slot-end slot)
441
`(declaim (inline ,reader-name))
443
`(defun ,reader-name (,name)
444
(declare (,name ,name))
445
,(bitfield-slot-unpack
447
`(ldb (byte ,(- end start) ,start) ,name))))
448
;; Define the cloner.
449
(declaim (inline ,cloner))
451
(,name &key ,@(loop for slot in slots
452
for reader-name in reader-names
454
`(,(bitfield-slot-name slot)
455
(,reader-name ,name))))
456
(declare (,name ,name))
458
,@(loop for slot in slots
460
`(ash ,(bitfield-slot-pack slot (bitfield-slot-name slot))
461
,(bitfield-slot-start slot)))))
462
;; Define the constructor.
463
(declaim (inline ,constructor))
465
(&key ,@(loop for slot in slots
467
`(,(bitfield-slot-name slot)
468
,(bitfield-slot-initform slot))))
470
,@(loop for slot in slots
472
`(ash ,(bitfield-slot-pack slot (bitfield-slot-name slot))
473
,(bitfield-slot-start slot)))))
477
(declaim (type (simple-array (simple-bit-vector 4) (16)) *bit-map*))
478
(defvar *bit-map* #(#*0000
496
`(member #\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9
497
#\a #\b #\c #\d #\e #\f
498
#\A #\B #\C #\D #\E #\F))
500
(declaim (ftype (function (hexchar) (integer 0 16)) hexchar->int)
501
(inline hexchar-to-int))
502
(defun hexchar-to-int (char)
503
"Return the bit vector associated with a hex-value character CHAR from *bit-map*."
504
(declare (optimize (speed 2) (safety 0)))
505
(cond ((char<= #\0 char #\9) (- (char-code char) #.(char-code #\0)))
506
((char<= #\a char #\f) (- (char-code char) #.(- (char-code #\a) 10)))
507
(t (- (char-code char) #.(- (char-code #\A) 10))
508
;; always return these results
509
#+nil (char<= #\A char #\F))))
512
(defun hex-string-to-octet-vector (string &aux (start 0) (end (length string)))
513
"Parses a substring of STRING delimited by START and END of
514
hexadecimal digits into a byte array."
515
(declare (type string string))
517
(ash (- end start) -1)
518
#+nil (/ (- end start) 2))
519
(key (make-array length :element-type '(unsigned-byte 8))))
520
(declare (type (simple-array (unsigned-byte 8)) key))
522
for j from start below end by 2
523
do (setf (aref key i)
524
(+ (* (hexchar-to-int (char string j)) 16)
525
(hexchar-to-int (char string (1+ j)))))
526
finally (return key))))
528
(defun octet-vector-to-hex-string (vector)
529
"Return a string containing the hexadecimal representation of the
530
subsequence of VECTOR between START and END. ELEMENT-TYPE controls
531
the element-type of the returned string."
532
(declare (type (vector (unsigned-byte 8)) vector))
533
(let* ((length (length vector))
534
(hexdigits #.(coerce "0123456789abcdef" 'simple-base-string)))
535
(loop with string = (make-string (* length 2) :element-type 'base-char)
536
for i from 0 below length
538
do (let ((byte (aref vector i)))
539
(declare (optimize (safety 0)))
540
(setf (aref string j)
541
(aref hexdigits (ldb (byte 4 4) byte))
543
(aref hexdigits (ldb (byte 4 0) byte))))
544
finally (return string))))
546
(defun octets-to-integer (octet-vec &optional (bytes (length octet-vec)))
547
"Return the integer representation of OCTET-VEC by reading BYTES number of
548
bytes from the start."
549
(declare (type (simple-array (unsigned-byte 8)) octet-vec))
553
(setf sum (+ (aref octet-vec j) (ash sum 8)))))
555
(defun integer-to-octets (bignum &optional (n-bits (integer-length bignum)))
556
"Return an octet-vector representation of BIGNUM using N-BITS number of bits."
557
(let* ((n-bytes (ceiling n-bits 8))
558
(octet-vec (make-array n-bytes :element-type '(unsigned-byte 8))))
559
(declare (type (simple-array (unsigned-byte 8)) octet-vec))
560
(loop for i from (1- n-bytes) downto 0
562
do (setf (aref octet-vec index) (ldb (byte 8 (* i 8)) bignum))
563
finally (return octet-vec))))
565
(defun octets-to-integer-le (octet-vec &optional (bytes (length octet-vec)))
566
"Return the integer representation of OCTET-VEC in little-endian by reading
567
BYTES number of bytes from the start."
568
(declare (type (simple-array (unsigned-byte 8)) octet-vec))
569
(loop for i from 0 below bytes
570
sum (ash (aref octet-vec i) (* 8 i))))
572
(defun integer-to-octets-le (bignum &optional (n-bits (integer-length bignum)))
573
"Return an octet-vector representation of BIGNUM in little-endian using N-BITS
575
(let* ((n-bytes (ceiling n-bits 8))
576
(octet-vec (make-array n-bytes :element-type '(unsigned-byte 8))))
577
(declare (type (simple-array (unsigned-byte 8)) octet-vec))
578
(loop for i from 0 below n-bytes
579
do (setf (aref octet-vec i) (ldb (byte 8 (* i 8)) bignum))
580
finally (return octet-vec))))
582
(defun read-little-endian (s &optional (bytes 4))
583
"Read a number in little-endian format from a byte (octet) stream S,
584
the number having BYTES octets (defaulting to 4)."
585
(loop for i from 0 below bytes
586
sum (ash (read-byte s) (* 8 i))))
588
(defun write-little-endian (i s &optional (bytes 4))
589
"Write a number to a byte stream S in little-endian having BYTES octets."
590
(write-sequence (integer-to-octets-le i bytes) s))
592
(defun make-octets (dimensions &rest args)
593
"Like MAKE-ARRAY but with a hard-coded element-type of (unsigned-byte 8)."
594
(apply 'make-array dimensions :element-type 'octet args))
596
(defun octets (&rest bytes)
597
"Return an octet-vector with initial contents BYTES."
598
(make-octets (length bytes) :initial-contents bytes))