不与向量相邻的编码向量长度字段
Encoding vector length field not adjacent to the vector
我有以下我喜欢编码的结构。
我知道如果大小字段直接位于矢量数据前面,我可以使用 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[]
)
外部解码正常:
Header.numOfItems 从位向量中读取并使用 vectorOfN(provide(hdr.numOfItems, Item.codec))
创建项目
外部编码问题:
编码时,我希望从 items.length 中获取 numOfItem。
我知道我可以在更新项目 Vector 时使用附加代码设置 numOfItems 或使用 "before encoding callback".
之类的代码
问题是有没有更优雅的解决方案。对我来说 Header.numOfItems
与 Outer.items.length
是多余的,所以理想情况下只有
编码器应该知道 numOfItems。
您可以尝试使用 consume()
构建编解码器,然后不构建 Outer
object:
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))))
returns
Successful(BitVector(224 bits,
0xffffffff00000002fffffffe00000001000000020000000300000004))
^ ^ ^ ^-------^-> First Item
|-1 | |-2
|Vector length inserted between the two header fields
之后,您可以xmap()
Codec[OuterExpanded]
将其他 header 字段一起打包到它们自己的 object 中。即(将Outer
和OuterExpanded
两种转换方式相加):
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)
}
这可能适用于更复杂的情况,尽管我并不完全熟悉 shapeless 的异构列表——或 HList
——并且可能有更好的方法来实现向量的长度,而不是在上面的例子中调用 _.tail.head.length
,特别是如果你在编码值的数量之后有一个以上的字段。
此外,Codec scaladoc 是发现有用运算符的好地方
根据之前的回答,我想到了类似下面的代码。
我使用了上面的消耗技巧形式和一个 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))
}
}
我有以下我喜欢编码的结构。 我知道如果大小字段直接位于矢量数据前面,我可以使用 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[]
)
外部解码正常:
Header.numOfItems 从位向量中读取并使用 vectorOfN(provide(hdr.numOfItems, Item.codec))
创建项目外部编码问题:
编码时,我希望从 items.length 中获取 numOfItem。 我知道我可以在更新项目 Vector 时使用附加代码设置 numOfItems 或使用 "before encoding callback".
之类的代码问题是有没有更优雅的解决方案。对我来说 Header.numOfItems
与 Outer.items.length
是多余的,所以理想情况下只有
编码器应该知道 numOfItems。
您可以尝试使用 consume()
构建编解码器,然后不构建 Outer
object:
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))))
returns
Successful(BitVector(224 bits,
0xffffffff00000002fffffffe00000001000000020000000300000004))
^ ^ ^ ^-------^-> First Item
|-1 | |-2
|Vector length inserted between the two header fields
之后,您可以xmap()
Codec[OuterExpanded]
将其他 header 字段一起打包到它们自己的 object 中。即(将Outer
和OuterExpanded
两种转换方式相加):
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)
}
这可能适用于更复杂的情况,尽管我并不完全熟悉 shapeless 的异构列表——或 HList
——并且可能有更好的方法来实现向量的长度,而不是在上面的例子中调用 _.tail.head.length
,特别是如果你在编码值的数量之后有一个以上的字段。
此外,Codec scaladoc 是发现有用运算符的好地方
根据之前的回答,我想到了类似下面的代码。 我使用了上面的消耗技巧形式和一个 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))
}
}