dotnet / runtime

.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.
https://docs.microsoft.com/dotnet/core/
MIT License
14.58k stars 4.55k forks source link

ReadOnlySequence<byte>.PositionOf Error #102457

Open beetlex-io opened 1 month ago

beetlex-io commented 1 month ago

ReadOnlySequence.PositionOf fun return index larger than array length? Test code:

using System;
using System.Buffers;

using System.Net.Http.Json;
using System.Net.Sockets;
using System.Reflection;
using System.Reflection.Emit;
using System.Text;
using System.Text.Json.Serialization;

MemoryStream stream = new MemoryStream();
string name = "name:henryfan\r\n";
string email = "email:henryfan@msn.com\r\n";
string result;
stream.Write(name, Encoding.UTF8);
stream.Write(email, Encoding.UTF8);
ReadOnlySequence<byte> sequence = new ReadOnlySequence<byte>(stream.ToArray());
var point = sequence.IndexOf("\r\n");
result = point.Value.ReadString(Encoding.UTF8);
sequence = sequence.Slice(point.Value.Length);
point = sequence.IndexOf("\r\n");
result = point.Value.ReadString(Encoding.UTF8);

public struct TemporaryBuffer<T> : IDisposable
{

    public T[] Data { get; set; }

    public void Dispose()
    {
        ArrayPool<T>.Shared.Return(Data);
    }

    public static implicit operator TemporaryBuffer<T>(int length)
    {
        TemporaryBuffer<T> result = new TemporaryBuffer<T>();
        result.Data = ArrayPool<T>.Shared.Rent(length);
        return result;
    }
}
static class extend
{
    public static ReadOnlySequence<byte>? IndexOf(this ReadOnlySequence<byte> _buffer, string eof, Encoding coding = null)
    {
        coding = coding ?? Encoding.UTF8;
        var bytes = coding.GetBytes(eof);
        return IndexOf(_buffer, bytes);
    }
    public static string ReadString(this ReadOnlySequence<byte> _buffer, Encoding coding)
    {
        return coding.GetString(_buffer);
    }
    public static int Write(this Stream _buffer, string value, Encoding encoding)
    {
        if (string.IsNullOrEmpty(value))
            return 0;
        int result = 0;
        using (TemporaryBuffer<byte> bytes = value.Length * 6)
        {
            result = encoding.GetBytes(value, 0, value.Length, bytes.Data, 0);
            _buffer.Write(bytes.Data, 0, result);
            return result;
        }
    }
    public static ReadOnlySequence<byte>? IndexOf(this ReadOnlySequence<byte> _buffer, byte[] eof)
    {
        var point = _buffer.PositionOf(eof[^1]);
        if (point != null)
        {
            Console.WriteLine($"_buffersize:{_buffer.Length}\t {eof[^1]} index:{point.Value.GetInteger()} offsetInex:{_buffer.GetOffset(point.Value)}");
            if (eof.Length == 1)
                return _buffer.Slice(0, point.Value);
            else
            {
                var index = _buffer.GetOffset(point.Value);
                if (index >= eof.Length)
                {
                    var eofbuffer = _buffer.Slice(index - eof.Length + 1, eof.Length);
                    var data = eofbuffer.ToArray();
                    for (int i = 0; i < eof.Length; i++)
                    {
                        if (data[i] != eof[i])
                            return null;
                    }
                    return _buffer.Slice(0, index + 1);
                }
            }
        }
        return null;
    }
}

Debug info: image

dotnet-issue-labeler[bot] commented 1 month ago

I couldn't figure out the best area label to add to this issue. If you have write-permissions please help me learn by adding exactly one area label.

dotnet-policy-service[bot] commented 1 month ago

Tagging subscribers to this area: @dotnet/area-system-buffers See info in area-owners.md if you want to be subscribed.

lilinus commented 1 month ago

Could this be a duplicate of #75866?

beetlex-io commented 1 month ago

Maybe, use ReadOnlySequence.GetOffset(point) value does not change after the ReadOnlySequence.Slice method is called.

string name = "name:henryfan\r";
ReadOnlySequence<byte> sequence = new ReadOnlySequence<byte>(Encoding.UTF8.GetBytes(name));
var point = sequence.PositionOf((byte)'\r');
var index = sequence.GetOffset(point.Value);//return 13 right
sequence = sequence.Slice(2);
point = sequence.PositionOf((byte)'\r');
index = sequence.GetOffset(point.Value);//return 13 wrong