OOP_2022/node_modules/git/lib/git/loose_storage.js
2022-11-29 18:23:11 +01:00

173 lines
4.8 KiB
JavaScript

var util = require('util'),
fs = require('fs'),
BinaryParser = require('./binary_parser').BinaryParser,
Zlib = require('../zlib/zlib').Zlib,
RawObject = require('./raw_object').RawObject,
crypto = require('crypto'),
zlib = require('zlib');
var OBJ_TYPES = [null, "commit", "tree", "blob", "tag"];
LooseStorage = exports.LooseStorage = function(directory) {
var _directory = directory;
Object.defineProperty(this, "directory", { get: function() { return _directory; }, set: function(value) { _directory = value; }, enumerable: true});
}
LooseStorage.prototype.find = function(sha1) {
try {
sha1 = to_hex_string(sha1);
// If we don't have a valid sha
if(sha1.length != 40) return null;
// Directory path
var path = this.directory + "/" + sha1.substring(0, 2) + '/' + sha1.substring(2, 40);
return this.get_raw_object(fs.readFileSync(path));
} catch(err) {
return null;
}
}
// Read and parse the raw object
LooseStorage.prototype.get_raw_object = function(buf) {
if(buf.length < 2) throw "object file too small";
// Set up variables
var type = null;
var size = null;
var used = null;
var content = null;
if(this.is_legacy_loose_object(buf)) {
content = new Zlib.Unzip(buf).unzip();
content = Array.isArray(content) ? content[0] : content;
// Let's split the content up
var parts = content.split(/\0/)
var header = parts.shift();
content = parts.join("\0");
// if no header or content we got an invalid object header
if(header == null || content == null) throw "invalid object header";
// Split out the header
parts = header.split(/ /);
type = parts[0];
size = parts[1];
// Check that we have a valid type
if(['blob', 'tree', 'commit', 'tag'].indexOf(type) == -1 || !size.match(/^\d+$/)) throw "invalid object header";
// Convert parts
size = parseInt(size, 10);
} else {
var parts = this.unpack_object_header_gently(buf);
type = parts[0];
size = parts[1];
used = parts[2];
// Unpack content
content = new Zlib.Unzip(buf.slice(used, buf.length)).unzip();
content = Array.isArray(content) ? content[0] : content;
}
// Return a raw object
return new RawObject(type, content);
}
LooseStorage.prototype.unpack_object_header_gently = function(buf) {
var used = 0
var c = buf[used];
used = used + 1;
var type = (c >> 4) & 7;
var size = c & 15;
var shift = 4;
while(c & 0x80 != 0) {
if(buf.length <= used) throw "object file too short";
// Get next char
c = buf[used];
used = used + 1;
// Calculate size
size = size + ((c & 0x7f) << shift);
}
// Fetch the type
type = OBJ_TYPES[type];
// Check that we have a valid type
if(['blob', 'tree', 'commit', 'tag'].indexOf(type) == -1) throw "invalid loose object type";
return [type, size, used];
}
LooseStorage.prototype.is_legacy_loose_object = function(buf) {
var word = (buf[0] << 8) + buf[1];
return buf[0] == 0x78 && word % 31 == 0;
}
var to_hex_string = function(string) {
var hexString = '';
for(var index = 0; index < string.length; index++) {
var value = BinaryParser.toByte(string.substr(index, 1));
var number = value <= 15 ? "0" + value.toString(16) : value.toString(16);
hexString = hexString + number;
}
return hexString;
};
// currently, I'm using the legacy format because it's easier to do
// this function takes content and a type and writes out the loose object and returns a sha
LooseStorage.prototype.put_raw_object = function(content, type, callback) {
var self = this;
// Retrieve size of message
var size = content.length.toString();
// Verify that header is ok
LooseStorage.verify_header(type, size);
// Create header
var header = "" + type + " " + size + "\0";
var store = header + content;
// Use node crypto library to create sha1 hash
var hash = crypto.createHash("sha1");
hash.update(store);
// Return the hash digest
var sha1 = hash.digest('hex');
// Create path
var path = this.directory + "/" + sha1.substr(0, 2) + '/' + sha1.substr(2);
try {
fs.statSync(path);
} catch(err) {
// Deflate the data
var data = zlib.gunzip(store, function (err, buffer) {
if (err) {
throw err;
}
// File does not exist create the directory
fs.mkdir(self.directory + "/" + sha1.substr(0, 2), 16877, function (err) {
if (err) {
throw err;
}
fs.writeFile(path, data, 'binary', function (err) {
if (err) {
throw err;
}
callback(sha1);
});
});
});
}
}
LooseStorage.verify_header = function(type, size) {
if(["blob", "tree", "commit", "tag"].indexOf(type) == -1 || size.match(/^\d+$/) == null) {
throw "invalid object header";
}
}