internal/pipewire: size without serialisation
All checks were successful
Test / Create distribution (push) Successful in 35s
Test / Sandbox (push) Successful in 2m21s
Test / Hakurei (push) Successful in 3m16s
Test / Hpkg (push) Successful in 4m12s
Test / Sandbox (race detector) (push) Successful in 4m29s
Test / Hakurei (race detector) (push) Successful in 5m13s
Test / Flake checks (push) Successful in 1m28s
All checks were successful
Test / Create distribution (push) Successful in 35s
Test / Sandbox (push) Successful in 2m21s
Test / Hakurei (push) Successful in 3m16s
Test / Hpkg (push) Successful in 4m12s
Test / Sandbox (race detector) (push) Successful in 4m29s
Test / Hakurei (race detector) (push) Successful in 5m13s
Test / Flake checks (push) Successful in 1m28s
This is required to achieve zero allocation (other than reflect). Signed-off-by: Ophestra <cat@gensokyo.uk>
This commit is contained in:
parent
563b5e66fc
commit
73987be7d4
@ -46,6 +46,14 @@ type ClientInfo struct {
|
|||||||
Props *SPADict
|
Props *SPADict
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Size satisfies [KnownSize] with a value computed at runtime.
|
||||||
|
func (c *ClientInfo) Size() Word {
|
||||||
|
return SizePrefix +
|
||||||
|
Size(SizeInt) +
|
||||||
|
Size(SizeLong) +
|
||||||
|
c.Props.Size()
|
||||||
|
}
|
||||||
|
|
||||||
// MarshalBinary satisfies [encoding.BinaryMarshaler] via [Marshal].
|
// MarshalBinary satisfies [encoding.BinaryMarshaler] via [Marshal].
|
||||||
func (c *ClientInfo) MarshalBinary() ([]byte, error) { return Marshal(c) }
|
func (c *ClientInfo) MarshalBinary() ([]byte, error) { return Marshal(c) }
|
||||||
|
|
||||||
@ -58,6 +66,9 @@ type ClientUpdateProperties struct {
|
|||||||
Props *SPADict
|
Props *SPADict
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Size satisfies [KnownSize] with a value computed at runtime.
|
||||||
|
func (c *ClientUpdateProperties) Size() Word { return SizePrefix + c.Props.Size() }
|
||||||
|
|
||||||
// MarshalBinary satisfies [encoding.BinaryMarshaler] via [Marshal].
|
// MarshalBinary satisfies [encoding.BinaryMarshaler] via [Marshal].
|
||||||
func (c *ClientUpdateProperties) MarshalBinary() ([]byte, error) { return Marshal(c) }
|
func (c *ClientUpdateProperties) MarshalBinary() ([]byte, error) { return Marshal(c) }
|
||||||
|
|
||||||
|
|||||||
@ -102,6 +102,19 @@ type CoreInfo struct {
|
|||||||
Props *SPADict
|
Props *SPADict
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Size satisfies [KnownSize] with a value computed at runtime.
|
||||||
|
func (c *CoreInfo) Size() Word {
|
||||||
|
return SizePrefix +
|
||||||
|
Size(SizeInt) +
|
||||||
|
Size(SizeInt) +
|
||||||
|
SizeString[Word](c.UserName) +
|
||||||
|
SizeString[Word](c.HostName) +
|
||||||
|
SizeString[Word](c.Version) +
|
||||||
|
SizeString[Word](c.Name) +
|
||||||
|
Size(SizeLong) +
|
||||||
|
c.Props.Size()
|
||||||
|
}
|
||||||
|
|
||||||
// MarshalBinary satisfies [encoding.BinaryMarshaler] via [Marshal].
|
// MarshalBinary satisfies [encoding.BinaryMarshaler] via [Marshal].
|
||||||
func (c *CoreInfo) MarshalBinary() ([]byte, error) { return Marshal(c) }
|
func (c *CoreInfo) MarshalBinary() ([]byte, error) { return Marshal(c) }
|
||||||
|
|
||||||
@ -116,6 +129,9 @@ type CoreDone struct {
|
|||||||
Sequence Int
|
Sequence Int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Size satisfies [KnownSize] with a constant value.
|
||||||
|
func (c *CoreDone) Size() Word { return SizePrefix + Size(SizeInt) + Size(SizeInt) }
|
||||||
|
|
||||||
// MarshalBinary satisfies [encoding.BinaryMarshaler] via [Marshal].
|
// MarshalBinary satisfies [encoding.BinaryMarshaler] via [Marshal].
|
||||||
func (c *CoreDone) MarshalBinary() ([]byte, error) { return Marshal(c) }
|
func (c *CoreDone) MarshalBinary() ([]byte, error) { return Marshal(c) }
|
||||||
|
|
||||||
@ -133,6 +149,14 @@ type CoreBoundProps struct {
|
|||||||
Props *SPADict
|
Props *SPADict
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Size satisfies [KnownSize] with a value computed at runtime.
|
||||||
|
func (c *CoreBoundProps) Size() Word {
|
||||||
|
return SizePrefix +
|
||||||
|
Size(SizeInt) +
|
||||||
|
Size(SizeInt) +
|
||||||
|
c.Props.Size()
|
||||||
|
}
|
||||||
|
|
||||||
// MarshalBinary satisfies [encoding.BinaryMarshaler] via [Marshal].
|
// MarshalBinary satisfies [encoding.BinaryMarshaler] via [Marshal].
|
||||||
func (c *CoreBoundProps) MarshalBinary() ([]byte, error) { return Marshal(c) }
|
func (c *CoreBoundProps) MarshalBinary() ([]byte, error) { return Marshal(c) }
|
||||||
|
|
||||||
@ -145,6 +169,9 @@ type CoreHello struct {
|
|||||||
Version Int
|
Version Int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Size satisfies [KnownSize] with a constant value.
|
||||||
|
func (c *CoreHello) Size() Word { return SizePrefix + Size(SizeInt) }
|
||||||
|
|
||||||
// MarshalBinary satisfies [encoding.BinaryMarshaler] via [MarshalAppend].
|
// MarshalBinary satisfies [encoding.BinaryMarshaler] via [MarshalAppend].
|
||||||
func (c *CoreHello) MarshalBinary() ([]byte, error) {
|
func (c *CoreHello) MarshalBinary() ([]byte, error) {
|
||||||
return MarshalAppend(make([]byte, 0, 24), c)
|
return MarshalAppend(make([]byte, 0, 24), c)
|
||||||
@ -168,6 +195,9 @@ type CoreSync struct {
|
|||||||
Sequence Int
|
Sequence Int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Size satisfies [KnownSize] with a constant value.
|
||||||
|
func (c *CoreSync) Size() Word { return SizePrefix + Size(SizeInt) + Size(SizeInt) }
|
||||||
|
|
||||||
// MarshalBinary satisfies [encoding.BinaryMarshaler] via [MarshalAppend].
|
// MarshalBinary satisfies [encoding.BinaryMarshaler] via [MarshalAppend].
|
||||||
func (c *CoreSync) MarshalBinary() ([]byte, error) {
|
func (c *CoreSync) MarshalBinary() ([]byte, error) {
|
||||||
return MarshalAppend(make([]byte, 0, 40), c)
|
return MarshalAppend(make([]byte, 0, 40), c)
|
||||||
@ -191,6 +221,9 @@ type CoreGetRegistry struct {
|
|||||||
NewID Int
|
NewID Int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Size satisfies [KnownSize] with a constant value.
|
||||||
|
func (c *CoreGetRegistry) Size() Word { return SizePrefix + Size(SizeInt) + Size(SizeInt) }
|
||||||
|
|
||||||
// MarshalBinary satisfies [encoding.BinaryMarshaler] via [MarshalAppend].
|
// MarshalBinary satisfies [encoding.BinaryMarshaler] via [MarshalAppend].
|
||||||
func (c *CoreGetRegistry) MarshalBinary() ([]byte, error) {
|
func (c *CoreGetRegistry) MarshalBinary() ([]byte, error) {
|
||||||
return MarshalAppend(make([]byte, 0, 40), c)
|
return MarshalAppend(make([]byte, 0, 40), c)
|
||||||
|
|||||||
@ -44,11 +44,11 @@ const (
|
|||||||
SizePrefix = SizeSPrefix + SizeTPrefix
|
SizePrefix = SizeSPrefix + SizeTPrefix
|
||||||
|
|
||||||
// SizeId is the fixed, unpadded size of a [SPA_TYPE_Id] value.
|
// SizeId is the fixed, unpadded size of a [SPA_TYPE_Id] value.
|
||||||
SizeId = 4
|
SizeId Word = 4
|
||||||
// SizeInt is the fixed, unpadded size of a [SPA_TYPE_Int] value.
|
// SizeInt is the fixed, unpadded size of a [SPA_TYPE_Int] value.
|
||||||
SizeInt = 4
|
SizeInt Word = 4
|
||||||
// SizeLong is the fixed, unpadded size of a [SPA_TYPE_Long] value.
|
// SizeLong is the fixed, unpadded size of a [SPA_TYPE_Long] value.
|
||||||
SizeLong = 8
|
SizeLong Word = 8
|
||||||
)
|
)
|
||||||
|
|
||||||
/* Basic types */
|
/* Basic types */
|
||||||
@ -86,6 +86,24 @@ const (
|
|||||||
_SPA_TYPE_LAST // not part of ABI
|
_SPA_TYPE_LAST // not part of ABI
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// A KnownSize value has known POD encoded size before serialisation.
|
||||||
|
type KnownSize interface {
|
||||||
|
// Size returns the POD encoded size of the receiver.
|
||||||
|
Size() Word
|
||||||
|
}
|
||||||
|
|
||||||
|
// PaddingSize returns the padding size corresponding to a wire size.
|
||||||
|
func PaddingSize[W Word | int](wireSize W) W { return (SizeAlign - (wireSize)%SizeAlign) % SizeAlign }
|
||||||
|
|
||||||
|
// PaddedSize returns the padded size corresponding to a wire size.
|
||||||
|
func PaddedSize[W Word | int](wireSize W) W { return wireSize + PaddingSize(wireSize) }
|
||||||
|
|
||||||
|
// Size returns prefixed and padded size corresponding to a wire size.
|
||||||
|
func Size[W Word | int](wireSize W) W { return SizePrefix + PaddedSize(wireSize) }
|
||||||
|
|
||||||
|
// SizeString returns prefixed and padded size corresponding to a string.
|
||||||
|
func SizeString[W Word | int](s string) W { return Size(W(len(s)) + 1) }
|
||||||
|
|
||||||
// PODMarshaler is the interface implemented by an object that can
|
// PODMarshaler is the interface implemented by an object that can
|
||||||
// marshal itself into PipeWire POD encoding.
|
// marshal itself into PipeWire POD encoding.
|
||||||
type PODMarshaler interface {
|
type PODMarshaler interface {
|
||||||
@ -125,14 +143,13 @@ func appendInner(data []byte, f func(data []byte) ([]byte, error)) ([]byte, erro
|
|||||||
}
|
}
|
||||||
|
|
||||||
size := len(rData) - len(data) + SizeSPrefix
|
size := len(rData) - len(data) + SizeSPrefix
|
||||||
paddingSize := (SizeAlign - (size)%SizeAlign) % SizeAlign
|
|
||||||
// compensated for size and type prefix
|
// compensated for size and type prefix
|
||||||
wireSize := size - SizePrefix
|
wireSize := size - SizePrefix
|
||||||
if wireSize > math.MaxUint32 {
|
if wireSize > math.MaxUint32 {
|
||||||
return data, UnsupportedSizeError(wireSize)
|
return data, UnsupportedSizeError(wireSize)
|
||||||
}
|
}
|
||||||
binary.NativeEndian.PutUint32(rData[len(data)-SizeSPrefix:len(data)], Word(wireSize))
|
binary.NativeEndian.PutUint32(rData[len(data)-SizeSPrefix:len(data)], Word(wireSize))
|
||||||
rData = append(rData, make([]byte, paddingSize)...)
|
rData = append(rData, make([]byte, PaddingSize(size))...)
|
||||||
|
|
||||||
return rData, nil
|
return rData, nil
|
||||||
}
|
}
|
||||||
@ -242,7 +259,7 @@ func UnmarshalNext(data []byte, v any) (size Word, err error) {
|
|||||||
}
|
}
|
||||||
err = unmarshalValue(data, rv.Elem(), &size)
|
err = unmarshalValue(data, rv.Elem(), &size)
|
||||||
// prefix and padding size
|
// prefix and padding size
|
||||||
size += SizePrefix + (SizeAlign-(size)%SizeAlign)%SizeAlign
|
size = Size(size)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -325,9 +342,8 @@ func unmarshalValue(data []byte, v reflect.Value, wireSizeP *Word) error {
|
|||||||
if err := unmarshalValue(data, v.Field(i), &fieldWireSize); err != nil {
|
if err := unmarshalValue(data, v.Field(i), &fieldWireSize); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
paddingSize := (SizeAlign - (fieldWireSize)%SizeAlign) % SizeAlign
|
|
||||||
// bounds check completed in successful call to unmarshalValue
|
// bounds check completed in successful call to unmarshalValue
|
||||||
data = data[SizePrefix+fieldWireSize+paddingSize:]
|
data = data[Size(fieldWireSize):]
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(data) != 0 {
|
if len(data) != 0 {
|
||||||
@ -441,6 +457,17 @@ type SPADict struct {
|
|||||||
Items []SPADictItem
|
Items []SPADictItem
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Size satisfies [KnownSize] with a value computed at runtime.
|
||||||
|
func (d *SPADict) Size() Word {
|
||||||
|
// struct prefix, NItems value
|
||||||
|
size := SizePrefix + int(Size(SizeInt))
|
||||||
|
for i := range d.Items {
|
||||||
|
size += SizeString[int](d.Items[i].Key)
|
||||||
|
size += SizeString[int](d.Items[i].Value)
|
||||||
|
}
|
||||||
|
return Word(size)
|
||||||
|
}
|
||||||
|
|
||||||
// MarshalPOD satisfies [PODMarshaler] as [SPADict] violates the POD type system.
|
// MarshalPOD satisfies [PODMarshaler] as [SPADict] violates the POD type system.
|
||||||
func (d *SPADict) MarshalPOD() ([]byte, error) {
|
func (d *SPADict) MarshalPOD() ([]byte, error) {
|
||||||
return appendInner(nil, func(dataPrefix []byte) (data []byte, err error) {
|
return appendInner(nil, func(dataPrefix []byte) (data []byte, err error) {
|
||||||
|
|||||||
@ -5,11 +5,14 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"hakurei.app/internal/pipewire"
|
||||||
)
|
)
|
||||||
|
|
||||||
type encodingTestCases[V any, S interface {
|
type encodingTestCases[V any, S interface {
|
||||||
encoding.BinaryMarshaler
|
encoding.BinaryMarshaler
|
||||||
encoding.BinaryUnmarshaler
|
encoding.BinaryUnmarshaler
|
||||||
|
|
||||||
*V
|
*V
|
||||||
}] []struct {
|
}] []struct {
|
||||||
// Uninterpreted name of subtest.
|
// Uninterpreted name of subtest.
|
||||||
@ -51,6 +54,14 @@ func (testCases encodingTestCases[V, S]) run(t *testing.T) {
|
|||||||
t.Fatalf("MarshalBinary: %#v, want %#v", gotData, tc.wantData)
|
t.Fatalf("MarshalBinary: %#v, want %#v", gotData, tc.wantData)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if s, ok := any(&tc.value).(pipewire.KnownSize); ok {
|
||||||
|
t.Run("size", func(t *testing.T) {
|
||||||
|
if got := int(s.Size()); got != len(tc.wantData) {
|
||||||
|
t.Errorf("Size: %d, want %d", got, len(tc.wantData))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user