Binary
type Binary
An opaque, immutable sequence of bytes. A Binary never implicitly becomes text.
[Byte] is the materialized list of bytes, while Binary is the storage form. A file, an HTTP body, or a digest is a Binary. On the BEAM it is a native binary, so slicing shares storage instead of copying. None of the operations here walks a character list.
let data = Binary.fromHex("00686900").try
data.length # => 4
data.at(1) # => Just(104)
data.take(2).hex # => "0068"
data.showValue # => "#Binary<4 bytes>"
The type is opaque on purpose: there is no field to reach through, so text operations cannot be run on bytes by accident. Conversions are always explicit in both directions, and both directions can fail. Runtime conversions through to always return an Optional<T>:
"árvíz".to(Binary) # => Just(#Binary<7 bytes>) : Binary?
Binary.fromBytes([255]).to(String) # => None
Showable and Inspectable deliberately render the length alone. Neither ever decodes or interpolates the payload, so printing a binary cannot leak its contents or fail on bytes that are not text. Use hex, base64, or to(String) when you actually want to see it.
Implements Showable, Inspectable.
Functions
bytes
bytes : [Byte]
The bytes, as a list.
The explicit, allocating conversion out of binary storage. Reach for length, at, take, and drop when you do not need the whole list.
Returns every byte, in order
Examples
Binary.fromHex("0068").try.bytes # => [0, 104]
length
length : Integer
Number of bytes held.
Returns the byte count
Examples
"héllo".to(Binary).try.length # => 6
empty?
empty? : Bool
Whether it holds no bytes at all.
Returns true when the binary is empty
Examples
Binary.fromBytes([]).empty? # => true
at
at(index: Integer) -> Byte?
The byte at index, counting from zero.
Answers None for a negative or out-of-range index, so an index you did not check is something you handle rather than something that stops the program.
Parameters
index- the position to read
Returns the byte, or None when the index is outside the binary
Examples
let data = Binary.fromBytes([104, 105])
data.at(0) # => Just(104)
data.at(2) # => None
data.at(-1) # => None
get
get(index: Integer) -> Byte?
get(index: Integer, default: Byte) -> Byte
The byte at index, counting from zero. The same as at, matching the get that List and String carry.
Parameters
index- the position to read
Returns the byte, or None when the index is outside the binary
Examples
Binary.fromBytes([104, 105]).get(1) # => Just(105)
Binary.fromBytes([104, 105]).get(9) # => None
take
take(count: Integer) -> Binary
The first count bytes.
Clamped at both ends, like the list operations: a count of zero or less answers the empty binary, and an oversized one answers the whole binary.
Parameters
count- how many bytes to keep
Returns the leading bytes
Examples
let data = Binary.fromBytes([104, 105])
data.take(1).bytes # => [104]
data.take(0).bytes # => []
data.take(999) == data # => true
drop
drop(count: Integer) -> Binary
Everything after the first count bytes.
Clamped at both ends: a count of zero or less answers the whole binary, and an oversized one answers the empty binary. take(n) and drop(n) therefore always concatenate back to the original.
Parameters
count- how many bytes to skip
Returns the remaining bytes
Examples
let data = Binary.fromBytes([104, 105])
data.drop(1).bytes # => [105]
(data.take(1) + data.drop(1)) == data # => true
+
+(other: Binary) -> Binary
Joins two binaries end to end.
Parameters
other- the bytes to append
Returns the two payloads, in order
Examples
Binary.fromBytes([104]) + Binary.fromBytes([105]) # => #Binary<2 bytes>
hex
hex : String
The bytes as lowercase hexadecimal text, two digits per byte.
Returns the hexadecimal encoding
Examples
Binary.fromBytes([0, 104, 255]).hex # => "0068ff"
base64
base64 : String
The bytes as standard base64 text (RFC 4648), with canonical padding.
Returns the base64 encoding
Examples
Binary.fromBytes([0, 104, 105, 255]).base64 # => "AGhp/w=="
showValue Showable
showValue : String
The length-only rendering: never the payload.
Returns #Binary<N bytes>
Examples
Binary.fromBytes([104, 105]).showValue # => "#Binary<2 bytes>"
inspectValue Inspectable
inspectValue(_: Bool) -> String
The length-only rendering: never the payload. The same as showValue.
Returns #Binary<N bytes>
to
to(_) -> String?
Decodes the payload as UTF-8, or answers None when the bytes are not valid text.
This deliberately differs from showValue, which reveals only the byte count. Converting asks to inspect the actual payload and therefore makes the possibility of invalid text explicit.
Returns the decoded text, or None for invalid UTF-8
Examples
Decoding a text response while handling a binary one
match response.body.to(String) do
Just(text) => IO.printLine(text)
None => IO.printLine("received ${response.body.length} bytes")
end
module Binary
empty constant
empty : Binary
The binary holding no bytes.
Backed by Binary.fromBytes([]), this is the natural starting value this library uses, just like (Headers.empty).
Examples
Binary.empty.length # => 0
fromBytes
fromBytes(values: [Byte]) -> Binary
Builds a binary from a list of bytes.
Total: every Byte is in 0..255 by construction, so there is no rejection case. Binary.fromBytes([]) is the empty binary.
Parameters
values- the bytes to store
Returns the bytes, in binary storage
Examples
Binary.fromBytes([104, 105]) # => #Binary<2 bytes>
Binary.fromBytes([]) # => #Binary<0 bytes>
fromHex
fromHex(text: String) -> Binary?
Decodes lowercase hexadecimal text.
Strict, so that decoding is the exact inverse of hex: uppercase digits, an odd length, whitespace, a 0x prefix, and any non-hex character all answer None rather than being repaired.
Parameters
text- the hexadecimal text
Returns the bytes, or None when the text is not canonical hex
Examples
Binary.fromHex("0068") # => Just(#Binary<2 bytes>)
Binary.fromHex("0x68") # => None
Binary.fromHex("AB") # => None
Binary.fromHex("a") # => None
Reading a compact identifier from configuration
let key = env.get("CACHE_KEY").flatMap(~Binary.fromHex)
let cacheEnabled? = key.present?
fromBase64
fromBase64(text: String) -> Binary?
Decodes standard base64 text (RFC 4648).
Strict, so that decoding is the exact inverse of base64: the URL-safe alphabet, missing or malformed padding, whitespace, and any other noncanonical encoding all answer None.
Parameters
text- the base64 text
Returns the bytes, or None when the text is not canonical base64
Examples
Binary.fromBase64("AGhp/w==") # => Just(#Binary<4 bytes>)
Binary.fromBase64("AGhp_w==") # => None
Binary.fromBase64("AGhp/w") # => None
Loading binary key material from an environment variable
let signingKey = ENV.get("SIGNING_KEY").flatMap(~Binary.fromBase64)