singrdk/base/Applications/MapPointProxy/Base64Decoder.cs

161 lines
4.4 KiB
C#

///////////////////////////////////////////////////////////////////////////////
//
// Microsoft Research Singularity
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
///////////////////////////////////////////////////////////////////////////////
using System;
using Microsoft.Contracts;
internal class Base64Decoder
{
private string fSourceStr;
private int fCurStrIndex;
private bool fDone;
private int fBits;
private int fBitsFilled;
private const string CharsBase64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
private static readonly byte[] MapBase64 = ConstructMapBase64();
private const int MaxValidChar = (int)'z';
private const byte Invalid = unchecked((byte)-1);
public Base64Decoder(string! sourceString, int startIndex)
{
fSourceStr = sourceString;
fCurStrIndex = startIndex;
fDone = false;
Reset();
}
[Delayed]
public void Reset()
{
fBits = 0;
fBitsFilled = 0;
}
public byte[] Pump(int maxStrIndex, int maxReturnSize)
{
if ((fDone) || (fSourceStr == null))
{
return null;
}
int charsConsumed;
byte[] retval = InternalPump(fSourceStr, fCurStrIndex, maxStrIndex, maxReturnSize,
out charsConsumed, out fDone);
fCurStrIndex += charsConsumed;
return retval;
}
private byte[] InternalPump(string! sourceStr, int startIndex, int maxIndex, int maxReturnSize,
out int charsConsumed, out bool done)
{
// walk hex digits pairing them up and shoving the value of each pair into a byte
byte[] newChunk = new byte[maxReturnSize];
int bytePos = 0;
int charPos = startIndex;
int b = fBits;
int bFilled = fBitsFilled;
while ( charPos < maxIndex && bytePos < maxReturnSize )
{
char ch = sourceStr[charPos];
// end?
if ( ch == '=' ) {
done = true;
break;
}
charPos++;
// ignore white space
if (char.IsWhiteSpace(ch)) {
continue;
}
int digit;
if ( ch > 122 || ( digit = MapBase64[ch] ) == Invalid ) {
throw new Exception("Invalid Base64 character: " + ch);
}
b = ( b << 6 ) | digit;
bFilled += 6;
if ( bFilled >= 8 ) {
// get top eight valid bits
newChunk[bytePos++] = (byte)( ( b >> ( bFilled - 8 ) ) & 0xFF );
bFilled -= 8;
if ( bytePos == maxReturnSize ) {
break;
}
}
}
if (bytePos < maxReturnSize)
{
if ( charPos < maxIndex && sourceStr[charPos] == '=' ) {
bFilled = 0;
// ignore padding chars
do {
charPos++;
} while ( charPos < maxIndex && sourceStr[charPos] == '=' );
// ignore whitespace after the padding chars
if ( charPos < maxIndex ) {
while ( charPos < maxIndex ) {
if ( ! char.IsWhiteSpace(sourceStr[charPos++]) )
throw new Exception("Invalid Base64 character: " + sourceStr[charPos - 1]);
}
}
}
}
fBits = b;
fBitsFilled = bFilled;
charsConsumed = charPos - startIndex;
if (bytePos < maxReturnSize)
{
byte[] trimmed = new byte[bytePos];
Buffer.BlockCopy(newChunk, 0, trimmed, 0, bytePos);
newChunk = trimmed;
// We ran out of characters, so we're done
done = true;
}
else
{
done = false;
}
return newChunk;
}
private static byte[] ConstructMapBase64()
{
byte[] mapBase64 = new byte[MaxValidChar + 1];
for ( int i = 0; i < mapBase64.Length; i++ ) {
mapBase64[i]= Invalid;
}
for ( int i = 0; i < CharsBase64.Length; i++ ) {
mapBase64[(int)CharsBase64[i]] = (byte)i;
}
return mapBase64;
}
}