1% Licensed under the Apache License, Version 2.0 (the "License"); you may not 2% use this file except in compliance with the License. You may obtain a copy of 3% the License at 4% 5% http://www.apache.org/licenses/LICENSE-2.0 6% 7% Unless required by applicable law or agreed to in writing, software 8% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 9% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 10% License for the specific language governing permissions and limitations under 11% the License. 12 13-module(couch_base32). 14 15-export([encode/1, decode/1]). 16 17-define(SET, <<"ABCDEFGHIJKLMNOPQRSTUVWXYZ234567">>). 18 19 20-spec encode(binary()) -> binary(). 21encode(Plain) when is_binary(Plain) -> 22 IoList = encode(Plain, 0, byte_size(Plain) * 8, []), 23 iolist_to_binary(lists:reverse(IoList)). 24 25encode(_Plain, _ByteOffset, 0, Acc) -> 26 Acc; 27 28encode(Plain, ByteOffset, BitsRemaining, Acc) when BitsRemaining == 8 -> 29 <<A:5, B:3>> = binary:part(Plain, ByteOffset, 1), 30 [<<(binary:at(?SET, A)), 31 (binary:at(?SET, B bsl 2)), 32 "======">> | Acc]; 33 34encode(Plain, ByteOffset, BitsRemaining, Acc) when BitsRemaining == 16 -> 35 <<A:5, B:5, C:5, D:1>> = binary:part(Plain, ByteOffset, 2), 36 [<<(binary:at(?SET, A)), 37 (binary:at(?SET, B)), 38 (binary:at(?SET, C)), 39 (binary:at(?SET, D bsl 4)), 40 "====">> | Acc]; 41 42encode(Plain, ByteOffset, BitsRemaining, Acc) when BitsRemaining == 24 -> 43 <<A:5, B:5, C:5, D:5, E:4>> = binary:part(Plain, ByteOffset, 3), 44 [<<(binary:at(?SET, A)), 45 (binary:at(?SET, B)), 46 (binary:at(?SET, C)), 47 (binary:at(?SET, D)), 48 (binary:at(?SET, E bsl 1)), 49 "===">> | Acc]; 50 51encode(Plain, ByteOffset, BitsRemaining, Acc) when BitsRemaining == 32 -> 52 <<A:5, B:5, C:5, D:5, E:5, F:5, G:2>> = binary:part(Plain, ByteOffset, 4), 53 [<<(binary:at(?SET, A)), 54 (binary:at(?SET, B)), 55 (binary:at(?SET, C)), 56 (binary:at(?SET, D)), 57 (binary:at(?SET, E)), 58 (binary:at(?SET, F)), 59 (binary:at(?SET, G bsl 3)), 60 "=">> | Acc]; 61 62encode(Plain, ByteOffset, BitsRemaining, Acc) when BitsRemaining >= 40 -> 63 <<A:5, B:5, C:5, D:5, E:5, F:5, G:5, H:5>> = 64 binary:part(Plain, ByteOffset, 5), 65 Output = <<(binary:at(?SET, A)), 66 (binary:at(?SET, B)), 67 (binary:at(?SET, C)), 68 (binary:at(?SET, D)), 69 (binary:at(?SET, E)), 70 (binary:at(?SET, F)), 71 (binary:at(?SET, G)), 72 (binary:at(?SET, H))>>, 73 encode(Plain, ByteOffset + 5, BitsRemaining - 40, [Output | Acc]). 74 75 76-spec decode(binary()) -> binary(). 77decode(Encoded) when is_binary(Encoded) -> 78 IoList = decode(Encoded, 0, []), 79 iolist_to_binary(lists:reverse(IoList)). 80 81decode(Encoded, ByteOffset, Acc) when ByteOffset == byte_size(Encoded) -> 82 Acc; 83decode(Encoded, ByteOffset, Acc) -> 84 case binary:part(Encoded, ByteOffset, 8) of 85 <<A:1/binary, B:1/binary, "======">> -> 86 [<<(find_in_set(A)):5, 87 (find_in_set(B) bsr 2):3>> | Acc]; 88 <<A:1/binary, B:1/binary, C:1/binary, D:1/binary, "====">> -> 89 [<<(find_in_set(A)):5, 90 (find_in_set(B)):5, 91 (find_in_set(C)):5, 92 (find_in_set(D) bsr 4):1>> | Acc]; 93 <<A:1/binary, B:1/binary, C:1/binary, D:1/binary, E:1/binary, "===">> -> 94 [<<(find_in_set(A)):5, 95 (find_in_set(B)):5, 96 (find_in_set(C)):5, 97 (find_in_set(D)):5, 98 (find_in_set(E) bsr 1):4>> | Acc]; 99 <<A:1/binary, B:1/binary, C:1/binary, D:1/binary, 100 E:1/binary, F:1/binary, G:1/binary, "=">> -> 101 [<<(find_in_set(A)):5, 102 (find_in_set(B)):5, 103 (find_in_set(C)):5, 104 (find_in_set(D)):5, 105 (find_in_set(E)):5, 106 (find_in_set(F)):5, 107 (find_in_set(G) bsr 3):2>> | Acc]; 108 <<A:1/binary, B:1/binary, C:1/binary, D:1/binary, 109 E:1/binary, F:1/binary, G:1/binary, H:1/binary>> -> 110 decode(Encoded, ByteOffset + 8, 111 [<<(find_in_set(A)):5, 112 (find_in_set(B)):5, 113 (find_in_set(C)):5, 114 (find_in_set(D)):5, 115 (find_in_set(E)):5, 116 (find_in_set(F)):5, 117 (find_in_set(G)):5, 118 (find_in_set(H)):5>> | Acc]) 119 end. 120 121find_in_set(Char) -> 122 case binary:match(?SET, Char) of 123 nomatch -> 124 erlang:error(not_base32); 125 {Offset, _} -> 126 Offset 127 end. 128