0

我正在使用 Scodec 解码 Flac 元数据。其中一个规范是有一个 Header 和一个 Block 可以一起重复多次。Header 有一个标志,指示当前的 Header/Block 组合是否是最后一个。

我已经能够解码 Header 和 Block,但是我们如何根据这个规范创建一个 Vector。

这是分解的代码

  //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 是否提供此功能。两种方法 vectorOfN 和 sizedVector 不起作用,因为它们需要在解码之前知道项目的数量。

4

2 回答 2

0

我找到了使用 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))
  }
于 2017-08-12T04:04:14.747 回答
0

我目前正在做完全相同的练习——很想知道你的进步。

我也一直在尝试 VectorTerminatedByIsLastIndicator 问题,尽管采用的方法不同。

这是我创建的一些测试代码(用于 Bar 读取 MetadataBlockHeader)。请注意,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)
  }

}

可以在此处找到此示例的更多背景信息。

于 2018-03-03T10:03:52.157 回答