Last active
December 23, 2016 01:41
-
-
Save ajaychandran/92b5eeade99d0855b066 to your computer and use it in GitHub Desktop.
scodec: DelimitedCodec
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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