Writer.js 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. exports.Writer = void 0;
  4. const Slice_1 = require("./Slice");
  5. const EMPTY_UINT8 = new Uint8Array([]);
  6. const EMPTY_VIEW = new DataView(EMPTY_UINT8.buffer);
  7. const hasBuffer = typeof Buffer === 'function';
  8. const utf8Write = hasBuffer
  9. ? Buffer.prototype.utf8Write
  10. : null;
  11. const from = hasBuffer ? Buffer.from : null;
  12. const textEncoder = typeof TextEncoder !== 'undefined' ? new TextEncoder() : null;
  13. /**
  14. * Writer class provides an efficient way to encode binary data. It grows the
  15. * internal memory buffer automatically as more space is required. It is useful
  16. * in cases when it is not known in advance the size of memory needed.
  17. */
  18. class Writer {
  19. /**
  20. * @param allocSize Number of bytes to allocate at a time when buffer ends.
  21. */
  22. constructor(allocSize = 64 * 1024) {
  23. this.allocSize = allocSize;
  24. /** @ignore */
  25. this.view = EMPTY_VIEW;
  26. /** @ignore */
  27. this.x0 = 0;
  28. /** @ignore */
  29. this.x = 0;
  30. this.uint8 = new Uint8Array(allocSize);
  31. this.size = allocSize;
  32. this.view = new DataView(this.uint8.buffer);
  33. }
  34. /** @ignore */
  35. grow(size) {
  36. const x0 = this.x0;
  37. const x = this.x;
  38. const oldUint8 = this.uint8;
  39. const newUint8 = new Uint8Array(size);
  40. const view = new DataView(newUint8.buffer);
  41. const activeSlice = oldUint8.subarray(x0, x);
  42. newUint8.set(activeSlice, 0);
  43. this.x = x - x0;
  44. this.x0 = 0;
  45. this.uint8 = newUint8;
  46. this.size = size;
  47. this.view = view;
  48. }
  49. /**
  50. * Make sure the internal buffer has enough space to write the specified number
  51. * of bytes, otherwise resize the internal buffer to accommodate for more size.
  52. *
  53. * @param capacity Number of bytes.
  54. */
  55. ensureCapacity(capacity) {
  56. const byteLength = this.size;
  57. const remaining = byteLength - this.x;
  58. if (remaining < capacity) {
  59. const total = byteLength - this.x0;
  60. const required = capacity - remaining;
  61. const totalRequired = total + required;
  62. this.grow(totalRequired <= this.allocSize ? this.allocSize : totalRequired * 2);
  63. }
  64. }
  65. /** @todo Consider renaming to "skip"? */
  66. move(capacity) {
  67. this.ensureCapacity(capacity);
  68. this.x += capacity;
  69. }
  70. reset() {
  71. this.x0 = this.x;
  72. }
  73. /**
  74. * Allocates a new {@link ArrayBuffer}, useful when the underlying
  75. * {@link ArrayBuffer} cannot be shared between threads.
  76. *
  77. * @param size Size of memory to allocate.
  78. */
  79. newBuffer(size) {
  80. const uint8 = (this.uint8 = new Uint8Array(size));
  81. this.size = size;
  82. this.view = new DataView(uint8.buffer);
  83. this.x = this.x0 = 0;
  84. }
  85. /**
  86. * @returns Encoded memory buffer contents.
  87. */
  88. flush() {
  89. const result = this.uint8.subarray(this.x0, this.x);
  90. this.x0 = this.x;
  91. return result;
  92. }
  93. flushSlice() {
  94. const slice = new Slice_1.Slice(this.uint8, this.view, this.x0, this.x);
  95. this.x0 = this.x;
  96. return slice;
  97. }
  98. u8(char) {
  99. this.ensureCapacity(1);
  100. this.uint8[this.x++] = char;
  101. }
  102. u16(word) {
  103. this.ensureCapacity(2);
  104. this.view.setUint16(this.x, word);
  105. this.x += 2;
  106. }
  107. u32(dword) {
  108. this.ensureCapacity(4);
  109. this.view.setUint32(this.x, dword);
  110. this.x += 4;
  111. }
  112. i32(dword) {
  113. this.ensureCapacity(4);
  114. this.view.setInt32(this.x, dword);
  115. this.x += 4;
  116. }
  117. u64(qword) {
  118. this.ensureCapacity(8);
  119. this.view.setBigUint64(this.x, BigInt(qword));
  120. this.x += 8;
  121. }
  122. f64(float) {
  123. this.ensureCapacity(8);
  124. this.view.setFloat64(this.x, float);
  125. this.x += 8;
  126. }
  127. u8u16(u8, u16) {
  128. this.ensureCapacity(3);
  129. let x = this.x;
  130. this.uint8[x++] = u8;
  131. this.uint8[x++] = u16 >>> 8;
  132. this.uint8[x++] = u16 & 0xff;
  133. this.x = x;
  134. }
  135. u8u32(u8, u32) {
  136. this.ensureCapacity(5);
  137. let x = this.x;
  138. this.uint8[x++] = u8;
  139. this.view.setUint32(x, u32);
  140. this.x = x + 4;
  141. }
  142. u8u64(u8, u64) {
  143. this.ensureCapacity(9);
  144. let x = this.x;
  145. this.uint8[x++] = u8;
  146. this.view.setBigUint64(x, BigInt(u64));
  147. this.x = x + 8;
  148. }
  149. u8f32(u8, f32) {
  150. this.ensureCapacity(5);
  151. let x = this.x;
  152. this.uint8[x++] = u8;
  153. this.view.setFloat32(x, f32);
  154. this.x = x + 4;
  155. }
  156. u8f64(u8, f64) {
  157. this.ensureCapacity(9);
  158. let x = this.x;
  159. this.uint8[x++] = u8;
  160. this.view.setFloat64(x, f64);
  161. this.x = x + 8;
  162. }
  163. buf(buf, length) {
  164. this.ensureCapacity(length);
  165. const x = this.x;
  166. this.uint8.set(buf, x);
  167. this.x = x + length;
  168. }
  169. /**
  170. * Encodes string as UTF-8. You need to call .ensureCapacity(str.length * 4)
  171. * before calling
  172. *
  173. * @param str String to encode as UTF-8.
  174. * @returns The number of bytes written
  175. */
  176. utf8(str) {
  177. const theoreticalMaxLength = str.length * 4;
  178. if (theoreticalMaxLength < 168)
  179. return this.utf8Native(str);
  180. this.ensureCapacity(theoreticalMaxLength);
  181. const maxLength = this.size - this.x;
  182. if (utf8Write) {
  183. const writeLength = utf8Write.call(this.uint8, str, this.x, maxLength);
  184. this.x += writeLength;
  185. return writeLength;
  186. }
  187. else if (from) {
  188. const uint8 = this.uint8;
  189. const offset = uint8.byteOffset + this.x;
  190. const buf = from(uint8.buffer).subarray(offset, offset + maxLength);
  191. const writeLength = buf.write(str, 0, maxLength, 'utf8');
  192. this.x += writeLength;
  193. return writeLength;
  194. }
  195. else if (theoreticalMaxLength > 1024 && textEncoder) {
  196. const writeLength = textEncoder.encodeInto(str, this.uint8.subarray(this.x, this.x + maxLength)).written;
  197. this.x += writeLength;
  198. return writeLength;
  199. }
  200. return this.utf8Native(str);
  201. }
  202. utf8Native(str) {
  203. const length = str.length;
  204. const uint8 = this.uint8;
  205. let offset = this.x;
  206. let pos = 0;
  207. while (pos < length) {
  208. let value = str.charCodeAt(pos++);
  209. if ((value & 0xffffff80) === 0) {
  210. uint8[offset++] = value;
  211. continue;
  212. }
  213. else if ((value & 0xfffff800) === 0) {
  214. uint8[offset++] = ((value >> 6) & 0x1f) | 0xc0;
  215. }
  216. else {
  217. if (value >= 0xd800 && value <= 0xdbff) {
  218. if (pos < length) {
  219. const extra = str.charCodeAt(pos);
  220. if ((extra & 0xfc00) === 0xdc00) {
  221. pos++;
  222. value = ((value & 0x3ff) << 10) + (extra & 0x3ff) + 0x10000;
  223. }
  224. }
  225. }
  226. if ((value & 0xffff0000) === 0) {
  227. uint8[offset++] = ((value >> 12) & 0x0f) | 0xe0;
  228. uint8[offset++] = ((value >> 6) & 0x3f) | 0x80;
  229. }
  230. else {
  231. uint8[offset++] = ((value >> 18) & 0x07) | 0xf0;
  232. uint8[offset++] = ((value >> 12) & 0x3f) | 0x80;
  233. uint8[offset++] = ((value >> 6) & 0x3f) | 0x80;
  234. }
  235. }
  236. uint8[offset++] = (value & 0x3f) | 0x80;
  237. }
  238. const writeLength = offset - this.x;
  239. this.x = offset;
  240. return writeLength;
  241. }
  242. ascii(str) {
  243. const length = str.length;
  244. this.ensureCapacity(length);
  245. const uint8 = this.uint8;
  246. let x = this.x;
  247. let pos = 0;
  248. while (pos < length)
  249. uint8[x++] = str.charCodeAt(pos++);
  250. this.x = x;
  251. }
  252. }
  253. exports.Writer = Writer;
  254. //# sourceMappingURL=Writer.js.map