Permalink
Browse files

initial code commit

  • Loading branch information...
chrisdickinson committed Mar 17, 2013
1 parent 8de80a6 commit aaeff187e0272ff935c3e7a865480420f2f3de21
Showing with 333 additions and 0 deletions.
  1. +31 −0 index.js
  2. +18 −0 inflate-shim.js
  3. +1 −0 inflate.js
  4. +29 −0 package.json
  5. +181 −0 parse.js
  6. +73 −0 test/index.js
View
@@ -0,0 +1,31 @@
+module.exports = packfile
+
+var parse = require('./parse')
+
+function packfile(size, find_raw, read) {
+ return new Packfile(size, find_raw, read)
+}
+
+function Packfile(size, find_raw, read) {
+ this._size = size
+ this._find_raw = find_raw
+ this._read = read
+}
+
+var cons = Packfile
+ , proto = cons.prototype
+
+proto.constructor = cons
+
+proto.read = function(idxoffset, next_idxoffset, ready) {
+ next_idxoffset = next_idxoffset || this._size - 20
+
+ this._read(idxoffset, next_idxoffset)
+ .pipe(parse(this, idxoffset, next_idxoffset))
+ .on('error', function(err) {
+ ready(err)
+ })
+ .on('data', function(object) {
+ ready(null, object)
+ })
+}
View
@@ -0,0 +1,18 @@
+var zlib = require('zlib')
+
+module.exports = function(buf, ready) {
+ var input = new Uint8Array(buf.length)
+
+ for(var i = 0, len = input.length; i < len; ++i) {
+ input[i] = buf.get(i)
+ }
+
+ var result = zlib.inflateSync(input)
+ , output = new Buffer(result.length)
+
+ for(var i = 0, len = result.length; i < len; ++i) {
+ output.writeUInt8(result[i], i)
+ }
+
+ ready(null, output)
+}
View
@@ -0,0 +1 @@
+module.exports = require('zlib').inflate
View
@@ -0,0 +1,29 @@
+{
+ "name": "git-packfile",
+ "version": "0.0.0",
+ "description": "git packfile library for looking up and decoding git objects",
+ "main": "index.js",
+ "scripts": {
+ "test": "node test/index.js"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/chrisdickinson/git-packfile.js"
+ },
+ "keywords": [
+ "git",
+ "packfile"
+ ],
+ "author": "Chris Dickinson <chris@neversaw.us>",
+ "license": "MIT",
+ "gitHead": "8de80a60fa83c1ee944f51c9dfbb6622c5798279",
+ "dependencies": {
+ "git-apply-delta": "0.0.3",
+ "through": "~2.2.7"
+ },
+ "browser": { "./inflate.js": "./inflate-shim.js" },
+ "devDependencies": {
+ "tape": "~0.3.0",
+ "git-packidx-parser": "0.0.2"
+ }
+}
View
181 parse.js
@@ -0,0 +1,181 @@
+module.exports = parse
+
+var through = require('through')
+ , inflate = require('./inflate')
+ , apply_delta = require('git-apply-delta')
+ , Buffer = require('buffer').Buffer
+
+var _ = 0
+ , STATE_INITIAL_BYTE = _++
+ , STATE_EXPANDED_SIZE = _++
+ , STATE_PAYLOAD = _++
+
+var OFS_DELTA = 6
+ , REF_DELTA = 7
+
+function parse(packfile, idx_offset, next_idx_offset) {
+ var stream = through(write, end)
+ , state = STATE_INITIAL_BYTE
+ , payload_accum = []
+ , expanded_size = []
+ , expanded_size_value
+ , payload_size = 0
+ , type
+
+ return stream
+
+ function write(buf) {
+ var offset = 0
+ , len = buf.length
+ , byt
+
+ if(state === STATE_INITIAL_BYTE) {
+ byt = buf.readUInt8(0)
+ type = (byt & ~0x80) >> 4
+ expanded_size[expanded_size.length] = byt & 0x0F
+ ++offset
+ state = STATE_EXPANDED_SIZE
+ }
+
+ if(state === STATE_EXPANDED_SIZE && offset < len) {
+ do {
+ byt = buf.readUInt8(offset++)
+ expanded_size[expanded_size.length] = byt & ~0x80
+ } while(offset < len && byt & 0x80)
+
+ if(offset === len) {
+ return
+ }
+
+ var num_bytes = expanded_size.length
+ size = expanded_size.pop()
+ while(expanded_size.length) {
+ size |= expanded_size.pop() << (4 + (7 * (num_bytes - expanded_size.length)))
+ }
+
+ state = STATE_PAYLOAD
+ }
+
+ if(state === STATE_PAYLOAD && offset < len) {
+ if(offset === 0) {
+ payload_accum[payload_accum.length] = buf
+ payload_size += buf.length
+ } else {
+ payload_accum[payload_accum.length] = buf.slice(offset)
+ payload_size += len - offset
+ }
+ }
+ }
+
+ function end() {
+ // well super great.
+ if(state !== STATE_PAYLOAD) {
+ return stream.emit('error', new Error('unexpected end of object'))
+ }
+
+ if(type < 4) {
+ do_inflate()
+ } else if(type === OFS_DELTA) {
+ do_ofs_delta()
+ } else if(type === REF_DELTA) {
+ do_ref_delta()
+ } else {
+ return stream.emit('error', new Error('unknown object type:' + type))
+ }
+ }
+
+ function do_inflate() {
+ var buf = Buffer.concat(payload_accum, payload_size)
+ inflate(buf, function(err, data) {
+ if(err) {
+ return stream.emit('error', err)
+ }
+ stream.queue({
+ type: type
+ , data: data
+ })
+ stream.queue(null)
+ })
+ }
+
+ function do_ofs_delta() {
+ var buf = Buffer.concat(payload_accum, payload_size)
+ , _byte = buf.readUInt8(0)
+ , offset = _byte & 0x7F
+ , target_object
+ , idx = 1
+
+ while(_byte & 0x80) {
+ offset += 1
+ offset <<= 7
+ _byte = buf.readUInt8(idx++)
+ offset += _byte & 0x7F
+ }
+
+ // TODO: this doesn't take
+ // big offsets into account.
+ // should it?
+ return packfile.read(idx_offset - offset, idx_offset, onread)
+
+ // TODO: these could be done
+ // in parallel.
+
+ function onread(err, _target_object) {
+ if(err) {
+ return stream.emit('error', err)
+ }
+
+ target_object = _target_object
+
+ inflate(buf.slice(idx), ondelta)
+ }
+
+ function ondelta(err, delta) {
+ var new_data = apply_delta(delta, target_object.data)
+
+ stream.queue({
+ type: target_object.type
+ , data: new_data
+ })
+ stream.queue(null)
+ }
+ }
+
+ function do_ref_delta() {
+ var buf = Buffer.concat(payload_accum, payload_size)
+ , target_oid = buf.slice(0, 20)
+ , delta
+
+ buf = buf.slice(20)
+
+ return inflate(buf, ondelta)
+
+ // TODO: these could be done in
+ // parallel.
+
+ function ondelta(err, _delta) {
+ if(err) {
+ return stream.emit('error', err)
+ }
+
+ delta = _delta
+ packfile._find_raw(target_oid, ontarget)
+ }
+
+ function ontarget(err, target) {
+ var idx = 0
+ , out
+
+ while(target.data.readUInt8(idx++) !== 0) {
+ //
+ }
+
+ out = apply_delta(delta, target.data.slice(idx + 1))
+ stream.queue({
+ type: target.type
+ , data: out
+ })
+ stream.queue(null)
+ }
+ }
+}
View

Large diffs are not rendered by default.

Oops, something went wrong.

0 comments on commit aaeff18

Please sign in to comment.