interface.js (206 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 util = require('util');
var Result = require('./result');
var errors = require('./errors');
var AnnotatedBuffer = require('./annotated_buffer');
var errorHighlighter = require('./error_highlighter');
function makeAnnotatedBuffer(buffer, start, clear) {
// istanbul ignore if
if (start > 0) buffer = buffer.slice(start);
// istanbul ignore if
if (clear) buffer.fill(0);
return new AnnotatedBuffer(buffer);
}
function annotateError(res1, res2, start, annBuf) {
// istanbul ignore if
if (!res2.err ||
res2.offset !== res1.offset - start ||
res2.err.type !== res1.err.type ||
res2.err.message !== res1.err.message) {
res1.err = errors.UnstableRW(res1.err, {
otherMessage: res2.err && res2.err.message
});
} else {
res1.err.buffer = annBuf;
}
}
var emptyBuffer = Buffer.alloc(0);
function fromBuffer(rw, buffer, offset) {
return fromBufferResult(rw, buffer, offset).toValue();
}
function byteLength(rw, value) {
return byteLengthResult(rw, value).toValue();
}
function toBuffer(rw, value) {
return toBufferResult(rw, value).toValue();
}
function intoBuffer(rw, buffer, value) {
return intoBufferResult(rw, buffer, value).toValue();
}
// The "Tuple" methods are deprecated
/* istanbul ignore next */
function fromBufferTuple(rw, buffer, offset) {
return fromBufferResult(rw, buffer, offset).toTuple();
}
/* istanbul ignore next */
function byteLengthTuple(rw, value) {
return byteLengthResult(rw, value).toTuple();
}
/* istanbul ignore next */
function toBufferTuple(rw, value) {
return toBufferResult(rw, value).toTuple();
}
/* istanbul ignore next */
function intoBufferTuple(rw, buffer, value) {
return intoBufferResult(rw, buffer, value).toTuple();
}
function checkAllReadFrom(res, buffer) {
if (!res.err && res.offset !== buffer.length) {
res.err = errors.ShortRead({
remaining: buffer.length - res.offset,
buffer: buffer,
offset: res.offset
});
}
return res;
}
function genericResult(err, value, buffer, offset) {
if (err) {
if (err.offset === undefined) err.offset = offset;
if (err.buffer === undefined) err.buffer = buffer;
}
return new Result(err, value);
}
function fromBufferResult(rw, buffer, offset) {
var start = offset || 0;
var res = rw.readFrom(buffer, start);
res = checkAllReadFrom(res, buffer);
if (res.err) {
var annBuf = makeAnnotatedBuffer(buffer, start, false);
var res2 = rw.readFrom(annBuf, 0);
res2 = checkAllReadFrom(res2, buffer);
annotateError(res, res2, start, annBuf);
}
return genericResult(res.err, res.value, buffer, res.offset);
}
function byteLengthResult(rw, value) {
var lenRes = rw.byteLength(value);
if (lenRes.err) return new Result(lenRes.err, 0);
else return new Result(null, lenRes.length);
}
function toBufferResult(rw, value) {
var lenRes = rw.byteLength(value);
if (lenRes.err) return new Result(lenRes.err, emptyBuffer);
var length = lenRes.length;
var buffer = Buffer.alloc(length);
return intoBufferResult(rw, buffer, value);
}
function checkAllWroteOver(res, buffer) {
if (!res.err && res.offset !== buffer.length) {
res.err = errors.ShortWrite({
remaining: buffer.length - res.offset,
buffer: buffer,
offset: res.offset
});
}
return res;
}
function intoBufferResult(rw, buffer, value) {
var res = rw.writeInto(value, buffer, 0);
res = checkAllWroteOver(res, buffer);
return genericResult(res.err, buffer, buffer, res.offset);
}
// istanbul ignore next TODO
function formatError(err, options) {
options = options || {};
var name = err.name || err.constructor.name;
var str = util.format('%s: %s\n', name, err.message);
if (err.buffer && err.buffer.hexdump) {
str += err.buffer.hexdump({
colored: options.color,
boldStart: false,
highlight: options.color ? errorHighlighter(err, options) : null
});
} else if (Buffer.isBuffer(err.buffer)) {
if (options.color) {
str += formatBufferColored(err, options);
} else {
str += formatBufferUncolored(err, options);
}
}
return str;
}
// istanbul ignore next TODO
function formatBufferColored(err, options) {
// istanbul ignore else
if (!hex) {
return err.buffer.toString('hex');
}
options = options || {};
var opts = options.hexerOptions ? Object.create(options.hexerOptions) : {};
if (opts.colored === undefined) {
opts.colored = true;
}
var highlight = errorHighlighter(err, options);
opts.decorateHexen = highlight;
opts.decorateHuman = highlight;
return hex(err.buffer, opts);
}
// istanbul ignore next TODO
function formatBufferUncolored(err, options) {
// istanbul ignore else
if (!hex) {
return err.buffer.toString('hex');
}
options = options || {};
var hasOffset = !(err.offset === undefined || err.offset === null);
var hasEnd = !(err.endOffset === undefined || err.endOffset === null);
var markStart = options.markStart || '>';
var markEnd = options.markEnd || '<';
var accum = 0;
var opts = options.hexerOptions ? Object.create(options.hexerOptions) : {};
if (hasOffset) {
opts.groupSeparator = '';
if (hasEnd) {
opts.decorateHexen = decorateRangedError;
} else {
opts.decorateHexen = decorateError;
}
}
return hex(err.buffer, opts);
// TODO: suspected broken across lines, should either test and complete or
// use some sort of alternate notation such as interstitial lines
function decorateRangedError(totalOffset, screenOffset, hexen) {
var s;
if (totalOffset === err.offset) {
accum = 1;
s = markStart + hexen;
if (totalOffset === err.endOffset-1) {
s += markEnd;
accum = 0;
} else {
s = ' ' + s;
}
return s;
} else if (totalOffset === err.endOffset-1) {
s = hexen + markEnd;
while (accum-- > 0) s += ' ';
accum = 0;
return s;
} else if (accum) {
accum += 2;
return hexen;
} else {
return ' ' + hexen + ' ';
}
}
function decorateError(totalOffset, screenOffset, hexen) {
if (totalOffset === err.offset) {
return markStart + hexen + markEnd;
} else {
return ' ' + hexen + ' ';
}
}
}
module.exports.fromBuffer = fromBuffer;
module.exports.byteLength = byteLength;
module.exports.toBuffer = toBuffer;
module.exports.intoBuffer = intoBuffer;
module.exports.formatError = formatError;
module.exports.fromBufferTuple = fromBufferTuple;
module.exports.byteLengthTuple = byteLengthTuple;
module.exports.toBufferTuple = toBufferTuple;
module.exports.intoBufferTuple = intoBufferTuple;
module.exports.fromBufferResult = fromBufferResult;
module.exports.byteLengthResult = byteLengthResult;
module.exports.toBufferResult = toBufferResult;
module.exports.intoBufferResult = intoBufferResult;
module.exports.makeAnnotatedBuffer = makeAnnotatedBuffer;
module.exports.checkAllReadFrom = checkAllReadFrom;
module.exports.annotateError = annotateError;