编解码器:如何为可选字节创建编解码器

ian*_*eer 2 scodec

我必须为具有以下规范的消息创建一个编解码器。消息长度由一个字节指示,该字节的最低有效位是扩展标志,当置位时表示必须使用以下(可选)字节作为最高有效位字节。(希望有道理)可以描述如下:

+ ------------------------------------------------- --------------------------------- +
| 长度|
| |
+ ---------------------------------- + ----- +-+ ------ --------------------------------- +
| | | | |
| Len1(LSB)| 分机| | Len2(MSB)-可选|
+ ---- + ---- + ---- + ---- + ---- + ---- + ---- + ----- + + ---- +- -+ ---- + ---- + ---- + ---- + ---- + ---- +
| | | | | | | | | | | | | | | | | |
| | | | | | | | + | | | | | | | | | |
+ ---- + ---- + ---- + ---- + ---- + ---- + ---- + ---- +-|-+ + ---- +- -+ ---- + ---- + ---- + ---- + ---- + ---- +
                                      |
                                      |
                                      v
                        布尔值:如果为true,则使用Len2
                                否则只有Len1
    

后续数据的长度由该字段决定。我想将编解码器与预定义的编解码器和组合器一起使用。我想它将涉及使用flatZip,但是我不清楚如何将flatZip合并到HList组合器中。任何指向示例或文档的指针将不胜感激。

mpi*_*ist 6

一种方法是使用scodec.codecs.optional组合器,该组合器返回Codec[Option[A]]给定的a Codec[Boolean]和a Codec[A]

val structure: Codec[(Int, Option[Int])] = uint(7) ~ optional(bool, uint8)
Run Code Online (Sandbox Code Playgroud)

这为我们提供了(Int, Option[Int])- 的编解码器,我们需要将其转换为的编解码器Int。为此,我们需要提供从Int到的转换,(Int, Option[Int])并提供相反的转换。我们知道大小字段最多为2 ^ 15-1(7个LSB位和8个MSB位),因此从(Int, Option[Int])to 转换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))
  })
Run Code Online (Sandbox Code Playgroud)

最后,我们可以使用以下大小的编解码器通过以下方式对可变长度的结构进行编码variableSizeBytes

val str: Codec[String] = variableSizeBytes(size, ascii)
Run Code Online (Sandbox Code Playgroud)

这给了我们a Codec[String],它根据上面定义的方案对编码的字符串字节加上以字节为单位的大小作为前缀。