diff --git a/src/SIPSorcery.csproj b/src/SIPSorcery.csproj
index 4fb7ad2eaa..9c09dee2c7 100755
--- a/src/SIPSorcery.csproj
+++ b/src/SIPSorcery.csproj
@@ -21,6 +21,7 @@
     
     
     
+    
     
     
     
@@ -35,15 +36,22 @@
     
   
 
+  
+    
+      all
+      runtime; build; native; contentfiles; analyzers; buildtransitive
+    
+  
+
   
     netstandard2.0;netstandard2.1;netcoreapp3.1;net462;net5.0;net6.0;net8.0
     12.0
     true
-    $(NoWarn);SYSLIB0050
+    $(NoWarn);SYSLIB0050;CS1591;CS1573;CS1587
     True
+    $(WarningsNotAsErrors);NU1510;CS0809;CS0618;CS8632
     true
     
-    $(NoWarn);CS1591;CS1573;CS1587
     Aaron Clauson, Christophe Irles, Rafael Soares & Contributors
     Copyright © 2010-2025 Aaron Clauson
     BSD-3-Clause
@@ -94,6 +102,7 @@
     true
     snupkg
     true
+    true
   
 
 
diff --git a/src/core/SIP/SIPAuthorisationDigest.cs b/src/core/SIP/SIPAuthorisationDigest.cs
index 02c9cb3f79..6cd760757e 100644
--- a/src/core/SIP/SIPAuthorisationDigest.cs
+++ b/src/core/SIP/SIPAuthorisationDigest.cs
@@ -256,24 +256,116 @@ public string GetDigest()
 
         public override string ToString()
         {
-            string authHeader = AuthHeaders.AUTH_DIGEST_KEY + " ";
-
-            authHeader += (Username != null && Username.Trim().Length != 0) ? AuthHeaders.AUTH_USERNAME_KEY + "=\"" + Username + "\"" : null;
-            authHeader += (authHeader.IndexOf('=') != -1) ? "," + AuthHeaders.AUTH_REALM_KEY + "=\"" + Realm + "\"" : AuthHeaders.AUTH_REALM_KEY + "=\"" + Realm + "\"";
-            authHeader += (Nonce != null) ? "," + AuthHeaders.AUTH_NONCE_KEY + "=\"" + Nonce + "\"" : null;
-            authHeader += (URI != null && URI.Trim().Length != 0) ? "," + AuthHeaders.AUTH_URI_KEY + "=\"" + URI + "\"" : null;
-            authHeader += (Response != null && Response.Length != 0) ? "," + AuthHeaders.AUTH_RESPONSE_KEY + "=\"" + Response + "\"" : null;
-            authHeader += (Cnonce != null) ? "," + AuthHeaders.AUTH_CNONCE_KEY + "=\"" + Cnonce + "\"" : null;
-            authHeader += (NonceCount != 0) ? "," + AuthHeaders.AUTH_NONCECOUNT_KEY + "=" + GetPaddedNonceCount(NonceCount) : null;
-            authHeader += (Qop != null) ? "," + AuthHeaders.AUTH_QOP_KEY + "=" + Qop : null;
-            authHeader += (Opaque != null) ? "," + AuthHeaders.AUTH_OPAQUE_KEY + "=\"" + Opaque + "\"" : null;
-
-            string algorithmID = (DigestAlgorithm == DigestAlgorithmsEnum.SHA256) ? SHA256_ALGORITHM_ID : DigestAlgorithm.ToString();
-            authHeader += (Response != null) ? "," + AuthHeaders.AUTH_ALGORITHM_KEY + "=" + algorithmID : null;
-
-            return authHeader;
+            var builder = new ValueStringBuilder();
+
+            try
+            {
+                ToString(ref builder);
+
+                return builder.ToString();
+            }
+            finally
+            {
+                builder.Dispose();
+            }
+        }
+
+        internal void ToString(ref ValueStringBuilder builder)
+        {
+            builder.Append(AuthHeaders.AUTH_DIGEST_KEY);
+            builder.Append(' ');
+
+            bool hasUsername = !string.IsNullOrWhiteSpace(Username);
+            if (hasUsername)
+            {
+                builder.Append(AuthHeaders.AUTH_USERNAME_KEY);
+                builder.Append("=\"");
+                builder.Append(Username);
+                builder.Append('"');
+            }
+
+            builder.Append(hasUsername ? ',' : '\0');
+            builder.Append(AuthHeaders.AUTH_REALM_KEY);
+            builder.Append("=\"");
+            builder.Append(Realm);
+            builder.Append('"');
+
+            if (Nonce != null)
+            {
+                builder.Append(',');
+                builder.Append(AuthHeaders.AUTH_NONCE_KEY);
+                builder.Append("=\"");
+                builder.Append(Nonce);
+                builder.Append('"');
+            }
+
+            if (!string.IsNullOrWhiteSpace(URI))
+            {
+                builder.Append(',');
+                builder.Append(AuthHeaders.AUTH_URI_KEY);
+                builder.Append("=\"");
+                builder.Append(URI);
+                builder.Append('"');
+            }
+
+            if (!string.IsNullOrEmpty(Response))
+            {
+                builder.Append(',');
+                builder.Append(AuthHeaders.AUTH_RESPONSE_KEY);
+                builder.Append("=\"");
+                builder.Append(Response);
+                builder.Append('"');
+            }
+
+            if (Cnonce != null)
+            {
+                builder.Append(',');
+                builder.Append(AuthHeaders.AUTH_CNONCE_KEY);
+                builder.Append("=\"");
+                builder.Append(Cnonce);
+                builder.Append('"');
+            }
+
+            if (NonceCount != 0)
+            {
+                builder.Append(',');
+                builder.Append(AuthHeaders.AUTH_NONCECOUNT_KEY);
+                builder.Append('=');
+                builder.Append(GetPaddedNonceCount(NonceCount));
+        }
+
+            if (Qop != null)
+            {
+                builder.Append(',');
+                builder.Append(AuthHeaders.AUTH_QOP_KEY);
+                builder.Append('=');
+                builder.Append(Qop);
+            }
+
+            if (Opaque != null)
+            {
+                builder.Append(',');
+                builder.Append(AuthHeaders.AUTH_OPAQUE_KEY);
+                builder.Append("=\"");
+                builder.Append(Opaque);
+                builder.Append('"');
+            }
+
+            if (Response != null)
+            {
+                builder.Append(',');
+                builder.Append(AuthHeaders.AUTH_ALGORITHM_KEY);
+                builder.Append('=');
+
+                string algorithmID = (DigestAlgorithm == DigestAlgorithmsEnum.SHA256)
+                ? SHA256_ALGORITHM_ID
+                : DigestAlgorithm.ToString();
+
+                builder.Append(algorithmID);
+            }
         }
 
+
         public SIPAuthorisationDigest CopyOf()
         {
             var copy = new SIPAuthorisationDigest(AuthorisationType, Realm, Username, Password, URI, Nonce, RequestType, DigestAlgorithm);
@@ -385,7 +477,7 @@ public static string GetHashHex(DigestAlgorithmsEnum hashAlg, string val)
                 case DigestAlgorithmsEnum.SHA256:
                     using (var hash = new SHA256CryptoServiceProvider())
                     {
-                        return hash.ComputeHash(Encoding.UTF8.GetBytes(val)).HexStr().ToLower();
+                        return hash.ComputeHash(Encoding.UTF8.GetBytes(val)).AsSpan().HexStr(lowercase: true);
                     }
                 // This is commented because RFC8760 does not have an SHA-512 option. Instead it's HSA-512-sess which
                 // means the SIP request body needs to be included in the digest as well. Including the body will require 
@@ -393,13 +485,13 @@ public static string GetHashHex(DigestAlgorithmsEnum hashAlg, string val)
                 //case DigestAlgorithmsEnum.SHA512:
                 //    using (var hash = new SHA512CryptoServiceProvider())
                 //    {
-                //        return hash.ComputeHash(Encoding.UTF8.GetBytes(val)).HexStr().ToLower();
+                //        return hash.ComputeHash(Encoding.UTF8.GetBytes(val)).HexStr(lowercase: false);
                 //    }
                 case DigestAlgorithmsEnum.MD5:
                 default:
                     using (var hash = new MD5CryptoServiceProvider())
                     {
-                        return hash.ComputeHash(Encoding.UTF8.GetBytes(val)).HexStr().ToLower();
+                        return hash.ComputeHash(Encoding.UTF8.GetBytes(val)).AsSpan().HexStr(lowercase: true);
                     }
             }
 #pragma warning restore SYSLIB0021
diff --git a/src/core/SIP/SIPConstants.cs b/src/core/SIP/SIPConstants.cs
index 32da80841d..5caa269c54 100644
--- a/src/core/SIP/SIPConstants.cs
+++ b/src/core/SIP/SIPConstants.cs
@@ -541,17 +541,68 @@ public static string SIPURIUserUnescape(string escapedString)
 
         public static string SIPURIParameterEscape(string unescapedString)
         {
-            string result = unescapedString;
-            if (!result.IsNullOrBlank())
+            // Characters that need escaping
+            ReadOnlySpan specialChars = stackalloc char[] { ';', '?', '@', '=', ',', ' ' };
+
+            // Early exit if no special characters are found
+            var unescapedSpan = unescapedString.AsSpan();
+            int nextIndex = unescapedSpan.IndexOfAny(specialChars);
+            if (nextIndex == -1)
             {
-                result = result.Replace(";", "%3B");
-                result = result.Replace("?", "%3F");
-                result = result.Replace("@", "%40");
-                result = result.Replace("=", "%3D");
-                result = result.Replace(",", "%2C");
-                result = result.Replace(" ", "%20");
+                // No escaping needed
+                return unescapedString;
+            }
+
+            var builder = new ValueStringBuilder();
+
+            try
+            {
+                SIPURIParameterEscape(ref builder, unescapedSpan);
+
+                return builder.ToString();
+            }
+            finally
+            {
+                builder.Dispose();
+            }
+        }
+
+        internal static void SIPURIParameterEscape(ref ValueStringBuilder builder, ReadOnlySpan unescapedSpan)
+        {
+            // Characters that need escaping
+            ReadOnlySpan specialChars = stackalloc char[] { ';', '?', '@', '=', ',', ' ' };
+
+            var currentIndex = 0;
+            var nextIndex = unescapedSpan.IndexOfAny(specialChars);
+            while (nextIndex != -1)
+            {
+                // Append everything before the special character
+                builder.Append(unescapedSpan.Slice(currentIndex, nextIndex - currentIndex));
+
+                // Escape the special character
+                switch (unescapedSpan[nextIndex])
+                {
+                    case ';': builder.Append("%3B"); break;
+                    case '?': builder.Append("%3F"); break;
+                    case '@': builder.Append("%40"); break;
+                    case '=': builder.Append("%3D"); break;
+                    case ',': builder.Append("%2C"); break;
+                    case ' ': builder.Append("%20"); break;
+                }
+
+                currentIndex = nextIndex + 1;
+                nextIndex = unescapedSpan.Slice(currentIndex).IndexOfAny(specialChars);
+                if (nextIndex != -1)
+                {
+                    nextIndex += currentIndex; // Adjust relative index to absolute
+                }
+            }
+
+            // Append the remaining part
+            if (currentIndex < unescapedSpan.Length)
+            {
+                builder.Append(unescapedSpan.Slice(currentIndex));
             }
-            return result;
         }
 
         public static string SIPURIParameterUnescape(string escapedString)
diff --git a/src/core/SIP/SIPHeader.cs b/src/core/SIP/SIPHeader.cs
index e46e041006..1eb72d4cc5 100644
--- a/src/core/SIP/SIPHeader.cs
+++ b/src/core/SIP/SIPHeader.cs
@@ -18,7 +18,6 @@
 using System.Net;
 using System.Runtime.CompilerServices;
 using System.Runtime.Serialization;
-using System.Text;
 using System.Text.RegularExpressions;
 using Microsoft.Extensions.Logging;
 using SIPSorcery.Sys;
@@ -355,10 +354,32 @@ public static SIPViaHeader[] ParseSIPViaHeader(string viaHeaderStr)
 
         public new string ToString()
         {
-            string sipViaHeader = SIPHeaders.SIP_HEADER_VIA + ": " + this.Version + "/" + this.Transport.ToString().ToUpper() + " " + ContactAddress;
-            sipViaHeader += (ViaParameters != null && ViaParameters.Count > 0) ? ViaParameters.ToString() : null;
+            var builder = new ValueStringBuilder();
+            try
+            {
+                ToString(ref builder);
+                return builder.ToString();
+            }
+            finally
+            {
+                builder.Dispose();
+            }
+        }
+
+        internal void ToString(ref ValueStringBuilder builder)
+        {
+            builder.Append(SIPHeaders.SIP_HEADER_VIA);
+            builder.Append(": ");
+            builder.Append(Version);
+            builder.Append('/');
+            builder.Append(Transport.ToString().ToUpperInvariant());
+            builder.Append(' ');
+            builder.Append(ContactAddress);
 
-            return sipViaHeader;
+            if (ViaParameters != null && ViaParameters.Count > 0)
+            {
+                builder.Append(ViaParameters.ToString());
+            }
         }
     }
 
@@ -466,7 +487,23 @@ public static SIPFromHeader ParseFromHeader(string fromHeaderStr)
 
         public override string ToString()
         {
-            return m_userField.ToString();
+            var builder = new ValueStringBuilder();
+
+            try
+            {
+                ToString(ref builder);
+
+                return builder.ToString();
+            }
+            finally
+            {
+                builder.Dispose();
+            }
+        }
+
+        internal void ToString(ref ValueStringBuilder builder)
+        {
+            m_userField.ToString(ref builder);
         }
 
         /// 
@@ -574,7 +611,23 @@ public static SIPToHeader ParseToHeader(string toHeaderStr)
 
         public override string ToString()
         {
-            return m_userField.ToString();
+            var builder = new ValueStringBuilder();
+
+            try
+            {
+                ToString(ref builder);
+
+                return builder.ToString();
+            }
+            finally
+            {
+                builder.Dispose();
+            }
+        }
+
+        internal void ToString(ref ValueStringBuilder builder)
+        {
+            m_userField.ToString(ref builder);
         }
     }
 
@@ -797,16 +850,30 @@ public override string ToString()
             {
                 return SIPConstants.SIP_REGISTER_REMOVEALL;
             }
+
+            var builder = new ValueStringBuilder();
+
+            try
+            {
+                ToString(ref builder);
+
+                return builder.ToString();
+            }
+            finally
+            {
+                builder.Dispose();
+            }
+        }
+
+        internal void ToString(ref ValueStringBuilder builder)
+        {
+            if (m_userField.URI.Host == SIPConstants.SIP_REGISTER_REMOVEALL)
+            {
+                builder.Append(SIPConstants.SIP_REGISTER_REMOVEALL);
+            }
             else
             {
-                //if (m_userField.URI.Protocol == SIPProtocolsEnum.UDP)
-                //{
-                return m_userField.ToString();
-                //}
-                //else
-                //{
-                //    return m_userField.ToContactString();
-                //}
+                m_userField.ToString(ref builder);
             }
         }
 
@@ -902,21 +969,38 @@ private static string BuildAuthorisationHeaderName(SIPAuthorisationHeadersEnum a
         }
 
         public override string ToString()
+        {
+            var builder = new ValueStringBuilder();
+
+            try
+            {
+                ToString(ref builder);
+
+                return builder.ToString();
+            }
+            finally
+            {
+                builder.Dispose();
+            }
+        }
+
+        internal void ToString(ref ValueStringBuilder builder)
         {
             if (SIPDigest != null)
             {
-                var authorisationHeaderType = (SIPDigest.AuthorisationResponseType != SIPAuthorisationHeadersEnum.Unknown) ? SIPDigest.AuthorisationResponseType : SIPDigest.AuthorisationType;
+                var authorisationHeaderType = (SIPDigest.AuthorisationResponseType != SIPAuthorisationHeadersEnum.Unknown)
+                    ? SIPDigest.AuthorisationResponseType
+                    : SIPDigest.AuthorisationType;
+
                 string authHeader = BuildAuthorisationHeaderName(authorisationHeaderType);
-                return authHeader + SIPDigest.ToString();
+                builder.Append(authHeader);
+                SIPDigest.ToString(ref builder);
             }
             else if (!string.IsNullOrEmpty(Value))
             {
                 string authHeader = BuildAuthorisationHeaderName(AuthorisationType);
-                return authHeader + Value;
-            }
-            else
-            {
-                return null;
+                builder.Append(authHeader);
+                builder.Append(Value);
             }
         }
     }
