编解码器:如何为可选字节创建编解码器
Scodec: How to create a codec for an optional byte
我必须为具有以下规范的消息创建编解码器
消息长度由一个字节指示,该字节的最低有效位是一个扩展标志,当它被设置时表明后面的(可选)字节必须被用作最高有效字节。 (希望说得通)可以表示为:
+----------------------------------------------------------------------------------+
| LENGTH |
| |
+----------------------------------+-----+-+---------------------------------------+
| | | | |
| Len1 (LSB) | Ext | | Len2 (MSB) - Optional |
+----+----+----+----+----+----+----+-----+ +----+----+----+----+----+----+----+----+
| | | | | | | | | | | | | | | | | |
| | | | | | | | + | | | | | | | | | |
+----+----+----+----+----+----+----+--|--+ +----+----+----+----+----+----+----+----+
|
|
v
Boolean: if true then Len2 is used
else only Len1
后面的数据长度由该字段决定。我想将编解码器与预定义的编解码器和组合器一起使用。
我想这将涉及使用 flatZip,但我不清楚如何将 flatZip 合并到 HList 组合器中。
非常感谢任何指向示例或文档的指针。
一种方法是使用 scodec.codecs.optional
组合器,其中 returns 一个 Codec[Option[A]]
给定一个 Codec[Boolean]
和一个 Codec[A]
.
val structure: Codec[(Int, Option[Int])] = uint(7) ~ optional(bool, uint8)
这为我们提供了 (Int, Option[Int])
的编解码器 - 我们需要将其转换为 Int
的编解码器。为此,我们需要提供从 Int
到 (Int, Option[Int])
的转换以及另一个反向转换。我们知道大小字段最多为 2^15 - 1(7 个 LSB 位和 8 个 MSB 位),因此从 (Int, Option[Int])
转换为 Int
是总的,而反向转换可能会失败 - - 例如,2^16 不能在此结构中表示。因此,我们可以使用 widen
进行转换:
val size: Codec[Int] = structure.widen[Int](
{ case (lsb, msb) => lsb + msb.map(_ << 7).getOrElse(0) },
{ sz =>
val msb = sz >>> 7
if (msb > 255 || msb < 0) Attempt.failure(Err(s"invalid size $sz"))
else Attempt.successful((sz & 0x7F, if (msb > 0) Some(msb) else None))
})
最后,我们可以使用这个大小的编解码器通过variableSizeBytes
:
编码一个可变长度的结构
val str: Codec[String] = variableSizeBytes(size, ascii)
这给了我们一个Codec[String]
,它在编码的字符串字节前面加上以字节为单位的大小,根据上面定义的方案编码。
我必须为具有以下规范的消息创建编解码器 消息长度由一个字节指示,该字节的最低有效位是一个扩展标志,当它被设置时表明后面的(可选)字节必须被用作最高有效字节。 (希望说得通)可以表示为:
+----------------------------------------------------------------------------------+ | LENGTH | | | +----------------------------------+-----+-+---------------------------------------+ | | | | | | Len1 (LSB) | Ext | | Len2 (MSB) - Optional | +----+----+----+----+----+----+----+-----+ +----+----+----+----+----+----+----+----+ | | | | | | | | | | | | | | | | | | | | | | | | | | + | | | | | | | | | | +----+----+----+----+----+----+----+--|--+ +----+----+----+----+----+----+----+----+ | | v Boolean: if true then Len2 is used else only Len1
后面的数据长度由该字段决定。我想将编解码器与预定义的编解码器和组合器一起使用。 我想这将涉及使用 flatZip,但我不清楚如何将 flatZip 合并到 HList 组合器中。 非常感谢任何指向示例或文档的指针。
一种方法是使用 scodec.codecs.optional
组合器,其中 returns 一个 Codec[Option[A]]
给定一个 Codec[Boolean]
和一个 Codec[A]
.
val structure: Codec[(Int, Option[Int])] = uint(7) ~ optional(bool, uint8)
这为我们提供了 (Int, Option[Int])
的编解码器 - 我们需要将其转换为 Int
的编解码器。为此,我们需要提供从 Int
到 (Int, Option[Int])
的转换以及另一个反向转换。我们知道大小字段最多为 2^15 - 1(7 个 LSB 位和 8 个 MSB 位),因此从 (Int, Option[Int])
转换为 Int
是总的,而反向转换可能会失败 - - 例如,2^16 不能在此结构中表示。因此,我们可以使用 widen
进行转换:
val size: Codec[Int] = structure.widen[Int](
{ case (lsb, msb) => lsb + msb.map(_ << 7).getOrElse(0) },
{ sz =>
val msb = sz >>> 7
if (msb > 255 || msb < 0) Attempt.failure(Err(s"invalid size $sz"))
else Attempt.successful((sz & 0x7F, if (msb > 0) Some(msb) else None))
})
最后,我们可以使用这个大小的编解码器通过variableSizeBytes
:
val str: Codec[String] = variableSizeBytes(size, ascii)
这给了我们一个Codec[String]
,它在编码的字符串字节前面加上以字节为单位的大小,根据上面定义的方案编码。