From 17ca46b4822f2fa7e9e78c2e52244468748008b6 Mon Sep 17 00:00:00 2001 From: Yurii Soldak Date: Mon, 29 Jul 2024 01:35:05 +0200 Subject: [PATCH] optimisations for embedded use less heap allocations --- adapter.go | 26 ++++++++++++++------------ adapter_avr.go | 13 ++++++++----- adapter_common.go | 13 ++++++++----- message.go | 30 ++++++++++++++++++++++++++---- 4 files changed, 56 insertions(+), 26 deletions(-) diff --git a/adapter.go b/adapter.go index 4da7ffc..2a925c8 100644 --- a/adapter.go +++ b/adapter.go @@ -39,18 +39,22 @@ type Adapter struct { func NewAdapter(wire io.ReadWriter) *Adapter { return &Adapter{ wire: wire, + message: Message{ + Payload: []byte{}, + }, } } // Send a message. func (a *Adapter) Send(message *Message) error { - bytes := message.Bytes() + bytes := make([]byte, 5+maxPayload+1) // optimisation to avoid heap allocation: could allocate only required size, but that is not constant + _ = message.Bytes(bytes) logTs("SEND ") for _, b := range bytes { log(" %02X", b) } log("\n") - n, err := a.wire.Write(bytes) + n, err := a.wire.Write(bytes[0:message.Size()]) if err != nil { return ErrWrite } @@ -61,12 +65,12 @@ func (a *Adapter) Send(message *Message) error { } // Receive a message; returns nil if no message is available (yet). -func (a *Adapter) Receive() (*Message, error) { +func (a *Adapter) Receive(result *Message) error { buf := make([]byte, 16) for { n, err := a.wire.Read(buf) if err != nil || n == 0 { - return nil, ErrNoData + return ErrNoData } for i := 0; i < n; i++ { b := buf[i] @@ -100,7 +104,7 @@ func (a *Adapter) Receive() (*Message, error) { continue } a.message.Length = b - a.message.Payload = []byte{} + a.message.Payload = a.message.Payload[:0] a.message.Checksum = b a.state = stateCommand case stateCommand: @@ -116,19 +120,18 @@ func (a *Adapter) Receive() (*Message, error) { } case stateChecksum: logTs("PAYLOAD ") - for _, bb := range a.message.Bytes() { + for _, bb := range a.message.Payload { log(" %02X", bb) } log("\n") logTs("CHECKSUM expected %02X ?= %02X actual\n", a.message.Checksum, b) - result := a.message - a.message = Message{} + result.Copy(&a.message) a.state = stateIdle if result.Checksum == b { - a.handleBeaconMaybe(&result) - return &result, nil + a.handleBeaconMaybe(result) + return nil } else { - return nil, ErrWrongChecksum + return ErrWrongChecksum } } } @@ -138,7 +141,6 @@ func (a *Adapter) Receive() (*Message, error) { // Reset the state machine and clear the message buffer. func (a *Adapter) Reset() { a.state = stateIdle - a.message = Message{} buf := make([]byte, 16) for { n, err := a.wire.Read(buf) diff --git a/adapter_avr.go b/adapter_avr.go index 22f978c..2e10321 100644 --- a/adapter_avr.go +++ b/adapter_avr.go @@ -3,16 +3,19 @@ package csp // Wait for a message with the given command and direction. -func (a *Adapter) Wait(command Command, direction Direction, timeout int64) (*Message, error) { +func (a *Adapter) Wait(command Command, direction Direction, timeout int64, message *Message) error { start := runtime_nanotime() for runtime_nanotime()-start < timeout { - message, _ := a.Receive() + err := a.Receive(message) + if err != nil { + continue + } // wait for correct message - if message != nil && message.Command == command && message.Direction == direction { - return message, nil + if message.Command == command && message.Direction == direction { + return nil } } - return nil, ErrTimeout + return ErrTimeout } // BeaconTime returns the next time when a beacon with the given ID should be broadcasted. diff --git a/adapter_common.go b/adapter_common.go index c3f1be7..2baba33 100644 --- a/adapter_common.go +++ b/adapter_common.go @@ -5,16 +5,19 @@ package csp import "time" // Wait for a message with the given command and direction. -func (a *Adapter) Wait(command Command, direction Direction, timeout time.Duration) (*Message, error) { +func (a *Adapter) Wait(command Command, direction Direction, timeout time.Duration, message *Message) error { start := time.Now() for time.Since(start) < timeout { - message, _ := a.Receive() + err := a.Receive(message) + if err != nil { + continue + } // wait for correct message - if message != nil && message.Command == command && message.Direction == direction { - return message, nil + if message.Command == command && message.Direction == direction { + return nil } } - return nil, ErrTimeout + return ErrTimeout } // BeaconTime returns the next time when a beacon with the given ID should be broadcasted. diff --git a/message.go b/message.go index a1dbd50..b7a664b 100644 --- a/message.go +++ b/message.go @@ -1,5 +1,7 @@ package csp +import "errors" + // Commands type Command byte @@ -20,6 +22,8 @@ const ( CmdHit Command = 0x82 ) +var errBufferTooSmall = errors.New("buffer too small") + // Directions type Direction byte @@ -64,16 +68,34 @@ func NewMessage(direction Direction, command Command, data []byte) *Message { } } -func (m *Message) Bytes() []byte { - b := make([]byte, 5+len(m.Payload)+1) +func (m *Message) Copy(o *Message) { + copy(m.Header[:], o.Header[:]) + m.Direction = o.Direction + m.Length = o.Length + m.Command = o.Command + if m.Payload == nil { + m.Payload = make([]byte, o.Length) + } + copy(m.Payload, o.Payload) + m.Checksum = o.Checksum +} + +func (m *Message) Bytes(b []byte) error { + if len(b) < int(m.Size()) { + return errBufferTooSmall + } b[0] = m.Header[0] b[1] = m.Header[1] b[2] = byte(m.Direction) b[3] = m.Length b[4] = byte(m.Command) copy(b[5:], m.Payload) - b[len(b)-1] = m.Checksum - return b + b[m.Size()-1] = m.Checksum + return nil +} + +func (m *Message) Size() byte { + return 5 + m.Length + 1 } func (m *Message) IsRequest() bool {