From 2edcfe1e68078c467d12a04aff2fc1e45cb6dff2 Mon Sep 17 00:00:00 2001 From: Ophestra Date: Thu, 27 Nov 2025 00:54:56 +0900 Subject: [PATCH] internal/pipewire: define size constants This gets rid of magic numbers in marshal/unmarshal. Signed-off-by: Ophestra --- internal/pipewire/header.go | 12 ++++---- internal/pipewire/pod.go | 57 ++++++++++++++++++++++++------------- 2 files changed, 44 insertions(+), 25 deletions(-) diff --git a/internal/pipewire/header.go b/internal/pipewire/header.go index 3ab22d4..89d50ef 100644 --- a/internal/pipewire/header.go +++ b/internal/pipewire/header.go @@ -6,8 +6,8 @@ import ( ) const ( - // HeaderSize is the fixed size of [Header]. - HeaderSize = 16 + // SizeHeader is the fixed size of [Header]. + SizeHeader = 16 // SizeMax is the largest value of [Header.Size] that can be represented in its 3-byte segment. SizeMax = 0x00ffffff ) @@ -50,11 +50,11 @@ func (h *Header) MarshalBinary() (data []byte, err error) { if h.Size&^SizeMax != 0 { return nil, ErrSizeRange } - return h.append(make([]byte, 0, HeaderSize)), nil + return h.append(make([]byte, 0, SizeHeader)), nil } // unmarshalBinary decodes the protocol native message header. -func (h *Header) unmarshalBinary(data [HeaderSize]byte) { +func (h *Header) unmarshalBinary(data [SizeHeader]byte) { h.ID = binary.NativeEndian.Uint32(data[0:4]) h.Size = binary.NativeEndian.Uint32(data[4:8]) h.Opcode = byte(h.Size >> 24) @@ -65,9 +65,9 @@ func (h *Header) unmarshalBinary(data [HeaderSize]byte) { // UnmarshalBinary decodes the protocol native message header. func (h *Header) UnmarshalBinary(data []byte) error { - if len(data) != HeaderSize { + if len(data) != SizeHeader { return ErrBadHeader } - h.unmarshalBinary(([HeaderSize]byte)(data)) + h.unmarshalBinary(([SizeHeader]byte)(data)) return nil } diff --git a/internal/pipewire/pod.go b/internal/pipewire/pod.go index d36afca..9205049 100644 --- a/internal/pipewire/pod.go +++ b/internal/pipewire/pod.go @@ -32,6 +32,25 @@ type ( Bytes = []byte ) +const ( + // SizeAlign is the boundary which POD starts are always aligned to. + SizeAlign = 8 + + // SizeSPrefix is the fixed, unpadded size of the fixed-size prefix encoding POD wire size. + SizeSPrefix = 4 + // SizeTPrefix is the fixed, unpadded size of the fixed-size prefix encoding POD value type. + SizeTPrefix = 4 + // SizePrefix is the fixed, unpadded size of the fixed-size POD prefix. + SizePrefix = SizeSPrefix + SizeTPrefix + + // SizeId is the fixed, unpadded size of a [SPA_TYPE_Id] value. + SizeId = 4 + // SizeInt is the fixed, unpadded size of a [SPA_TYPE_Int] value. + SizeInt = 4 + // SizeLong is the fixed, unpadded size of a [SPA_TYPE_Long] value. + SizeLong = 8 +) + /* Basic types */ const ( /* POD's can contain a number of basic SPA types: */ @@ -98,21 +117,21 @@ func MarshalAppend(data []byte, v any) ([]byte, error) { // appendInner calls f and handles size prefix and padding around the appended data. // f must only append to data. func appendInner(data []byte, f func(data []byte) ([]byte, error)) ([]byte, error) { - data = append(data, make([]byte, 4)...) + data = append(data, make([]byte, SizeSPrefix)...) rData, err := f(data) if err != nil { return data, err } - size := len(rData) - len(data) + 4 - paddingSize := (8 - (size)%8) % 8 + size := len(rData) - len(data) + SizeSPrefix + paddingSize := (SizeAlign - (size)%SizeAlign) % SizeAlign // compensated for size and type prefix - wireSize := size - 8 + wireSize := size - SizePrefix if wireSize > math.MaxUint32 { return data, UnsupportedSizeError(wireSize) } - binary.NativeEndian.PutUint32(rData[len(data)-4:len(data)], Word(wireSize)) + binary.NativeEndian.PutUint32(rData[len(data)-SizeSPrefix:len(data)], Word(wireSize)) rData = append(rData, make([]byte, paddingSize)...) return rData, nil @@ -223,7 +242,7 @@ func UnmarshalNext(data []byte, v any) (size Word, err error) { } err = unmarshalValue(data, rv.Elem(), &size) // prefix and padding size - size += 8 + (8-(size)%8)%8 + size += SizePrefix + (SizeAlign-(size)%SizeAlign)%SizeAlign return } @@ -238,10 +257,10 @@ func (u *UnmarshalSetError) Error() string { return "cannot set: " + u.Type.Stri type TrailingGarbageError struct{ Data []byte } func (e *TrailingGarbageError) Error() string { - if len(e.Data) < 8 { + if len(e.Data) < SizePrefix { return "got " + strconv.Itoa(len(e.Data)) + " bytes of trailing garbage" } - return "data has extra values starting with type " + strconv.Itoa(int(binary.NativeEndian.Uint32(e.Data[4:]))) + return "data has extra values starting with type " + strconv.Itoa(int(binary.NativeEndian.Uint32(e.Data[SizeSPrefix:]))) } // A StringTerminationError describes an incorrectly terminated string @@ -272,7 +291,7 @@ func unmarshalValue(data []byte, v reflect.Value, wireSizeP *Word) error { switch v.Kind() { case reflect.Uint32: - *wireSizeP = 4 + *wireSizeP = SizeId if err := unmarshalCheckTypeBounds(&data, SPA_TYPE_Id, wireSizeP); err != nil { return err } @@ -280,7 +299,7 @@ func unmarshalValue(data []byte, v reflect.Value, wireSizeP *Word) error { return nil case reflect.Int32: - *wireSizeP = 4 + *wireSizeP = SizeInt if err := unmarshalCheckTypeBounds(&data, SPA_TYPE_Int, wireSizeP); err != nil { return err } @@ -288,7 +307,7 @@ func unmarshalValue(data []byte, v reflect.Value, wireSizeP *Word) error { return nil case reflect.Int64: - *wireSizeP = 8 + *wireSizeP = SizeLong if err := unmarshalCheckTypeBounds(&data, SPA_TYPE_Long, wireSizeP); err != nil { return err } @@ -306,9 +325,9 @@ func unmarshalValue(data []byte, v reflect.Value, wireSizeP *Word) error { if err := unmarshalValue(data, v.Field(i), &fieldWireSize); err != nil { return err } - paddingSize := (8 - (fieldWireSize)%8) % 8 + paddingSize := (SizeAlign - (fieldWireSize)%SizeAlign) % SizeAlign // bounds check completed in successful call to unmarshalValue - data = data[8+fieldWireSize+paddingSize:] + data = data[SizePrefix+fieldWireSize+paddingSize:] } if len(data) != 0 { @@ -317,10 +336,10 @@ func unmarshalValue(data []byte, v reflect.Value, wireSizeP *Word) error { return nil case reflect.Pointer: - if len(data) < 8 { + if len(data) < SizePrefix { return io.ErrUnexpectedEOF } - switch binary.NativeEndian.Uint32(data[4:]) { + switch binary.NativeEndian.Uint32(data[SizeSPrefix:]) { case SPA_TYPE_None: v.SetZero() return nil @@ -374,7 +393,7 @@ func (u *UnexpectedTypeError) Error() string { // unmarshalCheckTypeBounds performs bounds checks on data and validates the type and size prefixes. // An expected size of zero skips further bounds checks. func unmarshalCheckTypeBounds(data *[]byte, t Word, sizeP *Word) error { - if len(*data) < 8 { + if len(*data) < SizePrefix { return io.ErrUnexpectedEOF } @@ -385,16 +404,16 @@ func unmarshalCheckTypeBounds(data *[]byte, t Word, sizeP *Word) error { if wantSize != 0 && gotSize != wantSize { return &InconsistentSizeError{gotSize, wantSize} } - if len(*data)-8 < int(gotSize) { + if len(*data)-SizePrefix < int(gotSize) { return io.ErrUnexpectedEOF } - gotType := binary.NativeEndian.Uint32((*data)[4:]) + gotType := binary.NativeEndian.Uint32((*data)[SizeSPrefix:]) if gotType != t { return &UnexpectedTypeError{gotType, t} } - *data = (*data)[8 : gotSize+8] + *data = (*data)[SizePrefix : gotSize+SizePrefix] return nil }