/// <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)); }
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(); }