From 18fe04a57aa29219d63fda4d8eb6a8586a1f8091 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Fri, 7 Oct 2022 21:23:45 +0700 Subject: Span usage in encoders --- crypto/src/util/encoders/Base64Encoder.cs | 194 ++++++++++++++++++++++++++++-- crypto/src/util/encoders/HexEncoder.cs | 127 +++++++++++++++++-- crypto/src/util/encoders/IEncoder.cs | 8 ++ 3 files changed, 309 insertions(+), 20 deletions(-) diff --git a/crypto/src/util/encoders/Base64Encoder.cs b/crypto/src/util/encoders/Base64Encoder.cs index 7ec79eaa0..6ba9e97a4 100644 --- a/crypto/src/util/encoders/Base64Encoder.cs +++ b/crypto/src/util/encoders/Base64Encoder.cs @@ -46,6 +46,9 @@ namespace Org.BouncyCastle.Utilities.Encoders public int Encode(byte[] inBuf, int inOff, int inLen, byte[] outBuf, int outOff) { +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + return Encode(inBuf.AsSpan(inOff, inLen), outBuf.AsSpan(outOff)); +#else int inPos = inOff; int inEnd = inOff + inLen - 2; int outPos = outOff; @@ -88,8 +91,57 @@ namespace Org.BouncyCastle.Utilities.Encoders } return outPos - outOff; +#endif } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public int Encode(ReadOnlySpan input, Span output) + { + int inPos = 0; + int inEnd = input.Length - 2; + int outPos = 0; + + while (inPos < inEnd) + { + uint a1 = input[inPos++]; + uint a2 = input[inPos++]; + uint a3 = input[inPos++]; + + output[outPos++] = encodingTable[(a1 >> 2) & 0x3F]; + output[outPos++] = encodingTable[((a1 << 4) | (a2 >> 4)) & 0x3F]; + output[outPos++] = encodingTable[((a2 << 2) | (a3 >> 6)) & 0x3F]; + output[outPos++] = encodingTable[a3 & 0x3F]; + } + + switch (input.Length - inPos) + { + case 1: + { + uint a1 = input[inPos++]; + + output[outPos++] = encodingTable[(a1 >> 2) & 0x3F]; + output[outPos++] = encodingTable[(a1 << 4) & 0x3F]; + output[outPos++] = padding; + output[outPos++] = padding; + break; + } + case 2: + { + uint a1 = input[inPos++]; + uint a2 = input[inPos++]; + + output[outPos++] = encodingTable[(a1 >> 2) & 0x3F]; + output[outPos++] = encodingTable[((a1 << 4) | (a2 >> 4)) & 0x3F]; + output[outPos++] = encodingTable[(a2 << 2) & 0x3F]; + output[outPos++] = padding; + break; + } + } + + return outPos; + } +#endif + /** * encode the input data producing a base 64 output stream. * @@ -97,6 +149,9 @@ namespace Org.BouncyCastle.Utilities.Encoders */ public int Encode(byte[] buf, int off, int len, Stream outStream) { +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + return Encode(buf.AsSpan(off, len), outStream); +#else if (len < 0) return 0; @@ -111,8 +166,25 @@ namespace Org.BouncyCastle.Utilities.Encoders remaining -= inLen; } return (len + 2) / 3 * 4; +#endif } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public int Encode(ReadOnlySpan data, Stream outStream) + { + Span tmp = stackalloc byte[72]; + int result = (data.Length + 2) / 3 * 4; + while (!data.IsEmpty) + { + int inLen = System.Math.Min(54, data.Length); + int outLen = Encode(data[..inLen], tmp); + outStream.Write(tmp[..outLen]); + data = data[inLen..]; + } + return result; + } +#endif + private bool Ignore(char c) { return c == '\n' || c =='\r' || c == '\t' || c == ' '; @@ -126,6 +198,9 @@ namespace Org.BouncyCastle.Utilities.Encoders */ public int Decode(byte[] data, int off, int length, Stream outStream) { +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + return Decode(data.AsSpan(off, length), outStream); +#else byte b1, b2, b3, b4; byte[] outBuffer = new byte[54]; // S/MIME standard int bufOff = 0; @@ -140,10 +215,8 @@ namespace Org.BouncyCastle.Utilities.Encoders end--; } - int i = off; - int finish = end - 4; - - i = NextI(data, i, finish); + int finish = end - 4; + int i = NextI(data, off, finish); while (i < finish) { @@ -191,13 +264,85 @@ namespace Org.BouncyCastle.Utilities.Encoders outLen += DecodeLastBlock(outStream, (char)data[e0], (char)data[e1], (char)data[e2], (char)data[e3]); + return outLen; +#endif + } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public int Decode(ReadOnlySpan data, Stream outStream) + { + byte b1, b2, b3, b4; + Span outBuffer = stackalloc byte[54]; // S/MIME standard + int bufOff = 0; + int outLen = 0; + int end = data.Length; + + while (end > 0) + { + if (!Ignore((char)data[end - 1])) + break; + + end--; + } + + int finish = end - 4; + int i = NextI(data, 0, finish); + + while (i < finish) + { + b1 = decodingTable[data[i++]]; + + i = NextI(data, i, finish); + + b2 = decodingTable[data[i++]]; + + i = NextI(data, i, finish); + + b3 = decodingTable[data[i++]]; + + i = NextI(data, i, finish); + + b4 = decodingTable[data[i++]]; + + if ((b1 | b2 | b3 | b4) >= 0x80) + throw new IOException("invalid characters encountered in base64 data"); + + outBuffer[bufOff++] = (byte)((b1 << 2) | (b2 >> 4)); + outBuffer[bufOff++] = (byte)((b2 << 4) | (b3 >> 2)); + outBuffer[bufOff++] = (byte)((b3 << 6) | b4); + + if (bufOff == outBuffer.Length) + { + outStream.Write(outBuffer); + bufOff = 0; + } + + outLen += 3; + + i = NextI(data, i, finish); + } + + if (bufOff > 0) + { + outStream.Write(outBuffer[..bufOff]); + } + + int e0 = NextI(data, i, end); + int e1 = NextI(data, e0 + 1, end); + int e2 = NextI(data, e1 + 1, end); + int e3 = NextI(data, e2 + 1, end); + + outLen += DecodeLastBlock(outStream, (char)data[e0], (char)data[e1], (char)data[e2], (char)data[e3]); + return outLen; } +#endif - private int NextI( - byte[] data, - int i, - int finish) +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + private int NextI(ReadOnlySpan data, int i, int finish) +#else + private int NextI(byte[] data, int i, int finish) +#endif { while ((i < finish) && Ignore((char)data[i])) { @@ -232,10 +377,11 @@ namespace Org.BouncyCastle.Utilities.Encoders end--; } - int i = 0; - int finish = end - 4; - - i = NextI(data, i, finish); + int finish = end - 4; + int i = NextI(data, 0, finish); +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + Span buf = stackalloc byte[3]; +#endif while (i < finish) { @@ -256,9 +402,16 @@ namespace Org.BouncyCastle.Utilities.Encoders if ((b1 | b2 | b3 | b4) >= 0x80) throw new IOException("invalid characters encountered in base64 data"); +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + buf[0] = (byte)((b1 << 2) | (b2 >> 4)); + buf[1] = (byte)((b2 << 4) | (b3 >> 2)); + buf[2] = (byte)((b3 << 6) | b4); + outStream.Write(buf); +#else outStream.WriteByte((byte)((b1 << 2) | (b2 >> 4))); outStream.WriteByte((byte)((b2 << 4) | (b3 >> 2))); outStream.WriteByte((byte)((b3 << 6) | b4)); +#endif length += 3; @@ -302,8 +455,16 @@ namespace Org.BouncyCastle.Utilities.Encoders if ((b1 | b2 | b3) >= 0x80) throw new IOException("invalid characters encountered at end of base64 data"); +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + Span buf = stackalloc byte[2] { + (byte)((b1 << 2) | (b2 >> 4)), + (byte)((b2 << 4) | (b3 >> 2)), + }; + outStream.Write(buf); +#else outStream.WriteByte((byte)((b1 << 2) | (b2 >> 4))); outStream.WriteByte((byte)((b2 << 4) | (b3 >> 2))); +#endif return 2; } @@ -317,9 +478,18 @@ namespace Org.BouncyCastle.Utilities.Encoders if ((b1 | b2 | b3 | b4) >= 0x80) throw new IOException("invalid characters encountered at end of base64 data"); +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + Span buf = stackalloc byte[3] { + (byte)((b1 << 2) | (b2 >> 4)), + (byte)((b2 << 4) | (b3 >> 2)), + (byte)((b3 << 6) | b4), + }; + outStream.Write(buf); +#else outStream.WriteByte((byte)((b1 << 2) | (b2 >> 4))); outStream.WriteByte((byte)((b2 << 4) | (b3 >> 2))); outStream.WriteByte((byte)((b3 << 6) | b4)); +#endif return 3; } diff --git a/crypto/src/util/encoders/HexEncoder.cs b/crypto/src/util/encoders/HexEncoder.cs index bf68aff9d..6ccb184d9 100644 --- a/crypto/src/util/encoders/HexEncoder.cs +++ b/crypto/src/util/encoders/HexEncoder.cs @@ -1,3 +1,4 @@ +using Org.BouncyCastle.Crypto; using System; using System.IO; @@ -41,6 +42,9 @@ namespace Org.BouncyCastle.Utilities.Encoders public int Encode(byte[] inBuf, int inOff, int inLen, byte[] outBuf, int outOff) { +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + return Encode(inBuf.AsSpan(inOff, inLen), outBuf.AsSpan(outOff)); +#else int inPos = inOff; int inEnd = inOff + inLen; int outPos = outOff; @@ -54,8 +58,28 @@ namespace Org.BouncyCastle.Utilities.Encoders } return outPos - outOff; +#endif } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public int Encode(ReadOnlySpan input, Span output) + { + int inPos = 0; + int inEnd = input.Length; + int outPos = 0; + + while (inPos < inEnd) + { + uint b = input[inPos++]; + + output[outPos++] = encodingTable[b >> 4]; + output[outPos++] = encodingTable[b & 0xF]; + } + + return outPos; + } +#endif + /** * encode the input data producing a Hex output stream. * @@ -63,6 +87,9 @@ namespace Org.BouncyCastle.Utilities.Encoders */ public int Encode(byte[] buf, int off, int len, Stream outStream) { +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + return Encode(buf.AsSpan(off, len), outStream); +#else if (len < 0) return 0; @@ -77,8 +104,25 @@ namespace Org.BouncyCastle.Utilities.Encoders remaining -= inLen; } return len * 2; +#endif } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public int Encode(ReadOnlySpan data, Stream outStream) + { + Span tmp = stackalloc byte[72]; + int result = data.Length * 2; + while (!data.IsEmpty) + { + int inLen = System.Math.Min(36, data.Length); + int outLen = Encode(data[..inLen], tmp); + outStream.Write(tmp[..outLen]); + data = data[inLen..]; + } + return result; + } +#endif + private static bool Ignore(char c) { return c == '\n' || c =='\r' || c == '\t' || c == ' '; @@ -90,12 +134,11 @@ namespace Org.BouncyCastle.Utilities.Encoders * * @return the number of bytes produced. */ - public int Decode( - byte[] data, - int off, - int length, - Stream outStream) + public int Decode(byte[] data, int off, int length, Stream outStream) { +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + return Decode(data.AsSpan(off, length), outStream); +#else byte b1, b2; int outLen = 0; byte[] buf = new byte[36]; @@ -146,8 +189,66 @@ namespace Org.BouncyCastle.Utilities.Encoders outStream.Write(buf, 0, bufOff); } + return outLen; +#endif + } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public int Decode(ReadOnlySpan data, Stream outStream) + { + byte b1, b2; + int outLen = 0; + Span buf = stackalloc byte[36]; + int bufOff = 0; + int end = data.Length; + + while (end > 0) + { + if (!Ignore((char)data[end - 1])) + break; + + end--; + } + + int i = 0; + while (i < end) + { + while (i < end && Ignore((char)data[i])) + { + i++; + } + + b1 = decodingTable[data[i++]]; + + while (i < end && Ignore((char)data[i])) + { + i++; + } + + b2 = decodingTable[data[i++]]; + + if ((b1 | b2) >= 0x80) + throw new IOException("invalid characters encountered in Hex data"); + + buf[bufOff++] = (byte)((b1 << 4) | b2); + + if (bufOff == buf.Length) + { + outStream.Write(buf); + bufOff = 0; + } + + outLen++; + } + + if (bufOff > 0) + { + outStream.Write(buf[..bufOff]); + } + return outLen; } +#endif /** * decode the Hex encoded string data writing it to the given output stream, @@ -155,13 +256,15 @@ namespace Org.BouncyCastle.Utilities.Encoders * * @return the number of bytes produced. */ - public int DecodeString( - string data, - Stream outStream) + public int DecodeString(string data, Stream outStream) { byte b1, b2; int length = 0; +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + Span buf = stackalloc byte[36]; +#else byte[] buf = new byte[36]; +#endif int bufOff = 0; int end = data.Length; @@ -197,7 +300,11 @@ namespace Org.BouncyCastle.Utilities.Encoders if (bufOff == buf.Length) { +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + outStream.Write(buf); +#else outStream.Write(buf, 0, bufOff); +#endif bufOff = 0; } @@ -206,7 +313,11 @@ namespace Org.BouncyCastle.Utilities.Encoders if (bufOff > 0) { +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + outStream.Write(buf[..bufOff]); +#else outStream.Write(buf, 0, bufOff); +#endif } return length; diff --git a/crypto/src/util/encoders/IEncoder.cs b/crypto/src/util/encoders/IEncoder.cs index 5887d5daa..33a2cde18 100644 --- a/crypto/src/util/encoders/IEncoder.cs +++ b/crypto/src/util/encoders/IEncoder.cs @@ -11,8 +11,16 @@ namespace Org.BouncyCastle.Utilities.Encoders { int Encode(byte[] data, int off, int length, Stream outStream); +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + int Encode(ReadOnlySpan data, Stream outStream); +#endif + int Decode(byte[] data, int off, int length, Stream outStream); +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + int Decode(ReadOnlySpan data, Stream outStream); +#endif + int DecodeString(string data, Stream outStream); } } -- cgit 1.4.1