All Downloads are FREE. Search and download functionalities are using the official Maven repository.

scodec.Decoder.scala Maven / Gradle / Ivy

package scodec

import scala.language.higherKinds

import scodec.bits.BitVector

import shapeless.Lazy

/**
 * Supports decoding a value of type `A` from a `BitVector`.
 *
 * @groupname primary Primary Members
 * @groupprio primary 0
 *
 * @groupname combinators Basic Combinators
 * @groupprio combinators 10
 *
 * @groupname coproduct Coproduct Support
 * @groupprio coproduct 13
 */
trait Decoder[+A] { self =>

  /**
   * Attempts to decode a value of type `A` from the specified bit vector.
   *
   * @param bits bits to decode
   * @return error if value could not be decoded or the remaining bits and the decoded value
   * @group primary
   */
  def decode(bits: BitVector): Attempt[DecodeResult[A]]

  /**
   * Attempts to decode a value of type `A` from the specified bit vector and discards the remaining bits.
   *
   * @param bits bits to decode
   * @return error if value could not be decoded or the decoded value
   * @group primary
   */
  final def decodeValue(bits: BitVector): Attempt[A] = decode(bits).map { _.value }

  /**
   * Converts this decoder to a `Decoder[B]` using the supplied `A => B`.
   * @group combinators
   */
  def map[B](f: A => B): Decoder[B] = new Decoder[B] {
    def decode(bits: BitVector) = self.decode(bits) map { _ map f }
  }

  /**
   * Converts this decoder to a `Decoder[B]` using the supplied `A => Decoder[B]`.
   * @group combinators
   */
  def flatMap[B](f: A => Decoder[B]): Decoder[B] = new Decoder[B] {
    def decode(bits: BitVector) = self.decode(bits) flatMap { result => f(result.value).decode(result.remainder) }
  }

  /**
   * Converts this decoder to a `Decoder[B]` using the supplied `A => Attempt[B]`.
   * @group combinators
   */
  def emap[B](f: A => Attempt[B]): Decoder[B] = new Decoder[B] {
    def decode(bits: BitVector) = self.decode(bits) flatMap { result => f(result.value) map { b => DecodeResult(b, result.remainder) } }
  }

  /**
   * Converts this decoder to a new decoder that fails decoding if there are remaining bits.
   * @group combinators
   */
  def complete: Decoder[A] = new Decoder[A] {
    def decode(bits: BitVector) = self.decode(bits) flatMap { result =>
      if (result.remainder.isEmpty) Attempt.successful(result) else {
        val rem = result.remainder
        Attempt.failure {
          val max = 512L
          if (rem.sizeLessThan(max + 1)) {
            val preview = rem.take(max)
            Err(s"${preview.size} bits remaining: 0x${preview.toHex}")
          } else Err(s"more than $max bits remaining")
        }
      }
    }
  }

  /**
   * Gets this as a `Decoder`.
   * @group combinators
   */
  def asDecoder: Decoder[A] = this

  /**
   * Converts this to a codec that fails encoding with an error.
   * @group combinators
   */
  def decodeOnly[AA >: A]: Codec[AA] = new Codec[AA] {
    def sizeBound = SizeBound.unknown
    def encode(a: AA) = Attempt.failure(Err("encoding not supported"))
    def decode(bits: BitVector) = self.decode(bits)
  }
}

/**
 * Provides functions for working with decoders.
 *
 * @groupname conv Conveniences
 * @groupprio conv 2
 */
trait DecoderFunctions {

  /**
   * Decodes a tuple `(A, B)` by first decoding `A` and then using the remaining bits to decode `B`.
   * @group conv
   */
  final def decodeBoth[A, B](decA: Decoder[A], decB: Decoder[B])(buffer: BitVector): Attempt[DecodeResult[(A, B)]] =
    decodeBothCombine(decA, decB)(buffer) { (a, b) => (a, b) }

  /**
   * Decodes a `C` by first decoding `A` and then using the remaining bits to decode `B`, then applying the decoded values to the specified function to generate a `C`.
   * @group conv
   */
  final def decodeBothCombine[A, B, C](decA: Decoder[A], decB: Decoder[B])(buffer: BitVector)(f: (A, B) => C): Attempt[DecodeResult[C]] = {
    // Note: this could be written using flatMap on Decoder but this function is called *a lot* and needs to be very fast
    decA.decode(buffer) flatMap { aResult =>
      decB.decode(aResult.remainder) map { bResult => bResult map { b => f(aResult.value, b) } }
    }
  }

  /**
   * Repeatedly decodes values of type `A` from the specified vector, converts each value to a `B` and appends it to an accumulator of type
   * `B` using the supplied `zero` value and `append` function. Terminates when no more bits are available in the vector. Exits upon first decoding error.
   *
   * @return tuple consisting of the terminating error if any and the accumulated value
   * @group conv
   */
  final def decodeAll[A, B](buffer: BitVector)(zero: B, append: (B, B) => B)(f: A => B)(implicit decoder: Lazy[Decoder[A]]): (Option[Err], B) = {
    var remaining = buffer
    var acc = zero
    while (remaining.nonEmpty) {
      decoder.value.decode(remaining) match {
        case Attempt.Successful(DecodeResult(a, newRemaining)) =>
          remaining = newRemaining
          acc = append(acc, f(a))
        case Attempt.Failure(cause) =>
          return (Some(cause), acc)
      }
    }
    (None, acc)
  }

