Can anyone skilled in t-sql create companion table-valued functions to these C# array pack & unpack utility methods that escape delimiters and preserve nulls, null arrays, empty arrays through roundtrip encoding?
By packing any variety of string arrays into a string, you can pass such (small) arrays to SQL as parameters, which is useful in some scenarios.
static class ArrayUtil
{
    public static string Pack(string[] original)
    {
        return Pack(original, '|', '0', '~');
    }
    public static string[] Unpack(string original)
    {
        return Unpack(original, '|', '0', '~');
    }
    public static string Pack(string[] original, char delimiter, char zed, char escape)
    {
        if (delimiter == escape ||
            zed == escape ||
            delimiter == zed) throw new ArgumentException("special characters must be distinct");
        // Null array returns a null string
        if (original == null) return null;
        // Empty array returns an empty string
        if (original.Length == 0) return string.Empty;
        // Arrays with a single empty element are represented as just the escape character
        // to differentiate from an empty array
        if (original.Length == 1 && original[0] == string.Empty) return escape.ToString();
        // Otherwise
        StringBuilder sb = new StringBuilder();
        for (int i = 0, ol = original.Length; i < ol; i++)
        {
            string s = original[i];
            if (s == null)
            {
                sb.Append(zed); // zed == null
            }
            else
            {
                for (int j = 0, sl = s.Length; j < sl; j++)
                {
                    char c = s[j];
                    // escape literal delimiters, escapes, and leading zeds
                    if (c == delimiter ||
                        c == escape ||
                        (c == zed && j == 0)) sb.Append(escape);
                    sb.Append(c);
                }
            }
            if (i != ol - 1) sb.Append(delimiter); // no trailing delimiter
        }
        return sb.ToString();
    }
    public static string[] Unpack(string original, char delimiter, char zed, char escape)
    {
        if (delimiter == escape ||
            zed == escape ||
            delimiter == zed) throw new ArgumentException("special characters must be distinct");
        // Null string returns a null array
        if (original == null) return null;
        // Empty string returns an empty array
        if (original == string.Empty) return new string[] { };
        // A single escape character represents an array with a single empty element
        // to differentiate from an empty array
        if (original == escape.ToString()) return new string[] { string.Empty };
        // Otherwise
        StringBuilder sb = new StringBuilder(); // A place to store the current element
        StringReader sr = new StringReader(original); // A stream of the original string
        List<string> unpacked = new List<string>(); // The finished elements
        int next;
        while ((next = sr.Read()) >= 0)
        {
            char c = (char)next;
            if (c == zed && sb.Length == 0)
            {
                unpacked.Add(null);
                if ((next = sr.Peek()) >= 0 && (char)next != delimiter)
                    throw new ArgumentException("An element's leading zed character must be escaped or must alone be the element", "original");
                sb = null;
            }
            else if (c == delimiter)
            {
                if (sb != null) unpacked.Add(sb.ToString());
                sb = new StringBuilder();
            }
            else if (c == escape)
            {
                if ((next = sr.Read()) >= 0)
                {
                    sb.Append((char)next);
                }
                else
                    throw new ArgumentException("Escapee expected", "original");
            }
            else
            {
                sb.Append(c);
            }
        }
        // A final zed character will make sb = null, but otherwise we have an additional element
        if (sb != null) unpacked.Add(sb.ToString());
        return unpacked.ToArray();
    }
}
Example packings with Pack(original, '|', '0', '~'), basic case:
["hello", "world"] -> "hello|world"
Some special cases (as discovered by PEX)
null -> null
[null] -> "0"
[null, null] -> "0|0"
[] -> ""
[""] -> "~"
["", ""] -> "|"
["|", "|"] -> "~||~|"
 
    