Source File
packet_buffer.go
Belonging Package
github.com/pion/rtcp
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>// SPDX-License-Identifier: MITpackage rtcpimport ()// These functions implement an introspective structure// serializer/deserializer, designed to allow RTCP packet// Structs to be self-describing. They currently work with// fields of type uint8, uint16, uint32, and uint64 (and// types derived from them).//// - Unexported fields will take up space in the encoded// array, but wil be set to zero when written, and ignore// when read.//// - Fields that are marked with the tag `encoding:"omit"`// will be ignored when reading and writing data.//// For example://// type Example struct {// A uint32// B bool `encoding:"omit"`// _ uint64// C uint16// }//// "A" will be encoded as four bytes, in network order. "B"// will not be encoded at all. The anonymous uint64 will// encode as 8 bytes of value "0", followed by two bytes// encoding "C" in network order.type packetBuffer struct {bytes []byte}const omit = "omit"// Writes the structure passed to into the buffer that// PacketBuffer is initialized with. This function will// modify the PacketBuffer.bytes slice to exclude those// bytes that have been written into.func ( *packetBuffer) ( interface{}) error { //nolint:gocognit:= reflect.ValueOf()// Indirect is safe to call on non-pointers, and// will simply return the same value in such cases= reflect.Indirect()switch .Kind() {case reflect.Uint8:if len(.bytes) < 1 {return errWrongMarshalSize}if .CanInterface() {.bytes[0] = byte(.Uint())}.bytes = .bytes[1:]case reflect.Uint16:if len(.bytes) < 2 {return errWrongMarshalSize}if .CanInterface() {binary.BigEndian.PutUint16(.bytes, uint16(.Uint()))}.bytes = .bytes[2:]case reflect.Uint32:if len(.bytes) < 4 {return errWrongMarshalSize}if .CanInterface() {binary.BigEndian.PutUint32(.bytes, uint32(.Uint()))}.bytes = .bytes[4:]case reflect.Uint64:if len(.bytes) < 8 {return errWrongMarshalSize}if .CanInterface() {binary.BigEndian.PutUint64(.bytes, .Uint())}.bytes = .bytes[8:]case reflect.Slice:for := 0; < .Len(); ++ {if .Index().CanInterface() {if := .(.Index().Interface()); != nil {return}} else {.bytes = .bytes[.Index().Type().Size():]}}case reflect.Struct:for := 0; < .NumField(); ++ {:= .Type().Field().Tag.Get("encoding")if == omit {continue}if .Field().CanInterface() {if := .(.Field().Interface()); != nil {return}} else {:= int(.Field().Type().Size())if len(.bytes) < {return errWrongMarshalSize}.bytes = .bytes[:]}}default:return errBadStructMemberType}return nil}// Reads bytes from the buffer as necessary to populate// the structure passed as a parameter. This function will// modify the PacketBuffer.bytes slice to exclude those// bytes that have already been read.func ( *packetBuffer) ( interface{}) error { //nolint:gocognit:= reflect.ValueOf()if .Kind() != reflect.Ptr {return errBadReadParameter}:= reflect.Indirect()// If this is an interface, we need to make it concrete before using itif .Kind() == reflect.Interface {= reflect.ValueOf(.Interface())}= reflect.Indirect()switch .Kind() {case reflect.Uint8:if len(.bytes) < 1 {return errWrongMarshalSize}.SetUint(uint64(.bytes[0])).bytes = .bytes[1:]case reflect.Uint16:if len(.bytes) < 2 {return errWrongMarshalSize}.SetUint(uint64(binary.BigEndian.Uint16(.bytes))).bytes = .bytes[2:]case reflect.Uint32:if len(.bytes) < 4 {return errWrongMarshalSize}.SetUint(uint64(binary.BigEndian.Uint32(.bytes))).bytes = .bytes[4:]case reflect.Uint64:if len(.bytes) < 8 {return errWrongMarshalSize}.SetUint(binary.BigEndian.Uint64(.bytes)).bytes = .bytes[8:]case reflect.Slice:// If we encounter a slice, we consume the rest of the data// in the buffer and load it into the slice.for len(.bytes) > 0 {:= reflect.New(.Type().Elem())if := .(.Interface()); != nil {return}if .CanSet() {.Set(reflect.Append(, reflect.Indirect()))}}case reflect.Struct:for := 0; < .NumField(); ++ {:= .Type().Field().Tag.Get("encoding")if == omit {continue}if .Field().CanInterface() {:= .Field():= reflect.NewAt(.Type(), unsafe.Pointer(.UnsafeAddr())) //nolint:gosec // This is the only way to get a typed pointer to a structure's fieldif := .(.Interface()); != nil {return}} else {:= int(.Field().Type().Size())if len(.bytes) < {return errWrongMarshalSize}.bytes = .bytes[:]}}default:return errBadStructMemberType}return nil}// Consumes `size` bytes and returns them as an// independent PacketBufferfunc ( *packetBuffer) ( int) packetBuffer {if > len(.bytes) {= len(.bytes)}:= packetBuffer{bytes: .bytes[:]}.bytes = .bytes[:]return}// Returns the size that a structure will encode into.// This fuction doesn't check that Write() will succeed,// and may return unexpectedly large results for those// structures that Write() will fail onfunc wireSize( interface{}) int {:= reflect.ValueOf()// Indirect is safe to call on non-pointers, and// will simply return the same value in such cases= reflect.Indirect():= int(0)switch .Kind() {case reflect.Slice:for := 0; < .Len(); ++ {if .Index().CanInterface() {+= (.Index().Interface())} else {+= int(.Index().Type().Size())}}case reflect.Struct:for := 0; < .NumField(); ++ {:= .Type().Field().Tag.Get("encoding")if == omit {continue}if .Field().CanInterface() {+= (.Field().Interface())} else {+= int(.Field().Type().Size())}}default:= int(.Type().Size())}return}
![]() |
The pages are generated with Golds v0.8.2. (GOOS=linux GOARCH=amd64) Golds is a Go 101 project developed by Tapir Liu. PR and bug reports are welcome and can be submitted to the issue list. Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds. |