StringBuilder.ToString(int startIndex, int length) builds the wrong string

In general, the whole post is in the title.
Test code:

TestStringBuilder
static class TestStringBuilder
{
    private static string[] _initStrings =
    {
        "*CLS",
        "*ESE[ ?] <numeric>",
        "*ESR?",
        "*IDN?",
        "*OPC[?]",
        "*RST",
        "*SRE[?] <numeric>",
        "*STB?",
        "*TST?",
        "*WAI",
        ":FORMat:SREGister[?] ASCii|BINary|HEXadecimal|OCTal",
        ":SYSTem:ERRor[:NEXT]?",
        ":SYSTem:ERRor:ALL?",
        ":SYSTem:ERRor:COUNt?",
        ":SYSTem:ERRor:CODE[:NEXT]?",
        ":SYSTem:ERRor:CODE:ALL?",
        ":SYSTem:PROTect{1, 2}[:STATe][?] <boolean>,<string>",
        ":SYSTem:VERSion?",
        ":SYSTem:HELP:HEADers?",
        ":SYSTem:HELP:SYNTax? <string>",
        ":SYSTem:HELP:SYNTax:ALL?",
        ":STATus:OPERation[:EVENt]?",
        ":STATus:OPERation:CONDition?",
        ":STATus:OPERation:NTRansition[?] <numeric>",
        ":STATus:OPERation:PTRansition[?] <numeric>",
        ":STATus:OPERation:ENABle[?] <numeric>",
        ":STATus:QUEStionable[:EVENt]?",
        ":STATus:QUEStionable:CONDition?",
        ":STATus:QUEStionable:NTRansition[?] <numeric>",
        ":STATus:QUEStionable:PTRansition[?] <numeric>",
        ":STATus:QUEStionable:ENABle[?] <numeric>",
        ":STATus:PRESet"
    };


    private static readonly StringBuilder _stringBuilder;

    static TestStringBuilder()
    {
        _stringBuilder = new StringBuilder();
        foreach (var s in _initStrings)
        {
            _stringBuilder.Append('\n');
            _stringBuilder.Append(s);
        }
    }

    public static void Test()
    {
        string plainCorrectString = _stringBuilder.ToString();
        string plainWrongString   = _stringBuilder.ToString(0, _stringBuilder.Length);

        PrintText(plainCorrectString.Length, plainCorrectString.Substring); // return correct text
        PrintText(_stringBuilder.Length,     _stringBuilder.ToString);      // return wrong text
        PrintText(plainWrongString.Length,   plainWrongString.Substring);   // return wrong text
    }

    private const int SEND_LIMIT = 128;

    private delegate string ToSubstringHandler(int startIndex, int length);

    private static void PrintText(int totalLength, ToSubstringHandler toSubstring)
    {
        int lengthSend;
        int offset = 0;
        while (offset < totalLength)
        {
            lengthSend = System.Math.Min(totalLength - offset, SEND_LIMIT);
            Debug.Write(toSubstring(offset, lengthSend));
            offset += lengthSend;
        }
    }
}

Results:

plainCorrectString

*CLS
*ESE[ ?]
*ESR?
*IDN?
*OPC[?]
*RST
*SRE[?]
*STB?
*TST?
*WAI
:FORMat:SREGister[?] ASCii|BINary|HEXadecimal|OCTal
:SYSTem:ERRor[:NEXT]?
:SYSTem:ERRor:ALL?
:SYSTem:ERRor:COUNt?
:SYSTem:ERRor:CODE[:NEXT]?
:SYSTem:ERRor:CODE:ALL?
:SYSTem:PROTect{1, 2}[:STATe][?] ,
:SYSTem:VERSion?
:SYSTem:HELP:HEADers?
:SYSTem:HELP:SYNTax?
:SYSTem:HELP:SYNTax:ALL?
:STATus:OPERation[:EVENt]?
:STATus:OPERation:CONDition?
:STATus:OPERation:NTRansition[?]
:STATus:OPERation:PTRansition[?]
:STATus:OPERation:ENABle[?]
:STATus:QUEStionable[:EVENt]?
:STATus:QUEStionable:CONDition?
:STATus:QUEStionable:NTRansition[?]
:STATus:QUEStionable:PTRansition[?]
:STATus:QUEStionable:ENABle[?]
:STATus:PRESet

_stringBuilder

*CLS
*ESE[ ?] <T
*SRE[?] <numerGister[?] ASCii|BINary|HEXadecimal|OCTal
:SYSTem:ERRor[:NEXT]?
:SYSTem:ERRor:ALL?
:SYSTem:ERRor:COUNt?
:SYSTem:ERRor:CODE[:NEXT]?
:SYSTem:ERRor:CODE:ALL?
:SYSTem:PROTect{1, 2}[:STATe][?] ,
:SYSTem:VERSion?
:SYSTem:HELP:HEADers?
:SYSTem:HELP:SYNTax?
:SYSTem:HELP:SYNTax:ALL?
:STATus:OPERation[:EVENt]?
:STATus:OPERation:CONDition?
:STATus:OPERation:NTRansition[?]
:STATus:OPERation:PTRansition[?]
:STATus:OPERation:ENABle[?]
:STATus:QUEStionable[:EVENt]?
:STATus:QUEStionable:CONDition?
:STATus:QUEStionable:NTRansition[?]
:STATus:QUEStionable:PTRansition[?]
:STATus:QUEStionable:ENABle[?]
:STATus:PRESet

plainWrongString

*CLS
*ESE[ ?] <T
*SRE[?] <numerGister[?] ASCii|BINary|HEXadecimCOUNt?
:SYSTem:ERRor:CODE[:NEXT]?
:SYSTem:ERRor:CODE:ALL?
:SYSTeSYNTax:ALL?
:STATus:OPERation[:EVENt]?
:STATus:OPERation:CONDition?
:STATus:OPERation:NTRansition[?]
:STATus:OPERationc>
:STATus:PRESet

in line
System.Array.Copy(chunkChars, index, result, 0, charCount);
when constructing a line, the offset in the resulting array is not taken into account when passing chunks. It is always 0.

Nice catch! You should try to file an issue for this on their GitHub space, and hope that they will look into it. Maybe they will cleanup the code at the same time…

…or submit a PR - even more likely to get included, plus the added benefit of giving you an immediate fix by building your own fixed version of mscorlib

2 Likes

Open PR #1239. Corrections are minimal.

1 Like