Skip to content

Commit 944841a

Browse files
committed
Add missing implementation of encode/decode functions
Signed-off-by: Arvind Sudarsanam <[email protected]>
1 parent 526633f commit 944841a

File tree

1 file changed

+177
-0
lines changed

1 file changed

+177
-0
lines changed

llvm/lib/Support/Base64.cpp

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,3 +90,180 @@ llvm::Error llvm::decodeBase64(llvm::StringRef Input,
9090
}
9191
return Error::success();
9292
}
93+
94+
using namespace llvm;
95+
96+
namespace {
97+
98+
using byte = std::byte;
99+
100+
::llvm::Error makeError(const Twine &Msg) {
101+
return createStringError(std::error_code{}, Msg);
102+
}
103+
104+
class Base64Impl {
105+
private:
106+
static constexpr char EncodingTable[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
107+
"abcdefghijklmnopqrstuvwxyz"
108+
"0123456789+/";
109+
110+
static_assert(sizeof(EncodingTable) == 65, "");
111+
112+
// Compose an index into the encoder table from two bytes and the number of
113+
// significant bits in the lower byte until the byte boundary.
114+
static inline int composeInd(byte ByteLo, byte ByteHi, int BitsLo) {
115+
int Res = (int)((ByteHi << BitsLo) | (ByteLo >> (8 - BitsLo))) & 0x3F;
116+
return Res;
117+
}
118+
119+
// Decode a single character.
120+
static inline int decode(char Ch) {
121+
if (Ch >= 'A' && Ch <= 'Z') // 0..25
122+
return Ch - 'A';
123+
else if (Ch >= 'a' && Ch <= 'z') // 26..51
124+
return Ch - 'a' + 26;
125+
else if (Ch >= '0' && Ch <= '9') // 52..61
126+
return Ch - '0' + 52;
127+
else if (Ch == '+') // 62
128+
return 62;
129+
else if (Ch == '/') // 63
130+
return 63;
131+
return -1;
132+
}
133+
134+
// Decode a quadruple of characters.
135+
static inline Expected<bool> decode4(const char *Src, byte *Dst) {
136+
int BadCh = -1;
137+
138+
for (auto I = 0; I < 4; ++I) {
139+
char Ch = Src[I];
140+
int Byte = decode(Ch);
141+
142+
if (Byte < 0) {
143+
BadCh = Ch;
144+
break;
145+
}
146+
Dst[I] = (byte)Byte;
147+
}
148+
if (BadCh == -1)
149+
return true;
150+
return makeError("invalid char in Base64Impl encoding: 0x" + Twine(BadCh));
151+
}
152+
153+
public:
154+
static size_t getEncodedSize(size_t SrcSize) {
155+
constexpr int ByteSizeInBits = 8;
156+
constexpr int EncBitsPerChar = 6;
157+
return (SrcSize * ByteSizeInBits + (EncBitsPerChar - 1)) / EncBitsPerChar;
158+
}
159+
160+
static size_t encode(const byte *Src, raw_ostream &Out, size_t SrcSize) {
161+
size_t Off = 0;
162+
163+
// encode full byte triples
164+
for (size_t TriB = 0; TriB < SrcSize / 3; ++TriB) {
165+
Off = TriB * 3;
166+
byte Byte0 = Src[Off++];
167+
byte Byte1 = Src[Off++];
168+
byte Byte2 = Src[Off++];
169+
170+
Out << EncodingTable[(int)Byte0 & 0x3F];
171+
Out << EncodingTable[composeInd(Byte0, Byte1, 2)];
172+
Out << EncodingTable[composeInd(Byte1, Byte2, 4)];
173+
Out << EncodingTable[(int)(Byte2 >> 2) & 0x3F];
174+
}
175+
// encode the remainder
176+
int RemBytes = SrcSize - Off;
177+
178+
if (RemBytes > 0) {
179+
byte Byte0 = Src[Off + 0];
180+
Out << EncodingTable[(int)Byte0 & 0x3F];
181+
182+
if (RemBytes > 1) {
183+
byte Byte1 = Src[Off + 1];
184+
Out << EncodingTable[composeInd(Byte0, Byte1, 2)];
185+
Out << EncodingTable[(int)(Byte1 >> 4) & 0x3F];
186+
} else {
187+
Out << EncodingTable[(int)(Byte0 >> 6) & 0x3F];
188+
}
189+
}
190+
return getEncodedSize(SrcSize);
191+
}
192+
193+
static size_t getDecodedSize(size_t SrcSize) { return (SrcSize * 3 + 3) / 4; }
194+
195+
static Expected<size_t> decode(const char *Src, byte *Dst, size_t SrcSize) {
196+
size_t SrcOff = 0;
197+
size_t DstOff = 0;
198+
199+
// decode full quads
200+
for (size_t Qch = 0; Qch < SrcSize / 4; ++Qch, SrcOff += 4, DstOff += 3) {
201+
byte Ch[4];
202+
Expected<bool> TrRes = decode4(Src + SrcOff, Ch);
203+
204+
if (!TrRes)
205+
return TrRes.takeError();
206+
// each quad of chars produces three bytes of output
207+
Dst[DstOff + 0] = Ch[0] | (Ch[1] << 6);
208+
Dst[DstOff + 1] = (Ch[1] >> 2) | (Ch[2] << 4);
209+
Dst[DstOff + 2] = (Ch[2] >> 4) | (Ch[3] << 2);
210+
}
211+
auto RemChars = SrcSize - SrcOff;
212+
213+
if (RemChars == 0)
214+
return DstOff;
215+
// decode the remainder; variants:
216+
// 2 chars remain - produces single byte
217+
// 3 chars remain - produces two bytes
218+
219+
if (RemChars != 2 && RemChars != 3)
220+
return makeError("invalid encoded sequence length");
221+
222+
int Ch0 = decode(Src[SrcOff++]);
223+
int Ch1 = decode(Src[SrcOff++]);
224+
int Ch2 = RemChars == 3 ? decode(Src[SrcOff]) : 0;
225+
226+
if (Ch0 < 0 || Ch1 < 0 || Ch2 < 0)
227+
return makeError("invalid characters in the encoded sequence remainder");
228+
Dst[DstOff++] = (byte)Ch0 | (byte)((Ch1 << 6));
229+
230+
if (RemChars == 3)
231+
Dst[DstOff++] = (byte)(Ch1 >> 2) | (byte)(Ch2 << 4);
232+
return DstOff;
233+
}
234+
235+
static Expected<std::unique_ptr<byte[]>> decode(const char *Src,
236+
size_t SrcSize) {
237+
size_t DstSize = getDecodedSize(SrcSize);
238+
std::unique_ptr<byte[]> Dst(new byte[DstSize]);
239+
Expected<size_t> Res = decode(Src, Dst.get(), SrcSize);
240+
if (!Res)
241+
return Res.takeError();
242+
return Expected<std::unique_ptr<byte[]>>(std::move(Dst));
243+
}
244+
};
245+
246+
constexpr char Base64Impl::EncodingTable[];
247+
248+
} // anonymous namespace
249+
250+
size_t Base64::getEncodedSize(size_t SrcSize) {
251+
return Base64Impl::getEncodedSize(SrcSize);
252+
}
253+
254+
size_t Base64::encode(const byte *Src, raw_ostream &Out, size_t SrcSize) {
255+
return Base64Impl::encode(Src, Out, SrcSize);
256+
}
257+
258+
size_t Base64::getDecodedSize(size_t SrcSize) {
259+
return Base64Impl::getDecodedSize(SrcSize);
260+
}
261+
262+
Expected<size_t> Base64::decode(const char *Src, byte *Dst, size_t SrcSize) {
263+
return Base64Impl::decode(Src, Dst, SrcSize);
264+
}
265+
266+
Expected<std::unique_ptr<byte[]>> Base64::decode(const char *Src,
267+
size_t SrcSize) {
268+
return Base64Impl::decode(Src, SrcSize);
269+
}

0 commit comments

Comments
 (0)