@@ -1153,7 +1237,7 @@ public void RemoveBottomRoute()
             if (m_sipRoutes.Count > 0)
             {
                 m_sipRoutes.RemoveAt(m_sipRoutes.Count - 1);
-            };
+            }
         }
 
         public SIPRouteSet Reversed()
@@ -1191,19 +1275,36 @@ public void ReplaceRoute(string origSocket, string replacementSocket)
             }
         }
 
-        public new string ToString()
+        public override string ToString()
+        {
+            var builder = new ValueStringBuilder();
+
+            try
+            {
+                ToString(ref builder);
+
+                return builder.ToString();
+            }
+            finally
         {
-            string routeStr = null;
+                builder.Dispose();
+            }
+        }
 
+        internal void ToString(ref ValueStringBuilder builder)
+        {
             if (m_sipRoutes != null && m_sipRoutes.Count > 0)
             {
                 for (int routeIndex = 0; routeIndex < m_sipRoutes.Count; routeIndex++)
                 {
-                    routeStr += (routeStr != null) ? "," + m_sipRoutes[routeIndex].ToString() : m_sipRoutes[routeIndex].ToString();
+                    if (routeIndex > 0)
+                    {
+                        builder.Append(",");
+                    }
+
+                    builder.Append(m_sipRoutes[routeIndex].ToString());
                 }
             }
-
-            return routeStr;
         }
     }
 
@@ -1307,17 +1408,30 @@ public void PushViaHeader(SIPViaHeader viaHeader)
 
         public new string ToString()
         {
-            string viaStr = null;
+            var builder = new ValueStringBuilder();
+
+            try
+            {
+                ToString(ref builder);
+
+                return builder.ToString();
+            }
+            finally
+            {
+                builder.Dispose();
+            }
+        }
 
+        internal void ToString(ref ValueStringBuilder builder)
+        {
             if (m_viaHeaders != null && m_viaHeaders.Count > 0)
             {
                 for (int viaIndex = 0; viaIndex < m_viaHeaders.Count; viaIndex++)
                 {
-                    viaStr += (m_viaHeaders[viaIndex]).ToString() + m_CRLF;
+                    m_viaHeaders[viaIndex].ToString(ref builder);
+                    builder.Append(m_CRLF);
                 }
             }
-
-            return viaStr;
         }
     }
 
@@ -2201,148 +2315,531 @@ public static SIPHeader ParseSIPHeaders(string[] headersCollection)
         /// String representing the SIP headers.
         public new string ToString()
         {
+            var builder = new ValueStringBuilder();
+
             try
             {
-                StringBuilder headersBuilder = new StringBuilder();
+                ToString(ref builder);
+
+                return builder.ToString();
+            }
+            catch (Exception excp)
+            {
+                logger.LogError(excp, "Exception SIPHeader ToString. Exception: {ErrorMessage}", excp.Message);
+                throw;
+            }
+            finally
+            {
+                builder.Dispose();
+            }
+        }
+
+        internal void ToString(ref ValueStringBuilder builder)
+        {
+            Vias.ToString(ref builder);
+
+            if (To != null)
+            {
+                builder.Append(SIPHeaders.SIP_HEADER_TO);
+                builder.Append(": ");
+                To.ToString(ref builder);
+                builder.Append(m_CRLF);
+            }
+
+            if (From != null)
+            {
+                builder.Append(SIPHeaders.SIP_HEADER_FROM);
+                builder.Append(": ");
+                From.ToString(ref builder);
+                builder.Append(m_CRLF);
+            }
+
+            if (CallId != null)
+                {
+                builder.Append(SIPHeaders.SIP_HEADER_CALLID);
+                builder.Append(": ");
+                builder.Append(CallId);
+                builder.Append(m_CRLF);
+                }
 
-                headersBuilder.Append(Vias.ToString());
+            if (CSeq >= 0)
+            {
+                builder.Append(SIPHeaders.SIP_HEADER_CSEQ);
+                builder.Append(": ");
+                builder.Append(CSeq);
 
-                string cseqField = null;
-                if (this.CSeq >= 0)
+                if (CSeqMethod != SIPMethodsEnum.NONE)
                 {
-                    cseqField = (this.CSeqMethod != SIPMethodsEnum.NONE) ? this.CSeq + " " + this.CSeqMethod.ToString() : this.CSeq.ToString();
+                    builder.Append(' ');
+                    builder.Append(CSeqMethod.ToString());
                 }
 
-                headersBuilder.Append((To != null) ? SIPHeaders.SIP_HEADER_TO + ": " + this.To.ToString() + m_CRLF : null);
-                headersBuilder.Append((From != null) ? SIPHeaders.SIP_HEADER_FROM + ": " + this.From.ToString() + m_CRLF : null);
-                headersBuilder.Append((CallId != null) ? SIPHeaders.SIP_HEADER_CALLID + ": " + this.CallId + m_CRLF : null);
-                headersBuilder.Append((CSeq >= 0) ? SIPHeaders.SIP_HEADER_CSEQ + ": " + cseqField + m_CRLF : null);
+                builder.Append(m_CRLF);
+            }
 
                 #region Appending Contact header.
 
                 if (Contact != null && Contact.Count == 1)
                 {
-                    headersBuilder.Append(SIPHeaders.SIP_HEADER_CONTACT + ": " + Contact[0].ToString() + m_CRLF);
+                builder.Append(SIPHeaders.SIP_HEADER_CONTACT);
+                builder.Append(": ");
+                Contact[0].ToString(ref builder);
+                builder.Append(m_CRLF);
                 }
                 else if (Contact != null && Contact.Count > 1)
                 {
-                    StringBuilder contactsBuilder = new StringBuilder();
-                    contactsBuilder.Append(SIPHeaders.SIP_HEADER_CONTACT + ": ");
+                builder.Append(SIPHeaders.SIP_HEADER_CONTACT);
+                builder.Append(": ");
 
                     bool firstContact = true;
                     foreach (SIPContactHeader contactHeader in Contact)
                     {
-                        if (firstContact)
+                    if (!firstContact)
                         {
-                            contactsBuilder.Append(contactHeader.ToString());
+                        builder.Append(',');
                         }
-                        else
+
+                    contactHeader.ToString(ref builder);
+                    firstContact = false;
+                }
+
+                builder.Append(m_CRLF);
+            }
+
+            #endregion
+
+            if (MaxForwards >= 0)
+            {
+                builder.Append(SIPHeaders.SIP_HEADER_MAXFORWARDS);
+                builder.Append(": ");
+                builder.Append(MaxForwards);
+                builder.Append(m_CRLF);
+            }
+
+            if (Routes != null && Routes.Length > 0)
+            {
+                builder.Append(SIPHeaders.SIP_HEADER_ROUTE);
+                builder.Append(": ");
+                Routes.ToString(ref builder);
+                builder.Append(m_CRLF);
+            }
+
+            if (RecordRoutes != null && RecordRoutes.Length > 0)
+            {
+                builder.Append(SIPHeaders.SIP_HEADER_RECORDROUTE);
+                builder.Append(": ");
+                RecordRoutes.ToString(ref builder);
+                builder.Append(m_CRLF);
+            }
+
+            if (UserAgent != null && UserAgent.Trim().Length != 0)
+            {
+                builder.Append(SIPHeaders.SIP_HEADER_USERAGENT);
+                builder.Append(": ");
+                builder.Append(UserAgent);
+                builder.Append(m_CRLF);
+            }
+
+            if (Expires != -1)
+            {
+                builder.Append(SIPHeaders.SIP_HEADER_EXPIRES);
+                builder.Append(": ");
+                builder.Append(Expires);
+                builder.Append(m_CRLF);
+            }
+
+            if (MinExpires != -1)
+            {
+                builder.Append(SIPHeaders.SIP_HEADER_MINEXPIRES);
+                builder.Append(": ");
+                builder.Append(MinExpires);
+                builder.Append(m_CRLF);
+            }
+
+            if (Accept != null)
+            {
+                builder.Append(SIPHeaders.SIP_HEADER_ACCEPT);
+                builder.Append(": ");
+                builder.Append(Accept);
+                builder.Append(m_CRLF);
+            }
+
+            if (AcceptEncoding != null)
                         {
-                            contactsBuilder.Append("," + contactHeader.ToString());
+                builder.Append(SIPHeaders.SIP_HEADER_ACCEPTENCODING);
+                builder.Append(": ");
+                builder.Append(AcceptEncoding);
+                builder.Append(m_CRLF);
                         }
 
-                        firstContact = false;
+            if (AcceptLanguage != null)
+            {
+                builder.Append(SIPHeaders.SIP_HEADER_ACCEPTLANGUAGE);
+                builder.Append(": ");
+                builder.Append(AcceptLanguage);
+                builder.Append(m_CRLF);
                     }
 
-                    headersBuilder.Append(contactsBuilder.ToString() + m_CRLF);
+            if (Allow != null)
+            {
+                builder.Append(SIPHeaders.SIP_HEADER_ALLOW);
+                builder.Append(": ");
+                builder.Append(Allow);
+                builder.Append(m_CRLF);
                 }
 
-                #endregion
-
-                headersBuilder.Append((MaxForwards >= 0) ? SIPHeaders.SIP_HEADER_MAXFORWARDS + ": " + this.MaxForwards + m_CRLF : null);
-                headersBuilder.Append((Routes != null && Routes.Length > 0) ? SIPHeaders.SIP_HEADER_ROUTE + ": " + Routes.ToString() + m_CRLF : null);
-                headersBuilder.Append((RecordRoutes != null && RecordRoutes.Length > 0) ? SIPHeaders.SIP_HEADER_RECORDROUTE + ": " + RecordRoutes.ToString() + m_CRLF : null);
-                headersBuilder.Append((UserAgent != null && UserAgent.Trim().Length != 0) ? SIPHeaders.SIP_HEADER_USERAGENT + ": " + this.UserAgent + m_CRLF : null);
-                headersBuilder.Append((Expires != -1) ? SIPHeaders.SIP_HEADER_EXPIRES + ": " + this.Expires + m_CRLF : null);
-                headersBuilder.Append((MinExpires != -1) ? SIPHeaders.SIP_HEADER_MINEXPIRES + ": " + this.MinExpires + m_CRLF : null);
-                headersBuilder.Append((Accept != null) ? SIPHeaders.SIP_HEADER_ACCEPT + ": " + this.Accept + m_CRLF : null);
-                headersBuilder.Append((AcceptEncoding != null) ? SIPHeaders.SIP_HEADER_ACCEPTENCODING + ": " + this.AcceptEncoding + m_CRLF : null);
-                headersBuilder.Append((AcceptLanguage != null) ? SIPHeaders.SIP_HEADER_ACCEPTLANGUAGE + ": " + this.AcceptLanguage + m_CRLF : null);
-                headersBuilder.Append((Allow != null) ? SIPHeaders.SIP_HEADER_ALLOW + ": " + this.Allow + m_CRLF : null);
-                headersBuilder.Append((AlertInfo != null) ? SIPHeaders.SIP_HEADER_ALERTINFO + ": " + this.AlertInfo + m_CRLF : null);
-                headersBuilder.Append((AuthenticationInfo != null) ? SIPHeaders.SIP_HEADER_AUTHENTICATIONINFO + ": " + this.AuthenticationInfo + m_CRLF : null);
+            if (AlertInfo != null)
+            {
+                builder.Append(SIPHeaders.SIP_HEADER_ALERTINFO);
+                builder.Append(": ");
+                builder.Append(AlertInfo);
+                builder.Append(m_CRLF);
+            }
+
+            if (AuthenticationInfo != null)
+            {
+                builder.Append(SIPHeaders.SIP_HEADER_AUTHENTICATIONINFO);
+                builder.Append(": ");
+                builder.Append(AuthenticationInfo);
+                builder.Append(m_CRLF);
+            }
 
                 if (AuthenticationHeaders.Count > 0)
                 {
                     foreach (var authHeader in AuthenticationHeaders)
                     {
-                        var value = authHeader.ToString();
-                        if (value != null)
+                    if (authHeader is not null)
                         {
-                            headersBuilder.Append(authHeader.ToString() + m_CRLF);
+                        authHeader.ToString(ref builder);
+                        builder.Append(m_CRLF);
+                    }
+                }
+            }
+
+            if (CallInfo != null)
+            {
+                builder.Append(SIPHeaders.SIP_HEADER_CALLINFO);
+                builder.Append(": ");
+                builder.Append(CallInfo);
+                builder.Append(m_CRLF);
+            }
+
+            if (ContentDisposition != null)
+            {
+                builder.Append(SIPHeaders.SIP_HEADER_CONTENT_DISPOSITION);
+                builder.Append(": ");
+                builder.Append(ContentDisposition);
+                builder.Append(m_CRLF);
+            }
+
+            if (ContentEncoding != null)
+            {
+                builder.Append(SIPHeaders.SIP_HEADER_CONTENT_ENCODING);
+                builder.Append(": ");
+                builder.Append(ContentEncoding);
+                builder.Append(m_CRLF);
+            }
+
+            if (ContentLanguage != null)
+            {
+                builder.Append(SIPHeaders.SIP_HEADER_CONTENT_LANGUAGE);
+                builder.Append(": ");
+                builder.Append(ContentLanguage);
+                builder.Append(m_CRLF);
+            }
+
+            if (Date != null)
+            {
+                builder.Append(SIPHeaders.SIP_HEADER_DATE);
+                builder.Append(": ");
+                builder.Append(Date);
+                builder.Append(m_CRLF);
+            }
+
+            if (ErrorInfo != null)
+            {
+                builder.Append(SIPHeaders.SIP_HEADER_ERROR_INFO);
+                builder.Append(": ");
+                builder.Append(ErrorInfo);
+                builder.Append(m_CRLF);
+            }
+
+            if (InReplyTo != null)
+            {
+                builder.Append(SIPHeaders.SIP_HEADER_IN_REPLY_TO);
+                builder.Append(": ");
+                builder.Append(InReplyTo);
+                builder.Append(m_CRLF);
+            }
+
+            if (Organization != null)
+            {
+                builder.Append(SIPHeaders.SIP_HEADER_ORGANIZATION);
+                builder.Append(": ");
+                builder.Append(Organization);
+                builder.Append(m_CRLF);
+            }
+
+            if (Priority != null)
+            {
+                builder.Append(SIPHeaders.SIP_HEADER_PRIORITY);
+                builder.Append(": ");
+                builder.Append(Priority);
+                builder.Append(m_CRLF);
                         }
+
+            if (ProxyRequire != null)
+            {
+                builder.Append(SIPHeaders.SIP_HEADER_PROXY_REQUIRE);
+                builder.Append(": ");
+                builder.Append(ProxyRequire);
+                builder.Append(m_CRLF);
+            }
+
+            if (ReplyTo != null)
+            {
+                builder.Append(SIPHeaders.SIP_HEADER_REPLY_TO);
+                builder.Append(": ");
+                builder.Append(ReplyTo);
+                builder.Append(m_CRLF);
+            }
+
+            if (Require != null)
+            {
+                builder.Append(SIPHeaders.SIP_HEADER_REQUIRE);
+                builder.Append(": ");
+                builder.Append(Require);
+                builder.Append(m_CRLF);
+            }
+
+            if (RetryAfter != null)
+            {
+                builder.Append(SIPHeaders.SIP_HEADER_RETRY_AFTER);
+                builder.Append(": ");
+                builder.Append(RetryAfter);
+                builder.Append(m_CRLF);
+            }
+
+            if (Server != null && Server.Trim().Length != 0)
+            {
+                builder.Append(SIPHeaders.SIP_HEADER_SERVER);
+                builder.Append(": ");
+                builder.Append(Server);
+                builder.Append(m_CRLF);
+            }
+
+            if (Subject != null)
+            {
+                builder.Append(SIPHeaders.SIP_HEADER_SUBJECT);
+                builder.Append(": ");
+                builder.Append(Subject);
+                builder.Append(m_CRLF);
                     }
+
+            if (Supported != null)
+            {
+                builder.Append(SIPHeaders.SIP_HEADER_SUPPORTED);
+                builder.Append(": ");
+                builder.Append(Supported);
+                builder.Append(m_CRLF);
                 }
-                headersBuilder.Append((CallInfo != null) ? SIPHeaders.SIP_HEADER_CALLINFO + ": " + this.CallInfo + m_CRLF : null);
-                headersBuilder.Append((ContentDisposition != null) ? SIPHeaders.SIP_HEADER_CONTENT_DISPOSITION + ": " + this.ContentDisposition + m_CRLF : null);
-                headersBuilder.Append((ContentEncoding != null) ? SIPHeaders.SIP_HEADER_CONTENT_ENCODING + ": " + this.ContentEncoding + m_CRLF : null);
-                headersBuilder.Append((ContentLanguage != null) ? SIPHeaders.SIP_HEADER_CONTENT_LANGUAGE + ": " + this.ContentLanguage + m_CRLF : null);
-                headersBuilder.Append((Date != null) ? SIPHeaders.SIP_HEADER_DATE + ": " + Date + m_CRLF : null);
-                headersBuilder.Append((ErrorInfo != null) ? SIPHeaders.SIP_HEADER_ERROR_INFO + ": " + this.ErrorInfo + m_CRLF : null);
-                headersBuilder.Append((InReplyTo != null) ? SIPHeaders.SIP_HEADER_IN_REPLY_TO + ": " + this.InReplyTo + m_CRLF : null);
-                headersBuilder.Append((Organization != null) ? SIPHeaders.SIP_HEADER_ORGANIZATION + ": " + this.Organization + m_CRLF : null);
-                headersBuilder.Append((Priority != null) ? SIPHeaders.SIP_HEADER_PRIORITY + ": " + Priority + m_CRLF : null);
-                headersBuilder.Append((ProxyRequire != null) ? SIPHeaders.SIP_HEADER_PROXY_REQUIRE + ": " + this.ProxyRequire + m_CRLF : null);
-                headersBuilder.Append((ReplyTo != null) ? SIPHeaders.SIP_HEADER_REPLY_TO + ": " + this.ReplyTo + m_CRLF : null);
-                headersBuilder.Append((Require != null) ? SIPHeaders.SIP_HEADER_REQUIRE + ": " + Require + m_CRLF : null);
-                headersBuilder.Append((RetryAfter != null) ? SIPHeaders.SIP_HEADER_RETRY_AFTER + ": " + this.RetryAfter + m_CRLF : null);
-                headersBuilder.Append((Server != null && Server.Trim().Length != 0) ? SIPHeaders.SIP_HEADER_SERVER + ": " + this.Server + m_CRLF : null);
-                headersBuilder.Append((Subject != null) ? SIPHeaders.SIP_HEADER_SUBJECT + ": " + Subject + m_CRLF : null);
-                headersBuilder.Append((Supported != null) ? SIPHeaders.SIP_HEADER_SUPPORTED + ": " + Supported + m_CRLF : null);
-                headersBuilder.Append((Timestamp != null) ? SIPHeaders.SIP_HEADER_TIMESTAMP + ": " + Timestamp + m_CRLF : null);
-                headersBuilder.Append((Unsupported != null) ? SIPHeaders.SIP_HEADER_UNSUPPORTED + ": " + Unsupported + m_CRLF : null);
-                headersBuilder.Append((Warning != null) ? SIPHeaders.SIP_HEADER_WARNING + ": " + Warning + m_CRLF : null);
-                headersBuilder.Append((ETag != null) ? SIPHeaders.SIP_HEADER_ETAG + ": " + ETag + m_CRLF : null);
-                headersBuilder.Append(SIPHeaders.SIP_HEADER_CONTENTLENGTH + ": " + this.ContentLength + m_CRLF);
-                if (this.ContentType != null && this.ContentType.Trim().Length > 0)
+
+            if (Timestamp != null)
+            {
+                builder.Append(SIPHeaders.SIP_HEADER_TIMESTAMP);
+                builder.Append(": ");
+                builder.Append(Timestamp);
+                builder.Append(m_CRLF);
+            }
+
+            if (Unsupported != null)
+            {
+                builder.Append(SIPHeaders.SIP_HEADER_UNSUPPORTED);
+                builder.Append(": ");
+                builder.Append(Unsupported);
+                builder.Append(m_CRLF);
+            }
+
+            if (Warning != null)
+            {
+                builder.Append(SIPHeaders.SIP_HEADER_WARNING);
+                builder.Append(": ");
+                builder.Append(Warning);
+                builder.Append(m_CRLF);
+            }
+
+            if (ETag != null)
+            {
+                builder.Append(SIPHeaders.SIP_HEADER_ETAG);
+                builder.Append(": ");
+                builder.Append(ETag);
+                builder.Append(m_CRLF);
+            }
+
+            builder.Append(SIPHeaders.SIP_HEADER_CONTENTLENGTH);
+            builder.Append(": ");
+            builder.Append(ContentLength);
+            builder.Append(m_CRLF);
+
+            if (ContentType != null && ContentType.Trim().Length > 0)
                 {
-                    headersBuilder.Append(SIPHeaders.SIP_HEADER_CONTENTTYPE + ": " + this.ContentType + m_CRLF);
+                builder.Append(SIPHeaders.SIP_HEADER_CONTENTTYPE);
+                builder.Append(": ");
+                builder.Append(ContentType);
+                builder.Append(m_CRLF);
                 }
 
                 // Non-core SIP headers.
-                headersBuilder.Append((AllowEvents != null) ? SIPHeaders.SIP_HEADER_ALLOW_EVENTS + ": " + AllowEvents + m_CRLF : null);
-                headersBuilder.Append((Event != null) ? SIPHeaders.SIP_HEADER_EVENT + ": " + Event + m_CRLF : null);
-                headersBuilder.Append((SubscriptionState != null) ? SIPHeaders.SIP_HEADER_SUBSCRIPTION_STATE + ": " + SubscriptionState + m_CRLF : null);
-                headersBuilder.Append((ReferSub != null) ? SIPHeaders.SIP_HEADER_REFERSUB + ": " + ReferSub + m_CRLF : null);
-                headersBuilder.Append((ReferTo != null) ? SIPHeaders.SIP_HEADER_REFERTO + ": " + ReferTo + m_CRLF : null);
-                headersBuilder.Append((ReferredBy != null) ? SIPHeaders.SIP_HEADER_REFERREDBY + ": " + ReferredBy + m_CRLF : null);
-                headersBuilder.Append((Replaces != null) ? SIPHeaders.SIP_HEADER_REPLACES + ": " + Replaces + m_CRLF : null);
-                headersBuilder.Append((Reason != null) ? SIPHeaders.SIP_HEADER_REASON + ": " + Reason + m_CRLF : null);
-                headersBuilder.Append((RSeq != -1) ? SIPHeaders.SIP_HEADER_RELIABLE_SEQ + ": " + RSeq + m_CRLF : null);
-                headersBuilder.Append((RAckRSeq != -1) ? SIPHeaders.SIP_HEADER_RELIABLE_ACK + ": " + RAckRSeq + " " + RAckCSeq + " " + RAckCSeqMethod + m_CRLF : null);
+            if (AllowEvents != null)
+            {
+                builder.Append(SIPHeaders.SIP_HEADER_ALLOW_EVENTS);
+                builder.Append(": ");
+                builder.Append(AllowEvents);
+                builder.Append(m_CRLF);
+            }
+
+            if (Event != null)
+            {
+                builder.Append(SIPHeaders.SIP_HEADER_EVENT);
+                builder.Append(": ");
+                builder.Append(Event);
+                builder.Append(m_CRLF);
+            }
+
+            if (SubscriptionState != null)
+            {
+                builder.Append(SIPHeaders.SIP_HEADER_SUBSCRIPTION_STATE);
+                builder.Append(": ");
+                builder.Append(SubscriptionState);
+                builder.Append(m_CRLF);
+            }
+
+            if (ReferSub != null)
+            {
+                builder.Append(SIPHeaders.SIP_HEADER_REFERSUB);
+                builder.Append(": ");
+                builder.Append(ReferSub);
+                builder.Append(m_CRLF);
+            }
+
+            if (ReferTo != null)
+            {
+                builder.Append(SIPHeaders.SIP_HEADER_REFERTO);
+                builder.Append(": ");
+                builder.Append(ReferTo);
+                builder.Append(m_CRLF);
+            }
+
+            if (ReferredBy != null)
+            {
+                builder.Append(SIPHeaders.SIP_HEADER_REFERREDBY);
+                builder.Append(": ");
+                builder.Append(ReferredBy);
+                builder.Append(m_CRLF);
+            }
+
+            if (Replaces != null)
+            {
+                builder.Append(SIPHeaders.SIP_HEADER_REPLACES);
+                builder.Append(": ");
+                builder.Append(Replaces);
+                builder.Append(m_CRLF);
+            }
+
+            if (Reason != null)
+            {
+                builder.Append(SIPHeaders.SIP_HEADER_REASON);
+                builder.Append(": ");
+                builder.Append(Reason);
+                builder.Append(m_CRLF);
+            }
+
+            if (RSeq != -1)
+            {
+                builder.Append(SIPHeaders.SIP_HEADER_RELIABLE_SEQ);
+                builder.Append(": ");
+                builder.Append(RSeq);
+                builder.Append(m_CRLF);
+            }
+
+            if (RAckRSeq != -1)
+            {
+                builder.Append(SIPHeaders.SIP_HEADER_RELIABLE_ACK);
+                builder.Append(": ");
+                builder.Append(RAckRSeq);
+                builder.Append(' ');
+                builder.Append(RAckCSeq);
+                builder.Append(' ');
+                builder.Append(RAckCSeqMethod.ToString());
+                builder.Append(m_CRLF);
+            }
 
                 foreach (var PAI in PassertedIdentity)
                 {
-                    headersBuilder.Append(SIPHeaders.SIP_HEADER_PASSERTED_IDENTITY + ": " + PAI + m_CRLF);
+                if (PAI != null)
+                {
+                    builder.Append(SIPHeaders.SIP_HEADER_PASSERTED_IDENTITY);
+                    builder.Append(": ");
+                    builder.Append(PAI.ToString());
+                    builder.Append(m_CRLF);
+                }
                 }
 
                 foreach (var HistInfo in HistoryInfo)
                 {
-                    headersBuilder.Append(SIPHeaders.SIP_HEADER_HISTORY_INFO + ": " + HistInfo + m_CRLF);
+                if (HistInfo != null)
+                {
+                    builder.Append(SIPHeaders.SIP_HEADER_HISTORY_INFO);
+                    builder.Append(": ");
+                    builder.Append(HistInfo.ToString());
+                    builder.Append(m_CRLF);
+                }
                 }
 
                 foreach (var DiversionHeader in Diversion)
                 {
-                    headersBuilder.Append(SIPHeaders.SIP_HEADER_DIVERSION + ": " + DiversionHeader + m_CRLF);
+                if (DiversionHeader != null)
+                {
+                    builder.Append(SIPHeaders.SIP_HEADER_DIVERSION);
+                    builder.Append(": ");
+                    builder.Append(DiversionHeader.ToString());
+                    builder.Append(m_CRLF);
+                }
                 }
 
                 // Custom SIP headers.
-                headersBuilder.Append((ProxyReceivedFrom != null) ? SIPHeaders.SIP_HEADER_PROXY_RECEIVEDFROM + ": " + ProxyReceivedFrom + m_CRLF : null);
-                headersBuilder.Append((ProxyReceivedOn != null) ? SIPHeaders.SIP_HEADER_PROXY_RECEIVEDON + ": " + ProxyReceivedOn + m_CRLF : null);
-                headersBuilder.Append((ProxySendFrom != null) ? SIPHeaders.SIP_HEADER_PROXY_SENDFROM + ": " + ProxySendFrom + m_CRLF : null);
+            if (ProxyReceivedFrom != null)
+            {
+                builder.Append(SIPHeaders.SIP_HEADER_PROXY_RECEIVEDFROM);
+                builder.Append(": ");
+                builder.Append(ProxyReceivedFrom);
+                builder.Append(m_CRLF);
+            }
 
-                // Unknown SIP headers
-                foreach (string unknownHeader in UnknownHeaders)
+            if (ProxyReceivedOn != null)
                 {
-                    headersBuilder.Append(unknownHeader + m_CRLF);
+                builder.Append(SIPHeaders.SIP_HEADER_PROXY_RECEIVEDON);
+                builder.Append(": ");
+                builder.Append(ProxyReceivedOn);
+                builder.Append(m_CRLF);
                 }
 
-                return headersBuilder.ToString();
+            if (ProxySendFrom != null)
+            {
+                builder.Append(SIPHeaders.SIP_HEADER_PROXY_SENDFROM);
+                builder.Append(": ");
+                builder.Append(ProxySendFrom);
+                builder.Append(m_CRLF);
             }
-            catch (Exception excp)
+
+            // Unknown SIP headers
+            foreach (string unknownHeader in UnknownHeaders)
             {
-                logger.LogError(excp, "Exception SIPHeader ToString. Exception: {ErrorMessage}", excp.Message);
-                throw;
+                if (unknownHeader != null)
+            {
+                    builder.Append(unknownHeader);
+                    builder.Append(m_CRLF);
+                }
             }
         }
 
