Skip to content

Commit ed12222

Browse files
vbilopavclaude
andcommitted
Pre-compute UTF8 bytes for JSON structure chars in rendering hot paths
Replace per-request Encoding.UTF8.GetBytes() calls with pre-computed static byte arrays for {, }, [, ], comma, colon, and null. Eliminates 17 encoding calls per request across both rendering paths. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 42b91cf commit ed12222

2 files changed

Lines changed: 24 additions & 14 deletions

File tree

NpgsqlRest/Consts.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,14 @@ public static class Consts
3333
public const string EmptyArray = "[]";
3434
public const string EmptyObj = "{}";
3535
public const string SetContext = "select set_config($1,$2,false)";
36+
37+
// Pre-computed UTF8 bytes for JSON structure characters used in rendering hot paths.
38+
// Eliminates per-request Encoding.UTF8.GetBytes() calls for constant values.
39+
public static readonly byte[] Utf8OpenBrace = [(byte)'{'];
40+
public static readonly byte[] Utf8CloseBrace = [(byte)'}'];
41+
public static readonly byte[] Utf8OpenBracket = [(byte)'['];
42+
public static readonly byte[] Utf8CloseBracket = [(byte)']'];
43+
public static readonly byte[] Utf8Comma = [(byte)','];
44+
public static readonly byte[] Utf8Colon = [(byte)':'];
45+
public static readonly byte[] Utf8Null = "null"u8.ToArray();
3646
}

