# README
go-reflect
Zero-allocation reflection library for Go
Features
- 100% Compatibility APIs with
reflect
library - No allocation occurs when using the reflect.Type features
- You can choose to escape (
reflect.ValueOf
) or noescape (reflect.ValueNoEscapeOf
) when creating reflect.Value
Status
All the tests in the reflect library have been passed except the tests that use some private functions.
Installation
go get github.com/goccy/go-reflect
How to use
Replace import statement from reflect
to github.com/goccy/go-reflect
-import "reflect"
+import "github.com/goccy/go-reflect"
Benchmarks
Source https://github.com/goccy/go-reflect/blob/master/benchmark_test.go
Benchmark about reflect.Type
$ go test -bench TypeOf
goos: darwin
goarch: amd64
pkg: github.com/goccy/go-reflect
Benchmark_TypeOf_Reflect-12 100000000 13.8 ns/op 8 B/op 1 allocs/op
Benchmark_TypeOf_GoReflect-12 2000000000 1.70 ns/op 0 B/op 0 allocs/op
PASS
ok github.com/goccy/go-reflect 5.369s
Benchmark about reflect.Value
$ go test -bench ValueOf
goos: darwin
goarch: amd64
pkg: github.com/goccy/go-reflect
Benchmark_ValueOf_Reflect-12 100000000 13.0 ns/op 8 B/op 1 allocs/op
Benchmark_ValueOf_GoReflect-12 300000000 4.64 ns/op 0 B/op 0 allocs/op
PASS
ok github.com/goccy/go-reflect 3.578s
Real World Example
Implements Fast Marshaler
I would like to introduce the technique I use for github.com/goccy/go-json.
Using this technique, allocation can be suppressed to once for any marshaler.
Original Source is https://github.com/goccy/go-reflect/blob/master/benchmark_marshaler_test.go
package reflect_test
import (
"errors"
"strconv"
"sync"
"testing"
"unsafe"
"github.com/goccy/go-reflect"
)
var (
typeToEncoderMap sync.Map
bufpool = sync.Pool{
New: func() interface{} {
return &buffer{
b: make([]byte, 0, 1024),
}
},
}
)
type buffer struct {
b []byte
}
type encoder func(*buffer, unsafe.Pointer) error
func Marshal(v interface{}) ([]byte, error) {
// Technique 1.
// Get type information and pointer from interface{} value without allocation.
typ, ptr := reflect.TypeAndPtrOf(v)
typeID := reflect.TypeID(typ)
// Technique 2.
// Reuse the buffer once allocated using sync.Pool
buf := bufpool.Get().(*buffer)
buf.b = buf.b[:0]
defer bufpool.Put(buf)
// Technique 3.
// builds a optimized path by typeID and caches it
if enc, ok := typeToEncoderMap.Load(typeID); ok {
if err := enc.(encoder)(buf, ptr); err != nil {
return nil, err
}
// allocate a new buffer required length only
b := make([]byte, len(buf.b))
copy(b, buf.b)
return b, nil
}
// First time,
// builds a optimized path by type and caches it with typeID.
enc, err := compile(typ)
if err != nil {
return nil, err
}
typeToEncoderMap.Store(typeID, enc)
if err := enc(buf, ptr); err != nil {
return nil, err
}
// allocate a new buffer required length only
b := make([]byte, len(buf.b))
copy(b, buf.b)
return b, nil
}
func compile(typ reflect.Type) (encoder, error) {
switch typ.Kind() {
case reflect.Struct:
return compileStruct(typ)
case reflect.Int:
return compileInt(typ)
}
return nil, errors.New("unsupported type")
}
func compileStruct(typ reflect.Type) (encoder, error) {
encoders := []encoder{}
for i := 0; i < typ.NumField(); i++ {
field := typ.Field(i)
enc, err := compile(field.Type)
if err != nil {
return nil, err
}
offset := field.Offset
encoders = append(encoders, func(buf *buffer, p unsafe.Pointer) error {
return enc(buf, unsafe.Pointer(uintptr(p)+offset))
})
}
return func(buf *buffer, p unsafe.Pointer) error {
buf.b = append(buf.b, '{')
for _, enc := range encoders {
if err := enc(buf, p); err != nil {
return err
}
}
buf.b = append(buf.b, '}')
return nil
}, nil
}
func compileInt(typ reflect.Type) (encoder, error) {
return func(buf *buffer, p unsafe.Pointer) error {
value := *(*int)(p)
buf.b = strconv.AppendInt(buf.b, int64(value), 10)
return nil
}, nil
}
func Benchmark_Marshal(b *testing.B) {
b.ReportAllocs()
for n := 0; n < b.N; n++ {
bytes, err := Marshal(struct{ I int }{10})
if err != nil {
b.Fatal(err)
}
if string(bytes) != "{10}" {
b.Fatal("unexpected error")
}
}
}
The benchmark result is as follows.
$ go test -bench Benchmark_Marshal
goos: darwin
goarch: amd64
pkg: github.com/goccy/go-reflect
Benchmark_Marshal-16 16586372 71.0 ns/op 4 B/op 1 allocs/op
PASS
# Functions
Append appends the values x to a slice s and returns the resulting slice.
AppendSlice appends a slice t to a slice s and returns the resulting slice.
ArrayOf returns the array type with the given count and element type.
ChanOf returns the channel type with the given direction and element type.
Copy copies the contents of src into dst until either dst has been filled or src has been exhausted.
DeepEqual reports whether x and y are “deeply equal,” defined as follows.
FuncOf returns the function type with the given argument and result types.
Indirect returns the value that v points to.
MakeChan creates a new channel with the specified type and buffer size.
MakeFunc returns a new function of the given Type that wraps the function fn.
MakeMap creates a new map with the specified type.
MakeMapWithSize creates a new map with the specified type and initial space for approximately n elements.
MakeSlice creates a new zero-initialized slice value for the specified slice type, length, and capacity.
MapOf returns the map type with the given key and element types.
New returns a Value representing a pointer to a new zero value for the specified type.
NewAt returns a Value representing a pointer to a value of the specified type, using p as that pointer.
PtrTo returns the pointer type with element t.
Select executes a select operation described by the list of cases.
SliceOf returns the slice type with element type t.
StructOf returns the struct type containing fields.
No description provided by the author
ToReflectType convert Type to reflect.Type.
ToReflectValue convert Value to reflect.Value.
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
ToType convert reflect.Type to Type.
No description provided by the author
ToValue convert reflect.Value to Value.
TypeAndPtrOf returns raw Type and ptr value in favor of performance.
TypeID returns unique type identifier of v.
TypeOf returns the reflection Type that represents the dynamic type of i.
ValueNoEscapeOf no escape of ValueOf.
ValueOf returns a new Value initialized to the concrete value stored in the interface i.
Zero returns a Value representing the zero value for the specified type.
# Constants
No description provided by the author
No description provided by the author
chan.
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
<-chan.
default.
case <-Chan:.
case Chan <- Send.
chan<-.
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
# Structs
Method represents a single method.
A SelectCase describes a single case in a select operation.
A StructField describes a single field in a struct.
Value is the reflection interface to a Go value.
# Type aliases
ChanDir represents a channel type's direction.
A Kind represents the specific kind of type that a Type represents.
A MapIter is an iterator for ranging over a map.
No description provided by the author
SliceHeader is the runtime representation of a slice.
StringHeader is the runtime representation of a string.
A StructTag is the tag string in a struct field.
A ValueError occurs when a Value method is invoked on a Value that does not support it.