pickle.js 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. // sizeof(T).
  2. var SIZE_INT32 = 4
  3. var SIZE_UINT32 = 4
  4. var SIZE_INT64 = 8
  5. var SIZE_UINT64 = 8
  6. var SIZE_FLOAT = 4
  7. var SIZE_DOUBLE = 8
  8. // The allocation granularity of the payload.
  9. var PAYLOAD_UNIT = 64
  10. // Largest JS number.
  11. var CAPACITY_READ_ONLY = 9007199254740992
  12. // Aligns 'i' by rounding it up to the next multiple of 'alignment'.
  13. var alignInt = function (i, alignment) {
  14. return i + (alignment - (i % alignment)) % alignment
  15. }
  16. // PickleIterator reads data from a Pickle. The Pickle object must remain valid
  17. // while the PickleIterator object is in use.
  18. var PickleIterator = (function () {
  19. function PickleIterator (pickle) {
  20. this.payload = pickle.header
  21. this.payloadOffset = pickle.headerSize
  22. this.readIndex = 0
  23. this.endIndex = pickle.getPayloadSize()
  24. }
  25. PickleIterator.prototype.readBool = function () {
  26. return this.readInt() !== 0
  27. }
  28. PickleIterator.prototype.readInt = function () {
  29. return this.readBytes(SIZE_INT32, Buffer.prototype.readInt32LE)
  30. }
  31. PickleIterator.prototype.readUInt32 = function () {
  32. return this.readBytes(SIZE_UINT32, Buffer.prototype.readUInt32LE)
  33. }
  34. PickleIterator.prototype.readInt64 = function () {
  35. return this.readBytes(SIZE_INT64, Buffer.prototype.readInt64LE)
  36. }
  37. PickleIterator.prototype.readUInt64 = function () {
  38. return this.readBytes(SIZE_UINT64, Buffer.prototype.readUInt64LE)
  39. }
  40. PickleIterator.prototype.readFloat = function () {
  41. return this.readBytes(SIZE_FLOAT, Buffer.prototype.readFloatLE)
  42. }
  43. PickleIterator.prototype.readDouble = function () {
  44. return this.readBytes(SIZE_DOUBLE, Buffer.prototype.readDoubleLE)
  45. }
  46. PickleIterator.prototype.readString = function () {
  47. return this.readBytes(this.readInt()).toString()
  48. }
  49. PickleIterator.prototype.readBytes = function (length, method) {
  50. var readPayloadOffset = this.getReadPayloadOffsetAndAdvance(length)
  51. if (method != null) {
  52. return method.call(this.payload, readPayloadOffset, length)
  53. } else {
  54. return this.payload.slice(readPayloadOffset, readPayloadOffset + length)
  55. }
  56. }
  57. PickleIterator.prototype.getReadPayloadOffsetAndAdvance = function (length) {
  58. if (length > this.endIndex - this.readIndex) {
  59. this.readIndex = this.endIndex
  60. throw new Error('Failed to read data with length of ' + length)
  61. }
  62. var readPayloadOffset = this.payloadOffset + this.readIndex
  63. this.advance(length)
  64. return readPayloadOffset
  65. }
  66. PickleIterator.prototype.advance = function (size) {
  67. var alignedSize = alignInt(size, SIZE_UINT32)
  68. if (this.endIndex - this.readIndex < alignedSize) {
  69. this.readIndex = this.endIndex
  70. } else {
  71. this.readIndex += alignedSize
  72. }
  73. }
  74. return PickleIterator
  75. })()
  76. // This class provides facilities for basic binary value packing and unpacking.
  77. //
  78. // The Pickle class supports appending primitive values (ints, strings, etc.)
  79. // to a pickle instance. The Pickle instance grows its internal memory buffer
  80. // dynamically to hold the sequence of primitive values. The internal memory
  81. // buffer is exposed as the "data" of the Pickle. This "data" can be passed
  82. // to a Pickle object to initialize it for reading.
  83. //
  84. // When reading from a Pickle object, it is important for the consumer to know
  85. // what value types to read and in what order to read them as the Pickle does
  86. // not keep track of the type of data written to it.
  87. //
  88. // The Pickle's data has a header which contains the size of the Pickle's
  89. // payload. It can optionally support additional space in the header. That
  90. // space is controlled by the header_size parameter passed to the Pickle
  91. // constructor.
  92. var Pickle = (function () {
  93. function Pickle (buffer) {
  94. if (buffer) {
  95. this.initFromBuffer(buffer)
  96. } else {
  97. this.initEmpty()
  98. }
  99. }
  100. Pickle.prototype.initEmpty = function () {
  101. this.header = new Buffer(0)
  102. this.headerSize = SIZE_UINT32
  103. this.capacityAfterHeader = 0
  104. this.writeOffset = 0
  105. this.resize(PAYLOAD_UNIT)
  106. this.setPayloadSize(0)
  107. }
  108. Pickle.prototype.initFromBuffer = function (buffer) {
  109. this.header = buffer
  110. this.headerSize = buffer.length - this.getPayloadSize()
  111. this.capacityAfterHeader = CAPACITY_READ_ONLY
  112. this.writeOffset = 0
  113. if (this.headerSize > buffer.length) {
  114. this.headerSize = 0
  115. }
  116. if (this.headerSize !== alignInt(this.headerSize, SIZE_UINT32)) {
  117. this.headerSize = 0
  118. }
  119. if (this.headerSize === 0) {
  120. this.header = new Buffer(0)
  121. }
  122. }
  123. Pickle.prototype.createIterator = function () {
  124. return new PickleIterator(this)
  125. }
  126. Pickle.prototype.toBuffer = function () {
  127. return this.header.slice(0, this.headerSize + this.getPayloadSize())
  128. }
  129. Pickle.prototype.writeBool = function (value) {
  130. return this.writeInt(value ? 1 : 0)
  131. }
  132. Pickle.prototype.writeInt = function (value) {
  133. return this.writeBytes(value, SIZE_INT32, Buffer.prototype.writeInt32LE)
  134. }
  135. Pickle.prototype.writeUInt32 = function (value) {
  136. return this.writeBytes(value, SIZE_UINT32, Buffer.prototype.writeUInt32LE)
  137. }
  138. Pickle.prototype.writeInt64 = function (value) {
  139. return this.writeBytes(value, SIZE_INT64, Buffer.prototype.writeInt64LE)
  140. }
  141. Pickle.prototype.writeUInt64 = function (value) {
  142. return this.writeBytes(value, SIZE_UINT64, Buffer.prototype.writeUInt64LE)
  143. }
  144. Pickle.prototype.writeFloat = function (value) {
  145. return this.writeBytes(value, SIZE_FLOAT, Buffer.prototype.writeFloatLE)
  146. }
  147. Pickle.prototype.writeDouble = function (value) {
  148. return this.writeBytes(value, SIZE_DOUBLE, Buffer.prototype.writeDoubleLE)
  149. }
  150. Pickle.prototype.writeString = function (value) {
  151. var length = Buffer.byteLength(value, 'utf8')
  152. if (!this.writeInt(length)) {
  153. return false
  154. }
  155. return this.writeBytes(value, length)
  156. }
  157. Pickle.prototype.setPayloadSize = function (payloadSize) {
  158. return this.header.writeUInt32LE(payloadSize, 0)
  159. }
  160. Pickle.prototype.getPayloadSize = function () {
  161. return this.header.readUInt32LE(0)
  162. }
  163. Pickle.prototype.writeBytes = function (data, length, method) {
  164. var dataLength = alignInt(length, SIZE_UINT32)
  165. var newSize = this.writeOffset + dataLength
  166. if (newSize > this.capacityAfterHeader) {
  167. this.resize(Math.max(this.capacityAfterHeader * 2, newSize))
  168. }
  169. if (method != null) {
  170. method.call(this.header, data, this.headerSize + this.writeOffset)
  171. } else {
  172. this.header.write(data, this.headerSize + this.writeOffset, length)
  173. }
  174. var endOffset = this.headerSize + this.writeOffset + length
  175. this.header.fill(0, endOffset, endOffset + dataLength - length)
  176. this.setPayloadSize(newSize)
  177. this.writeOffset = newSize
  178. return true
  179. }
  180. Pickle.prototype.resize = function (newCapacity) {
  181. newCapacity = alignInt(newCapacity, PAYLOAD_UNIT)
  182. this.header = Buffer.concat([this.header, new Buffer(newCapacity)])
  183. this.capacityAfterHeader = newCapacity
  184. }
  185. return Pickle
  186. })()
  187. module.exports = Pickle