Skip to content

Instantly share code, notes, and snippets.

@nietras
Created July 12, 2023 12:06
Show Gist options
  • Save nietras/08c1ecef7477e6084bc608fd19000051 to your computer and use it in GitHub Desktop.
Save nietras/08c1ecef7477e6084bc608fd19000051 to your computer and use it in GitHub Desktop.
David Fowler RemoveInstanceIdFromQuery C# Optimization Challenge
using System;
using System.Runtime.InteropServices;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace RemoveInstanceIdFromQueryStringTest;
[TestClass]
public class RemoveInstanceIdFromQueryStringTest
{
// David Fowler tweet https://twitter.com/davidfowl/status/1678738294933159937
// Cases to consider:
// A. `query` is null/Empty => null
// B. `instanceId=` is not found => `query`
// C. `instanceId=<XX>` is `query` => null (assume return null since query empty returns null)
// D. `instanceId=` is found at start => copy of `query` after `instanceId=<XX>&`
// E. `instanceId=<XX>` is found at end => copy of `query` before `instanceId=<XX>`
// F. `instanceId=` is found at middle => copy of `query` before + after `instanceId=<XX>&`
// G. Ignore partial `instanceId=` match and repeat
// Code assumes `&` followed by something always.
[TestMethod]
public void Test()
{
// A.
Verify(null, null!);
Verify(null, string.Empty);
// B.
Verify("a");
Verify("k1=v1&k2=v2");
Verify("k1=v1&XinstanceId=v2");
Verify("k1=v1&instanceIdX=v2");
// C.
Verify(null, "instanceId=V");
// D.
Verify("k=v", "instanceId=V&k=v");
// E.
Verify("k=v", "k=v&instanceId=V");
// F.
Verify("k1=v1&k2=v2", "k1=v1&instanceId=V&k2=v2");
// G.
Verify("k1=v1&instanceIdX=v2", "instanceId=V&k1=v1&instanceIdX=v2");
Verify("k1=v1&instanceIdX=v2", "k1=v1&instanceId=V&instanceIdX=v2");
Verify("k1=v1&instanceIdX=v2", "k1=v1&instanceIdX=v2&instanceId=V");
}
static void Verify(string query) => Verify(query, query);
static void Verify(string? expected, string query) =>
Assert.AreEqual(expected, RemoveInstanceIdFromQueryString(query));
static string? RemoveInstanceIdFromQueryString(string query)
{
const string key = "instanceId=";
const char separator = '&';
// A. `query` is null/Empty => null
if (string.IsNullOrEmpty(query)) { return null; }
var end = 0;
do
{
var start = query.IndexOf(key, end, StringComparison.OrdinalIgnoreCase);
// B. `instanceId=` is not found => `query`
if (start < 0) { return query; }
var newEnd = start + key.Length;
end = query.IndexOf(separator, newEnd);
if (start == 0)
{
// C. `instanceId=<XX>` is `query` => null (assume return null since query empty returns null)
if (end < 0) { return null; }
// D. `instanceId=` is found at start => copy of `query` after `instanceId=<XX>&`
return query.Substring(end + 1);
}
// The key should be preceded by & if not at start
var separatorIndex = start - 1;
var isKey = query[separatorIndex] == '&';
if (isKey)
{
// E. `instanceId=<XX>` is found at end => copy of `query` before `instanceId=<XX>`
if (end < 0) { return query.Substring(0, separatorIndex); }
// F. `instanceId=` is found at middle => copy of `query` before + after `instanceId=<XX>&`
return CopyBeforeAfterToNewString(query, start, end);
}
// G. Ignore partial `instanceId=` match and repeat
end = newEnd;
}
while (true);
static string CopyBeforeAfterToNewString(ReadOnlySpan<char> query, int start, int end)
{
var queryBefore = query.Slice(0, start);
var queryAfter = query.Slice(end + 1);
// Create a zero initialized string (avoid filling)
var result = new string('\0', queryBefore.Length + queryAfter.Length);
// Jump through hoops to copy to that string
var resultSpan = MemoryMarshal.CreateSpan(
ref MemoryMarshal.GetReference(result.AsSpan()), result.Length);
queryBefore.CopyTo(resultSpan.Slice(0, queryBefore.Length));
queryAfter.CopyTo(resultSpan.Slice(queryBefore.Length));
return result;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment