Ejemplo n.º 1
0
        /// <summary>
        /// Allocates a new byte array, containing the data represented by the given Base62 string.
        /// </summary>
        public static byte[] FromBase62String(string base62String, Base62Alphabet alphabet = null)
        {
            // Prefer stackalloc over renting
            byte[] charBytes = null;
            var    chars     = base62String.Length <= 1024
                                ? stackalloc byte[base62String.Length]
                                : (charBytes = ArrayPool <byte> .Shared.Rent(base62String.Length));

            try
            {
                Encoding.ASCII.GetBytes(base62String, chars);
                var bytes = new byte[GetByteLength(chars.Length)];
                if (!TryFromBase62Chars(chars, bytes, out _, alphabet))
                {
                    throw new ArgumentException("The input is not valid Base62.");
                }
                return(bytes);
            }
            finally
            {
                if (charBytes != null)
                {
                    ArrayPool <byte> .Shared.Return(charBytes);
                }
            }
        }
Ejemplo n.º 2
0
        /// <summary>
        /// Allocates a new string, encoding the given bytes as alphanumeric Base62 characters.
        /// </summary>
        public static string ToBase62String(ReadOnlySpan <byte> bytes, Base62Alphabet alphabet = null)
        {
            // We need a temporary place to store bytes, since we get UTF8 but want a string
            // That is alright:
            // We cannot use String.Create with span input anyway, so we need to make a full copy somewhere - it might as well be here

            // As an alternative to filling a byte[] and using ASCII.GetString(), we could fill a char[] and use new string(chars)
            // Filling the chars could be slower, yet creating the string could be faster
            // The overall performance is similar, so we prefer our own simplest implementation

            // Prefer stackalloc over renting
            var outputLength = GetBase62Length(bytes.Length);

            byte[] charArray = null;
            var    chars     = outputLength <= 1024
                                ? stackalloc byte[outputLength]
                                : (charArray = ArrayPool <byte> .Shared.Rent(outputLength));

            try
            {
                if (!TryToBase62Chars(bytes, chars, out _, alphabet))
                {
                    throw new ArgumentException("The output span is too short.");
                }

                return(Encoding.ASCII.GetString(chars));
            }
            finally
            {
                if (charArray != null)
                {
                    ArrayPool <byte> .Shared.Return(charArray);
                }
            }
        }
Ejemplo n.º 3
0
        public static string ToBase62String(ReadOnlySpan <byte> bytes, Base62Alphabet alphabet = null)
        {
            // We need a temporary place to store bytes, since we get UTF8 but want a string
            // That is alright:
            // We cannot use String.Create with span input anyway, so we need to make a full copy somewhere - it might as well be here

            // Prefer stackalloc over renting
            var outputLength = GetBase62Length(bytes.Length);

            byte[] charArray = null;
            var    chars     = outputLength <= 1024
                                ? stackalloc byte[outputLength]
                                : (charArray = ArrayPool <byte> .Shared.Rent(outputLength));

            try
            {
                TryToBase62Chars(bytes, chars, out _, alphabet);                 // Output is always true
                return(Encoding.ASCII.GetString(chars));
            }
            finally
            {
                if (charArray != null)
                {
                    ArrayPool <byte> .Shared.Return(charArray);
                }
            }
        }
Ejemplo n.º 4
0
 /// <summary>
 /// Allocates a new string, encoding a subset of the given bytes as alphanumeric Base62 characters.
 /// </summary>
 public static string ToBase62String(byte[] bytes, int offset, int length, Base62Alphabet alphabet = null)
 {
     return(ToBase62String(bytes.AsSpan(offset, length), alphabet));
 }
Ejemplo n.º 5
0
 /// <summary>
 /// Allocates a new string, encoding the given bytes as alphanumeric Base62 characters.
 /// </summary>
 public static string ToBase62String(byte[] bytes, Base62Alphabet alphabet = null)
 {
     return(ToBase62String(bytes.AsSpan(), alphabet));
 }
Ejemplo n.º 6
0
        /// <summary>
        /// Core implementation.
        /// Returns true on success, or false if the output span was too short or the input was not valid Base62.
        /// </summary>
        /// <param name="shiftCount">When reading bytes, left shift the index by 0 (no change). When reading chars, left shift it by 1 (doubling it).</param>
        private static bool TryFromBase62CharsCore(ReadOnlySpan <byte> chars, int shiftCount, Span <byte> bytes, out int bytesWritten, Base62Alphabet alphabet = null)
        {
            System.Diagnostics.Debug.Assert(shiftCount == 0 || shiftCount == 1);

            if (alphabet is null)
            {
                alphabet = DefaultAlphabet;
            }
            var reverseAlphabet = alphabet.ReverseAlphabet;

            try
            {
                var outputLength = GetByteLength(chars.Length >> shiftCount);
                if (bytes.Length < outputLength)
                {
                    throw new ArgumentException("The output span is too small.");
                }
                bytes = bytes.Slice(0, outputLength);

                bytesWritten = outputLength;

                // Decode complete blocks
                while (chars.Length >= 11 << shiftCount)
                {
                    DecodeBlock(reverseAlphabet, chars, shiftCount, bytes);
                    chars = chars.Slice(11 << shiftCount);
                    bytes = bytes.Slice(8);
                }

                if (chars.IsEmpty)
                {
                    return(true);
                }

                // Decode the final (incomplete) block
                {
                    DecodeBlock(reverseAlphabet, chars, shiftCount, bytes);
                    return(true);
                }
            }
            catch (ArgumentException)
            {
                bytesWritten = 0;
                return(false);
            }
        }
