Skip to content

Commit 37a57e6

Browse files
committedMar 5, 2024
Add marshalInto to all types
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 '''
1 parent 11e6d57 commit 37a57e6

6 files changed

+167
-99
lines changed
 

Diff for: ‎common_description.go

+47-20
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,18 @@ package sdp
55

66
import (
77
"strconv"
8-
"strings"
98
)
109

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

1514
func (i Information) String() string {
16-
return string(i)
15+
return stringFromMarshal(i.marshalInto, i.marshalSize)
16+
}
17+
18+
func (i Information) marshalInto(b []byte) []byte {
19+
return append(b, i...)
1720
}
1821

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

3134
func (c ConnectionInformation) String() string {
32-
parts := []string{c.NetworkType, c.AddressType}
35+
return stringFromMarshal(c.marshalInto, c.marshalSize)
36+
}
37+
38+
func (c ConnectionInformation) marshalInto(b []byte) []byte {
39+
b = append(append(b, c.NetworkType...), ' ')
40+
b = append(b, c.AddressType...)
41+
3342
if c.Address != nil {
34-
parts = append(parts, c.Address.String())
43+
b = append(b, ' ')
44+
b = c.Address.marshalInto(b)
3545
}
36-
return strings.Join(parts, " ")
46+
47+
return b
3748
}
3849

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

5667
func (c *Address) String() string {
57-
var parts []string
58-
parts = append(parts, c.Address)
68+
return stringFromMarshal(c.marshalInto, c.marshalSize)
69+
}
70+
71+
func (c *Address) marshalInto(b []byte) []byte {
72+
b = append(b, c.Address...)
5973
if c.TTL != nil {
60-
parts = append(parts, strconv.Itoa(*c.TTL))
74+
b = append(b, '/')
75+
b = strconv.AppendInt(b, int64(*c.TTL), 10)
6176
}
62-
6377
if c.Range != nil {
64-
parts = append(parts, strconv.Itoa(*c.Range))
78+
b = append(b, '/')
79+
b = strconv.AppendInt(b, int64(*c.Range), 10)
6580
}
6681

67-
return strings.Join(parts, "/")
82+
return b
6883
}
6984

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

90105
func (b Bandwidth) String() string {
91-
var output string
106+
return stringFromMarshal(b.marshalInto, b.marshalSize)
107+
}
108+
109+
func (b Bandwidth) marshalInto(d []byte) []byte {
92110
if b.Experimental {
93-
output += "X-"
111+
d = append(d, "X-"...)
94112
}
95-
output += b.Type + ":" + strconv.FormatUint(b.Bandwidth, 10)
96-
return output
113+
d = append(append(d, b.Type...), ':')
114+
return strconv.AppendUint(d, b.Bandwidth, 10)
97115
}
98116

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

111129
func (e EncryptionKey) String() string {
112-
return string(e)
130+
return stringFromMarshal(e.marshalInto, e.marshalSize)
131+
}
132+
133+
func (e EncryptionKey) marshalInto(b []byte) []byte {
134+
return append(b, e...)
113135
}
114136

115137
func (e EncryptionKey) marshalSize() (size int) {
116-
return len(e.String())
138+
return len(e)
117139
}
118140

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

141163
func (a Attribute) String() string {
142-
output := a.Key
164+
return stringFromMarshal(a.marshalInto, a.marshalSize)
165+
}
166+
167+
func (a Attribute) marshalInto(b []byte) []byte {
168+
b = append(b, a.Key...)
143169
if len(a.Value) > 0 {
144-
output += ":" + a.Value
170+
b = append(append(b, ':'), a.Value...)
145171
}
146-
return output
172+
173+
return b
147174
}
148175

149176
func (a Attribute) marshalSize() (size int) {

Diff for: ‎marshal.go

+31-33
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,6 @@
33

44
package sdp
55

6-
import (
7-
"strings"
8-
)
9-
106
// Marshal takes a SDP struct to text
117
// https://tools.ietf.org/html/rfc4566#section-5
128
// Session description
@@ -44,81 +40,83 @@ import (
4440
func (s *SessionDescription) Marshal() ([]byte, error) {
4541
m := make(marshaller, 0, s.MarshalSize())
4642

47-
m.addKeyValue("v=", s.Version.String())
48-
m.addKeyValue("o=", s.Origin.String())
49-
m.addKeyValue("s=", s.SessionName.String())
43+
m.addKeyValue("v=", s.Version.marshalInto)
44+
m.addKeyValue("o=", s.Origin.marshalInto)
45+
m.addKeyValue("s=", s.SessionName.marshalInto)
5046

5147
if s.SessionInformation != nil {
52-
m.addKeyValue("i=", s.SessionInformation.String())
48+
m.addKeyValue("i=", s.SessionInformation.marshalInto)
5349
}
5450

5551
if s.URI != nil {
56-
m.addKeyValue("u=", s.URI.String())
52+
m = append(m, "u="...)
53+
m = append(m, s.URI.String()...)
54+
m = append(m, "\r\n"...)
5755
}
5856

5957
if s.EmailAddress != nil {
60-
m.addKeyValue("e=", s.EmailAddress.String())
58+
m.addKeyValue("e=", s.EmailAddress.marshalInto)
6159
}
6260

6361
if s.PhoneNumber != nil {
64-
m.addKeyValue("p=", s.PhoneNumber.String())
62+
m.addKeyValue("p=", s.PhoneNumber.marshalInto)
6563
}
6664

6765
if s.ConnectionInformation != nil {
68-
m.addKeyValue("c=", s.ConnectionInformation.String())
66+
m.addKeyValue("c=", s.ConnectionInformation.marshalInto)
6967
}
7068

7169
for _, b := range s.Bandwidth {
72-
m.addKeyValue("b=", b.String())
70+
m.addKeyValue("b=", b.marshalInto)
7371
}
7472

7573
for _, td := range s.TimeDescriptions {
76-
m.addKeyValue("t=", td.Timing.String())
74+
m.addKeyValue("t=", td.Timing.marshalInto)
7775
for _, r := range td.RepeatTimes {
78-
m.addKeyValue("r=", r.String())
76+
m.addKeyValue("r=", r.marshalInto)
7977
}
8078
}
8179

8280
if len(s.TimeZones) > 0 {
83-
var b strings.Builder
81+
m = append(m, "z="...)
8482
for i, z := range s.TimeZones {
8583
if i > 0 {
86-
b.WriteString(" ")
84+
m = append(m, ' ')
8785
}
88-
b.WriteString(z.String())
86+
m = z.marshalInto(m)
8987
}
90-
m.addKeyValue("z=", b.String())
88+
m = append(m, "\r\n"...)
9189
}
9290

9391
if s.EncryptionKey != nil {
94-
m.addKeyValue("k=", s.EncryptionKey.String())
92+
m.addKeyValue("k=", s.EncryptionKey.marshalInto)
9593
}
9694

9795
for _, a := range s.Attributes {
98-
m.addKeyValue("a=", a.String())
96+
m.addKeyValue("a=", a.marshalInto)
9997
}
10098

10199
for _, md := range s.MediaDescriptions {
102-
m.addKeyValue("m=", md.MediaName.String())
100+
m.addKeyValue("m=", md.MediaName.marshalInto)
103101

104102
if md.MediaTitle != nil {
105-
m.addKeyValue("i=", md.MediaTitle.String())
103+
m.addKeyValue("i=", md.MediaTitle.marshalInto)
106104
}
107105

108106
if md.ConnectionInformation != nil {
109-
m.addKeyValue("c=", md.ConnectionInformation.String())
107+
m.addKeyValue("c=", md.ConnectionInformation.marshalInto)
110108
}
111109

112110
for _, b := range md.Bandwidth {
113-
m.addKeyValue("b=", b.String())
111+
m.addKeyValue("b=", b.marshalInto)
114112
}
115113

116114
if md.EncryptionKey != nil {
117-
m.addKeyValue("k=", md.EncryptionKey.String())
115+
m.addKeyValue("k=", md.EncryptionKey.marshalInto)
118116
}
119117

120118
for _, a := range md.Attributes {
121-
m.addKeyValue("a=", a.String())
119+
m.addKeyValue("a=", a.marshalInto)
122120
}
123121
}
124122

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

215-
func (m *marshaller) addKeyValue(key, value string) {
216-
if value == "" {
217-
return
218-
}
219-
213+
func (m *marshaller) addKeyValue(key string, value func([]byte) []byte) {
220214
*m = append(*m, key...)
221-
*m = append(*m, value...)
215+
*m = value(*m)
222216
*m = append(*m, "\r\n"...)
223217
}
224218

@@ -240,3 +234,7 @@ func lenInt(i int64) (count int) {
240234
}
241235
return lenUint(uint64(i))
242236
}
237+
238+
func stringFromMarshal(marshalFunc func([]byte) []byte, sizeFunc func() int) string {
239+
return string(marshalFunc(make([]byte, 0, sizeFunc())))
240+
}

Diff for: ‎marshal_test.go

+2
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"testing"
1010
)
1111

12+
// nolint: goconst
1213
const (
1314
CanonicalMarshalSDP = "v=0\r\n" +
1415
"o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5\r\n" +
@@ -37,6 +38,7 @@ const (
3738
"a=rtpmap:99 h263-1998/90000\r\n"
3839
)
3940

41+
// nolint: goconst
4042
func TestMarshalCanonical(t *testing.T) {
4143
sd := &SessionDescription{
4244
Version: 0,

Diff for: ‎media_description.go

+34-22
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ package sdp
55

66
import (
77
"strconv"
8-
"strings"
98
)
109

1110
// MediaDescription represents a media type.
@@ -65,6 +64,15 @@ func (p *RangedPort) String() string {
6564
return output
6665
}
6766

67+
func (p RangedPort) marshalInto(b []byte) []byte {
68+
b = strconv.AppendInt(b, int64(p.Value), 10)
69+
if p.Range != nil {
70+
b = append(b, '/')
71+
b = strconv.AppendInt(b, int64(*p.Range), 10)
72+
}
73+
return b
74+
}
75+
6876
func (p RangedPort) marshalSize() (size int) {
6977
size = lenInt(int64(p.Value))
7078
if p.Range != nil {
@@ -83,34 +91,38 @@ type MediaName struct {
8391
}
8492

8593
func (m MediaName) String() string {
86-
return strings.Join([]string{
87-
m.Media,
88-
m.Port.String(),
89-
strings.Join(m.Protos, "/"),
90-
strings.Join(m.Formats, " "),
91-
}, " ")
94+
return stringFromMarshal(m.marshalInto, m.marshalSize)
9295
}
9396

94-
func (m MediaName) marshalSize() (size int) {
95-
size = len(m.Media)
96-
97-
size += 1 + m.Port.marshalSize()
98-
99-
for i, p := range m.Protos {
100-
if i != len(m.Protos) {
101-
size++
97+
func (m MediaName) marshalInto(b []byte) []byte {
98+
appendList := func(list []string, sep byte) {
99+
for i, p := range list {
100+
if i != 0 && i != len(list) {
101+
b = append(b, sep)
102+
}
103+
b = append(b, p...)
102104
}
103-
104-
size += len(p)
105105
}
106106

107-
for i, f := range m.Formats {
108-
if i != len(m.Formats) {
109-
size++
110-
}
107+
b = append(append(b, m.Media...), ' ')
108+
b = append(m.Port.marshalInto(b), ' ')
109+
appendList(m.Protos, '/')
110+
b = append(b, ' ')
111+
appendList(m.Formats, ' ')
112+
return b
113+
}
111114

112-
size += len(f)
115+
func (m MediaName) marshalSize() (size int) {
116+
listSize := func(list []string) {
117+
for _, p := range list {
118+
size += 1 + len(p)
119+
}
113120
}
114121

122+
size = len(m.Media)
123+
size += 1 + m.Port.marshalSize()
124+
listSize(m.Protos)
125+
listSize(m.Formats)
126+
115127
return size
116128
}

Diff for: ‎session_description.go

+37-15
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
package sdp
55

66
import (
7-
"fmt"
87
"net/url"
98
"strconv"
109
)
@@ -85,7 +84,11 @@ func (s *SessionDescription) Attribute(key string) (string, bool) {
8584
type Version int
8685

8786
func (v Version) String() string {
88-
return strconv.Itoa(int(v))
87+
return stringFromMarshal(v.marshalInto, v.marshalSize)
88+
}
89+
90+
func (v Version) marshalInto(b []byte) []byte {
91+
return strconv.AppendInt(b, int64(v), 10)
8992
}
9093

9194
func (v Version) marshalSize() (size int) {
@@ -104,15 +107,16 @@ type Origin struct {
104107
}
105108

106109
func (o Origin) String() string {
107-
return fmt.Sprintf(
108-
"%v %d %d %v %v %v",
109-
o.Username,
110-
o.SessionID,
111-
o.SessionVersion,
112-
o.NetworkType,
113-
o.AddressType,
114-
o.UnicastAddress,
115-
)
110+
return stringFromMarshal(o.marshalInto, o.marshalSize)
111+
}
112+
113+
func (o Origin) marshalInto(b []byte) []byte {
114+
b = append(append(b, o.Username...), ' ')
115+
b = append(strconv.AppendUint(b, o.SessionID, 10), ' ')
116+
b = append(strconv.AppendUint(b, o.SessionVersion, 10), ' ')
117+
b = append(append(b, o.NetworkType...), ' ')
118+
b = append(append(b, o.AddressType...), ' ')
119+
return append(b, o.UnicastAddress...)
116120
}
117121

118122
func (o Origin) marshalSize() (size int) {
@@ -130,7 +134,11 @@ func (o Origin) marshalSize() (size int) {
130134
type SessionName string
131135

132136
func (s SessionName) String() string {
133-
return string(s)
137+
return stringFromMarshal(s.marshalInto, s.marshalSize)
138+
}
139+
140+
func (s SessionName) marshalInto(b []byte) []byte {
141+
return append(b, s...)
134142
}
135143

136144
func (s SessionName) marshalSize() (size int) {
@@ -143,7 +151,11 @@ func (s SessionName) marshalSize() (size int) {
143151
type EmailAddress string
144152

145153
func (e EmailAddress) String() string {
146-
return string(e)
154+
return stringFromMarshal(e.marshalInto, e.marshalSize)
155+
}
156+
157+
func (e EmailAddress) marshalInto(b []byte) []byte {
158+
return append(b, e...)
147159
}
148160

149161
func (e EmailAddress) marshalSize() (size int) {
@@ -156,7 +168,11 @@ func (e EmailAddress) marshalSize() (size int) {
156168
type PhoneNumber string
157169

158170
func (p PhoneNumber) String() string {
159-
return string(p)
171+
return stringFromMarshal(p.marshalInto, p.marshalSize)
172+
}
173+
174+
func (p PhoneNumber) marshalInto(b []byte) []byte {
175+
return append(b, p...)
160176
}
161177

162178
func (p PhoneNumber) marshalSize() (size int) {
@@ -171,7 +187,13 @@ type TimeZone struct {
171187
}
172188

173189
func (z TimeZone) String() string {
174-
return strconv.FormatUint(z.AdjustmentTime, 10) + " " + strconv.FormatInt(z.Offset, 10)
190+
return stringFromMarshal(z.marshalInto, z.marshalSize)
191+
}
192+
193+
func (z TimeZone) marshalInto(b []byte) []byte {
194+
b = strconv.AppendUint(b, z.AdjustmentTime, 10)
195+
b = append(b, ' ')
196+
return strconv.AppendInt(b, z.Offset, 10)
175197
}
176198

177199
func (z TimeZone) marshalSize() (size int) {

Diff for: ‎time_description.go

+16-9
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ package sdp
55

66
import (
77
"strconv"
8-
"strings"
98
)
109

1110
// TimeDescription describes "t=", "r=" fields of the session description
@@ -29,9 +28,12 @@ type Timing struct {
2928
}
3029

3130
func (t Timing) String() string {
32-
output := strconv.FormatUint(t.StartTime, 10)
33-
output += " " + strconv.FormatUint(t.StopTime, 10)
34-
return output
31+
return stringFromMarshal(t.marshalInto, t.marshalSize)
32+
}
33+
34+
func (t Timing) marshalInto(b []byte) []byte {
35+
b = append(strconv.AppendUint(b, t.StartTime, 10), ' ')
36+
return strconv.AppendUint(b, t.StopTime, 10)
3537
}
3638

3739
func (t Timing) marshalSize() (size int) {
@@ -47,14 +49,19 @@ type RepeatTime struct {
4749
}
4850

4951
func (r RepeatTime) String() string {
50-
fields := make([]string, 0)
51-
fields = append(fields, strconv.FormatInt(r.Interval, 10))
52-
fields = append(fields, strconv.FormatInt(r.Duration, 10))
52+
return stringFromMarshal(r.marshalInto, r.marshalSize)
53+
}
54+
55+
func (r RepeatTime) marshalInto(b []byte) []byte {
56+
b = strconv.AppendInt(b, r.Interval, 10)
57+
b = append(b, ' ')
58+
b = strconv.AppendInt(b, r.Duration, 10)
5359
for _, value := range r.Offsets {
54-
fields = append(fields, strconv.FormatInt(value, 10))
60+
b = append(b, ' ')
61+
b = strconv.AppendInt(b, value, 10)
5562
}
5663

57-
return strings.Join(fields, " ")
64+
return b
5865
}
5966

6067
func (r RepeatTime) marshalSize() (size int) {

0 commit comments

Comments
 (0)
Please sign in to comment.