Detailed explanation of C# stream (3) - FileStream source code

【FileStream】

Constructor

If you create a FileStream, common parameters include Path, operation mode FileMode, and permission FileAccess.

Let’s talk about FileShare and SafeFileHandle.

We know that when reading files, there are usually two demands: one is how to read the file content faster; the other is how to reduce the consumption of reading files. A common way to speed up reading files is multi-threaded reading. Each thread reads a part of the file, which involves file sharing. There are the following modes:

  • None: Refuse to share, other threads (processes) will not be able to open, write, or delete the file
  • Read: Allows reading. Other threads can create a new FileStream instance to read files. Reading files between threads does not affect it. After one thread's FileStream is released, it does not affect other threads reading files.
  • Write: Allows writing, and other threads write in parallel. It should be noted that different threads must write at different stream positions and stream intervals, and do not overlap, otherwise the overlapping parts will be serial.
  • ReadWrite: Allows reading and writing. This method is not commonly used. The most important thing to ensure in parallel is that different threads read and write different intervals of the file without overlap.
  • Delete: Allow deletion
  • Ineritable: Allow file handles to be inherited by child processes

SafeFileHandle is rarely used, and is generally used in multi-language interactions. For example, if you open a file in C++, you need to pass the reference to C#, and C# will read the file, or C# will open the file and pass it to C++ to read the file. . This situation rarely occurs. If you really need to transfer file data in C++ and C#, you will usually pass the file path, open and read the file at one end, or open and read the file at one end and pass the buffer containing the data to the other end. One end. If you want to use it, the example is as follows:

    [DllImport("kernel32.dll", SetLastError = true, CharSet=CharSet.Unicode)]
    static extern SafeFileHandle CreateFile(string lpFileName, uint dwDesiredAccess,
      uint dwShareMode, IntPtr lpSecurityAttributes, uint dwCreationDisposition,
      uint dwFlagsAndAttributes, IntPtr hTemplateFile);

public void ReadFile()
{
SafeFileHandle fileHandle = CreateFile(
"example.txt",
GENERIC_READ,
FILE_SHARE_READ,
IntPtr.Zero,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
IntPtr.Zero
);

byte[] buffer = new byte[1024];
using (FileStream fileStream = new FileStream(fileHandle, FileAccess.Read))
{
int bytesRead = fileStream.Read(buffer, 0, buffer.Length);
//FileStream.SafeFileHandle.DangerousGetHandle() 获取文件句柄
}
}

method

There is nothing much to say about Read, Write, Dispose and Close, they are very commonly used. Here's a look at other methods that are less commonly used.

  • Flush: Reading and writing files is a very complicated process. When we call the write method to try to write data to the disk, even if we call the synchronization method, it is not written to the disk immediately, but first written to the buffer of the FileStream. . The default size of FileStream buffer is 4kb, we can specify the buffer size when instantiating. When the Write method is called, the data will be written to the buffer first. If the buffer is full, the data will be written to the operating system's buffer. (The buffer will allocate memory when writing. If the count written for the first time is greater than buffersize, it will be written directly into the operating system buffer. Otherwise, even if count>buffersize, the buffer will be filled first and then written to the operating system. This is an optimization point). Flush() is equivalent to Flush(false), which will immediately write the data in the buffer to the operating system's buffer and clear the buffer; Flush(true) will also clear the operating system's buffer and perform the writing operation to the disk.
  • Lock: Lock the file stream void Lock (long position, long length), lock a part of the file stream for exclusive access. The counterpart is UnLock.
  • Read(Span<Byte>): If you don’t know Span, you can learn about it first. Span represents a continuous piece of memory. Sometimes we want to directly operate a piece of memory. The safe way is to copy the contents of this piece of memory first, but the performance is not high. If you want high performance, you must use pointers to access, but this is unsafe. Span provides safe and efficient memory access. With this method, the read byte sequence can be directly placed into the continuous memory referenced by Span.

  • ReadExactly: The difference from Read is that after reading a certain byte sequence, the position of the stream will be advanced, that is, the value of the Position attribute will be changed.

