Skip to content

Instantly share code, notes, and snippets.

@ajaychandran
Last active December 23, 2016 01:41
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ajaychandran/92b5eeade99d0855b066 to your computer and use it in GitHub Desktop.
Save ajaychandran/92b5eeade99d0855b066 to your computer and use it in GitHub Desktop.
scodec: DelimitedCodec
package scodec.codecs
import scala.language.higherKinds
import scodec._
import scodec.bits.BitVector
object DelimitedCodec {
/**
* Encodes all elements of the specified sequence and concatenates the results separated by `delimiter`,
* or returns the first encountered error.
*/
final def encode[A](enc: Encoder[A], delimiter: BitVector)(seq: collection.immutable.Seq[A]): Attempt[BitVector] = {
val buf = new collection.mutable.ArrayBuffer[BitVector](seq.size)
seq foreach { a =>
enc.encode(a) match {
case Attempt.Successful(aa) => buf += aa
case Attempt.Failure(err) => return Attempt.failure(err.pushContext(buf.size.toString))
}
}
def merge(offset: Int, size: Int): BitVector = size match {
case 0 => BitVector.empty
case 1 => buf(offset)
case n =>
val half = size / 2
merge(offset, half) ++ delimiter ++ merge(offset + half, half + (if (size % 2 == 0) 0 else 1))
}
Attempt.successful(merge(0, buf.size))
}
/**
* Repeatedly decodes values of type `A`, separated by `delimiter`, from the specified vector and
* returns a collection of the specified type.
* Terminates when no more bits are available in the vector. Exits upon first decoding error.
*/
final def decode[F[_], A](dec: Decoder[A], delimiter: BitVector)(buffer: BitVector)(implicit cbf: collection.generic.CanBuildFrom[F[A], A, F[A]]): Attempt[DecodeResult[F[A]]] = {
val bldr = cbf()
var remaining = buffer
var next = BitVector.empty
var count = 0
var error: Option[Err] = None
while (remaining.nonEmpty) {
buffer.indexOfSlice(delimiter) match {
case -1 =>
next = remaining
remaining = BitVector.empty
case i =>
next = remaining.take(i)
remaining = remaining.drop(i + delimiter.size)
}
dec.decode(next) match {
case Attempt.Successful(DecodeResult(value, rest)) =>
bldr += value
count += 1
case Attempt.Failure(err) =>
error = Some(err.pushContext(count.toString))
remaining = BitVector.empty
}
}
Attempt.fromErrOption(error, DecodeResult(bldr.result, remaining))
}
}
private[codecs] class VectorDelimitedCodec[A](delimiter: BitVector, codec: Codec[A]) extends Codec[Vector[A]] {
def encode(value: Vector[A]): Attempt[BitVector] = DelimitedCodec.encode(codec, delimiter)(value)
def sizeBound: SizeBound = SizeBound.unknown
def decode(bits: BitVector): Attempt[DecodeResult[Vector[A]]] = DelimitedCodec.decode[Vector, A](codec, delimiter)(bits)
}
private[codecs] class ListDelimitedCodec[A](delimiter: BitVector, codec: Codec[A]) extends Codec[List[A]] {
def encode(value: List[A]): Attempt[BitVector] = DelimitedCodec.encode(codec, delimiter)(value)
def sizeBound: SizeBound = SizeBound.unknown
def decode(bits: BitVector): Attempt[DecodeResult[List[A]]] = DelimitedCodec.decode[List, A](codec, delimiter)(bits)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment