Выравнивание байта BitVector после декодирования смещенного значения
Я столкнулся с интересной проблемой со Scodec. У меня есть своеобразная схема кодирования, которая требует выравнивания байтов потока, когда текущий указатель бита mod 8 не равен нулю (не выровнен по ближайшему байту).
Теперь, как правило, это будет обработано byteAligned
кодек, но ситуация, похоже, нуждается в глобальном контексте всей операции декодирования.
Вот минимальный пример проблемы
case class Baz(abc : Int, qux : Int)
case class Foo(bar : Int,
items : Vector[Baz])
object Foo {
implicit val codec : Codec[Foo] = (
("bar" | uint8L) ::
("items" | vectorOfN(uint8L, (
("abc" | uint8L) ::
("qux" | uint2L)
).as[Baz]
))).as[Foo]
}
items
будет закодирован и декодирован как Vector
из Baz
Предметы. Обратите внимание, что qux
член в Baz
представляется в виде 2-битного целого без знака. Мы хотим abc
элемент должен быть выровнен по байту, что означает, что во время его декодирования он будет декодировать, и, если поток байтов не выровнен, после декодированных битов должно быть добавлено заполнение (случай C).
Это означает, что для вектора размера 1 (случай A) выходной битовый сектор будет смещен на 6 битов, что хорошо, поскольку больше нет элементов (abc
никогда не достигается снова в цикле векторного декодирования).
Вектор размера 2 без байтового выравнивания показан в случае B. Вектор с байтовым выравниванием показан в случае C.
Ниже каждого случая находится комментарий, разбивающий поток битов. 0x0a
является шестнадцатеричным для 10 десятичных и 0b01
является двоичным для 1 десятичного числа. Я буду переключаться между ними, когда требуется больше деталей и когда поток не выровнен по байту.
Случай А
Codec.encode(Foo(10, Vector(Baz(4,1))))
res43: Attempt[BitVector] = Successful(BitVector(26 bits, 0x0a01044))
// bar N items abc(0) qux(0)
// 0x0a 0x01 0x04 0b01
Дело Б
Codec.encode(Foo(10, Vector(Baz(4,1), Baz(15,3))))
res42: Attempt[BitVector] = Successful(BitVector(36 bits, 0x0a020443f))
// bar N items abc(0) qux(0) abc(1) qux(1)
// 0x0a 0x02 0x04 0b01 0b00001111 0b11
Дело С
Codec.encode(Foo(10, Vector(Baz(4,1), Baz(15,3))))
res42: Attempt[BitVector] = Successful(BitVector(42 bits, 0x0a020443c0c))
// bar N items abc(0) qux(0) abc(1) padding qux(1)
// 0x0a 0x02 0x04 0b01 0b00001111 0b000000 0b11
Еще один способ взглянуть на дело C - это развернуть кодеки.
// bar N items abc(0) qux(0) abc(1) padding qux(1)
(uint8L :: uint8L :: uint8L :: uint2L :: uint8L :: ignore(6) :: uint2L).
dropUnits.encode(10 :: 2 :: 4 :: 1 :: 15 :: 3 :: HNil)
res47: Attempt[BitVector] = Successful(BitVector(42 bits, 0x0a020443c0c))
Обратите внимание, что не было никаких отступов после abc(0)
поскольку поток был выровнен по байту в этой точке. В abc(1)
это не было выровнено из-за декодирования qux(0)
, После выравнивания qux
будет декодировать на выровненной границе. Мы всегда хотим qux
декодировать в позиции, выровненной по байту.
Мне нужен какой-то метод для получения глобального контекста, чтобы выяснить, выровнен ли текущий битовый указатель в общем BitVector. Если нет, мне нужен кодек, чтобы разумно знать, когда он не декодирует на выровненной границе, и корректировать поток после декодирования его текущего значения.
Я знаю, что кодеки могут получить текущий битовый поток для декодирования, но они не имеют представления о том, где они находятся в общем битовом потоке.
Любое направление или код будет наиболее ценным.
PS
Если вы хотите пройти тестирование в Аммоните, вам нужно это запустить
load.ivy("org.scodec" %% "scodec-core" % "1.8.3")
import scodec.codecs._
import scodec._
import shapeless._
При копировании и вставке необходимо начать с {
чтобы убедиться, что case-класс и case-объект определены одновременно.