gollum_rails 1.4.0.rc1 → 1.4.0.rc2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +6 -6
  3. data/Gemfile.lock +2 -1
  4. data/HISTORY.md +91 -91
  5. data/LICENSE +661 -661
  6. data/README.md +74 -74
  7. data/Rakefile +170 -170
  8. data/gollum_rails.gemspec +1 -1
  9. data/lib/core_ext/string.rb +7 -10
  10. data/lib/generators/gollum_rails/install/install_generator.rb +27 -27
  11. data/lib/generators/gollum_rails/install/templates/gollum_initializer.rb +22 -22
  12. data/lib/generators/gollum_rails/language/language_generator.rb +81 -81
  13. data/lib/generators/gollum_rails/model/model_generator.rb +51 -51
  14. data/lib/generators/gollum_rails/model/templates/model_template.erb +13 -13
  15. data/lib/gollum_rails.rb +1 -1
  16. data/lib/gollum_rails/adapters/activemodel.rb +36 -36
  17. data/lib/gollum_rails/adapters/activemodel/boolean.rb +15 -15
  18. data/lib/gollum_rails/adapters/activemodel/error.rb +27 -27
  19. data/lib/gollum_rails/adapters/activemodel/naming.rb +42 -42
  20. data/lib/gollum_rails/adapters/gollum.rb +54 -54
  21. data/lib/gollum_rails/adapters/gollum/.gitkeep +0 -0
  22. data/lib/gollum_rails/adapters/gollum/error.rb +19 -19
  23. data/lib/gollum_rails/adapters/gollum/page.rb +177 -177
  24. data/lib/gollum_rails/adapters/gollum/wiki.rb +42 -42
  25. data/lib/gollum_rails/modules/hash.rb +33 -33
  26. data/lib/gollum_rails/modules/loader.rb +5 -5
  27. data/lib/gollum_rails/page.rb +266 -266
  28. data/lib/gollum_rails/setup.rb +81 -81
  29. data/lib/grit/git-ruby/internal/pack.rb +397 -397
  30. data/spec/gollum_rails/adapters/activemodel/error_spec.rb +11 -11
  31. data/spec/gollum_rails/adapters/activemodel/naming_spec.rb +27 -27
  32. data/spec/gollum_rails/adapters/activemodel/validation_unused.rb +102 -102
  33. data/spec/gollum_rails/adapters/gollum/committer_spec.rb +0 -0
  34. data/spec/gollum_rails/adapters/gollum/connector_spec.rb +15 -15
  35. data/spec/gollum_rails/adapters/gollum/error_spec.rb +7 -7
  36. data/spec/gollum_rails/adapters/gollum/page_spec.rb +89 -89
  37. data/spec/gollum_rails/adapters/gollum/wiki_spec.rb +27 -27
  38. data/spec/gollum_rails/modules/hash_spec.rb +31 -31
  39. data/spec/gollum_rails/page_spec.rb +207 -207
  40. data/spec/gollum_rails/respository_spec.rb +0 -0
  41. data/spec/gollum_rails/setup_spec.rb +44 -44
  42. data/spec/gollum_rails/wiki_spec.rb +0 -0
  43. data/spec/spec.opts +3 -3
  44. data/spec/spec_helper.rb +43 -43
  45. metadata +2 -2
@@ -1,81 +1,81 @@
1
- module GollumRails
2
-
3
- # Setup functionality for Rails initializer
4
- #
5
- # will be generated by Rails generator: `rails g gollum_rails:install`
6
- #
7
- # manually:
8
- #
9
- # GollumRails::Setup.build do |config|
10
- # config.repository = '<path_to_your_repository>'
11
- # config.wiki = :default
12
- # config.startup
13
- # end
14
- #
15
- # TODO:
16
- # * more options
17
- #
18
- # FIXME:
19
- # currently nothing
20
- #
21
- class Setup
22
-
23
- # static baby
24
- class << self
25
-
26
-
27
- # Gets / Sets the repository
28
- attr_accessor :repository
29
-
30
- # Startup action for building wiki components
31
- #
32
- # Returns true or throws an exception if the path is invalid
33
- def startup=(action)
34
- if action
35
- Adapters::Gollum::Connector.enabled = true
36
- if @repository == :application
37
- initialize_wiki Rails.application.config.wiki_repository
38
- else
39
- initialize_wiki @repository
40
- end
41
- end
42
-
43
- end
44
-
45
- # defines block builder for Rails initializer.
46
- # executes public methods inside own class
47
- #
48
- def build(&block)
49
- block.call self
50
- end
51
-
52
- #######
53
- private
54
- #######
55
-
56
- # Checks if provided path is present and valid
57
- #
58
- # Example
59
- # path_valid? '/'
60
- # # =>true
61
- #
62
- # path_valid? nil
63
- # # =>false
64
- def path_valid?(path)
65
- return !(path.nil? || path.empty? || ! path.is_a?(String))
66
- end
67
-
68
- def initialize_wiki(path)
69
- if path_valid? path
70
- repository = Grit::Repo.new path.to_s
71
- GollumRails::Adapters::Gollum::Wiki.new repository
72
- true
73
- else
74
- raise GollumInternalError, 'no repository path specified'
75
- end
76
-
77
- end
78
-
79
- end
80
- end
81
- end
1
+ module GollumRails
2
+
3
+ # Setup functionality for Rails initializer
4
+ #
5
+ # will be generated by Rails generator: `rails g gollum_rails:install`
6
+ #
7
+ # manually:
8
+ #
9
+ # GollumRails::Setup.build do |config|
10
+ # config.repository = '<path_to_your_repository>'
11
+ # config.wiki = :default
12
+ # config.startup
13
+ # end
14
+ #
15
+ # TODO:
16
+ # * more options
17
+ #
18
+ # FIXME:
19
+ # currently nothing
20
+ #
21
+ class Setup
22
+
23
+ # static baby
24
+ class << self
25
+
26
+
27
+ # Gets / Sets the repository
28
+ attr_accessor :repository
29
+
30
+ # Startup action for building wiki components
31
+ #
32
+ # Returns true or throws an exception if the path is invalid
33
+ def startup=(action)
34
+ if action
35
+ Adapters::Gollum::Connector.enabled = true
36
+ if @repository == :application
37
+ initialize_wiki Rails.application.config.wiki_repository
38
+ else
39
+ initialize_wiki @repository
40
+ end
41
+ end
42
+
43
+ end
44
+
45
+ # defines block builder for Rails initializer.
46
+ # executes public methods inside own class
47
+ #
48
+ def build(&block)
49
+ block.call self
50
+ end
51
+
52
+ #######
53
+ private
54
+ #######
55
+
56
+ # Checks if provided path is present and valid
57
+ #
58
+ # Example
59
+ # path_valid? '/'
60
+ # # =>true
61
+ #
62
+ # path_valid? nil
63
+ # # =>false
64
+ def path_valid?(path)
65
+ return !(path.nil? || path.empty? || ! path.is_a?(String))
66
+ end
67
+
68
+ def initialize_wiki(path)
69
+ if path_valid? path
70
+ repository = Grit::Repo.new path.to_s
71
+ GollumRails::Adapters::Gollum::Wiki.new repository
72
+ true
73
+ else
74
+ raise GollumInternalError, 'no repository path specified'
75
+ end
76
+
77
+ end
78
+
79
+ end
80
+ end
81
+ end
@@ -1,397 +1,397 @@
1
- #
2
- # converted from the gitrb project
3
- #
4
- # authors:
5
- # Matthias Lederhofer <matled@gmx.net>
6
- # Simon 'corecode' Schubert <corecode@fs.ei.tum.de>
7
- # Scott Chacon <schacon@gmail.com>
8
- #
9
- # provides native ruby access to git objects and pack files
10
- #
11
-
12
- require 'zlib'
13
- require 'grit/git-ruby/internal/raw_object'
14
- require 'grit/git-ruby/internal/file_window'
15
-
16
- PACK_IDX_SIGNATURE = [0xFF, 0x74, 0x4F, 0x63]
17
-
18
- module Grit
19
- module GitRuby
20
- module Internal
21
- class PackFormatError < StandardError
22
- end
23
-
24
- class PackStorage
25
- OBJ_OFS_DELTA = 6
26
- OBJ_REF_DELTA = 7
27
-
28
- FanOutCount = 256
29
- SHA1Size = 20
30
- IdxOffsetSize = 4
31
- OffsetSize = 4
32
- ExtendedOffsetSize = 8
33
- CrcSize = 4
34
- OffsetStart = FanOutCount * IdxOffsetSize
35
- SHA1Start = OffsetStart + OffsetSize
36
- EntrySize = OffsetSize + SHA1Size
37
- EntrySizeV2 = SHA1Size + CrcSize + OffsetSize
38
-
39
- def initialize(file)
40
- if file =~ /\.idx$/
41
- file = file[0...-3] + 'pack'
42
- end
43
- @name = file
44
- @cache = {}
45
- init_pack
46
- end
47
-
48
- def with_idx(index_file = nil)
49
- index_file ||= @name[0...-4] + 'idx'
50
-
51
- begin
52
- idxfile = File.open(index_file, 'rb')
53
- rescue Errno::ENOENT => boom
54
- # file went away. bail out without yielding.
55
- return
56
- end
57
-
58
- # read header
59
- sig = idxfile.read(4).unpack("C*")
60
- ver = idxfile.read(4).unpack("N")[0]
61
-
62
- if sig == PACK_IDX_SIGNATURE
63
- if(ver != 2)
64
- raise PackFormatError, "pack #@name has unknown pack file version #{ver}"
65
- end
66
- @version = 2
67
- else
68
- @version = 1
69
- end
70
-
71
- idx = FileWindow.new(idxfile, @version)
72
- yield idx
73
- idx.unmap
74
- ensure
75
- idxfile.close if idxfile
76
- end
77
-
78
- def with_packfile
79
- begin
80
- packfile = File.open(@name, 'rb')
81
- rescue Errno::ENOENT
82
- # file went away. bail out without yielding.
83
- return
84
- end
85
- yield packfile
86
- ensure
87
- packfile.close if packfile
88
- end
89
-
90
- def cache_objects
91
- @cache = {}
92
- with_packfile do |packfile|
93
- each_entry do |sha, offset|
94
- data, type = unpack_object(packfile, offset, {:caching => true})
95
- if data
96
- @cache[sha] = RawObject.new(OBJ_TYPES[type], data)
97
- end
98
- end
99
- end
100
- end
101
-
102
- def name
103
- @name
104
- end
105
-
106
- def close
107
- # shouldnt be anything open now
108
- end
109
-
110
- # given an index file, list out the shas that it's packfile contains
111
- def get_shas
112
- shas = []
113
- each_sha1 { |sha| shas << sha.unpack("H*")[0] }
114
- shas
115
- end
116
-
117
- def [](sha1)
118
- if obj = @cache[sha1]
119
- return obj
120
- end
121
-
122
- offset = find_object(sha1)
123
- return nil if !offset
124
- @cache[sha1] = obj = parse_object(offset)
125
- return obj
126
- end
127
-
128
- def init_pack
129
- with_idx do |idx|
130
- @offsets = [0]
131
- FanOutCount.times do |i|
132
- pos = idx[i * IdxOffsetSize,IdxOffsetSize].unpack('N')[0]
133
- if pos < @offsets[i]
134
- raise PackFormatError, "pack #@name has discontinuous index #{i}"
135
- end
136
- @offsets << pos
137
- end
138
- @size = @offsets[-1]
139
- end
140
- end
141
-
142
- def each_entry
143
- with_idx do |idx|
144
- if @version == 2
145
- data = read_data_v2(idx)
146
- data.each do |sha1, crc, offset|
147
- yield sha1, offset
148
- end
149
- else
150
- pos = OffsetStart
151
- @size.times do
152
- offset = idx[pos,OffsetSize].unpack('N')[0]
153
- sha1 = idx[pos+OffsetSize,SHA1Size]
154
- pos += EntrySize
155
- yield sha1, offset
156
- end
157
- end
158
- end
159
- end
160
-
161
- def read_data_v2(idx)
162
- data = []
163
- pos = OffsetStart
164
- @size.times do |i|
165
- data[i] = [idx[pos,SHA1Size], 0, 0]
166
- pos += SHA1Size
167
- end
168
- @size.times do |i|
169
- crc = idx[pos,CrcSize]
170
- data[i][1] = crc
171
- pos += CrcSize
172
- end
173
- @size.times do |i|
174
- offset = idx[pos,OffsetSize].unpack('N')[0]
175
- data[i][2] = offset
176
- pos += OffsetSize
177
- end
178
- data
179
- end
180
- private :read_data_v2
181
-
182
- def each_sha1
183
- with_idx do |idx|
184
- if @version == 2
185
- data = read_data_v2(idx)
186
- data.each do |sha1, crc, offset|
187
- yield sha1
188
- end
189
- else
190
- pos = SHA1Start
191
- @size.times do
192
- sha1 = idx[pos,SHA1Size]
193
- pos += EntrySize
194
- yield sha1
195
- end
196
- end
197
- end
198
- end
199
-
200
- def find_object_in_index(idx, sha1)
201
- slot = sha1.getord(0)
202
- return nil if !slot
203
- first, last = @offsets[slot,2]
204
- while first < last
205
- mid = (first + last) / 2
206
- if @version == 2
207
- midsha1 = idx[OffsetStart + (mid * SHA1Size), SHA1Size]
208
- cmp = midsha1 <=> sha1
209
-
210
- if cmp < 0
211
- first = mid + 1
212
- elsif cmp > 0
213
- last = mid
214
- else
215
- pos = OffsetStart + (@size * (SHA1Size + CrcSize)) + (mid * OffsetSize)
216
- offset = idx[pos, OffsetSize].unpack('N')[0]
217
- if offset & 0x80000000 > 0
218
- offset &= 0x7fffffff
219
- pos = OffsetStart + (@size * (SHA1Size + CrcSize + OffsetSize)) + (offset * ExtendedOffsetSize)
220
- words = idx[pos, ExtendedOffsetSize].unpack('NN')
221
- offset = (words[0] << 32) | words[1]
222
- end
223
- return offset
224
- end
225
- else
226
- midsha1 = idx[SHA1Start + mid * EntrySize,SHA1Size]
227
- cmp = midsha1 <=> sha1
228
-
229
- if cmp < 0
230
- first = mid + 1
231
- elsif cmp > 0
232
- last = mid
233
- else
234
- pos = OffsetStart + mid * EntrySize
235
- offset = idx[pos,OffsetSize].unpack('N')[0]
236
- return offset
237
- end
238
- end
239
- end
240
- nil
241
- end
242
-
243
- def find_object(sha1)
244
- obj = nil
245
- with_idx do |idx|
246
- obj = find_object_in_index(idx, sha1)
247
- end
248
- obj
249
- end
250
- private :find_object
251
-
252
- def parse_object(offset)
253
- obj = nil
254
- with_packfile do |packfile|
255
- data, type = unpack_object(packfile, offset)
256
- obj = RawObject.new(OBJ_TYPES[type], data)
257
- end
258
- obj
259
- end
260
- protected :parse_object
261
-
262
- def unpack_object(packfile, offset, options = {})
263
- obj_offset = offset
264
- packfile.seek(offset)
265
-
266
- c = packfile.read(1).getord(0)
267
- size = c & 0xf
268
- type = (c >> 4) & 7
269
- shift = 4
270
- offset += 1
271
- while c & 0x80 != 0
272
- c = packfile.read(1).getord(0)
273
- size |= ((c & 0x7f) << shift)
274
- shift += 7
275
- offset += 1
276
- end
277
-
278
- return [false, false] if !(type == OBJ_COMMIT || type == OBJ_TREE) && options[:caching]
279
-
280
- case type
281
- when OBJ_OFS_DELTA, OBJ_REF_DELTA
282
- data, type = unpack_deltified(packfile, type, offset, obj_offset, size, options)
283
- #puts type
284
- when OBJ_COMMIT, OBJ_TREE, OBJ_BLOB, OBJ_TAG
285
- data = unpack_compressed(offset, size)
286
- else
287
- raise PackFormatError, "invalid type #{type}"
288
- end
289
- [data, type]
290
- end
291
- private :unpack_object
292
-
293
- def unpack_deltified(packfile, type, offset, obj_offset, size, options = {})
294
- packfile.seek(offset)
295
- data = packfile.read(SHA1Size)
296
-
297
- if type == OBJ_OFS_DELTA
298
- i = 0
299
- c = data.getord(i)
300
- base_offset = c & 0x7f
301
- while c & 0x80 != 0
302
- c = data.getord(i += 1)
303
- base_offset += 1
304
- base_offset <<= 7
305
- base_offset |= c & 0x7f
306
- end
307
- base_offset = obj_offset - base_offset
308
- offset += i + 1
309
- else
310
- base_offset = find_object(data)
311
- offset += SHA1Size
312
- end
313
-
314
- base, type = unpack_object(packfile, base_offset)
315
-
316
- return [false, false] if !(type == OBJ_COMMIT || type == OBJ_TREE) && options[:caching]
317
-
318
- delta = unpack_compressed(offset, size)
319
- [patch_delta(base, delta), type]
320
- end
321
- private :unpack_deltified
322
-
323
- def unpack_compressed(offset, destsize)
324
- outdata = ""
325
- with_packfile do |packfile|
326
- packfile.seek(offset)
327
- zstr = Zlib::Inflate.new
328
- while outdata.size < destsize
329
- indata = packfile.read(4096)
330
- if indata.size == 0
331
- raise PackFormatError, 'error reading pack data'
332
- end
333
- outdata << zstr.inflate(indata)
334
- end
335
- if outdata.size > destsize
336
- raise PackFormatError, 'error reading pack data'
337
- end
338
- zstr.close
339
- end
340
- outdata
341
- end
342
- private :unpack_compressed
343
-
344
- def patch_delta(base, delta)
345
- src_size, pos = patch_delta_header_size(delta, 0)
346
- if src_size != base.size
347
- raise PackFormatError, 'invalid delta data'
348
- end
349
-
350
- dest_size, pos = patch_delta_header_size(delta, pos)
351
- dest = ""
352
- while pos < delta.size
353
- c = delta.getord(pos)
354
- pos += 1
355
- if c & 0x80 != 0
356
- pos -= 1
357
- cp_off = cp_size = 0
358
- cp_off = delta.getord(pos += 1) if c & 0x01 != 0
359
- cp_off |= delta.getord(pos += 1) << 8 if c & 0x02 != 0
360
- cp_off |= delta.getord(pos += 1) << 16 if c & 0x04 != 0
361
- cp_off |= delta.getord(pos += 1) << 24 if c & 0x08 != 0
362
- cp_size = delta.getord(pos += 1) if c & 0x10 != 0
363
- cp_size |= delta.getord(pos += 1) << 8 if c & 0x20 != 0
364
- cp_size |= delta.getord(pos += 1) << 16 if c & 0x40 != 0
365
- cp_size = 0x10000 if cp_size == 0
366
- pos += 1
367
- dest << base[cp_off,cp_size]
368
- elsif c != 0
369
- dest << delta[pos,c]
370
- pos += c
371
- else
372
- raise PackFormatError, 'invalid delta data'
373
- end
374
- end
375
- dest
376
- end
377
- private :patch_delta
378
-
379
- def patch_delta_header_size(delta, pos)
380
- size = 0
381
- shift = 0
382
- begin
383
- c = delta.getord(pos)
384
- if c == nil
385
- raise PackFormatError, 'invalid delta header'
386
- end
387
- pos += 1
388
- size |= (c & 0x7f) << shift
389
- shift += 7
390
- end while c & 0x80 != 0
391
- [size, pos]
392
- end
393
- private :patch_delta_header_size
394
- end
395
- end
396
- end
397
- end
1
+ #
2
+ # converted from the gitrb project
3
+ #
4
+ # authors:
5
+ # Matthias Lederhofer <matled@gmx.net>
6
+ # Simon 'corecode' Schubert <corecode@fs.ei.tum.de>
7
+ # Scott Chacon <schacon@gmail.com>
8
+ #
9
+ # provides native ruby access to git objects and pack files
10
+ #
11
+
12
+ require 'zlib'
13
+ require 'grit/git-ruby/internal/raw_object'
14
+ require 'grit/git-ruby/internal/file_window'
15
+
16
+ PACK_IDX_SIGNATURE = [0xFF, 0x74, 0x4F, 0x63]
17
+
18
+ module Grit
19
+ module GitRuby
20
+ module Internal
21
+ class PackFormatError < StandardError
22
+ end
23
+
24
+ class PackStorage
25
+ OBJ_OFS_DELTA = 6
26
+ OBJ_REF_DELTA = 7
27
+
28
+ FanOutCount = 256
29
+ SHA1Size = 20
30
+ IdxOffsetSize = 4
31
+ OffsetSize = 4
32
+ ExtendedOffsetSize = 8
33
+ CrcSize = 4
34
+ OffsetStart = FanOutCount * IdxOffsetSize
35
+ SHA1Start = OffsetStart + OffsetSize
36
+ EntrySize = OffsetSize + SHA1Size
37
+ EntrySizeV2 = SHA1Size + CrcSize + OffsetSize
38
+
39
+ def initialize(file)
40
+ if file =~ /\.idx$/
41
+ file = file[0...-3] + 'pack'
42
+ end
43
+ @name = file
44
+ @cache = {}
45
+ init_pack
46
+ end
47
+
48
+ def with_idx(index_file = nil)
49
+ index_file ||= @name[0...-4] + 'idx'
50
+
51
+ begin
52
+ idxfile = File.open(index_file, 'rb')
53
+ rescue Errno::ENOENT => boom
54
+ # file went away. bail out without yielding.
55
+ return
56
+ end
57
+
58
+ # read header
59
+ sig = idxfile.read(4).unpack("C*")
60
+ ver = idxfile.read(4).unpack("N")[0]
61
+
62
+ if sig == PACK_IDX_SIGNATURE
63
+ if(ver != 2)
64
+ raise PackFormatError, "pack #@name has unknown pack file version #{ver}"
65
+ end
66
+ @version = 2
67
+ else
68
+ @version = 1
69
+ end
70
+
71
+ idx = FileWindow.new(idxfile, @version)
72
+ yield idx
73
+ idx.unmap
74
+ ensure
75
+ idxfile.close if idxfile
76
+ end
77
+
78
+ def with_packfile
79
+ begin
80
+ packfile = File.open(@name, 'rb')
81
+ rescue Errno::ENOENT
82
+ # file went away. bail out without yielding.
83
+ return
84
+ end
85
+ yield packfile
86
+ ensure
87
+ packfile.close if packfile
88
+ end
89
+
90
+ def cache_objects
91
+ @cache = {}
92
+ with_packfile do |packfile|
93
+ each_entry do |sha, offset|
94
+ data, type = unpack_object(packfile, offset, {:caching => true})
95
+ if data
96
+ @cache[sha] = RawObject.new(OBJ_TYPES[type], data)
97
+ end
98
+ end
99
+ end
100
+ end
101
+
102
+ def name
103
+ @name
104
+ end
105
+
106
+ def close
107
+ # shouldnt be anything open now
108
+ end
109
+
110
+ # given an index file, list out the shas that it's packfile contains
111
+ def get_shas
112
+ shas = []
113
+ each_sha1 { |sha| shas << sha.unpack("H*")[0] }
114
+ shas
115
+ end
116
+
117
+ def [](sha1)
118
+ if obj = @cache[sha1]
119
+ return obj
120
+ end
121
+
122
+ offset = find_object(sha1)
123
+ return nil if !offset
124
+ @cache[sha1] = obj = parse_object(offset)
125
+ return obj
126
+ end
127
+
128
+ def init_pack
129
+ with_idx do |idx|
130
+ @offsets = [0]
131
+ FanOutCount.times do |i|
132
+ pos = idx[i * IdxOffsetSize,IdxOffsetSize].unpack('N')[0]
133
+ if pos < @offsets[i]
134
+ raise PackFormatError, "pack #@name has discontinuous index #{i}"
135
+ end
136
+ @offsets << pos
137
+ end
138
+ @size = @offsets[-1]
139
+ end
140
+ end
141
+
142
+ def each_entry
143
+ with_idx do |idx|
144
+ if @version == 2
145
+ data = read_data_v2(idx)
146
+ data.each do |sha1, crc, offset|
147
+ yield sha1, offset
148
+ end
149
+ else
150
+ pos = OffsetStart
151
+ @size.times do
152
+ offset = idx[pos,OffsetSize].unpack('N')[0]
153
+ sha1 = idx[pos+OffsetSize,SHA1Size]
154
+ pos += EntrySize
155
+ yield sha1, offset
156
+ end
157
+ end
158
+ end
159
+ end
160
+
161
+ def read_data_v2(idx)
162
+ data = []
163
+ pos = OffsetStart
164
+ @size.times do |i|
165
+ data[i] = [idx[pos,SHA1Size], 0, 0]
166
+ pos += SHA1Size
167
+ end
168
+ @size.times do |i|
169
+ crc = idx[pos,CrcSize]
170
+ data[i][1] = crc
171
+ pos += CrcSize
172
+ end
173
+ @size.times do |i|
174
+ offset = idx[pos,OffsetSize].unpack('N')[0]
175
+ data[i][2] = offset
176
+ pos += OffsetSize
177
+ end
178
+ data
179
+ end
180
+ private :read_data_v2
181
+
182
+ def each_sha1
183
+ with_idx do |idx|
184
+ if @version == 2
185
+ data = read_data_v2(idx)
186
+ data.each do |sha1, crc, offset|
187
+ yield sha1
188
+ end
189
+ else
190
+ pos = SHA1Start
191
+ @size.times do
192
+ sha1 = idx[pos,SHA1Size]
193
+ pos += EntrySize
194
+ yield sha1
195
+ end
196
+ end
197
+ end
198
+ end
199
+
200
+ def find_object_in_index(idx, sha1)
201
+ slot = sha1.getord(0)
202
+ return nil if !slot
203
+ first, last = @offsets[slot,2]
204
+ while first < last
205
+ mid = (first + last) / 2
206
+ if @version == 2
207
+ midsha1 = idx[OffsetStart + (mid * SHA1Size), SHA1Size]
208
+ cmp = midsha1 <=> sha1
209
+
210
+ if cmp < 0
211
+ first = mid + 1
212
+ elsif cmp > 0
213
+ last = mid
214
+ else
215
+ pos = OffsetStart + (@size * (SHA1Size + CrcSize)) + (mid * OffsetSize)
216
+ offset = idx[pos, OffsetSize].unpack('N')[0]
217
+ if offset & 0x80000000 > 0
218
+ offset &= 0x7fffffff
219
+ pos = OffsetStart + (@size * (SHA1Size + CrcSize + OffsetSize)) + (offset * ExtendedOffsetSize)
220
+ words = idx[pos, ExtendedOffsetSize].unpack('NN')
221
+ offset = (words[0] << 32) | words[1]
222
+ end
223
+ return offset
224
+ end
225
+ else
226
+ midsha1 = idx[SHA1Start + mid * EntrySize,SHA1Size]
227
+ cmp = midsha1 <=> sha1
228
+
229
+ if cmp < 0
230
+ first = mid + 1
231
+ elsif cmp > 0
232
+ last = mid
233
+ else
234
+ pos = OffsetStart + mid * EntrySize
235
+ offset = idx[pos,OffsetSize].unpack('N')[0]
236
+ return offset
237
+ end
238
+ end
239
+ end
240
+ nil
241
+ end
242
+
243
+ def find_object(sha1)
244
+ obj = nil
245
+ with_idx do |idx|
246
+ obj = find_object_in_index(idx, sha1)
247
+ end
248
+ obj
249
+ end
250
+ private :find_object
251
+
252
+ def parse_object(offset)
253
+ obj = nil
254
+ with_packfile do |packfile|
255
+ data, type = unpack_object(packfile, offset)
256
+ obj = RawObject.new(OBJ_TYPES[type], data)
257
+ end
258
+ obj
259
+ end
260
+ protected :parse_object
261
+
262
+ def unpack_object(packfile, offset, options = {})
263
+ obj_offset = offset
264
+ packfile.seek(offset)
265
+
266
+ c = packfile.read(1).getord(0)
267
+ size = c & 0xf
268
+ type = (c >> 4) & 7
269
+ shift = 4
270
+ offset += 1
271
+ while c & 0x80 != 0
272
+ c = packfile.read(1).getord(0)
273
+ size |= ((c & 0x7f) << shift)
274
+ shift += 7
275
+ offset += 1
276
+ end
277
+
278
+ return [false, false] if !(type == OBJ_COMMIT || type == OBJ_TREE) && options[:caching]
279
+
280
+ case type
281
+ when OBJ_OFS_DELTA, OBJ_REF_DELTA
282
+ data, type = unpack_deltified(packfile, type, offset, obj_offset, size, options)
283
+ #puts type
284
+ when OBJ_COMMIT, OBJ_TREE, OBJ_BLOB, OBJ_TAG
285
+ data = unpack_compressed(offset, size)
286
+ else
287
+ raise PackFormatError, "invalid type #{type}"
288
+ end
289
+ [data, type]
290
+ end
291
+ private :unpack_object
292
+
293
+ def unpack_deltified(packfile, type, offset, obj_offset, size, options = {})
294
+ packfile.seek(offset)
295
+ data = packfile.read(SHA1Size)
296
+
297
+ if type == OBJ_OFS_DELTA
298
+ i = 0
299
+ c = data.getord(i)
300
+ base_offset = c & 0x7f
301
+ while c & 0x80 != 0
302
+ c = data.getord(i += 1)
303
+ base_offset += 1
304
+ base_offset <<= 7
305
+ base_offset |= c & 0x7f
306
+ end
307
+ base_offset = obj_offset - base_offset
308
+ offset += i + 1
309
+ else
310
+ base_offset = find_object(data)
311
+ offset += SHA1Size
312
+ end
313
+
314
+ base, type = unpack_object(packfile, base_offset)
315
+
316
+ return [false, false] if !(type == OBJ_COMMIT || type == OBJ_TREE) && options[:caching]
317
+
318
+ delta = unpack_compressed(offset, size)
319
+ [patch_delta(base, delta), type]
320
+ end
321
+ private :unpack_deltified
322
+
323
+ def unpack_compressed(offset, destsize)
324
+ outdata = ""
325
+ with_packfile do |packfile|
326
+ packfile.seek(offset)
327
+ zstr = Zlib::Inflate.new
328
+ while outdata.size < destsize
329
+ indata = packfile.read(4096)
330
+ if indata.size == 0
331
+ raise PackFormatError, 'error reading pack data'
332
+ end
333
+ outdata << zstr.inflate(indata)
334
+ end
335
+ if outdata.size > destsize
336
+ raise PackFormatError, 'error reading pack data'
337
+ end
338
+ zstr.close
339
+ end
340
+ outdata
341
+ end
342
+ private :unpack_compressed
343
+
344
+ def patch_delta(base, delta)
345
+ src_size, pos = patch_delta_header_size(delta, 0)
346
+ if src_size != base.size
347
+ raise PackFormatError, 'invalid delta data'
348
+ end
349
+
350
+ dest_size, pos = patch_delta_header_size(delta, pos)
351
+ dest = ""
352
+ while pos < delta.size
353
+ c = delta.getord(pos)
354
+ pos += 1
355
+ if c & 0x80 != 0
356
+ pos -= 1
357
+ cp_off = cp_size = 0
358
+ cp_off = delta.getord(pos += 1) if c & 0x01 != 0
359
+ cp_off |= delta.getord(pos += 1) << 8 if c & 0x02 != 0
360
+ cp_off |= delta.getord(pos += 1) << 16 if c & 0x04 != 0
361
+ cp_off |= delta.getord(pos += 1) << 24 if c & 0x08 != 0
362
+ cp_size = delta.getord(pos += 1) if c & 0x10 != 0
363
+ cp_size |= delta.getord(pos += 1) << 8 if c & 0x20 != 0
364
+ cp_size |= delta.getord(pos += 1) << 16 if c & 0x40 != 0
365
+ cp_size = 0x10000 if cp_size == 0
366
+ pos += 1
367
+ dest << base[cp_off,cp_size]
368
+ elsif c != 0
369
+ dest << delta[pos,c]
370
+ pos += c
371
+ else
372
+ raise PackFormatError, 'invalid delta data'
373
+ end
374
+ end
375
+ dest
376
+ end
377
+ private :patch_delta
378
+
379
+ def patch_delta_header_size(delta, pos)
380
+ size = 0
381
+ shift = 0
382
+ begin
383
+ c = delta.getord(pos)
384
+ if c == nil
385
+ raise PackFormatError, 'invalid delta header'
386
+ end
387
+ pos += 1
388
+ size |= (c & 0x7f) << shift
389
+ shift += 7
390
+ end while c & 0x80 != 0
391
+ [size, pos]
392
+ end
393
+ private :patch_delta_header_size
394
+ end
395
+ end
396
+ end
397
+ end