12.8 Функции для манипуляции с байтами

Common Lisp содержит несколько функций для работы с полями смежных битов произвольной длины, находящимися где-нибудь в целом числе. Такое множество смежных битов называется байт. В Common Lisp’е термин байт не означает некоторое определённое количество бит (как например восемь), а обозначает поле произвольной, определяемой пользователем длины.

Функции обработки байтом используют объекты, называющиеся спецификаторы байта для определения заданной позиции байта в целом числе. Представление спецификатора байта зависит от реализации. В частности оно может быть или не быть числом. Достаточно знать, что функция byte будет создавать такой спецификатор, и что функция для обработки байта будет принимать его. Функция byte принимает два целых числа указывающих на позицию и размер байта и возвращает его спецификатор. Такой спецификатор определяет байт, у которого ширина равна размеру, и биты которого имеют веса с 2position+size−1 по 2position.

[Функция] byte size position

byte принимает два целых числа, указывающих на размер и позицию байта и возвращает спецификатор, который может использоваться в функциях работы с байтами.


[Функция] byte-size bytespec
[Функция] byte-position bytespec

Принимая спецификатор байта, byte-size возвращает размер указанного байта, а byte-position — позицию этого байта. Например:

(byte-size (byte j k))  j
(byte-position (byte j k))  k


[Функция] ldb bytespec integer

Спецификатор байта bytespec указывает на байт, который будет извлечён из целого числа integer. Результат возвращается в качестве неотрицательного целого числа. Например:

(logbitp j (ldb (byte s p) n))  (and (< j s) (logbitp (+ j p) n))

Имя функции ldb означает «load byte (загрузить байт)».

Если аргумент integer задан формой, которая может использоваться в setf, тогда setf может использоваться вместе с ldb для изменения байта в целом числе указанном в форме integer. Это действие выполняется с помощью операции dpb.


[Функция] ldb-test bytespec integer

ldb-test является предикатом, который истинен, если любой из битов, указанных в спецификаторе байта bytespec, в целом числе integer имеет значение 1. Другими словами, предикат истинен, если указанное поле не равно нулю.

(ldb-test bytespec n)  (not (zerop (ldb bytespec n)))


[Функция] mask-field bytespec integer

Функция похожа а ldb. Однако результат содержит указанный байт целого числа integer в той же позиции, что указана в спецификаторе bytespec, а не в нулевой позиции, как делает ldb. Таким образом результат в указанном байте совпадает с целым числом integer , но в остальных местах имеет нулевые биты. Например:

(ldb bs (mask-field bs n))  (ldb bs n)

(logbitp j (mask-field (byte s p) n))
    (and (>= j p) (< j (+ p s)) (logbitp j n))

(mask-field bs n)  (logand n (dpb -1 bs 0))

Если аргумент integer задан формой, которая может использоваться в setf, тогда setf может использоваться вместе с mask-field для изменения байта в целом числе указанном в форме integer. Это действие выполняется с помощью операции deposit-field.


[Функция] dpb newbyte bytespec integer

Данная функция возвращает число, которое похоже на integer за исключением битов, указанных спецификатором bytespec.. Пусть s будет размером, указанным в спецификаторе bytespec. Тогда целое число newbyte будет интерпретировано, будучи выровненным вправо, как если бы было результатом ldb. Например:

(logbitp j (dpb m (byte s p) n))
   (if (and (>= j p) (< j (+ p s)))
(logbitp (- j p) m)
(logbitp j n))

Имя функции dpb означает «deposit byte (сохранить байт)».


[Функция] deposit-field newbyte bytespec integer

Данная функция относится к mask-field так же, как dpb относится к ldb. Результатом является целое число, которое содержит биты newbyte в том месте, куда указывает спецификатор bytespec, и биты числа integer во всех остальных местах.

(logbitp j (deposit-field m (byte s p) n))
    (if (and (>= j p) (< j (+ p s)))
(logbitp j m)
(logbitp j n))

__________________________________________________________________________

Заметка для реализации: If the bytespec is a constant, one may of course construct, at compile time, an equivalent mask m, for example by computing (deposit-field -1 bytespec 0). Given this mask m, one may then compute

(deposit-field newbyte bytespec integer)

by computing

(logior (logand newbyte m) (logand integer (lognot m)))

where the result of (lognot m) can of course also be computed at compile time. However, the following expression may also be used and may require fewer temporary registers in some situations:

(logxor integer (logand m (logxor integer newbyte)))

A related, though possibly less useful, trick is that

(let ((z (logand (logxor x y) m)))
  (setq x (logxor z x))
  (setq y (logxor z y)))

interchanges those bits of x and y for which the mask m is 1, and leaves alone those bits of x and y for which m is 0.

__________________________________________________________________________