Ejemplo n.º 1
0
    /// <inheritdoc />
    public override void Write(decimal value, NpgsqlWriteBuffer buf, NpgsqlParameter?parameter)
    {
        var raw = new DecimalRaw(value);

        var scaleDifference = MoneyScale - raw.Scale;

        if (scaleDifference > 0)
        {
            DecimalRaw.Multiply(ref raw, DecimalRaw.Powers10[scaleDifference]);
        }
        else
        {
            value = Math.Round(value, MoneyScale, MidpointRounding.AwayFromZero);
            raw   = new DecimalRaw(value);
        }

        var result = (long)raw.Mid << 32 | raw.Low;

        if (raw.Negative)
        {
            result = -result;
        }
        buf.WriteInt64(result);
    }
Ejemplo n.º 2
0
    /// <inheritdoc />
    public override async ValueTask <decimal> Read(NpgsqlReadBuffer buf, int len, bool async, FieldDescription?fieldDescription = null)
    {
        await buf.Ensure(4 *sizeof(short), async);

        var result = new DecimalRaw();
        var groups = buf.ReadInt16();
        var weight = buf.ReadInt16() - groups + 1;
        var sign   = buf.ReadUInt16();

        if ((sign & SignSpecialMask) == SignSpecialMask)
        {
            throw sign switch
                  {
                      SignNan => new InvalidCastException("Numeric NaN not supported by System.Decimal"),
                      SignPinf => new InvalidCastException("Numeric Infinity not supported by System.Decimal"),
                      SignNinf => new InvalidCastException("Numeric -Infinity not supported by System.Decimal"),
                      _ => new InvalidCastException($"Numeric special value {sign} not supported by System.Decimal")
                  };
        }

        if (sign == SignNegative)
        {
            DecimalRaw.Negate(ref result);
        }

        var scale = buf.ReadInt16();

        if (scale < 0 is var exponential && exponential)
        {
            scale = (short)(-scale);
        }
        else
        {
            result.Scale = scale;
        }

        if (scale > MaxDecimalScale)
        {
            throw new OverflowException("Numeric value does not fit in a System.Decimal");
        }

        var scaleDifference = exponential
            ? weight * MaxGroupScale
            : weight * MaxGroupScale + scale;

        if (groups > MaxGroupCount)
        {
            throw new OverflowException("Numeric value does not fit in a System.Decimal");
        }

        await buf.Ensure(groups *sizeof(ushort), async);

        if (groups == MaxGroupCount)
        {
            while (groups-- > 1)
            {
                DecimalRaw.Multiply(ref result, MaxGroupSize);
                DecimalRaw.Add(ref result, buf.ReadUInt16());
            }

            var group     = buf.ReadUInt16();
            var groupSize = DecimalRaw.Powers10[-scaleDifference];
            if (group % groupSize != 0)
            {
                throw new OverflowException("Numeric value does not fit in a System.Decimal");
            }

            DecimalRaw.Multiply(ref result, MaxGroupSize / groupSize);
            DecimalRaw.Add(ref result, group / groupSize);
        }
        else
        {
            while (groups-- > 0)
            {
                DecimalRaw.Multiply(ref result, MaxGroupSize);
                DecimalRaw.Add(ref result, buf.ReadUInt16());
            }

            if (scaleDifference < 0)
            {
                DecimalRaw.Divide(ref result, DecimalRaw.Powers10[-scaleDifference]);
            }
            else
            {
                while (scaleDifference > 0)
                {
                    var scaleChunk = Math.Min(DecimalRaw.MaxUInt32Scale, scaleDifference);
                    DecimalRaw.Multiply(ref result, DecimalRaw.Powers10[scaleChunk]);
                    scaleDifference -= scaleChunk;
                }
            }
        }

        return(result.Value);
    }