goscript 0.2.5 → 0.2.7
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.
- package/cmd/goscript/cmd-compile.go +7 -0
- package/cmd/goscript/cmd_compile_test.go +83 -0
- package/compiler/compile-request.go +3 -0
- package/compiler/compiler-cache.go +828 -0
- package/compiler/compiler-cache_test.go +705 -0
- package/compiler/config.go +2 -0
- package/compiler/index.test.ts +26 -1
- package/compiler/index.ts +5 -0
- package/compiler/lowered-program.go +31 -20
- package/compiler/lowering.go +349 -93
- package/compiler/lowering_bench_test.go +1 -0
- package/compiler/override-facts.go +309 -8
- package/compiler/override-parity-verifier.go +45 -1
- package/compiler/override-parity-verifier_test.go +100 -0
- package/compiler/override-registry_test.go +1 -0
- package/compiler/package-graph.go +40 -12
- package/compiler/package-graph_test.go +29 -0
- package/compiler/runtime-contract.go +8 -0
- package/compiler/service.go +98 -11
- package/compiler/skeleton_test.go +110 -14
- package/compiler/typescript-emitter.go +120 -23
- package/dist/compiler/index.d.ts +2 -0
- package/dist/compiler/index.js +3 -0
- package/dist/compiler/index.js.map +1 -1
- package/dist/gs/builtin/builtin.d.ts +24 -33
- package/dist/gs/builtin/builtin.js +54 -61
- package/dist/gs/builtin/builtin.js.map +1 -1
- package/dist/gs/builtin/hostio.d.ts +1 -0
- package/dist/gs/builtin/hostio.js +1 -1
- package/dist/gs/builtin/hostio.js.map +1 -1
- package/dist/gs/builtin/index.d.ts +1 -0
- package/dist/gs/builtin/index.js +1 -0
- package/dist/gs/builtin/index.js.map +1 -1
- package/dist/gs/builtin/panic.d.ts +18 -0
- package/dist/gs/builtin/panic.js +98 -0
- package/dist/gs/builtin/panic.js.map +1 -0
- package/dist/gs/builtin/slice.d.ts +10 -0
- package/dist/gs/builtin/slice.js +110 -53
- package/dist/gs/builtin/slice.js.map +1 -1
- package/dist/gs/builtin/type.js +15 -3
- package/dist/gs/builtin/type.js.map +1 -1
- package/dist/gs/builtin/varRef.d.ts +1 -1
- package/dist/gs/builtin/varRef.js +3 -2
- package/dist/gs/builtin/varRef.js.map +1 -1
- package/dist/gs/bytes/bytes.gs.js +51 -38
- package/dist/gs/bytes/bytes.gs.js.map +1 -1
- package/dist/gs/bytes/reader.gs.d.ts +1 -1
- package/dist/gs/bytes/reader.gs.js +6 -7
- package/dist/gs/bytes/reader.gs.js.map +1 -1
- package/dist/gs/cmp/index.d.ts +1 -1
- package/dist/gs/cmp/index.js +43 -10
- package/dist/gs/cmp/index.js.map +1 -1
- package/dist/gs/context/context.d.ts +2 -2
- package/dist/gs/context/context.js +1 -1
- package/dist/gs/context/context.js.map +1 -1
- package/dist/gs/embed/index.js +1 -1
- package/dist/gs/embed/index.js.map +1 -1
- package/dist/gs/encoding/binary/index.js +201 -8
- package/dist/gs/encoding/binary/index.js.map +1 -1
- package/dist/gs/encoding/json/index.d.ts +5 -0
- package/dist/gs/encoding/json/index.js +388 -25
- package/dist/gs/encoding/json/index.js.map +1 -1
- package/dist/gs/errors/errors.js +17 -24
- package/dist/gs/errors/errors.js.map +1 -1
- package/dist/gs/fmt/fmt.js +129 -35
- package/dist/gs/fmt/fmt.js.map +1 -1
- package/dist/gs/golang.org/x/crypto/cryptobyte/index.js +1 -1
- package/dist/gs/golang.org/x/crypto/cryptobyte/index.js.map +1 -1
- package/dist/gs/internal/bytealg/index.js +43 -8
- package/dist/gs/internal/bytealg/index.js.map +1 -1
- package/dist/gs/internal/byteorder/index.d.ts +2 -2
- package/dist/gs/internal/byteorder/index.js +2 -2
- package/dist/gs/internal/byteorder/index.js.map +1 -1
- package/dist/gs/io/fs/format.js +2 -2
- package/dist/gs/io/fs/format.js.map +1 -1
- package/dist/gs/io/fs/fs.d.ts +1 -1
- package/dist/gs/io/fs/fs.js +1 -1
- package/dist/gs/io/fs/fs.js.map +1 -1
- package/dist/gs/io/io.d.ts +21 -21
- package/dist/gs/io/io.js +49 -50
- package/dist/gs/io/io.js.map +1 -1
- package/dist/gs/math/bits/index.js +26 -8
- package/dist/gs/math/bits/index.js.map +1 -1
- package/dist/gs/math/copysign.gs.js +10 -17
- package/dist/gs/math/copysign.gs.js.map +1 -1
- package/dist/gs/math/pow.gs.js +5 -0
- package/dist/gs/math/pow.gs.js.map +1 -1
- package/dist/gs/math/signbit.gs.js +6 -2
- package/dist/gs/math/signbit.gs.js.map +1 -1
- package/dist/gs/mime/index.js +1 -0
- package/dist/gs/mime/index.js.map +1 -1
- package/dist/gs/net/http/index.d.ts +6 -6
- package/dist/gs/net/http/index.js +507 -43
- package/dist/gs/net/http/index.js.map +1 -1
- package/dist/gs/os/stat.gs.d.ts +2 -2
- package/dist/gs/os/types.gs.d.ts +1 -1
- package/dist/gs/os/types.gs.js +1 -1
- package/dist/gs/os/types.gs.js.map +1 -1
- package/dist/gs/os/types_js.gs.d.ts +1 -1
- package/dist/gs/os/types_js.gs.js +7 -7
- package/dist/gs/os/types_js.gs.js.map +1 -1
- package/dist/gs/os/types_unix.gs.d.ts +1 -1
- package/dist/gs/os/types_unix.gs.js +1 -1
- package/dist/gs/os/types_unix.gs.js.map +1 -1
- package/dist/gs/os/zero_copy_posix.gs.d.ts +1 -1
- package/dist/gs/os/zero_copy_posix.gs.js +1 -1
- package/dist/gs/os/zero_copy_posix.gs.js.map +1 -1
- package/dist/gs/path/filepath/match.js +8 -4
- package/dist/gs/path/filepath/match.js.map +1 -1
- package/dist/gs/path/filepath/path.js +216 -42
- package/dist/gs/path/filepath/path.js.map +1 -1
- package/dist/gs/path/match.js +6 -3
- package/dist/gs/path/match.js.map +1 -1
- package/dist/gs/reflect/type.d.ts +5 -4
- package/dist/gs/reflect/type.js +29 -11
- package/dist/gs/reflect/type.js.map +1 -1
- package/dist/gs/slices/slices.js +11 -11
- package/dist/gs/slices/slices.js.map +1 -1
- package/dist/gs/strconv/atof.gs.js +156 -43
- package/dist/gs/strconv/atof.gs.js.map +1 -1
- package/dist/gs/strconv/atoi.gs.d.ts +3 -2
- package/dist/gs/strconv/atoi.gs.js +86 -67
- package/dist/gs/strconv/atoi.gs.js.map +1 -1
- package/dist/gs/strconv/ftoa.gs.js +73 -3
- package/dist/gs/strconv/ftoa.gs.js.map +1 -1
- package/dist/gs/strconv/itoa.gs.d.ts +4 -4
- package/dist/gs/strconv/itoa.gs.js +5 -4
- package/dist/gs/strconv/itoa.gs.js.map +1 -1
- package/dist/gs/strconv/quote.gs.d.ts +1 -1
- package/dist/gs/strconv/quote.gs.js +311 -103
- package/dist/gs/strconv/quote.gs.js.map +1 -1
- package/dist/gs/strings/reader.d.ts +1 -1
- package/dist/gs/strings/reader.js +8 -8
- package/dist/gs/strings/reader.js.map +1 -1
- package/dist/gs/strings/strings.js +87 -61
- package/dist/gs/strings/strings.js.map +1 -1
- package/dist/gs/sync/atomic/doc_64.gs.d.ts +14 -14
- package/dist/gs/sync/atomic/doc_64.gs.js +10 -10
- package/dist/gs/sync/atomic/doc_64.gs.js.map +1 -1
- package/dist/gs/sync/atomic/type.gs.d.ts +22 -22
- package/dist/gs/sync/atomic/type.gs.js +4 -4
- package/dist/gs/sync/atomic/type.gs.js.map +1 -1
- package/dist/gs/sync/sync.js +50 -12
- package/dist/gs/sync/sync.js.map +1 -1
- package/dist/gs/syscall/fs.d.ts +6 -6
- package/dist/gs/syscall/fs.js +1 -1
- package/dist/gs/syscall/fs.js.map +1 -1
- package/dist/gs/time/time.d.ts +18 -18
- package/dist/gs/time/time.js +58 -55
- package/dist/gs/time/time.js.map +1 -1
- package/dist/gs/unicode/tables.d.ts +11 -0
- package/dist/gs/unicode/tables.js +635 -0
- package/dist/gs/unicode/tables.js.map +1 -0
- package/dist/gs/unicode/unicode.d.ts +58 -38
- package/dist/gs/unicode/unicode.js +362 -278
- package/dist/gs/unicode/unicode.js.map +1 -1
- package/go.sum +13 -0
- package/gs/builtin/builtin.ts +83 -93
- package/gs/builtin/hostio.ts +1 -1
- package/gs/builtin/index.ts +1 -0
- package/gs/builtin/panic.test.ts +189 -0
- package/gs/builtin/panic.ts +107 -0
- package/gs/builtin/runtime-contract.test.ts +5 -5
- package/gs/builtin/slice.test.ts +23 -0
- package/gs/builtin/slice.ts +133 -95
- package/gs/builtin/type.ts +16 -3
- package/gs/builtin/varRef.ts +4 -2
- package/gs/builtin/wide-int.test.ts +41 -0
- package/gs/bytes/bytes.gs.ts +54 -41
- package/gs/bytes/bytes.test.ts +18 -1
- package/gs/bytes/reader.gs.ts +7 -8
- package/gs/cmp/index.test.ts +55 -0
- package/gs/cmp/index.ts +45 -9
- package/gs/context/context.ts +3 -3
- package/gs/embed/index.ts +2 -2
- package/gs/encoding/binary/index.test.ts +104 -0
- package/gs/encoding/binary/index.ts +259 -11
- package/gs/encoding/json/index.test.ts +107 -0
- package/gs/encoding/json/index.ts +400 -29
- package/gs/errors/errors.test.ts +44 -1
- package/gs/errors/errors.ts +15 -31
- package/gs/fmt/fmt.test.ts +70 -2
- package/gs/fmt/fmt.ts +128 -34
- package/gs/golang.org/x/crypto/cryptobyte/index.ts +1 -1
- package/gs/internal/bytealg/index.test.ts +26 -1
- package/gs/internal/bytealg/index.ts +44 -8
- package/gs/internal/byteorder/index.ts +6 -4
- package/gs/io/fs/format.ts +2 -2
- package/gs/io/fs/fs.ts +2 -2
- package/gs/io/fs/stat.test.ts +2 -2
- package/gs/io/fs/sub.test.ts +2 -2
- package/gs/io/fs/walk.test.ts +2 -2
- package/gs/io/io.test.ts +47 -5
- package/gs/io/io.ts +73 -73
- package/gs/io/limit.test.ts +103 -0
- package/gs/math/bits/index.test.ts +128 -0
- package/gs/math/bits/index.ts +26 -8
- package/gs/math/copysign.gs.test.ts +3 -1
- package/gs/math/copysign.gs.ts +10 -22
- package/gs/math/pow.gs.test.ts +4 -5
- package/gs/math/pow.gs.ts +5 -0
- package/gs/math/signbit.gs.test.ts +2 -1
- package/gs/math/signbit.gs.ts +6 -3
- package/gs/mime/index.ts +1 -0
- package/gs/net/http/index.test.ts +683 -2
- package/gs/net/http/index.ts +598 -57
- package/gs/net/http/meta.json +3 -0
- package/gs/os/stat.gs.ts +2 -2
- package/gs/os/types.gs.ts +2 -2
- package/gs/os/types_js.gs.ts +9 -9
- package/gs/os/types_unix.gs.ts +2 -2
- package/gs/os/zero_copy_posix.gs.ts +2 -2
- package/gs/path/filepath/match.test.ts +16 -0
- package/gs/path/filepath/match.ts +8 -4
- package/gs/path/filepath/path.test.ts +91 -9
- package/gs/path/filepath/path.ts +223 -49
- package/gs/path/match.test.ts +32 -0
- package/gs/path/match.ts +6 -3
- package/gs/reflect/deepequal.test.ts +1 -1
- package/gs/reflect/field.test.ts +1 -1
- package/gs/reflect/function-types.test.ts +6 -6
- package/gs/reflect/sliceat.test.ts +13 -13
- package/gs/reflect/structof.test.ts +4 -4
- package/gs/reflect/type.ts +34 -14
- package/gs/reflect/typefor.test.ts +5 -5
- package/gs/runtime/pprof/index.test.ts +20 -0
- package/gs/runtime/trace/index.test.ts +3 -0
- package/gs/slices/slices.test.ts +31 -0
- package/gs/slices/slices.ts +11 -11
- package/gs/strconv/append.test.ts +99 -0
- package/gs/strconv/atof.gs.ts +156 -42
- package/gs/strconv/atof.test.ts +45 -0
- package/gs/strconv/atoi.gs.ts +87 -69
- package/gs/strconv/atoi.test.ts +49 -0
- package/gs/strconv/ftoa.gs.ts +85 -10
- package/gs/strconv/ftoa.test.ts +43 -0
- package/gs/strconv/itoa.gs.ts +10 -9
- package/gs/strconv/quote.gs.ts +335 -108
- package/gs/strconv/quote.test.ts +111 -0
- package/gs/strings/reader.test.ts +10 -10
- package/gs/strings/reader.ts +9 -9
- package/gs/strings/strings.test.ts +18 -5
- package/gs/strings/strings.ts +81 -68
- package/gs/sync/atomic/doc_64.gs.ts +24 -24
- package/gs/sync/atomic/doc_64.test.ts +5 -5
- package/gs/sync/atomic/type.gs.ts +28 -28
- package/gs/sync/sync.test.ts +109 -1
- package/gs/sync/sync.ts +46 -12
- package/gs/syscall/fs.ts +8 -8
- package/gs/syscall/net.test.ts +1 -1
- package/gs/time/parse.test.ts +45 -0
- package/gs/time/time.test.ts +46 -23
- package/gs/time/time.ts +69 -66
- package/gs/unicode/gen.go +198 -0
- package/gs/unicode/tables.ts +646 -0
- package/gs/unicode/unicode.test.ts +69 -0
- package/gs/unicode/unicode.ts +396 -312
- package/package.json +2 -2
- package/dist/gs/github.com/aperturerobotics/util/conc/index.d.ts +0 -20
- package/dist/gs/github.com/aperturerobotics/util/conc/index.js +0 -134
- package/dist/gs/github.com/aperturerobotics/util/conc/index.js.map +0 -1
- package/gs/github.com/aperturerobotics/util/conc/index.test.ts +0 -30
- package/gs/github.com/aperturerobotics/util/conc/index.ts +0 -172
- package/gs/github.com/aperturerobotics/util/conc/meta.json +0 -9
package/gs/fmt/fmt.test.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { afterEach, describe, expect, it, vi } from 'vitest'
|
|
2
2
|
import { resetHostRuntimeForTests } from '@goscript/builtin/hostio.js'
|
|
3
3
|
import * as $ from '@goscript/builtin/index.js'
|
|
4
|
+
import * as errors from '@goscript/errors/index.js'
|
|
4
5
|
import * as os from '@goscript/os/index.js'
|
|
5
6
|
import * as fmt from './fmt.js'
|
|
6
7
|
|
|
@@ -86,6 +87,21 @@ describe('fmt basic value formatting', () => {
|
|
|
86
87
|
expect(fmt.Sprintf('%q', 97)).toBe(JSON.stringify('a'))
|
|
87
88
|
})
|
|
88
89
|
|
|
90
|
+
it('%c encodes runes including astral planes and invalid code points', () => {
|
|
91
|
+
expect(fmt.Sprintf('%c', 0x597d)).toBe('好')
|
|
92
|
+
// U+1F600 is above U+FFFF; fromCharCode would have truncated it.
|
|
93
|
+
expect(fmt.Sprintf('%c', 0x1f600)).toBe('😀')
|
|
94
|
+
expect(fmt.Sprintf('%c', 0x1f600).codePointAt(0)).toBe(0x1f600)
|
|
95
|
+
// Invalid code points map to U+FFFD instead of throwing.
|
|
96
|
+
expect(fmt.Sprintf('%c', 0x110000)).toBe('�')
|
|
97
|
+
expect(fmt.Sprintf('%c', -1)).toBe('�')
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
it('%q on an astral rune does not throw and round-trips the code point', () => {
|
|
101
|
+
expect(fmt.Sprintf('%q', 0x1f600)).toBe(JSON.stringify('😀'))
|
|
102
|
+
expect(fmt.Sprintf('%q', 0x110000)).toBe(JSON.stringify('�'))
|
|
103
|
+
})
|
|
104
|
+
|
|
89
105
|
it('%p pointer-ish formatting fallback', () => {
|
|
90
106
|
expect(fmt.Sprintf('%p', {})).toBe('0x0')
|
|
91
107
|
expect(fmt.Sprintf('%p', { __address: 255 })).toBe('0xff')
|
|
@@ -127,8 +143,33 @@ describe('fmt basic value formatting', () => {
|
|
|
127
143
|
return '<go stringer>'
|
|
128
144
|
},
|
|
129
145
|
}
|
|
130
|
-
//
|
|
131
|
-
expect(fmt.Sprintf('
|
|
146
|
+
// Go consults GoString only for %#v, never plain %v / Sprint.
|
|
147
|
+
expect(fmt.Sprintf('%#v', goStringer)).toBe('<go stringer>')
|
|
148
|
+
expect(fmt.Sprintf('%v', goStringer)).not.toBe('<go stringer>')
|
|
149
|
+
expect(fmt.Sprint(goStringer)).not.toBe('<go stringer>')
|
|
150
|
+
})
|
|
151
|
+
|
|
152
|
+
it('applies # base prefixes and +/space sign flags (Go parity)', () => {
|
|
153
|
+
expect(fmt.Sprintf('%#x', 15)).toBe('0xf')
|
|
154
|
+
expect(fmt.Sprintf('%#X', 15)).toBe('0XF')
|
|
155
|
+
expect(fmt.Sprintf('%#o', 15)).toBe('017')
|
|
156
|
+
expect(fmt.Sprintf('%#x', 0)).toBe('0x0')
|
|
157
|
+
expect(fmt.Sprintf('%+d', 15)).toBe('+15')
|
|
158
|
+
expect(fmt.Sprintf('% d', 15)).toBe(' 15')
|
|
159
|
+
expect(fmt.Sprintf('%+d', -15)).toBe('-15')
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
it('%v prefers Stringer while %#v prefers GoStringer', () => {
|
|
163
|
+
const g = {
|
|
164
|
+
String() {
|
|
165
|
+
return 'G(7)'
|
|
166
|
+
},
|
|
167
|
+
GoString() {
|
|
168
|
+
return 'main.G{n:7}'
|
|
169
|
+
},
|
|
170
|
+
}
|
|
171
|
+
expect(fmt.Sprintf('%v|%#v', g, g)).toBe('G(7)|main.G{n:7}')
|
|
172
|
+
expect(fmt.Sprint(g)).toBe('G(7)')
|
|
132
173
|
})
|
|
133
174
|
|
|
134
175
|
it('%w formats errors by Error method', () => {
|
|
@@ -136,6 +177,21 @@ describe('fmt basic value formatting', () => {
|
|
|
136
177
|
expect(fmt.Errorf('wrap: %w', err)?.Error()).toBe('wrap: root')
|
|
137
178
|
})
|
|
138
179
|
|
|
180
|
+
it('%w wraps so Unwrap and errors.Is reach the operand', () => {
|
|
181
|
+
const base = $.newError('root')
|
|
182
|
+
const wrapped = fmt.Errorf('ctx: %w', base)
|
|
183
|
+
expect((wrapped as any).Unwrap()).toBe(base)
|
|
184
|
+
expect(errors.Is(wrapped, base)).toBe(true)
|
|
185
|
+
})
|
|
186
|
+
|
|
187
|
+
it('multiple %w unwrap to every operand', () => {
|
|
188
|
+
const a = $.newError('a')
|
|
189
|
+
const b = $.newError('b')
|
|
190
|
+
const wrapped = fmt.Errorf('%w and %w', a, b)
|
|
191
|
+
expect(errors.Is(wrapped, a)).toBe(true)
|
|
192
|
+
expect(errors.Is(wrapped, b)).toBe(true)
|
|
193
|
+
})
|
|
194
|
+
|
|
139
195
|
it('%s formats stringers by String method', () => {
|
|
140
196
|
const stringer = {
|
|
141
197
|
String() {
|
|
@@ -297,4 +353,16 @@ describe('fmt scanning', () => {
|
|
|
297
353
|
expect(start.value).toBe(12)
|
|
298
354
|
expect(end.value).toBe(34)
|
|
299
355
|
})
|
|
356
|
+
|
|
357
|
+
it('allows trailing input after the matched verbs (Go parity)', () => {
|
|
358
|
+
const v = $.varRef(0)
|
|
359
|
+
const [n, err] = fmt.Sscanf(
|
|
360
|
+
'123 trailing',
|
|
361
|
+
'%d',
|
|
362
|
+
$.interfaceValue(v, '*int64'),
|
|
363
|
+
)
|
|
364
|
+
expect(err).toBeNull()
|
|
365
|
+
expect(n).toBe(1)
|
|
366
|
+
expect(v.value).toBe(123)
|
|
367
|
+
})
|
|
300
368
|
})
|
package/gs/fmt/fmt.ts
CHANGED
|
@@ -25,19 +25,58 @@ export interface State {
|
|
|
25
25
|
Write(b: $.Bytes): [number, $.GoError | null]
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
+
// formatInt renders an integer value in the given radix, applying Go's # flag
|
|
29
|
+
// (0x/0X/0 base prefix) and +/space sign flags. The sign precedes the prefix,
|
|
30
|
+
// matching fmt's "%+#x" ordering.
|
|
31
|
+
function formatInt(
|
|
32
|
+
value: any,
|
|
33
|
+
radix: number,
|
|
34
|
+
flags: string,
|
|
35
|
+
upper: boolean,
|
|
36
|
+
): string {
|
|
37
|
+
const big = typeof value === 'bigint'
|
|
38
|
+
const neg = big ? (value as bigint) < 0n : Number(value) < 0
|
|
39
|
+
let digits = big
|
|
40
|
+
? (neg ? -(value as bigint) : (value as bigint)).toString(radix)
|
|
41
|
+
: Math.abs(Math.trunc(Number(value))).toString(radix)
|
|
42
|
+
if (upper) {
|
|
43
|
+
digits = digits.toUpperCase()
|
|
44
|
+
}
|
|
45
|
+
let prefix = ''
|
|
46
|
+
if (flags.includes('#')) {
|
|
47
|
+
if (radix === 16) {
|
|
48
|
+
prefix = upper ? '0X' : '0x'
|
|
49
|
+
} else if (radix === 8 && !digits.startsWith('0')) {
|
|
50
|
+
prefix = '0'
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
let sign = ''
|
|
54
|
+
if (neg) {
|
|
55
|
+
sign = '-'
|
|
56
|
+
} else if (flags.includes('+')) {
|
|
57
|
+
sign = '+'
|
|
58
|
+
} else if (flags.includes(' ')) {
|
|
59
|
+
sign = ' '
|
|
60
|
+
}
|
|
61
|
+
return sign + prefix + digits
|
|
62
|
+
}
|
|
63
|
+
|
|
28
64
|
// Simple printf-style formatting implementation
|
|
29
|
-
function formatValue(value: any, verb: string): string {
|
|
65
|
+
function formatValue(value: any, verb: string, flags = ''): string {
|
|
30
66
|
if (value === null || value === undefined) {
|
|
31
67
|
return '<nil>'
|
|
32
68
|
}
|
|
33
69
|
|
|
34
70
|
switch (verb) {
|
|
35
71
|
case 'v': // default format
|
|
72
|
+
if (flags.includes('#') && hasGoString(value)) {
|
|
73
|
+
return defaultFormat(value.GoString())
|
|
74
|
+
}
|
|
36
75
|
return defaultFormat(value)
|
|
37
76
|
case 'w': // wrapped error
|
|
38
77
|
return defaultFormat(value)
|
|
39
78
|
case 'd': // decimal integer
|
|
40
|
-
return
|
|
79
|
+
return formatInt(value, 10, flags, false)
|
|
41
80
|
case 'f': // decimal point, no exponent
|
|
42
81
|
return Number(value).toString()
|
|
43
82
|
case 's': // string
|
|
@@ -55,15 +94,18 @@ function formatValue(value: any, verb: string): string {
|
|
|
55
94
|
if (typeof value === 'string') return 'string'
|
|
56
95
|
return typeof value
|
|
57
96
|
case 'c': // character (Unicode code point)
|
|
58
|
-
|
|
97
|
+
// Go's %c encodes the rune as a Unicode character; fromCharCode truncates
|
|
98
|
+
// any code point above U+FFFF and mangles surrogates. runeToString owns
|
|
99
|
+
// the Go string(rune) rule (astral intact, invalid -> U+FFFD, no throw).
|
|
100
|
+
return $.runeToString(Number(value))
|
|
59
101
|
case 'x': // hexadecimal lowercase
|
|
60
|
-
return
|
|
102
|
+
return formatInt(value, 16, flags, false)
|
|
61
103
|
case 'X': // hexadecimal uppercase
|
|
62
|
-
return
|
|
104
|
+
return formatInt(value, 16, flags, true)
|
|
63
105
|
case 'o': // octal
|
|
64
|
-
return
|
|
106
|
+
return formatInt(value, 8, flags, false)
|
|
65
107
|
case 'b': // binary
|
|
66
|
-
return
|
|
108
|
+
return formatInt(value, 2, flags, false)
|
|
67
109
|
case 'e': // scientific notation lowercase
|
|
68
110
|
return Number(value).toExponential()
|
|
69
111
|
case 'E': // scientific notation uppercase
|
|
@@ -74,9 +116,9 @@ function formatValue(value: any, verb: string): string {
|
|
|
74
116
|
return Number(value).toPrecision().toUpperCase()
|
|
75
117
|
case 'q': // quoted string / rune
|
|
76
118
|
if (typeof value === 'number' && Number.isInteger(value)) {
|
|
77
|
-
// emulate quoted rune
|
|
78
|
-
|
|
79
|
-
return JSON.stringify(
|
|
119
|
+
// emulate quoted rune; runeToString avoids fromCodePoint throwing on
|
|
120
|
+
// invalid code points and maps them to U+FFFD as Go's %q does.
|
|
121
|
+
return JSON.stringify($.runeToString(value))
|
|
80
122
|
}
|
|
81
123
|
return JSON.stringify(String(value))
|
|
82
124
|
case 'p': {
|
|
@@ -98,6 +140,16 @@ function hasGoTypeName(value: unknown): value is { __goType: string } {
|
|
|
98
140
|
)
|
|
99
141
|
}
|
|
100
142
|
|
|
143
|
+
// hasGoString reports whether value implements the GoStringer interface. Go
|
|
144
|
+
// invokes GoString only for the %#v verb, never for %v, %s, or Sprint.
|
|
145
|
+
function hasGoString(value: unknown): value is { GoString: () => string } {
|
|
146
|
+
return (
|
|
147
|
+
value !== null &&
|
|
148
|
+
typeof value === 'object' &&
|
|
149
|
+
typeof (value as { GoString?: unknown }).GoString === 'function'
|
|
150
|
+
)
|
|
151
|
+
}
|
|
152
|
+
|
|
101
153
|
type MaybeString = string | PromiseLike<string>
|
|
102
154
|
|
|
103
155
|
function isPromiseLike<T = unknown>(value: unknown): value is PromiseLike<T> {
|
|
@@ -145,17 +197,9 @@ function defaultFormatMaybe(value: any): MaybeString {
|
|
|
145
197
|
if (Array.isArray(value))
|
|
146
198
|
return joinMaybe(value.map(defaultFormatMaybe), ' ', '[', ']')
|
|
147
199
|
if (typeof value === 'object') {
|
|
148
|
-
//
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
typeof (value as any).GoString === 'function'
|
|
152
|
-
) {
|
|
153
|
-
try {
|
|
154
|
-
return toMaybeString((value as any).GoString())
|
|
155
|
-
} catch {
|
|
156
|
-
// Ignore error by continuing to next case.
|
|
157
|
-
}
|
|
158
|
-
}
|
|
200
|
+
// GoStringer is intentionally not consulted here: Go calls GoString only
|
|
201
|
+
// for the %#v verb, which formatValue handles before reaching this default
|
|
202
|
+
// path. %v, %s, Sprint, and Print use Error then Stringer.
|
|
159
203
|
// Prefer error interface if present
|
|
160
204
|
if ((value as any).Error && typeof (value as any).Error === 'function') {
|
|
161
205
|
try {
|
|
@@ -233,18 +277,21 @@ function parseFormat(format: string, args: any[]): string {
|
|
|
233
277
|
return formatted
|
|
234
278
|
}
|
|
235
279
|
|
|
236
|
-
function formatValueMaybe(value: any, verb: string): MaybeString {
|
|
280
|
+
function formatValueMaybe(value: any, verb: string, flags = ''): MaybeString {
|
|
237
281
|
switch (verb) {
|
|
238
282
|
case 'v':
|
|
283
|
+
if (flags.includes('#') && hasGoString(value)) {
|
|
284
|
+
return toMaybeString(value.GoString())
|
|
285
|
+
}
|
|
286
|
+
return defaultFormatMaybe(value)
|
|
239
287
|
case 'w':
|
|
288
|
+
return defaultFormatMaybe(value)
|
|
240
289
|
case 's':
|
|
241
|
-
if (
|
|
242
|
-
|
|
243
|
-
if (value instanceof Uint8Array) return $.bytesToString(value)
|
|
244
|
-
}
|
|
290
|
+
if (typeof value === 'string') return value
|
|
291
|
+
if (value instanceof Uint8Array) return $.bytesToString(value)
|
|
245
292
|
return defaultFormatMaybe(value)
|
|
246
293
|
default:
|
|
247
|
-
return formatValue(value, verb)
|
|
294
|
+
return formatValue(value, verb, flags)
|
|
248
295
|
}
|
|
249
296
|
}
|
|
250
297
|
|
|
@@ -351,8 +398,8 @@ function parseFormatMaybe(
|
|
|
351
398
|
if (formatted === null) {
|
|
352
399
|
formatted =
|
|
353
400
|
allowAsync ?
|
|
354
|
-
formatValueMaybe(arg, verb)
|
|
355
|
-
: formatValue(arg, verb)
|
|
401
|
+
formatValueMaybe(arg, verb, flags)
|
|
402
|
+
: formatValue(arg, verb, flags)
|
|
356
403
|
}
|
|
357
404
|
|
|
358
405
|
parts.push(
|
|
@@ -591,7 +638,52 @@ export function Appendln(b: $.Bytes, ...a: any[]): $.Bytes {
|
|
|
591
638
|
// Error creation
|
|
592
639
|
export function Errorf(format: string, ...a: any[]): any {
|
|
593
640
|
const message = parseFormat(format, a)
|
|
594
|
-
|
|
641
|
+
const err = errors.New(message)
|
|
642
|
+
// %w operands are wrapped: the result must Unwrap to them so errors.Is/As can
|
|
643
|
+
// walk the chain. One %w unwraps to a single error; multiple %w (Go 1.20+)
|
|
644
|
+
// unwrap to an []error, matching the depth-first traversal in errors.Is.
|
|
645
|
+
const wrapped = errorfWrappedArgs(format, a)
|
|
646
|
+
if (wrapped.length === 1) {
|
|
647
|
+
;(err as any).Unwrap = (): $.GoError => wrapped[0]
|
|
648
|
+
} else if (wrapped.length > 1) {
|
|
649
|
+
;(err as any).Unwrap = (): $.GoError[] => wrapped
|
|
650
|
+
}
|
|
651
|
+
return err
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
// errorfWrappedArgs returns the error operands consumed by %w verbs, in order.
|
|
655
|
+
// It walks the format string with the same flag/width/precision/verb skip and
|
|
656
|
+
// the same positional arg consumption as parseFormat, so the operand a %w binds
|
|
657
|
+
// to here is exactly the one it formatted in the message.
|
|
658
|
+
function errorfWrappedArgs(format: string, args: any[]): $.GoError[] {
|
|
659
|
+
const wrapped: $.GoError[] = []
|
|
660
|
+
let argIndex = 0
|
|
661
|
+
for (let i = 0; i < format.length; i++) {
|
|
662
|
+
if (format[i] !== '%' || i + 1 >= format.length) {
|
|
663
|
+
continue
|
|
664
|
+
}
|
|
665
|
+
if (format[i + 1] === '%') {
|
|
666
|
+
i++
|
|
667
|
+
continue
|
|
668
|
+
}
|
|
669
|
+
let j = i + 1
|
|
670
|
+
while (j < format.length && '+-# 0'.includes(format[j])) j++
|
|
671
|
+
while (j < format.length && format[j] >= '0' && format[j] <= '9') j++
|
|
672
|
+
if (j < format.length && format[j] === '.') {
|
|
673
|
+
j++
|
|
674
|
+
while (j < format.length && format[j] >= '0' && format[j] <= '9') j++
|
|
675
|
+
}
|
|
676
|
+
if (j < format.length) {
|
|
677
|
+
if (argIndex < args.length) {
|
|
678
|
+
if (format[j] === 'w') {
|
|
679
|
+
wrapped.push(args[argIndex] as $.GoError)
|
|
680
|
+
}
|
|
681
|
+
argIndex++
|
|
682
|
+
}
|
|
683
|
+
i = j
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
return wrapped
|
|
595
687
|
}
|
|
596
688
|
|
|
597
689
|
// FormatString - simplified implementation
|
|
@@ -676,12 +768,15 @@ export function Sscanf(
|
|
|
676
768
|
function buildScanPattern(
|
|
677
769
|
format: string,
|
|
678
770
|
): { pattern: RegExp; verbs: string[] } | null {
|
|
771
|
+
// Anchor at the start only: Go's Sscanf consumes a prefix and allows trailing
|
|
772
|
+
// input to remain, so no end anchor. A run of whitespace in the format matches
|
|
773
|
+
// spaces and tabs but not a newline, which Go treats as a record boundary.
|
|
679
774
|
let source = '^'
|
|
680
775
|
const verbs: string[] = []
|
|
681
776
|
for (let i = 0; i < format.length; i++) {
|
|
682
777
|
const ch = format[i]
|
|
683
778
|
if (ch !== '%') {
|
|
684
|
-
source += /\s/.test(ch) ? '\\
|
|
779
|
+
source += /\s/.test(ch) ? '[ \\t]+' : escapeRegExp(ch)
|
|
685
780
|
continue
|
|
686
781
|
}
|
|
687
782
|
const verb = format[++i]
|
|
@@ -690,18 +785,17 @@ function buildScanPattern(
|
|
|
690
785
|
continue
|
|
691
786
|
}
|
|
692
787
|
if (verb === 'd') {
|
|
693
|
-
source += '([+-]?\\d+)'
|
|
788
|
+
source += '\\s*([+-]?\\d+)' // verbs skip leading whitespace, incl. newlines
|
|
694
789
|
verbs.push(verb)
|
|
695
790
|
continue
|
|
696
791
|
}
|
|
697
792
|
if (verb === 's') {
|
|
698
|
-
source += '(\\S+)'
|
|
793
|
+
source += '\\s*(\\S+)'
|
|
699
794
|
verbs.push(verb)
|
|
700
795
|
continue
|
|
701
796
|
}
|
|
702
797
|
return null
|
|
703
798
|
}
|
|
704
|
-
source += '$'
|
|
705
799
|
return { pattern: new RegExp(source), verbs }
|
|
706
800
|
}
|
|
707
801
|
|
|
@@ -1378,7 +1378,7 @@ function makeTime(
|
|
|
1378
1378
|
const millis =
|
|
1379
1379
|
globalThis.Date.UTC(year, month - 1, day, hour, minute, second) -
|
|
1380
1380
|
offsetMinutes * 60 * 1000
|
|
1381
|
-
return time.Unix(Math.floor(millis / 1000),
|
|
1381
|
+
return time.Unix(BigInt(Math.floor(millis / 1000)), 0n)
|
|
1382
1382
|
}
|
|
1383
1383
|
|
|
1384
1384
|
function parseGeneralizedTime(value: string): time.Time | null {
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import { describe, expect, test } from 'vitest'
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
CountString,
|
|
5
|
+
IndexByteString,
|
|
6
|
+
IndexString,
|
|
7
|
+
LastIndexByteString,
|
|
8
|
+
} from './index.js'
|
|
4
9
|
|
|
5
10
|
describe('internal/bytealg string byte indexes', () => {
|
|
6
11
|
test('finds first and last byte positions in strings', () => {
|
|
@@ -15,4 +20,24 @@ describe('internal/bytealg string byte indexes', () => {
|
|
|
15
20
|
expect(CountString('hello', 'x'.charCodeAt(0))).toBe(0)
|
|
16
21
|
expect(CountString('', 'x'.charCodeAt(0))).toBe(0)
|
|
17
22
|
})
|
|
23
|
+
|
|
24
|
+
test('indexes UTF-8 bytes, not UTF-16 code units (Go semantics)', () => {
|
|
25
|
+
// '你' encodes to UTF-8 bytes E4 BD A0. A 0xA0 byte lives at byte index 2,
|
|
26
|
+
// and the trailing 'A' at byte index 3; UTF-16 search would mislocate both.
|
|
27
|
+
const s = '你A'
|
|
28
|
+
expect(IndexByteString(s, 0xe4)).toBe(0)
|
|
29
|
+
expect(IndexByteString(s, 0xa0)).toBe(2)
|
|
30
|
+
expect(IndexByteString(s, 'A'.charCodeAt(0))).toBe(3)
|
|
31
|
+
expect(LastIndexByteString(s, 0xbd)).toBe(1)
|
|
32
|
+
expect(CountString('日本語', 0xe6)).toBe(2)
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
test('IndexString returns the byte offset of a multibyte substring', () => {
|
|
36
|
+
// 'A你B': 'B' is at byte index 4 (1 + 3 UTF-8 bytes for 你).
|
|
37
|
+
expect(IndexString('A你B', 'B')).toBe(4)
|
|
38
|
+
expect(IndexString('A你B', '你')).toBe(1)
|
|
39
|
+
expect(IndexString('hello', '')).toBe(0)
|
|
40
|
+
expect(IndexString('hi', 'hello')).toBe(-1)
|
|
41
|
+
expect(IndexString('hello', 'xyz')).toBe(-1)
|
|
42
|
+
})
|
|
18
43
|
})
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
// Placeholder bytealg module for reflect package compatibility
|
|
2
2
|
|
|
3
|
+
import * as $ from '@goscript/builtin/index.js'
|
|
4
|
+
|
|
3
5
|
// Helper function to normalize bytes input
|
|
4
6
|
function normalizeBytes(b: any): any[] {
|
|
5
7
|
if (b === null || b === undefined) {
|
|
@@ -126,21 +128,35 @@ export function IndexRabinKarp(s: any, sep: any): number {
|
|
|
126
128
|
return Index(s, sep)
|
|
127
129
|
}
|
|
128
130
|
|
|
131
|
+
// Go bytealg operates on the UTF-8 byte representation of a string and returns
|
|
132
|
+
// byte indices, not UTF-16 code-unit indices. String.fromCharCode/indexOf would
|
|
133
|
+
// search code units, mislocating any byte >127 or any multi-byte rune. Search
|
|
134
|
+
// the Go bytes via the builtin string owner so indices match Go.
|
|
129
135
|
export function IndexByteString(s: string, b: number): number {
|
|
130
|
-
const
|
|
131
|
-
|
|
136
|
+
const bytes = $.stringToBytes(s)
|
|
137
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
138
|
+
if (bytes[i] === b) {
|
|
139
|
+
return i
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return -1
|
|
132
143
|
}
|
|
133
144
|
|
|
134
145
|
export function LastIndexByteString(s: string, b: number): number {
|
|
135
|
-
const
|
|
136
|
-
|
|
146
|
+
const bytes = $.stringToBytes(s)
|
|
147
|
+
for (let i = bytes.length - 1; i >= 0; i--) {
|
|
148
|
+
if (bytes[i] === b) {
|
|
149
|
+
return i
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
return -1
|
|
137
153
|
}
|
|
138
154
|
|
|
139
155
|
export function CountString(s: string, b: number): number {
|
|
140
|
-
const
|
|
156
|
+
const bytes = $.stringToBytes(s)
|
|
141
157
|
let count = 0
|
|
142
|
-
for (let i = 0; i <
|
|
143
|
-
if (
|
|
158
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
159
|
+
if (bytes[i] === b) {
|
|
144
160
|
count++
|
|
145
161
|
}
|
|
146
162
|
}
|
|
@@ -148,7 +164,27 @@ export function CountString(s: string, b: number): number {
|
|
|
148
164
|
}
|
|
149
165
|
|
|
150
166
|
export function IndexString(s: string, substr: string): number {
|
|
151
|
-
|
|
167
|
+
const bytes = $.stringToBytes(s)
|
|
168
|
+
const sub = $.stringToBytes(substr)
|
|
169
|
+
if (sub.length === 0) {
|
|
170
|
+
return 0
|
|
171
|
+
}
|
|
172
|
+
if (sub.length > bytes.length) {
|
|
173
|
+
return -1
|
|
174
|
+
}
|
|
175
|
+
for (let i = 0; i <= bytes.length - sub.length; i++) {
|
|
176
|
+
let found = true
|
|
177
|
+
for (let j = 0; j < sub.length; j++) {
|
|
178
|
+
if (bytes[i + j] !== sub[j]) {
|
|
179
|
+
found = false
|
|
180
|
+
break
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
if (found) {
|
|
184
|
+
return i
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
return -1
|
|
152
188
|
}
|
|
153
189
|
|
|
154
190
|
export function MakeNoZero(n: number): Uint8Array {
|
|
@@ -12,8 +12,9 @@ export function BEUint32(b: $.Bytes): number {
|
|
|
12
12
|
return (((b![0] << 24) >>> 0) | (b![1] << 16) | (b![2] << 8) | b![3]) >>> 0
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
export function BEUint64(b: $.Bytes):
|
|
16
|
-
return
|
|
15
|
+
export function BEUint64(b: $.Bytes): bigint {
|
|
16
|
+
return BigInt.asUintN(
|
|
17
|
+
64,
|
|
17
18
|
(BigInt(b![0]) << 56n) |
|
|
18
19
|
(BigInt(b![1]) << 48n) |
|
|
19
20
|
(BigInt(b![2]) << 40n) |
|
|
@@ -33,8 +34,9 @@ export function LEUint32(b: $.Bytes): number {
|
|
|
33
34
|
return b![0] | (b![1] << 8) | (b![2] << 16) | (b![3] << 24)
|
|
34
35
|
}
|
|
35
36
|
|
|
36
|
-
export function LEUint64(b: $.Bytes):
|
|
37
|
-
return
|
|
37
|
+
export function LEUint64(b: $.Bytes): bigint {
|
|
38
|
+
return BigInt.asUintN(
|
|
39
|
+
64,
|
|
38
40
|
BigInt(b![0]) |
|
|
39
41
|
(BigInt(b![1]) << 8n) |
|
|
40
42
|
(BigInt(b![2]) << 16n) |
|
package/gs/io/fs/format.ts
CHANGED
package/gs/io/fs/fs.ts
CHANGED
|
@@ -335,7 +335,7 @@ export type FileInfo = null | {
|
|
|
335
335
|
// base name of the file
|
|
336
336
|
Name(): string
|
|
337
337
|
// length in bytes for regular files; system-dependent for others
|
|
338
|
-
Size():
|
|
338
|
+
Size(): bigint
|
|
339
339
|
// underlying data source (can return nil)
|
|
340
340
|
Sys(): null | any
|
|
341
341
|
}
|
|
@@ -359,7 +359,7 @@ $.registerInterfaceType(
|
|
|
359
359
|
{
|
|
360
360
|
name: 'Size',
|
|
361
361
|
args: [],
|
|
362
|
-
returns: [{ type: { kind: $.TypeKind.Basic, name: '
|
|
362
|
+
returns: [{ type: { kind: $.TypeKind.Basic, name: 'int64' } }],
|
|
363
363
|
},
|
|
364
364
|
{
|
|
365
365
|
name: 'Sys',
|
package/gs/io/fs/stat.test.ts
CHANGED
package/gs/io/fs/sub.test.ts
CHANGED
package/gs/io/fs/walk.test.ts
CHANGED
package/gs/io/io.test.ts
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import * as $ from '@goscript/builtin/index.js'
|
|
2
2
|
import {
|
|
3
|
+
EOF,
|
|
3
4
|
LimitedReader,
|
|
4
5
|
MultiWriter,
|
|
5
6
|
NewSectionReader,
|
|
6
7
|
NopCloser,
|
|
7
8
|
Pipe,
|
|
9
|
+
ReadAll,
|
|
8
10
|
TeeReader,
|
|
9
11
|
} from './index.js'
|
|
10
12
|
import { describe, expect, test } from 'vitest'
|
|
@@ -32,8 +34,8 @@ class captureWriter {
|
|
|
32
34
|
class syncReaderAt {
|
|
33
35
|
constructor(private data: Uint8Array) {}
|
|
34
36
|
|
|
35
|
-
ReadAt(p: $.Bytes, off:
|
|
36
|
-
const n = $.copy(p, this.data.subarray(off))
|
|
37
|
+
ReadAt(p: $.Bytes, off: bigint): [number, $.GoError] {
|
|
38
|
+
const n = $.copy(p, this.data.subarray(Number(off)))
|
|
37
39
|
return [n, n < $.len(p) ? (new Error('EOF') as $.GoError) : null]
|
|
38
40
|
}
|
|
39
41
|
}
|
|
@@ -42,7 +44,7 @@ describe('io override', () => {
|
|
|
42
44
|
test('LimitedReader accepts generated struct-literal construction', async () => {
|
|
43
45
|
const reader = new LimitedReader({
|
|
44
46
|
R: new sliceReader($.stringToBytes('abcdef')),
|
|
45
|
-
N:
|
|
47
|
+
N: 3n,
|
|
46
48
|
})
|
|
47
49
|
const buf = new Uint8Array(8)
|
|
48
50
|
|
|
@@ -154,10 +156,10 @@ describe('io override', () => {
|
|
|
154
156
|
test('SectionReader awaits async ReaderAt', async () => {
|
|
155
157
|
const reader = NewSectionReader(
|
|
156
158
|
{
|
|
157
|
-
async ReadAt(p: $.Bytes, off:
|
|
159
|
+
async ReadAt(p: $.Bytes, off: bigint): Promise<[number, $.GoError]> {
|
|
158
160
|
await Promise.resolve()
|
|
159
161
|
const data = $.stringToBytes('abcdef')
|
|
160
|
-
const n = $.copy(p, data.subarray(off))
|
|
162
|
+
const n = $.copy(p, data.subarray(Number(off)))
|
|
161
163
|
return [n, n < $.len(p) ? (new Error('EOF') as $.GoError) : null]
|
|
162
164
|
},
|
|
163
165
|
} as any,
|
|
@@ -187,4 +189,44 @@ describe('io override', () => {
|
|
|
187
189
|
expect(readBytes).toBe(5)
|
|
188
190
|
expect(Buffer.from(buf).toString('utf8')).toBe('later')
|
|
189
191
|
})
|
|
192
|
+
|
|
193
|
+
test('ReadAll returns the bytes already read with a non-EOF error', async () => {
|
|
194
|
+
const boom = new Error('boom') as $.GoError
|
|
195
|
+
let called = false
|
|
196
|
+
const reader = {
|
|
197
|
+
Read(p: $.Bytes): [number, $.GoError] {
|
|
198
|
+
if (called) {
|
|
199
|
+
return [0, EOF]
|
|
200
|
+
}
|
|
201
|
+
called = true
|
|
202
|
+
const data = $.stringToBytes('hello')
|
|
203
|
+
p!.set(data, 0)
|
|
204
|
+
return [$.len(data), boom]
|
|
205
|
+
},
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const [data, err] = await ReadAll(reader)
|
|
209
|
+
expect(err).toBe(boom)
|
|
210
|
+
expect($.bytesToString(data)).toBe('hello')
|
|
211
|
+
})
|
|
212
|
+
|
|
213
|
+
test('ReadAll reports EOF as a nil error', async () => {
|
|
214
|
+
const data = $.stringToBytes('world')
|
|
215
|
+
let offset = 0
|
|
216
|
+
const reader = {
|
|
217
|
+
Read(p: $.Bytes): [number, $.GoError] {
|
|
218
|
+
if (offset >= $.len(data)) {
|
|
219
|
+
return [0, EOF]
|
|
220
|
+
}
|
|
221
|
+
const n = Math.min($.len(p), $.len(data) - offset)
|
|
222
|
+
p!.set((data as Uint8Array).subarray(offset, offset + n), 0)
|
|
223
|
+
offset += n
|
|
224
|
+
return [n, null]
|
|
225
|
+
},
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
const [out, err] = await ReadAll(reader)
|
|
229
|
+
expect(err).toBeNull()
|
|
230
|
+
expect($.bytesToString(out)).toBe('world')
|
|
231
|
+
})
|
|
190
232
|
})
|