Source: encoding/tlv/tlv-structure-decoder.js

  1. /**
  2. * Copyright (C) 2014-2016 Regents of the University of California.
  3. * @author: Jeff Thompson <jefft0@remap.ucla.edu>
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU Lesser General Public License as published by
  7. * the Free Software Foundation, either version 3 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU Lesser General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU Lesser General Public License
  16. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. * A copy of the GNU Lesser General Public License is in the file COPYING.
  18. */
  19. /** @ignore */
  20. var TlvDecoder = require('./tlv-decoder.js').TlvDecoder;
  21. /**
  22. * Create and initialize a TlvStructureDecoder.
  23. * @constructor
  24. */
  25. var TlvStructureDecoder = function TlvStructureDecoder()
  26. {
  27. this.gotElementEnd = false;
  28. this.offset = 0;
  29. this.state = TlvStructureDecoder.READ_TYPE;
  30. this.headerLength = 0;
  31. this.useHeaderBuffer = false;
  32. // 8 bytes is enough to hold the extended bytes in the length encoding
  33. // where it is an 8-byte number.
  34. this.headerBuffer = new Buffer(8);
  35. this.nBytesToRead = 0;
  36. };
  37. exports.TlvStructureDecoder = TlvStructureDecoder;
  38. TlvStructureDecoder.READ_TYPE = 0;
  39. TlvStructureDecoder.READ_TYPE_BYTES = 1;
  40. TlvStructureDecoder.READ_LENGTH = 2;
  41. TlvStructureDecoder.READ_LENGTH_BYTES = 3;
  42. TlvStructureDecoder.READ_VALUE_BYTES = 4;
  43. /**
  44. * Continue scanning input starting from this.offset to find the element end.
  45. * If the end of the element which started at offset 0 is found, this returns
  46. * true and getOffset() is the length of the element. Otherwise, this returns
  47. * false which means you should read more into input and call again.
  48. * @param {Buffer} input The input buffer. You have to pass in input each time
  49. * because the buffer could be reallocated.
  50. * @returns {boolean} true if found the element end, false if not.
  51. */
  52. TlvStructureDecoder.prototype.findElementEnd = function(input)
  53. {
  54. if (this.gotElementEnd)
  55. // Someone is calling when we already got the end.
  56. return true;
  57. var decoder = new TlvDecoder(input);
  58. while (true) {
  59. if (this.offset >= input.length)
  60. // All the cases assume we have some input. Return and wait for more.
  61. return false;
  62. if (this.state == TlvStructureDecoder.READ_TYPE) {
  63. var firstOctet = input[this.offset];
  64. this.offset += 1;
  65. if (firstOctet < 253)
  66. // The value is simple, so we can skip straight to reading the length.
  67. this.state = TlvStructureDecoder.READ_LENGTH;
  68. else {
  69. // Set up to skip the type bytes.
  70. if (firstOctet == 253)
  71. this.nBytesToRead = 2;
  72. else if (firstOctet == 254)
  73. this.nBytesToRead = 4;
  74. else
  75. // value == 255.
  76. this.nBytesToRead = 8;
  77. this.state = TlvStructureDecoder.READ_TYPE_BYTES;
  78. }
  79. }
  80. else if (this.state == TlvStructureDecoder.READ_TYPE_BYTES) {
  81. var nRemainingBytes = input.length - this.offset;
  82. if (nRemainingBytes < this.nBytesToRead) {
  83. // Need more.
  84. this.offset += nRemainingBytes;
  85. this.nBytesToRead -= nRemainingBytes;
  86. return false;
  87. }
  88. // Got the type bytes. Move on to read the length.
  89. this.offset += this.nBytesToRead;
  90. this.state = TlvStructureDecoder.READ_LENGTH;
  91. }
  92. else if (this.state == TlvStructureDecoder.READ_LENGTH) {
  93. var firstOctet = input[this.offset];
  94. this.offset += 1;
  95. if (firstOctet < 253) {
  96. // The value is simple, so we can skip straight to reading
  97. // the value bytes.
  98. this.nBytesToRead = firstOctet;
  99. if (this.nBytesToRead == 0) {
  100. // No value bytes to read. We're finished.
  101. this.gotElementEnd = true;
  102. return true;
  103. }
  104. this.state = TlvStructureDecoder.READ_VALUE_BYTES;
  105. }
  106. else {
  107. // We need to read the bytes in the extended encoding of
  108. // the length.
  109. if (firstOctet == 253)
  110. this.nBytesToRead = 2;
  111. else if (firstOctet == 254)
  112. this.nBytesToRead = 4;
  113. else
  114. // value == 255.
  115. this.nBytesToRead = 8;
  116. // We need to use firstOctet in the next state.
  117. this.firstOctet = firstOctet;
  118. this.state = TlvStructureDecoder.READ_LENGTH_BYTES;
  119. }
  120. }
  121. else if (this.state == TlvStructureDecoder.READ_LENGTH_BYTES) {
  122. var nRemainingBytes = input.length - this.offset;
  123. if (!this.useHeaderBuffer && nRemainingBytes >= this.nBytesToRead) {
  124. // We don't have to use the headerBuffer. Set nBytesToRead.
  125. decoder.seek(this.offset);
  126. this.nBytesToRead = decoder.readExtendedVarNumber(this.firstOctet);
  127. // Update this.offset to the decoder's offset after reading.
  128. this.offset = decoder.getOffset();
  129. }
  130. else {
  131. this.useHeaderBuffer = true;
  132. var nNeededBytes = this.nBytesToRead - this.headerLength;
  133. if (nNeededBytes > nRemainingBytes) {
  134. // We can't get all of the header bytes from this input.
  135. // Save in headerBuffer.
  136. if (this.headerLength + nRemainingBytes > this.headerBuffer.length)
  137. // We don't expect this to happen.
  138. throw new Error
  139. ("Cannot store more header bytes than the size of headerBuffer");
  140. input.slice(this.offset, this.offset + nRemainingBytes).copy
  141. (this.headerBuffer, this.headerLength);
  142. this.offset += nRemainingBytes;
  143. this.headerLength += nRemainingBytes;
  144. return false;
  145. }
  146. // Copy the remaining bytes into headerBuffer, read the
  147. // length and set nBytesToRead.
  148. if (this.headerLength + nNeededBytes > this.headerBuffer.length)
  149. // We don't expect this to happen.
  150. throw new Error
  151. ("Cannot store more header bytes than the size of headerBuffer");
  152. input.slice(this.offset, this.offset + nNeededBytes).copy
  153. (this.headerBuffer, this.headerLength);
  154. this.offset += nNeededBytes;
  155. // Use a local decoder just for the headerBuffer.
  156. var bufferDecoder = new TlvDecoder(this.headerBuffer);
  157. // Replace nBytesToRead with the length of the value.
  158. this.nBytesToRead = bufferDecoder.readExtendedVarNumber(this.firstOctet);
  159. }
  160. if (this.nBytesToRead == 0) {
  161. // No value bytes to read. We're finished.
  162. this.gotElementEnd = true;
  163. return true;
  164. }
  165. // Get ready to read the value bytes.
  166. this.state = TlvStructureDecoder.READ_VALUE_BYTES;
  167. }
  168. else if (this.state == TlvStructureDecoder.READ_VALUE_BYTES) {
  169. nRemainingBytes = input.length - this.offset;
  170. if (nRemainingBytes < this.nBytesToRead) {
  171. // Need more.
  172. this.offset += nRemainingBytes;
  173. this.nBytesToRead -= nRemainingBytes;
  174. return false;
  175. }
  176. // Got the bytes. We're finished.
  177. this.offset += this.nBytesToRead;
  178. this.gotElementEnd = true;
  179. return true;
  180. }
  181. else
  182. // We don't expect this to happen.
  183. throw new Error("findElementEnd: unrecognized state");
  184. }
  185. };
  186. /**
  187. * Get the current offset into the input buffer.
  188. * @returns {number} The offset.
  189. */
  190. TlvStructureDecoder.prototype.getOffset = function()
  191. {
  192. return this.offset;
  193. };
  194. /**
  195. * Set the offset into the input, used for the next read.
  196. * @param {number} offset The new offset.
  197. */
  198. TlvStructureDecoder.prototype.seek = function(offset)
  199. {
  200. this.offset = offset;
  201. };