gofront 1.8.0 → 1.9.0

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 (177) hide show
  1. package/README.md +3 -3
  2. package/package.json +5 -4
  3. package/src/backend/js/expressions.js +849 -371
  4. package/src/backend/js/index.js +241 -147
  5. package/src/backend/js/natives.js +383 -0
  6. package/src/backend/js/runtime.js +512 -2547
  7. package/src/backend/js/statements.js +506 -103
  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 +177 -13
  11. package/src/backend/shared/fixed-float.js +20 -0
  12. package/src/backend/shared/fmt-types.js +63 -171
  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 +643 -256
  16. package/src/backend/wasm/emit-exprs.js +124 -57
  17. package/src/backend/wasm/emit-maps.js +17 -367
  18. package/src/backend/wasm/emit-stmts.js +325 -75
  19. package/src/backend/wasm/emit.js +104 -13
  20. package/src/backend/wasm/globals.js +47 -8
  21. package/src/backend/wasm/glue.js +2 -4
  22. package/src/backend/wasm/index.js +36 -530
  23. package/src/backend/wasm/method-expr.js +12 -1
  24. package/src/backend/wasm/module-emitter.js +92 -717
  25. package/src/backend/wasm/monomorph.js +103 -6
  26. package/src/backend/wasm/natives.js +596 -0
  27. package/src/backend/wasm/prescan.js +5 -153
  28. package/src/backend/wasm/prune-funcs.js +89 -0
  29. package/src/backend/wasm/stdlib/index.js +2 -32
  30. package/src/backend/wasm/types.js +68 -20
  31. package/src/backend/wasm/variadic.js +3 -3
  32. package/src/cli/cli-core.js +5 -1
  33. package/src/cli/test-runner.js +115 -342
  34. package/src/compiler.js +108 -130
  35. package/src/lexer/lexer.js +105 -11
  36. package/src/lexer/templ-lexer.js +61 -18
  37. package/src/lexer/tokens.js +1 -0
  38. package/src/link/stdlib.js +354 -132
  39. package/src/link/type-names.js +148 -0
  40. package/src/link/wasm.js +30 -6
  41. package/src/lower/bounds.js +153 -0
  42. package/src/lower/captures.js +92 -33
  43. package/src/lower/embedding.js +32 -3
  44. package/src/lower/errors-as.js +196 -0
  45. package/src/lower/fmt-formatters.js +905 -0
  46. package/src/lower/functions.js +2 -0
  47. package/src/lower/gen-types.js +274 -0
  48. package/src/lower/index.js +7 -1
  49. package/src/lower/json-codecs.js +228 -247
  50. package/src/lower/ownership.js +25 -7
  51. package/src/lower/reachability.js +69 -51
  52. package/src/lower/walk.js +12 -0
  53. package/src/parser/declarations.js +28 -5
  54. package/src/parser/expressions.js +86 -16
  55. package/src/parser/statements.js +1 -0
  56. package/src/parser/templ.js +44 -5
  57. package/src/parser/types.js +1 -0
  58. package/src/resolver.js +1 -2
  59. package/src/typechecker/assignability.js +124 -11
  60. package/src/typechecker/const-fold.js +70 -27
  61. package/src/typechecker/constants.js +32 -6
  62. package/src/typechecker/expressions.js +326 -98
  63. package/src/typechecker/index.js +208 -33
  64. package/src/typechecker/init-order.js +136 -40
  65. package/src/typechecker/operand-text.js +40 -0
  66. package/src/typechecker/resolve.js +192 -36
  67. package/src/typechecker/statements.js +122 -11
  68. package/src/typechecker/stdlib/excluded.js +9 -5
  69. package/src/typechecker/stdlib/index.js +25 -9
  70. package/src/typechecker/termination.js +10 -1
  71. package/src/typechecker/types.js +203 -573
  72. package/stdlib/bytes/buffer.go +479 -0
  73. package/stdlib/bytes/bytes.go +1010 -0
  74. package/stdlib/bytes/reader.go +161 -0
  75. package/stdlib/context/context.go +115 -0
  76. package/stdlib/crypto/rand/rand.go +34 -0
  77. package/stdlib/crypto/sha256/sha256.go +249 -0
  78. package/stdlib/encoding/base64/base64.go +331 -0
  79. package/stdlib/encoding/binary/binary.go +332 -0
  80. package/stdlib/encoding/hex/hex.go +140 -0
  81. package/stdlib/encoding/json/json.go +62 -0
  82. package/stdlib/errors/errors.go +174 -0
  83. package/stdlib/fmt/errors.go +110 -0
  84. package/stdlib/fmt/format.go +732 -0
  85. package/stdlib/fmt/print.go +1232 -0
  86. package/stdlib/gom/gom.go +293 -0
  87. package/stdlib/html/html.go +251 -0
  88. package/stdlib/internal/fmtrt/fmtrt.go +163 -0
  89. package/stdlib/internal/jsonrt/dec.go +171 -38
  90. package/stdlib/internal/jsonrt/enc.go +186 -36
  91. package/stdlib/internal/jsonrt/scanner.go +17 -8
  92. package/stdlib/io/io.go +157 -1
  93. package/stdlib/maps/maps.go +131 -0
  94. package/stdlib/math/bits/bits.go +507 -0
  95. package/stdlib/math/extra.go +16 -14
  96. package/stdlib/math/math.go +439 -0
  97. package/stdlib/math/rand/rand.go +297 -0
  98. package/stdlib/os/os.go +93 -0
  99. package/stdlib/path/filepath/filepath.go +47 -0
  100. package/stdlib/path/path.go +394 -0
  101. package/stdlib/regexp/regexp.go +231 -0
  102. package/stdlib/slices/slices.go +526 -0
  103. package/stdlib/slices/sort.go +209 -0
  104. package/stdlib/slices/zsort_cmp.go +480 -0
  105. package/stdlib/slices/zsort_ordered.go +482 -0
  106. package/stdlib/sort/sort.go +391 -452
  107. package/stdlib/sort/zsort_func.go +480 -0
  108. package/stdlib/sort/zsort_iface.go +480 -0
  109. package/stdlib/strconv/float.go +1699 -0
  110. package/stdlib/strconv/isprint.go +752 -0
  111. package/stdlib/strconv/quote.go +235 -50
  112. package/stdlib/strconv/strconv.go +616 -0
  113. package/stdlib/strings/builder.go +95 -0
  114. package/stdlib/strings/reader.go +163 -0
  115. package/stdlib/strings/replacer.go +102 -0
  116. package/stdlib/strings/strings.go +807 -0
  117. package/stdlib/sync/sync.go +268 -0
  118. package/stdlib/testing/async_js.go +61 -0
  119. package/stdlib/testing/benchmark.go +297 -0
  120. package/stdlib/testing/testing.go +523 -0
  121. package/stdlib/time/duration.go +233 -0
  122. package/stdlib/time/format.go +745 -0
  123. package/stdlib/time/parse.go +790 -0
  124. package/stdlib/time/sleep_js.go +63 -0
  125. package/stdlib/time/time.go +834 -0
  126. package/stdlib/unicode/unicode.go +141 -3
  127. package/stdlib/unicode/utf8/utf8.go +464 -0
  128. package/src/backend/js/fmt-runtime.js +0 -573
  129. package/src/backend/js/stdlib/base64.js +0 -12
  130. package/src/backend/js/stdlib/binary.js +0 -102
  131. package/src/backend/js/stdlib/bits.js +0 -119
  132. package/src/backend/js/stdlib/builder.js +0 -98
  133. package/src/backend/js/stdlib/bytes.js +0 -368
  134. package/src/backend/js/stdlib/errors.js +0 -45
  135. package/src/backend/js/stdlib/fmt.js +0 -517
  136. package/src/backend/js/stdlib/gom.js +0 -155
  137. package/src/backend/js/stdlib/hex.js +0 -17
  138. package/src/backend/js/stdlib/html.js +0 -20
  139. package/src/backend/js/stdlib/io.js +0 -137
  140. package/src/backend/js/stdlib/json.js +0 -1167
  141. package/src/backend/js/stdlib/maps.js +0 -37
  142. package/src/backend/js/stdlib/math.js +0 -85
  143. package/src/backend/js/stdlib/os.js +0 -13
  144. package/src/backend/js/stdlib/path.js +0 -48
  145. package/src/backend/js/stdlib/rand.js +0 -32
  146. package/src/backend/js/stdlib/regexp.js +0 -54
  147. package/src/backend/js/stdlib/sha256.js +0 -12
  148. package/src/backend/js/stdlib/shared.js +0 -17
  149. package/src/backend/js/stdlib/simd.js +0 -34
  150. package/src/backend/js/stdlib/slices.js +0 -107
  151. package/src/backend/js/stdlib/sort.js +0 -30
  152. package/src/backend/js/stdlib/strconv.js +0 -79
  153. package/src/backend/js/stdlib/strings.js +0 -301
  154. package/src/backend/js/stdlib/sync.js +0 -13
  155. package/src/backend/js/stdlib/testing.js +0 -41
  156. package/src/backend/js/stdlib/time.js +0 -152
  157. package/src/backend/js/stdlib/unicode.js +0 -25
  158. package/src/backend/js/stdlib/utf8.js +0 -26
  159. package/src/backend/wasm/stdlib/bytes.js +0 -6716
  160. package/src/backend/wasm/stdlib/core.js +0 -10278
  161. package/src/backend/wasm/stdlib/io.js +0 -3578
  162. package/src/backend/wasm/stdlib/packages.js +0 -2402
  163. package/src/backend/wasm/stdlib/pure.js +0 -720
  164. package/src/backend/wasm/stdlib/time.js +0 -2229
  165. package/src/typechecker/stdlib/core.js +0 -1079
  166. package/src/typechecker/stdlib/extended.js +0 -1210
  167. package/stdlib/bytes/iter.go +0 -192
  168. package/stdlib/bytes/search.go +0 -240
  169. package/stdlib/bytes/split.go +0 -218
  170. package/stdlib/errors/overlay.go +0 -12
  171. package/stdlib/maps/iter.go +0 -65
  172. package/stdlib/slices/extra.go +0 -73
  173. package/stdlib/slices/iter.go +0 -113
  174. package/stdlib/strings/extra.go +0 -35
  175. package/stdlib/strings/iter.go +0 -122
  176. package/stdlib/sync/extra.go +0 -105
  177. package/stdlib/unicode/utf8/bytes.go +0 -239
