• C# 流Stream详解(3)——FileStream源码


    【FileStream】

    构造函数

    如果创建一个FileStream,常见的参数例如路径Path、操作方式FileMode、权限FileAccess。

    这里说下FileShare和SafeFileHandle。

    我们知道在读取文件时,通常会有两个诉求:一是如何更快的读取文件内容;二是如何减少读取文件的消耗。常见的加快读取文件的方式是多线程读取,每个线程读取文件的一部分,这就涉及到文件的共享,有以下几种模式:

    • None:拒绝共享,其他线程(进程)将不能打开、写入、删除该文件
    • Read:允许读取,其他线程可以自己new一个FileStream实例来读取文件,线程之间读文件不影响,其中一个线程的FileStream释放后,不影响其他线程读文件。
    • Write:允许写入,其他线程之间并行写入,需要注意的是,不同线程要在不同的流位置和流区间写入,不要重叠,否则重叠的部分是串行的。
    • ReadWrite:允许读写,这种方式不常用,在并行时最需要保证的是不同线程读写文件的不同区间,不要重叠。
    • Delete:允许删除
    • Ineritable:允许文件句柄由子进程继承

    SafeFileHandle用的很少,一般来多语言交互的时候用到,比如在C++打开了一个文件,要把引用传递给C#,C#这边来读取文件,或者C#打开文件,传递给C++读取文件。这种情况出现的很少,如果真的需要C++和C#传递文件数据,一般会将文件路径传递,打开读取文件在一端进行,或者在一端打开读取文件后将包含数据的buffer传递到另一端。如果要用的话,示例如下:

    1. [DllImport("kernel32.dll", SetLastError = true, CharSet=CharSet.Unicode)]
    2. static extern SafeFileHandle CreateFile(string lpFileName, uint dwDesiredAccess,
    3. uint dwShareMode, IntPtr lpSecurityAttributes, uint dwCreationDisposition,
    4. uint dwFlagsAndAttributes, IntPtr hTemplateFile);
    5. public void ReadFile()
    6. {
    7. SafeFileHandle fileHandle = CreateFile(
    8. "example.txt",
    9. GENERIC_READ,
    10. FILE_SHARE_READ,
    11. IntPtr.Zero,
    12. OPEN_EXISTING,
    13. FILE_ATTRIBUTE_NORMAL,
    14. IntPtr.Zero
    15. );
    16. byte[] buffer = new byte[1024];
    17. using (FileStream fileStream = new FileStream(fileHandle, FileAccess.Read))
    18. {
    19. int bytesRead = fileStream.Read(buffer, 0, buffer.Length);
    20. //FileStream.SafeFileHandle.DangerousGetHandle() 获取文件句柄
    21. }
    22. }

    方法

    Read、Write、Dispose和Close没什么好说的,很常用。这里看看不常用的其他方法。

    • Flush:读写文件是一个很复杂的过程,当我们调用write方法尝试将数据写到磁盘上时,即使我们调用了同步的方法,也不是立即写入磁盘,而是先写入FileStream的buffer中。FileStream buffer的默认大小为4kb,我们可以在实例化的时候指定buffer大小。当调用Write方法时,会先将数据写入buffer,如果buffer满了,就将数据写入操作系统的buffer中。(buffer在写入时会分配内存,如果第一次写入的count大于buffersize,那么直接写入操作系统buffer中,否则即使count>buffersize,也是先把buffer填满,再写入操作系统中,这里是个优化的点)。Flush()相当于Flush(false),会立即将buffer中的数据写入操作系统的buffer,并清空buffer中;Flush(true)会将操作系统的buffer也清空,并执行写入磁盘的操作。
    • Lock:锁定文件流void Lock (long position, long length),将文件流的一部分锁定进行独占访问。与之对应的是UnLock。
    • Read(Span):如果不了解Span,可以先了解下。Span表示一段连续的内存,有时我们希望直接操作一段内存,安全的方式是先将这段内存的内容copy出来,但是这样性能不高。想要高性能,就要使用指针去访问,但这样不安全。Span提供安全高效的内存访问。用该方法,可以直接将读取的字节序列放到Span引用的连续内存中。

    • ReadExactly:其与Read的区别是,在读取一定的字节序列后,会推进流的位置,也即改变Position属性的值。

    源码(反编译来的)

    对于上层调用者来说,FileStream提供了一个中间缓存层。每多一个中间层,就需要在中间层中处理好中间层和下层的共同属性之间的关系,这里指Position的关系。

    读取数据

    1. public override int Read([In][Out] byte[] array, int offset, int count)
    2. {
    3. if (array == null)
    4. {
    5. throw new ArgumentNullException("array", Environment.GetResourceString("ArgumentNull_Buffer"));
    6. }
    7. if (offset < 0)
    8. {
    9. throw new ArgumentOutOfRangeException("offset", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
    10. }
    11. if (count < 0)
    12. {
    13. throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
    14. }
    15. if (array.Length - offset < count)
    16. {
    17. throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen"));
    18. }
    19. if (_handle.IsClosed)//这是SafeFileHandle
    20. {
    21. __Error.FileNotOpen();
    22. }
    23. bool flag = false;
    24. int num = _readLen - _readPos;//read和write共用一个buffer,readlen表示用于读数据的长度,这里求的是读数据buffer的剩余可用大小
    25. if (num == 0)//用于读的buffer的长度和位置相等,表示用于读的buffer用满了
    26. {
    27. if (!CanRead)
    28. {
    29. __Error.ReadNotSupported();
    30. }
    31. if (_writePos > 0)//此时需要清空写入的buffer
    32. {
    33. FlushWrite(calledFromFinalizer: false);
    34. }
    35. if (!CanSeek || count >= _bufferSize)//如果要求的count大于buffer的大小时,会直接将读出来的数据放入到指定的array中,少了copy的步骤
    36. { //如果FilsStream只是用于读数据,可以指定小的buffersize,将读到的数据直接放入指定的array中,减少从buffer到array的拷贝
    37. num = ReadCore(array, offset, count);
    38. _readPos = 0;
    39. _readLen = 0;
    40. return num;
    41. }
    42. if (_buffer == null)//实例化buffer
    43. {
    44. _buffer = new byte[_bufferSize];
    45. }
    46. num = ReadCore(_buffer, 0, _bufferSize);//注意,如果指定的count小于buffer,那么实际是按照buffersize的大小来读取数据的
    47. if (num == 0) //这样做是为了减少IO消耗,底层在读取磁盘数据时,会一次性读取扇区里的全部内容,而不是按照上层指定的读取只读取几个字节
    48. //因此,读文件的操作不一定真的有IO消耗,如果每次读的小,会用到这里的缓存数据
    49. {
    50. return 0;
    51. }
    52. flag = (num < _bufferSize);//这种情况表示文件大小(或者文件剩余大小)小于指定的buffer大小,buffer大小默认4kb
    53. _readPos = 0;
    54. _readLen = num;
    55. }
    56. if (num > count)
    57. {
    58. num = count;
    59. }
    60. Buffer.InternalBlockCopy(_buffer, _readPos, array, offset, num);//将buffer里的数据copy到指定的array中
    61. _readPos += num;//读数据时流的位置会增加
    62. if (!_isPipe && num < count && !flag)//isPipe一般为false,这种情况表示指定的count比读数据buffer的可用数据大
    63. { //步骤是先将buffer里的数据copy到指定的array中,然后再从文件中读剩余(count-num)个数据
    64. int num2 = ReadCore(array, offset + num, count - num);
    65. num += num2;
    66. _readPos = 0;//读数据的Buffer被读取完了,流的位置和大小都回置为0
    67. _readLen = 0;
    68. }
    69. return num;
    70. }

    写入数据

    1. public override void Write(byte[] array, int offset, int count)
    2. {
    3. if (array == null)
    4. {
    5. throw new ArgumentNullException("array", Environment.GetResourceString("ArgumentNull_Buffer"));
    6. }
    7. if (offset < 0)
    8. {
    9. throw new ArgumentOutOfRangeException("offset", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
    10. }
    11. if (count < 0)
    12. {
    13. throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
    14. }
    15. if (array.Length - offset < count)
    16. {
    17. throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen"));
    18. }
    19. if (_handle.IsClosed)
    20. {
    21. __Error.FileNotOpen();
    22. }
    23. if (_writePos == 0
    24. {
    25. if (!CanWrite)
    26. {
    27. __Error.WriteNotSupported();
    28. }
    29. if (_readPos < _readLen)//这种情况表示要写数据时,buffer中还有一些数据没被上层读完,需要情况,回退读的位置
    30. { //读写共用一个buffer,读时清空写的数据,写时清空读的数据
    31. FlushRead();
    32. }
    33. _readPos = 0;
    34. _readLen = 0;
    35. }
    36. if (_writePos > 0)
    37. {
    38. int num = _bufferSize - _writePos;//计算剩余可写入大小,
    39. if (num > 0)
    40. {
    41. if (num > count)
    42. {
    43. num = count;
    44. }
    45. Buffer.InternalBlockCopy(array, offset, _buffer, _writePos, num);//将array中的数据copy了一份到buffer中,不是立即写入,会在关闭流的时候再写入
    46. _writePos += num;//更新写入流的位置
    47. if (count == num)
    48. {
    49. return;//这种情况是剩余的写入大小大于指定的count
    50. }
    51. offset += num;
    52. count -= num;
    53. }
    54. if (_isAsync)
    55. {
    56. IAsyncResult asyncResult = BeginWriteCore(_buffer, 0, _writePos, null, null);
    57. EndWrite(asyncResult);
    58. }
    59. else
    60. {
    61. WriteCore(_buffer, 0, _writePos);//能走到这里,是因为count>num,此时buffer中数据已满,需要写入磁盘中
    62. }
    63. _writePos = 0;
    64. }
    65. if (count >= _bufferSize)//如果剩余的count或首次的count大于buffer大小,直接写入
    66. { //这里写入是先将buffer填满再写入,多余的直接写入
    67. WriteCore(array, offset, count);
    68. }
    69. else if (count != 0)
    70. {
    71. if (_buffer == null)
    72. {
    73. _buffer = new byte[_bufferSize];
    74. }
    75. Buffer.InternalBlockCopy(array, offset, _buffer, _writePos, count);//如果写入指定的count小于剩余的写入大小,只是将array中的数据copy了一份到buffer中,不是立即写入,会在关闭流的时候再写入
    76. _writePos = count;//更新写入流的位置
    77. }
    78. }

    寻找位置

    1. public override long Position
    2. {
    3. [SecuritySafeCritical]
    4. get
    5. {
    6. if (_handle.IsClosed)
    7. {
    8. __Error.FileNotOpen();
    9. }
    10. if (!CanSeek)
    11. {
    12. __Error.SeekNotSupported();
    13. }
    14. if (_exposedHandle)//该值一般为false,get文件句柄时会被设置为true,此时文件流的位置,需要重新确定
    15. {
    16. VerifyOSHandlePosition();
    17. }
    18. //这里获取的不是真正的文件流的位置,而是上层调用者认为的流的位置
    19. return _pos + (_readPos - _readLen + _writePos);
    20. }
    21. set
    22. {
    23. if (value < 0)
    24. {
    25. throw new ArgumentOutOfRangeException("value", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
    26. }
    27. if (_writePos > 0)//如果有需要写入的内容,会将内容先写入
    28. {
    29. FlushWrite(calledFromFinalizer: false);
    30. }
    31. _readPos = 0;
    32. _readLen = 0;
    33. Seek(value, SeekOrigin.Begin);//Position属性表示以流开始为起点的位置
    34. }
    35. }
    36. public override long Seek(long offset, SeekOrigin origin)
    37. {
    38. if (origin < SeekOrigin.Begin || origin > SeekOrigin.End)
    39. {
    40. throw new ArgumentException(Environment.GetResourceString("Argument_InvalidSeekOrigin"));
    41. }
    42. if (_handle.IsClosed)
    43. {
    44. __Error.FileNotOpen();
    45. }
    46. if (!CanSeek)
    47. {
    48. __Error.SeekNotSupported();
    49. }
    50. if (_writePos > 0)
    51. {
    52. FlushWrite(calledFromFinalizer: false);
    53. }
    54. else if (origin == SeekOrigin.Current)
    55. {
    56. offset -= _readLen - _readPos;//如果以当前为起点,_readPos表示FilsStream的buffer的Pos,而不是文件流的真正的Position
    57. } //offset表示调用者认为的Position,(_readLen - _readPos)表示文件流真正的Position和调用者认为的Position之间的差值
    58. if (_exposedHandle)
    59. {
    60. VerifyOSHandlePosition();//实际上时调用SeekCore(0L, SeekOrigin.Current),重新定位下文件流的位置
    61. }
    62. long num = _pos + (_readPos - _readLen);//计算出来的文件流的真正位置
    63. long num2 = SeekCore(offset, origin);//重新定位文件流的位置
    64. if (_appendStart != -1 && num2 < _appendStart)
    65. {
    66. SeekCore(num, SeekOrigin.Begin);
    67. throw new IOException(Environment.GetResourceString("IO.IO_SeekAppendOverwrite"));
    68. }
    69. if (_readLen > 0)//这表示之前已经读取了部分文件信息
    70. {
    71. if (num == num2)//一般是这种情况
    72. {
    73. if (_readPos > 0)//以readPos为分界线,把buffer中后面的数据拷贝到前面,这样readPos为0了,readLen减少了
    74. {
    75. Buffer.InternalBlockCopy(_buffer, _readPos, _buffer, 0, _readLen - _readPos);
    76. _readLen -= _readPos;
    77. _readPos = 0;
    78. }
    79. if (_readLen > 0)//恢复文件流的真正位置
    80. {
    81. SeekCore(_readLen, SeekOrigin.Current);
    82. }
    83. }
    84. else if (num - _readPos < num2 && num2 < num + _readLen - _readPos)//表示重新定位的文件流的位置小于计算出来的文件流的位置
    85. { //大于上次读取文件流时得位置
    86. int num3 = (int)(num2 - num);
    87. Buffer.InternalBlockCopy(_buffer, _readPos + num3, _buffer, 0, _readLen - (_readPos + num3));
    88. _readLen -= _readPos + num3;
    89. _readPos = 0;
    90. if (_readLen > 0)
    91. {
    92. SeekCore(_readLen, SeekOrigin.Current);
    93. }
    94. }
    95. else
    96. {
    97. _readPos = 0;
    98. _readLen = 0;
    99. }
    100. }
    101. return num2;
    102. }
    103. //可以看到Seek流程很复杂,为了提高性能,应该避免再读数据时比默认buffersize大,直接读到指定的array中,不要经过FileStream的buffer。
    104. //一定要避免写数据和读数据交叉进行
    105. //因为filsStream的设计考虑了通用,当我们按照一定的规范去使用时,可以减少很多为通用情况而做的耗费性能的设计

    【MemoryStream】

    构造函数

    只需要关注红色方框里的两个构造函数即可,其他的都是重载。

    memorystream也有一个buffer来缓存数据,在new的时候可以指定这个buffer的大小,那么这个buffer的实例化在new的时候完成,如果在写数据时这个buffer的大小不够用,则会自动扩容。

    也可以自己实例化一个buffer,并在new的时候通过index和count来指定memorystream可以用这个buffer的哪一部分。通过这种方式new时,如果写数据时大小不够用,是不能扩容的。

    writable表示是否可以写入

    publiclyVisible表示是否可以拿到memorystream内部的buffer

    方法

    写入数据

    1. public override void Write(byte[] buffer, int offset, int count)
    2. {
    3. if (buffer == null)
    4. {
    5. throw new ArgumentNullException("buffer", Environment.GetResourceString("ArgumentNull_Buffer"));
    6. }
    7. if (offset < 0)
    8. {
    9. throw new ArgumentOutOfRangeException("offset", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
    10. }
    11. if (count < 0)
    12. {
    13. throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
    14. }
    15. if (buffer.Length - offset < count)
    16. {
    17. throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen"));
    18. }
    19. if (!_isOpen)
    20. {
    21. __Error.StreamIsClosed();
    22. }
    23. EnsureWriteable();//确保可以写入,不能不能写入会报错,不继续执行了
    24. int num = _position + count; //计算加入全部写入时流的位置
    25. if (num < 0) //超出int可表示的最大值的检查,可以注意下,一般自己写代码时很少会做这种检查,虽然一般情况下也不需要
    26. { //MemoryStream的最大容量是int.MaxValue
    27. throw new IOException(Environment.GetResourceString("IO.IO_StreamTooLong"));
    28. }
    29. if (num > _length)//全部写入时流的位置大于buffer的长度,就需要扩容了
    30. {
    31. bool flag = _position > _length;
    32. if (num > _capacity && EnsureCapacity(num))
    33. {
    34. flag = false;
    35. }
    36. if (flag)//流的位置大于Buffer长度,需要清除多余的部分
    37. {
    38. Array.Clear(_buffer, _length, num - _length);
    39. }
    40. _length = num;
    41. }
    42. if (count <= 8 && buffer != _buffer) //当字节小于8时则一个个读
    43. {
    44. int num2 = count;
    45. while (--num2 >= 0)
    46. {
    47. _buffer[_position + num2] = buffer[offset + num2];
    48. }
    49. }
    50. else//将提供的buffer数据拷贝到MemoryStream的buffer种
    51. { //Buffer.BlockCopy比Array.Copy更快
    52. //https://stackoverflow.com/questions/1389821/array-copy-vs-buffer-blockcopy
    53. Buffer.InternalBlockCopy(buffer, offset, _buffer, _position, count);
    54. }
    55. _position = num;//更新流的位置
    56. }
    57. private bool EnsureCapacity(int value)
    58. {
    59. if (value < 0)
    60. {
    61. throw new IOException(Environment.GetResourceString("IO.IO_StreamTooLong"));
    62. }
    63. if (value > _capacity)
    64. {
    65. int num = value;
    66. if (num < 256)
    67. {
    68. num = 256;//容量小于256时,会被规范为256
    69. }
    70. if (num < _capacity * 2)
    71. {
    72. num = _capacity * 2;//两倍扩容
    73. }
    74. if ((uint)(_capacity * 2) > 2147483591u)//处理超限
    75. {
    76. num = ((value > 2147483591) ? value : 2147483591);
    77. }
    78. Capacity = num;
    79. return true;
    80. }
    81. return false;
    82. }
    83. public virtual int Capacity
    84. {
    85. [__DynamicallyInvokable]
    86. get
    87. {
    88. if (!_isOpen)
    89. {
    90. __Error.StreamIsClosed();
    91. }
    92. return _capacity - _origin;
    93. }
    94. [__DynamicallyInvokable]
    95. set
    96. {
    97. if (value < Length)
    98. {
    99. throw new ArgumentOutOfRangeException("value", Environment.GetResourceString("ArgumentOutOfRange_SmallCapacity"));
    100. }
    101. if (!_isOpen)
    102. {
    103. __Error.StreamIsClosed();
    104. }
    105. if (!_expandable && value != Capacity)
    106. {
    107. __Error.MemoryStreamNotExpandable();
    108. }
    109. if (!_expandable || value == _capacity)//new时指定了buffer就不能扩容了
    110. {
    111. return;
    112. }
    113. if (value > 0)
    114. {
    115. byte[] array = new byte[value];
    116. if (_length > 0)//扩容时会将原来的数据copy到新的buffer种
    117. {
    118. Buffer.InternalBlockCopy(_buffer, 0, array, 0, _length);
    119. }
    120. _buffer = array;
    121. }
    122. else
    123. {
    124. _buffer = null;
    125. }
    126. _capacity = value;
    127. }
    128. }

    读取数据

    1. public override int Read([In][Out] byte[] buffer, int offset, int count)//理解了write后,read方法很简单
    2. {
    3. if (buffer == null)
    4. {
    5. throw new ArgumentNullException("buffer", Environment.GetResourceString("ArgumentNull_Buffer"));
    6. }
    7. if (offset < 0)
    8. {
    9. throw new ArgumentOutOfRangeException("offset", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
    10. }
    11. if (count < 0)
    12. {
    13. throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
    14. }
    15. if (buffer.Length - offset < count)
    16. {
    17. throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen"));
    18. }
    19. if (!_isOpen)
    20. {
    21. __Error.StreamIsClosed();
    22. }
    23. int num = _length - _position;
    24. if (num > count)
    25. {
    26. num = count;
    27. }
    28. if (num <= 0)
    29. {
    30. return 0;
    31. }
    32. if (num <= 8)
    33. {
    34. int num2 = num;
    35. while (--num2 >= 0)
    36. {
    37. buffer[offset + num2] = _buffer[_position + num2];
    38. }
    39. }
    40. else
    41. {
    42. Buffer.InternalBlockCopy(_buffer, _position, buffer, offset, num);//将数据拷贝到指定的buffer中
    43. }
    44. _position += num;//读完后,流的position增加
    45. return num;
    46. }

    【BinaryWriter】

    构造函数

     Stream参数,FileStream、MemoryStream都继承自Stream,这里传递进来主要是要用这些Stream的buffer

    Encoding 编码类型,默认是new UTF8Encoding()

    leaveOpen:表示close时要不要把stream要不要保持打开,默认为false,会将stream也close 

    方法

    1. //可以每次写入实际是将数据写入Stream的buffer中,BinaryWriter将数据序列化了,这里只提供基本数据类型的序列化
    2. public virtual void Write(bool value)//写入bool
    3. {
    4. _buffer[0] = (byte)(value ? 1u : 0u);//bool也是byte
    5. OutStream.Write(_buffer, 0, 1);//BinaryWriter也有有个buffer,固定的长度,为16,之所以为16是因为有个decimal类型要有16个字节表示
    6. }
    7. public virtual void Write(byte value)//byte直接写入
    8. {
    9. OutStream.WriteByte(value);
    10. }
    11. public virtual void Write(byte[] buffer)
    12. {
    13. if (buffer == null)
    14. {
    15. throw new ArgumentNullException("buffer");
    16. }
    17. OutStream.Write(buffer, 0, buffer.Length);//byte[]一样是直接写入到stream的buffer中
    18. }
    19. public virtual void Write(short value)
    20. {
    21. _buffer[0] = (byte)value;//先取到低八位
    22. _buffer[1] = (byte)(value >> 8);//右移取到高八位
    23. OutStream.Write(_buffer, 0, 2);
    24. }
    25. public virtual void Write(int value)
    26. {
    27. _buffer[0] = (byte)value;
    28. _buffer[1] = (byte)(value >> 8);
    29. _buffer[2] = (byte)(value >> 16);
    30. _buffer[3] = (byte)(value >> 24);
    31. OutStream.Write(_buffer, 0, 4);
    32. }
    33. public virtual void Write(long value)
    34. {
    35. _buffer[0] = (byte)value;
    36. _buffer[1] = (byte)(value >> 8);
    37. _buffer[2] = (byte)(value >> 16);
    38. _buffer[3] = (byte)(value >> 24);
    39. _buffer[4] = (byte)(value >> 32);
    40. _buffer[5] = (byte)(value >> 40);
    41. _buffer[6] = (byte)(value >> 48);
    42. _buffer[7] = (byte)(value >> 56);
    43. OutStream.Write(_buffer, 0, 8);
    44. }
    45. public unsafe virtual void Write(string value)
    46. {
    47. if (value == null)
    48. {
    49. throw new ArgumentNullException("value");
    50. }
    51. int byteCount = _encoding.GetByteCount(value);
    52. Write7BitEncodedInt(byteCount);//会先写入字符串的byte长度
    53. if (_largeByteBuffer == null)
    54. {
    55. _largeByteBuffer = new byte[256];//先尝试用一个256长度的Buffer做首次尝试
    56. _maxChars = _largeByteBuffer.Length / _encoding.GetMaxByteCount(1);//获取该编码格式下,最大的字符需要多少byte,maxChar表示largeByteBuffer最多可以容纳多少个字符
    57. }
    58. if (byteCount <= _largeByteBuffer.Length)//小256,就直接将编码得到的bytes放入largeByteBuffer,再copy到Stream的buffer中
    59. {
    60. _encoding.GetBytes(value, 0, value.Length, _largeByteBuffer, 0);
    61. OutStream.Write(_largeByteBuffer, 0, byteCount);
    62. return;
    63. }
    64. int num = 0;
    65. int num2 = value.Length;
    66. while (num2 > 0)//字符串的长度大于128时,将字符串拆分转为bytes,分别写入largeByteBuffer
    67. {
    68. int num3 = (num2 > _maxChars) ? _maxChars : num2;
    69. if (num < 0 || num3 < 0 || checked(num + num3) > value.Length)
    70. {
    71. throw new ArgumentOutOfRangeException("charCount");
    72. }
    73. int bytes2;
    74. fixed (char* ptr = value)
    75. {
    76. fixed (byte* bytes = _largeByteBuffer)
    77. {
    78. //因为字符串不可修改,要使用指针读取,这个方法表示的意思和之前的_encoder.GetBytes是一样的,
    79. bytes2 = _encoder.GetBytes((char*)checked(unchecked((nuint)ptr) + unchecked((nuint)checked(unchecked((nint)num) * (nint)2))), num3, bytes, _largeByteBuffer.Length, num3 == num2);
    80. }
    81. }
    82. OutStream.Write(_largeByteBuffer, 0, bytes2);
    83. num += num3;
    84. num2 -= num3;
    85. }
    86. }
    87. protected void Write7BitEncodedInt(int value)//用于将整数值编码为7位压缩格式并写入流中。
    88. { //它通常用于数据序列化或网络通信过程中,以减小整数值的存储空间和传输开销。编码过程中,整数值按照7位的块进行分割,并将每个块的最高位设置为1,表示后面还有更多的块。每个块的其余7位用于存储整数值的一部分。
    89. uint num; //这样,较小的整数值可以用较少的字节进行编码,而较大的整数值则需要更多的字节。
    90. for (num = (uint)value; num >= 128; num >>= 7) //这种方式相比于之前的Write(int value),会减少存储空间
    91. {
    92. Write((byte)(num | 0x80));//0x80是 1000 0000,这里直接舍去了num的后七位
    93. }
    94. Write((byte)num);
    95. }

    【BinaryReader】

    构造函数

     方法

    1. //可以发现真正读数据都是Stream完成的,BinaryReader将读出来的数据反序列化了,这里只提供byte到基本数据类型的反序列化
    2. public virtual byte ReadByte()//读byte
    3. {
    4. if (m_stream == null)
    5. {
    6. __Error.FileNotOpen();
    7. }
    8. int num = m_stream.ReadByte();//实际调用的是Stream的ReadByte方法,最低读8位
    9. if (num == -1)
    10. {
    11. __Error.EndOfFile();
    12. }
    13. return (byte)num;
    14. }
    15. public virtual short ReadInt16()//读short
    16. {
    17. FillBuffer(2);//16/8=2
    18. return (short)(m_buffer[0] | (m_buffer[1] << 8));//低位在前面,高位在后面,这是小端模式存储
    19. }
    20. public virtual int ReadInt32()//读int
    21. {
    22. if (m_isMemoryStream)//new BinaryStream会判断下Stream是不是MemoryStream
    23. {
    24. if (m_stream == null)
    25. {
    26. __Error.FileNotOpen();
    27. }
    28. MemoryStream memoryStream = m_stream as MemoryStream;
    29. return memoryStream.InternalReadInt32();
    30. }
    31. FillBuffer(4);//32/8=4
    32. return m_buffer[0] | (m_buffer[1] << 8) | (m_buffer[2] << 16) | (m_buffer[3] << 24);
    33. }
    34. public unsafe virtual float ReadSingle()//读取float
    35. {
    36. FillBuffer(4);
    37. uint num = (uint)(m_buffer[0] | (m_buffer[1] << 8) | (m_buffer[2] << 16) | (m_buffer[3] << 24));
    38. return *(float*)(&num);
    39. }
    40. public unsafe virtual double ReadDouble()//读取double
    41. {
    42. FillBuffer(8);
    43. uint num = (uint)(m_buffer[0] | (m_buffer[1] << 8) | (m_buffer[2] << 16) | (m_buffer[3] << 24));
    44. uint num2 = (uint)(m_buffer[4] | (m_buffer[5] << 8) | (m_buffer[6] << 16) | (m_buffer[7] << 24));
    45. ulong num3 = ((ulong)num2 << 32) | num;
    46. return *(double*)(&num3);
    47. }
    48. public virtual string ReadString()
    49. {
    50. if (m_stream == null)
    51. {
    52. __Error.FileNotOpen();
    53. }
    54. int num = 0;
    55. int num2 = Read7BitEncodedInt();//读取字符串长度
    56. if (num2 < 0)
    57. {
    58. throw new IOException(Environment.GetResourceString("IO.IO_InvalidStringLen_Len", num2));
    59. }
    60. if (num2 == 0)
    61. {
    62. return string.Empty;
    63. }
    64. if (m_charBytes == null)
    65. {
    66. m_charBytes = new byte[128];//这里是存储读取到的字节数组
    67. }
    68. if (m_charBuffer == null)
    69. {
    70. m_charBuffer = new char[m_maxCharsSize];//这里存储的是字符数组 m_maxCharsSize = encoding.GetMaxCharCount(128);maxCharSize是该编码下128个字符的最大大小
    71. }
    72. StringBuilder stringBuilder = null;
    73. do
    74. {
    75. int count = (num2 - num > 128) ? 128 : (num2 - num);
    76. int num3 = m_stream.Read(m_charBytes, 0, count);
    77. if (num3 == 0)
    78. {
    79. __Error.EndOfFile();
    80. }
    81. //每次将charBytes数组中起始节点为0,数量为num3的数据解码放大charBuffer中,在charBuffer中的起始地址为0
    82. int chars = m_decoder.GetChars(m_charBytes, 0, num3, m_charBuffer, 0);
    83. if (num == 0 && num3 == num2)
    84. {
    85. return new string(m_charBuffer, 0, chars);//字符串长度小于128的就直接返回了
    86. }
    87. if (stringBuilder == null)
    88. {
    89. stringBuilder = StringBuilderCache.Acquire(Math.Min(num2, 360));
    90. }
    91. stringBuilder.Append(m_charBuffer, 0, chars);
    92. num += num3;
    93. }
    94. while (num < num2);
    95. return StringBuilderCache.GetStringAndRelease(stringBuilder);
    96. }
    97. protected internal int Read7BitEncodedInt()
    98. {
    99. int num = 0;
    100. int num2 = 0;
    101. byte b;
    102. do
    103. {
    104. if (num2 == 35)
    105. {
    106. throw new FormatException(Environment.GetResourceString("Format_Bad7BitInt32"));
    107. }
    108. b = ReadByte();
    109. num |= (b & 0x7F) << num2;
    110. num2 += 7;
    111. }
    112. while ((b & 0x80) != 0);
    113. return num;
    114. }

  • 相关阅读:
    渗透测试基础,初识渗透测试
    【NR 物理资源】
    循环玩具游戏
    java题目汇总(一)
    ElasticSearch中关于Nasted嵌套查询的介绍:生动案例,通俗易懂,彻底吸收
    运用动态内存实现通讯录(增删查改+排序)
    矩阵可逆的充要条件及证明
    【Spring MVC】MVC如何浏览器请求(service方法)
    力扣(LeetCode)1732. 找到最高海拔(C++)
    java生成、识别条形码和二维码
  • 原文地址:https://blog.csdn.net/enternalstar/article/details/132477043