Source code (decompiled)

For upper-level callers, FileStream provides an intermediate caching layer. For each additional middle layer, the relationship between the common attributes of the middle layer and the lower layer needs to be handled in the middle layer. This refers to the relationship between Position.

Read data

public override int Read([In][Out] byte[] array, int offset, int count)
{
    if (array == null)
    {
        throw new ArgumentNullException("array", Environment.GetResourceString("ArgumentNull_Buffer"));
    }

    if (offset < 0)
    {
        throw new ArgumentOutOfRangeException("offset", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
    }

    if (count < 0)
    {
        throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
    }

    if (array.Length - offset < count)
    {
        throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen"));
    }

    if (_handle.IsClosed)//这是SafeFileHandle
    {
        __Error.FileNotOpen();
    }

    bool flag = false;
    int num = _readLen - _readPos;//read和write共用一个buffer,readlen表示用于读数据的长度,这里求的是读数据buffer的剩余可用大小
    if (num == 0)//用于读的buffer的长度和位置相等,表示用于读的buffer用满了
    {
        if (!CanRead)
        {
            __Error.ReadNotSupported();
        }

        if (_writePos > 0)//此时需要清空写入的buffer
        {
            FlushWrite(calledFromFinalizer: false);
        }

        if (!CanSeek || count >= _bufferSize)//如果要求的count大于buffer的大小时,会直接将读出来的数据放入到指定的array中,少了copy的步骤
        {                                   //如果FilsStream只是用于读数据,可以指定小的buffersize,将读到的数据直接放入指定的array中,减少从buffer到array的拷贝
            num = ReadCore(array, offset, count);
            _readPos = 0;
            _readLen = 0;
            return num;
        }

        if (_buffer == null)//实例化buffer
        {
            _buffer = new byte[_bufferSize];
        }

        num = ReadCore(_buffer, 0, _bufferSize);//注意,如果指定的count小于buffer,那么实际是按照buffersize的大小来读取数据的
        if (num == 0)                           //这样做是为了减少IO消耗,底层在读取磁盘数据时,会一次性读取扇区里的全部内容,而不是按照上层指定的读取只读取几个字节
                                                //因此,读文件的操作不一定真的有IO消耗,如果每次读的小,会用到这里的缓存数据
        {
            return 0;
        }

        flag = (num < _bufferSize);//这种情况表示文件大小(或者文件剩余大小)小于指定的buffer大小,buffer大小默认4kb
        _readPos = 0;
        _readLen = num;
    }

    if (num > count)
    {
        num = count;
    }

    Buffer.InternalBlockCopy(_buffer, _readPos, array, offset, num);//将buffer里的数据copy到指定的array中
    _readPos += num;//读数据时流的位置会增加
    if (!_isPipe && num < count && !flag)//isPipe一般为false,这种情况表示指定的count比读数据buffer的可用数据大
    {                                  //步骤是先将buffer里的数据copy到指定的array中,然后再从文件中读剩余(count-num)个数据
        int num2 = ReadCore(array, offset + num, count - num);
        num += num2;
        _readPos = 0;//读数据的Buffer被读取完了,流的位置和大小都回置为0
        _readLen = 0;
    }

    return num;
}

data input

public override void Write(byte[] array, int offset, int count)
{
    if (array == null)
    {
        throw new ArgumentNullException("array", Environment.GetResourceString("ArgumentNull_Buffer"));
    }

    if (offset < 0)
    {
        throw new ArgumentOutOfRangeException("offset", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
    }

    if (count < 0)
    {
        throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
    }

    if (array.Length - offset < count)
    {
        throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen"));
    }

    if (_handle.IsClosed)
    {
        __Error.FileNotOpen();
    }

    if (_writePos == 0
    {
        if (!CanWrite)
        {
            __Error.WriteNotSupported();
        }

        if (_readPos < _readLen)//这种情况表示要写数据时,buffer中还有一些数据没被上层读完,需要情况,回退读的位置
        {                       //读写共用一个buffer,读时清空写的数据,写时清空读的数据
            FlushRead();
        }

        _readPos = 0;
        _readLen = 0;
    }

    if (_writePos > 0)
    {
        int num = _bufferSize - _writePos;//计算剩余可写入大小,
        if (num > 0)
        {
            if (num > count)
            {
                num = count;
            }

            Buffer.InternalBlockCopy(array, offset, _buffer, _writePos, num);//将array中的数据copy了一份到buffer中,不是立即写入,会在关闭流的时候再写入
            _writePos += num;//更新写入流的位置
            if (count == num)
            {
                return;//这种情况是剩余的写入大小大于指定的count
            }

            offset += num;
            count -= num;
        }

        if (_isAsync)
        {
            IAsyncResult asyncResult = BeginWriteCore(_buffer, 0, _writePos, null, null);
            EndWrite(asyncResult);
        }
        else
        {
            WriteCore(_buffer, 0, _writePos);//能走到这里,是因为count>num,此时buffer中数据已满,需要写入磁盘中
        }

        _writePos = 0;
    }

    if (count >= _bufferSize)//如果剩余的count或首次的count大于buffer大小,直接写入
    {                       //这里写入是先将buffer填满再写入,多余的直接写入
        WriteCore(array, offset, count);
    }
    else if (count != 0)
    {
        if (_buffer == null)
        {
            _buffer = new byte[_bufferSize];
        }

        Buffer.InternalBlockCopy(array, offset, _buffer, _writePos, count);//如果写入指定的count小于剩余的写入大小,只是将array中的数据copy了一份到buffer中,不是立即写入,会在关闭流的时候再写入
        _writePos = count;//更新写入流的位置
    }
}

Find location

public override long Position
{
    [SecuritySafeCritical]
    get
    {
        if (_handle.IsClosed)
        {
            __Error.FileNotOpen();
        }

        if (!CanSeek)
        {
            __Error.SeekNotSupported();
        }

        if (_exposedHandle)//该值一般为false,get文件句柄时会被设置为true,此时文件流的位置,需要重新确定
        {
            VerifyOSHandlePosition();
        }
        //这里获取的不是真正的文件流的位置,而是上层调用者认为的流的位置
        return _pos + (_readPos - _readLen + _writePos);
    }
    set
    {
        if (value < 0)
        {
            throw new ArgumentOutOfRangeException("value", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
        }

        if (_writePos > 0)//如果有需要写入的内容,会将内容先写入
        {
            FlushWrite(calledFromFinalizer: false);
        }

        _readPos = 0;
        _readLen = 0;
        Seek(value, SeekOrigin.Begin);//Position属性表示以流开始为起点的位置
    }
}


public override long Seek(long offset, SeekOrigin origin)
{
    if (origin < SeekOrigin.Begin || origin > SeekOrigin.End)
    {
        throw new ArgumentException(Environment.GetResourceString("Argument_InvalidSeekOrigin"));
    }

    if (_handle.IsClosed)
    {
        __Error.FileNotOpen();
    }

    if (!CanSeek)
    {
        __Error.SeekNotSupported();
    }

    if (_writePos > 0)
    {
        FlushWrite(calledFromFinalizer: false);
    }
    else if (origin == SeekOrigin.Current)
    {
        offset -= _readLen - _readPos;//如果以当前为起点,_readPos表示FilsStream的buffer的Pos,而不是文件流的真正的Position
    }                                   //offset表示调用者认为的Position,(_readLen - _readPos)表示文件流真正的Position和调用者认为的Position之间的差值

    if (_exposedHandle)
    {
        VerifyOSHandlePosition();//实际上时调用SeekCore(0L, SeekOrigin.Current),重新定位下文件流的位置
    }

    long num = _pos + (_readPos - _readLen);//计算出来的文件流的真正位置
    long num2 = SeekCore(offset, origin);//重新定位文件流的位置
    if (_appendStart != -1 && num2 < _appendStart)
    {
        SeekCore(num, SeekOrigin.Begin);
        throw new IOException(Environment.GetResourceString("IO.IO_SeekAppendOverwrite"));
    }

    if (_readLen > 0)//这表示之前已经读取了部分文件信息
    {
        if (num == num2)//一般是这种情况
        {
            if (_readPos > 0)//以readPos为分界线,把buffer中后面的数据拷贝到前面,这样readPos为0了,readLen减少了
            {
                Buffer.InternalBlockCopy(_buffer, _readPos, _buffer, 0, _readLen - _readPos);
                _readLen -= _readPos;
                _readPos = 0;
            }

            if (_readLen > 0)//恢复文件流的真正位置
            {
                SeekCore(_readLen, SeekOrigin.Current);
            }
        }
        else if (num - _readPos < num2 && num2 < num + _readLen - _readPos)//表示重新定位的文件流的位置小于计算出来的文件流的位置
        {                                                               //大于上次读取文件流时得位置
            int num3 = (int)(num2 - num);
            Buffer.InternalBlockCopy(_buffer, _readPos + num3, _buffer, 0, _readLen - (_readPos + num3));
            _readLen -= _readPos + num3;
            _readPos = 0;
            if (_readLen > 0)
            {
                SeekCore(_readLen, SeekOrigin.Current);
            }
        }
        else
        {
            _readPos = 0;
            _readLen = 0;
        }
    }

    return num2;
}
//可以看到Seek流程很复杂,为了提高性能,应该避免再读数据时比默认buffersize大,直接读到指定的array中,不要经过FileStream的buffer。
//一定要避免写数据和读数据交叉进行
//因为filsStream的设计考虑了通用,当我们按照一定的规范去使用时,可以减少很多为通用情况而做的耗费性能的设计

【MemoryStream】

Constructor

You only need to pay attention to the two constructors in the red box, the others are overloaded.

Memorystream also has a buffer to cache data. You can specify the size of this buffer when new is used. Then the instantiation of this buffer is completed when new is used. If the size of the buffer is not enough when writing data, it will be automatically expanded.

You can also instantiate a buffer yourself, and use index and count to specify which part of the buffer the memory stream can use when using new. When using new in this way, if the size is not enough when writing data, it cannot be expanded.

writable indicates whether it can be written

publiclyVisible indicates whether the buffer inside the memorystream can be obtained

method

data input

public override void Write(byte[] buffer, int offset, int count)
{
    if (buffer == null)
    {
        throw new ArgumentNullException("buffer", Environment.GetResourceString("ArgumentNull_Buffer"));
    }

    if (offset < 0)
    {
        throw new ArgumentOutOfRangeException("offset", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
    }

    if (count < 0)
    {
        throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
    }

    if (buffer.Length - offset < count)
    {
        throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen"));
    }

    if (!_isOpen)
    {
        __Error.StreamIsClosed();
    }

    EnsureWriteable();//确保可以写入,不能不能写入会报错,不继续执行了
    int num = _position + count; //计算加入全部写入时流的位置
    if (num < 0) //超出int可表示的最大值的检查,可以注意下,一般自己写代码时很少会做这种检查,虽然一般情况下也不需要
    {              //MemoryStream的最大容量是int.MaxValue
        throw new IOException(Environment.GetResourceString("IO.IO_StreamTooLong"));
    }

    if (num > _length)//全部写入时流的位置大于buffer的长度,就需要扩容了
    {
        bool flag = _position > _length;
        if (num > _capacity && EnsureCapacity(num))
        {
            flag = false;
        }

        if (flag)//流的位置大于Buffer长度,需要清除多余的部分
        {
            Array.Clear(_buffer, _length, num - _length);
        }

        _length = num;
    }

    if (count <= 8 && buffer != _buffer) //当字节小于8时则一个个读
    {
        int num2 = count;
        while (--num2 >= 0)
        {
            _buffer[_position + num2] = buffer[offset + num2];
        }
    }
    else//将提供的buffer数据拷贝到MemoryStream的buffer种
    {  //Buffer.BlockCopy比Array.Copy更快
       //https://stackoverflow.com/questions/1389821/array-copy-vs-buffer-blockcopy
        Buffer.InternalBlockCopy(buffer, offset, _buffer, _position, count);
    }

   _position = num;//更新流的位置
}


private bool EnsureCapacity(int value)
{
    if (value < 0)
    {
        throw new IOException(Environment.GetResourceString("IO.IO_StreamTooLong"));
    }

    if (value > _capacity)
    {
        int num = value;
        if (num < 256)
        {
            num = 256;//容量小于256时,会被规范为256
        }

        if (num < _capacity * 2)
        {
            num = _capacity * 2;//两倍扩容
        }

        if ((uint)(_capacity * 2) > 2147483591u)//处理超限
        {
            num = ((value > 2147483591) ? value : 2147483591);
        }

        Capacity = num;
        return true;
    }

    return false;
}

public virtual int Capacity
{
    [__DynamicallyInvokable]
    get
    {
        if (!_isOpen)
        {
            __Error.StreamIsClosed();
        }

        return _capacity - _origin;
    }
    [__DynamicallyInvokable]
    set
    {
        if (value < Length)
        {
            throw new ArgumentOutOfRangeException("value", Environment.GetResourceString("ArgumentOutOfRange_SmallCapacity"));
        }

        if (!_isOpen)
        {
            __Error.StreamIsClosed();
        }

        if (!_expandable && value != Capacity)
        {
            __Error.MemoryStreamNotExpandable();
        }

        if (!_expandable || value == _capacity)//new时指定了buffer就不能扩容了
        {
            return;
        }

        if (value > 0)
        {
            byte[] array = new byte[value];
            if (_length > 0)//扩容时会将原来的数据copy到新的buffer种
            {
                Buffer.InternalBlockCopy(_buffer, 0, array, 0, _length);
            }

            _buffer = array;
        }
        else
        {
            _buffer = null;
        }

        _capacity = value;
    }
}

Read data

public override int Read([In][Out] byte[] buffer, int offset, int count)//理解了write后,read方法很简单
{
    if (buffer == null)
    {
        throw new ArgumentNullException("buffer", Environment.GetResourceString("ArgumentNull_Buffer"));
    }

    if (offset < 0)
    {
        throw new ArgumentOutOfRangeException("offset", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
    }

    if (count < 0)
    {
        throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
    }

    if (buffer.Length - offset < count)
    {
        throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen"));
    }

    if (!_isOpen)
    {
        __Error.StreamIsClosed();
    }

    int num = _length - _position;
    if (num > count)
    {
        num = count;
    }

    if (num <= 0)
    {
        return 0;
    }

    if (num <= 8)
    {
        int num2 = num;
        while (--num2 >= 0)
        {
            buffer[offset + num2] = _buffer[_position + num2];
        }
    }
    else
    {
        Buffer.InternalBlockCopy(_buffer, _position, buffer, offset, num);//将数据拷贝到指定的buffer中
    }

    _position += num;//读完后,流的position增加
    return num;
}

【BinaryWriter】

Constructor

 Stream parameters, FileStream, and MemoryStream are all inherited from Stream. The main purpose of passing them in here is to use the buffers of these Streams.

Encoding encoding type, the default is new UTF8Encoding()

leaveOpen: Indicates whether to keep the stream open when closing. The default is false, which will also close the stream. 

method

//可以每次写入实际是将数据写入Stream的buffer中,BinaryWriter将数据序列化了,这里只提供基本数据类型的序列化
public virtual void Write(bool value)//写入bool
{
    _buffer[0] = (byte)(value ? 1u : 0u);//bool也是byte
    OutStream.Write(_buffer, 0, 1);//BinaryWriter也有有个buffer,固定的长度,为16,之所以为16是因为有个decimal类型要有16个字节表示
}

public virtual void Write(byte value)//byte直接写入
{
    OutStream.WriteByte(value);
}

public virtual void Write(byte[] buffer)
{
    if (buffer == null)
    {
        throw new ArgumentNullException("buffer");
    }

    OutStream.Write(buffer, 0, buffer.Length);//byte[]一样是直接写入到stream的buffer中
}

public virtual void Write(short value)
{
    _buffer[0] = (byte)value;//先取到低八位
    _buffer[1] = (byte)(value >> 8);//右移取到高八位
    OutStream.Write(_buffer, 0, 2);
}

public virtual void Write(int value)
{
    _buffer[0] = (byte)value;
    _buffer[1] = (byte)(value >> 8);
    _buffer[2] = (byte)(value >> 16);
    _buffer[3] = (byte)(value >> 24);
    OutStream.Write(_buffer, 0, 4);
}

public virtual void Write(long value)
{
    _buffer[0] = (byte)value;
    _buffer[1] = (byte)(value >> 8);
    _buffer[2] = (byte)(value >> 16);
    _buffer[3] = (byte)(value >> 24);
    _buffer[4] = (byte)(value >> 32);
    _buffer[5] = (byte)(value >> 40);
    _buffer[6] = (byte)(value >> 48);
    _buffer[7] = (byte)(value >> 56);
    OutStream.Write(_buffer, 0, 8);
}

public unsafe virtual void Write(string value)
{
    if (value == null)
    {
        throw new ArgumentNullException("value");
    }

    int byteCount = _encoding.GetByteCount(value);
    Write7BitEncodedInt(byteCount);//会先写入字符串的byte长度
    if (_largeByteBuffer == null)
    {
        _largeByteBuffer = new byte[256];//先尝试用一个256长度的Buffer做首次尝试
        _maxChars = _largeByteBuffer.Length / _encoding.GetMaxByteCount(1);//获取该编码格式下,最大的字符需要多少byte,maxChar表示largeByteBuffer最多可以容纳多少个字符
    }

    if (byteCount <= _largeByteBuffer.Length)//小256,就直接将编码得到的bytes放入largeByteBuffer,再copy到Stream的buffer中
    {
        _encoding.GetBytes(value, 0, value.Length, _largeByteBuffer, 0);
        OutStream.Write(_largeByteBuffer, 0, byteCount);
        return;
    }

    int num = 0;
    int num2 = value.Length;
    while (num2 > 0)//字符串的长度大于128时,将字符串拆分转为bytes,分别写入largeByteBuffer
    {
        int num3 = (num2 > _maxChars) ? _maxChars : num2;
        if (num < 0 || num3 < 0 || checked(num + num3) > value.Length)
        {
            throw new ArgumentOutOfRangeException("charCount");
        }

        int bytes2;
        fixed (char* ptr = value)
        {
            fixed (byte* bytes = _largeByteBuffer)
            {
                //因为字符串不可修改,要使用指针读取,这个方法表示的意思和之前的_encoder.GetBytes是一样的,
                bytes2 = _encoder.GetBytes((char*)checked(unchecked((nuint)ptr) + unchecked((nuint)checked(unchecked((nint)num) * (nint)2))), num3, bytes, _largeByteBuffer.Length, num3 == num2);
            }
        }

        OutStream.Write(_largeByteBuffer, 0, bytes2);
        num += num3;
        num2 -= num3;
    }
}


protected void Write7BitEncodedInt(int value)//用于将整数值编码为7位压缩格式并写入流中。
{                                           //它通常用于数据序列化或网络通信过程中,以减小整数值的存储空间和传输开销。编码过程中,整数值按照7位的块进行分割,并将每个块的最高位设置为1,表示后面还有更多的块。每个块的其余7位用于存储整数值的一部分。
    uint num;                               //这样,较小的整数值可以用较少的字节进行编码,而较大的整数值则需要更多的字节。
    for (num = (uint)value; num >= 128; num >>= 7) //这种方式相比于之前的Write(int value),会减少存储空间
    {
        Write((byte)(num | 0x80));//0x80是 1000 0000,这里直接舍去了num的后七位
    }

    Write((byte)num);
}

【BinaryReader】

Constructor

 method

//可以发现真正读数据都是Stream完成的,BinaryReader将读出来的数据反序列化了,这里只提供byte到基本数据类型的反序列化
public virtual byte ReadByte()//读byte
{
    if (m_stream == null)
    {
        __Error.FileNotOpen();
    }

    int num = m_stream.ReadByte();//实际调用的是Stream的ReadByte方法,最低读8位
    if (num == -1)
    {
        __Error.EndOfFile();
    }

    return (byte)num;
}

public virtual short ReadInt16()//读short
{
    FillBuffer(2);//16/8=2
    return (short)(m_buffer[0] | (m_buffer[1] << 8));//低位在前面,高位在后面,这是小端模式存储
}

public virtual int ReadInt32()//读int
{
    if (m_isMemoryStream)//new BinaryStream会判断下Stream是不是MemoryStream
    {
        if (m_stream == null)
        {
            __Error.FileNotOpen();
        }

        MemoryStream memoryStream = m_stream as MemoryStream;
        return memoryStream.InternalReadInt32();
    }

    FillBuffer(4);//32/8=4
    return m_buffer[0] | (m_buffer[1] << 8) | (m_buffer[2] << 16) | (m_buffer[3] << 24);
}

public unsafe virtual float ReadSingle()//读取float
{
    FillBuffer(4);
    uint num = (uint)(m_buffer[0] | (m_buffer[1] << 8) | (m_buffer[2] << 16) | (m_buffer[3] << 24));
    return *(float*)(&num);
}

public unsafe virtual double ReadDouble()//读取double
{
    FillBuffer(8);
    uint num = (uint)(m_buffer[0] | (m_buffer[1] << 8) | (m_buffer[2] << 16) | (m_buffer[3] << 24));
    uint num2 = (uint)(m_buffer[4] | (m_buffer[5] << 8) | (m_buffer[6] << 16) | (m_buffer[7] << 24));
    ulong num3 = ((ulong)num2 << 32) | num;
    return *(double*)(&num3);
}


public virtual string ReadString()
{
    if (m_stream == null)
    {
        __Error.FileNotOpen();
    }

    int num = 0;
    int num2 = Read7BitEncodedInt();//读取字符串长度
    if (num2 < 0)
    {
        throw new IOException(Environment.GetResourceString("IO.IO_InvalidStringLen_Len", num2));
    }

    if (num2 == 0)
    {
        return string.Empty;
    }

    if (m_charBytes == null)
    {
        m_charBytes = new byte[128];//这里是存储读取到的字节数组
    }

    if (m_charBuffer == null)
    {
        m_charBuffer = new char[m_maxCharsSize];//这里存储的是字符数组  m_maxCharsSize = encoding.GetMaxCharCount(128);maxCharSize是该编码下128个字符的最大大小
    }

    StringBuilder stringBuilder = null;
    do
    {
        int count = (num2 - num > 128) ? 128 : (num2 - num);
        int num3 = m_stream.Read(m_charBytes, 0, count);
        if (num3 == 0)
        {
            __Error.EndOfFile();
        }
        //每次将charBytes数组中起始节点为0,数量为num3的数据解码放大charBuffer中,在charBuffer中的起始地址为0
        int chars = m_decoder.GetChars(m_charBytes, 0, num3, m_charBuffer, 0);
        if (num == 0 && num3 == num2)
        {
            return new string(m_charBuffer, 0, chars);//字符串长度小于128的就直接返回了
        }

        if (stringBuilder == null)
        {
            stringBuilder = StringBuilderCache.Acquire(Math.Min(num2, 360));
        }

        stringBuilder.Append(m_charBuffer, 0, chars);
        num += num3;
    }
    while (num < num2);
    return StringBuilderCache.GetStringAndRelease(stringBuilder);
}

protected internal int Read7BitEncodedInt()
{
    int num = 0;
    int num2 = 0;
    byte b;
    do
    {
        if (num2 == 35)
        {
            throw new FormatException(Environment.GetResourceString("Format_Bad7BitInt32"));
        }

        b = ReadByte();
        num |= (b & 0x7F) << num2;
        num2 += 7;
    }
    while ((b & 0x80) != 0);
    return num;
}

Guess you like

Origin blog.csdn.net/enternalstar/article/details/132477043