diff --git a/src/core/SIP/SIPMessageBase.cs b/src/core/SIP/SIPMessageBase.cs
index 5bdb00da29..bb8b019f38 100644
--- a/src/core/SIP/SIPMessageBase.cs
+++ b/src/core/SIP/SIPMessageBase.cs
@@ -1,32 +1,32 @@
-//-----------------------------------------------------------------------------
-// Filename: SIPMessageBase.cs
-//
-// Description: Common base class for SIPRequest and SIPResponse classes.
-//
-// Author(s):
+//-----------------------------------------------------------------------------
+// Filename: SIPMessageBase.cs
+//
+// Description: Common base class for SIPRequest and SIPResponse classes.
+//
+// Author(s):
 // Salih YILDIRIM (github: salihy)
-// Aaron Clauson (aaron@sipsorcery.com)
-//
-// History:
+// Aaron Clauson (aaron@sipsorcery.com)
+//
+// History:
 // 26 Nov 2019	Salih YILDIRIM  Created.
-// 26 Nov 2019  Aaron Clauson   Converted from interface to base class to extract common properties.
-//
-// License:
-// BSD 3-Clause "New" or "Revised" License, see included LICENSE.md file.
-//-----------------------------------------------------------------------------
+// 26 Nov 2019  Aaron Clauson   Converted from interface to base class to extract common properties.
+//
+// License:
+// BSD 3-Clause "New" or "Revised" License, see included LICENSE.md file.
+//-----------------------------------------------------------------------------
 
 using System;
 using System.Text;
 using Microsoft.Extensions.Logging;
 using SIPSorcery.Sys;