  /**
   * Repeatedly decodes values of type `A` from the specified vector and returns a collection of the specified type.
   * Terminates when no more bits are available in the vector or when `limit` is defined and that many records have been
   * decoded. Exits upon first decoding error.
   * @group conv
   */
  final def decodeCollect[F[_], A](dec: Decoder[A], limit: Option[Int])(buffer: BitVector)(implicit cbf: collection.generic.CanBuildFrom[F[A], A, F[A]]): Attempt[DecodeResult[F[A]]] = {
    val bldr = cbf()
    var remaining = buffer
    var count = 0
    var maxCount = limit getOrElse Int.MaxValue
    var error: Option[Err] = None
    while (count < maxCount && remaining.nonEmpty) {
      dec.decode(remaining) match {
        case Attempt.Successful(DecodeResult(value, rest)) =>
          bldr += value
          count += 1
          remaining = rest
        case Attempt.Failure(err) =>
          error = Some(err.pushContext(count.toString))
          remaining = BitVector.empty
      }
    }
    Attempt.fromErrOption(error, DecodeResult(bldr.result, remaining))
  }

  /**
   * Creates a decoder that decodes with each of the specified decoders, returning
   * the first successful result.
   * @group conv
   */
  final def choiceDecoder[A](decoders: Decoder[A]*): Decoder[A] = new Decoder[A] {
    def decode(buffer: BitVector) = {
      @annotation.tailrec def go(rem: List[Decoder[A]], lastErr: Err): Attempt[DecodeResult[A]] = rem match {
        case Nil => Attempt.failure(lastErr)
        case hd :: tl =>
          hd.decode(buffer) match {
            case res @ Attempt.Successful(_) => res
            case Attempt.Failure(err) => go(tl, err)
          }
      }
      go(decoders.toList, Err("no decoders provided"))
    }
  }
}

/**
 * Companion for [[Decoder]].
 *
 * @groupname ctor Constructors
 * @groupprio ctor 1
 *
 * @groupname inst Typeclass Instances
 * @groupprio inst 3
 */
object Decoder extends DecoderFunctions {

  /**
   * Provides syntax for summoning a `Decoder[A]` from implicit scope.
   * @group ctor
   */
  def apply[A](implicit dec: Lazy[Decoder[A]]): Decoder[A] = dec.value

  /**
   * Creates a decoder from the specified function.
   * @group ctor
   */
  def apply[A](f: BitVector => Attempt[DecodeResult[A]]): Decoder[A] = new Decoder[A] {
    def decode(bits: BitVector) = f(bits)
  }

  /**
   * Decodes the specified bit vector in to a value of type `A` using an implicitly available codec.
   * @group conv
   */
  def decode[A](bits: BitVector)(implicit d: Lazy[Decoder[A]]): Attempt[DecodeResult[A]] = d.value.decode(bits)

  /**
   * Creates a decoder that always decodes the specified value and returns the input bit vector unmodified.
   * @group ctor
   */
  def point[A](a: => A): Decoder[A] = new Decoder[A] {
    private lazy val value = a
    def decode(bits: BitVector) = Attempt.successful(DecodeResult(value, bits))
    override def toString = s"const($value)"
  }

  /**
   * Lifts a value of `Attempt[A]` in to a `Decoder`.
   * @group ctor
   */
  def liftAttempt[A](attempt: Attempt[A]): Decoder[A] = new Decoder[A] {
    def decode(b: BitVector) = attempt map { a => DecodeResult(a, b) }
    override def toString = s"constAttempt($attempt)"
  }

  /**
   * Gets a decoder that returns the input bit vector as its value and also returns the value as its remainder.
   * @group ctor
   */
  def get: Decoder[BitVector] = new Decoder[BitVector] {
    def decode(b: BitVector) = Attempt.successful(DecodeResult(b, b))
    override def toString = "get"
  }

  /**
   * Gets a decoder that ignores its input bit vector and sets the remainder to the specified value.
   * @group ctor
   */
  def set(remainder: BitVector): Decoder[Unit] = new Decoder[Unit] {
    def decode(b: BitVector) = Attempt.successful(DecodeResult((), remainder))
    override def toString = s"set($remainder)"
  }

  /**
   * Gets a decoder that transforms the input bit vector with the specified function and returns the result as the remainder.
   * @group ctor
   */
  def modify(f: BitVector => BitVector): Decoder[Unit] = new Decoder[Unit] {
    def decode(b: BitVector) = Attempt.successful(DecodeResult((), f(b)))
    override def toString = s"modify"
  }

  /**
   * Transform typeclass instance.
   * @group inst
   */
  implicit val transformInstance: Transform[Decoder] = new Transform[Decoder] {
    def exmap[A, B](decoder: Decoder[A], f: A => Attempt[B], g: B => Attempt[A]): Decoder[B] =
      decoder.emap(f)

    override def xmap[A, B](decoder: Decoder[A], f: A => B, g: B => A): Decoder[B] =
      decoder.map(f)
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy