• 骚操作之 持有 ReadOnlySpan 数据


    ReadOnlySpan 可以说现在高性能操作的重要基石

    其原理有兴趣的同学可以看 2018 的介绍Span文章

    其为了保障大家安全使用做了相应的限制

    那么有没方法绕过呢?

    在class中持有 ReadOnlySpan

    直接持有是不可能的,本身为 ref struct 就保障了大家写不出持有它的代码

    但是我们可以玩骚操作,无法持有你,我们可以创造一个一模一样的你

    如下面代码,我们获取span 对应的指针

    public unsafe class ReadOnlySpanReaderBuffer<T> 
    {
        internal void* _buffer;
        internal int _length;
    
        public ReadOnlySpanReaderBuffer(Span span)
        {
            _buffer = Unsafe.AsPointer(ref span.GetPinnableReference());
            _length = span.Length;
        }
    
        public ReadOnlySpanReaderBuffer(ReadOnlySpan span)
        {
            _buffer = Unsafe.AsPointer(ref MemoryMarshal.GetReference(span));
            _length = span.Length;
        }
    

    在需要使用的时候通过指针重新创建一个一模一样的span

    public ReadOnlySpan Readed
    {
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        get => new ReadOnlySpan(_buffer, _length);
    }
    

    将 Span 转换成 Memory

    同样出于安全考虑,默认Span 无法转换成 Memory

    但是我们可以玩骚操作,无法转换你,我们可以创造一个

    首先我们需要建立一个Memory的基础类, 通过它来告诉 Memory 如何拿去我们从 Span里面偷出来的指针

    public sealed unsafe class UnmanagedMemoryManager<T> : MemoryManager<T>
    {
        private readonly T* _pointer;
        private readonly int _length;
    
        public UnmanagedMemoryManager(Span span)
        {
            fixed (T* ptr = &MemoryMarshal.GetReference(span))
            {
                _pointer = ptr;
                _length = span.Length;
            }
        }
    
        public UnmanagedMemoryManager(T* pointer, int length)
        {
            if (length < 0) throw new ArgumentOutOfRangeException(nameof(length));
            _pointer = pointer;
            _length = length;
        }
    
        public UnmanagedMemoryManager(nint pointer, int length) : this((T*)pointer.ToPointer(), length) { }
    
        public override Span GetSpan() => new Span(_pointer, _length);
    
        // 一切的关键就在这个方法
        public override MemoryHandle Pin(int elementIndex = 0)
        {
            if (elementIndex < 0 || elementIndex >= _length)
                throw new ArgumentOutOfRangeException(nameof(elementIndex));
            return new MemoryHandle(_pointer + elementIndex);
        }
    
        public override void Unpin() { }
    
        protected override void Dispose(bool disposing) { }
    }
    
    

    在需要使用的时候通过指针重新创建一个一模一样的Memory

    public ReadOnlyMemory ReadedMemory
    {
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        get => new UnmanagedMemoryManager((IntPtr)_buffer, _length).Memory;
    }
    

    道路千万条,安全第一条,大家慎用骚操作啊

  • 相关阅读:
    解析网页数据并且处理网页正则表达式与re模块
    c语言实现通讯录
    CP AUTOSAR标准之COM(AUTOSAR_CP_SWS_COM)(更新中……)
    Internet协议栈 TCP/IP模型 物理层、链路层、网络层、传输层、应用层的作用
    Matplotlib绘图添加标题,图例,网格,参考线,注释文本,数学表达式,标签,修改刻度
    猿创征文| 怎么提高 Java 开发效率?使用 Idea 和它的插件
    GenICam标准(三)
    多线程(1)
    javascript基础七:说说你对Javascript中作用域的理解?
    FT2000+ arm64服务器 kylin server 虚拟机 编译fuse_dfs 解决挂载目录中的故障
  • 原文地址:https://www.cnblogs.com/fs7744/p/18043193