Bladeren bron

Read fan-out table always, read file sha1.

Jelmer Vernooij 16 jaren geleden
bovenliggende
commit
4b4ee3895c
2 gewijzigde bestanden met toevoegingen van 81 en 27 verwijderingen
  1. 72 26
      dulwich/pack.py
  2. 9 1
      dulwich/tests/test_pack.py

+ 72 - 26
dulwich/pack.py

@@ -68,17 +68,21 @@ def simple_mmap(f, offset, size, access=mmap.ACCESS_READ):
             def __getitem__(self, i):
                 return self.array[i+self.offset]
 
-        mem = mmap.mmap(f.fileno(), size, access=access)
+            def __len__(self):
+                return len(self.array) - self.offset
+
+        mem = mmap.mmap(f.fileno(), size+offset, access=access)
         if offset == 0:
             return mem
         return ArraySkipper(mem, offset)
 
 
 def multi_ord(map, start, count):
-  value = 0
-  for i in range(count):
-    value = value * 0x100 + ord(map[start+i])
-  return value
+    value = 0
+    for i in range(count):
+        value = value * 0x100 + ord(map[start+i])
+    return value
+
 
 class PackIndex(object):
   """An index in to a packfile.
@@ -94,8 +98,7 @@ class PackIndex(object):
   the start and end offset and then bisect in to find if the value is present.
   """
 
-  header_record_size = 4
-  header_size = 256 * header_record_size
+  PACK_INDEX_HEADER_SIZE = 0x100 * 4
   index_size = 4
   sha_bytes = 20
   record_size = sha_bytes + index_size
@@ -111,8 +114,32 @@ class PackIndex(object):
     # Take the size now, so it can be checked each time we map the file to
     # ensure that it hasn't changed.
     self._size = os.path.getsize(filename)
-    assert self._size > self.header_size, "%s is too small to be a packfile" % \
-        filename
+    self._fan_out_table = self._read_fan_out_table()
+
+  def __len__(self):
+    ret = 0
+    for v in self._fan_out_table.itervalues():
+        ret += v
+    return v
+
+  def _read_fan_out_table(self):
+    f = open(self._filename, 'r')
+    try:
+        contents = f.read(256 * 4)
+    finally:
+        f.close()
+    ret = {}
+    for i in range(0x100):
+        (ret[i],) = struct.unpack(">L", contents[i*4:(i+1)*4])
+    return ret
+
+  def file_sha1(self):
+    """Return the SHA1 file stored for this header file itself."""
+    f = open(self._filename, 'r')
+    try:
+        return simple_mmap(f, self._size-20, 20)
+    finally:
+        f.close()
 
   def object_index(self, sha):
     """Return the index in to the corresponding packfile for the object.
@@ -131,24 +158,27 @@ class PackIndex(object):
     finally:
       f.close()
 
+  def _entry_offset(self, i):
+      return self.PACK_INDEX_HEADER_SIZE + (i * self.record_size)
+
   def _object_index(self, map, hexsha):
-    """See object_index"""
-    first_byte = hex_to_sha(hexsha[:2])
-    header_offset = self.header_record_size * first_byte
-    start = multi_ord(map, header_offset-self.header_record_size, self.header_record_size)
-    end = multi_ord(map, header_offset, self.header_record_size)
-    sha = hex_to_sha(hexsha)
-    while start < end:
-      i = (start + end)/2
-      offset = self.header_size + (i * self.record_size)
-      file_sha = multi_ord(map, offset + self.index_size, self.sha_bytes)
-      if file_sha == sha:
-        return multi_ord(map, offset, self.index_size)
-      elif file_sha < sha:
-        start = offset + 1
-      else:
-        end = offset - 1
-    return None
+      """See object_index"""
+      first_byte = hex_to_sha(hexsha[:2])
+      header_offset = 4 * first_byte
+      start = multi_ord(map, header_offset-4, 4)
+      end = multi_ord(map, header_offset, 4)
+      sha = hex_to_sha(hexsha)
+      while start < end:
+        i = (start + end)/2
+        offset = self._entry_offset(i)
+        file_sha = multi_ord(map, offset + self.index_size, self.sha_bytes)
+        if file_sha == sha:
+          return multi_ord(map, offset, self.index_size)
+        elif file_sha < sha:
+          start = offset + 1
+        else:
+          end = offset - 1
+      return None
 
 
 class PackData(object):
@@ -249,3 +279,19 @@ class PackData(object):
     obj = ShaFile.from_raw_string(type, uncomp)
     return obj
 
+
+def write_pack(filename, objects):
+    """Write a new pack file.
+
+    :param filename: The filename of the new pack file.
+    :param objects: List of objects to write.
+    """
+    f = open(filename, 'w')
+    try:
+        f.write("PACK")               # Pack header
+        f.write(struct.pack(">L", 2)) # Pack version
+        f.write(struct.pack(">L", len(objects))) # Number of objects in pack
+        for o in objects:
+            pass # FIXME: Write object
+    finally:
+        f.close()

+ 9 - 1
dulwich/tests/test_pack.py

@@ -79,10 +79,18 @@ class TestPackData(PackTests):
     self.assertEqual(obj._type, 'commit')
     self.assertEqual(obj.sha().hexdigest(), commit_sha)
 
-  def test_len(self):
+  def test_pack_len(self):
     p = self.get_pack_data(pack1_sha)
     self.assertEquals(3, len(p))
 
+  def test_index_len(self):
+    p = self.get_pack_index(pack1_sha)
+    self.assertEquals(3, len(p))
+
+  def test_file_sha1(self):
+    p = self.get_pack_index(pack1_sha)
+    self.assertEquals("bla", p.file_sha1())
+
 
 class TestHexToSha(unittest.TestCase):