package benchmarks import ( "encoding/binary" "fmt" "testing" "github.com/jfraeys/fetch_ml/internal/api" ) var benchmarkDataPayload = func() []byte { buf := make([]byte, 4096) for i := range buf { buf[i] = byte(i % 251) } return buf }() var benchmarkPackets = []struct { name string packet *api.ResponsePacket }{ { name: "success", packet: &api.ResponsePacket{ PacketType: api.PacketTypeSuccess, Timestamp: 1_732_000_000, SuccessMessage: "Job 'benchmark' queued successfully", }, }, { name: "error", packet: &api.ResponsePacket{ PacketType: api.PacketTypeError, Timestamp: 1_732_000_000, ErrorCode: api.ErrorCodeDatabaseError, ErrorMessage: "Failed to enqueue task", ErrorDetails: "database connection refused", }, }, { name: "data", packet: &api.ResponsePacket{ PacketType: api.PacketTypeData, Timestamp: 1_732_000_000, DataType: "status", DataPayload: benchmarkDataPayload, }, }, { name: "progress", packet: &api.ResponsePacket{ PacketType: api.PacketTypeProgress, Timestamp: 1_732_000_000, ProgressType: api.ProgressTypePercentage, ProgressValue: 42, ProgressTotal: 100, ProgressMessage: "running", }, }, } func BenchmarkResponsePacketSerialize(b *testing.B) { for _, variant := range benchmarkPackets { variant := variant b.Run(variant.name+"/current", func(b *testing.B) { benchmarkSerializePacket(b, variant.packet) }) b.Run(variant.name+"/legacy", func(b *testing.B) { benchmarkLegacySerializePacket(b, variant.packet) }) } } func benchmarkSerializePacket(b *testing.B, packet *api.ResponsePacket) { b.Helper() b.ReportAllocs() for i := 0; i < b.N; i++ { if _, err := packet.Serialize(); err != nil { b.Fatalf("serialize failed: %v", err) } } } func benchmarkLegacySerializePacket(b *testing.B, packet *api.ResponsePacket) { b.Helper() b.ReportAllocs() for i := 0; i < b.N; i++ { if _, err := legacySerializePacket(packet); err != nil { b.Fatalf("legacy serialize failed: %v", err) } } } func legacySerializePacket(p *api.ResponsePacket) ([]byte, error) { var buf []byte buf = append(buf, p.PacketType) timestampBytes := make([]byte, 8) binary.BigEndian.PutUint64(timestampBytes, p.Timestamp) buf = append(buf, timestampBytes...) switch p.PacketType { case api.PacketTypeSuccess: buf = append(buf, legacySerializeString(p.SuccessMessage)...) case api.PacketTypeError: buf = append(buf, p.ErrorCode) buf = append(buf, legacySerializeString(p.ErrorMessage)...) buf = append(buf, legacySerializeString(p.ErrorDetails)...) case api.PacketTypeProgress: buf = append(buf, p.ProgressType) valueBytes := make([]byte, 4) binary.BigEndian.PutUint32(valueBytes, p.ProgressValue) buf = append(buf, valueBytes...) totalBytes := make([]byte, 4) binary.BigEndian.PutUint32(totalBytes, p.ProgressTotal) buf = append(buf, totalBytes...) buf = append(buf, legacySerializeString(p.ProgressMessage)...) case api.PacketTypeStatus: buf = append(buf, legacySerializeString(p.StatusData)...) case api.PacketTypeData: buf = append(buf, legacySerializeString(p.DataType)...) buf = append(buf, legacySerializeBytes(p.DataPayload)...) case api.PacketTypeLog: buf = append(buf, p.LogLevel) buf = append(buf, legacySerializeString(p.LogMessage)...) default: return nil, fmt.Errorf("unknown packet type: %d", p.PacketType) } return buf, nil } func legacySerializeString(s string) []byte { length := uint16(len(s)) buf := make([]byte, 2+len(s)) binary.BigEndian.PutUint16(buf[:2], length) copy(buf[2:], s) return buf } func legacySerializeBytes(b []byte) []byte { length := uint32(len(b)) buf := make([]byte, 4+len(b)) binary.BigEndian.PutUint32(buf[:4], length) copy(buf[4:], b) return buf }