Input
, Output
, ByteAccess
In order to allow for seamless integration in all kinds of application environments borer abstracts over decoding input, encoding output as well as general “chunks of bytes” with three additional type classes Input[T]
, Output[T]
and ByteAccess[T]
.
Input
For decoding borer happily consumes any type for which an Input.Provider[T]
is implicitly available, which is responsible for constructing an Input from or around T
:
source/**
* Responsible for converting an instance of [[T]]
* to a respective [[Input]] instance.
*/
trait Provider[T]:
type Bytes
def byteAccess: ByteAccess[Bytes]
def apply(value: T): Input[Bytes]
Currently borer comes with predefined Input
implementations for these types:
Array[Byte]
java.nio.ByteBuffer
java.io.InputStream
java.io.File
akka.util.ByteString
(with theborer-compat-akka
module)pekko.util.ByteString
(with theborer-compat-pekko
module)scodec.bits.ByteVector
(with theborer-compat-scodec
module)Iterator[T]
, provided that there is anInput.Provider[T]
available implicitly
The latter is a great way to consume large input in a streaming fashion, without having to load everything into memory at once. The FromFileInput
, for example, relies on it to parse large files as an iteration of chunks.
The Input trait isn’t particularly hard to implement, especially since it merely has to support single-pass access to the underlying bytes with minimal buffering and without random access.
Output
On the encoding side borer can either produce any type T
for which an Output.ToTypeProvider[T]
is available, or “push” the output into a value of type T
if an Output.ToValueProvider[T]
is available:
source/**
* Responsible for providing an Output that produces instances of [[T]].
*/
trait ToTypeProvider[T]:
type Out <: Output { type Result = T }
def apply(bufferSize: Int, allowBufferCaching: Boolean): Out
/**
* Responsible for providing an Output that outputs into the given value [[T]].
*/
trait ToValueProvider[T]:
type Out <: Output { type Result = T }
def apply(value: T, bufferSize: Int, allowBufferCaching: Boolean): Out
Currently borer comes with predefined Output implementations for these types:
Array[Byte]
java.nio.ByteBuffer
java.io.OutputStream
(to a an existing instance)java.io.File
(to a an existing instance)akka.util.ByteString
(with theborer-compat-akka
module)pekko.util.ByteString
(with theborer-compat-pekko
module)scodec.bits.ByteVector
(with theborer-compat-scodec
module)
The Output trait isn’t hard to implement as it simply writes out all bytes in a single pass.
Here are a few examples to illustrate the top-level output API:
sourceimport io.bullet.borer.Cbor
val value = List("foo", "bar", "baz") // example value
// encodes into a new byte array instance,
// relies on the `Output.ToTypeProvider[Array[Byte]]` type class instance
Cbor.encode(value).to[Array[Byte]].result ==> hex"9f63666f6f636261726362617aff"
// same as above but slightly more convenient
Cbor.encode(value).toByteArray ==> hex"9f63666f6f636261726362617aff"
// encodes into an existing file,
// relies on the `Output.ToValueProvider[File]` type class instance
val file = new java.io.File(filename)
Cbor.encode(value).to(file).result ==> file
ByteAccess
Unfortunately Scala (and the whole JVM eco-system) has no single, versatile abstraction for a “chunk of bytes” that fits the needs of all applications. In order to remain open to the preferences of the application borer also abstracts over “chunks of bytes” by allowing the use of any type T
, for which a ByteAccess[T]
is available.
Currently borer comes with predefined ByteAccess implementations for these types:
Array[Byte]
java.nio.ByteBuffer
akka.util.ByteString
(with theborer-compat-akka
module)pekko.util.ByteString
(with theborer-compat-pekko
module)scodec.bits.ByteVector
(with theborer-compat-scodec
module)