Packages
gleam_stdlib
0.19.3
1.0.3
1.0.2
1.0.1
1.0.0
0.71.0
0.70.0
0.69.0
0.68.1
0.68.0
0.67.1
0.67.0
0.65.0
0.64.0
0.63.2
0.63.1
0.63.0
0.62.1
0.62.0
0.61.0
0.60.0
0.59.0
0.58.0
0.57.0
0.56.0
0.55.0
0.54.0
0.53.0
0.52.0
0.51.0
0.50.0
0.49.0
0.48.0
0.47.0
0.46.0
0.45.0
0.44.0
0.43.0
0.42.0
0.41.0
0.40.0
0.39.0
0.38.0
0.37.0
0.36.0
0.35.1
0.35.0
0.34.0
0.33.1
0.33.0
0.32.1
0.32.0
0.31.0
0.30.2
0.30.1
0.30.0
0.29.2
0.29.1
0.29.0
0.28.2
0.28.1
0.28.0
0.27.0
0.26.1
0.26.0
0.25.0
0.24.0
0.23.0
0.22.3
0.22.2
0.22.1
0.22.0
0.21.0
0.20.0
0.19.3
0.19.2
0.19.1
0.19.0
0.18.1
0.18.0
0.18.0-rc1
0.17.1
0.17.0
0.16.0
0.15.0
0.14.0
0.13.0
0.12.0
0.11.0
0.10.1
0.10.0
0.9.0
0.8.0
0.7.0
0.6.0
0.5.0
0.4.0
0.4.0-rc1
0.3.1
0.3.0
0.2.0
retired
A standard library for the Gleam programming language
Current section
Files
Jump to
Current section
Files
src/gleam/bit_builder.gleam
//// BitBuilder is a type used for efficiently concatenating bits to create bit
//// strings.
////
//// If we append one bit string to another the bit strings must be copied to a
//// new location in memory so that they can sit together. This behaviour
//// enables efficient reading of the string but copying can be expensive,
//// especially if we want to join many bit strings together.
////
//// BitBuilder is different in that it can be joined together in constant
//// time using minimal memory, and then can be efficiently converted to a
//// bit string using the `to_bit_string` function.
////
//// On Erlang this type is compatible with Erlang's iolists.
import gleam/string_builder.{StringBuilder}
import gleam/bit_string
import gleam/list
if erlang {
pub external type BitBuilder
}
if javascript {
pub opaque type BitBuilder {
Bits(BitString)
Text(StringBuilder)
Many(List(BitBuilder))
}
}
/// Prepends a bit string to the start of a builder.
///
/// Runs in constant time.
///
pub fn prepend(to: BitBuilder, prefix: BitString) -> BitBuilder {
append_builder(from_bit_string(prefix), to)
}
/// Appends a bit string to the end of a builder.
///
/// Runs in constant time.
///
pub fn append(to: BitBuilder, suffix: BitString) -> BitBuilder {
append_builder(to, from_bit_string(suffix))
}
/// Prepends a builder onto the start of another.
///
/// Runs in constant time.
///
pub fn prepend_builder(to: BitBuilder, prefix: BitBuilder) -> BitBuilder {
append_builder(prefix, to)
}
/// Appends a builder onto the end of another.
///
/// Runs in constant time.
///
pub fn append_builder(
to first: BitBuilder,
suffix second: BitBuilder,
) -> BitBuilder {
do_append_builder(first, second)
}
if erlang {
external fn do_append_builder(
to: BitBuilder,
suffix: BitBuilder,
) -> BitBuilder =
"gleam_stdlib" "iodata_append"
}
if javascript {
fn do_append_builder(first: BitBuilder, second: BitBuilder) -> BitBuilder {
case second {
Many(builders) -> Many([first, ..builders])
_ -> Many([first, second])
}
}
}
/// Prepends a string onto the start of a builder.
///
/// Runs in constant time when running on Erlang.
/// Runs in linear time with the length of the string otherwise.
///
pub fn prepend_string(to: BitBuilder, prefix: String) -> BitBuilder {
append_builder(from_string(prefix), to)
}
/// Appends a string onto the end of a builder.
///
/// Runs in constant time when running on Erlang.
/// Runs in linear time with the length of the string otherwise.
///
pub fn append_string(to: BitBuilder, suffix: String) -> BitBuilder {
append_builder(to, from_string(suffix))
}
/// Joins a list of builders into a single builders.
///
/// Runs in constant time.
///
pub fn concat(builders: List(BitBuilder)) -> BitBuilder {
do_concat(builders)
}
if erlang {
external fn do_concat(List(BitBuilder)) -> BitBuilder =
"gleam_stdlib" "identity"
}
if javascript {
fn do_concat(builders: List(BitBuilder)) -> BitBuilder {
Many(builders)
}
}
/// Creates a new builder from a string.
///
/// Runs in constant time when running on Erlang.
/// Runs in linear time otherwise.
///
pub fn from_string(string: String) -> BitBuilder {
do_from_string(string)
}
if erlang {
external fn do_from_string(String) -> BitBuilder =
"gleam_stdlib" "wrap_list"
}
if javascript {
fn do_from_string(string: String) -> BitBuilder {
Text(string_builder.from_string(string))
}
}
/// Creates a new builder from a string builder.
///
/// Runs in constant time when running on Erlang.
/// Runs in linear time otherwise.
///
pub fn from_string_builder(builder: StringBuilder) -> BitBuilder {
do_from_string_builder(builder)
}
if erlang {
external fn do_from_string_builder(StringBuilder) -> BitBuilder =
"gleam_stdlib" "identity"
}
if javascript {
fn do_from_string_builder(builder: StringBuilder) -> BitBuilder {
Text(builder)
}
}
/// Creates a new builder from a bit string.
///
/// Runs in constant time.
///
pub fn from_bit_string(bits: BitString) -> BitBuilder {
do_from_bit_string(bits)
}
if erlang {
external fn do_from_bit_string(BitString) -> BitBuilder =
"gleam_stdlib" "wrap_list"
}
if javascript {
fn do_from_bit_string(bits: BitString) -> BitBuilder {
Bits(bits)
}
}
/// Turns an builder into a bit string.
///
/// Runs in linear time.
///
/// When running on Erlang this function is implemented natively by the
/// virtual machine and is highly optimised.
///
pub fn to_bit_string(builder: BitBuilder) -> BitString {
do_to_bit_string(builder)
}
if erlang {
external fn do_to_bit_string(BitBuilder) -> BitString =
"erlang" "list_to_bitstring"
}
if javascript {
fn do_to_bit_string(builder: BitBuilder) -> BitString {
[[builder]]
|> to_list([])
|> list.reverse
|> bit_string.concat
}
fn to_list(
stack: List(List(BitBuilder)),
acc: List(BitString),
) -> List(BitString) {
case stack {
[] -> acc
[[], ..remaining_stack] -> to_list(remaining_stack, acc)
[[Bits(bits), ..rest], ..remaining_stack] ->
to_list([rest, ..remaining_stack], [bits, ..acc])
[[Text(builder), ..rest], ..remaining_stack] -> {
let bits = bit_string.from_string(string_builder.to_string(builder))
to_list([rest, ..remaining_stack], [bits, ..acc])
}
[[Many(builders), ..rest], ..remaining_stack] ->
to_list([builders, rest, ..remaining_stack], acc)
}
}
}
/// Returns the size of the builder's content in bytes.
///
/// Runs in linear time.
///
pub fn byte_size(builder: BitBuilder) -> Int {
do_byte_size(builder)
}
if erlang {
external fn do_byte_size(BitBuilder) -> Int =
"erlang" "iolist_size"
}
if javascript {
fn do_byte_size(builder: BitBuilder) -> Int {
[[builder]]
|> to_list([])
|> list.fold(0, fn(acc, builder) { bit_string.byte_size(builder) + acc })
}
}