Skip to content

Commit

Permalink
Add marshalInto to all types
Browse files Browse the repository at this point in the history
This reduces the amount of allocations need to marshal
a SessionDescription

Before
'''
5000 ns/op            2064 B/op         53 allocs/op
5050 ns/op            2064 B/op         53 allocs/op
5033 ns/op            2064 B/op         53 allocs/op
5009 ns/op            2064 B/op         53 allocs/op
4991 ns/op            2064 B/op         53 allocs/op
'''

After
'''
2237 ns/op             800 B/op          7 allocs/op
2273 ns/op             800 B/op          7 allocs/op
2271 ns/op             800 B/op          7 allocs/op
2285 ns/op             800 B/op          7 allocs/op
2265 ns/op             800 B/op          7 allocs/op
'''
  • Loading branch information
Sean-Der committed Mar 5, 2024
1 parent 11e6d57 commit 37a57e6
Show file tree
Hide file tree
Showing 6 changed files with 167 additions and 99 deletions.
67 changes: 47 additions & 20 deletions common_description.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,18 @@ package sdp

import (
"strconv"
"strings"
)

// Information describes the "i=" field which provides textual information
// about the session.
type Information string

func (i Information) String() string {
return string(i)
return stringFromMarshal(i.marshalInto, i.marshalSize)
}

func (i Information) marshalInto(b []byte) []byte {
return append(b, i...)
}

