aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorOphestra <cat@gensokyo.uk>2025-11-25 04:04:07 +0900
committerOphestra <cat@gensokyo.uk>2025-11-25 04:05:22 +0900
commit8f4a3bcf9f3d2dc0d8363b2f66ba41f5bc9f7c6a (patch)
tree003dd46060ad3c1de2a013074845181901b1827a
parent827dc9e1ba600622fd93f12f0e96de67e4b1347b (diff)
internal/pipewire: use custom marshaler when available
This reduces special cases. This change also exposes unmarshalled message size on the wire. Signed-off-by: Ophestra <cat@gensokyo.uk>
-rw-r--r--internal/pipewire/core.go5
-rw-r--r--internal/pipewire/pod.go71
2 files changed, 61 insertions, 15 deletions
diff --git a/internal/pipewire/core.go b/internal/pipewire/core.go
index a833009f..9a408e2f 100644
--- a/internal/pipewire/core.go
+++ b/internal/pipewire/core.go
@@ -76,4 +76,7 @@ type CoreHello struct {
func (c *CoreHello) MarshalBinary() ([]byte, error) { return MarshalAppend(make([]byte, 0, 24), c) }
// UnmarshalBinary satisfies [encoding.BinaryUnmarshaler] via [Unmarshal].
-func (c *CoreHello) UnmarshalBinary(data []byte) error { return Unmarshal(data, c) }
+func (c *CoreHello) UnmarshalBinary(data []byte) error {
+ _, err := Unmarshal(data, c)
+ return err
+}
diff --git a/internal/pipewire/pod.go b/internal/pipewire/pod.go
index cf018ccd..4ba4c6ca 100644
--- a/internal/pipewire/pod.go
+++ b/internal/pipewire/pod.go
@@ -65,6 +65,13 @@ const (
_SPA_TYPE_LAST // not part of ABI
)
+// PODMarshaler is the interface implemented by an object that can
+// marshal itself into PipeWire POD encoding.
+type PODMarshaler interface {
+ // MarshalPOD encodes the receiver into PipeWire POD encoding and returns the result.
+ MarshalPOD() ([]byte, error)
+}
+
// An UnsupportedTypeError is returned by [Marshal] when attempting
// to encode an unsupported value type.
type UnsupportedTypeError struct{ Type reflect.Type }
@@ -86,11 +93,12 @@ func MarshalAppend(data []byte, v any) ([]byte, error) {
return marshalValueAppend(data, reflect.ValueOf(v))
}
-// marshalValueAppendRaw implements [MarshalAppend] on [reflect.Value].
-func marshalValueAppend(data []byte, v reflect.Value) ([]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)...)
- rData, err := marshalValueAppendRaw(data, v)
+ rData, err := f(data)
if err != nil {
return data, err
}
@@ -108,6 +116,18 @@ func marshalValueAppend(data []byte, v reflect.Value) ([]byte, error) {
return rData, nil
}
+// marshalValueAppendRaw implements [MarshalAppend] on [reflect.Value].
+func marshalValueAppend(data []byte, v reflect.Value) ([]byte, error) {
+ if v.CanInterface() {
+ if m, ok := v.Interface().(PODMarshaler); ok {
+ extraData, err := m.MarshalPOD()
+ return append(data, extraData...), err
+ }
+ }
+
+ return appendInner(data, func(data []byte) ([]byte, error) { return marshalValueAppendRaw(data, v) })
+}
+
// marshalValueAppendRaw implements [MarshalAppend] on [reflect.Value] without the size prefix.
func marshalValueAppendRaw(data []byte, v reflect.Value) ([]byte, error) {
switch v.Kind() {
@@ -146,6 +166,15 @@ func marshalValueAppendRaw(data []byte, v reflect.Value) ([]byte, error) {
}
}
+// PODUnmarshaler is the interface implemented by an object that can
+// unmarshal a PipeWire POD encoding representation of itself.
+type PODUnmarshaler interface {
+ // UnmarshalPOD must be able to decode the form generated by MarshalPOD.
+ // UnmarshalPOD must copy the data if it wishes to retain the data
+ // after returning.
+ UnmarshalPOD(data []byte) (Word, error)
+}
+
// An InvalidUnmarshalError describes an invalid argument passed to [Unmarshal].
// (The argument to [Unmarshal] must be a non-nil pointer.)
type InvalidUnmarshalError struct{ Type reflect.Type }
@@ -164,12 +193,17 @@ func (e *InvalidUnmarshalError) Error() string {
// Unmarshal parses the JSON-encoded data and stores the result
// in the value pointed to by v. If v is nil or not a pointer,
// Unmarshal returns an [InvalidUnmarshalError].
-func Unmarshal(data []byte, v any) error {
+func Unmarshal(data []byte, v any) (size Word, err error) {
rv := reflect.ValueOf(v)
if rv.Kind() != reflect.Pointer || rv.IsNil() {
- return &InvalidUnmarshalError{reflect.TypeOf(v)}
+ return 0, &InvalidUnmarshalError{reflect.TypeOf(v)}
}
- return unmarshalValue(data, rv.Elem(), new(Word))
+ err = unmarshalValue(data, rv.Elem(), &size)
+ // size and type prefix
+ size += 8
+ // padding
+ size += (8 - (size)%8) % 8
+ return
}
// UnmarshalSetError describes a value that cannot be set during [Unmarshal].
@@ -199,6 +233,22 @@ func (e StringTerminationError) Error() string {
// unmarshalValue implements [Unmarshal] on [reflect.Value].
func unmarshalValue(data []byte, v reflect.Value, sizeP *Word) error {
+ if !v.CanSet() {
+ return &UnmarshalSetError{v.Type()}
+ }
+
+ if v.CanInterface() {
+ if v.Kind() == reflect.Pointer {
+ v.Set(reflect.New(v.Type().Elem()))
+ }
+
+ if u, ok := v.Interface().(PODUnmarshaler); ok {
+ var err error
+ *sizeP, err = u.UnmarshalPOD(data)
+ return err
+ }
+ }
+
switch v.Kind() {
case reflect.Int32:
@@ -206,9 +256,6 @@ func unmarshalValue(data []byte, v reflect.Value, sizeP *Word) error {
if err := unmarshalCheckTypeBounds(&data, SPA_TYPE_Int, sizeP); err != nil {
return err
}
- if !v.CanSet() {
- return &UnmarshalSetError{v.Type()}
- }
v.SetInt(int64(binary.NativeEndian.Uint32(data)))
return nil
@@ -223,7 +270,7 @@ func unmarshalValue(data []byte, v reflect.Value, sizeP *Word) error {
return err
}
paddingSize := (8 - (fieldWireSize)%8) % 8
- // already bounds checked by the successful unmarshalValue call
+ // bounds check completed in successful call to unmarshalValue
data = data[8+fieldWireSize+paddingSize:]
}
@@ -233,10 +280,6 @@ func unmarshalValue(data []byte, v reflect.Value, sizeP *Word) error {
return nil
case reflect.Pointer:
- if !v.CanSet() {
- return &UnmarshalSetError{v.Type()}
- }
-
if len(data) < 8 {
return io.ErrUnexpectedEOF
}