Кодек для вектора [N], где N определяет конец вектора
Я использую Scodec для декодирования метаданных Flac. Одна из спецификаций состоит в том, что есть Заголовок и Блок, которые могут повторяться несколько раз вместе. Заголовок имеет флаг, который указывает, является ли текущая комбинация заголовка / блока последней.
Я смог декодировать заголовок и блок, но как мы можем создать векторную базу на основе этой спецификации.
Вот код сломан
//isLastBlock determines if this is the last Header/Block combo to decode.
case class Header(isLastBlock: Boolean)
//Some example data.
case class Block(someData: Int)
object Codec {
//Codec for Header
val headerCodec : Codec[Header] = bool.as[Header]
//Coded for Block
val blockCodec: Codec[Block] = int32.as[Block]
//We are guaranteed at least one Header/Block Combo, but how can we do this?
val headerBlock: Codec[(Header, Block, Vector[(Header, Block)])] = ???
}
Не уверен, что scodec предоставляет эту функцию. 2 метода vectorOfN и sizeVector не работают, потому что они требуют знать количество элементов перед декодированием.
2 ответа
Я нашел решение, используя flatMap и рекурсию.
//create a single Codec
val headerBlockCode: Codec[(Header,Block)] = headerCodec ~ blockCodec
//Takes the last decode and continues to decode until Header.isLastBlock
def repeat(priorDecode: DecodeResult[(Header,Block)]) : Attempt[DecodeResult[List[(Header, Block)]]] = {
if (priorDecode.value._1.isLastBlock) Successful(priorDecode.map(List(_)))
else {
headerBlockCode.decode(priorDecode.remainder) match {
case f: Failure => f
case s: Successful[DecodeResult[(Header, Block)]] => {
repeat(s.value) match {
case f: Failure => f
case Successful(list) => {
Successful(list.map(decList => s.value.value :: decList))
}
}
}
}
}
}
//Initial the decode
val bv = BitVector(data)
for {
first <- headerBlockCode.decode(bv)
list <- repeat(first)
} yield {
list.map(l => (list.value, l))
}
В настоящее время я делаю то же самое упражнение - так интересно узнать, как вы продвинулись.
Я также экспериментировал с проблемой VectorTeridityByIsLastIndicator, хотя принятый подход отличается.
Это тестовый код, который я создал (для Bar читайте MetadataBlockHeader). Обратите внимание, что класс case для MetadataBlockHeader не включает индикатор isLast - он просто добавляется во время кодирования и удаляется во время декодирования.
case class Bar(value: Int)
case class Foo(list1: List[Bar], list2: List[Bar])
object Foo {
def main(args: Array[String]) : Unit = {
implicit val barCodec : Codec[Bar] = {
("value" | int32).hlist
}.as[Bar]
implicit val barListCodec : Codec[List[Bar]] = new Codec[List[Bar]] {
case class IsLast[A](isLast: Boolean, thing: A)
implicit val lastBarCodec : Codec[IsLast[Bar]] = {
("isLast" | bool) :: ("bar" | Codec[Bar])
}.as[IsLast[Bar]]
override def sizeBound: SizeBound = SizeBound.unknown
override def encode(bars: List[Bar]): Attempt[BitVector] = {
if (bars.size == 0) {
Failure(Err("Cannot encode zero length list"))
} else {
val zippedBars = bars.zipWithIndex
val lastBars = zippedBars.map(bi => IsLast(bi._2 + 1 == bars.size, bi._1))
Codec.encodeSeq(lastBarCodec)(lastBars)
}
}
override def decode(b: BitVector): Attempt[DecodeResult[List[Bar]]] = {
val lastBars = decode(b, List.empty)
val bars = lastBars.map(dr => dr.value.thing)
Successful(DecodeResult(bars, lastBars.last.remainder))
}
@tailrec
private def decode(b: BitVector, lastBars: List[DecodeResult[IsLast[Bar]]]) : List[DecodeResult[IsLast[Bar]]] = {
val lastBar = lastBarCodec.decode(b).require
val lastBarsInterim = lastBars :+ lastBar
if (lastBar.value.isLast) lastBarsInterim
else decode(lastBar.remainder, lastBarsInterim)
}
}
implicit val fooCodec : Codec[Foo] = {
(("list1" | Codec[List[Bar]])
:: ("list2" | Codec[List[Bar]]))
}.as[Foo]
val bar1 = Bar(1)
val bar2 = Bar(2)
val bar3 = Bar(3)
val bar4 = Bar(4)
val aFoo = Foo(Seq(bar1, bar2).toList, Seq(bar3, bar4).toList)
println("aFoo: " + aFoo)
val encodedFoo = fooCodec.encode(aFoo).require
println("encodedFoo: " + encodedFoo)
val decodedFoo = fooCodec.decode(encodedFoo).require.value
println("decodedFoo: " + decodedFoo)
assert(decodedFoo == aFoo)
}
}
Немного больше информации об этом примере можно найти здесь.