diff options
author | Louis Pilfold <louis@lpil.uk> | 2023-10-19 22:22:51 +0100 |
---|---|---|
committer | Louis Pilfold <louis@lpil.uk> | 2023-10-26 12:24:40 +0100 |
commit | 0831802978afac21d7d3b397d83715e46799e30c (patch) | |
tree | 44b6b1c371c672450c56a005decb0bc66818e28c /src | |
parent | b713e6fd185ad8b4da90f4b686aa9ecf0f60f0e4 (diff) | |
download | gleam_stdlib-0831802978afac21d7d3b397d83715e46799e30c.tar.gz gleam_stdlib-0831802978afac21d7d3b397d83715e46799e30c.zip |
bytes_builder
Diffstat (limited to 'src')
-rw-r--r-- | src/gleam/bit_builder.gleam | 238 | ||||
-rw-r--r-- | src/gleam/bytes_builder.gleam | 188 |
2 files changed, 221 insertions, 205 deletions
diff --git a/src/gleam/bit_builder.gleam b/src/gleam/bit_builder.gleam index 319a823..ce6fe52 100644 --- a/src/gleam/bit_builder.gleam +++ b/src/gleam/bit_builder.gleam @@ -1,252 +1,80 @@ -//// 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. +//// This module has been deprecated in favour of `gleam/bytes_builder`. +import gleam/bytes_builder import gleam/string_builder.{type StringBuilder} -@target(javascript) -import gleam/list -@target(javascript) -import gleam/bit_string - -@target(erlang) -pub type BitBuilder - -@target(javascript) -pub opaque type BitBuilder { - Bits(BitArray) - Text(StringBuilder) - Many(List(BitBuilder)) -} -/// Create an empty `BitBuilder`. Useful as the start of a pipe chaining many -/// builders together. -/// +pub type BitBuilder = + bytes_builder.BytesBuilder + +@deprecated("Please use the `gleam/bytes_builder` module instead.") pub fn new() -> BitBuilder { - do_concat([]) + bytes_builder.new() } -/// Prepends a bit string to the start of a builder. -/// -/// Runs in constant time. -/// +@deprecated("Please use the `gleam/bytes_builder` module instead.") pub fn prepend(to: BitBuilder, prefix: BitArray) -> BitBuilder { - append_builder(from_bit_string(prefix), to) + bytes_builder.prepend(to, prefix) } -/// Appends a bit string to the end of a builder. -/// -/// Runs in constant time. -/// +@deprecated("Please use the `gleam/bytes_builder` module instead.") pub fn append(to: BitBuilder, suffix: BitArray) -> BitBuilder { - append_builder(to, from_bit_string(suffix)) + bytes_builder.append(to, suffix) } -/// Prepends a builder onto the start of another. -/// -/// Runs in constant time. -/// +@deprecated("Please use the `gleam/bytes_builder` module instead.") pub fn prepend_builder(to: BitBuilder, prefix: BitBuilder) -> BitBuilder { - append_builder(prefix, to) + bytes_builder.prepend_builder(to, prefix) } -/// Appends a builder onto the end of another. -/// -/// Runs in constant time. -/// +@deprecated("Please use the `gleam/bytes_builder` module instead.") pub fn append_builder( to first: BitBuilder, suffix second: BitBuilder, ) -> BitBuilder { - do_append_builder(first, second) -} - -@target(erlang) -@external(erlang, "gleam_stdlib", "iodata_append") -fn do_append_builder(to to: BitBuilder, suffix suffix: BitBuilder) -> BitBuilder - -@target(javascript) -fn do_append_builder(first: BitBuilder, second: BitBuilder) -> BitBuilder { - case second { - Many(builders) -> Many([first, ..builders]) - _ -> Many([first, second]) - } + bytes_builder.append_builder(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. -/// +@deprecated("Please use the `gleam/bytes_builder` module instead.") pub fn prepend_string(to: BitBuilder, prefix: String) -> BitBuilder { - append_builder(from_string(prefix), to) + bytes_builder.prepend_string(to, prefix) } -/// 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. -/// +@deprecated("Please use the `gleam/bytes_builder` module instead.") pub fn append_string(to: BitBuilder, suffix: String) -> BitBuilder { - append_builder(to, from_string(suffix)) + bytes_builder.append_string(to, suffix) } -/// Joins a list of builders into a single builder. -/// -/// Runs in constant time. -/// +@deprecated("Please use the `gleam/bytes_builder` module instead.") pub fn concat(builders: List(BitBuilder)) -> BitBuilder { - do_concat(builders) -} - -@target(erlang) -@external(erlang, "gleam_stdlib", "identity") -fn do_concat(a: List(BitBuilder)) -> BitBuilder - -@target(javascript) -fn do_concat(builders: List(BitBuilder)) -> BitBuilder { - Many(builders) + bytes_builder.concat(builders) } -/// Joins a list of bit strings into a single builder. -/// -/// Runs in constant time. -/// +@deprecated("Please use the `gleam/bytes_builder` module instead.") pub fn concat_bit_strings(bits: List(BitArray)) -> BitBuilder { - do_concat_bit_strings(bits) + bytes_builder.concat_bit_arrays(bits) } -@target(erlang) -@external(erlang, "gleam_stdlib", "identity") -fn do_concat_bit_strings(a: List(BitArray)) -> BitBuilder - -@target(javascript) -fn do_concat_bit_strings(bits: List(BitArray)) -> BitBuilder { - bits - |> list.map(fn(b) { from_bit_string(b) }) - |> concat() -} - -/// Creates a new builder from a string. -/// -/// Runs in constant time when running on Erlang. -/// Runs in linear time otherwise. -/// +@deprecated("Please use the `gleam/bytes_builder` module instead.") pub fn from_string(string: String) -> BitBuilder { - do_from_string(string) -} - -@target(erlang) -@external(erlang, "gleam_stdlib", "wrap_list") -fn do_from_string(a: String) -> BitBuilder - -@target(javascript) -fn do_from_string(string: String) -> BitBuilder { - Text(string_builder.from_string(string)) + bytes_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. -/// +@deprecated("Please use the `gleam/bytes_builder` module instead.") pub fn from_string_builder(builder: StringBuilder) -> BitBuilder { - do_from_string_builder(builder) -} - -@target(erlang) -@external(erlang, "gleam_stdlib", "wrap_list") -fn do_from_string_builder(a: StringBuilder) -> BitBuilder - -@target(javascript) -fn do_from_string_builder(builder: StringBuilder) -> BitBuilder { - Text(builder) + bytes_builder.from_string_builder(builder) } -/// Creates a new builder from a bit string. -/// -/// Runs in constant time. -/// +@deprecated("Please use the `gleam/bytes_builder` module instead.") pub fn from_bit_string(bits: BitArray) -> BitBuilder { - do_from_bit_string(bits) -} - -@target(erlang) -@external(erlang, "gleam_stdlib", "wrap_list") -fn do_from_bit_string(a: BitArray) -> BitBuilder - -@target(javascript) -fn do_from_bit_string(bits: BitArray) -> BitBuilder { - Bits(bits) + bytes_builder.from_bit_array(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. -/// +@deprecated("Please use the `gleam/bytes_builder` module instead.") pub fn to_bit_string(builder: BitBuilder) -> BitArray { - do_to_bit_string(builder) -} - -@target(erlang) -@external(erlang, "erlang", "list_to_bitstring") -fn do_to_bit_string(a: BitBuilder) -> BitArray - -@target(javascript) -fn do_to_bit_string(builder: BitBuilder) -> BitArray { - [[builder]] - |> to_list([]) - |> list.reverse - |> bit_string.concat -} - -@target(javascript) -fn to_list(stack: List(List(BitBuilder)), acc: List(BitArray)) -> List(BitArray) { - 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) - } + bytes_builder.to_bit_array(builder) } -/// Returns the size of the builder's content in bytes. -/// -/// Runs in linear time. -/// +@deprecated("Please use the `gleam/bytes_builder` module instead.") pub fn byte_size(builder: BitBuilder) -> Int { - do_byte_size(builder) -} - -@target(erlang) -@external(erlang, "erlang", "iolist_size") -fn do_byte_size(a: BitBuilder) -> Int - -@target(javascript) -fn do_byte_size(builder: BitBuilder) -> Int { - [[builder]] - |> to_list([]) - |> list.fold(0, fn(acc, builder) { bit_string.byte_size(builder) + acc }) + bytes_builder.byte_size(builder) } diff --git a/src/gleam/bytes_builder.gleam b/src/gleam/bytes_builder.gleam new file mode 100644 index 0000000..da341ec --- /dev/null +++ b/src/gleam/bytes_builder.gleam @@ -0,0 +1,188 @@ +//// BytesBuilder is a type used for efficiently concatenating bytes together +//// without copying. +//// +//// If we append one bit array to another the bit arrays 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 arrays together. +//// +//// BytesBuilder is different in that it can be joined together in constant +//// time using minimal memory, and then can be efficiently converted to a +//// bit array using the `to_bit_array` function. +//// +//// Byte builders are always byte aligned, so that a number of bits that is not +//// divisible by 8 will be padded with 0s. +//// +//// On Erlang this type is compatible with Erlang's iolists. + +// TODO: pad bit arrays to byte boundaries when adding to a builder. +import gleam/string_builder.{type StringBuilder} +import gleam/list +import gleam/bit_array + +pub opaque type BytesBuilder { + Bytes(BitArray) + Text(StringBuilder) + Many(List(BytesBuilder)) +} + +/// Create an empty `BytesBuilder`. Useful as the start of a pipe chaining many +/// builders together. +/// +pub fn new() -> BytesBuilder { + concat([]) +} + +/// Prepends a bit array to the start of a builder. +/// +/// Runs in constant time. +/// +pub fn prepend(to: BytesBuilder, prefix: BitArray) -> BytesBuilder { + append_builder(from_bit_array(prefix), to) +} + +/// Appends a bit array to the end of a builder. +/// +/// Runs in constant time. +/// +pub fn append(to: BytesBuilder, suffix: BitArray) -> BytesBuilder { + append_builder(to, from_bit_array(suffix)) +} + +/// Prepends a builder onto the start of another. +/// +/// Runs in constant time. +/// +pub fn prepend_builder(to: BytesBuilder, prefix: BytesBuilder) -> BytesBuilder { + append_builder(prefix, to) +} + +/// Appends a builder onto the end of another. +/// +/// Runs in constant time. +/// +@external(erlang, "gleam_stdlib", "iodata_append") +pub fn append_builder( + to first: BytesBuilder, + suffix second: BytesBuilder, +) -> BytesBuilder { + 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: BytesBuilder, prefix: String) -> BytesBuilder { + 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: BytesBuilder, suffix: String) -> BytesBuilder { + append_builder(to, from_string(suffix)) +} + +/// Joins a list of builders into a single builder. +/// +/// Runs in constant time. +/// +@external(erlang, "gleam_stdlib", "identity") +pub fn concat(builders: List(BytesBuilder)) -> BytesBuilder { + Many(builders) +} + +/// Joins a list of bit arrays into a single builder. +/// +/// Runs in constant time. +/// +@external(erlang, "gleam_stdlib", "identity") +pub fn concat_bit_arrays(bits: List(BitArray)) -> BytesBuilder { + bits + |> list.map(fn(b) { from_bit_array(b) }) + |> concat() +} + +/// Creates a new builder from a string. +/// +/// Runs in constant time when running on Erlang. +/// Runs in linear time otherwise. +/// +@external(erlang, "gleam_stdlib", "wrap_list") +pub fn from_string(string: String) -> BytesBuilder { + 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. +/// +@external(erlang, "gleam_stdlib", "wrap_list") +pub fn from_string_builder(builder: StringBuilder) -> BytesBuilder { + Text(builder) +} + +/// Creates a new builder from a bit array. +/// +/// Runs in constant time. +/// +@external(erlang, "gleam_stdlib", "wrap_list") +pub fn from_bit_array(bits: BitArray) -> BytesBuilder { + Bytes(bits) +} + +/// Turns an builder into a bit array. +/// +/// Runs in linear time. +/// +/// When running on Erlang this function is implemented natively by the +/// virtual machine and is highly optimised. +/// +@external(erlang, "erlang", "list_to_bitstring") +pub fn to_bit_array(builder: BytesBuilder) -> BitArray { + [[builder]] + |> to_list([]) + |> list.reverse + |> bit_array.concat +} + +fn to_list( + stack: List(List(BytesBuilder)), + acc: List(BitArray), +) -> List(BitArray) { + case stack { + [] -> acc + + [[], ..remaining_stack] -> to_list(remaining_stack, acc) + + [[Bytes(bits), ..rest], ..remaining_stack] -> + to_list([rest, ..remaining_stack], [bits, ..acc]) + + [[Text(builder), ..rest], ..remaining_stack] -> { + let bits = bit_array.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. +/// +@external(erlang, "erlang", "iolist_size") +pub fn byte_size(builder: BytesBuilder) -> Int { + [[builder]] + |> to_list([]) + |> list.fold(0, fn(acc, builder) { bit_array.byte_size(builder) + acc }) +} |