gofront 1.8.0 → 1.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (178) hide show
  1. package/README.md +3 -3
  2. package/package.json +6 -5
  3. package/src/backend/js/expressions.js +1157 -408
  4. package/src/backend/js/index.js +327 -153
  5. package/src/backend/js/natives.js +383 -0
  6. package/src/backend/js/runtime.js +559 -2552
  7. package/src/backend/js/statements.js +670 -158
  8. package/src/backend/js/stdlib/index.js +45 -93
  9. package/src/backend/js/templ.js +32 -45
  10. package/src/backend/js/type-info.js +298 -19
  11. package/src/backend/shared/fixed-float.js +20 -0
  12. package/src/backend/shared/fmt-types.js +91 -170
  13. package/src/backend/shared/host-unicode.js +36 -0
  14. package/src/backend/wasm/boundary.js +490 -965
  15. package/src/backend/wasm/emit-builtins.js +884 -256
  16. package/src/backend/wasm/emit-exprs.js +185 -77
  17. package/src/backend/wasm/emit-maps.js +17 -367
  18. package/src/backend/wasm/emit-shared.js +4 -1
  19. package/src/backend/wasm/emit-stmts.js +524 -240
  20. package/src/backend/wasm/emit.js +176 -38
  21. package/src/backend/wasm/globals.js +48 -8
  22. package/src/backend/wasm/glue.js +2 -4
  23. package/src/backend/wasm/index.js +44 -530
  24. package/src/backend/wasm/method-expr.js +12 -1
  25. package/src/backend/wasm/module-emitter.js +299 -770
  26. package/src/backend/wasm/monomorph.js +139 -8
  27. package/src/backend/wasm/natives.js +600 -0
  28. package/src/backend/wasm/prescan.js +5 -153
  29. package/src/backend/wasm/prune-funcs.js +89 -0
  30. package/src/backend/wasm/stdlib/index.js +2 -32
  31. package/src/backend/wasm/types.js +97 -20
  32. package/src/backend/wasm/variadic.js +9 -4
  33. package/src/cli/cli-core.js +5 -1
  34. package/src/cli/test-runner.js +115 -342
  35. package/src/compiler.js +113 -130
  36. package/src/lexer/lexer.js +106 -11
  37. package/src/lexer/templ-lexer.js +61 -18
  38. package/src/lexer/tokens.js +1 -0
  39. package/src/link/stdlib.js +355 -132
  40. package/src/link/type-names.js +204 -0
  41. package/src/link/wasm.js +30 -6
  42. package/src/lower/bounds.js +153 -0
  43. package/src/lower/captures.js +92 -33
  44. package/src/lower/embedding.js +62 -5
  45. package/src/lower/errors-as.js +196 -0
  46. package/src/lower/fmt-formatters.js +929 -0
  47. package/src/lower/functions.js +225 -7
  48. package/src/lower/gen-types.js +293 -0
  49. package/src/lower/index.js +14 -2
  50. package/src/lower/json-codecs.js +334 -245
  51. package/src/lower/ownership.js +25 -7
  52. package/src/lower/reachability.js +69 -51
  53. package/src/lower/walk.js +12 -0
  54. package/src/parser/declarations.js +30 -5
  55. package/src/parser/expressions.js +111 -57
  56. package/src/parser/statements.js +108 -0
  57. package/src/parser/templ.js +44 -5
  58. package/src/parser/types.js +12 -0
  59. package/src/resolver.js +1 -2
  60. package/src/typechecker/assignability.js +214 -19
  61. package/src/typechecker/const-fold.js +70 -27
  62. package/src/typechecker/constants.js +156 -8
  63. package/src/typechecker/expressions.js +490 -108
  64. package/src/typechecker/index.js +346 -42
  65. package/src/typechecker/init-order.js +136 -40
  66. package/src/typechecker/operand-text.js +41 -0
  67. package/src/typechecker/resolve.js +490 -65
  68. package/src/typechecker/statements.js +288 -27
  69. package/src/typechecker/stdlib/excluded.js +9 -5
  70. package/src/typechecker/stdlib/index.js +25 -9
  71. package/src/typechecker/termination.js +10 -1
  72. package/src/typechecker/types.js +287 -575
  73. package/stdlib/bytes/buffer.go +479 -0
  74. package/stdlib/bytes/bytes.go +1010 -0
  75. package/stdlib/bytes/reader.go +161 -0
  76. package/stdlib/context/context.go +115 -0
  77. package/stdlib/crypto/rand/rand.go +34 -0
  78. package/stdlib/crypto/sha256/sha256.go +249 -0
  79. package/stdlib/encoding/base64/base64.go +331 -0
  80. package/stdlib/encoding/binary/binary.go +332 -0
  81. package/stdlib/encoding/hex/hex.go +140 -0
  82. package/stdlib/encoding/json/json.go +62 -0
  83. package/stdlib/errors/errors.go +174 -0
  84. package/stdlib/fmt/errors.go +110 -0
  85. package/stdlib/fmt/format.go +732 -0
  86. package/stdlib/fmt/print.go +1232 -0
  87. package/stdlib/gom/gom.go +293 -0
  88. package/stdlib/html/html.go +251 -0
  89. package/stdlib/internal/fmtrt/fmtrt.go +163 -0
  90. package/stdlib/internal/jsonrt/dec.go +171 -38
  91. package/stdlib/internal/jsonrt/enc.go +186 -36
  92. package/stdlib/internal/jsonrt/scanner.go +17 -8
  93. package/stdlib/io/io.go +157 -1
  94. package/stdlib/maps/maps.go +131 -0
  95. package/stdlib/math/bits/bits.go +507 -0
  96. package/stdlib/math/extra.go +16 -14
  97. package/stdlib/math/math.go +439 -0
  98. package/stdlib/math/rand/rand.go +297 -0
  99. package/stdlib/os/os.go +93 -0
  100. package/stdlib/path/filepath/filepath.go +47 -0
  101. package/stdlib/path/path.go +394 -0
  102. package/stdlib/regexp/regexp.go +231 -0
  103. package/stdlib/slices/slices.go +526 -0
  104. package/stdlib/slices/sort.go +209 -0
  105. package/stdlib/slices/zsort_cmp.go +480 -0
  106. package/stdlib/slices/zsort_ordered.go +482 -0
  107. package/stdlib/sort/sort.go +391 -452
  108. package/stdlib/sort/zsort_func.go +480 -0
  109. package/stdlib/sort/zsort_iface.go +480 -0
  110. package/stdlib/strconv/float.go +1699 -0
  111. package/stdlib/strconv/isprint.go +752 -0
  112. package/stdlib/strconv/quote.go +235 -50
  113. package/stdlib/strconv/strconv.go +616 -0
  114. package/stdlib/strings/builder.go +95 -0
  115. package/stdlib/strings/reader.go +163 -0
  116. package/stdlib/strings/replacer.go +102 -0
  117. package/stdlib/strings/strings.go +807 -0
  118. package/stdlib/sync/sync.go +268 -0
  119. package/stdlib/testing/async_js.go +61 -0
  120. package/stdlib/testing/benchmark.go +297 -0
  121. package/stdlib/testing/testing.go +523 -0
  122. package/stdlib/time/duration.go +233 -0
  123. package/stdlib/time/format.go +745 -0
  124. package/stdlib/time/parse.go +790 -0
  125. package/stdlib/time/sleep_js.go +63 -0
  126. package/stdlib/time/time.go +834 -0
  127. package/stdlib/unicode/unicode.go +141 -3
  128. package/stdlib/unicode/utf8/utf8.go +464 -0
  129. package/src/backend/js/fmt-runtime.js +0 -573
  130. package/src/backend/js/stdlib/base64.js +0 -12
  131. package/src/backend/js/stdlib/binary.js +0 -102
  132. package/src/backend/js/stdlib/bits.js +0 -119
  133. package/src/backend/js/stdlib/builder.js +0 -98
  134. package/src/backend/js/stdlib/bytes.js +0 -368
  135. package/src/backend/js/stdlib/errors.js +0 -45
  136. package/src/backend/js/stdlib/fmt.js +0 -517
  137. package/src/backend/js/stdlib/gom.js +0 -155
  138. package/src/backend/js/stdlib/hex.js +0 -17
  139. package/src/backend/js/stdlib/html.js +0 -20
  140. package/src/backend/js/stdlib/io.js +0 -137
  141. package/src/backend/js/stdlib/json.js +0 -1167
  142. package/src/backend/js/stdlib/maps.js +0 -37
  143. package/src/backend/js/stdlib/math.js +0 -85
  144. package/src/backend/js/stdlib/os.js +0 -13
  145. package/src/backend/js/stdlib/path.js +0 -48
  146. package/src/backend/js/stdlib/rand.js +0 -32
  147. package/src/backend/js/stdlib/regexp.js +0 -54
  148. package/src/backend/js/stdlib/sha256.js +0 -12
  149. package/src/backend/js/stdlib/shared.js +0 -17
  150. package/src/backend/js/stdlib/simd.js +0 -34
  151. package/src/backend/js/stdlib/slices.js +0 -107
  152. package/src/backend/js/stdlib/sort.js +0 -30
  153. package/src/backend/js/stdlib/strconv.js +0 -79
  154. package/src/backend/js/stdlib/strings.js +0 -301
  155. package/src/backend/js/stdlib/sync.js +0 -13
  156. package/src/backend/js/stdlib/testing.js +0 -41
  157. package/src/backend/js/stdlib/time.js +0 -152
  158. package/src/backend/js/stdlib/unicode.js +0 -25
  159. package/src/backend/js/stdlib/utf8.js +0 -26
  160. package/src/backend/wasm/stdlib/bytes.js +0 -6716
  161. package/src/backend/wasm/stdlib/core.js +0 -10278
  162. package/src/backend/wasm/stdlib/io.js +0 -3578
  163. package/src/backend/wasm/stdlib/packages.js +0 -2402
  164. package/src/backend/wasm/stdlib/pure.js +0 -720
  165. package/src/backend/wasm/stdlib/time.js +0 -2229
  166. package/src/typechecker/stdlib/core.js +0 -1079
  167. package/src/typechecker/stdlib/extended.js +0 -1210
  168. package/stdlib/bytes/iter.go +0 -192
  169. package/stdlib/bytes/search.go +0 -240
  170. package/stdlib/bytes/split.go +0 -218
  171. package/stdlib/errors/overlay.go +0 -12
  172. package/stdlib/maps/iter.go +0 -65
  173. package/stdlib/slices/extra.go +0 -73
  174. package/stdlib/slices/iter.go +0 -113
  175. package/stdlib/strings/extra.go +0 -35
  176. package/stdlib/strings/iter.go +0 -122
  177. package/stdlib/sync/extra.go +0 -105
  178. package/stdlib/unicode/utf8/bytes.go +0 -239