-
-namespace SIPSorcery.SIP
-{
-    public class SIPMessageBase
+
+namespace SIPSorcery.SIP
+{
+    public class SIPMessageBase
     {
         protected static ILogger logger = Log.Logger;
 
-        protected const string m_CRLF = SIPConstants.CRLF;
+        protected const string m_CRLF = SIPConstants.CRLF;
         protected const string m_sipFullVersion = SIPConstants.SIP_FULLVERSION_STRING;
         protected const string m_allowedSIPMethods = SIPConstants.ALLOWED_SIP_METHODS;
 
@@ -43,15 +43,15 @@ public SIPMessageBase(Encoding sipEncoding, Encoding sipBodyEncoding)
             SIPBodyEncoding = sipBodyEncoding;
         }
 
-        /// 
-        /// The SIP request/response's headers collection.
-        /// 
+        /// 
+        /// The SIP request/response's headers collection.
+        /// 
         public SIPHeader Header;
 
-        /// 
+        /// 
         /// The optional body or payload for the SIP request/response. This Body property
-        /// should be used for Session Description Protocol (SDP) and other string payloads.
-        /// 
+        /// should be used for Session Description Protocol (SDP) and other string payloads.
+        /// 
         public string Body
         {
             get
@@ -87,51 +87,65 @@ public byte[] BodyBuffer
         {
             get => _body;
             set => _body = value;
-        }
-
-        /// 
-        /// Timestamp for the SIP request/response's creation.
-        /// 
-        public DateTime Created = DateTime.Now;
-
-        /// 
-        /// The remote SIP socket the request/response was received from.
-        /// 
-        public SIPEndPoint RemoteSIPEndPoint { get; protected set; }
-
-        /// 
-        /// The local SIP socket the request/response was received on.
-        /// 
+        }
+
+        /// 
+        /// Timestamp for the SIP request/response's creation.
+        /// 
+        public DateTime Created = DateTime.Now;
+
+        /// 
+        /// The remote SIP socket the request/response was received from.
+        /// 
+        public SIPEndPoint RemoteSIPEndPoint { get; protected set; }
+
+        /// 
+        /// The local SIP socket the request/response was received on.
+        /// 
         public SIPEndPoint LocalSIPEndPoint { get; protected set; }
 
-        /// 
+        /// 
         /// When the SIP transport layer has multiple channels it will use this ID hint to choose amongst them when 
-        /// sending this request/response.
-        /// 
-        public string SendFromHintChannelID;
-
-        /// 
+        /// sending this request/response.
+        /// 
+        public string SendFromHintChannelID;
+
+        /// 
         /// For connection oriented SIP transport channels this ID provides a hint about the specific connection to use
-        /// when sending this request/response.
-        /// 
+        /// when sending this request/response.
+        /// 
         public string SendFromHintConnectionID;
 
         protected byte[] GetBytes(string firstLine)
         {
-            string headers = firstLine + this.Header.ToString() + m_CRLF;
+            var builder = new ValueStringBuilder();
 
-            if (_body != null && _body.Length > 0)
+            try
             {
-                var headerBytes = SIPEncoding.GetBytes(headers);
-                byte[] buffer = new byte[headerBytes.Length + _body.Length];
-                Buffer.BlockCopy(headerBytes, 0, buffer, 0, headerBytes.Length);
-                Buffer.BlockCopy(_body, 0, buffer, headerBytes.Length, _body.Length);
+                builder.Append(firstLine);
+                this.Header.ToString(ref builder);
+                builder.Append(m_CRLF);
+
+                if (_body is { Length: > 0 })
+                {
+                    var headerByteCount = SIPEncoding.GetByteCount(builder.AsSpan());
+                    var buffer = new byte[headerByteCount + _body.Length];
+                    SIPEncoding.GetBytes(builder.AsSpan(), buffer.AsSpan());
+                    _body.CopyTo(buffer.AsSpan(headerByteCount));
                 return buffer;
             }
             else
             {
-                return SIPEncoding.GetBytes(headers);
+                    var headerByteCount = SIPEncoding.GetByteCount(builder.AsSpan());
+                    var buffer = new byte[headerByteCount];
+                    SIPEncoding.GetBytes(builder.AsSpan(), buffer.AsSpan());
+                    return buffer;
+                }
+            }
+            finally
+            {
+                builder.Dispose();
             }
         }
-    }
-}
+    }
+}
diff --git a/src/core/SIP/SIPParameters.cs b/src/core/SIP/SIPParameters.cs
index ab5f45aa93..f1951a57df 100644
--- a/src/core/SIP/SIPParameters.cs
+++ b/src/core/SIP/SIPParameters.cs
@@ -289,24 +289,51 @@ public string[] GetKeys()
 
         public override string ToString()
         {
-            string paramStr = null;
+            var builder = new ValueStringBuilder();
 
+            try
+            {
+                ToString(ref builder, TagDelimiter);
+
+                return builder.ToString();
+            }
+            finally
+            {
+                builder.Dispose();
+            }
+        }
+
+        internal void ToString(ref ValueStringBuilder builder, char firstDelimiter)
+        {
             if (m_dictionary != null)
             {
-                foreach (KeyValuePair param in m_dictionary)
+                bool isFirst = true;
+
+                foreach (var (key, value) in m_dictionary)
                 {
-                    if (param.Value != null && param.Value.Trim().Length > 0)
+                    if (isFirst)
                     {
-                        paramStr += TagDelimiter + param.Key + TAG_NAME_VALUE_SEPERATOR + SIPEscape.SIPURIParameterEscape(param.Value);
+                        builder.Append(firstDelimiter);
+                        isFirst = false;
                     }
                     else
                     {
-                        paramStr += TagDelimiter + param.Key;
+                        builder.Append(TagDelimiter);
+                    }
+
+                    builder.Append(key);
+
+                    if (value is not null)
+                    {
+                        var valueSpan = value.AsSpan();
+                        if (!valueSpan.Trim().IsEmpty)
+                        {
+                            builder.Append(TAG_NAME_VALUE_SEPERATOR);
+                            SIPEscape.SIPURIParameterEscape(ref builder, valueSpan);
+                        }
                     }
                 }
             }
-
-            return paramStr;
         }
 
         public override int GetHashCode()
diff --git a/src/core/SIP/SIPURI.cs b/src/core/SIP/SIPURI.cs
index e7cff1ae70..d44c960044 100644
--- a/src/core/SIP/SIPURI.cs
+++ b/src/core/SIP/SIPURI.cs
@@ -485,35 +485,57 @@ public static bool TryParse(string uriStr, out SIPURI uri)
 
         public override string ToString()
         {
+            var builder = new ValueStringBuilder();
+
             try
             {
-                string uriStr = Scheme.ToString() + SCHEME_ADDR_SEPARATOR;
+                ToString(ref builder);
 
-                uriStr = (User != null) ? uriStr + User + USER_HOST_SEPARATOR + Host : uriStr + Host;
+                return builder.ToString();
+            }
+            catch (Exception excp)
+            {
+                logger.LogError(excp, "Exception SIPURI ToString. {ErrorMessage}", excp.Message);
+                throw;
+            }
+            finally
+            {
+                builder.Dispose();
+            }
+        }
 
-                if (Parameters != null && Parameters.Count > 0)
+        internal void ToString(ref ValueStringBuilder builder)
                 {
-                    uriStr += Parameters.ToString();
+            builder.Append(Scheme.ToString());
+            builder.Append(SCHEME_ADDR_SEPARATOR);
+
+            if (User != null)
+            {
+                builder.Append(User);
+                builder.Append(USER_HOST_SEPARATOR);
                 }
 
-                // If the URI's protocol is not implied already set the transport parameter.
-                if (Scheme != SIPSchemesEnum.sips && Protocol != SIPProtocolsEnum.udp && !Parameters.Has(m_uriParamTransportKey))
+            builder.Append(Host);
+
+            if (Parameters != null && Parameters.Count > 0)
                 {
-                    uriStr += PARAM_TAG_DELIMITER + m_uriParamTransportKey + TAG_NAME_VALUE_SEPERATOR + Protocol.ToString();
+                builder.Append(Parameters.ToString());
                 }
 
-                if (Headers != null && Headers.Count > 0)
-                {
-                    string headerStr = Headers.ToString();
-                    uriStr += HEADER_START_DELIMITER + headerStr.Substring(1);
+            // If the URI's protocol is not implied already, set the transport parameter.
+            if (Scheme != SIPSchemesEnum.sips &&
+                Protocol != SIPProtocolsEnum.udp &&
+                !Parameters.Has(m_uriParamTransportKey))
+            {
+                builder.Append(PARAM_TAG_DELIMITER);
+                builder.Append(m_uriParamTransportKey);
+                builder.Append(TAG_NAME_VALUE_SEPERATOR);
+                builder.Append(Protocol.ToString());
                 }
 
-                return uriStr;
-            }
-            catch (Exception excp)
+            if (Headers != null && Headers.Count > 0)
             {
-                logger.LogError(excp, "Exception SIPURI ToString. {ErrorMessage}", excp.Message);
-                throw;
+                Headers.ToString(ref builder, HEADER_START_DELIMITER);
             }
         }
 
diff --git a/src/core/SIP/SIPUserField.cs b/src/core/SIP/SIPUserField.cs
index 6901c3f1ff..5d1146b0d6 100644
--- a/src/core/SIP/SIPUserField.cs
+++ b/src/core/SIP/SIPUserField.cs
@@ -142,33 +142,38 @@ public static SIPUserField ParseSIPUserField(string userFieldStr)
 
         public override string ToString()
         {
+            var builder = new ValueStringBuilder();
+
             try
             {
-                string userFieldStr = null;
+                ToString(ref builder);
 
-                if (Name != null)
+                return builder.ToString();
+            }
+            catch (Exception excp)
                 {
-                    /*if(Regex.Match(Name, @"\s").Success)
+                logger.LogError(excp, "Exception SIPUserField ToString. {Message}", excp.Message);
+                throw;
+            }
+            finally
                     {
-                    userFieldStr = "\"" + Name + "\" ";
+                builder.Dispose();
                 }
-                    else
-                    {
-                        userFieldStr = Name + " ";
-                    }*/
-
-                    userFieldStr = "\"" + Name + "\" ";
                 }
 
-                userFieldStr += "<" + URI.ToString() + ">" + Parameters.ToString();
-
-                return userFieldStr;
-            }
-            catch (Exception excp)
+        internal void ToString(ref ValueStringBuilder builder)
+        {
+            if (Name != null)
             {
-                logger.LogError(excp, "Exception SIPUserField ToString. {Message}", excp.Message);
-                throw;
+                builder.Append("\"");
+                builder.Append(Name);
+                builder.Append("\" ");
             }
+
+            builder.Append('<');
+            builder.Append(URI.ToString());
+            builder.Append('>');
+            builder.Append(Parameters.ToString());
         }
 
         public string ToParameterlessString()
diff --git a/src/net/SDP/SDPAudioVideoMediaFormat.cs b/src/net/SDP/SDPAudioVideoMediaFormat.cs
index 34d3ee79a7..6d3378281d 100755
--- a/src/net/SDP/SDPAudioVideoMediaFormat.cs
+++ b/src/net/SDP/SDPAudioVideoMediaFormat.cs
@@ -38,7 +38,7 @@ public struct SDPAudioVideoMediaFormat
         public const int DYNAMIC_ID_MAX = 127;
         public const int DEFAULT_AUDIO_CHANNEL_COUNT = 1;
 
-        public static SDPAudioVideoMediaFormat Empty = new SDPAudioVideoMediaFormat() { _isEmpty = true };
+        public static SDPAudioVideoMediaFormat Empty = new SDPAudioVideoMediaFormat();
 
         /// 
         /// Indicates whether the format is for audio or video.
@@ -116,7 +116,7 @@ public IEnumerable SupportedRtcpFeedbackMessages
         /// 
         //public string Name { get; set; }
 
-        private bool _isEmpty;
+        private bool _isNotEmpty;
 
         /// 
         /// Creates a new SDP media format for a well known media type. Well known type are those that use 
@@ -130,7 +130,7 @@ public SDPAudioVideoMediaFormat(SDPWellKnownMediaFormatsEnum knownFormat)
             ID = (int)knownFormat;
             Rtpmap = null;
             Fmtp = null;
-            _isEmpty = false;
+            _isNotEmpty = true;
 
             if (Kind == SDPMediaTypesEnum.audio)
             {
@@ -144,28 +144,20 @@ public SDPAudioVideoMediaFormat(SDPWellKnownMediaFormatsEnum knownFormat)
             }
         }
 
-        public bool IsH264
-        {
-            get
-            {
-                return (Rtpmap ?? "").ToUpperInvariant().Trim().StartsWith("H264");
-            }
-        }
+        public bool IsH264 => RtmapIs("H264");
 
-        public bool IsMJPEG
-        {
-            get
-            {
-                return (Rtpmap ?? "").ToUpperInvariant().Trim().StartsWith("JPEG");
-            }
-        }
+        public bool IsMJPEG => RtmapIs("JPEG");
+
+        public bool isH265 => RtmapIs("H265");
 
-        public bool isH265
+        private bool RtmapIs(string codec)
         {
-            get
+            if (Rtpmap is null)
             {
-                return (Rtpmap ?? "").ToUpperInvariant().Trim().StartsWith("H265");
-            }
+                return false;
+        	}
+
+            return Rtpmap.AsSpan().TrimStart().StartsWith(codec.AsSpan(), StringComparison.OrdinalIgnoreCase);
         }
 
         public bool CheckCompatible()
@@ -224,7 +216,7 @@ public SDPAudioVideoMediaFormat(SDPMediaTypesEnum kind, int id, string rtpmap, s
             ID = id;
             Rtpmap = rtpmap;
             Fmtp = fmtp;
-            _isEmpty = false;
+            _isNotEmpty = true;
         }
 
         /// 
@@ -246,7 +238,7 @@ public SDPAudioVideoMediaFormat(SDPMediaTypesEnum kind, int id, string name, int
             ID = id;
             Rtpmap = null;
             Fmtp = fmtp;
-            _isEmpty = false;
+            _isNotEmpty = true;
 
             Rtpmap = SetRtpmap(name, clockRate, channels);
         }
@@ -263,7 +255,7 @@ public SDPAudioVideoMediaFormat(AudioFormat audioFormat)
             ID = audioFormat.FormatID;
             Rtpmap = null;
             Fmtp = audioFormat.Parameters;
-            _isEmpty = false;
+            _isNotEmpty = true;
 
             Rtpmap = SetRtpmap(audioFormat.FormatName, audioFormat.RtpClockRate, audioFormat.ChannelCount);
         }
@@ -280,7 +272,7 @@ public SDPAudioVideoMediaFormat(VideoFormat videoFormat)
             ID = videoFormat.FormatID;
             Rtpmap = null;
             Fmtp = videoFormat.Parameters;
-            _isEmpty = false;
+            _isNotEmpty = true;
 
             Rtpmap = SetRtpmap(videoFormat.FormatName, videoFormat.ClockRate);
         }
@@ -291,7 +283,7 @@ public SDPAudioVideoMediaFormat(TextFormat textFormat)
             ID = textFormat.FormatID;
             Rtpmap = null;  
             Fmtp = textFormat.Parameters;
-            _isEmpty = false;
+            _isNotEmpty = true;
 
             Rtpmap = SetRtpmap(textFormat.FormatName, textFormat.ClockRate);
         }
@@ -300,7 +292,7 @@ private string SetRtpmap(string name, int clockRate, int channels = DEFAULT_AUDI
                 ? $"{name}/{clockRate}"
                 : (channels == DEFAULT_AUDIO_CHANNEL_COUNT) ? $"{name}/{clockRate}" : $"{name}/{clockRate}/{channels}";
 
-        public bool IsEmpty() => _isEmpty;
+        public bool IsEmpty() => !_isNotEmpty;
         public int ClockRate()
         {
             if (Kind == SDPMediaTypesEnum.video)
@@ -599,16 +591,20 @@ public static SDPAudioVideoMediaFormat GetCommonRtpEventFormat(ListIf found the matching format or the empty format if not.
         public static SDPAudioVideoMediaFormat GetFormatForName(List formats, string formatName)
         {
-            if (formats == null || formats.Count == 0)
+            if (formats != null && formats.Count != 0 && formatName != null)
             {
+                foreach (var format in formats)
+                {
+                    if (string.Equals(format.Name(), formatName, StringComparison.OrdinalIgnoreCase))
+                    {
+                        return format;
+                    }
+                }
+
                 return Empty;
             }
-            else
-            {
-                return formats.Any(x => x.Name()?.ToLower() == formatName?.ToLower()) ?
-                   formats.First(x => x.Name()?.ToLower() == formatName?.ToLower()) :
-                   Empty;
-            }
+
+            return Empty;
         }
     }
 }
diff --git a/src/net/SDP/SDPConnectionInformation.cs b/src/net/SDP/SDPConnectionInformation.cs
index 537dd3950e..dd3d57540e 100644
--- a/src/net/SDP/SDPConnectionInformation.cs
+++ b/src/net/SDP/SDPConnectionInformation.cs
@@ -15,6 +15,7 @@
 
 using System.Net;
 using System.Net.Sockets;
+using SIPSorcery.Sys;
 
 namespace SIPSorcery.Net
 {
@@ -63,5 +64,17 @@ public override string ToString()
         {
             return "c=" + ConnectionNetworkType + " " + ConnectionAddressType + " " + ConnectionAddress + m_CRLF;
         }
+
+        internal void ToString(ref ValueStringBuilder builder)
+        {
+            builder.Append("c=");
+            builder.Append(ConnectionNetworkType);
+            builder.Append(' ');
+            builder.Append(ConnectionAddressType);
+            builder.Append(' ');
+            builder.Append(ConnectionAddress);
+            builder.Append(m_CRLF);
+
+        }
     }
 }
diff --git a/src/net/SDP/SDPMediaAnnouncement.cs b/src/net/SDP/SDPMediaAnnouncement.cs
index c31bbde8bc..7a8f31ac56 100755
--- a/src/net/SDP/SDPMediaAnnouncement.cs
+++ b/src/net/SDP/SDPMediaAnnouncement.cs
@@ -32,6 +32,7 @@
 using System.Text;
 using System.Text.RegularExpressions;
 using Microsoft.Extensions.Logging;
+using SIPSorcery.Sys;
 using SIPSorceryMedia.Abstractions;
 
 namespace SIPSorcery.Net
