40 s.update(text) |
40 s.update(text) |
41 return s.digest() |
41 return s.digest() |
42 |
42 |
43 def compress(text): |
43 def compress(text): |
44 """ generate a possibly-compressed representation of text """ |
44 """ generate a possibly-compressed representation of text """ |
45 if not text: return ("", text) |
45 if not text: |
|
46 return ("", text) |
46 if len(text) < 44: |
47 if len(text) < 44: |
47 if text[0] == '\0': return ("", text) |
48 if text[0] == '\0': |
|
49 return ("", text) |
48 return ('u', text) |
50 return ('u', text) |
49 bin = zlib.compress(text) |
51 bin = zlib.compress(text) |
50 if len(bin) > len(text): |
52 if len(bin) > len(text): |
51 if text[0] == '\0': return ("", text) |
53 if text[0] == '\0': |
|
54 return ("", text) |
52 return ('u', text) |
55 return ('u', text) |
53 return ("", bin) |
56 return ("", bin) |
54 |
57 |
55 def decompress(bin): |
58 def decompress(bin): |
56 """ decompress the given input """ |
59 """ decompress the given input """ |
57 if not bin: return bin |
60 if not bin: |
|
61 return bin |
58 t = bin[0] |
62 t = bin[0] |
59 if t == '\0': return bin |
63 if t == '\0': |
60 if t == 'x': return zlib.decompress(bin) |
64 return bin |
61 if t == 'u': return bin[1:] |
65 if t == 'x': |
|
66 return zlib.decompress(bin) |
|
67 if t == 'u': |
|
68 return bin[1:] |
62 raise RevlogError(_("unknown compression type %r") % t) |
69 raise RevlogError(_("unknown compression type %r") % t) |
63 |
70 |
64 indexformatv0 = ">4l20s20s20s" |
71 indexformatv0 = ">4l20s20s20s" |
65 v0shaoffset = 56 |
72 v0shaoffset = 56 |
66 # index ng: |
73 # index ng: |
104 during a commit, we need to make sure the rev being added is |
111 during a commit, we need to make sure the rev being added is |
105 not a duplicate. This requires loading the entire index, |
112 not a duplicate. This requires loading the entire index, |
106 which is fairly slow. loadmap can load up just the node map, |
113 which is fairly slow. loadmap can load up just the node map, |
107 which takes much less time. |
114 which takes much less time. |
108 """ |
115 """ |
109 if self.allmap: return |
116 if self.allmap: |
|
117 return |
110 end = self.datasize |
118 end = self.datasize |
111 self.allmap = 1 |
119 self.allmap = 1 |
112 cur = 0 |
120 cur = 0 |
113 count = 0 |
121 count = 0 |
114 blocksize = self.s * 256 |
122 blocksize = self.s * 256 |
124 break |
132 break |
125 off += self.s |
133 off += self.s |
126 cur += blocksize |
134 cur += blocksize |
127 |
135 |
128 def loadblock(self, blockstart, blocksize, data=None): |
136 def loadblock(self, blockstart, blocksize, data=None): |
129 if self.all: return |
137 if self.all: |
|
138 return |
130 if data is None: |
139 if data is None: |
131 self.dataf.seek(blockstart) |
140 self.dataf.seek(blockstart) |
132 if blockstart + blocksize > self.datasize: |
141 if blockstart + blocksize > self.datasize: |
133 # the revlog may have grown since we've started running, |
142 # the revlog may have grown since we've started running, |
134 # but we don't have space in self.index for more entries. |
143 # but we don't have space in self.index for more entries. |
149 self.map[n] = i + x |
158 self.map[n] = i + x |
150 off += self.s |
159 off += self.s |
151 |
160 |
152 def findnode(self, node): |
161 def findnode(self, node): |
153 """search backwards through the index file for a specific node""" |
162 """search backwards through the index file for a specific node""" |
154 if self.allmap: return None |
163 if self.allmap: |
|
164 return None |
155 |
165 |
156 # hg log will cause many many searches for the manifest |
166 # hg log will cause many many searches for the manifest |
157 # nodes. After we get called a few times, just load the whole |
167 # nodes. After we get called a few times, just load the whole |
158 # thing. |
168 # thing. |
159 if self.mapfind_count > 8: |
169 if self.mapfind_count > 8: |
211 blocksize = self.s * 64 |
222 blocksize = self.s * 64 |
212 end = blockstart + blocksize |
223 end = blockstart + blocksize |
213 while blockstart < end: |
224 while blockstart < end: |
214 self.loadblock(blockstart, blocksize) |
225 self.loadblock(blockstart, blocksize) |
215 blockstart += blocksize |
226 blockstart += blocksize |
216 if all: self.all = True |
227 if all: |
|
228 self.all = True |
217 |
229 |
218 class lazyindex(object): |
230 class lazyindex(object): |
219 """a lazy version of the index array""" |
231 """a lazy version of the index array""" |
220 def __init__(self, parser): |
232 def __init__(self, parser): |
221 self.p = parser |
233 self.p = parser |
275 def __setitem__(self, key, val): |
287 def __setitem__(self, key, val): |
276 self.p.map[key] = val |
288 self.p.map[key] = val |
277 def __delitem__(self, key): |
289 def __delitem__(self, key): |
278 del self.p.map[key] |
290 del self.p.map[key] |
279 |
291 |
280 class RevlogError(Exception): pass |
292 class RevlogError(Exception): |
281 class LookupError(RevlogError): pass |
293 pass |
|
294 class LookupError(RevlogError): |
|
295 pass |
282 |
296 |
283 def getoffset(q): |
297 def getoffset(q): |
284 if q & 0xFFFF: |
298 if q & 0xFFFF: |
285 raise RevlogError(_('incompatible revision flag %x') % q) |
299 raise RevlogError(_('incompatible revision flag %x') % q) |
286 return int(q >> 16) |
300 return int(q >> 16) |
472 """loads the map from the lazy parser""" |
486 """loads the map from the lazy parser""" |
473 if isinstance(self.nodemap, lazymap): |
487 if isinstance(self.nodemap, lazymap): |
474 self.nodemap.p.loadmap() |
488 self.nodemap.p.loadmap() |
475 self.nodemap = self.nodemap.p.map |
489 self.nodemap = self.nodemap.p.map |
476 |
490 |
477 def _inline(self): return self.version & REVLOGNGINLINEDATA |
491 def _inline(self): |
478 |
492 return self.version & REVLOGNGINLINEDATA |
479 def tip(self): return self.node(len(self.index) - 2) |
493 def tip(self): |
480 def count(self): return len(self.index) - 1 |
494 return self.node(len(self.index) - 2) |
481 def node(self, rev): |
495 def count(self): |
482 return self.index[rev][7] |
496 return len(self.index) - 1 |
|
497 |
483 def rev(self, node): |
498 def rev(self, node): |
484 try: |
499 try: |
485 return self.nodemap[node] |
500 return self.nodemap[node] |
486 except KeyError: |
501 except KeyError: |
487 raise LookupError(_('%s: no node %s') % (self.indexfile, hex(node))) |
502 raise LookupError(_('%s: no node %s') % (self.indexfile, hex(node))) |
|
503 def node(self, rev): |
|
504 return self.index[rev][7] |
488 def linkrev(self, node): |
505 def linkrev(self, node): |
489 return self.index[self.rev(node)][4] |
506 return self.index[self.rev(node)][4] |
490 def parents(self, node): |
507 def parents(self, node): |
491 d = self.index[self.rev(node)][5:7] |
508 d = self.index[self.rev(node)][5:7] |
492 return (self.node(d[0]), self.node(d[1])) |
509 return (self.node(d[0]), self.node(d[1])) |
493 def parentrevs(self, rev): |
510 def parentrevs(self, rev): |
494 return self.index[rev][5:7] |
511 return self.index[rev][5:7] |
495 def start(self, rev): |
512 def start(self, rev): |
496 return getoffset(self.index[rev][0]) |
513 return getoffset(self.index[rev][0]) |
497 def end(self, rev): return self.start(rev) + self.length(rev) |
514 def end(self, rev): |
|
515 return self.start(rev) + self.length(rev) |
|
516 def length(self, rev): |
|
517 return self.index[rev][1] |
|
518 def base(self, rev): |
|
519 return self.index[rev][3] |
498 |
520 |
499 def size(self, rev): |
521 def size(self, rev): |
500 """return the length of the uncompressed text for a given revision""" |
522 """return the length of the uncompressed text for a given revision""" |
501 l = self.index[rev][2] |
523 l = self.index[rev][2] |
502 if l >= 0: |
524 if l >= 0: |
522 l = len(text) |
544 l = len(text) |
523 for x in xrange(base + 1, rev + 1): |
545 for x in xrange(base + 1, rev + 1): |
524 l = mdiff.patchedsize(l, self.chunk(x)) |
546 l = mdiff.patchedsize(l, self.chunk(x)) |
525 return l |
547 return l |
526 """ |
548 """ |
527 |
|
528 def length(self, rev): |
|
529 return self.index[rev][1] |
|
530 def base(self, rev): |
|
531 return self.index[rev][3] |
|
532 |
549 |
533 def reachable(self, node, stop=None): |
550 def reachable(self, node, stop=None): |
534 """return a hash of all nodes ancestral to a given node, including |
551 """return a hash of all nodes ancestral to a given node, including |
535 the node itself, stopping when stop is matched""" |
552 the node itself, stopping when stop is matched""" |
536 reachable = {} |
553 reachable = {} |
762 except LookupError: |
779 except LookupError: |
763 pass # may be partial hex id |
780 pass # may be partial hex id |
764 try: |
781 try: |
765 # str(rev) |
782 # str(rev) |
766 rev = int(id) |
783 rev = int(id) |
767 if str(rev) != id: raise ValueError |
784 if str(rev) != id: |
768 if rev < 0: rev = self.count() + rev |
785 raise ValueError |
769 if rev < 0 or rev >= self.count(): raise ValueError |
786 if rev < 0: |
|
787 rev = self.count() + rev |
|
788 if rev < 0 or rev >= self.count(): |
|
789 raise ValueError |
770 return self.node(rev) |
790 return self.node(rev) |
771 except (ValueError, OverflowError): |
791 except (ValueError, OverflowError): |
772 pass |
792 pass |
773 if len(id) == 40: |
793 if len(id) == 40: |
774 try: |
794 try: |
849 offset = start - cache_start |
868 offset = start - cache_start |
850 else: |
869 else: |
851 loadcache(df) |
870 loadcache(df) |
852 offset = 0 |
871 offset = 0 |
853 |
872 |
854 #def checkchunk(): |
|
855 # df = self.opener(self.datafile) |
|
856 # df.seek(start) |
|
857 # return df.read(length) |
|
858 #assert s == checkchunk() |
|
859 return decompress(self._io.chunkcache[1][offset:offset + length]) |
873 return decompress(self._io.chunkcache[1][offset:offset + length]) |
860 |
874 |
861 def delta(self, node): |
875 def delta(self, node): |
862 """return or calculate a delta between a node and its predecessor""" |
876 """return or calculate a delta between a node and its predecessor""" |
863 r = self.rev(node) |
877 r = self.rev(node) |
873 return self.diff(self.revision(self.node(rev1)), |
887 return self.diff(self.revision(self.node(rev1)), |
874 self.revision(self.node(rev2))) |
888 self.revision(self.node(rev2))) |
875 |
889 |
876 def revision(self, node): |
890 def revision(self, node): |
877 """return an uncompressed revision of a given""" |
891 """return an uncompressed revision of a given""" |
878 if node == nullid: return "" |
892 if node == nullid: |
879 if self.cache and self.cache[0] == node: return self.cache[2] |
893 return "" |
|
894 if self.cache and self.cache[0] == node: |
|
895 return self.cache[2] |
880 |
896 |
881 # look up what we need to read |
897 # look up what we need to read |
882 text = None |
898 text = None |
883 rev = self.rev(node) |
899 rev = self.rev(node) |
884 base = self.base(rev) |
900 base = self.base(rev) |
976 dfh = None |
992 dfh = None |
977 ifh = self.opener(self.indexfile, "a+") |
993 ifh = self.opener(self.indexfile, "a+") |
978 return self._addrevision(text, transaction, link, p1, p2, d, ifh, dfh) |
994 return self._addrevision(text, transaction, link, p1, p2, d, ifh, dfh) |
979 |
995 |
980 def _addrevision(self, text, transaction, link, p1, p2, d, ifh, dfh): |
996 def _addrevision(self, text, transaction, link, p1, p2, d, ifh, dfh): |
981 if text is None: text = "" |
997 if text is None: |
982 if p1 is None: p1 = self.tip() |
998 text = "" |
983 if p2 is None: p2 = nullid |
999 if p1 is None: |
|
1000 p1 = self.tip() |
|
1001 if p2 is None: |
|
1002 p2 = nullid |
984 |
1003 |
985 node = hash(text, p1, p2) |
1004 node = hash(text, p1, p2) |
986 |
1005 |
987 if node in self.nodemap: |
1006 if node in self.nodemap: |
988 return node |
1007 return node |