UNPKG

4.87 kBJavaScriptView Raw
1var util = require('util'),
2 fs = require('fs'),
3 BinaryParser = require('./binary_parser').BinaryParser,
4 Zlib = require('../zlib/zlib').Zlib,
5 RawObject = require('./raw_object').RawObject,
6 crypto = require('crypto'),
7 zlib = require('zlib');
8
9var OBJ_TYPES = [null, "commit", "tree", "blob", "tag"];
10
11LooseStorage = exports.LooseStorage = function(directory) {
12 var _directory = directory;
13
14 Object.defineProperty(this, "directory", { get: function() { return _directory; }, set: function(value) { _directory = value; }, enumerable: true});
15}
16
17LooseStorage.prototype.find = function(sha1) {
18 try {
19 sha1 = to_hex_string(sha1);
20 // If we don't have a valid sha
21 if(sha1.length != 40) return null;
22 // Directory path
23 var path = this.directory + "/" + sha1.substring(0, 2) + '/' + sha1.substring(2, 40);
24 return this.get_raw_object(fs.readFileSync(path));
25 } catch(err) {
26 return null;
27 }
28}
29
30// Read and parse the raw object
31LooseStorage.prototype.get_raw_object = function(buf) {
32 if(buf.length < 2) throw "object file too small";
33
34 // Set up variables
35 var type = null;
36 var size = null;
37 var used = null;
38 var content = null;
39
40 if(this.is_legacy_loose_object(buf)) {
41 content = new Zlib.Unzip(buf).unzip();
42 content = Array.isArray(content) ? content[0] : content;
43 // Let's split the content up
44 var parts = content.split(/\0/)
45 var header = parts.shift();
46 content = parts.join("\0");
47
48 // if no header or content we got an invalid object header
49 if(header == null || content == null) throw "invalid object header";
50
51 // Split out the header
52 parts = header.split(/ /);
53 type = parts[0];
54 size = parts[1];
55 // Check that we have a valid type
56 if(['blob', 'tree', 'commit', 'tag'].indexOf(type) == -1 || !size.match(/^\d+$/)) throw "invalid object header";
57 // Convert parts
58 size = parseInt(size, 10);
59 } else {
60 var parts = this.unpack_object_header_gently(buf);
61 type = parts[0];
62 size = parts[1];
63 used = parts[2];
64 // Unpack content
65 content = new Zlib.Unzip(buf.slice(used, buf.length)).unzip();
66 content = Array.isArray(content) ? content[0] : content;
67 }
68 // Return a raw object
69 return new RawObject(type, content);
70}
71
72LooseStorage.prototype.unpack_object_header_gently = function(buf) {
73 var used = 0
74 var c = buf[used];
75 used = used + 1;
76
77 var type = (c >> 4) & 7;
78 var size = c & 15;
79 var shift = 4;
80
81 while(c & 0x80 != 0) {
82 if(buf.length <= used) throw "object file too short";
83 // Get next char
84 c = buf[used];
85 used = used + 1;
86 // Calculate size
87 size = size + ((c & 0x7f) << shift);
88 }
89
90 // Fetch the type
91 type = OBJ_TYPES[type];
92 // Check that we have a valid type
93 if(['blob', 'tree', 'commit', 'tag'].indexOf(type) == -1) throw "invalid loose object type";
94 return [type, size, used];
95}
96
97LooseStorage.prototype.is_legacy_loose_object = function(buf) {
98 var word = (buf[0] << 8) + buf[1];
99 return buf[0] == 0x78 && word % 31 == 0;
100}
101
102var to_hex_string = function(string) {
103 var hexString = '';
104 for(var index = 0; index < string.length; index++) {
105 var value = BinaryParser.toByte(string.substr(index, 1));
106 var number = value <= 15 ? "0" + value.toString(16) : value.toString(16);
107 hexString = hexString + number;
108 }
109 return hexString;
110};
111
112// currently, I'm using the legacy format because it's easier to do
113// this function takes content and a type and writes out the loose object and returns a sha
114LooseStorage.prototype.put_raw_object = function(content, type, callback) {
115 var self = this;
116 // Retrieve size of message
117 var size = content.length.toString();
118 // Verify that header is ok
119 LooseStorage.verify_header(type, size);
120 // Create header
121 var header = "" + type + " " + size + "\0";
122 var store = header + content;
123 // Use node crypto library to create sha1 hash
124 var hash = crypto.createHash("sha1");
125 hash.update(store);
126 // Return the hash digest
127 var sha1 = hash.digest('hex');
128 // Create path
129 var path = this.directory + "/" + sha1.substr(0, 2) + '/' + sha1.substr(2);
130
131 try {
132 fs.statSync(path);
133 } catch(err) {
134 // Deflate the data
135 var data = zlib.gunzip(store, function (err, buffer) {
136 if (err) {
137 throw err;
138 }
139
140 // File does not exist create the directory
141 fs.mkdir(self.directory + "/" + sha1.substr(0, 2), 16877, function (err) {
142 if (err) {
143 throw err;
144 }
145
146 fs.writeFile(path, data, 'binary', function (err) {
147 if (err) {
148 throw err;
149 }
150
151 callback(sha1);
152 });
153 });
154 });
155 }
156}
157
158LooseStorage.verify_header = function(type, size) {
159 if(["blob", "tree", "commit", "tag"].indexOf(type) == -1 || size.match(/^\d+$/) == null) {
160 throw "invalid object header";
161 }
162}
163
164
165
166
167
168
169
170
171
172