mirror of
https://github.com/AlexxIT/go2rtc.git
synced 2025-09-27 04:36:12 +08:00
Improve HomeKit TLV format parser
This commit is contained in:
@@ -46,6 +46,8 @@ func Marshal(v any) ([]byte, error) {
|
||||
}
|
||||
|
||||
switch kind {
|
||||
case reflect.Slice:
|
||||
return appendSlice(nil, value)
|
||||
case reflect.Struct:
|
||||
return appendStruct(nil, value)
|
||||
}
|
||||
@@ -53,6 +55,23 @@ func Marshal(v any) ([]byte, error) {
|
||||
return nil, errors.New("tlv8: not implemented: " + kind.String())
|
||||
}
|
||||
|
||||
// separator the most confusing meaning in the documentation.
|
||||
// It can have a value of 0x00 or 0xFF or even 0x05.
|
||||
const separator = 0xFF
|
||||
|
||||
func appendSlice(b []byte, value reflect.Value) ([]byte, error) {
|
||||
for i := 0; i < value.Len(); i++ {
|
||||
if i > 0 {
|
||||
b = append(b, separator, 0)
|
||||
}
|
||||
var err error
|
||||
if b, err = appendStruct(b, value.Index(i)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func appendStruct(b []byte, value reflect.Value) ([]byte, error) {
|
||||
valueType := value.Type()
|
||||
|
||||
@@ -121,7 +140,7 @@ func appendValue(b []byte, tag byte, value reflect.Value) ([]byte, error) {
|
||||
case reflect.Slice:
|
||||
for i := 0; i < value.Len(); i++ {
|
||||
if i > 0 {
|
||||
b = append(b, 0, 0)
|
||||
b = append(b, separator, 0)
|
||||
}
|
||||
if b, err = appendValue(b, tag, value.Index(i)); err != nil {
|
||||
return nil, err
|
||||
@@ -179,32 +198,37 @@ func Unmarshal(data []byte, v any) error {
|
||||
kind = value.Kind()
|
||||
}
|
||||
|
||||
if kind != reflect.Struct {
|
||||
return errors.New("tlv8: not implemented: " + kind.String())
|
||||
}
|
||||
|
||||
switch kind {
|
||||
case reflect.Slice:
|
||||
return unmarshalSlice(data, value)
|
||||
case reflect.Struct:
|
||||
return unmarshalStruct(data, value)
|
||||
}
|
||||
|
||||
func unmarshalStruct(b []byte, value reflect.Value) error {
|
||||
var waitSlice bool
|
||||
return errors.New("tlv8: not implemented: " + kind.String())
|
||||
}
|
||||
|
||||
// unmarshalTLV can return two types of errors:
|
||||
// - critical and then the value of []byte will be nil
|
||||
// - not critical and then []byte will contain the value
|
||||
func unmarshalTLV(b []byte, value reflect.Value) ([]byte, error) {
|
||||
if len(b) < 2 {
|
||||
return nil, errors.New("tlv8: wrong size: " + value.Type().Name())
|
||||
}
|
||||
|
||||
for len(b) >= 2 {
|
||||
t := b[0]
|
||||
l := int(b[1])
|
||||
|
||||
// array item divider
|
||||
if t == 0 && l == 0 {
|
||||
b = b[2:]
|
||||
waitSlice = true
|
||||
continue
|
||||
// array item divider (t == 0x00 || t == 0xFF)
|
||||
if l == 0 {
|
||||
return b[2:], errors.New("tlv8: zero item")
|
||||
}
|
||||
|
||||
var v []byte
|
||||
|
||||
for {
|
||||
if len(b) < 2+l {
|
||||
return errors.New("tlv8: wrong size: " + value.Type().Name())
|
||||
return nil, errors.New("tlv8: wrong size: " + value.Type().Name())
|
||||
}
|
||||
|
||||
v = append(v, b[2:2+l]...)
|
||||
@@ -222,21 +246,38 @@ func unmarshalStruct(b []byte, value reflect.Value) error {
|
||||
|
||||
valueField, ok := getStructField(value, tag)
|
||||
if !ok {
|
||||
return fmt.Errorf("tlv8: can't find T=%d,L=%d,V=%x for: %s", t, l, v, value.Type().Name())
|
||||
}
|
||||
|
||||
if waitSlice {
|
||||
if valueField.Kind() != reflect.Slice {
|
||||
return fmt.Errorf("tlv8: should be slice T=%d,L=%d,V=%x for: %s", t, l, v, value.Type().Name())
|
||||
}
|
||||
waitSlice = false
|
||||
return b, fmt.Errorf("tlv8: can't find T=%d,L=%d,V=%x for: %s", t, l, v, value.Type().Name())
|
||||
}
|
||||
|
||||
if err := unmarshalValue(v, valueField); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func unmarshalSlice(b []byte, value reflect.Value) error {
|
||||
valueIndex := value.Index(growSlice(value))
|
||||
for len(b) > 0 {
|
||||
var err error
|
||||
if b, err = unmarshalTLV(b, valueIndex); err != nil {
|
||||
if b != nil {
|
||||
valueIndex = value.Index(growSlice(value))
|
||||
continue
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func unmarshalStruct(b []byte, value reflect.Value) error {
|
||||
for len(b) > 0 {
|
||||
var err error
|
||||
if b, err = unmarshalTLV(b, value); b == nil && err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@@ -2,6 +2,7 @@ package tlv8
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
@@ -107,3 +108,49 @@ func TestInterface(t *testing.T) {
|
||||
|
||||
require.Equal(t, src, dst)
|
||||
}
|
||||
|
||||
func TestSlice1(t *testing.T) {
|
||||
var v struct {
|
||||
VideoAttrs []struct {
|
||||
Width uint16 `tlv8:"1"`
|
||||
Height uint16 `tlv8:"2"`
|
||||
Framerate uint8 `tlv8:"3"`
|
||||
} `tlv8:"3"`
|
||||
}
|
||||
|
||||
s := `030b010280070202380403011e ff00 030b010200050202d00203011e`
|
||||
b1, err := hex.DecodeString(strings.ReplaceAll(s, " ", ""))
|
||||
require.NoError(t, err)
|
||||
|
||||
err = Unmarshal(b1, &v)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Len(t, v.VideoAttrs, 2)
|
||||
|
||||
b2, err := Marshal(v)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, b1, b2)
|
||||
}
|
||||
|
||||
func TestSlice2(t *testing.T) {
|
||||
var v []struct {
|
||||
Width uint16 `tlv8:"1"`
|
||||
Height uint16 `tlv8:"2"`
|
||||
Framerate uint8 `tlv8:"3"`
|
||||
}
|
||||
|
||||
s := `010280070202380403011e ff00 010200050202d00203011e`
|
||||
b1, err := hex.DecodeString(strings.ReplaceAll(s, " ", ""))
|
||||
require.NoError(t, err)
|
||||
|
||||
err = Unmarshal(b1, &v)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Len(t, v, 2)
|
||||
|
||||
b2, err := Marshal(v)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, b1, b2)
|
||||
}
|
||||
|
Reference in New Issue
Block a user