[英]scodec decode/encode split length field
I am using scodec: https://github.com/scodec/scodec to decode/encode a binary protocol. 我正在使用scodec: https : //github.com/scodec/scodec解码/编码二进制协议。
I am struggling with a part of the spec where a "length" field is split into two parts by a "moreflag". 我在规范的一部分中苦苦挣扎,其中“ length”字段被“ moreflag”分为两部分。 The moreflag indicates if the length field needs more space.
moreflag指示长度字段是否需要更多空间。
Example:
Byte 1: identifier bits 8-7, moreFlag bit 6, length bits 5-0 // first length field filled with 0's if moreFlag is false
Byte 2: moreFlag bit 8, length bits 7-0
Byte 3: otherJunk bits 8-0
My problem is I want to encode/decode both of these length fields into a single case class field: 我的问题是我想将这两个长度字段都编码/解码为单个case类字段:
case class Header(totalLength: Int, otherJunk: Int)
I have tried a few different things, but nothing has worked out so far: 我尝试了几种不同的方法,但是到目前为止,仍然没有任何结果:
implicit val headerCodec: Codec[Header] = (
("identifier" | uint2) :~>:
("moreFlag" | bool).compact >>:~ { meta =>
if (meta.last) {
// more flag enabled, combine lengths somehow
("first length part" | uint(5)) :: ("moreFlag2DontCare" | uint(1) :~>: ("second length part - how to combine?" | uint(7)) :: ("otherJunk" | uint8)
}
else {
("first length part always 0s" | constant(bin"00000")) :: ("moreFlag2DontCare" | uint(1) :~>: ("fullLength" | uint(7)) :: ("otherJunk" | uint8)
}
}
).as[Header]
Am I on the right track here? 我在正确的轨道上吗? Thanks!
谢谢!
One way to accomplish this is to use the predefined combinators to define the structure of the binary format -- not the combination logic. 实现此目的的一种方法是使用预定义的组合器来定义二进制格式的结构,而不是组合逻辑。 Then put the combination logic inside a function passed to
xmap
. 然后将组合逻辑放在传递给
xmap
的函数中。
import scodec._
import bits._
import codecs._
import shapeless._
case class Header(totalLength: Int, otherJunk: Int)
object Header {
implicit val codec: Codec[Header] = {
type Struct = Int :: Boolean :: BitVector :: Boolean :: BitVector :: Int :: HNil
val struct: Codec[Struct] = ("identifier" | uint2) :: ("moreFlag" | bool) :: ("upper length bits" | codecs.bits(5)) :: ("moreFlag2" | bool) :: ("lower length bits" | codecs.bits(7)) :: ("other junk" | uint8)
def to(header: Header): Struct = {
val lengthBits = uint(12).encodeValid(header.totalLength)
val more = !(lengthBits startsWith bin"00000")
0 :: more :: lengthBits.take(5) :: false :: lengthBits.drop(5) :: header.otherJunk :: HNil
}
def from(struct: Struct): Header = struct match {
case id :: moreFlag :: upperLengthBits :: moreFlag2 :: lowerLengthBits :: otherJunk :: HNil =>
val length =
if (moreFlag) uint(12).decodeValidValue(upperLengthBits ++ lowerLengthBits)
else uint(7).decodeValidValue(lowerLengthBits)
Header(length, otherJunk)
}
struct.xmap[Header](from, to)
}
}
Note that the upper and lower length bits are encoded as bits(5)
and bits(7)
and then manually combined in the from
function and decoded. 注意,将上下长度的位编码为
bits(5)
和bits(7)
,然后在from
函数中手动组合并解码。 The decoding is safe, despite using the unsafe decodeValidValue
, because the uint(12)
codec is total on 12-bit inputs -- it is impossible for it to return a left. 尽管使用了不安全的
decodeValidValue
,但解码是安全的,因为uint(12)
编解码器是12位输入上的总计-不可能返回左。
However, the to
function uses encodeValid
, which is clearly unsafe -- eg, Int.MaxValue
will cause an exception. 但是,
to
函数使用encodeValid
,这显然是不安全的-例如, Int.MaxValue
将导致异常。 We can fix this with a slightly more complicated version, where the to
function returns an Err \\/ Struct
, and we call widen
instead of xmap
: 我们可以使用稍微复杂些的版本来解决此问题,其中
to
函数将返回Err \\/ Struct
,然后调用widen
而不是xmap
:
object Header {
implicit val codec: Codec[Header] = {
type Struct = Int :: Boolean :: BitVector :: Boolean :: BitVector :: Int :: HNil
val struct: Codec[Struct] = ("identifier" | uint2) :: ("moreFlag" | bool) :: ("upper length bits" | codecs.bits(5)) :: ("moreFlag2" | bool) :: ("lower length bits" | codecs.bits(7)) :: ("other junk" | uint8)
def to(header: Header): Err \/ Struct = {
uint(12).encode(header.totalLength) map { lengthBits =>
val more = !(lengthBits startsWith bin"00000")
0 :: more :: lengthBits.take(5) :: false :: lengthBits.drop(5) :: header.otherJunk :: HNil
}
}
def from(struct: Struct): Header = struct match {
case id :: moreFlag :: upperLengthBits :: moreFlag2 :: lowerLengthBits :: otherJunk :: HNil =>
val length =
if (moreFlag) uint(12).decodeValidValue(upperLengthBits ++ lowerLengthBits)
else uint(7).decodeValidValue(lowerLengthBits)
Header(length, otherJunk)
}
struct.widen[Header](from, to)
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.