aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLouis Pilfold <louis@lpil.uk>2020-06-26 17:31:53 +0100
committerLouis Pilfold <louis@lpil.uk>2020-06-30 12:14:27 +0100
commitd90199e49c75d051853af52156ee8adaddc58564 (patch)
tree63a116d0a011d82bc56e2283a0652e812f35dabd
parent8dab27e6cd093c509e2ca0379dfd41524edc17dc (diff)
downloadgleam_stdlib-d90199e49c75d051853af52156ee8adaddc58564.tar.gz
gleam_stdlib-d90199e49c75d051853af52156ee8adaddc58564.zip
BitBuilder
-rw-r--r--src/gleam/bit_builder.gleam90
-rw-r--r--src/gleam/bit_string.gleam3
-rw-r--r--test/gleam/bit_string_builder_test.gleam58
3 files changed, 149 insertions, 2 deletions
diff --git a/src/gleam/bit_builder.gleam b/src/gleam/bit_builder.gleam
new file mode 100644
index 0000000..c3c88b0
--- /dev/null
+++ b/src/gleam/bit_builder.gleam
@@ -0,0 +1,90 @@
+import gleam/bit_string.{BitString}
+
+/// 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.
+///
+pub external type BitBuilder
+
+/// Prepend a bit string to the start of a builder.
+///
+/// Runs in constant time.
+///
+pub external fn prepend(to: BitBuilder, prefix: BitString) -> BitBuilder =
+ "gleam_stdlib" "iodata_prepend"
+
+/// Append a bit string to the end of a builder.
+///
+/// Runs in constant time.
+///
+pub external fn append(to: BitBuilder, suffix: BitString) -> BitBuilder =
+ "gleam_stdlib" "iodata_append"
+
+/// Prepend a builder onto the start of another.
+///
+/// Runs in constant time.
+///
+pub external fn prepend_builder(
+ to: BitBuilder,
+ prefix: BitBuilder,
+) -> BitBuilder =
+ "gleam_stdlib" "iodata_prepend"
+
+/// Append a builder onto the end of another.
+///
+/// Runs in constant time.
+///
+pub external fn append_builder(
+ to: BitBuilder,
+ suffix: BitBuilder,
+) -> BitBuilder =
+ "gleam_stdlib" "iodata_append"
+
+/// Prepend a string onto the start of a builder.
+///
+/// Runs in constant time.
+///
+pub external fn prepend_string(to: BitBuilder, prefix: String) -> BitBuilder =
+ "gleam_stdlib" "iodata_prepend"
+
+/// Append a string onto the end of a builder.
+///
+/// Runs in constant time.
+///
+pub external fn append_string(to: BitBuilder, suffix: String) -> BitBuilder =
+ "gleam_stdlib" "iodata_append"
+
+/// Joins a list of builders into a single builders.
+///
+/// Runs in constant time.
+///
+pub external fn concat(List(BitBuilder)) -> BitBuilder =
+ "gleam_stdlib" "identity"
+
+/// Create a new builder from a bit string.
+///
+/// Runs in constant time.
+///
+pub external fn from_bit_string(BitString) -> BitBuilder =
+ "gleam_stdlib" "identity"
+
+/// Turns an builder into a bit string.
+///
+/// This function is implemented natively by the virtual machine and is highly
+/// optimised.
+///
+pub external fn to_bit_string(BitBuilder) -> BitString =
+ "erlang" "list_to_bitstring"
+
+/// Returns the size of the builder's content in bytes.
+///
+pub external fn byte_size(BitBuilder) -> Int =
+ "erlang" "iolist_size"
diff --git a/src/gleam/bit_string.gleam b/src/gleam/bit_string.gleam
index 1176418..99d91b1 100644
--- a/src/gleam/bit_string.gleam
+++ b/src/gleam/bit_string.gleam
@@ -2,8 +2,7 @@
//// The BitString type should be used instead of a String type when not utf8
//// encoded.
-// TODO: determine which of these functions once we have bit string syntax
-pub external type BitString
+pub type BitString = BitString
/// Convert a utf8 String type into a raw BitString type.
///
diff --git a/test/gleam/bit_string_builder_test.gleam b/test/gleam/bit_string_builder_test.gleam
new file mode 100644
index 0000000..43e010a
--- /dev/null
+++ b/test/gleam/bit_string_builder_test.gleam
@@ -0,0 +1,58 @@
+import gleam/should
+import gleam/bit_builder
+
+pub fn builder_test() {
+ let data = bit_builder.from_bit_string(<<1>>)
+ |> bit_builder.append(<<2>>)
+ |> bit_builder.append(<<3>>)
+ |> bit_builder.prepend(<<0>>)
+
+ data
+ |> bit_builder.to_bit_string
+ |> should.equal(<<0, 1, 2, 3>>)
+
+ data
+ |> bit_builder.byte_size
+ |> should.equal(4)
+}
+
+pub fn builder_with_strings_test() {
+ let data = bit_builder.from_bit_string(<<1>>)
+ |> bit_builder.append_string("2")
+ |> bit_builder.append_string("3")
+ |> bit_builder.prepend_string("0")
+
+ data
+ |> bit_builder.to_bit_string
+ |> should.equal(<<"0":utf8, 1, "2":utf8, "3":utf8>>)
+
+ data
+ |> bit_builder.byte_size
+ |> should.equal(4)
+}
+
+pub fn builder_with_builders_test() {
+ let data = bit_builder.from_bit_string(<<1>>)
+ |> bit_builder.append_builder(bit_builder.from_bit_string(<<2>>))
+ |> bit_builder.append_builder(bit_builder.from_bit_string(<<3>>))
+ |> bit_builder.prepend_builder(bit_builder.from_bit_string(<<0>>))
+
+ data
+ |> bit_builder.to_bit_string
+ |> should.equal(<<0, 1, 2, 3>>)
+
+ data
+ |> bit_builder.byte_size
+ |> should.equal(4)
+}
+
+pub fn concat_test() {
+ [
+ bit_builder.from_bit_string(<<1, 2>>),
+ bit_builder.from_bit_string(<<3, 4>>),
+ bit_builder.from_bit_string(<<5, 6>>),
+ ]
+ |> bit_builder.concat
+ |> bit_builder.to_bit_string
+ |> should.equal(<<1, 2, 3, 4, 5, 6>>)
+}