2

我有以下我喜欢编码的结构。我知道如果大小字段直接位于矢量数据的前面,我可以使用 vector() 对矢量进行编码。但是这里编码向量大小的字段是不相邻的。

case class Item(
    address: Int,
    size: Int,
)
case class Header {
    // lots of other fields before
    numOfItems: Int,
    // lots of other fields after
}
case class Outer(
    hdr: Header,
    items: Vector[]
)

Outer 的解码没问题:

从位向量中读取 Header.numOfItems 并使用 vectorOfN(provide(hdr.numOfItems, Item.codec)) 创建项目

Outer 的编码是问题所在:

编码时,我希望从 items.length 中获取 numOfItem。我知道我可以在更新项目向量时使用附加代码设置 numOfItems 或使用“编码前回调”之类的东西。

问题是是否有更优雅的解决方案。对我来说Header.numOfItems是多余的Outer.items.length,所以理想情况下只有编码器应该知道 numOfItems。

4

2 回答 2

3

您可以尝试使用构建编解码器consume()并在不构建Outer对象的情况下开始:

case class OuterExpanded(
  fieldBefore: Int, // Field before number of items in the binary encoding
  fieldAdter: Int,  // Field after number of items in the binary encoding
  items: Vector[Item] // Encoded items
)

// Single Item codec
def itemC: Codec[Item] = (int32 :: int32).as[Item] 

def outerExpandedC: Codec[OuterExpanded] = ( 
  int32 ::                          // Field before count
  int32.consume( c =>               // Item count 
      int32 ::                      // Field after count
      vectorOfN(provide(c), itemC))   // 'consume' (use and forget) the count
    (_.tail.head.length)              // provide the length when encoding
  ).as[OuterExpanded]

如上定义,编码时会得到以下信息outerExpandedC.encode(OuterExpanded(-1, -1, Vector(Item(1,2), Item(3,4))))

Successful(BitVector(224 bits, 
     0xffffffff00000002fffffffe00000001000000020000000300000004))
              ^       ^       ^       ^-------^-> First Item
              |-1     |       |-2
                      |Vector length inserted between the two header fields

之后,您可以xmap()Codec[OuterExpanded]其他标头字段一起打包到它们自己的对象中。即(添加两个转换方法到OuterOuterExpanded):

def outerC: Codec[Outer] = 
  outerExpandedC.xmap(_.toOuter,_.expand)

case class OuterExpanded(fieldBefore: Int, fieldAfter: Int,  items: Vector[Item]) {
  def toOuter = Outer(Hdr(fieldBefore,fieldAfter), items)
}

case class Outer(header: Hdr, items: Vector[Item]) {
  def expand = OuterExpanded(header.beforeField1, header.beforeField1, items)
}

这可能适用于更复杂的情况,尽管我并不完全熟悉无形的异构列表 - 或者HList- 可能有更好的方法来获取向量的长度而不是_.tail.head.length在上面的示例中调用,特别是如果你在编码值的数量之后有多个字段。

此外,Codec scaladoc是发现有用运算符的好地方

于 2016-10-20T16:18:30.220 回答
0

根据前面的答案,我想出了类似下面的代码。我使用了上面的消耗技巧形式和一个 AtomicInteger 来保存向量的大小。

import java.util.concurrent.atomic.AtomicInteger
import scala.Vector
import org.scalatest._
import scodec._
import scodec.Attempt._
import scodec.codecs._
import scodec.bits._

object SomeStructure {
  case class Item(
    address: Int,
    size: Int)

  def itemC: Codec[Item] = (int32 :: int32).as[Item]

  case class Hdr(
    beforeField1: Int,
    // vectorSize would be here
    afterField1: Int)
  // vectorSize is an "in" param when encoding and an "out" param when decoding
  def hdrC(vectorSize: AtomicInteger): Codec[Hdr] =
    (int32 ::
      int32.consume(c => {
        vectorSize.set(c);
        int32
      })((i) => vectorSize.get)).as[Hdr]

  case class Outer(
    hdr: Hdr,
    var items: Vector[Item])

  def outerC() = {
    // when decoding the length is in this atomic integer
    // when encoding it is set before 
    val c = new AtomicInteger(-1)
    (hdrC(c) :: lazily(vectorOfN(provide(c.get), itemC)))
      .xmapc(identity)((g) => { c.set(g.tail.head.length); g })
  }.as[Outer]
}

import SomeStructure._
class SomeStructureSpec extends FlatSpec with Matchers {
  val bv = hex"ffffffff00000002ffffffff00000001000000020000000300000004".bits
  val v = Vector(Item(1, 2), Item(3, 4))
  val bv2 = hex"ffffffff00000003ffffffff000000010000000200000003000000040000000500000006".bits
  val v2 = Vector(Item(1, 2), Item(3, 4), Item(5, 6))
  val o = Outer(Hdr(-1, -1), v)

  "outerC" should "encode" in {
    o.items = v
    outerC.encode(o) shouldBe Successful(bv)
    o.items = v2
    outerC.encode(o) shouldBe Successful(bv2)
  }
  it should "decode" in {
    o.items = v
    outerC.decode(bv) shouldBe Successful(DecodeResult(o, BitVector.empty))
    o.items = v2
    outerC.decode(bv2) shouldBe Successful(DecodeResult(o, BitVector.empty))
  }
}
于 2016-10-21T09:55:08.387 回答