bufs.js 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. // Copyright 2012 The Obvious Corporation.
  2. /*
  3. * bufs: Buffer utilities.
  4. */
  5. /*
  6. * Modules used
  7. */
  8. "use strict";
  9. /*
  10. * Module variables
  11. */
  12. /** Pool of buffers, where `bufPool[x].length === x`. */
  13. var bufPool = [];
  14. /** Maximum length of kept temporary buffers. */
  15. var TEMP_BUF_MAXIMUM_LENGTH = 20;
  16. /** Minimum exactly-representable 64-bit int. */
  17. var MIN_EXACT_INT64 = -0x8000000000000000;
  18. /** Maximum exactly-representable 64-bit int. */
  19. var MAX_EXACT_INT64 = 0x7ffffffffffffc00;
  20. /** Maximum exactly-representable 64-bit uint. */
  21. var MAX_EXACT_UINT64 = 0xfffffffffffff800;
  22. /**
  23. * The int value consisting just of a 1 in bit #32 (that is, one more
  24. * than the maximum 32-bit unsigned value).
  25. */
  26. var BIT_32 = 0x100000000;
  27. /**
  28. * The int value consisting just of a 1 in bit #64 (that is, one more
  29. * than the maximum 64-bit unsigned value).
  30. */
  31. var BIT_64 = 0x10000000000000000;
  32. /*
  33. * Helper functions
  34. */
  35. /**
  36. * Masks off all but the lowest bit set of the given number.
  37. */
  38. function lowestBit(num) {
  39. return num & -num;
  40. }
  41. /**
  42. * Gets whether trying to add the second number to the first is lossy
  43. * (inexact). The first number is meant to be an accumulated result.
  44. */
  45. function isLossyToAdd(accum, num) {
  46. if (num === 0) {
  47. return false;
  48. }
  49. var lowBit = lowestBit(num);
  50. var added = accum + lowBit;
  51. if (added === accum) {
  52. return true;
  53. }
  54. if (added - lowBit !== accum) {
  55. return true;
  56. }
  57. return false;
  58. }
  59. /*
  60. * Exported functions
  61. */
  62. /**
  63. * Allocates a buffer of the given length, which is initialized
  64. * with all zeroes. This returns a buffer from the pool if it is
  65. * available, or a freshly-allocated buffer if not.
  66. */
  67. function alloc(length) {
  68. var result = bufPool[length];
  69. if (result) {
  70. bufPool[length] = undefined;
  71. } else {
  72. result = new Buffer(length);
  73. }
  74. result.fill(0);
  75. return result;
  76. }
  77. /**
  78. * Releases a buffer back to the pool.
  79. */
  80. function free(buffer) {
  81. var length = buffer.length;
  82. if (length < TEMP_BUF_MAXIMUM_LENGTH) {
  83. bufPool[length] = buffer;
  84. }
  85. }
  86. /**
  87. * Resizes a buffer, returning a new buffer. Returns the argument if
  88. * the length wouldn't actually change. This function is only safe to
  89. * use if the given buffer was allocated within this module (since
  90. * otherwise the buffer might possibly be shared externally).
  91. */
  92. function resize(buffer, length) {
  93. if (length === buffer.length) {
  94. return buffer;
  95. }
  96. var newBuf = alloc(length);
  97. buffer.copy(newBuf);
  98. free(buffer);
  99. return newBuf;
  100. }
  101. /**
  102. * Reads an arbitrary signed int from a buffer.
  103. */
  104. function readInt(buffer) {
  105. var length = buffer.length;
  106. var positive = buffer[length - 1] < 0x80;
  107. var result = positive ? 0 : -1;
  108. var lossy = false; // Note: We can't use bit manipulation here, since that stops
  109. // working if the result won't fit in a 32-bit int.
  110. if (length < 7) {
  111. // Common case which can't possibly be lossy (because the result has
  112. // no more than 48 bits, and loss only happens with 54 or more).
  113. for (var i = length - 1; i >= 0; i--) {
  114. result = result * 0x100 + buffer[i];
  115. }
  116. } else {
  117. for (var _i = length - 1; _i >= 0; _i--) {
  118. var one = buffer[_i];
  119. result *= 0x100;
  120. if (isLossyToAdd(result, one)) {
  121. lossy = true;
  122. }
  123. result += one;
  124. }
  125. }
  126. return {
  127. value: result,
  128. lossy: lossy
  129. };
  130. }
  131. /**
  132. * Reads an arbitrary unsigned int from a buffer.
  133. */
  134. function readUInt(buffer) {
  135. var length = buffer.length;
  136. var result = 0;
  137. var lossy = false; // Note: See above in re bit manipulation.
  138. if (length < 7) {
  139. // Common case which can't possibly be lossy (see above).
  140. for (var i = length - 1; i >= 0; i--) {
  141. result = result * 0x100 + buffer[i];
  142. }
  143. } else {
  144. for (var _i2 = length - 1; _i2 >= 0; _i2--) {
  145. var one = buffer[_i2];
  146. result *= 0x100;
  147. if (isLossyToAdd(result, one)) {
  148. lossy = true;
  149. }
  150. result += one;
  151. }
  152. }
  153. return {
  154. value: result,
  155. lossy: lossy
  156. };
  157. }
  158. /**
  159. * Writes a little-endian 64-bit signed int into a buffer.
  160. */
  161. function writeInt64(value, buffer) {
  162. if (value < MIN_EXACT_INT64 || value > MAX_EXACT_INT64) {
  163. throw new Error("Value out of range.");
  164. }
  165. if (value < 0) {
  166. value += BIT_64;
  167. }
  168. writeUInt64(value, buffer);
  169. }
  170. /**
  171. * Writes a little-endian 64-bit unsigned int into a buffer.
  172. */
  173. function writeUInt64(value, buffer) {
  174. if (value < 0 || value > MAX_EXACT_UINT64) {
  175. throw new Error("Value out of range.");
  176. }
  177. var lowWord = value % BIT_32;
  178. var highWord = Math.floor(value / BIT_32);
  179. buffer.writeUInt32LE(lowWord, 0);
  180. buffer.writeUInt32LE(highWord, 4);
  181. }
  182. module.exports = {
  183. alloc: alloc,
  184. free: free,
  185. readInt: readInt,
  186. readUInt: readUInt,
  187. resize: resize,
  188. writeInt64: writeInt64,
  189. writeUInt64: writeUInt64
  190. };