Data commit
This commit is contained in:
parent
7387c8f97b
commit
cb5bb5e222
199093 changed files with 3378972 additions and 0 deletions
190
Task/Base64-decode-data/Ecstasy/base64-decode-data.ecstasy
Normal file
190
Task/Base64-decode-data/Ecstasy/base64-decode-data.ecstasy
Normal file
|
|
@ -0,0 +1,190 @@
|
|||
/**
|
||||
* 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.print($"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}";
|
||||
};
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue