2 require 'git/raw/internal/object'
3 require 'git/raw/internal/mmap'
5 module Git module Raw module Internal
6 class PackFormatError < StandardError
17 OffsetStart = FanOutCount * IdxOffsetSize
18 SHA1Start = OffsetStart + OffsetSize
19 EntrySize = OffsetSize + SHA1Size
23 file = file[0...-3] + 'pack'
27 @packfile = File.open(file)
28 @idxfile = File.open(file[0...-4]+'idx')
29 @idx = Mmap.new(@idxfile)
32 FanOutCount.times do |i|
33 pos = @idx[i * IdxOffsetSize,IdxOffsetSize].unpack('N')[0]
35 raise PackFormatError, "pack #@name has discontinuous index #{i}"
50 offset = find_object(sha1)
52 return parse_object(offset)
58 offset = @idx[pos,OffsetSize].unpack('N')[0]
59 sha1 = @idx[pos+OffsetSize,SHA1Size]
66 # unpacking the offset is quite expensive, so
67 # we avoid using #each
70 sha1 = @idx[pos,SHA1Size]
78 first, last = @offsets[slot,2]
80 mid = (first + last) / 2
81 midsha1 = @idx[SHA1Start + mid * EntrySize,SHA1Size]
82 cmp = midsha1 <=> sha1
89 pos = OffsetStart + mid * EntrySize
90 offset = @idx[pos,OffsetSize].unpack('N')[0]
99 def parse_object(offset)
100 data, type = unpack_object(offset)
101 RawObject.new(OBJ_TYPES[type], data)
103 protected :parse_object
105 def unpack_object(offset)
107 @packfile.seek(offset)
109 c = @packfile.read(1)[0]
115 c = @packfile.read(1)[0]
116 size |= ((c & 0x7f) << shift)
122 when OBJ_OFS_DELTA, OBJ_REF_DELTA
123 data, type = unpack_deltified(type, offset, obj_offset, size)
124 when OBJ_COMMIT, OBJ_TREE, OBJ_BLOB, OBJ_TAG
125 data = unpack_compressed(offset, size)
127 raise PackFormatError, "invalid type #{type}"
131 private :unpack_object
133 def unpack_deltified(type, offset, obj_offset, size)
134 @packfile.seek(offset)
135 data = @packfile.read(SHA1Size)
137 if type == OBJ_OFS_DELTA
140 base_offset = c & 0x7f
145 base_offset |= c & 0x7f
147 base_offset = obj_offset - base_offset
150 base_offset = find_object(data)
154 base, type = unpack_object(base_offset)
155 delta = unpack_compressed(offset, size)
156 [patch_delta(base, delta), type]
158 private :unpack_deltified
160 def unpack_compressed(offset, destsize)
162 @packfile.seek(offset)
163 zstr = Zlib::Inflate.new
164 while outdata.size < destsize
165 indata = @packfile.read(4096)
167 raise PackFormatError, 'error reading pack data'
169 outdata += zstr.inflate(indata)
171 if outdata.size > destsize
172 raise PackFormatError, 'error reading pack data'
177 private :unpack_compressed
179 def patch_delta(base, delta)
180 src_size, pos = patch_delta_header_size(delta, 0)
181 if src_size != base.size
182 raise PackFormatError, 'invalid delta data'
185 dest_size, pos = patch_delta_header_size(delta, pos)
187 while pos < delta.size
193 cp_off = delta[pos += 1] if c & 0x01 != 0
194 cp_off |= delta[pos += 1] << 8 if c & 0x02 != 0
195 cp_off |= delta[pos += 1] << 16 if c & 0x04 != 0
196 cp_off |= delta[pos += 1] << 24 if c & 0x08 != 0
197 cp_size = delta[pos += 1] if c & 0x10 != 0
198 cp_size |= delta[pos += 1] << 8 if c & 0x20 != 0
199 cp_size |= delta[pos += 1] << 16 if c & 0x40 != 0
200 cp_size = 0x10000 if cp_size == 0
202 dest += base[cp_off,cp_size]
207 raise PackFormatError, 'invalid delta data'
214 def patch_delta_header_size(delta, pos)
220 raise PackFormatError, 'invalid delta header'
223 size |= (c & 0x7f) << shift
225 end while c & 0x80 != 0
228 private :patch_delta_header_size
234 storage = Git::Internal::PackStorage.new(path)
235 storage.each_sha1 do |sha1|
237 puts "%s %s" % [obj.sha1.unpack('H*'), obj.type]