Skip to content

Commit 743031c

Browse files
committed
Refactor SDP parsing to use Span<T> and StringBuilder
Replaced Regex and string.Split with Span-based parsing for SDP lines and attributes. Updated ToString methods to use StringBuilder, reducing allocations and improving performance. Standardized string comparisons and improved parsing robustness. Changes applied across SDP, media format, connection, and security description classes for better efficiency and maintainability.
1 parent 6f49ffa commit 743031c

6 files changed

Lines changed: 1097 additions & 704 deletions

File tree

src/SIPSorcery/net/SDP/SDP.cs

Lines changed: 764 additions & 486 deletions
Large diffs are not rendered by default.

src/SIPSorcery/net/SDP/SDPAudioVideoMediaFormat.cs

Lines changed: 48 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
using System;
1818
using System.Collections.Generic;
1919
using System.Linq;
20+
using Polyfills;
21+
using SIPSorcery.Sys;
2022
using SIPSorceryMedia.Abstractions;
2123

2224
namespace SIPSorcery.Net
@@ -148,7 +150,7 @@ public bool IsH264
148150
{
149151
get
150152
{
151-
return (Rtpmap ?? "").ToUpperInvariant().Trim().StartsWith("H264");
153+
return Rtpmap.AsSpan().Trim().StartsWith("H264", StringComparison.OrdinalIgnoreCase);
152154
}
153155
}
154156

@@ -164,15 +166,15 @@ public bool IsMJPEG
164166
{
165167
get
166168
{
167-
return (Rtpmap ?? "").ToUpperInvariant().Trim().StartsWith("JPEG");
169+
return Rtpmap.AsSpan().Trim().StartsWith("JPEG", StringComparison.OrdinalIgnoreCase);
168170
}
169171
}
170172

171173
public bool isH265
172174
{
173175
get
174176
{
175-
return (Rtpmap ?? "").ToUpperInvariant().Trim().StartsWith("H265");
177+
return Rtpmap.AsSpan().Trim().StartsWith("H265", StringComparison.OrdinalIgnoreCase);
176178
}
177179
}
178180

@@ -194,18 +196,20 @@ public bool CheckCompatible()
194196