@@ -0,0 +1,268 @@
1
+ //go:build gofront
2
+
3
+ //gofront:target both
4
+
5
+ // Package sync provides the basic synchronization primitives for a single
6
+ // thread of execution.
7
+ //
8
+ // GoFront has no goroutines, so nothing here ever blocks: a lock that would
9
+ // have to wait for another goroutine can never be released, and reports the
10
+ // deadlock Go's runtime would. Like Go, a Mutex, RWMutex, Once or WaitGroup
11
+ // must not be copied after first use; a copy is an independent value that
12
+ // starts in the state of the original (this is not diagnosed).
13
+ package sync
14
+
15
+ // deadlock is the message of Go's runtime when every goroutine is blocked.
16
+ const deadlock = "fatal error: all goroutines are asleep - deadlock!"
17
+
18
+ // A Locker represents an object that can be locked and unlocked.
19
+ type Locker interface {
20
+ Lock()
21
+ Unlock()
22
+ }
23
+
24
+ // A Mutex is a mutual exclusion lock. The zero value is an unlocked mutex.
25
+ type Mutex struct {
26
+ locked bool
27
+ }
28
+
29
+ // Lock locks m. Locking a mutex that is already locked can never succeed
30
+ // without another goroutine, so it reports a deadlock.
31
+ func (m *Mutex) Lock() {
32
+ if m.locked {
33
+ panic(deadlock)
34
+ }
35
+ m.locked = true
36
+ }
37
+
38
+ // TryLock tries to lock m and reports whether it succeeded.
39
+ func (m *Mutex) TryLock() bool {
40
+ if m.locked {
41
+ return false
42
+ }
43
+ m.locked = true
44
+ return true
45
+ }
46
+
47
+ // Unlock unlocks m. It is a run-time error if m is not locked on entry.
48
+ func (m *Mutex) Unlock() {
49
+ if !m.locked {
50
+ panic("sync: unlock of unlocked mutex")
51
+ }
52
+ m.locked = false
53
+ }
54
+
55
+ // A RWMutex is a reader/writer mutual exclusion lock. The zero value is an
56
+ // unlocked mutex.
57
+ type RWMutex struct {
58
+ writer bool
59
+ readers int
60
+ }
61
+
62
+ // Lock locks rw for writing. It reports a deadlock when rw is already locked
63
+ // for reading or writing.
64
+ func (rw *RWMutex) Lock() {
65
+ if rw.writer || rw.readers > 0 {
66
+ panic(deadlock)
67
+ }
68
+ rw.writer = true
69
+ }
70
+
71
+ // TryLock tries to lock rw for writing and reports whether it succeeded.
72
+ func (rw *RWMutex) TryLock() bool {
73
+ if rw.writer || rw.readers > 0 {
74
+ return false
75
+ }
76
+ rw.writer = true
77
+ return true
78
+ }
79
+
80
+ // Unlock unlocks rw for writing. It is a run-time error if rw is not locked
81
+ // for writing on entry.
82
+ func (rw *RWMutex) Unlock() {
83
+ if !rw.writer {
84
+ panic("sync: Unlock of unlocked RWMutex")
85
+ }
86
+ rw.writer = false
87
+ }
88
+
89
+ // RLock locks rw for reading. It reports a deadlock when rw is locked for
90
+ // writing.
91
+ func (rw *RWMutex) RLock() {
92
+ if rw.writer {
93
+ panic(deadlock)
94
+ }
95
+ rw.readers++
96
+ }
97
+
98
+ // TryRLock tries to lock rw for reading and reports whether it succeeded.
99
+ func (rw *RWMutex) TryRLock() bool {
100
+ if rw.writer {
101
+ return false
102
+ }
103
+ rw.readers++
104
+ return true
105
+ }
106
+
107
+ // RUnlock undoes a single RLock call. It is a run-time error if rw is not
108
+ // locked for reading on entry.
109
+ func (rw *RWMutex) RUnlock() {
110
+ if rw.readers <= 0 {
111
+ panic("sync: RUnlock of unlocked RWMutex")
112
+ }
113
+ rw.readers--
114
+ }
115
+
116
+ type rlocker struct {
117
+ rw *RWMutex
118
+ }
119
+
120
+ func (r *rlocker) Lock() { r.rw.RLock() }
121
+
122
+ func (r *rlocker) Unlock() { r.rw.RUnlock() }
123
+
124
+ // RLocker returns a Locker interface that implements the Lock and Unlock
125
+ // methods by calling rw.RLock and rw.RUnlock.
126
+ func (rw *RWMutex) RLocker() Locker {
127
+ return &rlocker{rw: rw}
128
+ }
129
+
130
+ // Once is an object that will perform exactly one action.
131
+ type Once struct {
132
+ done bool
133
+ m Mutex
134
+ }
135
+
136
+ // Do calls the function f if and only if Do is being called for the first
137
+ // time for this instance of Once. If f panics, Do considers it to have
138
+ // returned; future calls of Do return without calling f. Calling Do from
139
+ // within f reports a deadlock, as in Go.
140
+ func (o *Once) Do(f func()) {
141
+ if !o.done {
142
+ o.doSlow(f)
143
+ }
144
+ }
145
+
146
+ func (o *Once) doSlow(f func()) {
147
+ o.m.Lock()
148
+ defer o.m.Unlock()
149
+ if !o.done {
150
+ defer func() { o.done = true }()
151
+ f()
152
+ }
153
+ }
154
+
155
+ // A WaitGroup waits for a collection of goroutines to finish. Without
156
+ // goroutines the counter only moves with Add and Done.
157
+ type WaitGroup struct {
158
+ n int
159
+ }
160
+
161
+ // Add adds delta, which may be negative, to the WaitGroup counter.
162
+ // If the counter becomes negative, Add panics.
163
+ func (wg *WaitGroup) Add(delta int) {
164
+ n := wg.n + delta
165
+ if n < 0 {
166
+ panic("sync: negative WaitGroup counter")
167
+ }
168
+ wg.n = n
169
+ }
170
+
171
+ // Done decrements the WaitGroup counter by one.
172
+ func (wg *WaitGroup) Done() {
173
+ wg.Add(-1)
174
+ }
175
+
176
+ // Wait returns when the counter is zero. With a positive counter nothing
177
+ // could ever call Done, so it reports a deadlock.
178
+ func (wg *WaitGroup) Wait() {
179
+ if wg.n > 0 {
180
+ panic(deadlock)
181
+ }
182
+ }
183
+
184
+ // OnceFunc returns a function that invokes f only once. The returned function
185
+ // may be called concurrently.
186
+ //
187
+ // If f panics, the returned function will panic with the same value on every call.
188
+ func OnceFunc(f func()) func() {
189
+ var (
190
+ once Once
191
+ valid bool
192
+ p any
193
+ )
194
+ return func() {
195
+ once.Do(func() {
196
+ defer func() {
197
+ p = recover()
198
+ if !valid {
199
+ panic(p)
200
+ }
201
+ }()
202
+ f()
203
+ valid = true
204
+ })
205
+ if !valid {
206
+ panic(p)
207
+ }
208
+ }
209
+ }
210
+
211
+ // OnceValue returns a function that invokes f only once and returns the value
212
+ // returned by f. The returned function may be called concurrently.
213
+ //
214
+ // If f panics, the returned function will panic with the same value on every call.
215
+ func OnceValue[T any](f func() T) func() T {
216
+ var (
217
+ once Once
218
+ valid bool
219
+ p any
220
+ result T
221
+ )
222
+ return func() T {
223
+ once.Do(func() {
224
+ defer func() {
225
+ p = recover()
226
+ if !valid {
227
+ panic(p)
228
+ }
229
+ }()
230
+ result = f()
231
+ valid = true
232
+ })
233
+ if !valid {
234
+ panic(p)
235
+ }
236
+ return result
237
+ }
238
+ }
239
+
240
+ // OnceValues returns a function that invokes f only once and returns the values
241
+ // returned by f. The returned function may be called concurrently.
242
+ //
243
+ // If f panics, the returned function will panic with the same value on every call.
244
+ func OnceValues[T1, T2 any](f func() (T1, T2)) func() (T1, T2) {
245
+ var (
246
+ once Once
247
+ valid bool
248
+ p any
249
+ r1 T1
250
+ r2 T2
251
+ )
252
+ return func() (T1, T2) {
253
+ once.Do(func() {
254
+ defer func() {
255
+ p = recover()
256
+ if !valid {
257
+ panic(p)
258
+ }
259
+ }()
260
+ r1, r2 = f()
261
+ valid = true
262
+ })
263
+ if !valid {
264
+ panic(p)
265
+ }
266
+ return r1, r2
267
+ }
268
+ }
@@ -0,0 +1,61 @@
1
+ //go:build gofront
2
+
3
+ //gofront:target js
4
+
5
+ package testing
6
+
7
+ // Async tests and subtests, for js packages. The body of an `async func`
8
+ // test returns a promise that the call through testing_call keeps; the
9
+ // driver awaits it before it reports the test.
10
+
11
+ // call calls f(t) and returns what the function returned: a promise when f
12
+ // is async, nil otherwise.
13
+ //
14
+ //gofront:intrinsic testing_call
15
+ func call(f any, t any) any
16
+
17
+ func jsCall(f func(*T), t *T) any {
18
+ return call(f, t)
19
+ }
20
+
21
+ // settle waits for the async subtests of t, in the order they started, and
22
+ // reports them.
23
+ async func (t *T) settle() {
24
+ for len(t.pending) > 0 {
25
+ p := t.pending[0]
26
+ t.pending = t.pending[1:]
27
+ await p.sub.wait(p.res)
28
+ await p.sub.settle()
29
+ p.sub.finish()
30
+ }
31
+ }
32
+
33
+ // wait waits for the promise of a test body, recording a rejection as a
34
+ // failure of the test.
35
+ async func (t *T) wait(res any) {
36
+ defer t.Guard()
37
+ await res
38
+ }
39
+
40
+ // RunAsync is Run for a js package: the bodies of async tests are awaited,
41
+ // in order, and so are the async subtests they start. TestMain cannot use
42
+ // it.
43
+ async func (m *M) RunAsync() int {
44
+ callHook = jsCall
45
+ tests, benches := m.plan()
46
+ for _, tt := range tests {
47
+ t := &T{common: common{name: tt.Name}}
48
+ t.begin()
49
+ await t.wait(t.invoke(tt.F))
50
+ await t.settle()
51
+ t.finish()
52
+ if t.failed {
53
+ m.failures++
54
+ }
55
+ }
56
+ callHook = nil
57
+ for _, bb := range benches {
58
+ m.runBenchmark(bb)
59
+ }
60
+ return m.conclude()
61
+ }
@@ -0,0 +1,297 @@
1
+ //go:build gofront
2
+
3
+ //gofront:target both
4
+
5
+ package testing
6
+
7
+ import (
8
+ "fmt"
9
+ "time"
10
+ )
11
+
12
+ // B is a type passed to Benchmark functions to manage benchmark timing and
13
+ // control the number of iterations.
14
+ type B struct {
15
+ common
16
+ // N is the number of iterations the benchmark function must run.
17
+ N int
18
+ timerOn bool
19
+ timerStart int64
20
+ elapsedNs int64
21
+ bytes int64
22
+ targetNs int64
23
+
24
+ // hasSub is set once b.Run has run a sub-benchmark: the parent then
25
+ // reports no result of its own and is not grown.
26
+ hasSub bool
27
+
28
+ loopStarted bool
29
+ loopIters int
30
+ nextCheck int
31
+ }
32
+
33
+ func newB(name string, targetNs int64) *B {
34
+ return &B{
35
+ common: common{name: name},
36
+ N: 1,
37
+ timerOn: true,
38
+ timerStart: nanotime(),
39
+ targetNs: targetNs,
40
+ }
41
+ }
42
+
43
+ // ResetTimer zeroes the elapsed benchmark time.
44
+ func (b *B) ResetTimer() {
45
+ b.timerStart = nanotime()
46
+ b.elapsedNs = 0
47
+ }
48
+
49
+ // StartTimer starts timing a test.
50
+ func (b *B) StartTimer() {
51
+ if !b.timerOn {
52
+ b.timerStart = nanotime()
53
+ b.timerOn = true
54
+ }
55
+ }
56
+
57
+ // StopTimer stops timing a test.
58
+ func (b *B) StopTimer() {
59
+ if b.timerOn {
60
+ b.elapsedNs += nanotime() - b.timerStart
61
+ b.timerOn = false
62
+ }
63
+ }
64
+
65
+ // ReportAllocs enables malloc statistics. They are not collected.
66
+ func (b *B) ReportAllocs() {}
67
+
68
+ // ReportMetric adds a custom metric. It is not recorded.
69
+ func (b *B) ReportMetric(n float64, unit string) {}
70
+
71
+ // SetBytes records the number of bytes processed in a single operation.
72
+ func (b *B) SetBytes(n int64) { b.bytes = n }
73
+
74
+ // Elapsed returns the measured elapsed time of the benchmark.
75
+ func (b *B) Elapsed() time.Duration {
76
+ ns := b.elapsedNs
77
+ if b.timerOn {
78
+ ns += nanotime() - b.timerStart
79
+ }
80
+ return time.Duration(ns)
81
+ }
82
+
83
+ // Loop returns true as long as the benchmark should continue running.
84
+ func (b *B) Loop() bool {
85
+ if !b.loopStarted {
86
+ b.loopStarted = true
87
+ b.ResetTimer()
88
+ if b.targetNs == 0 {
89
+ b.targetNs = 1000000000
90
+ }
91
+ b.loopIters = 0
92
+ b.nextCheck = 1
93
+ }
94
+ if b.failed || b.skipped {
95
+ return false
96
+ }
97
+ b.loopIters++
98
+ b.N = b.loopIters
99
+ if b.loopIters >= b.nextCheck {
100
+ elapsed := nanotime() - b.timerStart
101
+ if elapsed >= b.targetNs || b.loopIters >= 1000000000 {
102
+ // This call starts no iteration.
103
+ b.loopIters--
104
+ b.N = b.loopIters
105
+ b.StopTimer()
106
+ return false
107
+ }
108
+ nsPerOp := max(int64(1), elapsed/int64(b.loopIters))
109
+ remaining := b.targetNs - elapsed
110
+ add := max(int64(1), min(int64(100000), remaining/(nsPerOp*2)))
111
+ b.nextCheck = b.loopIters + int(add)
112
+ }
113
+ return true
114
+ }
115
+
116
+ func (b *B) invoke(f func(*B)) {
117
+ defer b.Guard()
118
+ f(b)
119
+ }
120
+
121
+ // Run runs f as a sub-benchmark of b called name, growing b.N until the run
122
+ // takes the target time, and reports its result. It reports whether f
123
+ // succeeded.
124
+ func (b *B) Run(name string, f func(b *B)) bool {
125
+ full := name
126
+ if b.name != "" {
127
+ full = b.name + "/" + name
128
+ }
129
+ if !selected(full) {
130
+ return true
131
+ }
132
+ b.hasSub = true
133
+ sub := newB(full, b.targetNs)
134
+ sub.parent = &b.common
135
+ sub.begin()
136
+ sub.ResetTimer()
137
+ sub.invoke(f)
138
+ sub.grow(f)
139
+ sub.StopTimer()
140
+ sub.runCleanups()
141
+ if sub.failed {
142
+ // A failure in a regrown run did not reach the parent.
143
+ b.Fail()
144
+ }
145
+ if !sub.onlyParent() {
146
+ sub.reportBenchmark()
147
+ }
148
+ return !sub.failed
149
+ }
150
+
151
+ // BenchmarkResult contains the results of a benchmark run.
152
+ type BenchmarkResult struct {
153
+ N int
154
+ T time.Duration
155
+ Bytes int64
156
+ MemAllocs uint64
157
+ MemBytes uint64
158
+ Extra map[string]float64
159
+ }
160
+
161
+ // NsPerOp returns the "ns/op" metric.
162
+ func (r BenchmarkResult) NsPerOp() int64 {
163
+ if r.N <= 0 {
164
+ return 0
165
+ }
166
+ return int64(r.T) / int64(r.N)
167
+ }
168
+
169
+ // AllocsPerOp returns the "allocs/op" metric.
170
+ func (r BenchmarkResult) AllocsPerOp() int64 {
171
+ if r.N <= 0 {
172
+ return 0
173
+ }
174
+ return int64(r.MemAllocs) / int64(r.N)
175
+ }
176
+
177
+ // AllocedBytesPerOp returns the "B/op" metric.
178
+ func (r BenchmarkResult) AllocedBytesPerOp() int64 {
179
+ if r.N <= 0 {
180
+ return 0
181
+ }
182
+ return int64(r.MemBytes) / int64(r.N)
183
+ }
184
+
185
+ // MemString returns r.AllocedBytesPerOp and r.AllocsPerOp in the same format
186
+ // as 'go test'.
187
+ func (r BenchmarkResult) MemString() string {
188
+ return fmt.Sprintf("%d B/op\t%d allocs/op", r.AllocedBytesPerOp(), r.AllocsPerOp())
189
+ }
190
+
191
+ // String returns a summary of the benchmark results.
192
+ func (r BenchmarkResult) String() string {
193
+ s := fmt.Sprintf("%d\t%d ns/op", r.N, r.NsPerOp())
194
+ if r.Bytes > 0 && r.T > 0 {
195
+ mbPerSec := float64(r.Bytes*int64(r.N)) / (float64(r.T) / 1e9) / (1024 * 1024)
196
+ s += fmt.Sprintf("\t%.2f MB/s", mbPerSec)
197
+ }
198
+ return s
199
+ }
200
+
201
+ // grow reruns f with a larger b.N until the run takes the target time. A
202
+ // benchmark that calls b.Loop sizes itself.
203
+ func (b *B) grow(f func(*B)) {
204
+ if b.loopStarted || b.hasSub || b.failed || b.skipped {
205
+ return
206
+ }
207
+ b.StopTimer()
208
+ b.runCleanups()
209
+ n := 1
210
+ for !b.failed && !b.skipped {
211
+ elapsed := int64(b.Elapsed())
212
+ if elapsed >= b.targetNs || n >= 1000000000 {
213
+ break
214
+ }
215
+ var next int
216
+ if elapsed == 0 {
217
+ next = n * 100
218
+ } else {
219
+ next = max(n+1, min(1000000000, int(float64(n)*(float64(b.targetNs)/float64(elapsed))*1.2)))
220
+ }
221
+ if next <= n {
222
+ break
223
+ }
224
+ n = next
225
+ nb := newB(b.name, b.targetNs)
226
+ nb.N = n
227
+ nb.ResetTimer()
228
+ nb.invoke(f)
229
+ nb.StopTimer()
230
+ nb.runCleanups()
231
+ b.N = nb.N
232
+ b.elapsedNs = nb.elapsedNs
233
+ b.timerOn = false
234
+ b.failed = nb.failed
235
+ b.skipped = nb.skipped
236
+ b.logs = nb.logs
237
+ }
238
+ }
239
+
240
+ // Benchmark benchmarks a single function.
241
+ func Benchmark(f func(b *B)) BenchmarkResult {
242
+ b := newB("Benchmark", 1000000000)
243
+ b.ResetTimer()
244
+ b.invoke(f)
245
+ b.grow(f)
246
+ b.runCleanups()
247
+ return BenchmarkResult{N: b.N, T: b.Elapsed(), Bytes: b.bytes}
248
+ }
249
+
250
+ // runBenchmark runs one benchmark of the table and prints its line.
251
+ func (m *M) runBenchmark(bb GofrontBenchmark) {
252
+ if cfg.verbose {
253
+ say(dim("=== RUN ") + bb.Name)
254
+ }
255
+ b := newB(bb.Name, 100000000)
256
+ b.ResetTimer()
257
+ b.invoke(bb.F)
258
+ b.grow(bb.F)
259
+ b.runCleanups()
260
+ if b.failed {
261
+ m.failures++
262
+ }
263
+ if !b.onlyParent() {
264
+ b.reportBenchmark()
265
+ }
266
+ }
267
+
268
+ // onlyParent reports whether b just ran sub-benchmarks, which reported their
269
+ // own results, so it has none of its own.
270
+ func (b *B) onlyParent() bool {
271
+ return b.hasSub && !b.failed && !b.skipped
272
+ }
273
+
274
+ func (b *B) reportBenchmark() {
275
+ if b.skipped {
276
+ say("--- " + yellow("SKIP") + ": " + b.name)
277
+ b.printLogs(plain)
278
+ return
279
+ }
280
+ if b.failed {
281
+ say("--- " + bold(red("FAIL")) + ": " + b.name)
282
+ b.printLogs(red)
283
+ return
284
+ }
285
+ elapsed := int64(b.Elapsed())
286
+ nsPerOp := 0.0
287
+ if b.N > 0 {
288
+ nsPerOp = float64(elapsed) / float64(b.N)
289
+ }
290
+ line := fmt.Sprintf("%-20s\t%8d\t%10.0f ns/op", b.name, b.N, nsPerOp)
291
+ if b.bytes > 0 && elapsed > 0 {
292
+ mbPerSec := float64(b.bytes*int64(b.N)) / (float64(elapsed) / 1e9) / (1024 * 1024)
293
+ line += fmt.Sprintf("\t%8.2f MB/s", mbPerSec)
294
+ }
295
+ say(line)
296
+ b.printLogs(plain)
297
+ }