@@ -51,12 +51,6 @@ func (e *InvalidUnmarshalError) Error() string {
51
51
  return "json: Unmarshal(nil " + e.Type + ")"
52
52
  }
53
53
 
54
- var (
55
- OptEscapeHTML = true
56
- OptUseNumber bool
57
- OptDisallowUnknownFields bool
58
- )
59
-
60
54
  // Dec is the state of one Unmarshal call. The input has been checked to be
61
55
  // valid JSON, so the readers below do not look for syntax errors.
62
56
  type Dec struct {
@@ -64,8 +58,9 @@ type Dec struct {
64
58
  pos int
65
59
  Err error
66
60
  fatal bool
67
- structs []string
68
- fields []string
61
+ structs []string // stack of struct names, depth entries live
62
+ fields []string // stack of field names, depth entries live
63
+ depth int
69
64
  key string
70
65
  UseNumber bool
71
66
  DisallowUnknownFields bool
@@ -77,11 +72,7 @@ func NewDec(data []byte) (*Dec, error) {
77
72
  if err := CheckValid(data); err != nil {
78
73
  return nil, err
79
74
  }
80
- return &Dec{
81
- data: data,
82
- UseNumber: OptUseNumber,
83
- DisallowUnknownFields: OptDisallowUnknownFields,
84
- }, nil
75
+ return &Dec{data: data}, nil
85
76
  }
86
77
 
87
78
  // UnknownField records an error when an unknown struct field is encountered with DisallowUnknownFields.
@@ -117,15 +108,21 @@ func (d *Dec) Failed() bool {
117
108
  }
118
109
 
119
110
  // Enter and Leave track the struct field being decoded for error messages.
111
+ // The stacks keep their entries and move an index, so no slice is resliced
112
+ // or copied per struct.
120
113
  func (d *Dec) Enter(structName, field string) {
121
- d.structs = append(d.structs, structName)
122
- d.fields = append(d.fields, field)
114
+ if d.depth < len(d.fields) {
115
+ d.structs[d.depth] = structName
116
+ d.fields[d.depth] = field
117
+ } else {
118
+ d.structs = append(d.structs, structName)
119
+ d.fields = append(d.fields, field)
120
+ }
121
+ d.depth++
123
122
  }
124
123
 
125
124
  func (d *Dec) Leave() {
126
- n := len(d.fields) - 1
127
- d.structs = d.structs[:n]
128
- d.fields = d.fields[:n]
125
+ d.depth--
129
126
  }
130
127
 
131
128
  func (d *Dec) ws() {
@@ -169,9 +166,9 @@ func (d *Dec) typeErr(value, typ string) {
169
166
  return
170
167
  }
171
168
  e := &UnmarshalTypeError{Value: value, Type: typ, Offset: int64(d.pos)}
172
- if len(d.fields) > 0 {
173
- e.Struct = d.structs[len(d.structs)-1]
174
- e.Field = strings.Join(d.fields, ".")
169
+ if d.depth > 0 {
170
+ e.Struct = d.structs[d.depth-1]
171
+ e.Field = strings.Join(d.fields[:d.depth], ".")
175
172
  }
176
173
  d.Err = e
177
174
  }
@@ -239,12 +236,18 @@ func (d *Dec) Unquote(typ, kind string) *Dec {
239
236
  d.Skip()
240
237
  return nil
241
238
  }
242
- s := d.readString()
239
+ s := readStr(d)
243
240
  b := []byte(s)
244
241
  if s == "null" {
245
242
  return nil
246
243
  }
247
244
  ok := len(b) > 0 && CheckValid(b) == nil
245
+ // A number that starts like one but is not valid fails like any other
246
+ // malformed number.
247
+ if !ok && kind == "number" && len(b) > 0 && (b[0] == '-' || (b[0] >= '0' && b[0] <= '9')) {
248
+ d.typeErr("number "+s, typ)
249
+ return nil
250
+ }
248
251
  if ok {
249
252
  c := b[0]
250
253
  switch kind {
@@ -260,7 +263,7 @@ func (d *Dec) Unquote(typ, kind string) *Dec {
260
263
  d.Save(errors.New("json: invalid use of ,string struct tag, trying to unmarshal " + strconv.Quote(s) + " into " + typ))
261
264
  return nil
262
265
  }
263
- return &Dec{data: b, structs: d.structs, fields: d.fields}
266
+ return &Dec{data: b, structs: d.structs, fields: d.fields, depth: d.depth}
264
267
  }
265
268
 
266
269
  // Merge takes over the first error of a decoder made by Unquote.
@@ -364,7 +367,10 @@ func (d *Dec) Offset() int {
364
367
  return d.pos
365
368
  }
366
369
 
367
- func (d *Dec) readNumber() string {
370
+ // readNum consumes the number literal at the current position.
371
+ //
372
+ //gofront:native jsonrt.readNum
373
+ func readNum(d *Dec) string {
368
374
  start := d.pos
369
375
  for d.pos < len(d.data) {
370
376
  c := d.data[d.pos]
@@ -383,18 +389,20 @@ func (d *Dec) Number(typ string) (string, bool) {
383
389
  return "", false
384
390
  }
385
391
  if d.peek() == '"' {
392
+ start := d.pos
386
393
  s, ok := d.String(typ)
387
394
  if !ok {
388
395
  return "", false
389
396
  }
390
397
  if !isValidNumber(s) {
391
- d.Save(fmt.Errorf("json: invalid number literal, trying to unmarshal %q into Number", s))
398
+ // Go quotes the whole JSON literal and stops decoding.
399
+ d.Fatal(fmt.Errorf("json: invalid number literal, trying to unmarshal %q into Number", string(d.data[start:d.pos])))
392
400
  return "", false
393
401
  }
394
402
  return s, true
395
403
  }
396
404
  if d.kind() == "number" {
397
- return d.readNumber(), true
405
+ return readNum(d), true
398
406
  }
399
407
  d.Mismatch(typ)
400
408
  return "", false
@@ -432,7 +440,119 @@ func (d *Dec) String(typ string) (v string, ok bool) {
432
440
  d.typeErr(k, typ)
433
441
  return "", false
434
442
  }
435
- return d.readString(), true
443
+ return readStr(d), true
444
+ }
445
+
446
+ // plainInt consumes a decimal integer of at most 15 digits, with no fraction
447
+ // or exponent, that fits in bits bits (and is not negative unless signed), and
448
+ // returns it. It leaves the input alone and reports false for anything else,
449
+ // which the caller then parses and reports from its text. The value stays in
450
+ // an int, which is a plain number in JavaScript.
451
+ func (d *Dec) plainInt(bits int, signed bool) (int, bool) {
452
+ data := d.data
453
+ end := len(data)
454
+ i := d.pos
455
+ neg := false
456
+ if i < end && data[i] == '-' {
457
+ if !signed {
458
+ return 0, false
459
+ }
460
+ neg = true
461
+ i++
462
+ }
463
+ start := i
464
+ v := 0
465
+ for i < end {
466
+ c := data[i]
467
+ if c < '0' || c > '9' {
468
+ break
469
+ }
470
+ if i-start == 15 {
471
+ return 0, false
472
+ }
473
+ v = v*10 + int(c-'0')
474
+ i++
475
+ }
476
+ if i == start {
477
+ return 0, false
478
+ }
479
+ if i < end {
480
+ if c := data[i]; c == '.' || c == 'e' || c == 'E' || c == '+' || c == '-' {
481
+ return 0, false
482
+ }
483
+ }
484
+ if neg {
485
+ v = -v
486
+ }
487
+ if bits < 53 {
488
+ lim := 1 << (bits - 1)
489
+ if !signed {
490
+ lim = 1 << bits
491
+ if v >= lim {
492
+ return 0, false
493
+ }
494
+ } else if v >= lim || v < -lim {
495
+ return 0, false
496
+ }
497
+ }
498
+ d.pos = i
499
+ return v, true
500
+ }
501
+
502
+ var pow10 = [...]float64{1, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11, 1e12, 1e13, 1e14, 1e15}
503
+
504
+ // plainFloat consumes a decimal number of at most 15 digits with no exponent.
505
+ // Such a number is a mantissa that a float64 holds exactly divided by a power
506
+ // of ten that it holds exactly, so one division rounds it correctly. It
507
+ // leaves the input alone and reports false for anything else.
508
+ func (d *Dec) plainFloat() (float64, bool) {
509
+ data := d.data
510
+ end := len(data)
511
+ i := d.pos
512
+ neg := false
513
+ if i < end && data[i] == '-' {
514
+ neg = true
515
+ i++
516
+ }
517
+ start := i
518
+ digits := 0
519
+ frac := 0
520
+ v := 0
521
+ seenDot := false
522
+ for i < end {
523
+ c := data[i]
524
+ if c == '.' && !seenDot {
525
+ seenDot = true
526
+ i++
527
+ continue
528
+ }
529
+ if c < '0' || c > '9' {
530
+ break
531
+ }
532
+ if digits == 15 {
533
+ return 0, false
534
+ }
535
+ v = v*10 + int(c-'0')
536
+ digits++
537
+ if seenDot {
538
+ frac++
539
+ }
540
+ i++
541
+ }
542
+ if digits == 0 || i-start == 0 {
543
+ return 0, false
544
+ }
545
+ if i < end {
546
+ if c := data[i]; c == 'e' || c == 'E' || c == '+' || c == '-' {
547
+ return 0, false
548
+ }
549
+ }
550
+ f := float64(v) / pow10[frac]
551
+ if neg {
552
+ f = -f
553
+ }
554
+ d.pos = i
555
+ return f, true
436
556
  }
437
557
 
438
558
  // Int decodes an integer that must fit in bits bits.
@@ -440,13 +560,16 @@ func (d *Dec) Int(bits int, typ string) (v int64, ok bool) {
440
560
  if d.fatal || d.Null() {
441
561
  return 0, false
442
562
  }
443
- if d.peek() == '"' || d.peek() == '{' || d.peek() == '[' || d.peek() == 't' || d.peek() == 'f' {
563
+ if c := d.peek(); c == '"' || c == '{' || c == '[' || c == 't' || c == 'f' {
444
564
  k := d.kind()
445
565
  d.Skip()
446
566
  d.typeErr(k, typ)
447
567
  return 0, false
448
568
  }
449
- s := d.readNumber()
569
+ if n, ok := d.plainInt(bits, true); ok {
570
+ return int64(n), true
571
+ }
572
+ s := readNum(d)
450
573
  n, err := strconv.ParseInt(s, 10, bits)
451
574
  if err != nil {
452
575
  d.typeErr("number "+s, typ)
@@ -460,13 +583,16 @@ func (d *Dec) Uint(bits int, typ string) (v uint64, ok bool) {
460
583
  if d.fatal || d.Null() {
461
584
  return 0, false
462
585
  }
463
- if d.peek() == '"' || d.peek() == '{' || d.peek() == '[' || d.peek() == 't' || d.peek() == 'f' {
586
+ if c := d.peek(); c == '"' || c == '{' || c == '[' || c == 't' || c == 'f' {
464
587
  k := d.kind()
465
588
  d.Skip()
466
589
  d.typeErr(k, typ)
467
590
  return 0, false
468
591
  }
469
- s := d.readNumber()
592
+ if n, ok := d.plainInt(bits, false); ok {
593
+ return uint64(n), true
594
+ }
595
+ s := readNum(d)
470
596
  n, err := strconv.ParseUint(s, 10, bits)
471
597
  if err != nil {
472
598
  d.typeErr("number "+s, typ)
@@ -480,13 +606,18 @@ func (d *Dec) Float(bits int, typ string) (v float64, ok bool) {
480
606
  if d.fatal || d.Null() {
481
607
  return 0, false
482
608
  }
483
- if d.peek() == '"' || d.peek() == '{' || d.peek() == '[' || d.peek() == 't' || d.peek() == 'f' {
609
+ if c := d.peek(); c == '"' || c == '{' || c == '[' || c == 't' || c == 'f' {
484
610
  k := d.kind()
485
611
  d.Skip()
486
612
  d.typeErr(k, typ)
487
613
  return 0, false
488
614
  }
489
- s := d.readNumber()
615
+ if bits == 64 {
616
+ if f, ok := d.plainFloat(); ok {
617
+ return f, true
618
+ }
619
+ }
620
+ s := readNum(d)
490
621
  n, err := strconv.ParseFloat(s, bits)
491
622
  if bits == 32 {
492
623
  // Round to float32 here: the number may be out of float32 range.
@@ -530,7 +661,7 @@ func (d *Dec) NextKey() bool {
530
661
  d.pos++
531
662
  }
532
663
  d.ws()
533
- d.key = d.readString()
664
+ d.key = readStr(d)
534
665
  d.ws()
535
666
  d.pos++ // the colon
536
667
  return true
@@ -577,8 +708,10 @@ func EqualFold(a, b string) bool {
577
708
  return strings.EqualFold(a, b)
578
709
  }
579
710
 
580
- // readString consumes the string at the current position.
581
- func (d *Dec) readString() string {
711
+ // readStr consumes the string at the current position.
712
+ //
713
+ //gofront:native jsonrt.readStr
714
+ func readStr(d *Dec) string {
582
715
  d.pos++ // opening quote
583
716
  start := d.pos
584
717
  plain := true
@@ -714,7 +847,7 @@ func (d *Dec) Any() any {
714
847
  }
715
848
  return a
716
849
  case '"':
717
- return d.readString()
850
+ return readStr(d)
718
851
  case 't':
719
852
  d.pos += 4
720
853
  return true
@@ -725,7 +858,7 @@ func (d *Dec) Any() any {
725
858
  d.pos += 4
726
859
  return nil
727
860
  }
728
- s := d.readNumber()
861
+ s := readNum(d)
729
862
  if d.UseNumber {
730
863
  return Number(s)
731
864
  }
@@ -56,15 +56,19 @@ func (e *MarshalerError) Unwrap() error { return e.Err }
56
56
 
57
57
  // Enc is the state of one Marshal call: the output so far and the first
58
58
  // error. After an error every method does nothing.
59
+ //
60
+ // The output is buf[:n]; the rest of buf is room to grow into, so appending
61
+ // a byte is an index write rather than a new slice header.
59
62
  type Enc struct {
60
- B []byte
63
+ buf []byte
64
+ n int
61
65
  Err error
62
66
  EscapeHTML bool
63
67
  }
64
68
 
65
69
  // NewEnc returns an encoder that escapes <, > and & like json.Marshal.
66
70
  func NewEnc() *Enc {
67
- return &Enc{EscapeHTML: OptEscapeHTML}
71
+ return &Enc{buf: make([]byte, 256), EscapeHTML: true}
68
72
  }
69
73
 
70
74
  // Result returns the encoded bytes, or the first error.
@@ -72,7 +76,114 @@ func (e *Enc) Result() ([]byte, error) {
72
76
  if e.Err != nil {
73
77
  return nil, e.Err
74
78
  }
75
- return e.B, nil
79
+ return e.buf[:e.n], nil
80
+ }
81
+
82
+ // room makes sure k more bytes fit after the output.
83
+ func (e *Enc) room(k int) {
84
+ if e.n+k <= len(e.buf) {
85
+ return
86
+ }
87
+ nb := make([]byte, 2*len(e.buf)+k)
88
+ copy(nb, e.buf[:e.n])
89
+ e.buf = nb
90
+ }
91
+
92
+ // putBytes appends b to the output.
93
+ func (e *Enc) putBytes(b []byte) {
94
+ e.room(len(b))
95
+ copy(e.buf[e.n:], b)
96
+ e.n += len(b)
97
+ }
98
+
99
+ // putString appends the bytes of s to the output.
100
+ //
101
+ //gofront:native jsonrt.putString
102
+ func putString(e *Enc, s string) {
103
+ n := len(s)
104
+ e.room(n)
105
+ for i := 0; i < n; i++ {
106
+ e.buf[e.n+i] = s[i]
107
+ }
108
+ e.n += n
109
+ }
110
+
111
+ // putInt appends the decimal form of v.
112
+ //
113
+ //gofront:native jsonrt.putInt
114
+ func putInt(e *Enc, v int64) {
115
+ if v < 0 {
116
+ e.Byte('-')
117
+ putUint(e, uint64(-v))
118
+ return
119
+ }
120
+ putUint(e, uint64(v))
121
+ }
122
+
123
+ // putUint appends the decimal form of v.
124
+ //
125
+ //gofront:native jsonrt.putUint
126
+ func putUint(e *Enc, v uint64) {
127
+ n := 1
128
+ for t := v; t >= 10; t /= 10 {
129
+ n++
130
+ }
131
+ e.room(n)
132
+ for i := e.n + n - 1; i >= e.n; i-- {
133
+ e.buf[i] = byte('0' + v%10)
134
+ v /= 10
135
+ }
136
+ e.n += n
137
+ }
138
+
139
+ // putFloat appends a finite f in the format encoding/json uses: shortest
140
+ // digits that read back as a float of the given size, as if by ES6 number to
141
+ // string conversion (an exponent only below 1e-6 and from 1e21).
142
+ //
143
+ //gofront:native jsonrt.putFloat
144
+ func putFloat(e *Enc, f float64, bits int) {
145
+ var tmp [64]byte
146
+ b := tmp[:0]
147
+ format := floatFormat(f, bits)
148
+ if bits == 32 {
149
+ f = shortest32(f)
150
+ }
151
+ b = strconv.AppendFloat(b, f, format, -1, 64)
152
+ if format == 'e' {
153
+ b = cleanExponent(b)
154
+ }
155
+ e.putBytes(b)
156
+ }
157
+
158
+ // LooksFloat32 reports whether f, found in an interface, is more likely a
159
+ // float32 than a float64. Where a float32 and a float64 share one run-time
160
+ // representation (JavaScript numbers) a float64 that a float32 holds exactly
161
+ // is taken for one; elsewhere the interface tells them apart.
162
+ //
163
+ //gofront:native jsonrt.LooksFloat32
164
+ func LooksFloat32(f float64) bool {
165
+ return false
166
+ }
167
+
168
+ // putQuoted appends s as a quoted JSON string.
169
+ //
170
+ //gofront:native jsonrt.putQuoted
171
+ func putQuoted(e *Enc, s string) {
172
+ n := len(s)
173
+ for i := 0; i < n; i++ {
174
+ c := s[i]
175
+ if c < 0x20 || c >= utf8.RuneSelf || c == '"' || c == '\\' || (e.EscapeHTML && (c == '<' || c == '>' || c == '&')) {
176
+ e.putBytes(AppendString(nil, []byte(s), e.EscapeHTML))
177
+ return
178
+ }
179
+ }
180
+ e.room(n + 2)
181
+ e.buf[e.n] = '"'
182
+ for i := 0; i < n; i++ {
183
+ e.buf[e.n+1+i] = s[i]
184
+ }
185
+ e.buf[e.n+n+1] = '"'
186
+ e.n += n + 2
76
187
  }
77
188
 
78
189
  // Fail records the first error.
@@ -87,19 +198,51 @@ func (e *Enc) FailType(typ string) {
87
198
  e.Fail(&UnsupportedTypeError{typ})
88
199
  }
89
200
 
201
+ // encoders are the dynamic encoders every package with generated codecs
202
+ // registers at start-up: each encodes the values of its own package's types
203
+ // and reports whether it knew the value.
204
+ type encoderList struct {
205
+ fns []func(*Enc, any) bool
206
+ }
207
+
208
+ var encoders = &encoderList{}
209
+
210
+ // RegisterEncoder adds a package's dynamic encoder. Each target fills its own
211
+ // list from the package initializers it runs, so the list is built through a local.
212
+ func RegisterEncoder(f func(*Enc, any) bool) bool {
213
+ list := encoders
214
+ list.fns = append(list.fns, f)
215
+ return true
216
+ }
217
+
218
+ // EncodeDynamic encodes v with the first registered encoder that knows its
219
+ // type, and fails the encoding when none does.
220
+ func (e *Enc) EncodeDynamic(v any) {
221
+ for _, f := range encoders.fns {
222
+ if f(e, v) {
223
+ return
224
+ }
225
+ }
226
+ e.FailType("unsupported dynamic type")
227
+ }
228
+
90
229
  // Lit appends s, which is already valid JSON text.
91
230
  func (e *Enc) Lit(s string) {
92
- e.B = appendStr(e.B, s)
231
+ putString(e, s)
93
232
  }
94
233
 
95
234
  // Byte appends one byte of JSON punctuation.
96
235
  func (e *Enc) Byte(c byte) {
97
- e.B = append(e.B, c)
236
+ if e.n == len(e.buf) {
237
+ e.room(1)
238
+ }
239
+ e.buf[e.n] = c
240
+ e.n++
98
241
  }
99
242
 
100
243
  // Null appends null.
101
244
  func (e *Enc) Null() {
102
- e.B = appendStr(e.B, "null")
245
+ putString(e, "null")
103
246
  }
104
247
 
105
248
  // Number appends the literal representation of a Number.
@@ -108,14 +251,14 @@ func (e *Enc) Number(v string) {
108
251
  return
109
252
  }
110
253
  if v == "" {
111
- e.B = append(e.B, '0')
254
+ e.Byte('0')
112
255
  return
113
256
  }
114
257
  if !isValidNumber(v) {
115
258
  e.Fail(fmt.Errorf("json: invalid number literal %q", v))
116
259
  return
117
260
  }
118
- e.B = appendStr(e.B, v)
261
+ putString(e, v)
119
262
  }
120
263
 
121
264
  func isValidNumber(s string) bool {
@@ -132,20 +275,20 @@ func isValidNumber(s string) bool {
132
275
  // Bool appends true or false.
133
276
  func (e *Enc) Bool(v bool) {
134
277
  if v {
135
- e.B = appendStr(e.B, "true")
278
+ putString(e, "true")
136
279
  } else {
137
- e.B = appendStr(e.B, "false")
280
+ putString(e, "false")
138
281
  }
139
282
  }
140
283
 
141
284
  // Int appends a signed integer.
142
285
  func (e *Enc) Int(v int64) {
143
- e.B = strconv.AppendInt(e.B, v, 10)
286
+ putInt(e, v)
144
287
  }
145
288
 
146
289
  // Uint appends an unsigned integer.
147
290
  func (e *Enc) Uint(v uint64) {
148
- e.B = strconv.AppendUint(e.B, v, 10)
291
+ putUint(e, v)
149
292
  }
150
293
 
151
294
  // Float appends a floating-point number in the format encoding/json uses.
@@ -158,21 +301,14 @@ func (e *Enc) Float(f float64, bits int) {
158
301
  return
159
302
  }
160
303
  if f == 0 && math.Signbit(f) {
161
- e.B = append(e.B, "-0"...)
304
+ putString(e, "-0")
162
305
  return
163
306
  }
164
- format := floatFormat(f, bits)
165
- if bits == 32 {
166
- f = shortest32(f)
167
- }
168
- e.B = strconv.AppendFloat(e.B, f, format, -1, 64)
169
- if format == 'e' {
170
- e.B = cleanExponent(e.B)
171
- }
307
+ putFloat(e, f, bits)
172
308
  }
173
309
 
174
310
  // floatFormat picks 'e' or 'f' as if by ES6 number to string conversion.
175
- func floatFormat(f float64, bits int) int {
311
+ func floatFormat(f float64, bits int) byte {
176
312
  abs := math.Abs(f)
177
313
  if abs == 0 {
178
314
  return 'f'
@@ -210,34 +346,35 @@ func shortest32(f float64) float64 {
210
346
 
211
347
  // Str appends a quoted, escaped string.
212
348
  func (e *Enc) Str(s string) {
213
- e.B = AppendString(e.B, []byte(s), e.EscapeHTML)
349
+ putQuoted(e, s)
214
350
  }
215
351
 
216
352
  // QStr appends a string for the ,string tag option: the quoted form of the
217
353
  // quoted form.
218
354
  func (e *Enc) QStr(s string) {
219
355
  inner := AppendString(nil, []byte(s), e.EscapeHTML)
220
- e.B = AppendString(e.B, inner, false)
356
+ e.putBytes(AppendString(nil, inner, false))
221
357
  }
222
358
 
223
359
  // Key appends the separator for member n of an object (a comma when n > 0)
224
360
  // and the member name, a plain name that needs no escaping.
225
361
  func (e *Enc) Key(n int, name string) {
226
362
  if n > 0 {
227
- e.B = append(e.B, ',')
363
+ e.Byte(',')
228
364
  }
229
- e.B = append(e.B, '"')
230
- e.B = appendStr(e.B, name)
231
- e.B = append(e.B, '"', ':')
365
+ e.Byte('"')
366
+ putString(e, name)
367
+ e.Byte('"')
368
+ e.Byte(':')
232
369
  }
233
370
 
234
371
  // KeyStr is Key for a member name that may need escaping.
235
372
  func (e *Enc) KeyStr(n int, name string) {
236
373
  if n > 0 {
237
- e.B = append(e.B, ',')
374
+ e.Byte(',')
238
375
  }
239
- e.B = AppendString(e.B, []byte(name), e.EscapeHTML)
240
- e.B = append(e.B, ':')
376
+ putQuoted(e, name)
377
+ e.Byte(':')
241
378
  }
242
379
 
243
380
  // Bytes appends a []byte as a base64 string, or null for a nil slice.
@@ -246,9 +383,9 @@ func (e *Enc) Bytes(v []byte) {
246
383
  e.Null()
247
384
  return
248
385
  }
249
- e.B = append(e.B, '"')
250
- e.B = appendStr(e.B, base64.StdEncoding.EncodeToString(v))
251
- e.B = append(e.B, '"')
386
+ e.Byte('"')
387
+ putString(e, base64.StdEncoding.EncodeToString(v))
388
+ e.Byte('"')
252
389
  }
253
390
 
254
391
  // Raw appends a json.RawMessage: validated and compacted, null when empty.
@@ -268,15 +405,28 @@ func (e *Enc) Marshaled(out []byte, err error, typ string) {
268
405
  }
269
406
  if err == nil {
270
407
  var b []byte
271
- b, err = AppendCompact(e.B, out, e.EscapeHTML)
408
+ b, err = AppendCompact(nil, out, e.EscapeHTML)
272
409
  if err == nil {
273
- e.B = b
410
+ e.putBytes(b)
274
411
  return
275
412
  }
276
413
  }
277
414
  e.Fail(&MarshalerError{typ, err, "MarshalJSON"})
278
415
  }
279
416
 
417
+ // TextMarshaled appends the result of a MarshalText call as a JSON string, or
418
+ // the call's error wrapped like Go does.
419
+ func (e *Enc) TextMarshaled(out []byte, err error, typ string) {
420
+ if e.Err != nil {
421
+ return
422
+ }
423
+ if err != nil {
424
+ e.Fail(&MarshalerError{typ, err, "MarshalText"})
425
+ return
426
+ }
427
+ e.Str(string(out))
428
+ }
429
+
280
430
  // SortStrings sorts map keys.
281
431
  func SortStrings(a []string) {
282
432
  sort.Strings(a)