195197
private static Dictionary<string, string> ParseWebRtcParameters(string input)
196198
{
197-
var parameters = new Dictionary<string, string>();
199+
var parameters = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
198200
if (string.IsNullOrEmpty(input))
199201
{
200202
return parameters;
201203
}
202204

203-
foreach (var pair in input.Split(';'))
205+
Span<Range> keyValueRange = stackalloc Range[3];
206+
var inputSpan = input.AsSpan();
207+
foreach (var pairRange in inputSpan.Split(';'))
204208
{
205-
var keyValue = pair.Split('=');
206-
if (keyValue.Length == 2)
209+
var pair = inputSpan[pairRange];
210+
if (pair.Split(keyValueRange, '=') == 2)
207211
{
208-
parameters[keyValue[0].Trim().ToLowerInvariant()] = keyValue[1].Trim();
212+
parameters[pair[keyValueRange[0]].Trim().ToString()] = pair[keyValueRange[1]].Trim().ToString();
209213
}
210214
}
211215

@@ -314,7 +318,7 @@ public SDPAudioVideoMediaFormat(TextFormat textFormat)
314318
{
315319
Kind = SDPMediaTypesEnum.text;
316320
ID = textFormat.FormatID;
317-
Rtpmap = null;
321+
Rtpmap = null;
318322
Fmtp = textFormat.Parameters;
319323
_isEmpty = false;
320324

@@ -458,7 +462,7 @@ public static bool AreMatch(SDPAudioVideoMediaFormat format1, SDPAudioVideoMedia
458462
// rtpmap takes priority as well known format ID's can be overruled.
459463
if (format1.Rtpmap != null && format2.Rtpmap != null)
460464
{
461-
if (string.Equals(format1.Rtpmap.Trim(), format2.Rtpmap.Trim(), StringComparison.OrdinalIgnoreCase))
465+
if (format1.Rtpmap.AsSpan().Trim().Equals(format2.Rtpmap.AsSpan().Trim(), StringComparison.OrdinalIgnoreCase))
462466
{
463467
return true;
464468
}
@@ -471,7 +475,7 @@ public static bool AreMatch(SDPAudioVideoMediaFormat format1, SDPAudioVideoMedia
471475
return true;
472476
}
473477
return false;
474-
478+
475479
}
476480

477481
/// <summary>
@@ -558,24 +562,46 @@ public static bool TryParseRtpmap(string rtpmap, out string name, out int clockR
558562
}
559563
else
560564
{
561-
string[] fields = rtpmap.Trim().Split('/');
565+
var rtpmapSpan = rtpmap.AsSpan().Trim();
566+
var nameSpan = default(ReadOnlySpan<char>);
567+
var clockRateSpan = default(ReadOnlySpan<char>);
568+
var channelsSpan = default(ReadOnlySpan<char>);
569+
var fieldIndex = 0;
570+
571+
foreach (var fieldRange in rtpmapSpan.Split('/'))
572+
{
573+
var field = rtpmapSpan[fieldRange].Trim();
574+
575+
if (fieldIndex == 0)
576+
{
577+
nameSpan = field;
578+
}
579+
else if (fieldIndex == 1)
580+
{
581+
clockRateSpan = field;
582+
}
583+
else if (fieldIndex == 2)
584+
{
585+
channelsSpan = field;
586+
break;
587+
}
588+
589+
fieldIndex++;
590+
}
562591

563-
if (fields.Length >= 2)
592+
if (fieldIndex >= 1)
564593
{
565-
name = fields[0].Trim();
566-
if (!int.TryParse(fields[1].Trim(), out clockRate))
594+
if (!int.TryParse(clockRateSpan, out clockRate))
567595
{
568596
return false;
569597
}
570598

571-
if (fields.Length >= 3)
599+
if (!channelsSpan.IsEmpty && !int.TryParse(channelsSpan, out channels))
572600
{
573-
if (!int.TryParse(fields[2].Trim(), out channels))
574-
{
575-
return false;
576-
}
601+
return false;
577602
}
578603

604+
name = nameSpan.ToString();
579605
return true;
580606
}
581607
else
@@ -630,8 +656,8 @@ public static SDPAudioVideoMediaFormat GetFormatForName(List<SDPAudioVideoMediaF
630656
}
631657
else
632658
{
633-
return formats.Any(x => x.Name()?.ToLower() == formatName?.ToLower()) ?
634-
formats.First(x => x.Name()?.ToLower() == formatName?.ToLower()) :
659+
return formats.Any(x => string.Equals(x.Name(), formatName, StringComparison.OrdinalIgnoreCase)) ?
660+
formats.First(x => string.Equals(x.Name(), formatName, StringComparison.OrdinalIgnoreCase)) :
635661
Empty;
636662
}
637663
}

src/SIPSorcery/net/SDP/SDPConnectionInformation.cs

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,11 @@
1313
// BSD 3-Clause "New" or "Revised" License, see included LICENSE.md file.
1414
//-----------------------------------------------------------------------------
1515

16+
using System;
1617
using System.Net;
1718
using System.Net.Sockets;
19+
using Polyfills;
20+
using SIPSorcery.Sys;
1821

1922
namespace SIPSorcery.Net
2023
{
@@ -52,16 +55,34 @@ public SDPConnectionInformation(IPAddress connectionAddress)
5255
public static SDPConnectionInformation ParseConnectionInformation(string connectionLine)
5356
{
5457
SDPConnectionInformation connectionInfo = new SDPConnectionInformation();
55-
string[] connectionFields = connectionLine.Substring(2).Trim().Split(' ');
56-
connectionInfo.ConnectionNetworkType = connectionFields[0].Trim();
57-
connectionInfo.ConnectionAddressType = connectionFields[1].Trim();
58-
connectionInfo.ConnectionAddress = connectionFields[2].Trim();
58+
var connectionFields = connectionLine.AsSpan(2).Trim();
59+
var fieldIndex = 0;
60+
foreach (var fieldRange in connectionFields.Split(' '))
61+
{
62+
var field = connectionFields[fieldRange].Trim().ToString();
63+
if (fieldIndex == 0)
64+
{
65+
connectionInfo.ConnectionNetworkType = field;
66+
}
67+
else if (fieldIndex == 1)
68+
{
69+
connectionInfo.ConnectionAddressType = field;
70+
}
71+
else if (fieldIndex == 2)
72+
{
73+
connectionInfo.ConnectionAddress = field;
74+
break;
75+
}
76+
77+
fieldIndex++;
78+
}
79+
5980
return connectionInfo;
6081
}
6182

6283
public override string ToString()
6384
{
64-
return "c=" + ConnectionNetworkType + " " + ConnectionAddressType + " " + ConnectionAddress + m_CRLF;
85+
return $"c={ConnectionNetworkType} {ConnectionAddressType} {ConnectionAddress}{m_CRLF}";
6586
}
6687
}
6788
}

0 commit comments

Comments
 (0)