StreamUtil.cs 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. using System;
  2. using System.IO;
  3. using System.Text;
  4. using Best.HTTP.Shared.PlatformSupport.Memory;
  5. namespace Best.HTTP.Shared.Databases.Utils
  6. {
  7. public static class StreamUtil
  8. {
  9. public static void WriteLengthPrefixedString(this Stream stream, string str)
  10. {
  11. if (str != null)
  12. {
  13. var byteCount = Encoding.UTF8.GetByteCount(str);
  14. if (byteCount >= 1 << 16)
  15. throw new ArgumentException($"byteCount({byteCount})");
  16. stream.EncodeUnsignedVariableByteInteger((ulong)byteCount);
  17. byte[] tmp = BufferPool.Get(byteCount, true);
  18. Encoding.UTF8.GetBytes(str, 0, str.Length, tmp, 0);
  19. stream.Write(tmp, 0, byteCount);
  20. BufferPool.Release(tmp);
  21. }
  22. else
  23. {
  24. stream.WriteByte(0);
  25. }
  26. }
  27. public static string ReadLengthPrefixedString(this Stream stream)
  28. {
  29. int strLength = (int)stream.DecodeUnsignedVariableByteInteger();
  30. string result = null;
  31. if (strLength != 0)
  32. {
  33. byte[] buffer = BufferPool.Get(strLength, true);
  34. stream.Read(buffer, 0, strLength);
  35. result = System.Text.Encoding.UTF8.GetString(buffer, 0, strLength);
  36. BufferPool.Release(buffer);
  37. }
  38. return result;
  39. }
  40. public static void EncodeUnsignedVariableByteInteger(this Stream encodeTo, ulong value)
  41. {
  42. if (value < 0)
  43. throw new NotSupportedException($"Can't encode negative value({value:N0})!");
  44. byte encodedByte;
  45. do
  46. {
  47. encodedByte = (byte)(value % 128);
  48. value /= 128;
  49. // if there are more data to encode, set the top bit of this byte
  50. if (value > 0)
  51. encodedByte = (byte)(encodedByte | 128);
  52. encodeTo.WriteByte(encodedByte);
  53. }
  54. while (value > 0);
  55. }
  56. public static ulong DecodeUnsignedVariableByteInteger(this Stream decodeFrom)
  57. {
  58. ulong multiplier = 1;
  59. ulong value = 0;
  60. byte encodedByte = 0;
  61. do
  62. {
  63. encodedByte = (byte)decodeFrom.ReadByte();
  64. value += (ulong)((ulong)(encodedByte & 127) * multiplier);
  65. multiplier *= 128;
  66. } while ((encodedByte & 128) != 0);
  67. return value;
  68. }
  69. public static void EncodeSignedVariableByteInteger(this Stream encodeTo, long value)
  70. {
  71. bool more = true;
  72. while (more)
  73. {
  74. byte chunk = (byte)(value & 0x7fL); // extract a 7-bit chunk
  75. value >>= 7;
  76. bool signBitSet = (chunk & 0x40) != 0; // sign bit is the msb of a 7-bit byte, so 0x40
  77. more = !((value == 0 && !signBitSet) || (value == -1 && signBitSet));
  78. if (more) { chunk |= 0x80; } // set msb marker that more bytes are coming
  79. encodeTo.WriteByte(chunk);
  80. };
  81. }
  82. public static long DecodeSignedVariableByteInteger(this Stream stream)
  83. {
  84. long value = 0;
  85. int shift = 0;
  86. bool more = true;
  87. bool signBitSet = false;
  88. while (more)
  89. {
  90. var next = stream.ReadByte();
  91. if (next < 0)
  92. throw new InvalidOperationException("Unexpected end of stream");
  93. byte b = (byte)next;
  94. more = (b & 0x80) != 0; // extract msb
  95. signBitSet = (b & 0x40) != 0; // sign bit is the msb of a 7-bit byte, so 0x40
  96. long chunk = b & 0x7fL; // extract lower 7 bits
  97. value |= chunk << shift;
  98. shift += 7;
  99. };
  100. // extend the sign of shorter negative numbers
  101. if (shift < (sizeof(long) * 8) && signBitSet) { value |= -1L << shift; }
  102. return value;
  103. }
  104. }
  105. }