1 package pgtype
2
3 import (
4 "bytes"
5 "database/sql/driver"
6 "encoding/binary"
7 "fmt"
8 "math"
9 "math/big"
10 "strconv"
11 "strings"
12
13 "github.com/jackc/pgx/v5/internal/pgio"
14 )
15
16
17 const nbase = 10000
18
19 const (
20 pgNumericNaN = 0x00000000c0000000
21 pgNumericNaNSign = 0xc000
22
23 pgNumericPosInf = 0x00000000d0000000
24 pgNumericPosInfSign = 0xd000
25
26 pgNumericNegInf = 0x00000000f0000000
27 pgNumericNegInfSign = 0xf000
28 )
29
30 var big0 *big.Int = big.NewInt(0)
31 var big1 *big.Int = big.NewInt(1)
32 var big10 *big.Int = big.NewInt(10)
33 var big100 *big.Int = big.NewInt(100)
34 var big1000 *big.Int = big.NewInt(1000)
35
36 var bigNBase *big.Int = big.NewInt(nbase)
37 var bigNBaseX2 *big.Int = big.NewInt(nbase * nbase)
38 var bigNBaseX3 *big.Int = big.NewInt(nbase * nbase * nbase)
39 var bigNBaseX4 *big.Int = big.NewInt(nbase * nbase * nbase * nbase)
40
41 type NumericScanner interface {
42 ScanNumeric(v Numeric) error
43 }
44
45 type NumericValuer interface {
46 NumericValue() (Numeric, error)
47 }
48
49 type Numeric struct {
50 Int *big.Int
51 Exp int32
52 NaN bool
53 InfinityModifier InfinityModifier
54 Valid bool
55 }
56
57 func (n *Numeric) ScanNumeric(v Numeric) error {
58 *n = v
59 return nil
60 }
61
62 func (n Numeric) NumericValue() (Numeric, error) {
63 return n, nil
64 }
65
66 func (n Numeric) Float64Value() (Float8, error) {
67 if !n.Valid {
68 return Float8{}, nil
69 } else if n.NaN {
70 return Float8{Float64: math.NaN(), Valid: true}, nil
71 } else if n.InfinityModifier == Infinity {
72 return Float8{Float64: math.Inf(1), Valid: true}, nil
73 } else if n.InfinityModifier == NegativeInfinity {
74 return Float8{Float64: math.Inf(-1), Valid: true}, nil
75 }
76
77 buf := make([]byte, 0, 32)
78
79 if n.Int == nil {
80 buf = append(buf, '0')
81 } else {
82 buf = append(buf, n.Int.String()...)
83 }
84 buf = append(buf, 'e')
85 buf = append(buf, strconv.FormatInt(int64(n.Exp), 10)...)
86
87 f, err := strconv.ParseFloat(string(buf), 64)
88 if err != nil {
89 return Float8{}, err
90 }
91
92 return Float8{Float64: f, Valid: true}, nil
93 }
94
95 func (n *Numeric) ScanInt64(v Int8) error {
96 if !v.Valid {
97 *n = Numeric{}
98 return nil
99 }
100
101 *n = Numeric{Int: big.NewInt(v.Int64), Valid: true}
102 return nil
103 }
104
105 func (n Numeric) Int64Value() (Int8, error) {
106 if !n.Valid {
107 return Int8{}, nil
108 }
109
110 bi, err := n.toBigInt()
111 if err != nil {
112 return Int8{}, err
113 }
114
115 if !bi.IsInt64() {
116 return Int8{}, fmt.Errorf("cannot convert %v to int64", n)
117 }
118
119 return Int8{Int64: bi.Int64(), Valid: true}, nil
120 }
121
122 func (n *Numeric) ScanScientific(src string) error {
123 if !strings.ContainsAny("eE", src) {
124 return scanPlanTextAnyToNumericScanner{}.Scan([]byte(src), n)
125 }
126
127 if bigF, ok := new(big.Float).SetString(string(src)); ok {
128 smallF, _ := bigF.Float64()
129 src = strconv.FormatFloat(smallF, 'f', -1, 64)
130 }
131
132 num, exp, err := parseNumericString(src)
133 if err != nil {
134 return err
135 }
136
137 *n = Numeric{Int: num, Exp: exp, Valid: true}
138
139 return nil
140 }
141
142 func (n *Numeric) toBigInt() (*big.Int, error) {
143 if n.Exp == 0 {
144 return n.Int, nil
145 }
146
147 num := &big.Int{}
148 num.Set(n.Int)
149 if n.Exp > 0 {
150 mul := &big.Int{}
151 mul.Exp(big10, big.NewInt(int64(n.Exp)), nil)
152 num.Mul(num, mul)
153 return num, nil
154 }
155
156 div := &big.Int{}
157 div.Exp(big10, big.NewInt(int64(-n.Exp)), nil)
158 remainder := &big.Int{}
159 num.DivMod(num, div, remainder)
160 if remainder.Cmp(big0) != 0 {
161 return nil, fmt.Errorf("cannot convert %v to integer", n)
162 }
163 return num, nil
164 }
165
166 func parseNumericString(str string) (n *big.Int, exp int32, err error) {
167 idx := strings.IndexByte(str, '.')
168
169 if idx == -1 {
170 for len(str) > 1 && str[len(str)-1] == '0' && str[len(str)-2] != '-' {
171 str = str[:len(str)-1]
172 exp++
173 }
174 } else {
175 exp = int32(-(len(str) - idx - 1))
176 str = str[:idx] + str[idx+1:]
177 }
178
179 accum := &big.Int{}
180 if _, ok := accum.SetString(str, 10); !ok {
181 return nil, 0, fmt.Errorf("%s is not a number", str)
182 }
183
184 return accum, exp, nil
185 }
186
187 func nbaseDigitsToInt64(src []byte) (accum int64, bytesRead, digitsRead int) {
188 digits := len(src) / 2
189 if digits > 4 {
190 digits = 4
191 }
192
193 rp := 0
194
195 for i := 0; i < digits; i++ {
196 if i > 0 {
197 accum *= nbase
198 }
199 accum += int64(binary.BigEndian.Uint16(src[rp:]))
200 rp += 2
201 }
202
203 return accum, rp, digits
204 }
205
206
207 func (n *Numeric) Scan(src any) error {
208 if src == nil {
209 *n = Numeric{}
210 return nil
211 }
212
213 switch src := src.(type) {
214 case string:
215 return scanPlanTextAnyToNumericScanner{}.Scan([]byte(src), n)
216 }
217
218 return fmt.Errorf("cannot scan %T", src)
219 }
220
221
222 func (n Numeric) Value() (driver.Value, error) {
223 if !n.Valid {
224 return nil, nil
225 }
226
227 buf, err := NumericCodec{}.PlanEncode(nil, 0, TextFormatCode, n).Encode(n, nil)
228 if err != nil {
229 return nil, err
230 }
231 return string(buf), err
232 }
233
234 func (n Numeric) MarshalJSON() ([]byte, error) {
235 if !n.Valid {
236 return []byte("null"), nil
237 }
238
239 if n.NaN {
240 return []byte(`"NaN"`), nil
241 }
242
243 return n.numberTextBytes(), nil
244 }
245
246 func (n *Numeric) UnmarshalJSON(src []byte) error {
247 if bytes.Equal(src, []byte(`null`)) {
248 *n = Numeric{}
249 return nil
250 }
251 if bytes.Equal(src, []byte(`"NaN"`)) {
252 *n = Numeric{NaN: true, Valid: true}
253 return nil
254 }
255 return scanPlanTextAnyToNumericScanner{}.Scan(src, n)
256 }
257
258
259 func (n Numeric) numberTextBytes() []byte {
260 intStr := n.Int.String()
261
262 buf := &bytes.Buffer{}
263
264 if len(intStr) > 0 && intStr[:1] == "-" {
265 intStr = intStr[1:]
266 buf.WriteByte('-')
267 }
268
269 exp := int(n.Exp)
270 if exp > 0 {
271 buf.WriteString(intStr)
272 for i := 0; i < exp; i++ {
273 buf.WriteByte('0')
274 }
275 } else if exp < 0 {
276 if len(intStr) <= -exp {
277 buf.WriteString("0.")
278 leadingZeros := -exp - len(intStr)
279 for i := 0; i < leadingZeros; i++ {
280 buf.WriteByte('0')
281 }
282 buf.WriteString(intStr)
283 } else if len(intStr) > -exp {
284 dpPos := len(intStr) + exp
285 buf.WriteString(intStr[:dpPos])
286 buf.WriteByte('.')
287 buf.WriteString(intStr[dpPos:])
288 }
289 } else {
290 buf.WriteString(intStr)
291 }
292
293 return buf.Bytes()
294 }
295
296 type NumericCodec struct{}
297
298 func (NumericCodec) FormatSupported(format int16) bool {
299 return format == TextFormatCode || format == BinaryFormatCode
300 }
301
302 func (NumericCodec) PreferredFormat() int16 {
303 return BinaryFormatCode
304 }
305
306 func (NumericCodec) PlanEncode(m *Map, oid uint32, format int16, value any) EncodePlan {
307 switch format {
308 case BinaryFormatCode:
309 switch value.(type) {
310 case NumericValuer:
311 return encodePlanNumericCodecBinaryNumericValuer{}
312 case Float64Valuer:
313 return encodePlanNumericCodecBinaryFloat64Valuer{}
314 case Int64Valuer:
315 return encodePlanNumericCodecBinaryInt64Valuer{}
316 }
317 case TextFormatCode:
318 switch value.(type) {
319 case NumericValuer:
320 return encodePlanNumericCodecTextNumericValuer{}
321 case Float64Valuer:
322 return encodePlanNumericCodecTextFloat64Valuer{}
323 case Int64Valuer:
324 return encodePlanNumericCodecTextInt64Valuer{}
325 }
326 }
327
328 return nil
329 }
330
331 type encodePlanNumericCodecBinaryNumericValuer struct{}
332
333 func (encodePlanNumericCodecBinaryNumericValuer) Encode(value any, buf []byte) (newBuf []byte, err error) {
334 n, err := value.(NumericValuer).NumericValue()
335 if err != nil {
336 return nil, err
337 }
338
339 return encodeNumericBinary(n, buf)
340 }
341
342 type encodePlanNumericCodecBinaryFloat64Valuer struct{}
343
344 func (encodePlanNumericCodecBinaryFloat64Valuer) Encode(value any, buf []byte) (newBuf []byte, err error) {
345 n, err := value.(Float64Valuer).Float64Value()
346 if err != nil {
347 return nil, err
348 }
349
350 if !n.Valid {
351 return nil, nil
352 }
353
354 if math.IsNaN(n.Float64) {
355 return encodeNumericBinary(Numeric{NaN: true, Valid: true}, buf)
356 } else if math.IsInf(n.Float64, 1) {
357 return encodeNumericBinary(Numeric{InfinityModifier: Infinity, Valid: true}, buf)
358 } else if math.IsInf(n.Float64, -1) {
359 return encodeNumericBinary(Numeric{InfinityModifier: NegativeInfinity, Valid: true}, buf)
360 }
361 num, exp, err := parseNumericString(strconv.FormatFloat(n.Float64, 'f', -1, 64))
362 if err != nil {
363 return nil, err
364 }
365
366 return encodeNumericBinary(Numeric{Int: num, Exp: exp, Valid: true}, buf)
367 }
368
369 type encodePlanNumericCodecBinaryInt64Valuer struct{}
370
371 func (encodePlanNumericCodecBinaryInt64Valuer) Encode(value any, buf []byte) (newBuf []byte, err error) {
372 n, err := value.(Int64Valuer).Int64Value()
373 if err != nil {
374 return nil, err
375 }
376
377 if !n.Valid {
378 return nil, nil
379 }
380
381 return encodeNumericBinary(Numeric{Int: big.NewInt(n.Int64), Valid: true}, buf)
382 }
383
384 func encodeNumericBinary(n Numeric, buf []byte) (newBuf []byte, err error) {
385 if !n.Valid {
386 return nil, nil
387 }
388
389 if n.NaN {
390 buf = pgio.AppendUint64(buf, pgNumericNaN)
391 return buf, nil
392 } else if n.InfinityModifier == Infinity {
393 buf = pgio.AppendUint64(buf, pgNumericPosInf)
394 return buf, nil
395 } else if n.InfinityModifier == NegativeInfinity {
396 buf = pgio.AppendUint64(buf, pgNumericNegInf)
397 return buf, nil
398 }
399
400 var sign int16
401 if n.Int.Cmp(big0) < 0 {
402 sign = 16384
403 }
404
405 absInt := &big.Int{}
406 wholePart := &big.Int{}
407 fracPart := &big.Int{}
408 remainder := &big.Int{}
409 absInt.Abs(n.Int)
410
411
412
413 var exp int32
414 switch n.Exp % 4 {
415 case 1, -3:
416 exp = n.Exp - 1
417 absInt.Mul(absInt, big10)
418 case 2, -2:
419 exp = n.Exp - 2
420 absInt.Mul(absInt, big100)
421 case 3, -1:
422 exp = n.Exp - 3
423 absInt.Mul(absInt, big1000)
424 default:
425 exp = n.Exp
426 }
427
428 if exp < 0 {
429 divisor := &big.Int{}
430 divisor.Exp(big10, big.NewInt(int64(-exp)), nil)
431 wholePart.DivMod(absInt, divisor, fracPart)
432 fracPart.Add(fracPart, divisor)
433 } else {
434 wholePart = absInt
435 }
436
437 var wholeDigits, fracDigits []int16
438
439 for wholePart.Cmp(big0) != 0 {
440 wholePart.DivMod(wholePart, bigNBase, remainder)
441 wholeDigits = append(wholeDigits, int16(remainder.Int64()))
442 }
443
444 if fracPart.Cmp(big0) != 0 {
445 for fracPart.Cmp(big1) != 0 {
446 fracPart.DivMod(fracPart, bigNBase, remainder)
447 fracDigits = append(fracDigits, int16(remainder.Int64()))
448 }
449 }
450
451 buf = pgio.AppendInt16(buf, int16(len(wholeDigits)+len(fracDigits)))
452
453 var weight int16
454 if len(wholeDigits) > 0 {
455 weight = int16(len(wholeDigits) - 1)
456 if exp > 0 {
457 weight += int16(exp / 4)
458 }
459 } else {
460 weight = int16(exp/4) - 1 + int16(len(fracDigits))
461 }
462 buf = pgio.AppendInt16(buf, weight)
463
464 buf = pgio.AppendInt16(buf, sign)
465
466 var dscale int16
467 if n.Exp < 0 {
468 dscale = int16(-n.Exp)
469 }
470 buf = pgio.AppendInt16(buf, dscale)
471
472 for i := len(wholeDigits) - 1; i >= 0; i-- {
473 buf = pgio.AppendInt16(buf, wholeDigits[i])
474 }
475
476 for i := len(fracDigits) - 1; i >= 0; i-- {
477 buf = pgio.AppendInt16(buf, fracDigits[i])
478 }
479
480 return buf, nil
481 }
482
483 type encodePlanNumericCodecTextNumericValuer struct{}
484
485 func (encodePlanNumericCodecTextNumericValuer) Encode(value any, buf []byte) (newBuf []byte, err error) {
486 n, err := value.(NumericValuer).NumericValue()
487 if err != nil {
488 return nil, err
489 }
490
491 return encodeNumericText(n, buf)
492 }
493
494 type encodePlanNumericCodecTextFloat64Valuer struct{}
495
496 func (encodePlanNumericCodecTextFloat64Valuer) Encode(value any, buf []byte) (newBuf []byte, err error) {
497 n, err := value.(Float64Valuer).Float64Value()
498 if err != nil {
499 return nil, err
500 }
501
502 if !n.Valid {
503 return nil, nil
504 }
505
506 if math.IsNaN(n.Float64) {
507 buf = append(buf, "NaN"...)
508 } else if math.IsInf(n.Float64, 1) {
509 buf = append(buf, "Infinity"...)
510 } else if math.IsInf(n.Float64, -1) {
511 buf = append(buf, "-Infinity"...)
512 } else {
513 buf = append(buf, strconv.FormatFloat(n.Float64, 'f', -1, 64)...)
514 }
515 return buf, nil
516 }
517
518 type encodePlanNumericCodecTextInt64Valuer struct{}
519
520 func (encodePlanNumericCodecTextInt64Valuer) Encode(value any, buf []byte) (newBuf []byte, err error) {
521 n, err := value.(Int64Valuer).Int64Value()
522 if err != nil {
523 return nil, err
524 }
525
526 if !n.Valid {
527 return nil, nil
528 }
529
530 buf = append(buf, strconv.FormatInt(n.Int64, 10)...)
531 return buf, nil
532 }
533
534 func encodeNumericText(n Numeric, buf []byte) (newBuf []byte, err error) {
535 if !n.Valid {
536 return nil, nil
537 }
538
539 if n.NaN {
540 buf = append(buf, "NaN"...)
541 return buf, nil
542 } else if n.InfinityModifier == Infinity {
543 buf = append(buf, "Infinity"...)
544 return buf, nil
545 } else if n.InfinityModifier == NegativeInfinity {
546 buf = append(buf, "-Infinity"...)
547 return buf, nil
548 }
549
550 buf = append(buf, n.numberTextBytes()...)
551
552 return buf, nil
553 }
554
555 func (NumericCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan {
556
557 switch format {
558 case BinaryFormatCode:
559 switch target.(type) {
560 case NumericScanner:
561 return scanPlanBinaryNumericToNumericScanner{}
562 case Float64Scanner:
563 return scanPlanBinaryNumericToFloat64Scanner{}
564 case Int64Scanner:
565 return scanPlanBinaryNumericToInt64Scanner{}
566 case TextScanner:
567 return scanPlanBinaryNumericToTextScanner{}
568 }
569 case TextFormatCode:
570 switch target.(type) {
571 case NumericScanner:
572 return scanPlanTextAnyToNumericScanner{}
573 case Float64Scanner:
574 return scanPlanTextAnyToFloat64Scanner{}
575 case Int64Scanner:
576 return scanPlanTextAnyToInt64Scanner{}
577 }
578 }
579
580 return nil
581 }
582
583 type scanPlanBinaryNumericToNumericScanner struct{}
584
585 func (scanPlanBinaryNumericToNumericScanner) Scan(src []byte, dst any) error {
586 scanner := (dst).(NumericScanner)
587
588 if src == nil {
589 return scanner.ScanNumeric(Numeric{})
590 }
591
592 if len(src) < 8 {
593 return fmt.Errorf("numeric incomplete %v", src)
594 }
595
596 rp := 0
597 ndigits := binary.BigEndian.Uint16(src[rp:])
598 rp += 2
599 weight := int16(binary.BigEndian.Uint16(src[rp:]))
600 rp += 2
601 sign := binary.BigEndian.Uint16(src[rp:])
602 rp += 2
603 dscale := int16(binary.BigEndian.Uint16(src[rp:]))
604 rp += 2
605
606 if sign == pgNumericNaNSign {
607 return scanner.ScanNumeric(Numeric{NaN: true, Valid: true})
608 } else if sign == pgNumericPosInfSign {
609 return scanner.ScanNumeric(Numeric{InfinityModifier: Infinity, Valid: true})
610 } else if sign == pgNumericNegInfSign {
611 return scanner.ScanNumeric(Numeric{InfinityModifier: NegativeInfinity, Valid: true})
612 }
613
614 if ndigits == 0 {
615 return scanner.ScanNumeric(Numeric{Int: big.NewInt(0), Valid: true})
616 }
617
618 if len(src[rp:]) < int(ndigits)*2 {
619 return fmt.Errorf("numeric incomplete %v", src)
620 }
621
622 accum := &big.Int{}
623
624 for i := 0; i < int(ndigits+3)/4; i++ {
625 int64accum, bytesRead, digitsRead := nbaseDigitsToInt64(src[rp:])
626 rp += bytesRead
627
628 if i > 0 {
629 var mul *big.Int
630 switch digitsRead {
631 case 1:
632 mul = bigNBase
633 case 2:
634 mul = bigNBaseX2
635 case 3:
636 mul = bigNBaseX3
637 case 4:
638 mul = bigNBaseX4
639 default:
640 return fmt.Errorf("invalid digitsRead: %d (this can't happen)", digitsRead)
641 }
642 accum.Mul(accum, mul)
643 }
644
645 accum.Add(accum, big.NewInt(int64accum))
646 }
647
648 exp := (int32(weight) - int32(ndigits) + 1) * 4
649
650 if dscale > 0 {
651 fracNBaseDigits := int16(int32(ndigits) - int32(weight) - 1)
652 fracDecimalDigits := fracNBaseDigits * 4
653
654 if dscale > fracDecimalDigits {
655 multCount := int(dscale - fracDecimalDigits)
656 for i := 0; i < multCount; i++ {
657 accum.Mul(accum, big10)
658 exp--
659 }
660 } else if dscale < fracDecimalDigits {
661 divCount := int(fracDecimalDigits - dscale)
662 for i := 0; i < divCount; i++ {
663 accum.Div(accum, big10)
664 exp++
665 }
666 }
667 }
668
669 reduced := &big.Int{}
670 remainder := &big.Int{}
671 if exp >= 0 {
672 for {
673 reduced.DivMod(accum, big10, remainder)
674 if remainder.Cmp(big0) != 0 {
675 break
676 }
677 accum.Set(reduced)
678 exp++
679 }
680 }
681
682 if sign != 0 {
683 accum.Neg(accum)
684 }
685
686 return scanner.ScanNumeric(Numeric{Int: accum, Exp: exp, Valid: true})
687 }
688
689 type scanPlanBinaryNumericToFloat64Scanner struct{}
690
691 func (scanPlanBinaryNumericToFloat64Scanner) Scan(src []byte, dst any) error {
692 scanner := (dst).(Float64Scanner)
693
694 if src == nil {
695 return scanner.ScanFloat64(Float8{})
696 }
697
698 var n Numeric
699
700 err := scanPlanBinaryNumericToNumericScanner{}.Scan(src, &n)
701 if err != nil {
702 return err
703 }
704
705 f8, err := n.Float64Value()
706 if err != nil {
707 return err
708 }
709
710 return scanner.ScanFloat64(f8)
711 }
712
713 type scanPlanBinaryNumericToInt64Scanner struct{}
714
715 func (scanPlanBinaryNumericToInt64Scanner) Scan(src []byte, dst any) error {
716 scanner := (dst).(Int64Scanner)
717
718 if src == nil {
719 return scanner.ScanInt64(Int8{})
720 }
721
722 var n Numeric
723
724 err := scanPlanBinaryNumericToNumericScanner{}.Scan(src, &n)
725 if err != nil {
726 return err
727 }
728
729 bigInt, err := n.toBigInt()
730 if err != nil {
731 return err
732 }
733
734 if !bigInt.IsInt64() {
735 return fmt.Errorf("%v is out of range for int64", bigInt)
736 }
737
738 return scanner.ScanInt64(Int8{Int64: bigInt.Int64(), Valid: true})
739 }
740
741 type scanPlanBinaryNumericToTextScanner struct{}
742
743 func (scanPlanBinaryNumericToTextScanner) Scan(src []byte, dst any) error {
744 scanner := (dst).(TextScanner)
745
746 if src == nil {
747 return scanner.ScanText(Text{})
748 }
749
750 var n Numeric
751
752 err := scanPlanBinaryNumericToNumericScanner{}.Scan(src, &n)
753 if err != nil {
754 return err
755 }
756
757 sbuf, err := encodeNumericText(n, nil)
758 if err != nil {
759 return err
760 }
761
762 return scanner.ScanText(Text{String: string(sbuf), Valid: true})
763 }
764
765 type scanPlanTextAnyToNumericScanner struct{}
766
767 func (scanPlanTextAnyToNumericScanner) Scan(src []byte, dst any) error {
768 scanner := (dst).(NumericScanner)
769
770 if src == nil {
771 return scanner.ScanNumeric(Numeric{})
772 }
773
774 if string(src) == "NaN" {
775 return scanner.ScanNumeric(Numeric{NaN: true, Valid: true})
776 } else if string(src) == "Infinity" {
777 return scanner.ScanNumeric(Numeric{InfinityModifier: Infinity, Valid: true})
778 } else if string(src) == "-Infinity" {
779 return scanner.ScanNumeric(Numeric{InfinityModifier: NegativeInfinity, Valid: true})
780 }
781
782 num, exp, err := parseNumericString(string(src))
783 if err != nil {
784 return err
785 }
786
787 return scanner.ScanNumeric(Numeric{Int: num, Exp: exp, Valid: true})
788 }
789
790 func (c NumericCodec) DecodeDatabaseSQLValue(m *Map, oid uint32, format int16, src []byte) (driver.Value, error) {
791 if src == nil {
792 return nil, nil
793 }
794
795 if format == TextFormatCode {
796 return string(src), nil
797 }
798
799 var n Numeric
800 err := codecScan(c, m, oid, format, src, &n)
801 if err != nil {
802 return nil, err
803 }
804
805 buf, err := m.Encode(oid, TextFormatCode, n, nil)
806 if err != nil {
807 return nil, err
808 }
809 return string(buf), nil
810 }
811
812 func (c NumericCodec) DecodeValue(m *Map, oid uint32, format int16, src []byte) (any, error) {
813 if src == nil {
814 return nil, nil
815 }
816
817 var n Numeric
818 err := codecScan(c, m, oid, format, src, &n)
819 if err != nil {
820 return nil, err
821 }
822 return n, nil
823 }
824
View as plain text