NpgsqlRest/NpgsqlRestEndpoint.cs

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1922,7 +1922,7 @@ await LoginHandler.HandleAsync(
19221922
context.Response.ContentType = Application.Json;
19231923

19241924
// Write opening {
1925-
writer.Advance(Encoding.UTF8.GetBytes("{".AsSpan(), writer.GetSpan(1)));
1925+
Consts.Utf8OpenBrace.CopyTo(writer.GetSpan(1)); writer.Advance(1);
19261926

19271927
bool firstResultWritten = false;
19281928
for (int cmdIndex = 0; cmdIndex < routine.MultiCommandInfo.Length; cmdIndex++)
@@ -1956,7 +1956,7 @@ await LoginHandler.HandleAsync(
19561956
// Write comma between results
19571957
if (firstResultWritten)
19581958
{
1959-
writer.Advance(Encoding.UTF8.GetBytes(",".AsSpan(), writer.GetSpan(1)));
1959+
Consts.Utf8Comma.CopyTo(writer.GetSpan(1)); writer.Advance(1);
19601960
}
19611961
firstResultWritten = true;
19621962

@@ -1980,7 +1980,7 @@ await LoginHandler.HandleAsync(
19801980

19811981
if (!currentCmd.IsSingle)
19821982
{
1983-
writer.Advance(Encoding.UTF8.GetBytes("[".AsSpan(), writer.GetSpan(1)));
1983+
Consts.Utf8OpenBracket.CopyTo(writer.GetSpan(1)); writer.Advance(1);
19841984
}
19851985
bool mcFirstRow = true;
19861986
var mcRowBuilder = StringBuilderPool.Rent(512);
@@ -2206,7 +2206,7 @@ mcDescriptor.CompositeFieldNames is not null &&
22062206
if (currentCmd.IsSingle && mcRowCount == 0)
22072207
{
22082208
// Empty result with @single — write null
2209-
writer.Advance(Encoding.UTF8.GetBytes(Consts.Null.AsSpan(), writer.GetSpan(Encoding.UTF8.GetMaxByteCount(Consts.Null.Length))));
2209+
Consts.Utf8Null.CopyTo(writer.GetSpan(4)); writer.Advance(4);
22102210
}
22112211

22122212
if (mcRowBuilder.Length > 0)
@@ -2218,13 +2218,13 @@ mcDescriptor.CompositeFieldNames is not null &&
22182218

22192219
if (!currentCmd.IsSingle)
22202220
{
2221-
writer.Advance(Encoding.UTF8.GetBytes("]".AsSpan(), writer.GetSpan(1)));
2221+
Consts.Utf8CloseBracket.CopyTo(writer.GetSpan(1)); writer.Advance(1);
22222222
}
22232223
}
22242224
}
22252225

22262226
// Write closing }
2227-
writer.Advance(Encoding.UTF8.GetBytes("}".AsSpan(), writer.GetSpan(1)));
2227+
Consts.Utf8CloseBrace.CopyTo(writer.GetSpan(1)); writer.Advance(1);
22282228
await writer.FlushAsync(cancellationToken);
22292229
return;
22302230
}
@@ -2370,7 +2370,7 @@ Options.AuthenticationOptions.DefaultDataProtector is not null &&
23702370
{
23712371
if (endpoint.TextResponseNullHandling == TextResponseNullHandling.NullLiteral)
23722372
{
2373-
writer.Advance(Encoding.UTF8.GetBytes(Consts.Null.AsSpan(), writer.GetSpan(Encoding.UTF8.GetMaxByteCount(Consts.Null.Length))));
2373+
Consts.Utf8Null.CopyTo(writer.GetSpan(4)); writer.Advance(4);
23742374
}
23752375
else if (endpoint.TextResponseNullHandling == TextResponseNullHandling.NoContent)
23762376
{
@@ -2523,7 +2523,7 @@ Options.AuthenticationOptions.DefaultDataProtector is not null &&
25232523
bool multiCmdWriteWrapper = multiCmd is not null && binary is false && endpoint.Raw is false;
25242524
if (multiCmdWriteWrapper)
25252525
{
2526-
writer.Advance(Encoding.UTF8.GetBytes("{".AsSpan(), writer.GetSpan(1)));
2526+
Consts.Utf8OpenBrace.CopyTo(writer.GetSpan(1)); writer.Advance(1);
25272527
}
25282528

25292529
// Begin result set loop (single pass for normal, do/while for multi-command)
@@ -2543,7 +2543,7 @@ Options.AuthenticationOptions.DefaultDataProtector is not null &&
25432543
// Write command name key in JSON wrapper
25442544
if (multiCmdFirstWritten)
25452545
{
2546-
writer.Advance(Encoding.UTF8.GetBytes(",".AsSpan(), writer.GetSpan(1)));
2546+
Consts.Utf8Comma.CopyTo(writer.GetSpan(1)); writer.Advance(1);
25472547
}
25482548
var nameJson = string.Concat(cmdInfo.JsonName, ":");
25492549
writer.Advance(Encoding.UTF8.GetBytes(nameJson.AsSpan(), writer.GetSpan(Encoding.UTF8.GetMaxByteCount(nameJson.Length))));
@@ -2580,7 +2580,7 @@ Options.AuthenticationOptions.DefaultDataProtector is not null &&
25802580

25812581
if (routine.ReturnsSet && endpoint.Raw is false && binary is false && isSingleRecord is false)
25822582
{
2583-
writer.Advance(Encoding.UTF8.GetBytes(Consts.OpenBracket.ToString().AsSpan(), writer.GetSpan(Encoding.UTF8.GetMaxByteCount(1))));
2583+
Consts.Utf8OpenBracket.CopyTo(writer.GetSpan(1)); writer.Advance(1);
25842584
if (shouldCache)
25852585
{
25862586
cacheBuffer!.Append(Consts.OpenBracket);
@@ -2931,11 +2931,11 @@ descriptor.CompositeFieldNames is not null &&
29312931
if (multiCmd is not null)
29322932
{
29332933
// Multi-command: always write null for empty @single results
2934-
writer.Advance(Encoding.UTF8.GetBytes(Consts.Null.AsSpan(), writer.GetSpan(Encoding.UTF8.GetMaxByteCount(Consts.Null.Length))));
2934+
Consts.Utf8Null.CopyTo(writer.GetSpan(4)); writer.Advance(4);
29352935
}
29362936
else if (endpoint.TextResponseNullHandling == TextResponseNullHandling.NullLiteral)
29372937
{
2938-
writer.Advance(Encoding.UTF8.GetBytes(Consts.Null.AsSpan(), writer.GetSpan(Encoding.UTF8.GetMaxByteCount(Consts.Null.Length))));
2938+
Consts.Utf8Null.CopyTo(writer.GetSpan(4)); writer.Advance(4);
29392939
await writer.FlushAsync(cancellationToken);
29402940
}
29412941
else if (endpoint.TextResponseNullHandling == TextResponseNullHandling.NoContent)
@@ -2962,7 +2962,7 @@ descriptor.CompositeFieldNames is not null &&
29622962
}
29632963
if (routine.ReturnsSet && endpoint.Raw is false && isSingleRecord is false)
29642964
{
2965-
writer.Advance(Encoding.UTF8.GetBytes(Consts.CloseBracket.ToString().AsSpan(), writer.GetSpan(Encoding.UTF8.GetMaxByteCount(1))));
2965+
Consts.Utf8CloseBracket.CopyTo(writer.GetSpan(1)); writer.Advance(1);
29662966
if (shouldCache)
29672967
{
29682968
cacheBuffer!.Append(Consts.CloseBracket);
@@ -2982,7 +2982,7 @@ descriptor.CompositeFieldNames is not null &&
29822982
// Close multi-command JSON object
29832983
if (multiCmdWriteWrapper)
29842984
{
2985-
writer.Advance(Encoding.UTF8.GetBytes("}".AsSpan(), writer.GetSpan(1)));
2985+
Consts.Utf8CloseBrace.CopyTo(writer.GetSpan(1)); writer.Advance(1);
29862986
}
29872987

29882988
return;

0 commit comments

Comments
 (0)