Calculating length of a Span of bytes when encoding text [Answered]

BBecquerel9/7/2022
I need a method that takes text, encodes it and converts it to base 64 without allocations (if possible).

This is what I have so far:

    private Span<char> ToBase64(Span<char> text)
    {
        // _encoding is UTF-8
        
        // get how many bytes the text will take up when encoded
        var byteCount = _encoding.GetByteCount(text);
        
        // alloc array with that count so we can get a Span<byte>
        Span<byte> span = stackalloc byte[byteCount];
        
        // write the encoded text into the byte span
        _ = _encoding.GetBytes(text, span);
        
        // use the encoded byte span to actually go to base64
        // _encoder is a nuget package that asks for an ArraySegment<char>
        return _encoder.ToBaseNonAlloc(span.ToArray());
    }


I'm using this to encode/decode JSON. I have a unit test for roundtripping (encode/decode the same object and check it's equivalent) this that is failing.

    private class MyObject
    {
        public string Name { get; set; }
        public int Age { get; set; }
        public bool Alive { get; set; }
    }
    
    [Fact]
    public void Encode_ShouldRoundTrip()
    {
        var obj = new MyObject
        {
            Name = "John Doe",
            Age = 32,
            Alive = true
        };

        var json = JsonConvert.SerializeObject(obj);

        // _encoder here is my own class where the above method lives
        var encoded = _encoder.Encode(json);
        var unencoded = _encoder.Decode(encoded);

        var newObj = JsonConvert.DeserializeObject<MyObject>(unencoded);

        newObj.Should().BeEquivalentTo(obj);
    }


It decodes the actual JSON correctly, but then continues onwards, filling up the string with garbage data that causes deserialization to explode.

{"Name":"John Doe","Age":32,"Alive":truep4��bY,P�&��4]�!�V��!�]D��aQ<@0XbUh2VaplSHNXUThGbhhUTwQQP==
BBecquerel9/7/2022
My assumption is that I'm messing up how big the Span<byte> should be and that's causing it to include garbage data, but I don't see what I'm doing wrong.
BBecquerel9/7/2022
I am doing other stuff besides just encoding the JSON that I can explain if all of that looks OK, but wanted to check this first since I've not messed with spans before.
MMKP9/7/2022
check the Span<byte>'s [length] to make sure its null terminated
BBecquerel9/7/2022
doesn't appear so
Image
BBecquerel9/7/2022
should I set its length to byteCount + 1 and ensure the last element is null?
MMKP9/7/2022
yeah
BBecquerel9/7/2022
what's the correct syntax for that - span[^1] = null; complains that I can't assign null to byte
BBecquerel9/7/2022
or is it fine for it to be a Span<byte?>?
MMKP9/7/2022
0
BBecquerel9/7/2022
ah, got you - makes sense
BBecquerel9/7/2022
tried that and i'm still getting garbage data added in so i think i need to check the rest of my implementation
BBecquerel9/7/2022
figured it out, in my method where I parsed the base64, I was writing back into the same span<char> I was reading from 😄
MMKP9/7/2022
do /close
BBecquerel9/7/2022
mhmm
AAccord9/7/2022
✅ This post has been marked as answered!