Skip to content

Commit 316cda7

Browse files
committed
feat: Implement base64decode
1 parent bd7d43a commit 316cda7

File tree

4 files changed

+70
-2
lines changed

4 files changed

+70
-2
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ All algorithms use _only_ builtins and create _no subshells_
77
## Algorithms
88

99
- [x] base64 encode
10-
- [ ] base64 decode
10+
- [x] base64 decode
1111
- [ ] md5
1212
- [ ] sha1
1313
- [ ] sha256

pkg/lib/public/bash-algo.sh

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,3 +42,53 @@ algo.base64encode() {
4242
REPLY+="${output_byte_one}${output_byte_two}${output_byte_three}${output_byte_four}"
4343
done
4444
}
45+
46+
algo.base64decode() {
47+
unset REPLY; REPLY=
48+
local input="$1"
49+
50+
local input_byte_{one,two,three,four}=
51+
local index_{one,two,three,four}=
52+
local char_str="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
53+
for ((i=0; i<${#input}; i=i+4)); do
54+
printf -v input_byte_one '%c' "${input:$i:1}"
55+
printf -v input_byte_two '%c' "${input:$i+1:1}"
56+
printf -v input_byte_three '%c' "${input:$i+2:1}"
57+
printf -v input_byte_four '%c' "${input:$i+3:1}"
58+
59+
# TODO: `set -o nocasematch` likely affects this
60+
# Equivalent to char_str.indexOf(input)
61+
index_one="${char_str%$input_byte_one*}"; index_one=${#index_one}
62+
index_two="${char_str%$input_byte_two*}"; index_two=${#index_two}
63+
index_three="${char_str%$input_byte_three*}"; index_three=${#index_three}
64+
index_four="${char_str%$input_byte_four*}"; index_four=${#index_four}
65+
66+
# Output byte one
67+
bits_one=$(( ((index_one & 2#00111111) << 2) | ((index_two >> 4) & 2#00000011) ))
68+
printf -v output_byte_one '%03o' "$bits_one"
69+
printf -v output_byte_one "\\$output_byte_one"
70+
71+
# An index of '64' means char_str.`indexOf(input)` could not find the substring
72+
# i.e. `-1` in traditional languages. This occurs when an `=` is found
73+
74+
# Output byte two
75+
if ((index_three == 64)); then
76+
output_byte_two=
77+
else
78+
bits_two=$(( ((index_two & 2#00001111) << 4) | ((index_three >> 2) & 2#00001111) ))
79+
printf -v output_byte_two '%03o' "$bits_two"
80+
printf -v output_byte_two "\\$output_byte_two"
81+
fi
82+
83+
# Output byte three
84+
if ((index_four == 64)); then
85+
output_byte_three=
86+
else
87+
bits_three=$(( ((index_three & 2#00000011) << 6) | (index_four & 2#00111111) ))
88+
printf -v output_byte_three '%03o' "$bits_three"
89+
printf -v output_byte_three "\\$output_byte_three"
90+
fi
91+
92+
REPLY+="${output_byte_one}${output_byte_two}${output_byte_three}"
93+
done
94+
}

tests/all.bats

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
load './util/init.sh'
44

55
@test "base64encode" {
6-
algo.base64encode "paraguay-uruguay"
6+
algo.base64encode 'paraguay-uruguay'
77
assert [ "$REPLY" = 'cGFyYWd1YXktdXJ1Z3VheQ==' ]
88

99
test_util.base64encode 'A'
@@ -14,3 +14,16 @@ load './util/init.sh'
1414
test_util.base64encode 'kafka38quebec'
1515
test_util.base64encode 'EcHo##8(0}}'
1616
}
17+
18+
@test "base64decode" {
19+
algo.base64decode 'cGFyYWd1YXktdXJ1Z3VheQ=='
20+
assert [ "$REPLY" = 'paraguay-uruguay' ]
21+
22+
test_util.base64decode 'QQ=='
23+
test_util.base64decode 'QUI='
24+
test_util.base64decode 'QUJD'
25+
test_util.base64decode 'QUJDRA=='
26+
test_util.base64decode 'V09PRg=='
27+
test_util.base64decode 'a2Fma2EzOHF1ZWJlYw=='
28+
test_util.base64decode 'RWNIbyMjOCgwfX0='
29+
}

tests/util/test_util.sh

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,8 @@ test_util.base64encode() {
44
algo.base64encode "$1"
55
assert [ "$REPLY" = "$(printf '%s' "$1" | base64)" ]
66
}
7+
8+
test_util.base64decode() {
9+
algo.base64decode "$1"
10+
assert [ "$REPLY" = "$(printf '%s' "$1" | base64 --decode)" ]
11+
}

0 commit comments

Comments
 (0)