1
2
3
4
5
6
7 package json
8
9 import (
10 "bytes"
11 "cmp"
12 "errors"
13 "fmt"
14 "math"
15 "math/bits"
16 "reflect"
17 "strconv"
18 "strings"
19 "time"
20
21 "encoding/json/internal"
22 "encoding/json/internal/jsonflags"
23 "encoding/json/internal/jsonopts"
24 "encoding/json/internal/jsonwire"
25 "encoding/json/jsontext"
26 )
27
28 var (
29 timeDurationType = reflect.TypeFor[time.Duration]()
30 timeTimeType = reflect.TypeFor[time.Time]()
31 )
32
33 func makeTimeMarshaler(fncs *marshaler, t reflect.Type) *marshaler {
34
35
36
37
38
39
40
41
42 switch t {
43 case timeDurationType:
44 fncs.nonDefault = true
45 marshalNano := fncs.fnc
46 fncs.fnc = func(enc *jsontext.Encoder, va addressableValue, mo *jsonopts.Struct) error {
47 xe := export.Encoder(enc)
48 var m durationArshaler
49 if mo.Flags.Has(jsonflags.FormatTag) {
50 if !m.initFormat(mo.Format) {
51 return newInvalidFormatError(enc, t)
52 }
53 } else if mo.Flags.Get(jsonflags.FormatDurationAsNano) {
54 return marshalNano(enc, va, mo)
55 } else {
56
57 var workaround string
58 if mo.Flags.Get(jsonflags.FormatTagSupported) {
59 workaround = "; specify an explicit format"
60 }
61 return newMarshalErrorBefore(enc, t, errors.New("no default representation"+workaround))
62 }
63 if mo.Flags.Get(jsonflags.StringTag) && !m.isNumeric() && !mo.Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) {
64 return newMarshalErrorBefore(enc, t, errInvalidStringTag)
65 }
66
67 m.td, _ = reflect.TypeAssert[time.Duration](va.Value)
68 k := stringOrNumberKind(!m.isNumeric() || xe.Tokens.Last.NeedObjectName() || mo.Flags.Get(jsonflags.StringifyNumbers|jsonflags.StringTag))
69 if err := xe.AppendRaw(k, true, m.appendMarshal); err != nil {
70 if !isSyntacticError(err) && !export.IsIOError(err) {
71 err = newMarshalErrorBefore(enc, t, err)
72 }
73 return err
74 }
75 return nil
76 }
77 case timeTimeType:
78 fncs.nonDefault = true
79 fncs.fnc = func(enc *jsontext.Encoder, va addressableValue, mo *jsonopts.Struct) (err error) {
80 xe := export.Encoder(enc)
81 var m timeArshaler
82 if mo.Flags.Has(jsonflags.FormatTag) {
83 if !m.initFormat(mo.Format) {
84 return newInvalidFormatError(enc, t)
85 }
86 }
87 if mo.Flags.Get(jsonflags.StringTag) && !m.isNumeric() && !mo.Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) {
88 return newMarshalErrorBefore(enc, t, errInvalidStringTag)
89 }
90
91 m.tt, _ = reflect.TypeAssert[time.Time](va.Value)
92 k := stringOrNumberKind(!m.isNumeric() || xe.Tokens.Last.NeedObjectName() || mo.Flags.Get(jsonflags.StringifyNumbers|jsonflags.StringTag))
93 if err := xe.AppendRaw(k, !m.hasCustomFormat(), m.appendMarshal); err != nil {
94 if mo.Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) {
95 return internal.NewMarshalerError(va.Addr().Interface(), err, "MarshalJSON")
96 }
97 if !isSyntacticError(err) && !export.IsIOError(err) {
98 err = newMarshalErrorBefore(enc, t, err)
99 }
100 return err
101 }
102 return nil
103 }
104 }
105 return fncs
106 }
107
108 func makeTimeUnmarshaler(fncs *unmarshaler, t reflect.Type) *unmarshaler {
109
110
111
112
113
114
115
116
117 switch t {
118 case timeDurationType:
119 fncs.nonDefault = true
120 unmarshalNano := fncs.fnc
121 fncs.fnc = func(dec *jsontext.Decoder, va addressableValue, uo *jsonopts.Struct) error {
122 xd := export.Decoder(dec)
123 var u durationArshaler
124 if uo.Flags.Has(jsonflags.FormatTag) {
125 if !u.initFormat(uo.Format) {
126 return newInvalidFormatError(dec, t)
127 }
128 } else if uo.Flags.Get(jsonflags.FormatDurationAsNano) {
129 return unmarshalNano(dec, va, uo)
130 } else {
131
132 var workaround string
133 if uo.Flags.Get(jsonflags.FormatTagSupported) {
134 workaround = "; specify an explicit format"
135 }
136 return newUnmarshalErrorBeforeWithSkipping(dec, t, errors.New("no default representation"+workaround))
137 }
138 if uo.Flags.Get(jsonflags.StringTag) && !u.isNumeric() && !uo.Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) {
139 return newUnmarshalErrorBeforeWithSkipping(dec, t, errInvalidStringTag)
140 }
141
142 stringify := !u.isNumeric() || xd.Tokens.Last.NeedObjectName() || uo.Flags.Get(jsonflags.StringifyNumbers|jsonflags.StringTag)
143 var flags jsonwire.ValueFlags
144 td, _ := reflect.TypeAssert[*time.Duration](va.Addr())
145 val, err := xd.ReadValue(&flags)
146 if err != nil {
147 return err
148 }
149 switch k := val.Kind(); k {
150 case 'n':
151 if !uo.Flags.Get(jsonflags.MergeWithLegacySemantics) {
152 *td = time.Duration(0)
153 }
154 return nil
155 case '"':
156 if !stringify {
157 break
158 }
159 val = jsonwire.UnquoteMayCopy(val, flags.IsVerbatim())
160 if err := u.unmarshal(val); err != nil {
161 return newUnmarshalErrorAfter(dec, t, err)
162 }
163 *td = u.td
164 return nil
165 case '0':
166 if stringify {
167 break
168 }
169 if err := u.unmarshal(val); err != nil {
170 return newUnmarshalErrorAfter(dec, t, err)
171 }
172 *td = u.td
173 return nil
174 }
175 return newUnmarshalErrorAfter(dec, t, nil)
176 }
177 case timeTimeType:
178 fncs.nonDefault = true
179 fncs.fnc = func(dec *jsontext.Decoder, va addressableValue, uo *jsonopts.Struct) (err error) {
180 xd := export.Decoder(dec)
181 var u timeArshaler
182 if uo.Flags.Has(jsonflags.FormatTag) {
183 if !u.initFormat(uo.Format) {
184 return newInvalidFormatError(dec, t)
185 }
186 } else if uo.Flags.Get(jsonflags.ParseTimeWithLooseRFC3339) {
187 u.looseRFC3339 = true
188 }
189 if uo.Flags.Get(jsonflags.StringTag) && !u.isNumeric() && !uo.Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) {
190 return newUnmarshalErrorBeforeWithSkipping(dec, t, errInvalidStringTag)
191 }
192
193 stringify := !u.isNumeric() || xd.Tokens.Last.NeedObjectName() || uo.Flags.Get(jsonflags.StringifyNumbers|jsonflags.StringTag)
194 var flags jsonwire.ValueFlags
195 tt, _ := reflect.TypeAssert[*time.Time](va.Addr())
196 val, err := xd.ReadValue(&flags)
197 if err != nil {
198 return err
199 }
200 switch k := val.Kind(); k {
201 case 'n':
202 if !uo.Flags.Get(jsonflags.MergeWithLegacySemantics) {
203 *tt = time.Time{}
204 }
205 return nil
206 case '"':
207 if !stringify {
208 break
209 }
210 val = jsonwire.UnquoteMayCopy(val, flags.IsVerbatim())
211 if err := u.unmarshal(val); err != nil {
212 if uo.Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) {
213 return err
214 }
215 return newUnmarshalErrorAfter(dec, t, err)
216 }
217 *tt = u.tt
218 return nil
219 case '0':
220 if stringify {
221 break
222 }
223 if err := u.unmarshal(val); err != nil {
224 if uo.Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) {
225 return err
226 }
227 return newUnmarshalErrorAfter(dec, t, err)
228 }
229 *tt = u.tt
230 return nil
231 }
232 return newUnmarshalErrorAfter(dec, t, nil)
233 }
234 }
235 return fncs
236 }
237
238 type durationArshaler struct {
239 td time.Duration
240
241
242
243
244
245
246 base uint64
247 }
248
249 func (a *durationArshaler) initFormat(format string) (ok bool) {
250 switch format {
251 case "units":
252 a.base = 0
253 case "sec":
254 a.base = 1e9
255 case "milli":
256 a.base = 1e6
257 case "micro":
258 a.base = 1e3
259 case "nano":
260 a.base = 1e0
261 case "iso8601":
262 a.base = 8601
263 default:
264 return false
265 }
266 return true
267 }
268
269 func (a *durationArshaler) isNumeric() bool {
270 return a.base != 0 && a.base != 8601
271 }
272
273 func (a *durationArshaler) appendMarshal(b []byte) ([]byte, error) {
274 switch a.base {
275 case 0:
276 return append(b, a.td.String()...), nil
277 case 8601:
278 return appendDurationISO8601(b, a.td), nil
279 default:
280 return appendDurationBase10(b, a.td, a.base), nil
281 }
282 }
283
284 func (a *durationArshaler) unmarshal(b []byte) (err error) {
285 switch a.base {
286 case 0:
287 a.td, err = time.ParseDuration(string(b))
288 case 8601:
289 a.td, err = parseDurationISO8601(b)
290 default:
291 a.td, err = parseDurationBase10(b, a.base)
292 }
293 return err
294 }
295
296 type timeArshaler struct {
297 tt time.Time
298
299
300
301
302
303
304 base uint64
305 format string
306
307 looseRFC3339 bool
308 }
309
310 func (a *timeArshaler) initFormat(format string) bool {
311
312
313 if len(format) == 0 {
314 return false
315 }
316 a.base = math.MaxUint
317 if c := format[0]; !('a' <= c && c <= 'z') && !('A' <= c && c <= 'Z') {
318 a.format = format
319 return true
320 }
321 switch format {
322 case "ANSIC":
323 a.format = time.ANSIC
324 case "UnixDate":
325 a.format = time.UnixDate
326 case "RubyDate":
327 a.format = time.RubyDate
328 case "RFC822":
329 a.format = time.RFC822
330 case "RFC822Z":
331 a.format = time.RFC822Z
332 case "RFC850":
333 a.format = time.RFC850
334 case "RFC1123":
335 a.format = time.RFC1123
336 case "RFC1123Z":
337 a.format = time.RFC1123Z
338 case "RFC3339":
339 a.base = 0
340 a.format = time.RFC3339
341 case "RFC3339Nano":
342 a.base = 0
343 a.format = time.RFC3339Nano
344 case "Kitchen":
345 a.format = time.Kitchen
346 case "Stamp":
347 a.format = time.Stamp
348 case "StampMilli":
349 a.format = time.StampMilli
350 case "StampMicro":
351 a.format = time.StampMicro
352 case "StampNano":
353 a.format = time.StampNano
354 case "DateTime":
355 a.format = time.DateTime
356 case "DateOnly":
357 a.format = time.DateOnly
358 case "TimeOnly":
359 a.format = time.TimeOnly
360 case "unix":
361 a.base = 1e0
362 case "unixmilli":
363 a.base = 1e3
364 case "unixmicro":
365 a.base = 1e6
366 case "unixnano":
367 a.base = 1e9
368 default:
369
370 if strings.TrimFunc(format, isLetterOrDigit) == "" {
371 return false
372 }
373 a.format = format
374 }
375 return true
376 }
377
378 func (a *timeArshaler) isNumeric() bool {
379 return int(a.base) > 0
380 }
381
382 func (a *timeArshaler) hasCustomFormat() bool {
383 return a.base == math.MaxUint
384 }
385
386 func (a *timeArshaler) appendMarshal(b []byte) ([]byte, error) {
387 switch a.base {
388 case 0:
389 format := cmp.Or(a.format, time.RFC3339Nano)
390 n0 := len(b)
391 b = a.tt.AppendFormat(b, format)
392
393
394
395 switch b := b[n0:]; {
396 case b[len("9999")] != '-':
397 return b, errors.New("year outside of range [0,9999]")
398 case b[len(b)-1] != 'Z':
399 c := b[len(b)-len("Z07:00")]
400 if ('0' <= c && c <= '9') || parseDec2(b[len(b)-len("07:00"):]) >= 24 {
401 return b, errors.New("timezone hour outside of range [0,23]")
402 }
403 }
404 return b, nil
405 case math.MaxUint:
406 return a.tt.AppendFormat(b, a.format), nil
407 default:
408 return appendTimeUnix(b, a.tt, a.base), nil
409 }
410 }
411
412 func (a *timeArshaler) unmarshal(b []byte) (err error) {
413 switch a.base {
414 case 0:
415
416 if err := a.tt.UnmarshalText(b); err != nil {
417 return err
418 }
419
420
421
422
423
424 newParseError := func(layout, value, layoutElem, valueElem, message string) error {
425 return &time.ParseError{Layout: layout, Value: value, LayoutElem: layoutElem, ValueElem: valueElem, Message: message}
426 }
427 switch {
428 case a.looseRFC3339:
429 return nil
430 case b[len("2006-01-02T")+1] == ':':
431 return newParseError(time.RFC3339, string(b), "15", string(b[len("2006-01-02T"):][:1]), "")
432 case b[len("2006-01-02T15:04:05")] == ',':
433 return newParseError(time.RFC3339, string(b), ".", ",", "")
434 case b[len(b)-1] != 'Z':
435 switch {
436 case parseDec2(b[len(b)-len("07:00"):]) >= 24:
437 return newParseError(time.RFC3339, string(b), "Z07:00", string(b[len(b)-len("Z07:00"):]), ": timezone hour out of range")
438 case parseDec2(b[len(b)-len("00"):]) >= 60:
439 return newParseError(time.RFC3339, string(b), "Z07:00", string(b[len(b)-len("Z07:00"):]), ": timezone minute out of range")
440 }
441 }
442 return nil
443 case math.MaxUint:
444 a.tt, err = time.Parse(a.format, string(b))
445 return err
446 default:
447 a.tt, err = parseTimeUnix(b, a.base)
448 return err
449 }
450 }
451
452
453
454 func appendDurationBase10(b []byte, d time.Duration, pow10 uint64) []byte {
455 b, n := mayAppendDurationSign(b, d)
456 whole, frac := bits.Div64(0, n, uint64(pow10))
457 b = strconv.AppendUint(b, whole, 10)
458 return appendFracBase10(b, frac, pow10)
459 }
460
461
462
463 func parseDurationBase10(b []byte, pow10 uint64) (time.Duration, error) {
464 suffix, neg := consumeSign(b, false)
465 wholeBytes, fracBytes := bytesCutByte(suffix, '.', true)
466 whole, okWhole := jsonwire.ParseUint(wholeBytes)
467 frac, okFrac := parseFracBase10(fracBytes, pow10)
468 hi, lo := bits.Mul64(whole, uint64(pow10))
469 sum, co := bits.Add64(lo, uint64(frac), 0)
470 switch d := mayApplyDurationSign(sum, neg); {
471 case (!okWhole && whole != math.MaxUint64) || !okFrac:
472 return 0, fmt.Errorf("invalid duration %q: %w", b, strconv.ErrSyntax)
473 case !okWhole || hi > 0 || co > 0 || neg != (d < 0):
474 return 0, fmt.Errorf("invalid duration %q: %w", b, strconv.ErrRange)
475 default:
476 return d, nil
477 }
478 }
479
480
481
482
483
484 func appendDurationISO8601(b []byte, d time.Duration) []byte {
485 if d == 0 {
486 return append(b, "PT0S"...)
487 }
488 b, n := mayAppendDurationSign(b, d)
489 b = append(b, "PT"...)
490 n, nsec := bits.Div64(0, n, 1e9)
491 n, sec := bits.Div64(0, n, 60)
492 hour, min := bits.Div64(0, n, 60)
493 if hour > 0 {
494 b = append(strconv.AppendUint(b, hour, 10), 'H')
495 }
496 if min > 0 {
497 b = append(strconv.AppendUint(b, min, 10), 'M')
498 }
499 if sec > 0 || nsec > 0 {
500 b = append(appendFracBase10(strconv.AppendUint(b, sec, 10), nsec, 1e9), 'S')
501 }
502 return b
503 }
504
505
506
507
508
509 const daysPerYear = 365.2425
510
511 var errInaccurateDateUnits = errors.New("inaccurate year, month, week, or day units")
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556 func parseDurationISO8601(b []byte) (time.Duration, error) {
557 var invalid, overflow, inaccurate, sawFrac bool
558 var sumNanos, n, co uint64
559
560
561 cutBytes := func(b []byte, c0, c1 byte) (prefix, suffix []byte, ok bool) {
562 for i, c := range b {
563 if c == c0 || c == c1 {
564 return b[:i], b[i+1:], true
565 }
566 }
567 return b, nil, false
568 }
569
570
571
572
573 mayParseUnit := func(b []byte, desHi, desLo byte, unit time.Duration) []byte {
574 number, suffix, ok := cutBytes(b, desHi, desLo)
575 if !ok || sawFrac {
576 return b
577 }
578
579
580
581 whole, frac, ok := cutBytes(number, '.', ',')
582 if ok {
583 sawFrac = true
584 invalid = invalid || len(frac) == len("") || unit > time.Hour
585 if unit == time.Second {
586 n, ok = parsePaddedBase10(frac, uint64(time.Second))
587 invalid = invalid || !ok
588 } else {
589 f, err := strconv.ParseFloat("0."+string(frac), 64)
590 invalid = invalid || err != nil || len(bytes.Trim(frac[len("."):], "0123456789")) > 0
591 n = uint64(math.Round(f * float64(unit)))
592 }
593 sumNanos, co = bits.Add64(sumNanos, n, 0)
594 overflow = overflow || co > 0
595 }
596 for len(whole) > 1 && whole[0] == '0' {
597 whole = whole[len("0"):]
598 }
599 n, ok := jsonwire.ParseUint(whole)
600 hi, lo := bits.Mul64(n, uint64(unit))
601 sumNanos, co = bits.Add64(sumNanos, lo, 0)
602 invalid = invalid || (!ok && n != math.MaxUint64)
603 overflow = overflow || (!ok && n == math.MaxUint64) || hi > 0 || co > 0
604 inaccurate = inaccurate || unit > time.Hour
605 return suffix
606 }
607
608 suffix, neg := consumeSign(b, true)
609 prefix, suffix, okP := cutBytes(suffix, 'P', 'p')
610 durDate, durTime, okT := cutBytes(suffix, 'T', 't')
611 invalid = invalid || len(prefix) > 0 || !okP || (okT && len(durTime) == 0) || len(durDate)+len(durTime) == 0
612 if len(durDate) > 0 {
613 durDate = mayParseUnit(durDate, 'Y', 'y', time.Duration(daysPerYear*24*60*60*1e9))
614 durDate = mayParseUnit(durDate, 'M', 'm', time.Duration(daysPerYear/12*24*60*60*1e9))
615 durDate = mayParseUnit(durDate, 'W', 'w', time.Duration(7*24*60*60*1e9))
616 durDate = mayParseUnit(durDate, 'D', 'd', time.Duration(24*60*60*1e9))
617 invalid = invalid || len(durDate) > 0
618 }
619 if len(durTime) > 0 {
620 durTime = mayParseUnit(durTime, 'H', 'h', time.Duration(60*60*1e9))
621 durTime = mayParseUnit(durTime, 'M', 'm', time.Duration(60*1e9))
622 durTime = mayParseUnit(durTime, 'S', 's', time.Duration(1e9))
623 invalid = invalid || len(durTime) > 0
624 }
625 d := mayApplyDurationSign(sumNanos, neg)
626 overflow = overflow || (neg != (d < 0) && d != 0)
627
628 switch {
629 case invalid:
630 return 0, fmt.Errorf("invalid ISO 8601 duration %q: %w", b, strconv.ErrSyntax)
631 case overflow:
632 return 0, fmt.Errorf("invalid ISO 8601 duration %q: %w", b, strconv.ErrRange)
633 case inaccurate:
634 return d, fmt.Errorf("invalid ISO 8601 duration %q: %w", b, errInaccurateDateUnits)
635 default:
636 return d, nil
637 }
638 }
639
640
641 func mayAppendDurationSign(b []byte, d time.Duration) ([]byte, uint64) {
642 if d < 0 {
643 b = append(b, '-')
644 d *= -1
645 }
646 return b, uint64(d)
647 }
648
649
650 func mayApplyDurationSign(n uint64, neg bool) time.Duration {
651 if neg {
652 return -1 * time.Duration(n)
653 } else {
654 return +1 * time.Duration(n)
655 }
656 }
657
658
659
660 func appendTimeUnix(b []byte, t time.Time, pow10 uint64) []byte {
661 sec, nsec := t.Unix(), int64(t.Nanosecond())
662 if sec < 0 {
663 b = append(b, '-')
664 sec, nsec = negateSecNano(sec, nsec)
665 }
666 switch {
667 case pow10 == 1e0:
668 b = strconv.AppendUint(b, uint64(sec), 10)
669 return appendFracBase10(b, uint64(nsec), 1e9)
670 case uint64(sec) < 1e9:
671 b = strconv.AppendUint(b, uint64(sec)*uint64(pow10)+uint64(uint64(nsec)/(1e9/pow10)), 10)
672 return appendFracBase10(b, (uint64(nsec)*pow10)%1e9, 1e9)
673 default:
674 b = strconv.AppendUint(b, uint64(sec), 10)
675 b = appendPaddedBase10(b, uint64(nsec)/(1e9/pow10), pow10)
676 return appendFracBase10(b, (uint64(nsec)*pow10)%1e9, 1e9)
677 }
678 }
679
680
681
682 func parseTimeUnix(b []byte, pow10 uint64) (time.Time, error) {
683 suffix, neg := consumeSign(b, false)
684 wholeBytes, fracBytes := bytesCutByte(suffix, '.', true)
685 whole, okWhole := jsonwire.ParseUint(wholeBytes)
686 frac, okFrac := parseFracBase10(fracBytes, 1e9/pow10)
687 var sec, nsec int64
688 switch {
689 case pow10 == 1e0:
690 sec = int64(whole)
691 nsec = int64(frac)
692 case okWhole:
693 sec = int64(whole / pow10)
694 nsec = int64((whole%pow10)*(1e9/pow10) + frac)
695 case !okWhole && whole == math.MaxUint64:
696 width := int(math.Log10(float64(pow10)))
697 whole, okWhole = jsonwire.ParseUint(wholeBytes[:len(wholeBytes)-width])
698 mid, _ := parsePaddedBase10(wholeBytes[len(wholeBytes)-width:], pow10)
699 sec = int64(whole)
700 nsec = int64(mid*(1e9/pow10) + frac)
701 }
702 if neg {
703 sec, nsec = negateSecNano(sec, nsec)
704 }
705 switch t := time.Unix(sec, nsec).UTC(); {
706 case (!okWhole && whole != math.MaxUint64) || !okFrac:
707 return time.Time{}, fmt.Errorf("invalid time %q: %w", b, strconv.ErrSyntax)
708 case !okWhole || neg != (t.Unix() < 0):
709 return time.Time{}, fmt.Errorf("invalid time %q: %w", b, strconv.ErrRange)
710 default:
711 return t, nil
712 }
713 }
714
715
716 func negateSecNano(sec, nsec int64) (int64, int64) {
717 sec = ^sec
718 nsec = -nsec + 1e9
719 sec += int64(nsec / 1e9)
720 nsec %= 1e9
721 return sec, nsec
722 }
723
724
725
726 func appendFracBase10(b []byte, n, max10 uint64) []byte {
727 if n == 0 {
728 return b
729 }
730 return bytes.TrimRight(appendPaddedBase10(append(b, '.'), n, max10), "0")
731 }
732
733
734
735 func parseFracBase10(b []byte, max10 uint64) (n uint64, ok bool) {
736 switch {
737 case len(b) == 0:
738 return 0, true
739 case len(b) < len(".0") || b[0] != '.':
740 return 0, false
741 }
742 return parsePaddedBase10(b[len("."):], max10)
743 }
744
745
746
747 func appendPaddedBase10(b []byte, n, max10 uint64) []byte {
748 if n < max10/10 {
749
750
751 i := len(b)
752 b = strconv.AppendUint(b, n+max10/10, 10)
753 b[i]--
754 return b
755 }
756 return strconv.AppendUint(b, n, 10)
757 }
758
759
760
761
762
763 func parsePaddedBase10(b []byte, max10 uint64) (n uint64, ok bool) {
764 pow10 := uint64(1)
765 for pow10 < max10 {
766 n *= 10
767 if len(b) > 0 {
768 if b[0] < '0' || '9' < b[0] {
769 return n, false
770 }
771 n += uint64(b[0] - '0')
772 b = b[1:]
773 }
774 pow10 *= 10
775 }
776 if len(b) > 0 && len(bytes.TrimRight(b, "0123456789")) > 0 {
777 return n, false
778 }
779 return n, true
780 }
781
782
783 func consumeSign(b []byte, allowPlus bool) ([]byte, bool) {
784 if len(b) > 0 {
785 if b[0] == '-' {
786 return b[len("-"):], true
787 } else if b[0] == '+' && allowPlus {
788 return b[len("+"):], false
789 }
790 }
791 return b, false
792 }
793
794
795
796 func bytesCutByte(b []byte, c byte, include bool) ([]byte, []byte) {
797 if i := bytes.IndexByte(b, c); i >= 0 {
798 if include {
799 return b[:i], b[i:]
800 }
801 return b[:i], b[i+1:]
802 }
803 return b, nil
804 }
805
806
807
808 func parseDec2(b []byte) byte {
809 if len(b) < 2 {
810 return 0
811 }
812 return 10*(b[0]-'0') + (b[1] - '0')
813 }
814
View as plain text