Files
arpack/benchmarks/flatbuffers/move_fb.go
T

175 lines
5.2 KiB
Go
Raw Normal View History

2026-03-23 12:52:30 +03:00
package benchfbs
import (
flatbuffers "github.com/google/flatbuffers/go"
)
// Vec3 mirrors the MoveMessage's Vector3 fields for FlatBuffers encoding.
type Vec3 struct {
X, Y, Z float32
}
// MoveMsg is the Go struct used in the FlatBuffers benchmark.
type MoveMsg struct {
Position Vec3
Velocity [3]float32
Waypoints []Vec3
PlayerID uint32
Active, Visible, Ghost bool
Name string
}
// vtable slot indices for MoveMessage fields (0-based slot -> vtable offset = 4 + 2*slot)
// slot 0 -> position (vtable offset 4)
// slot 1 -> velocity (vtable offset 6)
// slot 2 -> waypoints (vtable offset 8)
// slot 3 -> player_id (vtable offset 10)
// slot 4 -> active (vtable offset 12)
// slot 5 -> visible (vtable offset 14)
// slot 6 -> ghost (vtable offset 16)
// slot 7 -> name (vtable offset 18)
const (
slotPosition = 0
slotVelocity = 1
slotWaypoints = 2
slotPlayerID = 3
slotActive = 4
slotVisible = 5
slotGhost = 6
slotName = 7
)
// buildVec3 writes a Vec3 as a table with 3 float32 fields and returns its offset.
// Vec3 slots: x=0, y=1, z=2
func buildVec3(b *flatbuffers.Builder, v Vec3) flatbuffers.UOffsetT {
b.StartObject(3)
b.PrependFloat32Slot(0, v.X, 0)
b.PrependFloat32Slot(1, v.Y, 0)
b.PrependFloat32Slot(2, v.Z, 0)
return b.EndObject()
}
// Marshal serialises msg into a FlatBuffer using b and returns the finished bytes.
// The builder is reset before use, so callers can reuse it across calls.
func Marshal(b *flatbuffers.Builder, msg *MoveMsg) []byte {
b.Reset()
// 1. Build all variable-length data first (must be done outside object construction).
// name string
nameOff := b.CreateString(msg.Name)
// velocity vector: 3 × float32
b.StartVector(4, 3, 4)
for i := 2; i >= 0; i-- {
b.PrependFloat32(msg.Velocity[i])
}
velOff := b.EndVector(3)
// waypoints vector: repeated Vec3 tables (build each table first, collect offsets)
wpOffsets := make([]flatbuffers.UOffsetT, len(msg.Waypoints))
for i, wp := range msg.Waypoints {
wpOffsets[i] = buildVec3(b, wp)
}
b.StartVector(4, len(wpOffsets), 4)
for i := len(wpOffsets) - 1; i >= 0; i-- {
b.PrependUOffsetT(wpOffsets[i])
}
wpVecOff := b.EndVector(len(wpOffsets))
// position table
posOff := buildVec3(b, msg.Position)
// 2. Build the MoveMessage table.
b.StartObject(8)
b.PrependUOffsetTSlot(slotPosition, posOff, 0)
b.PrependUOffsetTSlot(slotVelocity, velOff, 0)
b.PrependUOffsetTSlot(slotWaypoints, wpVecOff, 0)
b.PrependUint32Slot(slotPlayerID, msg.PlayerID, 0)
b.PrependBoolSlot(slotActive, msg.Active, false)
b.PrependBoolSlot(slotVisible, msg.Visible, false)
b.PrependBoolSlot(slotGhost, msg.Ghost, false)
b.PrependUOffsetTSlot(slotName, nameOff, 0)
root := b.EndObject()
b.Finish(root)
return b.FinishedBytes()
}
// readVec3 reads a Vec3 from a table at the given absolute position in buf.
func readVec3(buf []byte, tablePos flatbuffers.UOffsetT) Vec3 {
tab := flatbuffers.Table{Bytes: buf, Pos: tablePos}
var v Vec3
if o := tab.Offset(4); o != 0 { // slot 0 -> vtable offset 4
v.X = tab.GetFloat32(tab.Pos + flatbuffers.UOffsetT(o))
}
if o := tab.Offset(6); o != 0 { // slot 1 -> vtable offset 6
v.Y = tab.GetFloat32(tab.Pos + flatbuffers.UOffsetT(o))
}
if o := tab.Offset(8); o != 0 { // slot 2 -> vtable offset 8
v.Z = tab.GetFloat32(tab.Pos + flatbuffers.UOffsetT(o))
}
return v
}
// Unmarshal reads all fields from a finished FlatBuffer into out.
func Unmarshal(buf []byte, out *MoveMsg) {
// The root offset is stored at byte 0 of the finished buffer.
rootPos := flatbuffers.GetUOffsetT(buf)
tab := flatbuffers.Table{Bytes: buf, Pos: rootPos}
// position (slot 0, vtable offset 4)
if o := tab.Offset(4); o != 0 {
absOff := tab.Pos + flatbuffers.UOffsetT(o)
posPos := tab.Indirect(absOff)
out.Position = readVec3(buf, posPos)
}
// velocity vector (slot 1, vtable offset 6)
if o := tab.Offset(6); o != 0 {
vecStart := tab.Vector(flatbuffers.UOffsetT(o))
for i := 0; i < 3; i++ {
out.Velocity[i] = flatbuffers.GetFloat32(buf[int(vecStart)+i*4:])
}
}
// waypoints vector (slot 2, vtable offset 8)
if o := tab.Offset(8); o != 0 {
n := tab.VectorLen(flatbuffers.UOffsetT(o))
out.Waypoints = make([]Vec3, n)
vecStart := tab.Vector(flatbuffers.UOffsetT(o))
for i := 0; i < n; i++ {
// Each element is an UOffsetT pointing to the table.
elemOff := vecStart + flatbuffers.UOffsetT(i*4)
tablePos := elemOff + flatbuffers.GetUOffsetT(buf[elemOff:])
out.Waypoints[i] = readVec3(buf, tablePos)
}
}
// player_id (slot 3, vtable offset 10)
if o := tab.Offset(10); o != 0 {
out.PlayerID = tab.GetUint32(tab.Pos + flatbuffers.UOffsetT(o))
}
// active (slot 4, vtable offset 12)
if o := tab.Offset(12); o != 0 {
out.Active = tab.GetBool(tab.Pos + flatbuffers.UOffsetT(o))
}
// visible (slot 5, vtable offset 14)
if o := tab.Offset(14); o != 0 {
out.Visible = tab.GetBool(tab.Pos + flatbuffers.UOffsetT(o))
}
// ghost (slot 6, vtable offset 16)
if o := tab.Offset(16); o != 0 {
out.Ghost = tab.GetBool(tab.Pos + flatbuffers.UOffsetT(o))
}
// name (slot 7, vtable offset 18)
if o := tab.Offset(18); o != 0 {
absOff := tab.Pos + flatbuffers.UOffsetT(o)
out.Name = tab.String(absOff)
}
}