func (i Information) marshalSize() (size int) {
Expand All @@ -29,11 +32,19 @@ type ConnectionInformation struct {
}

func (c ConnectionInformation) String() string {
parts := []string{c.NetworkType, c.AddressType}
return stringFromMarshal(c.marshalInto, c.marshalSize)
}

func (c ConnectionInformation) marshalInto(b []byte) []byte {
b = append(append(b, c.NetworkType...), ' ')
b = append(b, c.AddressType...)

if c.Address != nil {
parts = append(parts, c.Address.String())
b = append(b, ' ')
b = c.Address.marshalInto(b)
}
return strings.Join(parts, " ")

return b
}

func (c ConnectionInformation) marshalSize() (size int) {
Expand All @@ -54,17 +65,21 @@ type Address struct {
}

func (c *Address) String() string {
var parts []string
parts = append(parts, c.Address)
return stringFromMarshal(c.marshalInto, c.marshalSize)
}

func (c *Address) marshalInto(b []byte) []byte {
b = append(b, c.Address...)
if c.TTL != nil {
parts = append(parts, strconv.Itoa(*c.TTL))
b = append(b, '/')
b = strconv.AppendInt(b, int64(*c.TTL), 10)
}

if c.Range != nil {
parts = append(parts, strconv.Itoa(*c.Range))
b = append(b, '/')
b = strconv.AppendInt(b, int64(*c.Range), 10)
}

return strings.Join(parts, "/")
return b
}

func (c Address) marshalSize() (size int) {
Expand All @@ -88,12 +103,15 @@ type Bandwidth struct {
}

func (b Bandwidth) String() string {
var output string
return stringFromMarshal(b.marshalInto, b.marshalSize)
}

func (b Bandwidth) marshalInto(d []byte) []byte {
if b.Experimental {
output += "X-"
d = append(d, "X-"...)
}
output += b.Type + ":" + strconv.FormatUint(b.Bandwidth, 10)
return output
d = append(append(d, b.Type...), ':')
return strconv.AppendUint(d, b.Bandwidth, 10)
}

func (b Bandwidth) marshalSize() (size int) {
Expand All @@ -109,11 +127,15 @@ func (b Bandwidth) marshalSize() (size int) {
type EncryptionKey string

func (e EncryptionKey) String() string {
return string(e)
return stringFromMarshal(e.marshalInto, e.marshalSize)
}

func (e EncryptionKey) marshalInto(b []byte) []byte {
return append(b, e...)
}

func (e EncryptionKey) marshalSize() (size int) {
return len(e.String())
return len(e)
}

// Attribute describes the "a=" field which represents the primary means for
Expand All @@ -139,11 +161,16 @@ func NewAttribute(key, value string) Attribute {
}

func (a Attribute) String() string {
output := a.Key
return stringFromMarshal(a.marshalInto, a.marshalSize)
}

func (a Attribute) marshalInto(b []byte) []byte {
b = append(b, a.Key...)
if len(a.Value) > 0 {
output += ":" + a.Value
b = append(append(b, ':'), a.Value...)
}
return output

return b
}

func (a Attribute) marshalSize() (size int) {
Expand Down
64 changes: 31 additions & 33 deletions marshal.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,6 @@

package sdp

import (
"strings"
)

// Marshal takes a SDP struct to text
// https://tools.ietf.org/html/rfc4566#section-5
// Session description
Expand Down Expand Up @@ -44,81 +40,83 @@ import (
func (s *SessionDescription) Marshal() ([]byte, error) {
m := make(marshaller, 0, s.MarshalSize())

m.addKeyValue("v=", s.Version.String())
m.addKeyValue("o=", s.Origin.String())
m.addKeyValue("s=", s.SessionName.String())
m.addKeyValue("v=", s.Version.marshalInto)
m.addKeyValue("o=", s.Origin.marshalInto)
m.addKeyValue("s=", s.SessionName.marshalInto)

if s.SessionInformation != nil {
m.addKeyValue("i=", s.SessionInformation.String())
m.addKeyValue("i=", s.SessionInformation.marshalInto)
}

if s.URI != nil {
m.addKeyValue("u=", s.URI.String())
m = append(m, "u="...)
m = append(m, s.URI.String()...)
m = append(m, "\r\n"...)
}

if s.EmailAddress != nil {
m.addKeyValue("e=", s.EmailAddress.String())
m.addKeyValue("e=", s.EmailAddress.marshalInto)
}

if s.PhoneNumber != nil {
m.addKeyValue("p=", s.PhoneNumber.String())
m.addKeyValue("p=", s.PhoneNumber.marshalInto)
}

if s.ConnectionInformation != nil {
m.addKeyValue("c=", s.ConnectionInformation.String())
m.addKeyValue("c=", s.ConnectionInformation.marshalInto)
}

for _, b := range s.Bandwidth {
m.addKeyValue("b=", b.String())
m.addKeyValue("b=", b.marshalInto)
}

for _, td := range s.TimeDescriptions {
m.addKeyValue("t=", td.Timing.String())
m.addKeyValue("t=", td.Timing.marshalInto)
for _, r := range td.RepeatTimes {
m.addKeyValue("r=", r.String())
m.addKeyValue("r=", r.marshalInto)
}
}

if len(s.TimeZones) > 0 {
var b strings.Builder
m = append(m, "z="...)
for i, z := range s.TimeZones {
if i > 0 {
b.WriteString(" ")
m = append(m, ' ')
}
b.WriteString(z.String())
m = z.marshalInto(m)
}
m.addKeyValue("z=", b.String())
m = append(m, "\r\n"...)
}

if s.EncryptionKey != nil {
m.addKeyValue("k=", s.EncryptionKey.String())
m.addKeyValue("k=", s.EncryptionKey.marshalInto)
}

for _, a := range s.Attributes {
m.addKeyValue("a=", a.String())
m.addKeyValue("a=", a.marshalInto)
}

for _, md := range s.MediaDescriptions {
m.addKeyValue("m=", md.MediaName.String())
m.addKeyValue("m=", md.MediaName.marshalInto)

if md.MediaTitle != nil {
m.addKeyValue("i=", md.MediaTitle.String())
m.addKeyValue("i=", md.MediaTitle.marshalInto)
}

if md.ConnectionInformation != nil {
m.addKeyValue("c=", md.ConnectionInformation.String())
m.addKeyValue("c=", md.ConnectionInformation.marshalInto)
}

for _, b := range md.Bandwidth {
m.addKeyValue("b=", b.String())
m.addKeyValue("b=", b.marshalInto)
}

if md.EncryptionKey != nil {
m.addKeyValue("k=", md.EncryptionKey.String())
m.addKeyValue("k=", md.EncryptionKey.marshalInto)
}

for _, a := range md.Attributes {
m.addKeyValue("a=", a.String())
m.addKeyValue("a=", a.marshalInto)
}
}

Expand Down Expand Up @@ -212,13 +210,9 @@ func (s *SessionDescription) MarshalSize() (marshalSize int) {
// marshaller contains state during marshaling.
type marshaller []byte

func (m *marshaller) addKeyValue(key, value string) {
if value == "" {
return
}

func (m *marshaller) addKeyValue(key string, value func([]byte) []byte) {
*m = append(*m, key...)
*m = append(*m, value...)
*m = value(*m)
*m = append(*m, "\r\n"...)
}

Expand All @@ -240,3 +234,7 @@ func lenInt(i int64) (count int) {
}
return lenUint(uint64(i))
}

func stringFromMarshal(marshalFunc func([]byte) []byte, sizeFunc func() int) string {
return string(marshalFunc(make([]byte, 0, sizeFunc())))
}
2 changes: 2 additions & 0 deletions marshal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"testing"
)

// nolint: goconst
const (
CanonicalMarshalSDP = "v=0\r\n" +
"o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5\r\n" +
Expand Down Expand Up @@ -37,6 +38,7 @@ const (
"a=rtpmap:99 h263-1998/90000\r\n"
)

// nolint: goconst
func TestMarshalCanonical(t *testing.T) {
sd := &SessionDescription{
Version: 0,
Expand Down
56 changes: 34 additions & 22 deletions media_description.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ package sdp

import (
"strconv"
"strings"
)

// MediaDescription represents a media type.
Expand Down Expand Up @@ -65,6 +64,15 @@ func (p *RangedPort) String() string {
return output
}

func (p RangedPort) marshalInto(b []byte) []byte {
b = strconv.AppendInt(b, int64(p.Value), 10)
if p.Range != nil {
b = append(b, '/')
b = strconv.AppendInt(b, int64(*p.Range), 10)
}
return b
}

func (p RangedPort) marshalSize() (size int) {
size = lenInt(int64(p.Value))
if p.Range != nil {
Expand All @@ -83,34 +91,38 @@ type MediaName struct {
}

func (m MediaName) String() string {
return strings.Join([]string{
m.Media,
m.Port.String(),
strings.Join(m.Protos, "/"),
strings.Join(m.Formats, " "),
}, " ")
return stringFromMarshal(m.marshalInto, m.marshalSize)
}

func (m MediaName) marshalSize() (size int) {
size = len(m.Media)

size += 1 + m.Port.marshalSize()

for i, p := range m.Protos {
if i != len(m.Protos) {
size++
func (m MediaName) marshalInto(b []byte) []byte {
appendList := func(list []string, sep byte) {
for i, p := range list {
if i != 0 && i != len(list) {
b = append(b, sep)
}
b = append(b, p...)
}

size += len(p)
}

for i, f := range m.Formats {
if i != len(m.Formats) {
size++
}
b = append(append(b, m.Media...), ' ')
b = append(m.Port.marshalInto(b), ' ')
appendList(m.Protos, '/')
b = append(b, ' ')
appendList(m.Formats, ' ')
return b
}

size += len(f)
func (m MediaName) marshalSize() (size int) {
listSize := func(list []string) {
for _, p := range list {
size += 1 + len(p)
}
}

size = len(m.Media)
size += 1 + m.Port.marshalSize()
listSize(m.Protos)
listSize(m.Formats)

return size
}
Loading

0 comments on commit 37a57e6

Please sign in to comment.