]> arthur.barton.de Git - bup.git/blob - cmd-split.py
Oops, multi-file split forced a split between each file.
[bup.git] / cmd-split.py
1 #!/usr/bin/env python
2 import sys, os, subprocess, errno, zlib, time
3 import hashsplit, git, options
4 from helpers import *
5
6 BLOB_LWM = 8192*2
7 BLOB_MAX = BLOB_LWM*2
8 BLOB_HWM = 1024*1024
9
10
11 class Buf:
12     def __init__(self):
13         self.data = ''
14         self.start = 0
15
16     def put(self, s):
17         #log('oldsize=%d+%d adding=%d\n' % (len(self.data), self.start, len(s)))
18         if s:
19             self.data = buffer(self.data, self.start) + s
20             self.start = 0
21             
22     def peek(self, count):
23         return buffer(self.data, self.start, count)
24     
25     def eat(self, count):
26         self.start += count
27
28     def get(self, count):
29         v = buffer(self.data, self.start, count)
30         self.start += count
31         return v
32
33     def used(self):
34         return len(self.data) - self.start
35
36
37 def splitbuf(buf):
38     b = buf.peek(buf.used())
39     ofs = hashsplit.splitbuf(b)
40     if ofs:
41         buf.eat(ofs)
42         return buffer(b, 0, ofs)
43     return None
44
45
46 def blobiter(files):
47     for f in files:
48         b = 1
49         while b:
50             b = f.read(BLOB_HWM)
51             if b:
52                 yield b
53     yield '' # EOF indicator
54
55
56 def autofiles(filenames):
57     if not filenames:
58         yield sys.stdin
59     else:
60         for n in filenames:
61             yield open(n)
62             
63     
64 def hashsplit_iter(f):
65     ofs = 0
66     buf = Buf()
67     fi = blobiter(f)
68     blob = 1
69
70     eof = 0
71     lv = 0
72     while blob or not eof:
73         if not eof and (buf.used() < BLOB_LWM or not blob):
74             bnew = fi.next()
75             if not bnew: eof = 1
76             #log('got %d, total %d\n' % (len(bnew), buf.used()))
77             buf.put(bnew)
78
79         blob = splitbuf(buf)
80         if eof and not blob:
81             blob = buf.get(buf.used())
82         if not blob and buf.used() >= BLOB_MAX:
83             blob = buf.get(BLOB_MAX)  # limit max blob size
84         if not blob and not eof:
85             continue
86
87         if blob:
88             yield (ofs, len(blob), git.hash_blob(blob))
89             ofs += len(blob)
90           
91         nv = (ofs + buf.used())/1000000
92         if nv != lv:
93             log('%d\t' % nv)
94             lv = nv
95
96
97 optspec = """
98 bup split [-t] [filenames...]
99 --
100 b,blobs    output a series of blob ids
101 t,tree     output a tree id
102 c,commit   output a commit id
103 n,name=    name of backup set to update (if any)
104 bench      print benchmark timings to stderr
105 """
106 o = options.Options('bup split', optspec)
107 (opt, flags, extra) = o.parse(sys.argv[1:])
108
109 if not (opt.blobs or opt.tree or opt.commit or opt.name):
110     log("bup split: use one or more of -b, -t, -c, -n\n")
111     o.usage()
112
113 start_time = time.time()
114 shalist = []
115
116 ofs = 0
117 last_ofs = 0
118 for (ofs, size, sha) in hashsplit_iter(autofiles(extra)):
119     #log('SPLIT @ %-8d size=%-8d\n' % (ofs, size))
120     if opt.blobs:
121         print sha
122             
123     # this silliness keeps chunk filenames "similar" when a file changes
124     # slightly.
125     bm = BLOB_MAX
126     while 1:
127         cn = ofs / bm * bm
128         #log('%x,%x,%x,%x\n' % (last_ofs,ofs,cn,bm))
129         if cn > last_ofs or ofs == last_ofs: break
130         bm /= 2
131     last_ofs = cn
132     shalist.append(('100644', 'bup.chunk.%016x' % cn, sha))
133 tree = git.gen_tree(shalist)
134 if opt.tree:
135     print tree
136 if opt.commit or opt.name:
137     msg = 'Generated by command:\n%r' % sys.argv
138     ref = opt.name and ('refs/heads/%s' % opt.name) or None
139     commit = git.gen_commit_easy(ref, tree, msg)
140     if opt.commit:
141         print commit
142
143 secs = time.time() - start_time
144 if opt.bench:
145     log('\nbup: %.2fkbytes in %.2f secs = %.2f kbytes/sec\n'
146         % (ofs/1024., secs, ofs/1024./secs))