• 骚操作之 持有 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;
    }
    

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

  • 相关阅读:
    【请求报错:javax.net.ssl.SSLHandshakeException: No appropriate protocol】
    HCIP笔记——数据链路层协议
    STM32外部Flash移植FATFS笔记
    C++基础知识3
    java中log使用总结
    年轻人不用太过于努力
    2022年Q2全国网络零售发展指数同比增长3.3%
    Azure DevOps (十二) 通过Azure Devops部署一个SpringBoot应用
    Enzo丨Enzo 链霉亲和素阻滞剂/稀释剂方案
    axios的二次封装(拦截器)、Vuex--modules
  • 原文地址:https://www.cnblogs.com/fs7744/p/18043193