Преобразование scodec variableSizePrefixBytes

У меня есть случай использования, когда заголовок может содержать 7 байтов плюс необязательный 0-15 байт информации, где информация о размере находится в младших 4 битах 5-го байта, поэтому формат:

4 bytes | 4 bits | 4 bits <- length of extra bytes | 2 bytes | 0-15 extra Bytes

И я смоделировал это в следующем случае класса

case class FHDR(DevAddr:Long, ADR:Boolean, ADRACKReq:Boolean, ACK:Boolean, FPending:Boolean, FCnt:Int, FOpts:ByteVector)

implicit val FHDRCodec = {
  ("dev_addr" | uint32L) ::
  ("adr" | bool) ::
  ("adr_ack_req" | bool) ::
  ("ack" | bool) ::
  ("f_pending" | bool) ::
  variableSizePrefixedBytes(uint4, 
    "f_cnt" | uint16L, 
    "f_opts" | bytes)
}.as[FHDR]

Согласно Scala Docs в этом случае я могу использовать variableSizePrefixedBytes метод для моделирования 2 дополнительных байтов между размером и дополнительными байтами.

Но я делаю что-то не так, так как компилятор не может доказать, что этот кодек может быть преобразован в / из FHDR класс, я смотрю на это некоторое время сейчас, но я не знаю,

1 ответ

Решение

Ошибка в этом случае:

error: Could not prove that shapeless.::[Long,shapeless.::[Boolean,shapeless.::[Boolean,shapeless.::[Boolean,shapeless.::[Boolean,shapeless.::[(Int, scodec.bits.ByteVector),shapeless.HNil]]]]]] can be converted to/from FHDR.
   }.as[FHDR]
       ^

Переформатирование ошибки для использования :: в инфиксной позиции дает:

error: Could not prove that Long :: Boolean :: Boolean :: Boolean :: Boolean :: (Int, ByteVector) :: HNil can be converted to/from FHDR.
   }.as[FHDR]
       ^

Общее представление FHDR является Long :: Boolean :: Boolean :: Boolean :: Boolean :: Int :: ByteVector :: HNil, Проблема здесь в том, что последний тип в HList является (Int, ByteVector), вызванный variableSizePrefixedBytes, который имеет эту подпись:

  def variableSizePrefixedBytes[A, B](size: Codec[Int], prefix: Codec[A], value: Codec[B], sizePadding: Int = 0): Codec[(A, B)] = ...

Нам нужно расправить этот кортеж с внешним HList структура, которая приведет к форме кодека, совпадающей с формой общего представления FHDR,

import scodec.bits._
import scodec.codecs._
import shapeless.syntax.std.product._

case class FHDR(DevAddr:Long, ADR:Boolean, ADRACKReq:Boolean, ACK:Boolean, FPending:Boolean, FCnt:Int, FOpts:ByteVector)

implicit val FHDRCodec = {
  ("dev_addr" | uint32L) ::
  ("adr" | bool) ::
  ("adr_ack_req" | bool) ::
  ("ack" | bool) ::
  ("f_pending" | bool) ::
  variableSizePrefixedBytes(uint4, 
    "f_cnt" | uint16L, 
    "f_opts" | bytes
  ).xmapc(_.toHList)(_.tupled)
}.as[FHDR]

Здесь мы используем .toHList на Tuple2, который исходит от shapeless.syntax.std.product._ Импортировать. Мы также используем .tupled чтобы изменить это преобразование.

Другие вопросы по тегам