Browse Source

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.
pull/919/merge
Jaden Weiss 5 years ago
committed by Ayke
parent
commit
473644d918
  1. 2
      compiler/compiler.go
  2. 126
      src/internal/bytealg/bytealg.go
  3. 11
      src/runtime/bytes.go
  4. 12
      src/runtime/string.go
  5. 23
      src/runtime/string_count.go
  6. 4
      src/runtime/strings_go111.go

2
compiler/compiler.go

@ -180,7 +180,7 @@ func Compile(pkgName string, machine llvm.TargetMachine, config *compileopts.Con
path = path[len(tinygoPath+"/src/"):]
}
switch path {
case "machine", "os", "reflect", "runtime", "runtime/interrupt", "runtime/volatile", "sync", "testing", "internal/reflectlite", "internal/task":
case "machine", "os", "reflect", "runtime", "runtime/interrupt", "runtime/volatile", "sync", "testing", "internal/reflectlite", "internal/bytealg", "internal/task":
return path
default:
if strings.HasPrefix(path, "device/") || strings.HasPrefix(path, "examples/") {

126
src/internal/bytealg/bytealg.go

@ -0,0 +1,126 @@
package bytealg
const (
// Index can search any valid length of string.
MaxLen = int(-1) >> 31
MaxBruteForce = MaxLen
)
// Compare two byte slices.
// Returns -1 if the first differing byte is lower in a, or 1 if the first differing byte is greater in b.
// If the byte slices are equal, returns 0.
// If the lengths are different and there are no differing bytes, compares based on length.
func Compare(a, b []byte) int {
// Compare for differing bytes.
for i := 0; i < len(a) && i < len(b); i++ {
switch {
case a[0] < b[0]:
return -1
case a[0] > b[0]:
return 1
}
}
// Compare lengths.
switch {
case len(a) > len(b):
return 1
case len(a) < len(b):
return -1
default:
return 0
}
}
// Count the number of instances of a byte in a slice.
func Count(b []byte, c byte) int {
// Use a simple implementation, as there is no intrinsic that does this like we want.
n := 0
for _, v := range b {
if v == c {
n++
}
}
return n
}
// Count the number of instances of a byte in a string.
func CountString(s string, c byte) int {
// Use a simple implementation, as there is no intrinsic that does this like we want.
// Currently, the compiler does not generate zero-copy byte-string conversions, so this needs to be seperate from Count.
n := 0
for i := 0; i < len(s); i++ {
if s[i] == c {
n++
}
}
return n
}
// Cutover is not reachable in TinyGo, but must exist as it is referenced.
func Cutover(n int) int {
// Setting MaxLen and MaxBruteForce should force a different path to be taken.
// This should never be called.
panic("cutover is unreachable")
}
// Equal checks if two byte slices are equal.
// It is equivalent to bytes.Equal.
func Equal(a, b []byte) bool {
if len(a) != len(b) {
return false
}
for i, v := range a {
if v != b[i] {
return false
}
}
return true
}
// Index finds the base index of the first instance of the byte sequence b in a.
// If a does not contain b, this returns -1.
func Index(a, b []byte) int {
for i := 0; i <= len(a)-len(b); i++ {
if Equal(a[i:i+len(b)], b) {
return i
}
}
return -1
}
// Index finds the index of the first instance of the specified byte in the slice.
// If the byte is not found, this returns -1.
func IndexByte(b []byte, c byte) int {
for i, v := range b {
if v == c {
return i
}
}
return -1
}
// Index finds the index of the first instance of the specified byte in the string.
// If the byte is not found, this returns -1.
func IndexByteString(s string, c byte) int {
for i := 0; i < len(s); i++ {
if s[i] == c {
return i
}
}
return -1
}
// Index finds the base index of the first instance of a substring in a string.
// If the substring is not found, this returns -1.
func IndexString(str, sub string) int {
for i := 0; i <= len(str)-len(sub); i++ {
if str[i:i+len(sub)] == sub {
return i
}
}
return -1
}

11
src/runtime/bytes.go

@ -1,11 +0,0 @@
package runtime
//go:linkname indexBytePortable internal/bytealg.IndexByte
func indexBytePortable(s []byte, c byte) int {
for i, b := range s {
if b == c {
return i
}
}
return -1
}

12
src/runtime/string.go

@ -204,15 +204,3 @@ func decodeUTF8(s string, index uintptr) (rune, uintptr) {
return 0xfffd, 1
}
}
// indexByteString returns the index of the first instance of c in s, or -1 if c
// is not present in s.
//go:linkname indexByteString internal/bytealg.IndexByteString
func indexByteString(s string, c byte) int {
for i := 0; i < len(s); i++ {
if s[i] == c {
return i
}
}
return -1
}

23
src/runtime/string_count.go

@ -1,23 +0,0 @@
// +build amd64 arm,go1.13 arm64 ppc64le ppc64 s390x
package runtime
// This file implements the string counting functions used by the strings
// package, for example. It must be reimplemented here as a replacement for the
// Go stdlib asm implementations, but only when the asm implementations are used
// (this varies by Go version).
// Track this file for updates:
// https://github.com/golang/go/blob/master/src/internal/bytealg/count_native.go
// countString copies the implementation from
// https://github.com/golang/go/blob/67f181bfd84dfd5942fe9a29d8a20c9ce5eb2fea/src/internal/bytealg/count_generic.go#L1
//go:linkname countString internal/bytealg.CountString
func countString(s string, c byte) int {
n := 0
for i := 0; i < len(s); i++ {
if s[i] == c {
n++
}
}
return n
}

4
src/runtime/strings_go111.go

@ -2,11 +2,13 @@
package runtime
import "internal/bytealg"
// indexByte provides compatibility with Go 1.11.
// See the following:
// https://github.com/tinygo-org/tinygo/issues/351
// https://github.com/golang/go/commit/ad4a58e31501bce5de2aad90a620eaecdc1eecb8
//go:linkname indexByte strings.IndexByte
func indexByte(s string, c byte) int {
return indexByteString(s, c)
return bytealg.IndexByteString(s, c)
}

Loading…
Cancel
Save