@@ -282,87 +283,189 @@ public void ParseMediaFormats(string formatList)
 
         public override string ToString()
         {
-            string announcement = "m=" + Media + " " + Port + " " + Transport + " " + GetFormatListToString() + m_CRLF;
+            var builder = new ValueStringBuilder();
 
-            announcement += !string.IsNullOrWhiteSpace(MediaDescription) ? "i=" + MediaDescription + m_CRLF : null;
+            try
+            {
+                ToString(ref builder);
 
-            announcement += (Connection == null) ? null : Connection.ToString();
+                return builder.ToString();
+            }
+            finally
+            {
+                builder.Dispose();
+            }
+        }
+
+        internal void ToString(ref ValueStringBuilder builder)
+        {
+            builder.Append("m=");
+            builder.Append(Media.ToString());
+            builder.Append(' ');
+            builder.Append(Port);
+            builder.Append(' ');
+            builder.Append(Transport);
+            builder.Append(' ');
+            WriteFormatListString(ref builder);
+            builder.Append(m_CRLF);
+
+            if (!string.IsNullOrWhiteSpace(MediaDescription))
+            {
+                builder.Append("i=");
+                builder.Append(MediaDescription);
+                builder.Append(m_CRLF);
+            }
+
+            if (Connection != null)
+            {
+                builder.Append(Connection.ToString());
+            }
 
             if (TIASBandwidth > 0)
             {
-                announcement += TIAS_BANDWIDTH_ATTRIBUE_PREFIX + TIASBandwidth + m_CRLF;
+                builder.Append(TIAS_BANDWIDTH_ATTRIBUE_PREFIX);
+                builder.Append(TIASBandwidth);
+                builder.Append(m_CRLF);
             }
 
             foreach (string bandwidthAttribute in BandwidthAttributes)
             {
-                announcement += "b=" + bandwidthAttribute + m_CRLF;
+                builder.Append("b=");
+                builder.Append(bandwidthAttribute);
+                builder.Append(m_CRLF);
+            }
+
+            if (!string.IsNullOrWhiteSpace(IceUfrag))
+            {
+                builder.Append("a=");
+                builder.Append(SDP.ICE_UFRAG_ATTRIBUTE_PREFIX);
+                builder.Append(':');
+                builder.Append(IceUfrag);
+                builder.Append(m_CRLF);
             }
 
-            announcement += !string.IsNullOrWhiteSpace(IceUfrag) ? "a=" + SDP.ICE_UFRAG_ATTRIBUTE_PREFIX + ":" + IceUfrag + m_CRLF : null;
-            announcement += !string.IsNullOrWhiteSpace(IcePwd) ? "a=" + SDP.ICE_PWD_ATTRIBUTE_PREFIX + ":" + IcePwd + m_CRLF : null;
-            announcement += !string.IsNullOrWhiteSpace(DtlsFingerprint) ? "a=" + SDP.DTLS_FINGERPRINT_ATTRIBUTE_PREFIX + ":" + DtlsFingerprint + m_CRLF : null;
-            announcement += IceRole != null ? $"a={SDP.ICE_SETUP_ATTRIBUTE_PREFIX}:{IceRole}{m_CRLF}" : null; 
+            if (!string.IsNullOrWhiteSpace(IcePwd))
+            {
+                builder.Append("a=");
+                builder.Append(SDP.ICE_PWD_ATTRIBUTE_PREFIX);
+                builder.Append(':');
+                builder.Append(IcePwd);
+                builder.Append(m_CRLF);
+            }
+
+            if (!string.IsNullOrWhiteSpace(DtlsFingerprint))
+            {
+                builder.Append("a=");
+                builder.Append(SDP.DTLS_FINGERPRINT_ATTRIBUTE_PREFIX);
+                builder.Append(':');
+                builder.Append(DtlsFingerprint);
+                builder.Append(m_CRLF);
+            }
+
+            if (IceRole != null)
+            {
+                builder.Append("a=");
+                builder.Append(SDP.ICE_SETUP_ATTRIBUTE_PREFIX);
+                builder.Append(':');
+                builder.Append(IceRole.ToString());
+                builder.Append(m_CRLF);
+            }
 
-            if (IceCandidates?.Count() > 0)
+            if (IceCandidates?.Any() == true)
             {
                 foreach (var candidate in IceCandidates)
                 {
-                    announcement += $"a={SDP.ICE_CANDIDATE_ATTRIBUTE_PREFIX}:{candidate}{m_CRLF}";
+                    builder.Append("a=");
+                    builder.Append(SDP.ICE_CANDIDATE_ATTRIBUTE_PREFIX);
+                    builder.Append(':');
+                    builder.Append(candidate);
+                    builder.Append(m_CRLF);
                 }
             }
 
             if (IceOptions != null)
             {
-                announcement += $"a={SDP.ICE_OPTIONS}:" + IceOptions + m_CRLF;
+                builder.Append("a=");
+                builder.Append(SDP.ICE_OPTIONS);
+                builder.Append(':');
+                builder.Append(IceOptions);
+                builder.Append(m_CRLF);
             }
 
             if (IceEndOfCandidates)
             {
-                announcement += $"a={SDP.END_ICE_CANDIDATES_ATTRIBUTE}" + m_CRLF;
+                builder.Append("a=");
+                builder.Append(SDP.END_ICE_CANDIDATES_ATTRIBUTE);
+                builder.Append(m_CRLF);
             }
 
-            announcement += !string.IsNullOrWhiteSpace(MediaID) ? "a=" + SDP.MEDIA_ID_ATTRIBUTE_PREFIX + ":" + MediaID + m_CRLF : null;
+            if (!string.IsNullOrWhiteSpace(MediaID))
+            {
+                builder.Append("a=");
+                builder.Append(SDP.MEDIA_ID_ATTRIBUTE_PREFIX);
+                builder.Append(':');
+                builder.Append(MediaID);
+                builder.Append(m_CRLF);
+            }
 
-            announcement += GetFormatListAttributesToString();
+            builder.Append(GetFormatListAttributesToString());
+
+            foreach (var ext in HeaderExtensions)
+            {
+                builder.Append(MEDIA_EXTENSION_MAP_ATTRIBUE_PREFIX);
+                builder.Append(ext.Value.Id);
+                builder.Append(' ');
+                builder.Append(ext.Value.Uri);
+                builder.Append(m_CRLF);
+            }
 
-            announcement += string.Join("", HeaderExtensions.Select(x => $"{MEDIA_EXTENSION_MAP_ATTRIBUE_PREFIX}{x.Value.Id} {x.Value.Uri}{m_CRLF}"));
             foreach (string extra in ExtraMediaAttributes)
             {
-                announcement += string.IsNullOrWhiteSpace(extra) ? null : extra + m_CRLF;
+                if (!string.IsNullOrWhiteSpace(extra))
+                {
+                    builder.Append(extra);
+                    builder.Append(m_CRLF);
+                }
             }
 
             foreach (SDPSecurityDescription desc in this.SecurityDescriptions)
             {
-                announcement += desc.ToString() + m_CRLF;
+                builder.Append(desc.ToString());
+                builder.Append(m_CRLF);
             }
 
             if (MediaStreamStatus != null)
             {
-                announcement += MediaStreamStatusType.GetAttributeForMediaStreamStatus(MediaStreamStatus.Value) + m_CRLF;
+                builder.Append(MediaStreamStatusType.GetAttributeForMediaStreamStatus(MediaStreamStatus.Value));
+                builder.Append(m_CRLF);
             }
 
             if (SsrcGroupID != null && SsrcAttributes.Count > 0)
             {
-                announcement += MEDIA_FORMAT_SSRC_GROUP_ATTRIBUE_PREFIX + SsrcGroupID;
+                builder.Append(MEDIA_FORMAT_SSRC_GROUP_ATTRIBUE_PREFIX);
+                builder.Append(SsrcGroupID);
                 foreach (var ssrcAttr in SsrcAttributes)
                 {
-                    announcement += $" {ssrcAttr.SSRC}";
+                    builder.Append(' ');
+                    builder.Append(ssrcAttr.SSRC);
                 }
-                announcement += m_CRLF;
+                builder.Append(m_CRLF);
             }
 
             if (SsrcAttributes.Count > 0)
             {
                 foreach (var ssrcAttr in SsrcAttributes)
                 {
+                    builder.Append(MEDIA_FORMAT_SSRC_ATTRIBUE_PREFIX);
+                    builder.Append(ssrcAttr.SSRC);
                     if (!string.IsNullOrWhiteSpace(ssrcAttr.Cname))
                     {
-                        announcement += $"{MEDIA_FORMAT_SSRC_ATTRIBUE_PREFIX}{ssrcAttr.SSRC} {SDPSsrcAttribute.MEDIA_CNAME_ATTRIBUE_PREFIX}:{ssrcAttr.Cname}" + m_CRLF;
-                    }
-                    else
-                    {
-                        announcement += $"{MEDIA_FORMAT_SSRC_ATTRIBUE_PREFIX}{ssrcAttr.SSRC}" + m_CRLF;
+                        builder.Append(' ');
+                        builder.Append(SDPSsrcAttribute.MEDIA_CNAME_ATTRIBUE_PREFIX);
+                        builder.Append(':');
+                        builder.Append(ssrcAttr.Cname);
                     }
+                    builder.Append(m_CRLF);
                 }
             }
 
@@ -371,50 +474,87 @@ public override string ToString()
             // an application sets it then it's likely to be for a specific reason.
             if (SctpMap != null)
             {
-                announcement += $"{MEDIA_FORMAT_SCTP_MAP_ATTRIBUE_PREFIX}{SctpMap}" + m_CRLF;
+                builder.Append(MEDIA_FORMAT_SCTP_MAP_ATTRIBUE_PREFIX);
+                builder.Append(SctpMap);
+                builder.Append(m_CRLF);
             }
             else
             {
                 if (SctpPort != null)
                 {
-                    announcement += $"{MEDIA_FORMAT_SCTP_PORT_ATTRIBUE_PREFIX}{SctpPort}" + m_CRLF;
+                    builder.Append(MEDIA_FORMAT_SCTP_PORT_ATTRIBUE_PREFIX);
+                    builder.Append(SctpPort);
+                    builder.Append(m_CRLF);
                 }
 
                 if (MaxMessageSize != 0)
                 {
-                    announcement += $"{MEDIA_FORMAT_MAX_MESSAGE_SIZE_ATTRIBUE_PREFIX}{MaxMessageSize}" + m_CRLF;
+                    builder.Append(MEDIA_FORMAT_MAX_MESSAGE_SIZE_ATTRIBUE_PREFIX);
+                    builder.Append(MaxMessageSize);
+                    builder.Append(m_CRLF);
+                }
                 }
             }
 
-            return announcement;
+        public string GetFormatListToString()
+        {
+            if (Media == SDPMediaTypesEnum.message)
+            {
+                return "*";
         }
 
-        public string GetFormatListToString()
+            var builder = new ValueStringBuilder();
+
+            try
         {
+                WriteFormatListString(ref builder);
+
             if (Media == SDPMediaTypesEnum.application)
             {
-                StringBuilder sb = new StringBuilder();
-                foreach (var appFormat in ApplicationMediaFormats)
+                    return builder.ToString();
+                }
+                else
+                {
+                    return builder.Length > 0 ? builder.ToString() : null;
+                }
+            }
+            finally
                 {
-                    sb.Append(appFormat.Key);
-                    sb.Append(" ");
+                builder.Dispose();
+            }
                 }
 
-                return sb.ToString().Trim();
+        internal void WriteFormatListString(ref ValueStringBuilder builder)
+        {
+            if (Media == SDPMediaTypesEnum.message)
+            {
+                builder.Append('*');
             }
-            else if (Media == SDPMediaTypesEnum.message)
+            else if (Media == SDPMediaTypesEnum.application)
             {
-                return "*";
+                var first = true;
+                foreach (var appFormat in ApplicationMediaFormats)
+                {
+                    if (!first)
+            {
+                        builder.Append(' ');
+                    }
+                    builder.Append(appFormat.Key);
+                    first = false;
+                }
             }
             else
             {
-                string mediaFormatList = null;
+                var first = true;
                 foreach (var mediaFormat in MediaFormats)
                 {
-                    mediaFormatList += mediaFormat.Key + " ";
+                    if (!first)
+                    {
+                        builder.Append(' ');
+                    }
+                    builder.Append(mediaFormat.Key);
+                    first = false;
                 }
-
-                return (mediaFormatList != null) ? mediaFormatList.Trim() : null;
             }
         }
 
diff --git a/src/net/SDP/SDPSecurityDescription.cs b/src/net/SDP/SDPSecurityDescription.cs
index a8025e57d9..e7f3434ef1 100644
--- a/src/net/SDP/SDPSecurityDescription.cs
+++ b/src/net/SDP/SDPSecurityDescription.cs
@@ -388,7 +388,7 @@ private static void parseKeySaltBase64(CryptoSuites cryptoSuite, string base64Ke
 
             private static void checkValidKeyInfoCharacters(string keyParameter, string keyInfo)
             {
-                foreach (char c in keyInfo.ToCharArray())
+                foreach (char c in keyInfo.AsSpan())
                 {
                     if (c < 0x21 || c > 0x7e)
                     {
diff --git a/src/net/STUN/STUNAttributes/STUNAddressAttribute.cs b/src/net/STUN/STUNAttributes/STUNAddressAttribute.cs
index ff00d9ed72..0ccf839dde 100755
--- a/src/net/STUN/STUNAttributes/STUNAddressAttribute.cs
+++ b/src/net/STUN/STUNAttributes/STUNAddressAttribute.cs
@@ -122,11 +122,12 @@ public override int ToByteBuffer(byte[] buffer, int startIndex)
             return STUNAttribute.STUNATTRIBUTE_HEADER_LENGTH + ADDRESS_ATTRIBUTE_IPV4_LENGTH;
         }
 
-        public override string ToString()
+        private protected override void ValueToString(ref ValueStringBuilder sb)
         {
-            string attrDescrStr = "STUN Attribute: " + base.AttributeType + ", address=" + Address.ToString() + ", port=" + Port + ".";
-
-            return attrDescrStr;
+            sb.Append("Address=");
+            sb.Append(Address.ToString());
+            sb.Append(", Port=");
+            sb.Append(Port);
         }
     }
 }
diff --git a/src/net/STUN/STUNAttributes/STUNAddressAttributeBase.cs b/src/net/STUN/STUNAttributes/STUNAddressAttributeBase.cs
index 6fea343082..1f95a3f5fb 100644
--- a/src/net/STUN/STUNAttributes/STUNAddressAttributeBase.cs
+++ b/src/net/STUN/STUNAttributes/STUNAddressAttributeBase.cs
@@ -2,6 +2,7 @@
 using System.Collections.Generic;
 using System.Net;
 using System.Text;
+using SIPSorcery.Sys;
 
 namespace SIPSorcery.Net
 {
@@ -37,5 +38,15 @@ public STUNAddressAttributeBase(STUNAttributeTypesEnum attributeType, byte[] val
             : base(attributeType, value)
         {
         }
+
+        private protected override void ValueToString(ref ValueStringBuilder sb)
+        {
+            sb.Append("Address=");
+            sb.Append(Address.ToString());
+            sb.Append(", Port=");
+            sb.Append(Port);
+            sb.Append(", Family=");
+            sb.Append(Family switch { 1 => "IPV4", 2 => "IPV6", _ => "Invalid", });
+        }
     }
 }
diff --git a/src/net/STUN/STUNAttributes/STUNAttribute.cs b/src/net/STUN/STUNAttributes/STUNAttribute.cs
index 1d84d78151..e1ebac04d9 100644
--- a/src/net/STUN/STUNAttributes/STUNAttribute.cs
+++ b/src/net/STUN/STUNAttributes/STUNAttribute.cs
@@ -272,11 +272,36 @@ public virtual int ToByteBuffer(byte[] buffer, int startIndex)
             return STUNAttribute.STUNATTRIBUTE_HEADER_LENGTH + PaddedLength;
         }
 
-        public new virtual string ToString()
+        public override string ToString()
         {
-            string attrDescrString = "STUN Attribute: " + AttributeType.ToString() + ", length=" + PaddedLength + ".";
+            var sb = new ValueStringBuilder(stackalloc char[256]);
 
-            return attrDescrString;
+            try
+            {
+                ToString(ref sb);
+
+                return sb.ToString();
+            }
+            finally
+            {
+                sb.Dispose();
+            }
+        }
+
+        internal void ToString(ref ValueStringBuilder sb)
+        {
+            sb.Append("STUN Attribute: ");
+            sb.Append(AttributeType.ToString());
+            sb.Append(", ");
+            ValueToString(ref sb);
+            sb.Append('.');
+        }
+
+        private protected virtual void ValueToString(ref ValueStringBuilder sb)
+        {
+            sb.Append(Value);
+            sb.Append(", length=");
+            sb.Append(PaddedLength);
         }
     }
 }
diff --git a/src/net/STUN/STUNAttributes/STUNChangeRequestAttribute.cs b/src/net/STUN/STUNAttributes/STUNChangeRequestAttribute.cs
index fff4156578..dd7378943f 100644
--- a/src/net/STUN/STUNAttributes/STUNChangeRequestAttribute.cs
+++ b/src/net/STUN/STUNAttributes/STUNChangeRequestAttribute.cs
@@ -14,6 +14,7 @@
 //-----------------------------------------------------------------------------
 
 using System;
+using SIPSorcery.Sys;
 
 namespace SIPSorcery.Net
 {
@@ -51,11 +52,14 @@ public STUNChangeRequestAttribute(byte[] attributeValue)
             }
         }
 
