internal/pipewire: use custom marshaler when available
All checks were successful
Test / Create distribution (push) Successful in 37s
Test / Sandbox (push) Successful in 45s
Test / Sandbox (race detector) (push) Successful in 2m19s
Test / Hakurei (push) Successful in 2m27s
Test / Hakurei (race detector) (push) Successful in 3m12s
Test / Hpkg (push) Successful in 3m31s
Test / Flake checks (push) Successful in 1m34s
All checks were successful
Test / Create distribution (push) Successful in 37s
Test / Sandbox (push) Successful in 45s
Test / Sandbox (race detector) (push) Successful in 2m19s
Test / Hakurei (push) Successful in 2m27s
Test / Hakurei (race detector) (push) Successful in 3m12s
Test / Hpkg (push) Successful in 3m31s
Test / Flake checks (push) Successful in 1m34s
This reduces special cases. This change also exposes unmarshalled message size on the wire. Signed-off-by: Ophestra <cat@gensokyo.uk>
This commit is contained in:
parent
827dc9e1ba
commit
8f4a3bcf9f
@ -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
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user