Simple Constructors and Combinators 

Constants 

Constant codecs are codecs that always encode a specific bit pattern. For example, the constant(bin"1110") always encodes the bit pattern 1110. When decoding, a constant codec consumes the same number of bits as its constant value, and then either validates that the consumed bits match the constant value or ignores the consumed bits. Codecs created by constant(...) perform validation when decoding and codecs created by constantLenient(...) ignore the consumed bits.

For example, decoding 0000 with each technique yields:

scala> val x = constant(bin"1110").decode(bin"0000")
x: scodec.Attempt[scodec.DecodeResult[Unit]] =
  Failure(expected constant BitVector(4 bits, 0xe) but got BitVector(4 bits, 0x0))

scala> val y = constantLenient(bin"1110").decode(bin"0000")
y: scodec.Attempt[scodec.DecodeResult[Unit]] =
  Successful(DecodeResult((),BitVector(empty)))

The constant(...) method returns a Codec[Unit], which may seem odd at first glance. Unit codecs occur frequently in scodec. They are used for a variety of use cases, including encoding a predefined value, decoding a specific pattern, manipulating the remainder during decoding, or raising errors from encoding/decoding.

Literal Constants 

When working with binary formats that make heavy use of constant values, manually wrapping each constant bit pattern with a constant method can be verbose. This verbosity can be avoided by importing implicit conversions that allow treating binary literals as codecs.

scala> import scodec.codecs.literals._
import scodec.codecs.literals._

scala> val c: Codec[Unit] = bin"1110"
c: scodec.Codec[Unit] = constant(BitVector(4 bits, 0xe))

Unit Codecs 

Any codec can be turned in to a unit codec — that is, a Codec[Unit] — using the unit method on the Codec type. The resulting codec encodes and decodes using the original codec, but the decoded value is thrown away, and the value to encode is “fixed” at tht time the unit codec is generated.

For example:

scala> val c = int8.unit(-1)
c: scodec.Codec[Unit] = ...

scala> val enc = c.encode(())
enc: scodec.Attempt[scodec.bits.BitVector] =
  Successful(BitVector(8 bits, 0xff))

scala> val dec = c.decode(bin"00000000 00000001")
dec: scodec.Attempt[scodec.DecodeResult[Unit]] =
  Successful(DecodeResult((),BitVector(8 bits, 0x01)))

In this example, the value to encode is fixed to -1 at the time c is created. Hence, every call to encode results in a call to int8.encode(-1). Decoding is interesting in that it consumed 8 bits of the vector.

In general, converting a codec to a unit codec may seem like a useless operation. However, it plays an important role in both tuple codecs and heterogeneous list codecs, which are both covered at length later.

Context 

Recall that scodec.Err includes a context stack along with a message and any error type specific data. The errors generated by the built-in codecs typically do not provide context in errors. Rather, context can be provided using the withContext method on Codec.

For example:

scala> val noContext = int8.decode(bin"1111")
noContext: scodec.Attempt[scodec.DecodeResult[Int]] =
  Failure(cannot acquire 8 bits from a vector that contains 4 bits)

scala> val oneContext = int8.withContext("x").decode(bin"1111")
oneContext: scodec.Attempt[scodec.DecodeResult[Int]] =
  Failure(x: cannot acquire 8 bits from a vector that contains 4 bits)


scala> val twoContext = int8.withContext("x").withContext("y").decode(bin"1111")
twoContext: scodec.Attempt[scodec.DecodeResult[Int]] =
  Failure(y/x: cannot acquire 8 bits from a vector that contains 4 bits)

Each of these examples fails with the same error, with the exception of the context stack.

Context stacking is typically used to provide the path in to a large structure, in the same way that lenses peer in to a large structure.

The | operator on a String is provided as an alias for withContext with the arguments reversed — e.g., "x" | codec is equivalent to codec.withContext("x"). In standalone examples, withContext is typically clearer but in large structures, where each field in a record is labelled with its name, the | syntax may be clearer.

Miscellaneous 

There are a number of other miscellaneous combinators that are useful.

Complete 

TODO - complete, compact, withToString