Skip to content

Commit 473644d

Browse files
niaowaykevl
authored andcommitted
internal/bytealg: reimplement bytealg in pure Go
Previously, we implemented individual bytealg functions via linknaming, and had to update them every once in a while when we hit linker errors. Instead, this change reimplements the bytealg package in pure Go. If something is missing, it will cause a compiler error rather than a linker error. This is easier to test and maintain.
1 parent 38fc340 commit 473644d

File tree

6 files changed

+130
-48
lines changed

6 files changed

+130
-48
lines changed

compiler/compiler.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ func Compile(pkgName string, machine llvm.TargetMachine, config *compileopts.Con
180180
path = path[len(tinygoPath+"/src/"):]
181181
}
182182
switch path {
183-
case "machine", "os", "reflect", "runtime", "runtime/interrupt", "runtime/volatile", "sync", "testing", "internal/reflectlite", "internal/task":
183+
case "machine", "os", "reflect", "runtime", "runtime/interrupt", "runtime/volatile", "sync", "testing", "internal/reflectlite", "internal/bytealg", "internal/task":
184184
return path
185185
default:
186186
if strings.HasPrefix(path, "device/") || strings.HasPrefix(path, "examples/") {

src/internal/bytealg/bytealg.go

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
package bytealg
2+
3+
const (
4+
// Index can search any valid length of string.
5+
6+
MaxLen = int(-1) >> 31
7+
MaxBruteForce = MaxLen
8+
)
9+
10+
// Compare two byte slices.
11+
// Returns -1 if the first differing byte is lower in a, or 1 if the first differing byte is greater in b.
12+
// If the byte slices are equal, returns 0.
13+
// If the lengths are different and there are no differing bytes, compares based on length.
14+
func Compare(a, b []byte) int {
15+
// Compare for differing bytes.
16+
for i := 0; i < len(a) && i < len(b); i++ {
17+
switch {
18+
case a[0] < b[0]:
19+
return -1
20+
case a[0] > b[0]:
21+
return 1
22+
}
23+
}
24+
25+
// Compare lengths.
26+
switch {
27+
case len(a) > len(b):
28+
return 1
29+
case len(a) < len(b):
30+
return -1
31+
default:
32+
return 0
33+
}
34+
}
35+
36+
// Count the number of instances of a byte in a slice.
37+
func Count(b []byte, c byte) int {
38+
// Use a simple implementation, as there is no intrinsic that does this like we want.
39+
n := 0
40+
for _, v := range b {
41+
if v == c {
42+
n++
43+
}
44+
}
45+
return n
46+
}
47+
48+
// Count the number of instances of a byte in a string.
49+
func CountString(s string, c byte) int {
50+
// Use a simple implementation, as there is no intrinsic that does this like we want.
51+
// Currently, the compiler does not generate zero-copy byte-string conversions, so this needs to be seperate from Count.
52+
n := 0
53+
for i := 0; i < len(s); i++ {
54+
if s[i] == c {
55+
n++
56+
}
57+
}
58+
return n
59+
}
60+
61+
// Cutover is not reachable in TinyGo, but must exist as it is referenced.
62+
func Cutover(n int) int {
63+
// Setting MaxLen and MaxBruteForce should force a different path to be taken.
64+
// This should never be called.
65+
panic("cutover is unreachable")
66+
}
67+
68+
// Equal checks if two byte slices are equal.
69+
// It is equivalent to bytes.Equal.
70+
func Equal(a, b []byte) bool {
71+
if len(a) != len(b) {
72+
return false
73+
}
74+
75+
for i, v := range a {
76+
if v != b[i] {
77+
return false
78+
}
79+
}
80+
81+
return true
82+
}
83+
84+
// Index finds the base index of the first instance of the byte sequence b in a.
85+
// If a does not contain b, this returns -1.
86+
func Index(a, b []byte) int {
87+
for i := 0; i <= len(a)-len(b); i++ {
88+
if Equal(a[i:i+len(b)], b) {
89+
return i
90+
}
91+
}
92+
return -1
93+
}
94+
95+
// Index finds the index of the first instance of the specified byte in the slice.
96+
// If the byte is not found, this returns -1.
97+
func IndexByte(b []byte, c byte) int {
98+
for i, v := range b {
99+
if v == c {
100+
return i
101+
}
102+
}
103+
return -1
104+
}
105+
106+
// Index finds the index of the first instance of the specified byte in the string.
107+
// If the byte is not found, this returns -1.
108+
func IndexByteString(s string, c byte) int {
109+
for i := 0; i < len(s); i++ {
110+
if s[i] == c {
111+
return i
112+
}
113+
}
114+
return -1
115+
}
116+
117+
// Index finds the base index of the first instance of a substring in a string.
118+
// If the substring is not found, this returns -1.
119+
func IndexString(str, sub string) int {
120+
for i := 0; i <= len(str)-len(sub); i++ {
121+
if str[i:i+len(sub)] == sub {
122+
return i
123+
}
124+
}
125+
return -1
126+
}

src/runtime/bytes.go

Lines changed: 0 additions & 11 deletions
This file was deleted.

src/runtime/string.go

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -204,15 +204,3 @@ func decodeUTF8(s string, index uintptr) (rune, uintptr) {
204204
return 0xfffd, 1
205205
}
206206
}
207-
208-
// indexByteString returns the index of the first instance of c in s, or -1 if c
209-
// is not present in s.
210-
//go:linkname indexByteString internal/bytealg.IndexByteString
211-
func indexByteString(s string, c byte) int {
212-
for i := 0; i < len(s); i++ {
213-
if s[i] == c {
214-
return i
215-
}
216-
}
217-
return -1
218-
}

src/runtime/string_count.go

Lines changed: 0 additions & 23 deletions
This file was deleted.

src/runtime/strings_go111.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,13 @@
22

33
package runtime
44

5+
import "internal/bytealg"
6+
57
// indexByte provides compatibility with Go 1.11.
68
// See the following:
79
// https://github.com/tinygo-org/tinygo/issues/351
810
// https://github.com/golang/go/commit/ad4a58e31501bce5de2aad90a620eaecdc1eecb8
911
//go:linkname indexByte strings.IndexByte
1012
func indexByte(s string, c byte) int {
11-
return indexByteString(s, c)
13+
return bytealg.IndexByteString(s, c)
1214
}

0 commit comments

Comments
 (0)