Skip to content
This repository was archived by the owner on Sep 10, 2022. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 10 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
# base32

[![Build Status](https://travis-ci.org/noumar/base32.svg?branch=master)](https://travis-ci.org/noumar/base32)
[![Docs](http://docrystal.org/badge.svg?style=round)](http://docrystal.org/github.com/noumar/base32)
[![Crystal CI](https://github.com/didactic-drunk/base32/actions/workflows/crystal.yml/badge.svg)](https://github.com/didactic-drunk/base32/actions/workflows/crystal.yml)
[![GitHub release](https://img.shields.io/github/release/didactic-drunk/base32.svg)](https://github.com/didactic-drunk/base32/releases)
![GitHub commits since latest release (by date) for a branch](https://img.shields.io/github/commits-since/didactic-drunk/base32/latest)
[![Docs](https://img.shields.io/badge/docs-available-brightgreen.svg)](https://didactic-drunk.github.io/base32/master)

Provides encoding and decoding of base32 and base32hex as defined in RFC 4648.

Maintained here temporarily (or permanently) until @noumar returns.

## Installation

Add this to your application's `shard.yml`:

```yaml
dependencies:
base32:
github: noumar/base32
github: didactic-drunk/base32
```

## Usage
Expand All @@ -33,12 +36,13 @@ TODO: Write development instructions here

## Contributing

1. Fork it ( https://github.com/noumar/base32/fork )
1. Fork it ( https://github.com/didactic-drunk/base32/fork )
2. Create your feature branch (git checkout -b my-new-feature)
3. Commit your changes (git commit -am 'Add some feature')
4. Push to the branch (git push origin my-new-feature)
5. Create a new Pull Request

## Contributors

- [noumar](https://github.com/noumar) / Mikael Karlsson - creator, maintainer
- [noumar](https://github.com/noumar) / Mikael Karlsson - creator
- [didactic-drunk](https://github.com/didactic-drunk) - current maintainer
16 changes: 16 additions & 0 deletions spec/base32_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ describe Base32 do
Base32.encode("foobar").should eq("MZXW6YTBOI======")

Base32.encode("Hello World!").should eq("JBSWY3DPEBLW64TMMQQQ====")

# Compared with Ruby version
Base32.encode(Bytes.new(4)).should eq("AAAAAAA=")
end

it "encodes to base32 (w/o padding)" do
Expand Down Expand Up @@ -89,4 +92,17 @@ describe Base32 do
Base32.hex_decode_string("CPNMUOJ1").should eq("fooba")
Base32.hex_decode_string("CPNMUOJ1E8").should eq("foobar")
end

describe "NUL byte" do
str = "\0foo\0bar\0"
bstr = "ABTG63YAMJQXEAA="

it "encode" do
Base32.encode(str).should eq bstr
end

it "decode" do
Base32.decode_string(bstr).should eq str
end
end
end
76 changes: 34 additions & 42 deletions src/base32.cr
Original file line number Diff line number Diff line change
Expand Up @@ -32,65 +32,57 @@ module Base32

# :nodoc:
private def to_base32(data, pad : Bool, chars : Array(Char)) : String
mio = IO::Memory.new((data.bytesize / 5) * 8)

data.to_slice.each_slice(5) do |slice|
bits = 0_u64
0.to(slice.size - 1) do |j|
bits = bits | (slice[j].to_u64 << (4 - j)*8)
ssize = data.bytesize * 8 // 5
ssize += 7 if pad
String.build(ssize) do |sb|
bits = 0
b = 0
data.to_slice.each do |c|
b = (b << 8) | c
bits += 8
while bits >= 5
bits -= 5
sb << chars[b >> bits]
mask = (1 << bits) - 1
b &= mask
end
end

mask = 0x1F_u64 << 35
7.to(0) do |i|
num = (bits & mask) >> i*5
mio << chars[num]
mask = (mask >> 5)
if bits > 0
sb << chars[b << (5 - bits)]
end
end

# Exclude empty trailing chars
while mio.buffer[mio.pos - 1] == chars[0].ord
mio.pos -= 1
end

# Fill with padding
until (mio.pos % 8 == 0)
mio << PAD
if pad
rem = sb.bytesize % 8
(8 - rem).times { sb << PAD } if rem > 0
end
end

# Exclude padding if not wanted
sl = mio.to_slice
while sl[-1] == PAD.ord
sl = sl[0, sl.size - 1]
end if sl.size > 0 && pad == false

String.new(sl)
end

IGNORE_CHARS = ['\n'.ord, '\r'.ord, '='.ord]

# :nodoc:
private def from_base32(data, map : Hash(Char, Int)) : Slice(UInt8)
mio = IO::Memory.new((data.bytesize / 8) * 5)
private def from_base32(data, map : Hash(Char, Int)) : Bytes
mio = IO::Memory.new((data.bytesize // 8) * 5)

data.to_slice.select { |s| !['\n'.ord, '\r'.ord, '='.ord].includes?(s) }.each_slice(8) do |slice|
data.to_slice.select { |s| !IGNORE_CHARS.includes?(s) }.each_slice(8) do |slice|
bits = 0_u64
0.to(slice.size - 1) do |j|
bits = bits | (map[slice[j].chr].to_u64 << (7 - j)*5)
slice.each_with_index do |b, j|
bits = bits | (map[b.chr].to_u64 << (7 - j)*5)
end

mask = 0xFF_u64 << 32
4.to(0) do |i|

rem_bytes = slice.size * 5 // 8

4.downto(5 - rem_bytes) do |i|
num = (bits & mask) >> i*8
mio.write_byte(num.to_u8)
mask = (mask >> 8)
end
end

# Exclude trailing zero bytes
sl = mio.to_slice
while sl[-1] == 0
sl = sl[0, sl.size - 1]
end if sl.size > 0
sl
mio.to_slice
end

# Encode data as base32 with padding, or without if `pad` = false
Expand All @@ -99,7 +91,7 @@ module Base32
end

# Decode base32 data, regardless if padded or not
def decode(data) : Slice(UInt8)
def decode(data) : Bytes
from_base32(data, DEC_STD)
end

Expand All @@ -114,7 +106,7 @@ module Base32
end

# Decode base32hex data, regardless if padded or not
def hex_decode(data) : Slice(UInt8)
def hex_decode(data) : Bytes
from_base32(data, DEC_HEX)
end

Expand Down