1 | var 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 |
|
9 | var OBJ_TYPES = [null, "commit", "tree", "blob", "tag"];
|
10 |
|
11 | LooseStorage = 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 |
|
17 | LooseStorage.prototype.find = function(sha1) {
|
18 | try {
|
19 | sha1 = to_hex_string(sha1);
|
20 |
|
21 | if(sha1.length != 40) return null;
|
22 |
|
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 |
|
31 | LooseStorage.prototype.get_raw_object = function(buf) {
|
32 | if(buf.length < 2) throw "object file too small";
|
33 |
|
34 |
|
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 |
|
44 | var parts = content.split(/\0/)
|
45 | var header = parts.shift();
|
46 | content = parts.join("\0");
|
47 |
|
48 |
|
49 | if(header == null || content == null) throw "invalid object header";
|
50 |
|
51 |
|
52 | parts = header.split(/ /);
|
53 | type = parts[0];
|
54 | size = parts[1];
|
55 |
|
56 | if(['blob', 'tree', 'commit', 'tag'].indexOf(type) == -1 || !size.match(/^\d+$/)) throw "invalid object header";
|
57 |
|
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 |
|
65 | content = new Zlib.Unzip(buf.slice(used, buf.length)).unzip();
|
66 | content = Array.isArray(content) ? content[0] : content;
|
67 | }
|
68 |
|
69 | return new RawObject(type, content);
|
70 | }
|
71 |
|
72 | LooseStorage.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 |
|
84 | c = buf[used];
|
85 | used = used + 1;
|
86 |
|
87 | size = size + ((c & 0x7f) << shift);
|
88 | }
|
89 |
|
90 |
|
91 | type = OBJ_TYPES[type];
|
92 |
|
93 | if(['blob', 'tree', 'commit', 'tag'].indexOf(type) == -1) throw "invalid loose object type";
|
94 | return [type, size, used];
|
95 | }
|
96 |
|
97 | LooseStorage.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 |
|
102 | var 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 |
|
113 |
|
114 | LooseStorage.prototype.put_raw_object = function(content, type, callback) {
|
115 | var self = this;
|
116 |
|
117 | var size = content.length.toString();
|
118 |
|
119 | LooseStorage.verify_header(type, size);
|
120 |
|
121 | var header = "" + type + " " + size + "\0";
|
122 | var store = header + content;
|
123 |
|
124 | var hash = crypto.createHash("sha1");
|
125 | hash.update(store);
|
126 |
|
127 | var sha1 = hash.digest('hex');
|
128 |
|
129 | var path = this.directory + "/" + sha1.substr(0, 2) + '/' + sha1.substr(2);
|
130 |
|
131 | try {
|
132 | fs.statSync(path);
|
133 | } catch(err) {
|
134 |
|
135 | var data = zlib.gunzip(store, function (err, buffer) {
|
136 | if (err) {
|
137 | throw err;
|
138 | }
|
139 |
|
140 |
|
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 |
|
158 | LooseStorage.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 |
|