UNPKG

min-stream-pkt-line

Version:

A pkt-line codec packaged as a min-stream push-filter.

173 lines (161 loc) 4.79 kB
"use strict"; var bops = require('bops'); // Accepts array of data, optional caps object, and optional request flag // caps and request can be props on the data array or standalone arguments // Returns binary representation in git format. exports.encode = function (data, caps, request) { var string = data.join(" "); if (caps === undefined && "caps" in data) caps = data.caps; if (request === undefined && "request" in data) request = data.request; if (caps) { string += "\0" + capList(caps); } string += request ? "\0" : "\n"; return string; }; exports.capList = capList; function capList(caps) { return Object.keys(caps).map(function (key) { var value = caps[key]; if (typeof value === "string") { return key + "=" + value; } return key; }).join(" "); } // Decode a binary line // returns the data array with caps and request tagging if they are found. exports.decode = function (line) { var result = []; if (line[line.length - 1] === "\0") { result.request = true; line = line.substr(0, line.length - 1); } line = line.trim(); var parts = line.split("\0"); result.push.apply(result, parts[0].split(" ")); if (parts[1]) { result.caps = {}; parts[1].split(" ").forEach(function (cap) { var pair = cap.split("="); result.caps[pair[0]] = pair[1] ? pair[1] : true; }); } return result; }; var PACK = bops.from("PACK"); exports.deframer = function (emit) { var state = 0; var offset = 4; var length = 0; var data; var fn = function (err, item) { // Forward the EOS marker if (item === undefined) return emit(err); // Once we're in pack mode, everything goes straight through if (state === 3) return emit(null, ["pack", item]); // Otherwise parse the data using a state machine. for (var i = 0, l = item.length; i < l; i++) { var byte = item[i]; if (state === 0) { var val = fromHexChar(byte); if (val === -1) { if (byte === PACK[0]) { offset = 1; state = 2; continue; } state = -1; return emit(new SyntaxError("Not a hex char: " + String.fromCharCode(byte))); } length |= val << ((--offset) * 4); if (offset === 0) { if (length === 4) { offset = 4; emit(null, ["line", ""]); } else if (length === 0) { offset = 4; emit(null, ["line", null]); } else if (length > 4) { length -= 4; data = bops.create(length); state = 1; } else { state = -1; return emit(new SyntaxError("Invalid length: " + length)); } } } else if (state === 1) { data[offset++] = byte; if (offset === length) { offset = 4; state = 0; length = 0; if (data[0] === 1) { emit(null, ["pack", bops.subarray(data, 1)]); } else if (data[0] === 2) { emit(null, ["progress", bops.to(bops.subarray(data, 1))]); } else if (data[0] === 3) { emit(null, ["error", bops.to(bops.subarray(data, 1))]); } else { emit(null, ["line", bops.to(data)]); } } } else if (state === 2) { if (offset < 4 && byte === PACK[offset++]) { continue; } state = 3; emit(null, ["pack", bops.join([PACK, bops.subarray(item, i)])]); break; } else { emit(new Error("pkt-line decoder in invalid state")); } } }; fn.is = "min-stream-write"; return fn; }; exports.deframer.is = "min-stream-push-filter"; exports.framer = function (emit) { var packMode = false; var fn = function (err, item) { if (item === undefined) return emit(err); if (typeof item === "string") item = bops.from(item); if (packMode) return emit(null, item); if (item === null) return emit(null, bops.from("0000")); if (item === true) { packMode = true; return; } var length = item.length + 4; var buf = bops.create(length); buf[0] = toHexChar(length >>> 12); buf[1] = toHexChar((length >>> 8) & 0xf); buf[2] = toHexChar((length >>> 4) & 0xf); buf[3] = toHexChar(length & 0xf); for (var i = 4; i < length; i++) { buf[i] = item[i - 4]; } emit(null, buf); }; fn.is = "min-stream-write"; return fn; }; exports.framer.is = "min-stream-push-filter"; function fromHexChar(val) { return (val >= 0x30 && val < 0x40) ? val - 0x30 : ((val > 0x60 && val <= 0x66) ? val - 0x57 : -1); } function toHexChar(val) { return val < 0x0a ? val + 0x30 : val + 0x57; }