Class: Amp::Core::Repositories::Git::PackFile::PackFileEntry

Inherits:
Struct
  • Object
show all
Includes:
Support
Defined in:
lib/amp-git/repo_format/packfile.rb

Overview

A single entry in a packfile. Dumb struct. However, it has some smart class methods for parsing these bad boys in from a packfile. Take a look at #at and #read.

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#contentObject

Returns the value of attribute content

Returns:

  • (Object)

    the current value of content



43
44
45
# File 'lib/amp-git/repo_format/packfile.rb', line 43

def content
  @content
end

#delta_offsetObject

Returns the value of attribute delta_offset

Returns:

  • (Object)

    the current value of delta_offset



43
44
45
# File 'lib/amp-git/repo_format/packfile.rb', line 43

def delta_offset
  @delta_offset
end

#hash_idObject

Returns the value of attribute hash_id

Returns:

  • (Object)

    the current value of hash_id



43
44
45
# File 'lib/amp-git/repo_format/packfile.rb', line 43

def hash_id
  @hash_id
end

#offsetObject

Returns the value of attribute offset

Returns:

  • (Object)

    the current value of offset



43
44
45
# File 'lib/amp-git/repo_format/packfile.rb', line 43

def offset
  @offset
end

#referenceObject

Returns the value of attribute reference

Returns:

  • (Object)

    the current value of reference



43
44
45
# File 'lib/amp-git/repo_format/packfile.rb', line 43

def reference
  @reference
end

#sizeObject

Returns the value of attribute size

Returns:

  • (Object)

    the current value of size



43
44
45
# File 'lib/amp-git/repo_format/packfile.rb', line 43

def size
  @size
end

#typeObject

Returns the value of attribute type

Returns:

  • (Object)

    the current value of type



43
44
45
# File 'lib/amp-git/repo_format/packfile.rb', line 43

def type
  @type
end

Class Method Details

.at(fp, pos) ⇒ PackFileEntry

Reads a Amp::Core::Repositories::Git::PackFile::PackFileEntry from the given file at a given offset. This is a helper method for the entry point for reading from an actual PackFile, since often, you’ll know where the entry will be located in the PackFile.

Parameters:

  • fp (IO, #read)

    the file to read from

Returns:

  • (PackFileEntry)

    an entry, decompressed and with its hash calculated.



54
55
56
57
# File 'lib/amp-git/repo_format/packfile.rb', line 54

def at(fp, pos)
  fp.seek pos, IO::SEEK_SET
  read fp
end

.read(fp) ⇒ PackFileEntry

Reads a Amp::Core::Repositories::Git::PackFile::PackFileEntry from the given file. This is the entry point for reading from an actual PackFile.

Parameters:

  • fp (IO, #read)

    the file to read from

Returns:

  • (PackFileEntry)

    an entry, decompressed and with its hash calculated.



65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
# File 'lib/amp-git/repo_format/packfile.rb', line 65

def read(fp)
  result = PackFileEntry.new
  result.offset = fp.pos
  result.type, result.size = read_header(fp)
  if result.type == OBJ_REF_DELTA
    result.reference = fp.read(20)
  elsif result.type == OBJ_OFS_DELTA
    result.delta_offset = result.offset - read_offset(fp)
  end
  result.content = read_data(fp, result.size)
  if result.type == OBJ_REF_DELTA
    
  elsif result.type == OBJ_OFS_DELTA
    cur = fp.tell
    patch = Amp::Core::Repositories::Git::Encoding::BinaryDelta.new(result.content)
    previous = self.at(fp, result.delta_offset)
    result.content = patch.apply(previous.content)
    result.size = result.content.size
    result.type = previous.type
    fp.seek(cur, IO::SEEK_SET)
  end
  result.calculate_hash!
  result
end

.read_data(fp, size) ⇒ String

Reads data from the file, uncompressing along the way, until size bytes have been decompressed. Since we don’t know how much that will be ahead of time, this is annoying slow. Oh wells.

Parameters:

  • fp (IO, #read)

    the IO source to read compressed data from

  • size (Integer)

    the amount of uncompressed data to expect

Returns:

  • (String)

    the uncompressed data



137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
# File 'lib/amp-git/repo_format/packfile.rb', line 137

def read_data(fp, size)
  result = ""
  z = Zlib::Inflate.new
  start = fp.tell
  while result.size < size && !z.stream_end?
    result += z.inflate(fp.read(1))
  end
  # final bytes... can't predict this yet though it's usually 5 bytes
  while !fp.eof?
    begin
      result += z.finish
      break
    rescue Zlib::BufError
      result += z.inflate(fp.read(1))
    end
  end
  z.close
  result
end

.read_header(fp) ⇒ Array(Integer, Integer)

Reads in a PackFileEntry header from the file. This will get us the type of the entry, as well as the size of its uncompressed data.

Parameters:

  • fp (IO, #read)

    the file to read the header from

Returns:

  • (Array(Integer, Integer))

    the type and size of the entry packed into a tuple.



116
117
118
119
120
121
122
123
124
125
126
127
# File 'lib/amp-git/repo_format/packfile.rb', line 116

def read_header(fp)
  tags = Support::HexString.from_bin(fp.read(1)).ord
  type = (tags & 0x70) >> 4
  size = tags & 0xF
  shift = 4
  while tags & 0x80 > 0
    tags = Support::HexString.from_bin(fp.read(1)).ord
    size += (tags & 0x7F) << shift
    shift += 7
  end
  [type, size]
end

.read_offset(fp) ⇒ Integer

Reads an OBJ_OFS_DELTA offset. N-bytes, encoded as a series of bytes. Each byte is shifted by (7 * n) bits, and added to the total. If the high bit (MSB) of a byte is 1, then another byte is read, If it’s 0, it stops.

Parameters:

  • fp (IO, #read)

    the IO stream to read from

Returns:

  • (Integer)

    the offset read



98
99
100
101
102
103
104
105
106
107
# File 'lib/amp-git/repo_format/packfile.rb', line 98

def read_offset(fp)
  byte = Support::HexString.from_bin(fp.read(1)).ord
  tot = byte & 0x7f
  while (byte & 0x80) > 0
    byte = Support::HexString.from_bin(fp.read(1)).ord
    tot = ((tot + 1) << 7) | (byte & 0x7f)
    break if (byte & 0x80) == 0
  end
  tot
end

Instance Method Details

#calculate_hash!Object

Calculates the hash of this particular entry. We need to reconstruct the loose object header to do this.



161
162
163
164
165
# File 'lib/amp-git/repo_format/packfile.rb', line 161

def calculate_hash!
  prefix = PREFIX_NAME_LOOKUP[self.type]
  # add special cases for refs
  self.hash_id = NodeId.sha1("#{prefix} #{self.size}\0#{self.content}")
end

#to_raw_object(opener = nil) ⇒ RawObject

Converts to an actual raw object.

Parameters:

  • an (Support::RootedOpener)

    opener in case this object references other things.… should usually be set

Returns:

  • (RawObject)

    this entry in raw object form



173
174
175
# File 'lib/amp-git/repo_format/packfile.rb', line 173

def to_raw_object(opener = nil)
  RawObject.construct(hash_id, opener, PREFIX_NAME_LOOKUP[type], content)
end