-        public override string ToString()
+        private protected override void ValueToString(ref ValueStringBuilder sb)
         {
-            string attrDescrStr = "STUN Attribute: " + STUNAttributeTypesEnum.ChangeRequest.ToString() + ", key byte=" + m_changeRequestByte.ToString("X") + ", change address=" + ChangeAddress + ", change port=" + ChangePort + ".";
-
-            return attrDescrStr;
+            sb.Append("key byte=");
+            sb.Append(m_changeRequestByte, "X");
+            sb.Append(", change address=");
+            sb.Append(ChangeAddress);
+            sb.Append(", change port=");
+            sb.Append(ChangePort);
         }
     }
 }
diff --git a/src/net/STUN/STUNAttributes/STUNConnectionIdAttribute.cs b/src/net/STUN/STUNAttributes/STUNConnectionIdAttribute.cs
index 33114ab83f..eced72b610 100644
--- a/src/net/STUN/STUNAttributes/STUNConnectionIdAttribute.cs
+++ b/src/net/STUN/STUNAttributes/STUNConnectionIdAttribute.cs
@@ -45,11 +45,10 @@ public STUNConnectionIdAttribute(uint connectionId)
             ConnectionId = connectionId;
         }
 
-        public override string ToString()
+        private protected override void ValueToString(ref ValueStringBuilder sb)
         {
-            string attrDescrStr = "STUN CONNECTION_ID Attribute: value=" + ConnectionId + ".";
-
-            return attrDescrStr;
+            sb.Append("connection ID=");
+            sb.Append(ConnectionId);
         }
     }
 }
diff --git a/src/net/STUN/STUNAttributes/STUNErrorCodeAttribute.cs b/src/net/STUN/STUNAttributes/STUNErrorCodeAttribute.cs
index b97bedb815..ee893f563d 100755
--- a/src/net/STUN/STUNAttributes/STUNErrorCodeAttribute.cs
+++ b/src/net/STUN/STUNAttributes/STUNErrorCodeAttribute.cs
@@ -15,6 +15,7 @@
 
 using System;
 using System.Text;
+using SIPSorcery.Sys;
 
 namespace SIPSorcery.Net
 {
@@ -61,11 +62,12 @@ public override int ToByteBuffer(byte[] buffer, int startIndex)
             return STUNAttribute.STUNATTRIBUTE_HEADER_LENGTH + 4 + reasonPhraseBytes.Length;
         }
 
-        public override string ToString()
+        private protected override void ValueToString(ref ValueStringBuilder sb)
         {
-            string attrDescrStr = "STUN ERROR_CODE_ADDRESS Attribute: error code=" + ErrorCode + ", reason phrase=" + ReasonPhrase + ".";
-
-            return attrDescrStr;
+            sb.Append("error code=");
+            sb.Append(ErrorCode);
+            sb.Append(", reason phrase=");
+            sb.Append(ReasonPhrase);
         }
     }
 }
diff --git a/src/net/STUN/STUNAttributes/STUNXORAddressAttribute.cs b/src/net/STUN/STUNAttributes/STUNXORAddressAttribute.cs
index c6cc025c0e..21c0f0eed2 100644
--- a/src/net/STUN/STUNAttributes/STUNXORAddressAttribute.cs
+++ b/src/net/STUN/STUNAttributes/STUNXORAddressAttribute.cs
@@ -173,5 +173,7 @@ public IPEndPoint GetIPEndPoint()
                 return null;
             }
         }
+
+        private protected override void ValueToString(ref ValueStringBuilder sb) => base.ValueToString(ref sb);
     }
 }
diff --git a/src/net/STUN/STUNDns.cs b/src/net/STUN/STUNDns.cs
index 7e316ea601..4598912539 100644
--- a/src/net/STUN/STUNDns.cs
+++ b/src/net/STUN/STUNDns.cs
@@ -185,7 +185,7 @@ private static async Task Resolve(STUNUri uri, QueryType queryType)
                         {
                             ServiceHostEntry srvResult = null;
                             // No explicit port so use a SRV -> (A | AAAA -> A) record lookup.
-                            var result = await _lookupClient.ResolveServiceAsync(uri.Host, uri.Scheme.ToString(), uri.Protocol.ToString().ToLower()).ConfigureAwait(false);
+                            var result = await _lookupClient.ResolveServiceAsync(uri.Host, uri.Scheme.ToString(), uri.Protocol.ToLowerString()).ConfigureAwait(false);
                             if (result == null || result.Count() == 0)
                             {
                                 //logger.LogDebug("STUNDns SRV lookup returned no results for {uri}.", uri);
diff --git a/src/sys/CRC32.cs b/src/sys/CRC32.cs
index 52df73bd51..ff89a9008d 100644
--- a/src/sys/CRC32.cs
+++ b/src/sys/CRC32.cs
@@ -1,6 +1,7 @@
 // from http://damieng.com/blog/2006/08/08/Calculating_CRC32_in_C_and_NET
 
 using System;
+using System.Buffers.Binary;
 using System.Security.Cryptography;
 
 namespace SIPSorcery.Sys
@@ -36,7 +37,18 @@ public override void Initialize()
 
         protected override void HashCore(byte[] buffer, int start, int length)
         {
-            hash = CalculateHash(table, hash, buffer, start, length);
+            hash = CalculateHash(table, hash, buffer.AsSpan(start, length));
+        }
+
+        protected
+#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER || NET5_0_OR_GREATER
+            override
+#else
+            virtual
+#endif
+            void HashCore(ReadOnlySpan buffer)
+        {
+            hash = CalculateHash(table, hash, buffer);
         }
 
         protected override byte[] HashFinal()
@@ -53,17 +65,32 @@ public override int HashSize
 
         public static UInt32 Compute(byte[] buffer)
         {
-            return ~CalculateHash(InitializeTable(DefaultPolynomial), DefaultSeed, buffer, 0, buffer.Length);
+            return Compute(buffer.AsSpan(0, buffer.Length));
         }
 
         public static UInt32 Compute(UInt32 seed, byte[] buffer)
         {
-            return ~CalculateHash(InitializeTable(DefaultPolynomial), seed, buffer, 0, buffer.Length);
+            return Compute(seed, buffer.AsSpan());
         }
 
         public static UInt32 Compute(UInt32 polynomial, UInt32 seed, byte[] buffer)
         {
-            return ~CalculateHash(InitializeTable(polynomial), seed, buffer, 0, buffer.Length);
+            return Compute(polynomial, seed, buffer.AsSpan());
+        }
+
+        public static uint Compute(ReadOnlySpan buffer)
+        {
+            return ~CalculateHash(InitializeTable(DefaultPolynomial), DefaultSeed, buffer);
+        }
+
+        public static uint Compute(uint seed, ReadOnlySpan buffer)
+        {
+            return ~CalculateHash(InitializeTable(DefaultPolynomial), seed, buffer);
+        }
+
+        public static uint Compute(uint polynomial, uint seed, ReadOnlySpan buffer)
+        {
+            return ~CalculateHash(InitializeTable(polynomial), seed, buffer);
         }
 
         private static UInt32[] InitializeTable(UInt32 polynomial)
@@ -99,27 +126,31 @@ private static UInt32[] InitializeTable(UInt32 polynomial)
             return createTable;
         }
 
-        private static UInt32 CalculateHash(UInt32[] table, UInt32 seed, byte[] buffer, int start, int size)
+        private static UInt32 CalculateHash(ReadOnlySpan table, uint seed, ReadOnlySpan buffer)
         {
-            UInt32 crc = seed;
-            for (int i = start; i < size; i++)
+            /*
+            if (Sse42.IsSupported)
+            {
+                uint crc = Sse42.Crc32(seed, value);
+            }
+            */
+
+            var crc = seed;
+            for (int i = 0; i < buffer.Length; i++)
             {
                 unchecked
                 {
-                    crc = (crc >> 8) ^ table[buffer[i] ^ crc & 0xff];
+                    crc = (crc >> 8) ^ table[buffer[i] ^ (byte)(crc & 0xff)];
                 }
             }
             return crc;
         }
 
-        private byte[] UInt32ToBigEndianBytes(UInt32 x)
+        private byte[] UInt32ToBigEndianBytes(uint x)
         {
-            return new byte[] {
-                (byte)((x >> 24) & 0xff),
-                (byte)((x >> 16) & 0xff),
-                (byte)((x >> 8) & 0xff),
-                (byte)(x & 0xff)
-            };
+            var result = new byte[sizeof(uint)];
+            BinaryPrimitives.WriteUInt32BigEndian(result, x);
+            return result;
         }
     }
 }
diff --git a/src/sys/EncodingExtensions.cs b/src/sys/EncodingExtensions.cs
new file mode 100644
index 0000000000..c285be0cf0
--- /dev/null
+++ b/src/sys/EncodingExtensions.cs
@@ -0,0 +1,57 @@
+using System;
+using System.Text;
+
+namespace SIPSorcery.Sys
+{
+    /// 
+    /// Extension methods for .
+    /// 
+    internal static class EncodingExtensions
+    {
+#if NETSTANDARD2_0 || NETFRAMEWORK
+        /// 
+        /// Decodes a sequence of bytes from a read-only span into a string.
+        /// 
+        /// The encoding to use for the conversion.
+        /// The span containing the sequence of bytes to decode.
+        /// A string containing the decoded characters.
+        public unsafe static string GetString(this Encoding encoding, ReadOnlySpan bytes)
+        {
+            fixed (byte* ptr = bytes)
+            {
+                return encoding.GetString(ptr, bytes.Length);
+            }
+        }
+
+        /// 
+        /// Encodes a set of characters from a read-only span into a sequence of bytes.
+        /// 
+        /// The encoding to use for the conversion.
+        /// The span containing the set of characters to encode.
+        /// The span to contain the resulting sequence of bytes.
+        /// The actual number of bytes written into the byte span.
+        public unsafe static int GetBytes(this Encoding encoding, ReadOnlySpan chars, Span bytes)
+        {
+            fixed (char* pChars = chars)
+            fixed (byte* pBytes = bytes)
+            {
+                return encoding.GetBytes(pChars, chars.Length, pBytes, bytes.Length);
+            }
+        }
+
+        /// 
+        /// Calculates the number of bytes needed to encode a set of characters.
+        /// 
+        /// The encoding to use for the calculation.
+        /// The span containing the set of characters to encode.
+        /// The number of bytes needed to encode the specified characters.
+        public unsafe static int GetByteCount(this Encoding encoding, ReadOnlySpan chars)
+        {
+            fixed (char* pChars = chars)
+            {
+                return encoding.GetByteCount(pChars, chars.Length);
+            }
+        }
+#endif
+    }
+}
diff --git a/src/sys/EnumExtensions.cs b/src/sys/EnumExtensions.cs
new file mode 100644
index 0000000000..8cacca3f0a
--- /dev/null
+++ b/src/sys/EnumExtensions.cs
@@ -0,0 +1,55 @@
+using System.Net.Sockets;
+
+namespace SIPSorcery.Sys;
+
+/// 
+/// Extension methods for enumeration types used in the system.
+/// 
+internal static class EnumExtensions
+{
+    /// 
+    /// Converts a ProtocolType enumeration value to its lowercase string representation.
+    /// 
+    /// The ProtocolType enumeration value to convert.
+    /// A lowercase string representation of the protocol type. For most protocols,
+    /// returns the standard abbreviated form (e.g. "tcp", "udp", "ipv6"). For unrecognized
+    /// protocol types, returns the enum value converted to lowercase.
+    /// 
+    /// This method provides standardized string representations for network protocols,
+    /// particularly useful for logging, configuration, and protocol-specific formatting needs.
+    /// 
+    public static string ToLowerString(this ProtocolType protocolType)
+    {
+        return protocolType switch
+        {
+            ProtocolType.IP => "ip",
+
+            ProtocolType.Icmp => "icmp",
+            ProtocolType.Igmp => "igmp",
+            ProtocolType.Ggp => "ggp",
+
+            ProtocolType.IPv4 => "ipv4",
+            ProtocolType.Tcp => "tcp",
+            ProtocolType.Pup => "pup",
+            ProtocolType.Udp => "udp",
+            ProtocolType.Idp => "idp",
+            ProtocolType.IPv6 => "ipv6",
+            ProtocolType.IPv6RoutingHeader => "routing",
+            ProtocolType.IPv6FragmentHeader => "fragment",
+            ProtocolType.IPSecEncapsulatingSecurityPayload => "ipsecencapsulatingsecuritypayload",
+            ProtocolType.IPSecAuthenticationHeader => "ipsecauthenticationheader",
+            ProtocolType.IcmpV6 => "icmpv6",
+            ProtocolType.IPv6NoNextHeader => "nonext",
+            ProtocolType.IPv6DestinationOptions => "dstopts",
+            ProtocolType.ND => "nd",
+            ProtocolType.Raw => "raw",
+
+            ProtocolType.Ipx => "ipx",
+            ProtocolType.Spx => "spx",
+            ProtocolType.SpxII => "spx2",
+            ProtocolType.Unknown => "unknown",
+
+            _ => protocolType.ToString().ToLowerInvariant()
+        };
+    }
+}
diff --git a/src/sys/JSONWriter.cs b/src/sys/JSONWriter.cs
index 50c5d2ab09..4435504729 100644
--- a/src/sys/JSONWriter.cs
+++ b/src/sys/JSONWriter.cs
@@ -15,11 +15,13 @@
 //-----------------------------------------------------------------------------
 
 using System;
+using System.Buffers;
 using System.Collections;
 using System.Collections.Generic;
 using System.Reflection;
 using System.Runtime.Serialization;
 using System.Text;
+using SIPSorcery.Sys;
 
 namespace TinyJson
 {
@@ -31,203 +33,250 @@ public static class JSONWriter
     {
         public static string ToJson(this object item)
         {
-            StringBuilder stringBuilder = new StringBuilder();
-            AppendValue(stringBuilder, item);
-            return stringBuilder.ToString();
+            var builder = new ValueStringBuilder();
+
+            try
+            {
+                AppendValue(ref builder, item);
+
+                return builder.ToString();
+            }
+            finally
+            {
+                builder.Dispose();
+            }
         }
 
-        static void AppendValue(StringBuilder stringBuilder, object item)
+        static void AppendValue(ref ValueStringBuilder builder, object item)
         {
             if (item == null)
             {
-                stringBuilder.Append("null");
+                builder.Append("null");
                 return;
             }
 
-            Type type = item.GetType();
-            if (type == typeof(string) || type == typeof(char))
+            var type = item.GetType();
+
+            if (type.IsEnum)
             {
-                stringBuilder.Append('"');
-                string str = item.ToString();
-                for (int i = 0; i < str.Length; ++i)
-                {
-                    if (str[i] < ' ' || str[i] == '"' || str[i] == '\\')
+                builder.Append('"');
+                builder.Append(item.ToString());
+                builder.Append('"');
+                return;
+            }
+
+            var typeCode = Type.GetTypeCode(type);
+
+            switch (typeCode)
+            {
+
+                case TypeCode.String:
                     {
-                        stringBuilder.Append('\\');
-                        int j = "\"\\\n\r\t\b\f".IndexOf(str[i]);
-                        if (j >= 0)
+                        builder.Append('"');
+                        var str = ((string)item).AsSpan();
+                        for (var i = 0; i < str.Length; i++)
                         {
-                            stringBuilder.Append("\"\\nrtbf"[j]);
-                        }
-                        else
-                        {
-                            stringBuilder.AppendFormat("u{0:X4}", (UInt32)str[i]);
+                            AppendEscapedChar(ref builder, str[i]);
                         }
+                        builder.Append('"');
+                        return;
                     }
-                    else
+
+                case TypeCode.Char:
                     {
-                        stringBuilder.Append(str[i]);
+                        builder.Append('"');
+                        AppendEscapedChar(ref builder, (char)item);
+                        builder.Append('"');
+                        return;
+
+                    }
+
+                case TypeCode.Boolean:
+                    {
+                        builder.Append((bool)item ? "true" : "false");
+                        return;
+                    }
+
+                case TypeCode.Single:
+                    {
+                        builder.Append((float)item, provider: System.Globalization.CultureInfo.InvariantCulture);
+                        return;
+                    }
+
+                case TypeCode.Double:
+                    {
+                        builder.Append((double)item, provider: System.Globalization.CultureInfo.InvariantCulture);
+                        return;
+                    }
+
+                case TypeCode.Decimal:
+                    {
+                        builder.Append((decimal)item, provider: System.Globalization.CultureInfo.InvariantCulture);
+                        return;
+                    }
+
+                case TypeCode.Byte:
+                case TypeCode.SByte:
+                case TypeCode.Int16:
+                case TypeCode.UInt16:
+                case TypeCode.Int32:
+                case TypeCode.UInt32:
+                case TypeCode.Int64:
+                case TypeCode.UInt64:
+                    {
+                        builder.Append(item.ToString());
+                        return;
+                    }
+
+                case TypeCode.DBNull:
+                case TypeCode.Empty:
+                    {
+                        builder.Append("null");
+                        return;
                     }
-                }
-                stringBuilder.Append('"');
-            }
-            else if (type == typeof(byte) || type == typeof(sbyte))
-            {
-                stringBuilder.Append(item.ToString());
-            }
-            else if (type == typeof(short) || type == typeof(ushort))
-            {
-                stringBuilder.Append(item.ToString());
-            }
-            else if (type == typeof(int) || type == typeof(uint))
-            {
-                stringBuilder.Append(item.ToString());
-            }
-            else if (type == typeof(long) || type == typeof(ulong))
-            {
-                stringBuilder.Append(item.ToString());
-            }
-            else if (type == typeof(float))
-            {
-                stringBuilder.Append(((float)item).ToString(System.Globalization.CultureInfo.InvariantCulture));
-            }
-            else if (type == typeof(double))
-            {
-                stringBuilder.Append(((double)item).ToString(System.Globalization.CultureInfo.InvariantCulture));
-            }
-            else if (type == typeof(decimal))
-            {
-                stringBuilder.Append(((decimal)item).ToString(System.Globalization.CultureInfo.InvariantCulture));
-            }
-            else if (type == typeof(bool))
-            {
-                stringBuilder.Append(((bool)item) ? "true" : "false");
             }
-            else if (type.IsEnum)
+
+            static void AppendEscapedChar(ref ValueStringBuilder builder, char ch)
             {
-                stringBuilder.Append('"');
-                stringBuilder.Append(item.ToString());
-                stringBuilder.Append('"');
+                if (ch is >= ' ' and not '"' and not '\\')
+                {
+                    builder.Append(ch);
+                }
+                else
+                {
+
+                    builder.Append('\\');
+                    switch (ch)
+                    {
+                        case '"': builder.Append('"'); break;
+                        case '\\': builder.Append('\\'); break;
+                        case '\n': builder.Append('n'); break;
+                        case '\r': builder.Append('r'); break;
+                        case '\t': builder.Append('t'); break;
+                        case '\b': builder.Append('b'); break;
+                        case '\f': builder.Append('f'); break;
+                        default:
+                            builder.Append('u');
+                            builder.Append(((uint)ch).ToString("X4"));
+                            break;
+                    }
+                }
             }
-            else if (item is IList)
+
+            if (item is IList list)
             {
-                stringBuilder.Append('[');
-                bool isFirst = true;
-                IList list = item as IList;
-                for (int i = 0; i < list.Count; i++)
+                builder.Append('[');
+                var isFirst = true;
+                for (var i = 0; i < list.Count; i++)
                 {
-                    if (isFirst)
+                    if (!isFirst)
                     {
-                        isFirst = false;
+                        builder.Append(',');
                     }
                     else
                     {
-                        stringBuilder.Append(',');
+                        isFirst = false;
                     }
-                    AppendValue(stringBuilder, list[i]);
+                    AppendValue(ref builder, list[i]);
                 }
-                stringBuilder.Append(']');
+                builder.Append(']');
             }
             else if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Dictionary<,>))
             {
-                Type keyType = type.GetGenericArguments()[0];
-
-                //Refuse to output dictionary keys that aren't of type string
+                var keyType = type.GetGenericArguments()[0];
                 if (keyType != typeof(string))
                 {
-                    stringBuilder.Append("{}");
+                    builder.Append("{}");
                     return;
                 }
 
-                stringBuilder.Append('{');
-                IDictionary dict = item as IDictionary;
-                bool isFirst = true;
-                foreach (object key in dict.Keys)
+                var dict = item as IDictionary;
+                builder.Append('{');
+                var isFirst = true;
+                foreach (var key in dict.Keys)
                 {
-                    if (isFirst)
+                    if (!isFirst)
                     {
-                        isFirst = false;
+                        builder.Append(',');
                     }
                     else
                     {
-                        stringBuilder.Append(',');
+                        isFirst = false;
                     }
-                    stringBuilder.Append('\"');
-                    stringBuilder.Append((string)key);
-                    stringBuilder.Append("\":");
-                    AppendValue(stringBuilder, dict[key]);
+                    builder.Append('\"');
+                    builder.Append((string)key);
+                    builder.Append("\":");
+                    AppendValue(ref builder, dict[key]);
                 }
-                stringBuilder.Append('}');
+                builder.Append('}');
             }
             else
             {
-                stringBuilder.Append('{');
+                builder.Append('{');
+                var isFirst = true;
 
-                bool isFirst = true;
-                FieldInfo[] fieldInfos = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.FlattenHierarchy);
-                for (int i = 0; i < fieldInfos.Length; i++)
+                var fieldInfos = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.FlattenHierarchy);
+                foreach (var field in fieldInfos)
                 {
-                    if (fieldInfos[i].IsDefined(typeof(IgnoreDataMemberAttribute), true))
+                    if (field.IsDefined(typeof(IgnoreDataMemberAttribute), true))
                     {
                         continue;
                     }
 
-                    object value = fieldInfos[i].GetValue(item);
+                    var value = field.GetValue(item);
                     if (value != null)
                     {
-                        if (isFirst)
+                        if (!isFirst)
                         {
-                            isFirst = false;
+                            builder.Append(',');
                         }
                         else
                         {
-                            stringBuilder.Append(',');
+                            isFirst = false;
                         }
-                        stringBuilder.Append('\"');
-                        stringBuilder.Append(GetMemberName(fieldInfos[i]));
-                        stringBuilder.Append("\":");
-                        AppendValue(stringBuilder, value);
+                        builder.Append('\"');
+                        builder.Append(GetMemberName(field));
+                        builder.Append("\":");
+                        AppendValue(ref builder, value);
                     }
                 }
-                PropertyInfo[] propertyInfo = type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.FlattenHierarchy);
-                for (int i = 0; i < propertyInfo.Length; i++)
+
+                var propertyInfos = type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.FlattenHierarchy);
+                foreach (var prop in propertyInfos)
                 {
-                    if (!propertyInfo[i].CanRead || propertyInfo[i].IsDefined(typeof(IgnoreDataMemberAttribute), true))
+                    if (!prop.CanRead || prop.IsDefined(typeof(IgnoreDataMemberAttribute), true))
                     {
                         continue;
                     }
 
-                    object value = propertyInfo[i].GetValue(item, null);
+                    var value = prop.GetValue(item, null);
                     if (value != null)
                     {
-                        if (isFirst)
+                        if (!isFirst)
                         {
-                            isFirst = false;
+                            builder.Append(',');
                         }
                         else
                         {
-                            stringBuilder.Append(',');
+                            isFirst = false;
                         }
-                        stringBuilder.Append('\"');
-                        stringBuilder.Append(GetMemberName(propertyInfo[i]));
-                        stringBuilder.Append("\":");
-                        AppendValue(stringBuilder, value);
+                        builder.Append('\"');
+                        builder.Append(GetMemberName(prop));
+                        builder.Append("\":");
+                        AppendValue(ref builder, value);
                     }
                 }
 
