From e51e81bb22a85f0324a80a32bb2b10f3a8616e17 Mon Sep 17 00:00:00 2001 From: Ophestra Date: Tue, 25 Nov 2025 04:08:52 +0900 Subject: [PATCH] internal/pipewire: implement spa_dict type This is a terrible type that defies the type system. It is implemented on the concrete type to avoid special cases. Signed-off-by: Ophestra --- internal/pipewire/pod.go | 68 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/internal/pipewire/pod.go b/internal/pipewire/pod.go index 4ba4c6c..6281913 100644 --- a/internal/pipewire/pod.go +++ b/internal/pipewire/pod.go @@ -360,6 +360,74 @@ func unmarshalCheckTypeBounds(data *[]byte, t Word, sizeP *Word) error { return nil } +// SPADictItem is an encoding-compatible representation of spa_dict_item. +type SPADictItem struct{ Key, Value string } + +// SPADict is an encoding-compatible representation of spa_dict. +type SPADict struct { + NItems Int + Items []SPADictItem +} + +func (d *SPADict) MarshalPOD() ([]byte, error) { + return appendInner(nil, func(data []byte) ([]byte, error) { + data = binary.NativeEndian.AppendUint32(data, SPA_TYPE_Struct) + if extraData, err := Marshal(d.NItems); err != nil { + return data, err + } else { + data = append(data, extraData...) + } + for i := range d.Items { + if extraData, err := Marshal(d.Items[i].Key); err != nil { + return data, err + } else { + data = append(data, extraData...) + } + if extraData, err := Marshal(d.Items[i].Value); err != nil { + return data, err + } else { + data = append(data, extraData...) + } + } + return data, nil + }) +} + +func (d *SPADict) UnmarshalPOD(data []byte) (Word, error) { + var wireSize Word + if err := unmarshalCheckTypeBounds(&data, SPA_TYPE_Struct, &wireSize); err != nil { + return wireSize, err + } + + if size, err := Unmarshal(data, &d.NItems); err != nil { + return wireSize, err + } else { + // bounds check completed in successful call to Unmarshal + data = data[size:] + } + + d.Items = make([]SPADictItem, d.NItems) + for i := range d.Items { + if size, err := Unmarshal(data, &d.Items[i].Key); err != nil { + return wireSize, err + } else { + // bounds check completed in successful call to Unmarshal + data = data[size:] + } + if size, err := Unmarshal(data, &d.Items[i].Value); err != nil { + return wireSize, err + } else { + // bounds check completed in successful call to Unmarshal + data = data[size:] + } + } + + if len(data) != 0 { + return wireSize, &TrailingGarbageError{data} + } + return wireSize, nil +} + /* Pointers */ const ( SPA_TYPE_POINTER_START = 0x10000 + iota