/// <summary> /// Decode a Base58 representation. /// </summary> /// <param name="text">Base58 encoded text.</param> /// <returns>Array of decoded bytes.</returns> public unsafe Span <byte> Decode(ReadOnlySpan <char> text) { int textLen = text.Length; if (textLen == 0) { return(Array.Empty <byte>()); } fixed(char *inputPtr = text) { char *pEnd = inputPtr + textLen; char *pInput = inputPtr; char zeroChar = this.Alphabet.Value[0]; while (*pInput == zeroChar && pInput != pEnd) { pInput++; } int numZeroes = (int)(pInput - inputPtr); if (pInput == pEnd) { return(new byte[numZeroes]); // initialized to zero } int outputLen = Alphabet.GetSafeByteCountForDecoding(text); var table = this.Alphabet.ReverseLookupTable; byte[] output = new byte[outputLen]; fixed(byte *outputPtr = output) { byte *pOutputEnd = outputPtr + outputLen - 1; byte *pMinOutput = pOutputEnd; while (pInput != pEnd) { char c = *pInput; int carry = table[c] - 1; if (carry < 0) { throw EncodingAlphabet.InvalidCharacter(c); } byte *pOutput = pOutputEnd; for (; pOutput >= outputPtr; pOutput--) { carry += 58 * (*pOutput); *pOutput = (byte)carry; if (pMinOutput > pOutput && carry != 0) { pMinOutput = pOutput; } carry /= 256; } pInput++; } pMinOutput -= numZeroes; return(output.AsSpan((int)(pMinOutput - outputPtr))); } } }
/// <summary> /// Decode given characters into bytes. /// </summary> /// <param name="text">Characters to decode.</param> /// <returns>Decoded bytes.</returns> public unsafe Span <byte> Decode(ReadOnlySpan <char> text) { int textLen = text.Length; if (textLen == 0) { return(Array.Empty <byte>()); } char?allZeroChar = this.Alphabet.AllZeroShortcut; char?allSpaceChar = this.Alphabet.AllSpaceShortcut; bool checkZero = allZeroChar.HasValue; bool checkSpace = allSpaceChar.HasValue; bool usingShortcuts = checkZero || checkSpace; // allocate a larger buffer if we're using shortcuts int decodeBufferLen = Alphabet.GetSafeByteCountForDecoding(text); byte[] decodeBuffer = new byte[decodeBufferLen]; var table = this.Alphabet.ReverseLookupTable; fixed(char *inputPtr = text) fixed(byte *decodeBufferPtr = decodeBuffer) { byte *pDecodeBuffer = decodeBufferPtr; char *pInput = inputPtr; char *pInputEnd = pInput + textLen; int blockIndex = 0; long value = 0; while (pInput != pInputEnd) { char c = *pInput++; if (isWhiteSpace(c)) { continue; } // handle shortcut characters if (checkZero && c == allZeroChar) { writeShortcut(ref pDecodeBuffer, ref blockIndex, 0); continue; } if (checkSpace && c == allSpaceChar) { writeShortcut(ref pDecodeBuffer, ref blockIndex, allSpace); continue; } // handle regular blocks int x = table[c] - 1; // map character to byte value if (x < 0) { throw EncodingAlphabet.InvalidCharacter(c); } value = (value * baseLength) + x; blockIndex += 1; if (blockIndex == stringBlockSize) { writeDecodedValue(ref pDecodeBuffer, value, byteBlockSize); blockIndex = 0; value = 0; } } if (blockIndex > 0) { // handle padding by treating the rest of the characters // as "u"s. so both big endianness and bit weirdness work out okay. for (int i = 0; i < stringBlockSize - blockIndex; i++) { value = (value * baseLength) + (baseLength - 1); } writeDecodedValue(ref pDecodeBuffer, value, blockIndex - 1); } int actualOutputLength = (int)(pDecodeBuffer - decodeBufferPtr); return(new Span <byte>(decodeBufferPtr, actualOutputLength)); } }