Base64 decode data: Difference between revisions

Add Ecstasy example
(→‎{{header|Vlang}}: Rename "Vlang" in "V (Vlang)")
(Add Ecstasy example)
Line 546:
"To err is human, but to really foul things up you need a computer.
-- Paul R. Ehrlich"</pre>
 
=={{header|Ecstasy}}==
<syntaxhighlight lang="java">
/**
* This implementation is encapsulated as Base64Format in the web.xtclang.org module,
* but the module is currently in flux, so the implementation is included "inline" here.
*/
module Base64
{
@Inject Console console;
void run()
{
String orig = \|VG8gZXJyIGlzIGh1bWFuLCBidXQgdG8gcmVhbGx5IGZvdWwgdGhpbmdzIHVwIH\
|lvdSBuZWVkIGEgY29tcHV0ZXIuCiAgICAtLSBQYXVsIFIuIEVocmxpY2g=
;
Byte[] bytes = decode(orig);
String text = encode(bytes, pad=True);
assert text == orig;
console.println($"base64={text}, bytes={bytes}");
}
 
static Byte[] read(Iterator<Char> stream)
{
Int charLen = 0;
charLen := stream.knownSize();
Byte[] byteBuf = new Byte[](charLen * 6 / 8);
Byte prevBits = 0;
Int prevCount = 0;
while (Char ch := stream.next())
{
if (Byte newBits := isBase64(ch, assertTrash=True))
{
if (prevCount == 0)
{
prevBits = newBits;
prevCount = 6;
}
else
{
byteBuf.add((prevBits << 8-prevCount) | (newBits >> prevCount-2));
prevBits = newBits;
prevCount -= 2;
}
}
}
 
return byteBuf.freeze(True);
}
 
static void write(Byte[] value, Appender<Char> stream, Boolean pad=False, Int? lineLength=Null)
{
lineLength ?:= Int.maxvalue;
 
Int lineOffset = 0;
Int totalChars = 0;
Byte prevByte = 0;
Int prevCount = 0; // number of leftover bits from the previous byte
Int byteOffset = 0;
Int byteLength = value.size;
while (True)
{
// glue together the next six bits, which will create one character of output
Byte sixBits;
if (byteOffset >= byteLength)
{
if (prevCount == 0)
{
break;
}
sixBits = prevByte << 6 - prevCount;
prevCount = 0;
}
else if (prevCount == 6)
{
sixBits = prevByte << 6 - prevCount;
prevCount = 0;
}
else
{
Byte nextByte = value[byteOffset++];
sixBits = (prevByte << 6 - prevCount) | (nextByte >> 2 + prevCount);
prevByte = nextByte;
prevCount += 2;
}
 
if (lineOffset >= lineLength)
{
stream.add('\r').add('\n');
totalChars += lineOffset;
lineOffset = 0;
}
 
stream.add(base64(sixBits & 0b111111));
++lineOffset;
}
 
if (pad)
{
totalChars += lineOffset;
for (Int i = 0, Int padCount = 4 - (totalChars & 0b11) & 0b11; i < padCount; ++i)
{
if (lineOffset >= lineLength)
{
stream.add('\r').add('\n');
lineOffset = 0;
}
 
stream.add('=');
++lineOffset;
}
}
}
 
static String encode(Byte[] value, Boolean pad=False, Int? lineLength=Null)
{
// calculate buffer size
Int byteLen = value.size;
Int charLen = (byteLen * 8 + 5) / 6;
if (pad)
{
charLen += 4 - (charLen & 0b11) & 0b11;
}
if (lineLength != Null)
{
charLen += ((charLen + lineLength - 1) / lineLength - 1).maxOf(0) * 2;
}
 
StringBuffer charBuf = new StringBuffer(charLen);
write(value, charBuf, pad, lineLength);
return charBuf.toString();
}
 
static Byte[] decode(String text)
{
Int charLen = text.size;
Byte[] byteBuf = new Byte[](charLen * 6 / 8);
Byte prevBits = 0;
Int prevCount = 0;
for (Int offset = 0; offset < charLen; ++offset)
{
if (Byte newBits := isBase64(text[offset], assertTrash=True))
{
if (prevCount == 0)
{
prevBits = newBits;
prevCount = 6;
}
else
{
byteBuf.add((prevBits << 8-prevCount) | (newBits >> prevCount-2));
prevBits = newBits;
prevCount -= 2;
}
}
}
 
return byteBuf.freeze(True);
}
 
/**
* Translate a single Base64 character to the least significant 6 bits of a `Byte` value.
*
* @param ch the Base64 character; no pad or newlines allowed
*
* @return the value in the range `0 ..< 64`
*/
static Byte valOf(Char ch)
{
return switch (ch)
{
case 'A'..'Z': (ch - 'A').toUInt8();
case 'a'..'z': (ch - 'a').toUInt8() + 26;
case '0'..'9': (ch - '0').toUInt8() + 52;
case '+': 62;
case '/': 63;
 
case '=': assert as $"Unexpected padding character in Base64: {ch.quoted()}";
case '\r', '\n': assert as $"Unexpected newline character in Base64: {ch.quoted()}";
default: assert as $"Invalid Base64 character: {ch.quoted()}";
};
}
 
/**
* Translate a single Base64 character to the least significant 6 bits of a `Byte` value.
*
* @param ch the character to test if it is Base64
* @param assertTrash (optional) pass True to assert on illegal Base64 characters
*
* @return the value in the range `0 ..< 64`
*/
static conditional Byte isBase64(Char ch, Boolean assertTrash=False)
{
return switch (ch)
{
case 'A'..'Z': (True, (ch - 'A').toUInt8());
case 'a'..'z': (True, (ch - 'a').toUInt8() + 26);
case '0'..'9': (True, (ch - '0').toUInt8() + 52);
case '+': (True, 62);
case '/': (True, 63);
 
case '=': // "pad" sometimes allowed (or required) at end
case '\r', '\n': // newlines sometimes allowed (or required)
False;
 
default: assertTrash ? assert as $"Invalid Base64 character: {ch.quoted()}" : False;
};
}
 
/**
* Convert the passed byte value to a Base64 character.
*
* @param the byte value, which must be in the range `0..63`
*
* @return the Base64 character
*/
static Char base64(Byte byte)
{
return switch (byte)
{
case 0 ..< 26: 'A'+byte;
case 26 ..< 52: 'a'+(byte-26);
case 52 ..< 62: '0'+(byte-52);
case 62: '+';
case 63: '/';
default: assert:bounds as $"byte={byte}";
};
}
}
</syntaxhighlight>
 
Output:
<syntaxhighlight>
base64=VG8gZXJyIGlzIGh1bWFuLCBidXQgdG8gcmVhbGx5IGZvdWwgdGhpbmdzIHVwIHlvdSBuZWVkIGEgY29tcHV0ZXIuCiAgICAtLSBQYXVsIFIuIEVocmxpY2g=, bytes=0x546F206572722069732068756D616E2C2062757420746F207265616C6C7920666F756C207468696E677320757020796F75206E656564206120636F6D70757465722E0A202020202D2D205061756C20522E204568726C696368
</syntaxhighlight>
 
=={{header|F_Sharp|F#}}==
162

edits