aboutsummaryrefslogtreecommitdiffhomepage
path: root/internal
diff options
context:
space:
mode:
authorOphestra <cat@gensokyo.uk>2025-11-25 03:33:57 +0900
committerOphestra <cat@gensokyo.uk>2025-11-25 04:00:59 +0900
commit827dc9e1ba600622fd93f12f0e96de67e4b1347b (patch)
tree2d8da1aaecd307f2fcdec462936102c3172d4c1f /internal
parentd92de1c70977308c5d9a5c5be325f1b27be2c2f4 (diff)
internal/pipewire: implement string type
This is still NUL terminated strings, and an extra NUL character on an 8-byte string does cause an extra 7 bytes of padding. Signed-off-by: Ophestra <cat@gensokyo.uk>
Diffstat (limited to 'internal')
-rw-r--r--internal/pipewire/pod.go33
-rw-r--r--internal/pipewire/pod_test.go12
2 files changed, 44 insertions, 1 deletions
diff --git a/internal/pipewire/pod.go b/internal/pipewire/pod.go
index ff82c72f..cf018ccd 100644
--- a/internal/pipewire/pod.go
+++ b/internal/pipewire/pod.go
@@ -135,6 +135,12 @@ func marshalValueAppendRaw(data []byte, v reflect.Value) ([]byte, error) {
}
return marshalValueAppendRaw(data, v.Elem())
+ case reflect.String:
+ data = binary.NativeEndian.AppendUint32(data, SPA_TYPE_String)
+ data = append(data, []byte(v.String())...)
+ data = append(data, 0)
+ return data, nil
+
default:
return data, &UnsupportedTypeError{v.Type()}
}
@@ -183,6 +189,14 @@ func (e *TrailingGarbageError) Error() string {
return "data has extra values starting with type " + strconv.Itoa(int(binary.NativeEndian.Uint32(e.Data[4:])))
}
+// A StringTerminationError describes an incorrectly terminated string
+// encountered during [Unmarshal].
+type StringTerminationError struct{ Value byte }
+
+func (e StringTerminationError) Error() string {
+ return "got byte " + strconv.Itoa(int(e.Value)) + " instead of NUL"
+}
+
// unmarshalValue implements [Unmarshal] on [reflect.Value].
func unmarshalValue(data []byte, v reflect.Value, sizeP *Word) error {
switch v.Kind() {
@@ -236,6 +250,25 @@ func unmarshalValue(data []byte, v reflect.Value, sizeP *Word) error {
return unmarshalValue(data, v.Elem(), sizeP)
}
+ case reflect.String:
+ if err := unmarshalCheckTypeBounds(&data, SPA_TYPE_String, sizeP); err != nil {
+ return err
+ }
+
+ // string size, one extra NUL byte
+ size := int(*sizeP)
+ if len(data) < size {
+ return io.ErrUnexpectedEOF
+ }
+
+ // the serialised strings still include NUL termination
+ if data[size-1] != 0 {
+ return StringTerminationError{data[size-1]}
+ }
+
+ v.SetString(string(data[:size-1]))
+ return nil
+
default:
return &UnsupportedTypeError{v.Type()}
}
diff --git a/internal/pipewire/pod_test.go b/internal/pipewire/pod_test.go
index a7982865..a39f9614 100644
--- a/internal/pipewire/pod_test.go
+++ b/internal/pipewire/pod_test.go
@@ -2,6 +2,7 @@ package pipewire_test
import (
"encoding"
+ "encoding/json"
"reflect"
"testing"
)
@@ -37,7 +38,7 @@ func (testCases encodingTestCases[V, S]) run(t *testing.T) {
t.Fatalf("UnmarshalBinary: error = %v", err)
}
if !reflect.DeepEqual(&value, &tc.value) {
- t.Fatalf("UnmarshalBinary: %#v, want %#v", value, tc.value)
+ t.Fatalf("UnmarshalBinary:\n%s\nwant\n%s", mustMarshalJSON(value), mustMarshalJSON(tc.value))
}
})
@@ -53,3 +54,12 @@ func (testCases encodingTestCases[V, S]) run(t *testing.T) {
})
}
}
+
+// mustMarshalJSON calls [json.Marshal] and returns the result.
+func mustMarshalJSON(v any) string {
+ if data, err := json.Marshal(v); err != nil {
+ panic(err)
+ } else {
+ return string(data)
+ }
+}