package pgtype
import (
)
const nbase = 10_000
const (
pgNumericNaN = 0x00000000c0000000
pgNumericNaNSign = 0xc000
pgNumericPosInf = 0x00000000d0000000
pgNumericPosInfSign = 0xd000
pgNumericNegInf = 0x00000000f0000000
pgNumericNegInfSign = 0xf000
)
var (
big1 *big.Int = big.NewInt(1)
big10 *big.Int = big.NewInt(10)
big100 *big.Int = big.NewInt(100)
big1000 *big.Int = big.NewInt(1000)
)
var (
bigNBase *big.Int = big.NewInt(nbase)
bigNBaseX2 *big.Int = big.NewInt(nbase * nbase)
bigNBaseX3 *big.Int = big.NewInt(nbase * nbase * nbase)
bigNBaseX4 *big.Int = big.NewInt(nbase * nbase * nbase * nbase)
)
type NumericScanner interface {
ScanNumeric(v Numeric) error
}
type NumericValuer interface {
NumericValue() (Numeric, error)
}
type Numeric struct {
Int *big.Int
Exp int32
NaN bool
InfinityModifier InfinityModifier
Valid bool
}
func ( *Numeric) ( Numeric) error {
* =
return nil
}
func ( Numeric) () (Numeric, error) {
return , nil
}
func ( Numeric) () (Float8, error) {
if !.Valid {
return Float8{}, nil
} else if .NaN {
return Float8{Float64: math.NaN(), Valid: true}, nil
} else if .InfinityModifier == Infinity {
return Float8{Float64: math.Inf(1), Valid: true}, nil
} else if .InfinityModifier == NegativeInfinity {
return Float8{Float64: math.Inf(-1), Valid: true}, nil
}
:= make([]byte, 0, 32)
if .Int == nil {
= append(, '0')
} else {
= append(, .Int.String()...)
}
= append(, 'e')
= append(, strconv.FormatInt(int64(.Exp), 10)...)
, := strconv.ParseFloat(string(), 64)
if != nil {
return Float8{},
}
return Float8{Float64: , Valid: true}, nil
}
func ( *Numeric) ( Int8) error {
if !.Valid {
* = Numeric{}
return nil
}
* = Numeric{Int: big.NewInt(.Int64), Valid: true}
return nil
}
func ( Numeric) () (Int8, error) {
if !.Valid {
return Int8{}, nil
}
, := .toBigInt()
if != nil {
return Int8{},
}
if !.IsInt64() {
return Int8{}, fmt.Errorf("cannot convert %v to int64", )
}
return Int8{Int64: .Int64(), Valid: true}, nil
}
func ( *Numeric) ( string) error {
if !strings.ContainsAny(, "eE") {
return scanPlanTextAnyToNumericScanner{}.Scan([]byte(), )
}
if , := new(big.Float).SetString(string()); {
, := .Float64()
= strconv.FormatFloat(, 'f', -1, 64)
}
, , := parseNumericString()
if != nil {
return
}
* = Numeric{Int: , Exp: , Valid: true}
return nil
}
func ( *Numeric) () (*big.Int, error) {
if .Exp == 0 {
return .Int, nil
}
:= &big.Int{}
.Set(.Int)
if .Exp > 0 {
:= &big.Int{}
.Exp(big10, big.NewInt(int64(.Exp)), nil)
.Mul(, )
return , nil
}
:= &big.Int{}
.Exp(big10, big.NewInt(int64(-.Exp)), nil)
:= &big.Int{}
.DivMod(, , )
if .Sign() != 0 {
return nil, fmt.Errorf("cannot convert %v to integer", )
}
return , nil
}
func ( string) ( *big.Int, int32, error) {
:= strings.IndexByte(, '.')
if == -1 {
for len() > 1 && [len()-1] == '0' && [len()-2] != '-' {
= [:len()-1]
++
}
} else {
= int32(-(len() - - 1))
= [:] + [+1:]
}
:= &big.Int{}
if , := .SetString(, 10); ! {
return nil, 0, fmt.Errorf("%s is not a number", )
}
return , , nil
}
func ( []byte) ( int64, , int) {
:= min(len()/2, 4)
:= 0
for := range {
if > 0 {
*= nbase
}
+= int64(binary.BigEndian.Uint16([:]))
+= 2
}
return , ,
}
func ( *Numeric) ( any) error {
if == nil {
* = Numeric{}
return nil
}
switch src := .(type) {
case string:
return scanPlanTextAnyToNumericScanner{}.Scan([]byte(), )
}
return fmt.Errorf("cannot scan %T", )
}
func ( Numeric) () (driver.Value, error) {
if !.Valid {
return nil, nil
}
, := NumericCodec{}.PlanEncode(nil, 0, TextFormatCode, ).Encode(, nil)
if != nil {
return nil,
}
return string(),
}
func ( Numeric) () ([]byte, error) {
if !.Valid {
return []byte("null"), nil
}
if .NaN {
return []byte(`"NaN"`), nil
}
return .numberTextBytes(), nil
}
func ( *Numeric) ( []byte) error {
if bytes.Equal(, []byte(`null`)) {
* = Numeric{}
return nil
}
if bytes.Equal(, []byte(`"NaN"`)) {
* = Numeric{NaN: true, Valid: true}
return nil
}
return scanPlanTextAnyToNumericScanner{}.Scan(, )
}
func ( Numeric) () []byte {
if .Int == nil {
return []byte("0")
}
:= .Int.String()
:= &bytes.Buffer{}
if len() > 0 && [:1] == "-" {
= [1:]
.WriteByte('-')
}
:= int(.Exp)
if > 0 {
.WriteString()
for range {
.WriteByte('0')
}
} else if < 0 {
if len() <= - {
.WriteString("0.")
:= - - len()
for range {
.WriteByte('0')
}
.WriteString()
} else if len() > - {
:= len() +
.WriteString([:])
.WriteByte('.')
.WriteString([:])
}
} else {
.WriteString()
}
return .Bytes()
}
type NumericCodec struct{}
func (NumericCodec) ( int16) bool {
return == TextFormatCode || == BinaryFormatCode
}
func (NumericCodec) () int16 {
return BinaryFormatCode
}
func (NumericCodec) ( *Map, uint32, int16, any) EncodePlan {
switch {
case BinaryFormatCode:
switch .(type) {
case NumericValuer:
return encodePlanNumericCodecBinaryNumericValuer{}
case Float64Valuer:
return encodePlanNumericCodecBinaryFloat64Valuer{}
case Int64Valuer:
return encodePlanNumericCodecBinaryInt64Valuer{}
}
case TextFormatCode:
switch .(type) {
case NumericValuer:
return encodePlanNumericCodecTextNumericValuer{}
case Float64Valuer:
return encodePlanNumericCodecTextFloat64Valuer{}
case Int64Valuer:
return encodePlanNumericCodecTextInt64Valuer{}
}
}
return nil
}
type encodePlanNumericCodecBinaryNumericValuer struct{}
func (encodePlanNumericCodecBinaryNumericValuer) ( any, []byte) ( []byte, error) {
, := .(NumericValuer).NumericValue()
if != nil {
return nil,
}
return encodeNumericBinary(, )
}
type encodePlanNumericCodecBinaryFloat64Valuer struct{}
func (encodePlanNumericCodecBinaryFloat64Valuer) ( any, []byte) ( []byte, error) {
, := .(Float64Valuer).Float64Value()
if != nil {
return nil,
}
if !.Valid {
return nil, nil
}
if math.IsNaN(.Float64) {
return encodeNumericBinary(Numeric{NaN: true, Valid: true}, )
} else if math.IsInf(.Float64, 1) {
return encodeNumericBinary(Numeric{InfinityModifier: Infinity, Valid: true}, )
} else if math.IsInf(.Float64, -1) {
return encodeNumericBinary(Numeric{InfinityModifier: NegativeInfinity, Valid: true}, )
}
, , := parseNumericString(strconv.FormatFloat(.Float64, 'f', -1, 64))
if != nil {
return nil,
}
return encodeNumericBinary(Numeric{Int: , Exp: , Valid: true}, )
}
type encodePlanNumericCodecBinaryInt64Valuer struct{}
func (encodePlanNumericCodecBinaryInt64Valuer) ( any, []byte) ( []byte, error) {
, := .(Int64Valuer).Int64Value()
if != nil {
return nil,
}
if !.Valid {
return nil, nil
}
return encodeNumericBinary(Numeric{Int: big.NewInt(.Int64), Valid: true}, )
}
func ( Numeric, []byte) ( []byte, error) {
if !.Valid {
return nil, nil
}
if .NaN {
= pgio.AppendUint64(, pgNumericNaN)
return , nil
} else if .InfinityModifier == Infinity {
= pgio.AppendUint64(, pgNumericPosInf)
return , nil
} else if .InfinityModifier == NegativeInfinity {
= pgio.AppendUint64(, pgNumericNegInf)
return , nil
}
var int16
if .Int != nil && .Int.Sign() < 0 {
= 16384
}
:= &big.Int{}
:= &big.Int{}
:= &big.Int{}
:= &big.Int{}
if .Int != nil {
.Abs(.Int)
}
var int32
switch .Exp % 4 {
case 1, -3:
= .Exp - 1
.Mul(, big10)
case 2, -2:
= .Exp - 2
.Mul(, big100)
case 3, -1:
= .Exp - 3
.Mul(, big1000)
default:
= .Exp
}
if < 0 {
:= &big.Int{}
.Exp(big10, big.NewInt(int64(-)), nil)
.DivMod(, , )
.Add(, )
} else {
=
}
var , []int16
for .Sign() != 0 {
.DivMod(, bigNBase, )
= append(, int16(.Int64()))
}
if .Sign() != 0 {
for .Cmp(big1) != 0 {
.DivMod(, bigNBase, )
= append(, int16(.Int64()))
}
}
= pgio.AppendInt16(, int16(len()+len()))
var int16
if len() > 0 {
= int16(len() - 1)
if > 0 {
+= int16( / 4)
}
} else {
= int16(/4) - 1 + int16(len())
}
= pgio.AppendInt16(, )
= pgio.AppendInt16(, )
var int16
if .Exp < 0 {
= int16(-.Exp)
}
= pgio.AppendInt16(, )
for := len() - 1; >= 0; -- {
= pgio.AppendInt16(, [])
}
for := len() - 1; >= 0; -- {
= pgio.AppendInt16(, [])
}
return , nil
}
type encodePlanNumericCodecTextNumericValuer struct{}
func (encodePlanNumericCodecTextNumericValuer) ( any, []byte) ( []byte, error) {
, := .(NumericValuer).NumericValue()
if != nil {
return nil,
}
return encodeNumericText(, )
}
type encodePlanNumericCodecTextFloat64Valuer struct{}
func (encodePlanNumericCodecTextFloat64Valuer) ( any, []byte) ( []byte, error) {
, := .(Float64Valuer).Float64Value()
if != nil {
return nil,
}
if !.Valid {
return nil, nil
}
if math.IsNaN(.Float64) {
= append(, "NaN"...)
} else if math.IsInf(.Float64, 1) {
= append(, "Infinity"...)
} else if math.IsInf(.Float64, -1) {
= append(, "-Infinity"...)
} else {
= append(, strconv.FormatFloat(.Float64, 'f', -1, 64)...)
}
return , nil
}
type encodePlanNumericCodecTextInt64Valuer struct{}
func (encodePlanNumericCodecTextInt64Valuer) ( any, []byte) ( []byte, error) {
, := .(Int64Valuer).Int64Value()
if != nil {
return nil,
}
if !.Valid {
return nil, nil
}
= append(, strconv.FormatInt(.Int64, 10)...)
return , nil
}
func ( Numeric, []byte) ( []byte, error) {
if !.Valid {
return nil, nil
}
if .NaN {
= append(, "NaN"...)
return , nil
} else if .InfinityModifier == Infinity {
= append(, "Infinity"...)
return , nil
} else if .InfinityModifier == NegativeInfinity {
= append(, "-Infinity"...)
return , nil
}
= append(, .numberTextBytes()...)
return , nil
}
func (NumericCodec) ( *Map, uint32, int16, any) ScanPlan {
switch {
case BinaryFormatCode:
switch .(type) {
case NumericScanner:
return scanPlanBinaryNumericToNumericScanner{}
case Float64Scanner:
return scanPlanBinaryNumericToFloat64Scanner{}
case Int64Scanner:
return scanPlanBinaryNumericToInt64Scanner{}
case TextScanner:
return scanPlanBinaryNumericToTextScanner{}
}
case TextFormatCode:
switch .(type) {
case NumericScanner:
return scanPlanTextAnyToNumericScanner{}
case Float64Scanner:
return scanPlanTextAnyToFloat64Scanner{}
case Int64Scanner:
return scanPlanTextAnyToInt64Scanner{}
}
}
return nil
}
type scanPlanBinaryNumericToNumericScanner struct{}
func (scanPlanBinaryNumericToNumericScanner) ( []byte, any) error {
:= ().(NumericScanner)
if == nil {
return .ScanNumeric(Numeric{})
}
if len() < 8 {
return fmt.Errorf("numeric incomplete %v", )
}
:= 0
:= binary.BigEndian.Uint16([:])
+= 2
:= int16(binary.BigEndian.Uint16([:]))
+= 2
:= binary.BigEndian.Uint16([:])
+= 2
:= int16(binary.BigEndian.Uint16([:]))
+= 2
if == pgNumericNaNSign {
return .ScanNumeric(Numeric{NaN: true, Valid: true})
} else if == pgNumericPosInfSign {
return .ScanNumeric(Numeric{InfinityModifier: Infinity, Valid: true})
} else if == pgNumericNegInfSign {
return .ScanNumeric(Numeric{InfinityModifier: NegativeInfinity, Valid: true})
}
if == 0 {
return .ScanNumeric(Numeric{Int: big.NewInt(0), Valid: true})
}
if len([:]) < int()*2 {
return fmt.Errorf("numeric incomplete %v", )
}
:= &big.Int{}
for := 0; < int(+3)/4; ++ {
, , := nbaseDigitsToInt64([:])
+=
if > 0 {
var *big.Int
switch {
case 1:
= bigNBase
case 2:
= bigNBaseX2
case 3:
= bigNBaseX3
case 4:
= bigNBaseX4
default:
return fmt.Errorf("invalid digitsRead: %d (this can't happen)", )
}
.Mul(, )
}
.Add(, big.NewInt())
}
:= (int32() - int32() + 1) * 4
if > 0 {
:= int() - int() - 1
:= * 4
:= int()
if > {
:= -
for range {
.Mul(, big10)
--
}
} else if < {
:= -
for range {
.Div(, big10)
++
}
}
}
:= &big.Int{}
:= &big.Int{}
if >= 0 {
for {
.DivMod(, big10, )
if .Sign() != 0 {
break
}
.Set()
++
}
}
if != 0 {
.Neg()
}
return .ScanNumeric(Numeric{Int: , Exp: , Valid: true})
}
type scanPlanBinaryNumericToFloat64Scanner struct{}
func (scanPlanBinaryNumericToFloat64Scanner) ( []byte, any) error {
:= ().(Float64Scanner)
if == nil {
return .ScanFloat64(Float8{})
}
var Numeric
:= scanPlanBinaryNumericToNumericScanner{}.Scan(, &)
if != nil {
return
}
, := .Float64Value()
if != nil {
return
}
return .ScanFloat64()
}
type scanPlanBinaryNumericToInt64Scanner struct{}
func (scanPlanBinaryNumericToInt64Scanner) ( []byte, any) error {
:= ().(Int64Scanner)
if == nil {
return .ScanInt64(Int8{})
}
var Numeric
:= scanPlanBinaryNumericToNumericScanner{}.Scan(, &)
if != nil {
return
}
, := .toBigInt()
if != nil {
return
}
if !.IsInt64() {
return fmt.Errorf("%v is out of range for int64", )
}
return .ScanInt64(Int8{Int64: .Int64(), Valid: true})
}
type scanPlanBinaryNumericToTextScanner struct{}
func (scanPlanBinaryNumericToTextScanner) ( []byte, any) error {
:= ().(TextScanner)
if == nil {
return .ScanText(Text{})
}
var Numeric
:= scanPlanBinaryNumericToNumericScanner{}.Scan(, &)
if != nil {
return
}
, := encodeNumericText(, nil)
if != nil {
return
}
return .ScanText(Text{String: string(), Valid: true})
}
type scanPlanTextAnyToNumericScanner struct{}
func (scanPlanTextAnyToNumericScanner) ( []byte, any) error {
:= ().(NumericScanner)
if == nil {
return .ScanNumeric(Numeric{})
}
if string() == "NaN" {
return .ScanNumeric(Numeric{NaN: true, Valid: true})
} else if string() == "Infinity" {
return .ScanNumeric(Numeric{InfinityModifier: Infinity, Valid: true})
} else if string() == "-Infinity" {
return .ScanNumeric(Numeric{InfinityModifier: NegativeInfinity, Valid: true})
}
, , := parseNumericString(string())
if != nil {
return
}
return .ScanNumeric(Numeric{Int: , Exp: , Valid: true})
}
func ( NumericCodec) ( *Map, uint32, int16, []byte) (driver.Value, error) {
if == nil {
return nil, nil
}
if == TextFormatCode {
return string(), nil
}
var Numeric
:= codecScan(, , , , , &)
if != nil {
return nil,
}
, := .Encode(, TextFormatCode, , nil)
if != nil {
return nil,
}
return string(), nil
}
func ( NumericCodec) ( *Map, uint32, int16, []byte) (any, error) {
if == nil {
return nil, nil
}
var Numeric
:= codecScan(, , , , , &)
if != nil {
return nil,
}
return , nil
}