/// <summary>Shifts a <see cref="BigInteger" /> value a specified number of bits to the right.</summary>
        /// <returns>A value that has been shifted to the right by the specified number of bits.</returns>
        /// <param name="value">The value whose bits are to be shifted.</param>
        /// <param name="shift">The number of bits to shift <paramref name="value" /> to the right.</param>
        public static BigInteger operator >>(BigInteger value, int shift)
        {
            switch (shift)
            {
            case 0:
                return(value);

            case int.MinValue:
                return(value << int.MaxValue << 1);

            default:
                break;
            }

            if (shift < 0)
            {
                return(value << -shift);
            }

            var digitShift = shift / 32;
            var smallShift = shift - (digitShift * 32);
            var negative   = GetPartsForBitManipulation(ref value, out var xd, out var xl);

            if (negative)
            {
                if (shift >= 32 * xl)
                {
                    return(MinusOne);
                }

                var temp = new uint[xl];
                Array.Copy(xd, temp, xl);
                xd = temp;
                NumericsHelpers.DangerousMakeTwosComplement(xd);
            }

            var zl = xl - digitShift;

            if (zl < 0)
            {
                zl = 0;
            }

            var zd = new uint[zl];

            if (smallShift != 0)
            {
                var  carryShift = 32 - smallShift;
                uint carry      = 0;
                for (var index = xl - 1; index >= digitShift; index--)
                {
                    var rot = xd[index];
                    if (!negative || index != xl - 1)
                    {
                        zd[index - digitShift] = (rot >> (smallShift & 31)) | carry;
                    }
                    else
                    {
                        zd[index - digitShift] = (rot >> (smallShift & 31)) | (0xFFFFFFFF << (carryShift & 31));
                    }

                    carry = rot << (carryShift & 31);
                }
            }
            else
            {
                for (var index = xl - 1; index >= digitShift; index--)
                {
                    zd[index - digitShift] = xd[index];
                }
            }

            if (negative)
            {
                NumericsHelpers.DangerousMakeTwosComplement(zd);
            }

            return(new BigInteger(zd, negative));
        }
