Skip to content

Instantly share code, notes, and snippets.

@marco6
Created January 26, 2023 11:32
Show Gist options
  • Save marco6/9c63524da9738ce2c61fb7457ff0bc84 to your computer and use it in GitHub Desktop.
Save marco6/9c63524da9738ce2c61fb7457ff0bc84 to your computer and use it in GitHub Desktop.
Listing for a faster reflection interpreter
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