Base1

Base1 is an Elixir library for encoding and decoding Base1 binaries.

Base1 is an Elixir port of Base1. Please read the "How it Works" section to learn more.

Binary encoding inspired by unary numbers.

The main reason Base1 exists is to support Base1 encoding and decoding as part of Multibase and other implementations that require dispatch to Base codecs.

Why

You probably should not use Base1 encoding unless you are building a multi-codec for Base encoding/decoding of some sort. It is grossly inefficient, slow, and according to some enlightened few, an abomination.

If these concerns still do not deter you, then you may find the following use-cases applicable for Base1:

Usage

If you want semi-unreasonable performance, it is best to work with lengths instead of binaries. You can do so as either integers or integers encoded as strings. The latter if for the case where your interface demands a binary (string) type returned.

Let's first be unreasonable and encode a binary to see why lengths are better:

# let's encode something very simple
Base1.encode!(<<4>>)
"AAAAA"

Base1.encode!("1")
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"

# pay our respects
 Base1.encode!("F")
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"

# If we want to give our CPU a workout
Base1.encode!("hello")
# output: lots of As, may take awhile

# we need to be careful encoding things that will produce giant binaries, or otherwise encode length instead
Base1.encode("huge binary to encode, but not really")
:error

Base1.encode!("huge binary to encode, but not really")
# raises ** (ArgumentError) Data is too large to binary encode as Base1 52448966988976840295297313244602659742710332201806431846112755526506725455904981438524794 bytes are required. Use encode_length/1 and decode_length/1 instead.
# perhaps we should listen....

Likewise, we can decode our binaries and show that encoding and decoding are transparent.

Base1.decode!("AAAAA")
<<4>>

Base1.decode!("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA")
"1"

#indeed
Base1.decode("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA")
"F"

# Fully transparent
Base1.encode!(<<1, 2, 3>>) |> Base1.decode!()
<<1, 2, 3>>

# We can handle errors too if we want pattern matching
Base1.encode!(<<1, 2, 3>>) |> Base1.decode()
{:ok, <<1, 2, 3>>}

# bad input data
Base1.decode("This is not base1, for it it does not scream to us")
:error

Now let's take a slightly more sane approach using lengths, all things considered.

# encodes much faster than binary
Base1.encode_length("hello")                                                           
452690013552

Base1.decode_length!(452690013552)  
"hello"

# we can use our error handling flavor as before too
Base1.decode_length(452690013552)  
{:ok, "hello"}


# also fully transparent and works better for larger input binaries than `encode/1`
 Base1.encode_length("Lettuceless Burritos Are Best Burritos") |> Base1.decode_length!()
"Lettuceless Burritos Are Best Burritos"

Fortunately, we can also take another approach if we really want our encoded data as binary, not numbers.

Base1.encode_length_bin("hello")
"452690013552"

# bigger strings now work too, just like with `encode_length/1`
Base1.encode_length_bin("Big strings are better when they are string cheese")
"680015850116389379759636622830644424760636765950574532108918486844800528727880164921633865142384902132161451816034661478"

# decoding works as everything else before it did
Base1.decode_length_bin!("452690013552")
"hello"

# and as before, we maintain transparency
Base1.encode_length_bin("not as fun as encoding lots of As") |> Base1.decode_length_bin!() 
"not as fun as encoding lots of As"

Limitations

Installation

Base1 is available via Hex. The package can be installed by adding base1 to your list of dependencies in mix.exs:

def deps do
  [
    {:base1, "~> 0.1.0"}
  ]
end

API Documentation can be found at https://hexdocs.pm/base1/base1.html.