Ejemplo n.º 7
0
        /// <summary>
        /// <para>
        /// Decodes the given Base62 characters back into the represented bytes, writing them to the output span.
        /// </para>
        /// <para>
        /// Returns false if the output span is too short or the input is not valid Base62.
        /// If false is returned, any number of bytes may have been written, and the output value of the number of bytes written has no meaning.
        /// </para>
        /// </summary>
        public static bool TryFromBase62Chars(ReadOnlySpan <char> chars, Span <byte> bytes, out int bytesWritten, Base62Alphabet alphabet = null)
        {
            bytesWritten = 0;

            // Since we will only be processing the lower byte of each char, make sure that no char has data in its higher byte
            // Otherwise, invalid input could get through
            {
                var vectors         = MemoryMarshal.Cast <char, Vector <ushort> >(chars);      // Takes only full vector blocks of the input span
                var hasNonAsciiByte = false;
                foreach (var vector in vectors)
                {
                    hasNonAsciiByte |= Vector.GreaterThanAny(vector, VectorOf127s);                                             // Generally compares 16 chars at a time
                }
                for (var i = vectors.Length * Vector <ushort> .Count; i < chars.Length; i++)
                {
                    hasNonAsciiByte |= chars[i] > 127;                                                                                        // Compare the remaining chars
                }
                if (hasNonAsciiByte)
                {
                    return(false);
                }
            }

            var charsAsBytePairs = MemoryMarshal.AsBytes(chars);

            return(TryFromBase62CharsCore(charsAsBytePairs, shiftCount: 1, bytes, out bytesWritten, alphabet));
        }
Ejemplo n.º 8
0
 /// <summary>
 /// <para>
 /// Decodes the given ASCII-encoded Base62 characters back into the represented bytes, writing them to the output span.
 /// </para>
 /// <para>
 /// Returns false if the output span is too short or the input is not valid Base62.
 /// If false is returned, any number of bytes may have been written, and the output value of the number of bytes written has no meaning.
 /// </para>
 /// </summary>
 public static bool TryFromBase62Chars(ReadOnlySpan <byte> chars, Span <byte> bytes, out int bytesWritten, Base62Alphabet alphabet = null)
 {
     return(TryFromBase62CharsCore(chars, shiftCount: 0, bytes, out bytesWritten, alphabet));
 }
Ejemplo n.º 9
0
 /// <summary>
 /// Allocates a new byte array, decoding the data represented by the given Base62 string.
 /// </summary>
 public static bool TryFromBase62String(string base62String, Span <byte> bytes, out int bytesWritten, Base62Alphabet alphabet = null)
 {
     return(TryFromBase62Chars(base62String.AsSpan(), bytes, out bytesWritten, alphabet));
 }
Ejemplo n.º 10
0
        /// <summary>
        /// Core implementation.
        /// Returns true on success or false if the output span was too short.
        /// </summary>
        /// <param name="shiftCount">When reading bytes, left shift the index by 0 (no change). When reading chars, left shift it by 1 (doubling it).</param>
        private static bool TryToBase62CharsCore(ReadOnlySpan <byte> bytes, Span <byte> chars, int shiftCount, out int charsWritten, Base62Alphabet alphabet = null)
        {
            System.Diagnostics.Debug.Assert(shiftCount == 0 || shiftCount == 1);

            var forwardAlphabet = (alphabet ?? DefaultAlphabet).ForwardAlphabet;

            charsWritten = 0;

            var outputLength = GetBase62Length(bytes.Length);

            if (chars.Length < outputLength << shiftCount)
            {
                return(false);
            }
            chars = chars.Slice(0, outputLength << shiftCount);

            charsWritten = outputLength;

            // Encode complete blocks
            while (bytes.Length >= 8)
            {
                EncodeBlock(forwardAlphabet, bytes, chars, shiftCount);
                bytes = bytes.Slice(8);
                chars = chars.Slice(11 << shiftCount);
            }

            if (bytes.IsEmpty)
            {
                return(true);
            }

            // Encode the final (incomplete) block
            {
                EncodeBlock(forwardAlphabet, bytes, chars, shiftCount);
                return(true);
            }
        }
