annotated_buffer.js (272 lines of code) (raw):

// Copyright (c) 2015 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. 'use strict'; var hex = require('hexer'); var color = require('ansi-color').set; var stripColor = require('./lib/strip_color.js'); var extend = require('xtend'); var inspect = require('util').inspect; function AnnotatedBuffer(buffer) { this.buffer = buffer; this.annotations = []; } Object.defineProperty(AnnotatedBuffer.prototype, 'length', { enumerable: true, get: function getLength() { return this.buffer.length; } }); // -- strings AnnotatedBuffer.prototype.toString = function toString(encoding, start, end) { var value = this.buffer.toString(encoding, start, end); this.annotations.push({ kind: 'read', name: 'string', value: value, encoding: encoding, start: start, end: end }); return value; }; // -- bytes // istanbul ignore next AnnotatedBuffer.prototype.copy = function copy(targetBuffer, targetStart, sourceStart, sourceEnd) { var copied = this.buffer.copy(targetBuffer, targetStart, sourceStart, sourceEnd); // istanbul ignore next var start = sourceStart || 0; var end = sourceEnd || start + copied; this.annotations.push({ kind: 'read', name: 'copy', value: this.buffer.slice(start, end), start: start, end: end }); return copied; }; // istanbul ignore next AnnotatedBuffer.prototype.slice = function slice(start, end) { var value = this.buffer.slice(start, end); this.annotations.push({ kind: 'read', name: 'slice', value: value, start: start, end: end }); return value; }; // -- atom readers // istanbul ignore next AnnotatedBuffer.prototype.readInt8 = function readInt8(offset, noAssert) { var value = this.buffer.readInt8(offset, noAssert); this.annotations.push({ kind: 'read', name: 'Int8', value: value, start: offset, end: offset + 1 }); return value; }; AnnotatedBuffer.prototype.readUInt8 = function readUInt8(offset, noAssert) { var value = this.buffer.readUInt8(offset, noAssert); this.annotations.push({ kind: 'read', name: 'UInt8', value: value, start: offset, end: offset + 1 }); return value; }; // istanbul ignore next AnnotatedBuffer.prototype.readUInt16LE = function readUInt16LE(offset, noAssert) { var value = this.buffer.readUInt16LE(offset, noAssert); this.annotations.push({ kind: 'read', name: 'UInt16LE', value: value, start: offset, end: offset + 2 }); return value; }; AnnotatedBuffer.prototype.readUInt16BE = function readUInt16BE(offset, noAssert) { var value = this.buffer.readUInt16BE(offset, noAssert); this.annotations.push({ kind: 'read', name: 'UInt16BE', value: value, start: offset, end: offset + 2 }); return value; }; // istanbul ignore next AnnotatedBuffer.prototype.readUInt32LE = function readUInt32LE(offset, noAssert) { var value = this.buffer.readUInt32LE(offset, noAssert); this.annotations.push({ kind: 'read', name: 'UInt32LE', value: value, start: offset, end: offset + 4 }); return value; }; // istanbul ignore next AnnotatedBuffer.prototype.readUInt32BE = function readUInt32BE(offset, noAssert) { var value = this.buffer.readUInt32BE(offset, noAssert); this.annotations.push({ kind: 'read', name: 'UInt32BE', value: value, start: offset, end: offset + 4 }); return value; }; // istanbul ignore next AnnotatedBuffer.prototype.readInt16LE = function readInt16LE(offset, noAssert) { var value = this.buffer.readInt16LE(offset, noAssert); this.annotations.push({ kind: 'read', name: 'Int16LE', value: value, start: offset, end: offset + 2 }); return value; }; // istanbul ignore next AnnotatedBuffer.prototype.readInt16BE = function readInt16BE(offset, noAssert) { var value = this.buffer.readInt16BE(offset, noAssert); this.annotations.push({ kind: 'read', name: 'Int16BE', value: value, start: offset, end: offset + 2 }); return value; }; // istanbul ignore next AnnotatedBuffer.prototype.readInt32LE = function readInt32LE(offset, noAssert) { var value = this.buffer.readInt32LE(offset, noAssert); this.annotations.push({ kind: 'read', name: 'Int32LE', value: value, start: offset, end: offset + 4 }); return value; }; // istanbul ignore next AnnotatedBuffer.prototype.readInt32BE = function readInt32BE(offset, noAssert) { var value = this.buffer.readInt32BE(offset, noAssert); this.annotations.push({ kind: 'read', name: 'Int32BE', value: value, start: offset, end: offset + 4 }); return value; }; // istanbul ignore next AnnotatedBuffer.prototype.readFloatLE = function readFloatLE(offset, noAssert) { var value = this.buffer.readFloatLE(offset, noAssert); this.annotations.push({ kind: 'read', name: 'FloatLE', value: value, start: offset, end: offset + 4 }); return value; }; // istanbul ignore next AnnotatedBuffer.prototype.readFloatBE = function readFloatBE(offset, noAssert) { var value = this.buffer.readFloatBE(offset, noAssert); this.annotations.push({ kind: 'read', name: 'FloatBE', value: value, start: offset, end: offset + 4 }); return value; }; // istanbul ignore next AnnotatedBuffer.prototype.readDoubleLE = function readDoubleLE(offset, noAssert) { var value = this.buffer.readDoubleLE(offset, noAssert); this.annotations.push({ kind: 'read', name: 'DoubleLE', value: value, start: offset, end: offset + 8 }); return value; }; // istanbul ignore next AnnotatedBuffer.prototype.readDoubleBE = function readDoubleBE(offset, noAssert) { var value = this.buffer.readDoubleBE(offset, noAssert); this.annotations.push({ kind: 'read', name: 'DoubleBE', value: value, start: offset, end: offset + 8 }); return value; }; // -- extras // istanbul ignore next AnnotatedBuffer.prototype.hexdump = function hexdump(options) { var self = this; options = extend(options, { emptyHuman: ' ', annotateLine: annotateLine }); if (options.boldStart === undefined) options.boldStart = true; options.decorateHexen = colorRegions; options.decorateHuman = colorRegions; var colors = options.colors || ['magenta', 'cyan', 'yellow', 'green']; var colorI = 0; var annI = 0; var last = 0; return hex(this.buffer, options); function annotateLine(start, end) { var parts = []; for (var i = last; i <= annI; i++) { var ann = self.annotations[i]; if (ann && ann.start >= start && ann.start < end) { if (options.colored) { ann.color = colors[i % colors.length]; } parts.push(ann); last = i + 1; } } return ' ' + parts.map(function(part) { var desc = part.name; if (typeof part.value !== 'string' && !Buffer.isBuffer(part.value)) { desc += '(' + inspect(part.value) + ')'; } if (part.color) { desc = color(desc, part.color); } else if (part.start === part.end) { desc += '@' + part.start.toString(16); } else { desc += '@[' + part.start.toString(16) + ',' + part.end.toString(16) + ']'; } if (options.highlight) { desc = options.highlight(part.start, 0, desc); } return desc; }).join(' '); } function colorRegions(i, j, str) { var ann = self.annotations[annI]; while (ann && i >= ann.end) { ann = self.annotations[++annI]; colorI = (colorI + 1) % colors.length; } if (ann && options.colored && i >= ann.start && i < ann.end) { str = stripColor(str); str = color(str, colors[colorI]); if (i === ann.start && options.boldStart) str = color(str, 'bold'); } if (options.highlight) { str = options.highlight(i, j, str); } return str; } }; module.exports = AnnotatedBuffer;