-
-
Save marco6/9c63524da9738ce2c61fb7457ff0bc84 to your computer and use it in GitHub Desktop.
Listing for a faster reflection interpreter
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package fast_reflection | |
import ( | |
"fmt" | |
"reflect" | |
"unsafe" | |
) | |
func writeBoolUnsafe(writer BinaryWriter, pVal unsafe.Pointer) { | |
value := *(*bool)(pVal) | |
writer.WriteBool(value) | |
} | |
func writeInt8Unsafe(writer BinaryWriter, pVal unsafe.Pointer) { | |
value := *(*int8)(pVal) | |
writer.WriteInt8(value) | |
} | |
func writeInt16Unsafe(writer BinaryWriter, pVal unsafe.Pointer) { | |
value := *(*int16)(pVal) | |
writer.WriteInt(int64(value)) | |
} | |
func writeInt32Unsafe(writer BinaryWriter, pVal unsafe.Pointer) { | |
value := *(*int32)(pVal) | |
writer.WriteInt(int64(value)) | |
} | |
func writeInt64Unsafe(writer BinaryWriter, pVal unsafe.Pointer) { | |
value := *(*int64)(pVal) | |
writer.WriteInt(value) | |
} | |
func writeIntUnsafe(writer BinaryWriter, pVal unsafe.Pointer) { | |
value := *(*int)(pVal) | |
writer.WriteInt(int64(value)) | |
} | |
func writeUint8Unsafe(writer BinaryWriter, pVal unsafe.Pointer) { | |
value := *(*uint8)(pVal) | |
writer.WriteUint8(value) | |
} | |
func writeUint16Unsafe(writer BinaryWriter, pVal unsafe.Pointer) { | |
value := *(*uint16)(pVal) | |
writer.WriteUint(uint64(value)) | |
} | |
func writeUint32Unsafe(writer BinaryWriter, pVal unsafe.Pointer) { | |
value := *(*uint32)(pVal) | |
writer.WriteUint(uint64(value)) | |
} | |
func writeUint64Unsafe(writer BinaryWriter, pVal unsafe.Pointer) { | |
value := *(*uint64)(pVal) | |
writer.WriteUint(uint64(value)) | |
} | |
func writeUintUnsafe(writer BinaryWriter, pVal unsafe.Pointer) { | |
value := *(*uint)(pVal) | |
writer.WriteUint(uint64(value)) | |
} | |
func writeFloat32Unsafe(writer BinaryWriter, pVal unsafe.Pointer) { | |
value := *(*float32)(pVal) | |
writer.WriteFloat32(value) | |
} | |
func writeFloat64Unsafe(writer BinaryWriter, pVal unsafe.Pointer) { | |
value := *(*float64)(pVal) | |
writer.WriteFloat64(value) | |
} | |
func writeComplex64Unsafe(writer BinaryWriter, pVal unsafe.Pointer) { | |
value := *(*complex64)(pVal) | |
writer.WriteFloat32(real(value)) | |
writer.WriteFloat32(imag(value)) | |
} | |
func writeComplex128Unsafe(writer BinaryWriter, pVal unsafe.Pointer) { | |
value := *(*complex128)(pVal) | |
writer.WriteFloat64(real(value)) | |
writer.WriteFloat64(imag(value)) | |
} | |
func writeStringUnsafe(writer BinaryWriter, pVal unsafe.Pointer) { | |
value := *(*string)(pVal) | |
writer.WriteString(value) | |
} | |
func writeMapUnsafe(writer BinaryWriter, pVal unsafe.Pointer, typ reflect.Type) { | |
if pVal == nil { | |
writer.WriteInt(0) | |
} else { | |
m := reflect.NewAt(typ, pVal).Elem() | |
writer.WriteInt(int64(m.Len())) | |
for iter := m.MapRange(); iter.Next(); { | |
serializeValue(writer, iter.Key()) | |
serializeValue(writer, iter.Value()) | |
} | |
} | |
} | |
type opcode int | |
const ( | |
invalid opcode = iota | |
writeBool // arg: offset | |
writeInt8 // arg: offset | |
writeInt16 // arg: offset | |
writeInt32 // arg: offset | |
writeInt64 // arg: offset | |
writeInt // arg: offset | |
writeUint8 // arg: offset | |
writeUint16 // arg: offset | |
writeUint32 // arg: offset | |
writeUint64 // arg: offset | |
writeUint // arg: offset | |
writeFloat32 // arg: offset | |
writeFloat64 // arg: offset | |
writeComplex64 // arg: offset | |
writeComplex128 // arg: offset | |
writeString // arg: offset | |
writeSlice // args: offset + jump size | |
writeMap // arg: offset | |
writePointer // arg: offset | |
) | |
func runSerializerCode(writer BinaryWriter, pVal unsafe.Pointer, code []int, mapTypes []reflect.Type) { | |
pc := 0 // program counter | |
for pc < len(code) { | |
op := opcode(code[pc]) | |
pc++ | |
offset := uintptr(code[pc]) | |
pc++ | |
fieldPtr := unsafe.Pointer(uintptr(pVal) + offset) | |
switch op { | |
case writeBool: | |
writeBoolUnsafe(writer, fieldPtr) | |
case writeInt8: | |
writeInt8Unsafe(writer, fieldPtr) | |
case writeInt16: | |
writeInt16Unsafe(writer, fieldPtr) | |
case writeInt32: | |
writeInt32Unsafe(writer, fieldPtr) | |
case writeInt64: | |
writeInt64Unsafe(writer, fieldPtr) | |
case writeInt: | |
writeIntUnsafe(writer, fieldPtr) | |
case writeUint8: | |
writeUint8Unsafe(writer, fieldPtr) | |
case writeUint16: | |
writeUint16Unsafe(writer, fieldPtr) | |
case writeUint32: | |
writeUint32Unsafe(writer, fieldPtr) | |
case writeUint64: | |
writeUint64Unsafe(writer, fieldPtr) | |
case writeUint: | |
writeUintUnsafe(writer, fieldPtr) | |
case writeFloat32: | |
writeFloat32Unsafe(writer, fieldPtr) | |
case writeFloat64: | |
writeFloat64Unsafe(writer, fieldPtr) | |
case writeComplex64: | |
writeComplex64Unsafe(writer, fieldPtr) | |
case writeComplex128: | |
writeComplex128Unsafe(writer, fieldPtr) | |
case writeString: | |
writeStringUnsafe(writer, fieldPtr) | |
case writePointer: | |
subroutine := code[pc] | |
pc++ | |
value := *(*unsafe.Pointer)(fieldPtr) | |
if value == nil { | |
writer.WriteBool(false) | |
} else { | |
writer.WriteBool(true) | |
runSerializerCode(writer, value, code[pc:(pc+subroutine)], mapTypes) | |
} | |
pc += int(subroutine) | |
case writeSlice: | |
itemSize := uintptr(code[pc]) | |
pc++ | |
subroutine := code[pc] | |
pc++ | |
slice := (*reflect.SliceHeader)(fieldPtr) | |
writer.WriteInt(int64(slice.Len)) | |
subcode := code[pc:(pc + subroutine)] | |
for i := 0; i < slice.Len; i++ { | |
runSerializerCode(writer, unsafe.Pointer(slice.Data+itemSize*uintptr(i)), subcode, mapTypes) | |
} | |
pc += subroutine | |
case writeMap: | |
mapTypIndex := code[pc] | |
pc++ | |
writeMapUnsafe(writer, fieldPtr, mapTypes[mapTypIndex]) | |
} | |
} | |
} | |
func makeSerializerCode(typ reflect.Type, offset uintptr, code *[]int, mapTyps map[reflect.Type]int) error { | |
switch typ.Kind() { | |
case reflect.Bool: | |
*code = append(*code, int(writeBool)) | |
*code = append(*code, int(offset)) | |
case reflect.Int8: | |
*code = append(*code, int(writeInt8)) | |
*code = append(*code, int(offset)) | |
case reflect.Int16: | |
*code = append(*code, int(writeInt16)) | |
*code = append(*code, int(offset)) | |
case reflect.Int32: | |
*code = append(*code, int(writeInt32)) | |
*code = append(*code, int(offset)) | |
case reflect.Int64: | |
*code = append(*code, int(writeInt64)) | |
*code = append(*code, int(offset)) | |
case reflect.Int: | |
*code = append(*code, int(writeInt)) | |
*code = append(*code, int(offset)) | |
case reflect.Uint8: | |
*code = append(*code, int(writeUint8)) | |
*code = append(*code, int(offset)) | |
case reflect.Uint16: | |
*code = append(*code, int(writeUint16)) | |
*code = append(*code, int(offset)) | |
case reflect.Uint32: | |
*code = append(*code, int(writeUint32)) | |
*code = append(*code, int(offset)) | |
case reflect.Uint64: | |
*code = append(*code, int(writeUint64)) | |
*code = append(*code, int(offset)) | |
case reflect.Uint: | |
*code = append(*code, int(writeUint)) | |
*code = append(*code, int(offset)) | |
case reflect.Float32: | |
*code = append(*code, int(writeFloat32)) | |
*code = append(*code, int(offset)) | |
case reflect.Float64: | |
*code = append(*code, int(writeFloat64)) | |
*code = append(*code, int(offset)) | |
case reflect.Complex64: | |
*code = append(*code, int(writeComplex64)) | |
*code = append(*code, int(offset)) | |
case reflect.Complex128: | |
*code = append(*code, int(writeComplex128)) | |
*code = append(*code, int(offset)) | |
case reflect.String: | |
*code = append(*code, int(writeString)) | |
*code = append(*code, int(offset)) | |
case reflect.Array: | |
elemTyp := typ.Elem() | |
elemSize := typ.Elem().Size() | |
len := typ.Len() | |
for i := 0; i < len; i++ { | |
makeSerializerCode(elemTyp, offset+elemSize*uintptr(i), code, mapTyps) | |
} | |
case reflect.Slice: | |
elemTyp := typ.Elem() | |
elemSize := typ.Elem().Size() | |
*code = append(*code, int(writeSlice)) | |
*code = append(*code, int(offset)) | |
*code = append(*code, int(elemSize)) | |
subroutine := []int{} | |
makeSerializerCode(elemTyp, 0, &subroutine, mapTyps) | |
*code = append(*code, len(subroutine)) | |
*code = append(*code, subroutine...) | |
case reflect.Map: | |
*code = append(*code, int(writeMap)) | |
*code = append(*code, int(offset)) | |
var typIndex int | |
if i, ok := mapTyps[typ]; ok { | |
typIndex = i | |
} else { | |
typIndex = len(mapTyps) | |
mapTyps[typ] = typIndex | |
} | |
*code = append(*code, int(typIndex)) | |
case reflect.Pointer: | |
elemTyp := typ.Elem() | |
*code = append(*code, int(writePointer)) | |
*code = append(*code, int(offset)) | |
subroutine := []int{} | |
makeSerializerCode(elemTyp, 0, &subroutine, mapTyps) | |
*code = append(*code, int(len(subroutine))) | |
*code = append(*code, subroutine...) | |
case reflect.Struct: | |
for i := 0; i < typ.NumField(); i++ { | |
field := typ.Field(i) | |
makeSerializerCode(field.Type, offset+field.Offset, code, mapTyps) | |
} | |
case reflect.Uintptr, reflect.Chan, reflect.Func, reflect.Interface, reflect.UnsafePointer: | |
return fmt.Errorf("Unsupported kind %s", typ.Kind()) | |
default: | |
panic("can't be here") | |
} | |
return nil | |
} | |
func MakeSerializer[T any]() (func(BinaryWriter, *T) error, error) { | |
code := make([]int, 0, 2) | |
typsMap := make(map[reflect.Type]int) | |
var _t T | |
makeSerializerCode(reflect.TypeOf(_t), 0, &code, typsMap) | |
typs := make([]reflect.Type, len(typsMap)) | |
for k, v := range typsMap { | |
typs[v] = k | |
} | |
return func(writer BinaryWriter, val *T) error { | |
if val == nil { | |
return fmt.Errorf("can't serialize nil") | |
} | |
runSerializerCode(writer, unsafe.Pointer(val), code, typs) | |
return nil | |
}, nil | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment