Skip to content

Commit

Permalink
Reduce heap allocations when parsing a host
Browse files Browse the repository at this point in the history
  • Loading branch information
Dubzer committed Sep 26, 2024
1 parent 0ca945f commit 73fed69
Show file tree
Hide file tree
Showing 3 changed files with 48 additions and 1 deletion.
2 changes: 1 addition & 1 deletion src/Dubzer.WhatwgUrl/HostParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ public static Result<string> Parse(string input, bool isOpaque)
// only when serializing the host.
var ipv6Result = Ipv6Parser.Parse(input[1..^1]);
return ipv6Result
? Result<string>.Success($"[{ipv6Result.Value}]")
? Result<string>.Success(ipv6Result.Value!)
: ipv6Result;
}

Expand Down
2 changes: 2 additions & 0 deletions src/Dubzer.WhatwgUrl/Ipv6Parser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ internal static Result<string> Parse(string input)
private static string SerializeIpv6(ReadOnlySpan<ushort> address)
{
var sb = new StringBuilder();
sb.Append('[');
// 2. Let compress be an index
// to the first IPv6 piece in the first longest sequences of address’s IPv6 pieces that are 0.
// 3. If there is no sequence of address’s IPv6 pieces that are 0 that is longer than 1,
Expand Down Expand Up @@ -202,6 +203,7 @@ private static string SerializeIpv6(ReadOnlySpan<ushort> address)
sb.Append(':');
}

sb.Append(']');
return sb.ToString();
}

Expand Down
45 changes: 45 additions & 0 deletions src/Dubzer.WhatwgUrl/Uts46/Idna.cs
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,51 @@ private static MappingTableRow FindMapping(uint val)

private static string Map(string input)
{
// 13 is the max mapping length. Presumably that's the worst case scenario
if (input.Length < Consts.MaxLengthOnStack.Char / 13)
{
Span<char> chars = stackalloc char[Consts.MaxLengthOnStack.Char];

var nextCharI = 0;
foreach (var rune in input.EnumerateRunes())
{
var mapping = FindMapping((uint)rune.Value);
switch (mapping.Status)
{
case IdnaStatus.Deviation:
case IdnaStatus.Valid:
case IdnaStatus.DisallowedSTD3Valid:
case IdnaStatus.Disallowed:
var codepoint = rune.Value;

// Inlined Rune.IsBmp
if (codepoint <= ushort.MaxValue)
{
chars[nextCharI] = (char) codepoint;
nextCharI++;
}
else
{
// Inlined Rune.EncodeToUtf16 => UnicodeUtility.GetUtf16SurrogatesFromSupplementaryPlaneScalar
chars[nextCharI] = (char) (codepoint + 56557568U >> 10);
chars[nextCharI + 1] = (char) ((codepoint & 1023) + 56320);
nextCharI += 2;
}

break;
case IdnaStatus.DisallowedSTD3Mapped:
case IdnaStatus.Mapped:
mapping.Mapping.CopyTo(chars[nextCharI..]);
nextCharI += mapping.Mapping.Length;
break;
case IdnaStatus.Ignored:
break;
}
}

return new string(chars[..nextCharI]);
}

var result = new StringBuilder(input.Length);
foreach (var rune in input.EnumerateRunes())
{
Expand Down

0 comments on commit 73fed69

Please sign in to comment.