]> arthur.barton.de Git - bup.git/blob - hashsplit.py
bup split: print extra output to stderr if -v or -vv is given.
[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 split_verbosely = 0
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     global split_verbosely
38     b = buf.peek(buf.used())
39     ofs = chashsplit.splitbuf(b)
40     if ofs:
41         if split_verbosely >= 2:
42             log('.')
43         buf.eat(ofs)
44         return buffer(b, 0, ofs)
45     return None
46
47
48 def blobiter(files):
49     for f in files:
50         b = 1
51         while b:
52             b = f.read(BLOB_HWM)
53             if b:
54                 yield b
55     yield '' # EOF indicator
56
57
58 def autofiles(filenames):
59     if not filenames:
60         yield sys.stdin
61     else:
62         for n in filenames:
63             yield open(n)
64             
65     
66 def hashsplit_iter(files):
67     global split_verbosely
68     ofs = 0
69     buf = Buf()
70     fi = blobiter(files)
71     blob = 1
72
73     eof = 0
74     lv = 0
75     while blob or not eof:
76         if not eof and (buf.used() < BLOB_LWM or not blob):
77             bnew = fi.next()
78             if not bnew: eof = 1
79             #log('got %d, total %d\n' % (len(bnew), buf.used()))
80             buf.put(bnew)
81
82         blob = splitbuf(buf)
83         if eof and not blob:
84             blob = buf.get(buf.used())
85         if not blob and buf.used() >= BLOB_MAX:
86             blob = buf.get(buf.used())  # limit max blob size
87         if not blob and not eof:
88             continue
89
90         if blob:
91             yield (ofs, len(blob), git.hash_blob(blob))
92             ofs += len(blob)
93           
94         nv = (ofs + buf.used())/1000000
95         if nv != lv:
96             if split_verbosely >= 1:
97                 log('%d\t' % nv)
98             lv = nv
99
100
101 total_split = 0
102 def split_to_shalist(files):
103     global total_split
104     ofs = 0
105     last_ofs = 0
106     for (ofs, size, sha) in hashsplit_iter(files):
107         #log('SPLIT @ %-8d size=%-8d\n' % (ofs, size))
108         # this silliness keeps chunk filenames "similar" when a file changes
109         # slightly.
110         bm = BLOB_MAX
111         while 1:
112             cn = ofs / bm * bm
113             #log('%x,%x,%x,%x\n' % (last_ofs,ofs,cn,bm))
114             if cn > last_ofs or ofs == last_ofs: break
115             bm /= 2
116         last_ofs = cn
117         total_split += size
118         yield ('100644', 'bup.chunk.%016x' % cn, sha)
119
120
121 def split_to_tree(files):
122     shalist = list(split_to_shalist(files))
123     tree = git.gen_tree(shalist)
124     return (shalist, tree)
125
126
127 def split_to_blob_or_tree(files):
128     (shalist, tree) = split_to_tree(files)
129     if len(shalist) == 1:
130         return (shalist[0][0], shalist[0][2])
131     elif len(shalist) == 0:
132         return ('100644', git.hash_blob(''))
133     else:
134         return ('40000', tree)