Esempio n. 2
0
        private static bool HexNumberToBigInteger(ref BigNumberBuffer number, out BigInteger result)
        {
            if (number.digits == null || number.digits.Length == 0)
            {
                result = default;
                return(false);
            }

            const int DigitsPerBlock = 8;

            int totalDigitCount = number.digits.Length - 1;   // Ignore trailing '\0'
            int blockCount, partialDigitCount;

            blockCount = Math.DivRem(totalDigitCount, DigitsPerBlock, out int remainder);
            if (remainder == 0)
            {
                partialDigitCount = 0;
            }
            else
            {
                blockCount       += 1;
                partialDigitCount = DigitsPerBlock - remainder;
            }

            bool isNegative   = HexConverter.FromChar(number.digits[0]) >= 8;
            uint partialValue = (isNegative && partialDigitCount > 0) ? 0xFFFFFFFFu : 0;

            int[]? arrayFromPool = null;

            Span <uint> bitsBuffer = (blockCount <= BigInteger.StackallocUInt32Limit)
                ? stackalloc uint[blockCount]
                : MemoryMarshal.Cast <int, uint>((arrayFromPool = ArrayPool <int> .Shared.Rent(blockCount)).AsSpan(0, blockCount));

            int bitsBufferPos = blockCount - 1;

            try
            {
                foreach (ReadOnlyMemory <char> digitsChunkMem in number.digits.GetChunks())
                {
                    ReadOnlySpan <char> chunkDigits = digitsChunkMem.Span;
                    for (int i = 0; i < chunkDigits.Length; i++)
                    {
                        char digitChar = chunkDigits[i];
                        if (digitChar == '\0')
                        {
                            break;
                        }

                        int hexValue = HexConverter.FromChar(digitChar);
                        Debug.Assert(hexValue != 0xFF);

                        partialValue = (partialValue << 4) | (uint)hexValue;
                        partialDigitCount++;

                        if (partialDigitCount == DigitsPerBlock)
                        {
                            bitsBuffer[bitsBufferPos] = partialValue;
                            bitsBufferPos--;
                            partialValue      = 0;
                            partialDigitCount = 0;
                        }
                    }
                }

                Debug.Assert(partialDigitCount == 0 && bitsBufferPos == -1);

                // BigInteger requires leading zero blocks to be truncated.
                bitsBuffer = bitsBuffer.TrimEnd(0u);

                int sign;
                uint[]? bits;

                if (bitsBuffer.IsEmpty)
                {
                    sign = 0;
                    bits = null;
                }
                else if (bitsBuffer.Length == 1)
                {
                    sign = (int)bitsBuffer[0];
                    bits = null;

                    if ((!isNegative && sign < 0) || sign == int.MinValue)
                    {
                        sign = isNegative ? -1 : 1;
                        bits = new[] { (uint)sign };
                    }
                }
                else
                {
                    sign = isNegative ? -1 : 1;
                    bits = bitsBuffer.ToArray();

                    if (isNegative)
                    {
                        NumericsHelpers.DangerousMakeTwosComplement(bits);
                    }
                }

                result = new BigInteger(sign, bits);
                return(true);
            }
            finally
            {
                if (arrayFromPool != null)
                {
                    ArrayPool <int> .Shared.Return(arrayFromPool);
                }
            }
        }
        public BigInteger(ReadOnlySpan <byte> value, bool isUnsigned = false, bool isBigEndian = false)
        {
            int byteCount = value.Length;

            bool isNegative;

            if (byteCount > 0)
            {
                byte mostSignificantByte = isBigEndian ? value[0] : value[byteCount - 1];
                isNegative = (mostSignificantByte & 0x80) != 0 && !isUnsigned;

                if (mostSignificantByte == 0)
                {
                    // Try to conserve space as much as possible by checking for wasted leading byte[] entries
                    if (isBigEndian)
                    {
                        int offset = 1;

                        while (offset < byteCount && value[offset] == 0)
                        {
                            offset++;
                        }

                        value     = value.Slice(offset);
                        byteCount = value.Length;
                    }
                    else
                    {
                        byteCount -= 2;

                        while (byteCount >= 0 && value[byteCount] == 0)
                        {
                            byteCount--;
                        }

                        byteCount++;
                    }
                }
            }
            else
            {
                isNegative = false;
            }

            if (byteCount == 0)
            {
                // BigInteger.Zero
                _sign = 0;
                _bits = null;
                AssertValid();
                return;
            }

            if (byteCount <= 4)
            {
                _sign = isNegative ? unchecked ((int)0xffffffff) : 0;

                if (isBigEndian)
                {
                    for (int i = 0; i < byteCount; i++)
                    {
                        _sign = (_sign << 8) | value[i];
                    }
                }
                else
                {
                    for (int i = byteCount - 1; i >= 0; i--)
                    {
                        _sign = (_sign << 8) | value[i];
                    }
                }

                _bits = null;
                if (_sign < 0 && !isNegative)
                {
                    // Int32 overflow
                    // Example: Int64 value 2362232011 (0xCB, 0xCC, 0xCC, 0x8C, 0x0)
                    // can be naively packed into 4 bytes (due to the leading 0x0)
                    // it overflows into the int32 sign bit
                    _bits = new uint[1] {
                        unchecked ((uint)_sign)
                    };
                    _sign = +1;
                }
                if (_sign == int.MinValue)
                {
                    this = s_bnMinInt;
                }
            }
            else
            {
                int    unalignedBytes  = byteCount % 4;
                int    dwordCount      = byteCount / 4 + (unalignedBytes == 0 ? 0 : 1);
                uint[] val             = new uint[dwordCount];
                int    byteCountMinus1 = byteCount - 1;

                // Copy all dwords, except don't do the last one if it's not a full four bytes
                int curDword, curByte;

                if (isBigEndian)
                {
                    curByte = byteCount - sizeof(int);
                    for (curDword = 0; curDword < dwordCount - (unalignedBytes == 0 ? 0 : 1); curDword++)
                    {
                        for (int byteInDword = 0; byteInDword < 4; byteInDword++)
                        {
                            byte curByteValue = value[curByte];
                            val[curDword] = (val[curDword] << 8) | curByteValue;
                            curByte++;
                        }

                        curByte -= 8;
                    }
                }
                else
                {
                    curByte = sizeof(int) - 1;
                    for (curDword = 0; curDword < dwordCount - (unalignedBytes == 0 ? 0 : 1); curDword++)
                    {
                        for (int byteInDword = 0; byteInDword < 4; byteInDword++)
                        {
                            byte curByteValue = value[curByte];
                            val[curDword] = (val[curDword] << 8) | curByteValue;
                            curByte--;
                        }

                        curByte += 8;
                    }
                }

                // Copy the last dword specially if it's not aligned
                if (unalignedBytes != 0)
                {
                    if (isNegative)
                    {
                        val[dwordCount - 1] = 0xffffffff;
                    }

                    if (isBigEndian)
                    {
                        for (curByte = 0; curByte < unalignedBytes; curByte++)
                        {
                            byte curByteValue = value[curByte];
                            val[curDword] = (val[curDword] << 8) | curByteValue;
                        }
                    }
                    else
                    {
                        for (curByte = byteCountMinus1; curByte >= byteCount - unalignedBytes; curByte--)
                        {
                            byte curByteValue = value[curByte];
                            val[curDword] = (val[curDword] << 8) | curByteValue;
                        }
                    }
                }

                if (isNegative)
                {
                    NumericsHelpers.DangerousMakeTwosComplement(val);                     // Mutates val

                    // Pack _bits to remove any wasted space after the twos complement
                    int len = val.Length - 1;
                    while (len >= 0 && val[len] == 0)
                    {
                        len--;
                    }
                    len++;

                    if (len == 1)
                    {
                        switch (val[0])
                        {
                        case 1:                         // abs(-1)
                            this = s_bnMinusOneInt;
                            return;

                        case kuMaskHighBit:                         // abs(Int32.MinValue)
                            this = s_bnMinInt;
                            return;

                        default:
                            if (unchecked ((int)val[0]) > 0)
                            {
                                _sign = (-1) * ((int)val[0]);
                                _bits = null;
                                AssertValid();
                                return;
                            }

                            break;
                        }
                    }

                    if (len != val.Length)
                    {
                        _sign = -1;
                        _bits = new uint[len];
                        Array.Copy(val, 0, _bits, 0, len);
                    }
                    else
                    {
                        _sign = -1;
                        _bits = val;
                    }
                }
                else
                {
                    _sign = +1;
                    _bits = val;
                }
            }
            AssertValid();
        }