123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265 |
- "use strict";
- var bodec = require('bodec');
- var modes = require('./modes');
- // (body) -> raw-buffer
- var encoders = exports.encoders = {
- blob: encodeBlob,
- tree: encodeTree,
- commit: encodeCommit,
- tag: encodeTag
- };
- // ({type:type, body:raw-buffer}) -> buffer
- exports.frame = frame;
- // (raw-buffer) -> body
- var decoders = exports.decoders ={
- blob: decodeBlob,
- tree: decodeTree,
- commit: decodeCommit,
- tag: decodeTag
- };
- // (buffer) -> {type:type, body:raw-buffer}
- exports.deframe = deframe;
- // Export git style path sort in case it's wanted.
- exports.treeMap = treeMap;
- exports.treeSort = treeSort;
- function encodeBlob(body) {
- if (!bodec.isBinary(body)) throw new TypeError("Blobs must be binary values");
- return body;
- }
- function treeMap(key) {
- /*jshint validthis:true*/
- var entry = this[key];
- return {
- name: key,
- mode: entry.mode,
- hash: entry.hash
- };
- }
- function treeSort(a, b) {
- var aa = (a.mode === modes.tree) ? a.name + "/" : a.name;
- var bb = (b.mode === modes.tree) ? b.name + "/" : b.name;
- return aa > bb ? 1 : aa < bb ? -1 : 0;
- }
- function encodeTree(body) {
- var tree = "";
- if (Array.isArray(body)) throw new TypeError("Tree must be in object form");
- var list = Object.keys(body).map(treeMap, body).sort(treeSort);
- for (var i = 0, l = list.length; i < l; i++) {
- var entry = list[i];
- tree += entry.mode.toString(8) + " " + bodec.encodeUtf8(entry.name) +
- "\0" + bodec.decodeHex(entry.hash);
- }
- return bodec.fromRaw(tree);
- }
- function encodeTag(body) {
- var str = "object " + body.object +
- "\ntype " + body.type +
- "\ntag " + body.tag +
- "\ntagger " + formatPerson(body.tagger) +
- "\n\n" + body.message;
- return bodec.fromUnicode(str);
- }
- function encodeCommit(body) {
- var str = "tree " + body.tree;
- for (var i = 0, l = body.parents.length; i < l; ++i) {
- str += "\nparent " + body.parents[i];
- }
- str += "\nauthor " + formatPerson(body.author) +
- "\ncommitter " + formatPerson(body.committer) +
- "\n\n" + body.message;
- return bodec.fromUnicode(str);
- }
- function formatPerson(person) {
- return safe(person.name) +
- " <" + safe(person.email) + "> " +
- formatDate(person.date);
- }
- function safe(string) {
- return string.replace(/(?:^[\.,:;<>"']+|[\0\n<>]+|[\.,:;<>"']+$)/gm, "");
- }
- function two(num) {
- return (num < 10 ? "0" : "") + num;
- }
- function formatDate(date) {
- var seconds, offset;
- if (date.seconds) {
- seconds = date.seconds;
- offset = date.offset;
- }
- // Also accept Date instances
- else {
- seconds = Math.floor(date.getTime() / 1000);
- offset = date.getTimezoneOffset();
- }
- var neg = "+";
- if (offset <= 0) offset = -offset;
- else neg = "-";
- offset = neg + two(Math.floor(offset / 60)) + two(offset % 60);
- return seconds + " " + offset;
- }
- function frame(obj) {
- var type = obj.type;
- var body = obj.body;
- if (!bodec.isBinary(body)) body = encoders[type](body);
- return bodec.join([
- bodec.fromRaw(type + " " + body.length + "\0"),
- body
- ]);
- }
- function decodeBlob(body) {
- return body;
- }
- function decodeTree(body) {
- var i = 0;
- var length = body.length;
- var start;
- var mode;
- var name;
- var hash;
- var tree = {};
- while (i < length) {
- start = i;
- i = indexOf(body, 0x20, start);
- if (i < 0) throw new SyntaxError("Missing space");
- mode = parseOct(body, start, i++);
- start = i;
- i = indexOf(body, 0x00, start);
- name = bodec.toUnicode(body, start, i++);
- hash = bodec.toHex(body, i, i += 20);
- tree[name] = {
- mode: mode,
- hash: hash
- };
- }
- return tree;
- }
- function decodeCommit(body) {
- var i = 0;
- var start;
- var key;
- var parents = [];
- var commit = {
- tree: "",
- parents: parents,
- author: "",
- committer: "",
- message: ""
- };
- while (body[i] !== 0x0a) {
- start = i;
- i = indexOf(body, 0x20, start);
- if (i < 0) throw new SyntaxError("Missing space");
- key = bodec.toRaw(body, start, i++);
- start = i;
- i = indexOf(body, 0x0a, start);
- if (i < 0) throw new SyntaxError("Missing linefeed");
- var value = bodec.toUnicode(body, start, i++);
- if (key === "parent") {
- parents.push(value);
- }
- else {
- if (key === "author" || key === "committer") {
- value = decodePerson(value);
- }
- commit[key] = value;
- }
- }
- i++;
- commit.message = bodec.toUnicode(body, i, body.length);
- return commit;
- }
- function decodeTag(body) {
- var i = 0;
- var start;
- var key;
- var tag = {};
- while (body[i] !== 0x0a) {
- start = i;
- i = indexOf(body, 0x20, start);
- if (i < 0) throw new SyntaxError("Missing space");
- key = bodec.toRaw(body, start, i++);
- start = i;
- i = indexOf(body, 0x0a, start);
- if (i < 0) throw new SyntaxError("Missing linefeed");
- var value = bodec.toUnicode(body, start, i++);
- if (key === "tagger") value = decodePerson(value);
- tag[key] = value;
- }
- i++;
- tag.message = bodec.toUnicode(body, i, body.length);
- return tag;
- }
- function decodePerson(string) {
- var match = string.match(/^([^<]*) <([^>]*)> ([^ ]*) (.*)$/);
- if (!match) throw new Error("Improperly formatted person string");
- return {
- name: match[1],
- email: match[2],
- date: {
- seconds: parseInt(match[3], 10),
- offset: parseInt(match[4], 10) / 100 * -60
- }
- };
- }
- function deframe(buffer, decode) {
- var space = indexOf(buffer, 0x20);
- if (space < 0) throw new Error("Invalid git object buffer");
- var nil = indexOf(buffer, 0x00, space);
- if (nil < 0) throw new Error("Invalid git object buffer");
- var body = bodec.slice(buffer, nil + 1);
- var size = parseDec(buffer, space + 1, nil);
- if (size !== body.length) throw new Error("Invalid body length.");
- var type = bodec.toRaw(buffer, 0, space);
- return {
- type: type,
- body: decode ? decoders[type](body) : body
- };
- }
- function indexOf(buffer, byte, i) {
- i |= 0;
- var length = buffer.length;
- for (;;i++) {
- if (i >= length) return -1;
- if (buffer[i] === byte) return i;
- }
- }
- function parseOct(buffer, start, end) {
- var val = 0;
- while (start < end) {
- val = (val << 3) + buffer[start++] - 0x30;
- }
- return val;
- }
- function parseDec(buffer, start, end) {
- var val = 0;
- while (start < end) {
- val = val * 10 + buffer[start++] - 0x30;
- }
- return val;
- }
|