Ejemplo n.º 11
0
        /// <summary>
        /// <para>
        /// Represents the given bytes as alphanumeric Base62 characters, writing them to the output span as UTF-16 characters.
        /// </para>
        /// <para>
        /// Returns false if the output span is too short.
        /// </para>
        /// </summary>
        public static bool TryToBase62Chars(ReadOnlySpan <byte> bytes, Span <char> chars, out int charsWritten, Base62Alphabet alphabet = null)
        {
            var charsAsBytes = MemoryMarshal.AsBytes(chars);

            return(TryToBase62CharsCore(bytes, charsAsBytes, shiftCount: 1, out charsWritten, alphabet));
        }
Ejemplo n.º 12
0
        /// <param name="shiftCount">When reading bytes, left shift the index by 0 (no change). When reading chars, left shift it by 1 (doubling it).</param>
        private static bool TryFromBase62Chars(ReadOnlySpan <byte> chars, int shiftCount, Span <byte> bytes, out int bytesWritten, Base62Alphabet alphabet = null)
        {
            System.Diagnostics.Debug.Assert(shiftCount == 0 || shiftCount == 1);

            if (alphabet is null)
            {
                alphabet = DefaultAlphabet;
            }
            var reverseAlphabet = alphabet.ReverseAlphabet;

            try
            {
                var outputLength = GetByteLength(chars.Length >> shiftCount);
                if (bytes.Length < outputLength)
                {
                    throw new ArgumentException("The output span is too small.");
                }
                bytes = bytes.Slice(0, outputLength);

                bytesWritten = outputLength;

                Span <uint> workspace      = stackalloc uint[8];
                var         workspaceBytes = MemoryMarshal.AsBytes(workspace);

                // Decode complete blocks
                while (chars.Length >= 43 << shiftCount)
                {
                    DecodeBlock(reverseAlphabet, workspace, workspaceBytes, chars, shiftCount, bytes);
                    workspaceBytes.Clear();
                    chars = chars.Slice(43 << shiftCount);
                    bytes = bytes.Slice(32);
                }

                if (chars.IsEmpty)
                {
                    return(true);
                }

                // Decode the final (incomplete) block
                {
                    Span <byte> finalInputBlock  = stackalloc byte[43 << shiftCount];
                    Span <byte> finalOutputBlock = stackalloc byte[32];
                    finalInputBlock.Fill(alphabet.ForwardAlphabet[0]);                     // Prefill with the character that represents 0
                    chars.CopyTo(finalInputBlock);
                    DecodeBlock(reverseAlphabet, workspace, workspaceBytes, finalInputBlock, shiftCount, finalOutputBlock);
                    finalOutputBlock.Slice(0, bytes.Length).CopyTo(bytes);
                    return(true);
                }
            }
            catch (ArgumentException)
            {
                bytesWritten = 0;
                return(false);
            }
        }
Ejemplo n.º 13
0
        /// <param name="shiftCount">When reading bytes, left shift the index by 0 (no change). When reading chars, left shift it by 1 (doubling it).</param>
        private static bool TryToBase62Chars(ReadOnlySpan <byte> bytes, Span <byte> chars, int shiftCount, out int charsWritten, Base62Alphabet alphabet = null)
        {
            System.Diagnostics.Debug.Assert(shiftCount == 0 || shiftCount == 1);

            var forwardAlphabet = (alphabet ?? DefaultAlphabet).ForwardAlphabet;

            charsWritten = 0;

            var outputLength = GetBase62Length(bytes.Length);

            if (chars.Length < outputLength << shiftCount)
            {
                return(false);
            }
            chars = chars.Slice(0, outputLength << shiftCount);

            charsWritten = outputLength;

            Span <uint> workspace      = stackalloc uint[8];
            var         workspaceBytes = MemoryMarshal.AsBytes(workspace);

            // Encode complete blocks
            while (bytes.Length >= 32)
            {
                EncodeBlock(forwardAlphabet, workspace, workspaceBytes, bytes, chars, shiftCount);
                bytes = bytes.Slice(32);
                chars = chars.Slice(43 << shiftCount);
            }

            if (bytes.IsEmpty)
            {
                return(true);
            }

            // Encode the final (incomplete) block
            {
                Span <byte> finalInputBlock  = stackalloc byte[32];
                Span <byte> finalOutputBlock = stackalloc byte[43 << shiftCount];
                bytes.CopyTo(finalInputBlock);
                EncodeBlock(forwardAlphabet, workspace, workspaceBytes, finalInputBlock, finalOutputBlock, shiftCount);
                finalOutputBlock.Slice(0, chars.Length).CopyTo(chars);
                return(true);
            }
        }
Ejemplo n.º 14
0
 public static bool TryToBase62Chars(ReadOnlySpan <byte> bytes, Span <byte> chars, out int charsWritten, Base62Alphabet alphabet = null)
 {
     return(TryToBase62Chars(bytes, chars, shiftCount: 0, out charsWritten, alphabet));
 }