Skip to content

Commit 11e6d57

Browse files
committedMar 5, 2024
Add marshalSize to all types
Next will add marshalInto. This will allow us to do a zero allocation Marshal of a SessionDescription
1 parent 9704a3f commit 11e6d57

5 files changed

+232
-7
lines changed
 

‎common_description.go

+51-3
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ func (i Information) String() string {
1616
return string(i)
1717
}
1818

19+
func (i Information) marshalSize() (size int) {
20+
return len(i)
21+
}
22+
1923
// ConnectionInformation defines the representation for the "c=" field
2024
// containing connection data.
2125
type ConnectionInformation struct {
@@ -26,12 +30,22 @@ type ConnectionInformation struct {
2630

2731
func (c ConnectionInformation) String() string {
2832
parts := []string{c.NetworkType, c.AddressType}
29-
if c.Address != nil && c.Address.String() != "" {
33+
if c.Address != nil {
3034
parts = append(parts, c.Address.String())
3135
}
3236
return strings.Join(parts, " ")
3337
}
3438

39+
func (c ConnectionInformation) marshalSize() (size int) {
40+
size = len(c.NetworkType)
41+
size += 1 + len(c.AddressType)
42+
if c.Address != nil {
43+
size += 1 + c.Address.marshalSize()
44+
}
45+
46+
return
47+
}
48+
3549
// Address desribes a structured address token from within the "c=" field.
3650
type Address struct {
3751
Address string
@@ -53,6 +67,18 @@ func (c *Address) String() string {
5367
return strings.Join(parts, "/")
5468
}
5569

70+
func (c Address) marshalSize() (size int) {
71+
size = len(c.Address)
72+
if c.TTL != nil {
73+
size += 1 + lenUint(uint64(*c.TTL))
74+
}
75+
if c.Range != nil {
76+
size += 1 + lenUint(uint64(*c.Range))
77+
}
78+
79+
return
80+
}
81+
5682
// Bandwidth describes an optional field which denotes the proposed bandwidth
5783
// to be used by the session or media.
5884
type Bandwidth struct {
@@ -70,11 +96,24 @@ func (b Bandwidth) String() string {
7096
return output
7197
}
7298

99+
func (b Bandwidth) marshalSize() (size int) {
100+
if b.Experimental {
101+
size += 2
102+
}
103+
104+
size += len(b.Type) + 1 + lenUint(b.Bandwidth)
105+
return
106+
}
107+
73108
// EncryptionKey describes the "k=" which conveys encryption key information.
74109
type EncryptionKey string
75110

76-
func (s EncryptionKey) String() string {
77-
return string(s)
111+
func (e EncryptionKey) String() string {
112+
return string(e)
113+
}
114+
115+
func (e EncryptionKey) marshalSize() (size int) {
116+
return len(e.String())
78117
}
79118

80119
// Attribute describes the "a=" field which represents the primary means for
@@ -107,6 +146,15 @@ func (a Attribute) String() string {
107146
return output
108147
}
109148

149+
func (a Attribute) marshalSize() (size int) {
150+
size = len(a.Key)
151+
if len(a.Value) > 0 {
152+
size += 1 + len(a.Value)
153+
}
154+
155+
return size
156+
}
157+
110158
// IsICECandidate returns true if the attribute key equals "candidate".
111159
func (a Attribute) IsICECandidate() bool {
112160
return a.Key == "candidate"

‎marshal.go

+104-4
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ import (
4242
// k=* (encryption key)
4343
// a=* (zero or more media attribute lines)
4444
func (s *SessionDescription) Marshal() ([]byte, error) {
45-
m := make(marshaller, 0, 1024)
45+
m := make(marshaller, 0, s.MarshalSize())
4646

4747
m.addKeyValue("v=", s.Version.String())
4848
m.addKeyValue("o=", s.Origin.String())
@@ -122,7 +122,91 @@ func (s *SessionDescription) Marshal() ([]byte, error) {
122122
}
123123
}
124124

125-
return m.bytes(), nil
125+
return m, nil
126+
}
127+
128+
// `$type=` and CRLF size
129+
const lineBaseSize = 4
130+
131+
// MarshalSize returns the size of the SessionDescription once marshaled.
132+
func (s *SessionDescription) MarshalSize() (marshalSize int) {
133+
marshalSize += lineBaseSize + s.Version.marshalSize()
134+
marshalSize += lineBaseSize + s.Origin.marshalSize()
135+
marshalSize += lineBaseSize + s.SessionName.marshalSize()
136+
137+
if s.SessionInformation != nil {
138+
marshalSize += lineBaseSize + s.SessionInformation.marshalSize()
139+
}
140+
141+
if s.URI != nil {
142+
marshalSize += lineBaseSize + len(s.URI.String())
143+
}
144+
145+
if s.EmailAddress != nil {
146+
marshalSize += lineBaseSize + s.EmailAddress.marshalSize()
147+
}
148+
149+
if s.PhoneNumber != nil {
150+
marshalSize += lineBaseSize + s.PhoneNumber.marshalSize()
151+
}
152+
153+
if s.ConnectionInformation != nil {
154+
marshalSize += lineBaseSize + s.ConnectionInformation.marshalSize()
155+
}
156+
157+
for _, b := range s.Bandwidth {
158+
marshalSize += lineBaseSize + b.marshalSize()
159+
}
160+
161+
for _, td := range s.TimeDescriptions {
162+
marshalSize += lineBaseSize + td.Timing.marshalSize()
163+
for _, r := range td.RepeatTimes {
164+
marshalSize += lineBaseSize + r.marshalSize()
165+
}
166+
}
167+
168+
if len(s.TimeZones) > 0 {
169+
marshalSize += lineBaseSize
170+
171+
for i, z := range s.TimeZones {
172+
if i > 0 {
173+
marshalSize++
174+
}
175+
marshalSize += z.marshalSize()
176+
}
177+
}
178+
179+
if s.EncryptionKey != nil {
180+
marshalSize += lineBaseSize + s.EncryptionKey.marshalSize()
181+
}
182+
183+
for _, a := range s.Attributes {
184+
marshalSize += lineBaseSize + a.marshalSize()
185+
}
186+
187+
for _, md := range s.MediaDescriptions {
188+
marshalSize += lineBaseSize + md.MediaName.marshalSize()
189+
if md.MediaTitle != nil {
190+
marshalSize += lineBaseSize + md.MediaTitle.marshalSize()
191+
}
192+
if md.ConnectionInformation != nil {
193+
marshalSize += lineBaseSize + md.ConnectionInformation.marshalSize()
194+
}
195+
196+
for _, b := range md.Bandwidth {
197+
marshalSize += lineBaseSize + b.marshalSize()
198+
}
199+
200+
if md.EncryptionKey != nil {
201+
marshalSize += lineBaseSize + md.EncryptionKey.marshalSize()
202+
}
203+
204+
for _, a := range md.Attributes {
205+
marshalSize += lineBaseSize + a.marshalSize()
206+
}
207+
}
208+
209+
return marshalSize
126210
}
127211

128212
// marshaller contains state during marshaling.
@@ -132,11 +216,27 @@ func (m *marshaller) addKeyValue(key, value string) {
132216
if value == "" {
133217
return
134218
}
219+
135220
*m = append(*m, key...)
136221
*m = append(*m, value...)
137222
*m = append(*m, "\r\n"...)
138223
}
139224

140-
func (m *marshaller) bytes() []byte {
141-
return *m
225+
func lenUint(i uint64) (count int) {
226+
if i == 0 {
227+
return 1
228+
}
229+
230+
for i != 0 {
231+
i /= 10
232+
count++
233+
}
234+
return
235+
}
236+
237+
func lenInt(i int64) (count int) {
238+
if i < 0 {
239+
return lenUint(uint64(-i)) + 1
240+
}
241+
return lenUint(uint64(i))
142242
}

‎media_description.go

+33
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,15 @@ func (p *RangedPort) String() string {
6565
return output
6666
}
6767

68+
func (p RangedPort) marshalSize() (size int) {
69+
size = lenInt(int64(p.Value))
70+
if p.Range != nil {
71+
size += 1 + lenInt(int64(*p.Range))
72+
}
73+
74+
return
75+
}
76+
6877
// MediaName describes the "m=" field storage structure.
6978
type MediaName struct {
7079
Media string
@@ -81,3 +90,27 @@ func (m MediaName) String() string {
8190
strings.Join(m.Formats, " "),
8291
}, " ")
8392
}
93+
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++
102+
}
103+
104+
size += len(p)
105+
}
106+
107+
for i, f := range m.Formats {
108+
if i != len(m.Formats) {
109+
size++
110+
}
111+
112+
size += len(f)
113+
}
114+
115+
return size
116+
}

‎session_description.go

+30
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,10 @@ func (v Version) String() string {
8888
return strconv.Itoa(int(v))
8989
}
9090

91+
func (v Version) marshalSize() (size int) {
92+
return lenInt(int64(v))
93+
}
94+
9195
// Origin defines the structure for the "o=" field which provides the
9296
// originator of the session plus a session identifier and version number.
9397
type Origin struct {
@@ -111,6 +115,16 @@ func (o Origin) String() string {
111115
)
112116
}
113117

118+
func (o Origin) marshalSize() (size int) {
119+
return len(o.Username) +
120+
lenUint(o.SessionID) +
121+
lenUint(o.SessionVersion) +
122+
len(o.NetworkType) +
123+
len(o.AddressType) +
124+
len(o.UnicastAddress) +
125+
5
126+
}
127+
114128
// SessionName describes a structured representations for the "s=" field
115129
// and is the textual session name.
116130
type SessionName string
@@ -119,6 +133,10 @@ func (s SessionName) String() string {
119133
return string(s)
120134
}
121135

136+
func (s SessionName) marshalSize() (size int) {
137+
return len(s)
138+
}
139+
122140
// EmailAddress describes a structured representations for the "e=" line
123141
// which specifies email contact information for the person responsible for
124142
// the conference.
@@ -128,6 +146,10 @@ func (e EmailAddress) String() string {
128146
return string(e)
129147
}
130148

149+
func (e EmailAddress) marshalSize() (size int) {
150+
return len(e)
151+
}
152+
131153
// PhoneNumber describes a structured representations for the "p=" line
132154
// specify phone contact information for the person responsible for the
133155
// conference.
@@ -137,6 +159,10 @@ func (p PhoneNumber) String() string {
137159
return string(p)
138160
}
139161

162+
func (p PhoneNumber) marshalSize() (size int) {
163+
return len(p)
164+
}
165+
140166
// TimeZone defines the structured object for "z=" line which describes
141167
// repeated sessions scheduling.
142168
type TimeZone struct {
@@ -147,3 +173,7 @@ type TimeZone struct {
147173
func (z TimeZone) String() string {
148174
return strconv.FormatUint(z.AdjustmentTime, 10) + " " + strconv.FormatInt(z.Offset, 10)
149175
}
176+
177+
func (z TimeZone) marshalSize() (size int) {
178+
return lenUint(z.AdjustmentTime) + 1 + lenInt(z.Offset)
179+
}

‎time_description.go

+14
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@ func (t Timing) String() string {
3434
return output
3535
}
3636

37+
func (t Timing) marshalSize() (size int) {
38+
return lenUint(t.StartTime) + 1 + lenUint(t.StopTime)
39+
}
40+
3741
// RepeatTime describes the "r=" fields of the session description which
3842
// represents the intervals and durations for repeated scheduled sessions.
3943
type RepeatTime struct {
@@ -52,3 +56,13 @@ func (r RepeatTime) String() string {
5256

5357
return strings.Join(fields, " ")
5458
}
59+
60+
func (r RepeatTime) marshalSize() (size int) {
61+
size = lenInt(r.Interval)
62+
size += 1 + lenInt(r.Duration)
63+
for _, o := range r.Offsets {
64+
size += 1 + lenInt(o)
65+
}
66+
67+
return
68+
}

0 commit comments

Comments
 (0)
Please sign in to comment.