Skip to content

Commit d97dfbf

Browse files
committed
internal/reflectlite: do not use the reflect package
This avoids a dependency on the reflect package. I've tried to deduplicate as much as reasonably possible, while keeping the code clean. Especially some tricky methods for the reflect.Type type have a shared implementation (using //go:linkname). In other cases, I've simply used the same type using a type alias, in the other direction (the reflect package now imports the internal/reflectlite package).
1 parent 04c7057 commit d97dfbf

File tree

11 files changed

+782
-372
lines changed

11 files changed

+782
-372
lines changed
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
//go:build mips
2+
3+
package reflectlite
4+
5+
import "unsafe"
6+
7+
// loadValue loads a value that may or may not be word-aligned. The number of
8+
// bytes given in size are loaded. The biggest possible size it can load is that
9+
// of an uintptr.
10+
func loadValue(ptr unsafe.Pointer, size uintptr) uintptr {
11+
loadedValue := uintptr(0)
12+
for i := uintptr(0); i < size; i++ {
13+
loadedValue <<= 8
14+
loadedValue |= uintptr(*(*byte)(ptr))
15+
ptr = unsafe.Add(ptr, 1)
16+
}
17+
return loadedValue
18+
}
19+
20+
// storeValue is the inverse of loadValue. It stores a value to a pointer that
21+
// doesn't need to be aligned.
22+
func storeValue(ptr unsafe.Pointer, size, value uintptr) {
23+
// This could perhaps be optimized using bits.ReverseBytes32 if needed.
24+
value <<= (unsafe.Sizeof(uintptr(0)) - size) * 8
25+
for i := uintptr(0); i < size; i++ {
26+
*(*byte)(ptr) = byte(value >> ((unsafe.Sizeof(uintptr(0)) - 1) * 8))
27+
ptr = unsafe.Add(ptr, 1)
28+
value <<= 8
29+
}
30+
}
31+
32+
// maskAndShift cuts out a part of a uintptr. Note that the offset may not be 0.
33+
func maskAndShift(value, offset, size uintptr) uintptr {
34+
mask := ^uintptr(0) >> ((unsafe.Sizeof(uintptr(0)) - size) * 8)
35+
return (uintptr(value) >> ((unsafe.Sizeof(uintptr(0)) - offset - size) * 8)) & mask
36+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
//go:build !mips
2+
3+
package reflectlite
4+
5+
import "unsafe"
6+
7+
// loadValue loads a value that may or may not be word-aligned. The number of
8+
// bytes given in size are loaded. The biggest possible size it can load is that
9+
// of an uintptr.
10+
func loadValue(ptr unsafe.Pointer, size uintptr) uintptr {
11+
loadedValue := uintptr(0)
12+
shift := uintptr(0)
13+
for i := uintptr(0); i < size; i++ {
14+
loadedValue |= uintptr(*(*byte)(ptr)) << shift
15+
shift += 8
16+
ptr = unsafe.Add(ptr, 1)
17+
}
18+
return loadedValue
19+
}
20+
21+
// storeValue is the inverse of loadValue. It stores a value to a pointer that
22+
// doesn't need to be aligned.
23+
func storeValue(ptr unsafe.Pointer, size, value uintptr) {
24+
for i := uintptr(0); i < size; i++ {
25+
*(*byte)(ptr) = byte(value)
26+
ptr = unsafe.Add(ptr, 1)
27+
value >>= 8
28+
}
29+
}
30+
31+
// maskAndShift cuts out a part of a uintptr. Note that the offset may not be 0.
32+
func maskAndShift(value, offset, size uintptr) uintptr {
33+
mask := ^uintptr(0) >> ((unsafe.Sizeof(uintptr(0)) - size) * 8)
34+
return (uintptr(value) >> (offset * 8)) & mask
35+
}

src/internal/reflectlite/error.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package reflectlite
2+
3+
type TypeError struct {
4+
Method string
5+
}
6+
7+
func (e *TypeError) Error() string {
8+
return "reflect: call of reflect.Type." + e.Method + " on invalid type"
9+
}
10+
11+
var (
12+
errTypeElem = &TypeError{"Elem"}
13+
)
14+
15+
type ValueError struct {
16+
Method string
17+
Kind Kind
18+
}
19+
20+
func (e *ValueError) Error() string {
21+
if e.Kind == 0 {
22+
return "reflect: call of " + e.Method + " on zero Value"
23+
}
24+
return "reflect: call of " + e.Method + " on " + e.Kind.String() + " Value"
25+
}

src/internal/reflectlite/reflect.go

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

src/internal/reflectlite/swapper.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package reflectlite
2+
3+
import "unsafe"
4+
5+
// Some of code here has been copied from the Go sources:
6+
// https://github.com/golang/go/blob/go1.15.2/src/reflect/swapper.go
7+
// It has the following copyright note:
8+
//
9+
// Copyright 2016 The Go Authors. All rights reserved.
10+
// Use of this source code is governed by a BSD-style
11+
// license that can be found in the LICENSE file.
12+
13+
func Swapper(slice interface{}) func(i, j int) {
14+
v := ValueOf(slice)
15+
if v.Kind() != Slice {
16+
panic(&ValueError{Method: "Swapper"})
17+
}
18+
19+
// Just return Nop func if nothing to swap.
20+
if v.Len() < 2 {
21+
return func(i, j int) {}
22+
}
23+
24+
typ := v.typecode.Elem()
25+
size := typ.Size()
26+
27+
header := (*sliceHeader)(v.value)
28+
tmp := unsafe.Pointer(&make([]byte, size)[0])
29+
30+
return func(i, j int) {
31+
if uint(i) >= uint(header.len) || uint(j) >= uint(header.len) {
32+
panic("reflect: slice index out of range")
33+
}
34+
val1 := unsafe.Add(header.data, uintptr(i)*size)
35+
val2 := unsafe.Add(header.data, uintptr(j)*size)
36+
memcpy(tmp, val1, size)
37+
memcpy(val1, val2, size)
38+
memcpy(val2, tmp, size)
39+
}
40+
}

0 commit comments

Comments
 (0)