-                stringBuilder.Append('}');
+                builder.Append('}');
             }
         }
 
         static string GetMemberName(MemberInfo member)
         {
-            if (member.IsDefined(typeof(DataMemberAttribute), true))
+            if (Attribute.GetCustomAttribute(member, typeof(DataMemberAttribute), true) is DataMemberAttribute attr &&
+                !string.IsNullOrEmpty(attr.Name))
             {
-                DataMemberAttribute dataMemberAttribute = (DataMemberAttribute)Attribute.GetCustomAttribute(member, typeof(DataMemberAttribute), true);
-                if (!string.IsNullOrEmpty(dataMemberAttribute.Name))
-                {
-                    return dataMemberAttribute.Name;
-                }
+                return attr.Name;
             }
 
             return member.Name;
diff --git a/src/sys/TypeExtensions.cs b/src/sys/TypeExtensions.cs
index 47d7e29998..cfadfc42c6 100755
--- a/src/sys/TypeExtensions.cs
+++ b/src/sys/TypeExtensions.cs
@@ -16,6 +16,7 @@
 //-----------------------------------------------------------------------------
 
 using System;
+using System.Buffers;
 using System.Collections.Generic;
 using System.Net;
 
@@ -55,7 +56,7 @@ public static class TypeExtensions
         ///     
         public static bool IsNullOrBlank(this string s)
         {
-            if (s == null || s.Trim(WhiteSpaceChars).Length == 0)
+            if (s == null || s.AsSpan().Trim(WhiteSpaceChars).Length == 0)
             {
                 return true;
             }
@@ -65,7 +66,7 @@ public static bool IsNullOrBlank(this string s)
 
         public static bool NotNullOrBlank(this string s)
         {
-            if (s == null || s.Trim(WhiteSpaceChars).Length == 0)
+            if (s == null || s.AsSpan().Trim(WhiteSpaceChars).Length == 0)
             {
                 return false;
             }
@@ -123,59 +124,29 @@ public static string Slice(this string s, char startDelimiter, char endDelimeter
 
         public static string HexStr(this byte[] buffer, char? separator = null)
         {
-            return buffer.HexStr(buffer.Length, separator);
+            return HexStr(buffer.AsSpan(), separator: separator, lowercase: false);
         }
 
         public static string HexStr(this byte[] buffer, int length, char? separator = null)
         {
-            if (separator is { } s)
-            {
-                int numberOfChars = length * 3 - 1;
-#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
-                return string.Create(numberOfChars, (buffer, length, s), PopulateNewStringWithSeparator);
-#else
-                var rv = new char[numberOfChars];
-                PopulateNewStringWithSeparator(rv, (buffer, length, s));
-                return new string(rv);
-#endif
-            }
-            else
-            {
-                int numberOfChars = length * 2;
-#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
-                return string.Create(numberOfChars, (buffer, length), PopulateNewStringWithoutSeparator);
-#else
-                var rv = new char[numberOfChars];
-                PopulateNewStringWithoutSeparator(rv, (buffer, length));
-                return new string(rv);
-#endif
-            }
+            return HexStr(buffer.AsSpan(0, buffer.Length), separator: separator, lowercase: false);
+        }
 
-            static void PopulateNewStringWithSeparator(Span chars, (byte[] buffer, int length, char separator) state)
-            {
-                var (buffer, length, s) = state;
-                for (int i = 0, j = 0; i < length; i++)
-                {
-                    var val = buffer[i];
-                    chars[j++] = char.ToUpperInvariant(hexmap[val >> 4]);
-                    chars[j++] = char.ToUpperInvariant(hexmap[val & 15]);
-                    if (j < chars.Length)
-                    {
-                        chars[j++] = s;
-                    }
-                }
-            }
+        public static string HexStr(this byte[] buffer, int length, char? separator = null, bool lowercase = false)
+        {
+            return HexStr(buffer.AsSpan(0, length), separator: separator, lowercase: lowercase);
+        }
 
-            static void PopulateNewStringWithoutSeparator(Span chars, (byte[] buffer, int length) state)
-            {
-                var (buffer, length) = state;
-                for (int i = 0, j = 0; i < length; i++)
-                {
-                    var val = buffer[i];
-                    chars[j++] = char.ToUpperInvariant(hexmap[val >> 4]);
-                    chars[j++] = char.ToUpperInvariant(hexmap[val & 15]);
-                }
-            }
+        public static string HexStr(this Span buffer, char? separator = null, bool lowercase = false)
+        {
+            return HexStr((ReadOnlySpan)buffer, separator: separator, lowercase: lowercase);
+        }
+
+        public static string HexStr(this ReadOnlySpan buffer, char? separator = null, bool lowercase = false)
+        {
+            using var sb = new ValueStringBuilder(stackalloc char[256]);
+            sb.Append(buffer, separator, lowercase);
+            return sb.ToString();
         }
 
         public static byte[] ParseHexStr(string hexStr)
diff --git a/src/sys/ValueStringBuilder.AppendSpanFormattable.cs b/src/sys/ValueStringBuilder.AppendSpanFormattable.cs
new file mode 100644
index 0000000000..9498a559fa
--- /dev/null
+++ b/src/sys/ValueStringBuilder.AppendSpanFormattable.cs
@@ -0,0 +1,30 @@
+using System;
+using System.Runtime.CompilerServices;
+
+namespace SIPSorcery.Sys
+{
+#if NET6_0_OR_GREATER
+    internal ref partial struct ValueStringBuilder
+    {
+        /// 
+        /// Appends a value that implements ISpanFormattable to the string builder using span-based formatting.
+        /// If span formatting fails, falls back to regular string formatting.
+        /// 
+        /// The type of the value to format. Must implement ISpanFormattable.
+        /// The value to append.
+        /// A format string that defines the formatting to apply, or null to use default formatting.
+        /// An object that supplies culture-specific formatting information, or null to use default formatting.
+        internal void AppendSpanFormattable(T value, string? format = null, IFormatProvider? provider = null) where T : ISpanFormattable
+        {
+            if (value.TryFormat(_chars.Slice(_pos), out int charsWritten, format, provider))
+            {
+                _pos += charsWritten;
+            }
+            else
+            {
+                Append(value.ToString(format, provider));
+            }
+        }
+    }
+#endif
+}
diff --git a/src/sys/ValueStringBuilder.Bytes.cs b/src/sys/ValueStringBuilder.Bytes.cs
new file mode 100644
index 0000000000..ccf0da837b
--- /dev/null
+++ b/src/sys/ValueStringBuilder.Bytes.cs
@@ -0,0 +1,75 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace SIPSorcery.Sys
+{
+    internal ref partial struct ValueStringBuilder
+    {
+        /// 
+        /// Character array for uppercase hexadecimal representation (0-9, A-F).
+        /// 
+        private static readonly char[] upperHexmap = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
+
+        /// 
+        /// Character array for lowercase hexadecimal representation (0-9, a-f).
+        /// 
+        private static readonly char[] lowerHexmap = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
+
+        /// 
+        /// Appends a byte array to the string builder as hexadecimal characters.
+        /// 
+        /// The byte array to append. Can be null.
+        /// Optional separator character to insert between bytes.
+        public void Append(byte[]? bytes, char? separator = null)
+        {
+            if (bytes is { Length: > 0 })
+            {
+                Append(bytes.AsSpan(), separator);
+            }
+        }
+
+        /// 
+        /// Appends a span of bytes to the string builder as hexadecimal characters.
+        /// 
+        /// The span of bytes to append.
+        /// Optional separator character to insert between bytes.
+        /// If true, uses lowercase hex characters (a-f); if false, uses uppercase (A-F).
+        /// 
+        /// Each byte is converted to two hexadecimal characters. If a separator is specified,
+        /// it will be inserted between each pair of hex characters representing a byte.
+        /// For example, with separator '-': "AA-BB-CC"
+        /// 
+        public void Append(ReadOnlySpan bytes, char? separator = null, bool lowercase = false)
+        {
+            var hexmap = lowercase ? lowerHexmap : upperHexmap;
+
+            if (bytes.IsEmpty)
+            {
+                return;
+            }
+
+            if (separator is { } s)
+            {
+                for (int i = 0; i < bytes.Length;)
+                {
+                    var b = bytes[i];
+                    Append(hexmap[(int)b >> 4]);
+                    Append(hexmap[(int)b & 0b1111]);
+                    if (++i < bytes.Length)
+                    {
+                        Append(s);
+                    }
+                }
+            }
+            else
+            {
+                for (var i = 0; i < bytes.Length; i++)
+                {
+                    var b = bytes[i];
+                    Append(hexmap[(int)b >> 4]);
+                    Append(hexmap[(int)b & 0b1111]);
+                }
+            }
+        }
+    }
+}
diff --git a/src/sys/ValueStringBuilder.cs b/src/sys/ValueStringBuilder.cs
new file mode 100644
index 0000000000..bcbf87ddc4
--- /dev/null
+++ b/src/sys/ValueStringBuilder.cs
@@ -0,0 +1,529 @@
+// Based on System.Text.ValueStringBuilder - System.Console
+
+using System;
+using System.Buffers;
+using System.Diagnostics;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+namespace SIPSorcery.Sys
+{
+    /// 
+    /// A ref struct that provides a low-allocation way to build strings.
+    /// Similar to StringBuilder but stackalloc-based for better performance.
+    /// 
+    internal ref partial struct ValueStringBuilder
+    {
+        /// The array to return to the array pool, if one was rented.
+        private char[]? _arrayToReturnToPool;
+        /// The span containing the characters written so far.
+        private Span _chars;
+        /// The current position within the span.
+        private int _pos;
+
+        /// 
+        /// Initializes a new instance of ValueStringBuilder with a provided character buffer.
+        /// 
+        /// The initial buffer to use for storing characters.
+        public ValueStringBuilder(Span initialBuffer)
+        {
+            _arrayToReturnToPool = null;
+            _chars = initialBuffer;
+            _pos = 0;
+        }
+
+        /// 
+        /// Initializes a new instance of ValueStringBuilder with a specified initial capacity.
+        /// 
+        /// The initial capacity of the internal buffer.
+        public ValueStringBuilder(int initialCapacity)
+        {
+            _arrayToReturnToPool = ArrayPool.Shared.Rent(initialCapacity);
+            _chars = _arrayToReturnToPool;
+            _pos = 0;
+        }
+
+        /// 
+        /// Gets or sets the length of the current builder's content.
+        /// 
+        public int Length
+        {
+            get => _pos;
+            set
+            {
+                Debug.Assert(value >= 0);
+                Debug.Assert(value <= _chars.Length);
+                _pos = value;
+            }
+        }
+
+        /// 
+        /// Gets the total capacity of the builder's buffer.
+        /// 
+        public int Capacity => _chars.Length;
+
+        /// 
+        /// Gets a read-only span containing the builder's characters.
+        /// 
+        public ReadOnlySpan Chars => _chars;
+
+        /// 
+        /// Ensures the builder has enough capacity to accommodate a specified total number of characters.
+        /// 
+        /// The minimum capacity needed.
+        public void EnsureCapacity(int capacity)
+        {
+            // This is not expected to be called this with negative capacity
+            Debug.Assert(capacity >= 0);
+
+            // If the caller has a bug and calls this with negative capacity, make sure to call Grow to throw an exception.
+            if ((uint)capacity > (uint)_chars.Length)
+            {
+                Grow(capacity - _pos);
+            }
+        }
+
+        /// 
+        /// Get a pinnable reference to the builder.
+        /// Does not ensure there is a null char after 
+        /// This overload is pattern matched in the C# 7.3+ compiler so you can omit
+        /// the explicit method call, and write eg "fixed (char* c = builder)"
+        /// 
+        /// A reference to the underlying characters.
+        public ref char GetPinnableReference() => ref MemoryMarshal.GetReference(_chars);
+
+        /// 
+        /// Get a pinnable reference to the builder.
+        /// 
+        /// If , ensures that the builder has a null char after 
+        /// A reference to the underlying characters.
+        public ref char GetPinnableReference(bool terminate)
+        {
+            if (terminate)
+            {
+                EnsureCapacity(Length + 1);
+                _chars[Length] = '\0';
+            }
+            return ref MemoryMarshal.GetReference(_chars);
+        }
+
+        /// 
+        /// Gets a reference to the character at the specified position.
+        /// 
+        /// The zero-based index of the character to get.
+        /// A reference to the character at the specified position.
+        public ref char this[int index]
+        {
+            get
+            {
+                Debug.Assert(index < _pos);
+                return ref _chars[index];
+            }
+        }
+
+        /// 
+        /// Returns the built string and disposes the builder.
+        /// 
+        /// The final string.
+        public new string ToString()
+        {
+            var s = _chars.Slice(0, _pos).ToString();
+            Dispose();
+            return s;
+        }
+
+        /// 
+        /// Returns the underlying storage of the builder.
+        /// 
+        public Span RawChars => _chars;
+
+        /// 
+        /// Returns a span around the contents of the builder.
+        /// 
+        /// If , ensures that the builder has a null char after 
+        /// A read-only span of the builder's content.
+        public ReadOnlySpan AsSpan(bool terminate)
+        {
+            if (terminate)
+            {
+                EnsureCapacity(Length + 1);
+                _chars[Length] = '\0';
+            }
+            return _chars.Slice(0, _pos);
+        }
+
+        /// Returns a read-only span of the builder's content.
+        public ReadOnlySpan AsSpan() => _chars.Slice(0, _pos);
+
+        /// Returns a read-only span starting at the specified index.
+        /// The starting index.
+        public ReadOnlySpan AsSpan(int start) => _chars.Slice(start, _pos - start);
+
+        /// Returns a read-only span of the specified length starting at the specified index.
+        /// The starting index.
+        /// The length of the span.
+        public ReadOnlySpan AsSpan(int start, int length) => _chars.Slice(start, length);
+
+        /// 
+        /// Attempts to copy the builder's contents to a destination span.
+        /// 
+        /// The destination span.
+        /// When this method returns, contains the number of characters that were copied.
+        ///  if the copy was successful; otherwise, .
+        public bool TryCopyTo(Span destination, out int charsWritten)
+        {
+            if (_chars.Slice(0, _pos).TryCopyTo(destination))
+            {
+                charsWritten = _pos;
+                Dispose();
+                return true;
+            }
+            else
+            {
+                charsWritten = 0;
+                Dispose();
+                return false;
+            }
+        }
+
+        /// 
+        /// Inserts a repeated character at the specified position.
+        /// 
+        /// The position to insert at.
+        /// The character to insert.
+        /// The number of times to insert the character.
+        public void Insert(int index, char value, int count)
+        {
+            if (_pos > _chars.Length - count)
+            {
+                Grow(count);
+            }
+
+            var remaining = _pos - index;
+            _chars.Slice(index, remaining).CopyTo(_chars.Slice(index + count));
+            _chars.Slice(index, count).Fill(value);
+            _pos += count;
+        }
+
+        /// 
+        /// Inserts a string at the specified position.
+        /// 
+        /// The position to insert at.
+        /// The string to insert.
+        public void Insert(int index, string? s)
+        {
+            if (s == null)
+            {
+                return;
+            }
+
+            var count = s.Length;
+
+            if (_pos > (_chars.Length - count))
+            {
+                Grow(count);
+            }
+
+            var remaining = _pos - index;
+            _chars.Slice(index, remaining).CopyTo(_chars.Slice(index + count));
+            s.AsSpan().CopyTo(_chars.Slice(index));
+            _pos += count;
+        }
+
+        /// 
+        /// Appends a boolean value as its string representation.
+        /// 
+        /// The value to append.
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public void Append(bool value) => Append(value ? "true" : "false");
+
+        /// 
+        /// Appends a character to the builder.
+        /// 
+        /// The character to append.
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public void Append(char c)
+        {
+            var pos = _pos;
+            var chars = _chars;
+            if ((uint)pos < (uint)chars.Length)
+            {
+                chars[pos] = c;
+                _pos = pos + 1;
+            }
+            else
+            {
+                GrowAndAppend(c);
+            }
+        }
+
+        /// 
+        /// Appends a string to the builder.
+        /// 
+        /// The string to append.
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public void Append(string? s)
+        {
+            if (s == null)
+            {
+                return;
+            }
+
+            var pos = _pos;
+            if (s.Length == 1 && (uint)pos < (uint)_chars.Length) // very common case, e.g. appending strings from NumberFormatInfo like separators, percent symbols, etc.
+            {
+                _chars[pos] = s[0];
+                _pos = pos + 1;
+            }
+            else
+            {
+                AppendSlow(s);
+            }
+        }
+
+        /// 
+        /// Slow path for appending a string when the fast path isn't applicable.
+        /// 
+        private void AppendSlow(string s)
+        {
+            var pos = _pos;
+            if (pos > _chars.Length - s.Length)
+            {
+                Grow(s.Length);
+            }
+
+            s.AsSpan().CopyTo(_chars.Slice(pos));
+            _pos += s.Length;
+        }
+
+        /// 
+        /// Appends a character multiple times to the builder.
+        /// 
+        /// The character to append.
+        /// The number of times to append the character.
+        public void Append(char c, int count)
+        {
+            if (_pos > _chars.Length - count)
+            {
+                Grow(count);
+            }
+
+            var dst = _chars.Slice(_pos, count);
+            for (var i = 0; i < dst.Length; i++)
+            {
+                dst[i] = c;
+            }
+            _pos += count;
+        }
+
+        /// 
+        /// Appends a span of characters to the builder.
+        /// 
+        /// The span to append.
+        public void Append(scoped ReadOnlySpan value)
+        {
+            var pos = _pos;
+            if (pos > _chars.Length - value.Length)
+            {
+                Grow(value.Length);
+            }
+
+            value.CopyTo(_chars.Slice(_pos));
+            _pos += value.Length;
+        }
+
+        /// 
+        /// Reserves space for a span of characters and returns a span that can be written to.
+        /// 
+        /// The number of characters to reserve space for.
+        /// A span that can be written to.
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public Span AppendSpan(int length)
+        {
+            var origPos = _pos;
+            if (origPos > _chars.Length - length)
+            {
+                Grow(length);
+            }
+
+            _pos = origPos + length;
+            return _chars.Slice(origPos, length);
+        }
+
+        /// 
+        /// Appends an  value to the builder.
+        /// 
+        /// The value to append.
+        /// An optional format string that guides the formatting, or null to use default formatting.
+        /// An optional object that provides culture-specific formatting services, or null to use default formatting.
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+#if NET6_0_OR_GREATER
+        public void Append(int value, string? format = null, IFormatProvider? provider = null) => AppendSpanFormattable(value, format, provider);
+#else
+        public void Append(int value, string? format = null, IFormatProvider? provider = null) => Append(value.ToString(format, provider));
+#endif
+
+        /// 
+        /// Appends an  value to the builder.
+        /// 
+        /// The value to append.
+        /// An optional format string that guides the formatting, or null to use default formatting.
+        /// An optional object that provides culture-specific formatting services, or null to use default formatting.
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+#if NET6_0_OR_GREATER
+        public void Append(uint value, string? format = null, IFormatProvider? provider = null) => AppendSpanFormattable(value, format, provider);
+#else
+        public void Append(uint value, string? format = null, IFormatProvider? provider = null) => Append(value.ToString(format, provider));
+#endif
+
+        /// 
+        /// Appends an  value to the builder.
+        /// 
+        /// The value to append.
+        /// An optional format string that guides the formatting, or null to use default formatting.
+        /// An optional object that provides culture-specific formatting services, or null to use default formatting.
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+#if NET6_0_OR_GREATER
+        public void Append(ushort value, string? format = null, IFormatProvider? provider = null) => AppendSpanFormattable(value, format, provider);
+#else
+        public void Append(ushort value, string? format = null, IFormatProvider? provider = null) => Append(value.ToString(format, provider));
+#endif
+
+        /// 
+        /// Appends an  value to the builder.
+        /// 
+        /// The value to append.
+        /// An optional format string that guides the formatting, or null to use default formatting.
+        /// An optional object that provides culture-specific formatting services, or null to use default formatting.
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+#if NET6_0_OR_GREATER
+        public void Append(ushort? value, string? format = null, IFormatProvider? provider = null)
+        {
+            if (value is { } v)
+            {
+                AppendSpanFormattable(v, format, provider);
+            }
+        }
+#else
+        public void Append(ushort? value, string? format = null, IFormatProvider? provider = null)
+        {
+            if (value is { } v)
+            {
+                Append(v.ToString(format, provider));
+            }
+        }
+#endif
+
+        /// 
+        /// Appends an  value to the builder.
+        /// 
+        /// The value to append.
+        /// An optional format string that guides the formatting, or null to use default formatting.
+        /// An optional object that provides culture-specific formatting services, or null to use default formatting.
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+#if NET6_0_OR_GREATER
+        public void Append(long value, string? format = null, IFormatProvider? provider = null) => AppendSpanFormattable(value, format, provider);
+#else
+        public void Append(long value, string? format = null, IFormatProvider? provider = null) => Append(value.ToString(format, provider));
+#endif
+
+        /// 
+        /// Appends an  value to the builder.
+        /// 
+        /// The value to append.
+        /// An optional format string that guides the formatting, or null to use default formatting.
+        /// An optional object that provides culture-specific formatting services, or null to use default formatting.
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+#if NET6_0_OR_GREATER
+        public void Append(float value, string? format = null, IFormatProvider? provider = null) => AppendSpanFormattable(value, format, provider);
+#else
+        public void Append(float value, string? format = null, IFormatProvider? provider = null) => Append(value.ToString(format, provider));
+#endif
+
+        /// 
+        /// Appends an  value to the builder.
+        /// 
+        /// The value to append.
+        /// An optional format string that guides the formatting, or null to use default formatting.
+        /// An optional object that provides culture-specific formatting services, or null to use default formatting.
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+#if NET6_0_OR_GREATER
+        public void Append(double value, string? format = null, IFormatProvider? provider = null) => AppendSpanFormattable(value, format, provider);
+#else
+        public void Append(double value, string? format = null, IFormatProvider? provider = null) => Append(value.ToString(format, provider));
+#endif
+
+        /// 
+        /// Appends an  value to the builder.
+        /// 
+        /// The value to append.
+        /// An optional format string that guides the formatting, or null to use default formatting.
+        /// An optional object that provides culture-specific formatting services, or null to use default formatting.
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+#if NET6_0_OR_GREATER
+        public void Append(decimal value, string? format = null, IFormatProvider? provider = null) => AppendSpanFormattable(value, format, provider);
+#else
+        public void Append(decimal value, string? format = null, IFormatProvider? provider = null) => Append(value.ToString(format, provider));
+#endif
+
+        /// 
+        /// Grows the buffer and appends a single character.
+        /// 
+        [MethodImpl(MethodImplOptions.NoInlining)]
+        private void GrowAndAppend(char c)
+        {
+            Grow(1);
+            Append(c);
+        }
+
+        /// 
+        /// Resize the internal buffer either by doubling current buffer size or
+        /// by adding  to
+        ///  whichever is greater.
+        /// 
+        /// 
+        /// Number of chars requested beyond current position.
+        /// 
+        [MethodImpl(MethodImplOptions.NoInlining)]
+        private void Grow(int additionalCapacityBeyondPos)
+        {
+            Debug.Assert(additionalCapacityBeyondPos > 0);
+            Debug.Assert(_pos > _chars.Length - additionalCapacityBeyondPos, "Grow called incorrectly, no resize is needed.");
+
+            const uint ArrayMaxLength = 0x7FFFFFC7; // same as Array.MaxLength
+
+            // Increase to at least the required size (_pos + additionalCapacityBeyondPos), but try
+            // to double the size if possible, bounding the doubling to not go beyond the max array length.
+            var newCapacity = (int)Math.Max(
+                (uint)(_pos + additionalCapacityBeyondPos),
+                Math.Min((uint)_chars.Length * 2, ArrayMaxLength));
+
+            // Make sure to let Rent throw an exception if the caller has a bug and the desired capacity is negative.
+            // This could also go negative if the actual required length wraps around.
+            var poolArray = ArrayPool.Shared.Rent(newCapacity);
+
+            _chars.Slice(0, _pos).CopyTo(poolArray);
+
+            var toReturn = _arrayToReturnToPool;
+            _chars = _arrayToReturnToPool = poolArray;
+            if (toReturn != null)
+            {
+                ArrayPool.Shared.Return(toReturn);
+            }
+        }
+
+        /// 
+        /// Disposes the builder, returning any rented array to the pool.
+        /// 
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public void Dispose()
+        {
+            var toReturn = _arrayToReturnToPool;
+            this = default; // for safety, to avoid using pooled array if this instance is erroneously appended to again
+            if (toReturn != null)
+            {
+                ArrayPool.Shared.Return(toReturn);
+            }
+        }
+    }
+}
diff --git a/test/unit/net/RTCP/RTCPHeaderUnitTest.cs b/test/unit/net/RTCP/RTCPHeaderUnitTest.cs
index 023dc76efc..b7d98a1a65 100644
--- a/test/unit/net/RTCP/RTCPHeaderUnitTest.cs
+++ b/test/unit/net/RTCP/RTCPHeaderUnitTest.cs
@@ -13,6 +13,7 @@
 // BSD 3-Clause "New" or "Revised" License, see included LICENSE.md file.
 //-----------------------------------------------------------------------------
 
+using System;
 using Microsoft.Extensions.Logging;
 using SIPSorcery.Sys;
 using Xunit;
@@ -62,7 +63,7 @@ public void RTCPHeaderRoundTripTest()
             logger.LogDebug("PacketType: {SrcPacketType}, {DstPacketType}", src.PacketType, dst.PacketType);
             logger.LogDebug("Length: {SrcLength}, {DstLength}", src.Length, dst.Length);
 
-            logger.LogDebug("Raw Header: {RawHeader}", headerBuffer.HexStr(headerBuffer.Length));
+            logger.LogDebug("Raw Header: {RawHeader}", headerBuffer.AsSpan(0, headerBuffer.Length).HexStr());
 
             Assert.True(src.Version == dst.Version, "Version was mismatched.");
             Assert.True(src.PaddingFlag == dst.PaddingFlag, "PaddingFlag was mismatched.");