]> arthur.barton.de Git - bup.git/blob - hashsplit.py
eb8139529a1be6a3ffb8813861d453930f55159f
[bup.git] / hashsplit.py
1 import sys
2 import git, chashsplit
3 from helpers import *
4
5 BLOB_LWM = 8192*2
6 BLOB_MAX = BLOB_LWM*2
7 BLOB_HWM = 1024*1024
8
9
10 class Buf:
11     def __init__(self):
12         self.data = ''
13         self.start = 0
14
15     def put(self, s):
16         #log('oldsize=%d+%d adding=%d\n' % (len(self.data), self.start, len(s)))
17         if s:
18             self.data = buffer(self.data, self.start) + s
19             self.start = 0
20             
21     def peek(self, count):
22         return buffer(self.data, self.start, count)
23     
24     def eat(self, count):
25         self.start += count
26
27     def get(self, count):
28         v = buffer(self.data, self.start, count)
29         self.start += count
30         return v
31
32     def used(self):
33         return len(self.data) - self.start
34
35
36 def splitbuf(buf):
37     b = buf.peek(buf.used())
38     ofs = chashsplit.splitbuf(b)
39     if ofs:
40         buf.eat(ofs)
41         return buffer(b, 0, ofs)
42     return None
43
44
45 def blobiter(files):
46     for f in files:
47         b = 1
48         while b:
49             b = f.read(BLOB_HWM)
50             if b:
51                 yield b
52     yield '' # EOF indicator
53
54
55 def autofiles(filenames):
56     if not filenames:
57         yield sys.stdin
58     else:
59         for n in filenames:
60             yield open(n)
61             
62     
63 def hashsplit_iter(files):
64     ofs = 0
65     buf = Buf()
66     fi = blobiter(files)
67     blob = 1
68
69     eof = 0
70     lv = 0
71     while blob or not eof:
72         if not eof and (buf.used() < BLOB_LWM or not blob):
73             bnew = fi.next()
74             if not bnew: eof = 1
75             #log('got %d, total %d\n' % (len(bnew), buf.used()))
76             buf.put(bnew)
77
78         blob = splitbuf(buf)
79         if eof and not blob:
80             blob = buf.get(buf.used())
81         if not blob and buf.used() >= BLOB_MAX:
82             blob = buf.get(BLOB_MAX)  # limit max blob size
83         if not blob and not eof:
84             continue
85
86         if blob:
87             yield (ofs, len(blob), git.hash_blob(blob))
88             ofs += len(blob)
89           
90         nv = (ofs + buf.used())/1000000
91         if nv != lv:
92             #log('%d\t' % nv)
93             lv = nv
94
95
96 def split_to_tree(files):
97     shalist = []
98     ofs = 0
99     last_ofs = 0
100     for (ofs, size, sha) in hashsplit_iter(files):
101         #log('SPLIT @ %-8d size=%-8d\n' % (ofs, size))
102         # this silliness keeps chunk filenames "similar" when a file changes
103         # slightly.
104         bm = BLOB_MAX
105         while 1:
106             cn = ofs / bm * bm
107             #log('%x,%x,%x,%x\n' % (last_ofs,ofs,cn,bm))
108             if cn > last_ofs or ofs == last_ofs: break
109             bm /= 2
110         last_ofs = cn
111         shalist.append(('100644', 'bup.chunk.%016x' % cn, sha))
112     tree = git.gen_tree(shalist)
113     return (shalist, tree)
114
115
116 def split_to_blob_or_tree(files):
117     (shalist, tree) = split_to_tree(files)
118     if len(shalist) == 1:
119         return (shalist[0][0], shalist[0][2])
120     elif len(shalist) == 0:
121         return ('100644', git.hash_blob(''))
122     else:
123         return ('040000', tree)