diff --git a/hgext3rd/clindex.pyx b/hgext3rd/clindex.pyx --- a/hgext3rd/clindex.pyx +++ b/hgext3rd/clindex.pyx @@ -11,6 +11,9 @@ Config:: [clindex] + # Use Rust childmap. + childmap = True + # Use Rust nodemap nodemap = True @@ -55,6 +58,7 @@ configtable = {} configitem = registrar.configitem(configtable) +configitem('clindex', 'childmap', default=True) configitem('clindex', 'nodemap', default=True) configitem('clindex', 'verify', default=True) @@ -74,6 +78,7 @@ cdef class clindex(object): cdef readonly _changelog cdef readonly localconfig _config + cdef readonly childmap _childmap cdef readonly nodemap _nodemap cdef _origindex cdef _vfs @@ -87,6 +92,7 @@ # For example, disabling Rust nodemap temporarily if strip happens. self._config = config.copy() self._nodemap = nodemap(self._origindex, data, vfs, config) + self._childmap = childmap(vfs, data, config) self._vfs = vfs def ancestors(self, *revs): @@ -119,6 +125,10 @@ rev = len(self._origindex) + rev self._origindex.insert(rev, entry) self._nodemap[entry[-1]] = rev + if self._config.childmap: + for p in entry[5:7]: + if p > -1: + self._childmap.insert(p, rev) def partialmatch(self, hexnode): return self._nodemap.partialmatch(hexnode) @@ -141,9 +151,11 @@ def destroying(self): _log(self._vfs, 'clindex: destroying') self._nodemap.destroying() + self._childmap.destroying() def updatecaches(self): self._nodemap.updatecache() + self._childmap.updatecache() cdef class nodemap(object): """mutable nodemap @@ -329,6 +341,81 @@ self._vfs.tryunlink('nodemap') self._config.nodemap = False +cdef class childmap(object): + cdef readonly _rustchildmap + cdef localconfig _config + cdef readonly bint _updated + cdef readonly _overrides + cdef _vfs + + emptyindex = indexes.childmap.emptyindexbuffer() + + def __cinit__(self, vfs, changelog, config): + self._config = config + self._overrides = {} + self._vfs = vfs + try: + index = util.buffer(util.mmapread(vfs('childmap'))) + if len(index) < len(self.emptyindex): + index = self.emptyindex + except IOError as ex: + if ex.errno != errno.ENOENT: + raise + _log(self._vfs, 'childmap: is empty') + index = self.emptyindex + try: + rustchildmap = indexes.childmap(changelog, index) + except Exception as ex: + _log(self._vfs, 'childmap: corrupted: %r' % ex) + rustchildmap = indexes.childmap(changelog, self.emptyindex) + self._rustchildmap = rustchildmap + self._updated = False + + def updatecache(self): + # updatecache may get called for *many* times. That is, an "outdated" + # changelog object being used across multiple transactions. This test + # avoids unnecessary re-updates. + if self._updated: + return + # childmap was disabled (ex. by destroying()). The changelog is now + # outdated. Do not rely on it building index. + if not self._config.childmap: + return + # Writing childmap has a cost. Do not update it if not lagging too much. + lag = self._rustchildmap.lag() + if lag == 0 or lag < self._config.lagthreshold: + return + _log(self._vfs, 'childmap: updating (lag=%s)' % lag) + with self._vfs('childmap', 'w', atomictemp=True) as f: + f.write(self._rustchildmap.build()) + self._updated = True + + cpdef insert(self, int parent, int child): + self._overrides.setdefault(parent, []).append(child) + + def __getitem__(self, int rev): + children = self._rustchildmap[rev] + if self._config.verify: + for child in children: + parents = self._rustchildmap.parentrevs(child) or () + if rev not in parents: + _logandraise('childmap: wrong child %r for parent %r' + % (child, rev)) + # pick up overrides + children.extend(self._overrides.get(rev, ())) + return children + + @property + def lag(self): + if self._config.childmap: + return self._rustchildmap.lag() + else: + return 0 + + def destroying(self): + self._vfs.tryunlink('childmap') + self._config.childmap = False + # These are unfortunate. But we need vfs access inside index.__init__. Doing # that properly requires API changes in revlog.__init__ and # revlogio.parseindex that might make things uglier, or break the (potential) @@ -341,12 +428,14 @@ # Lightweight config state that is dedicated for this extensions and is # decoupled from heavy-weight ui object. cdef class localconfig: + cdef public bint childmap cdef public bint nodemap cdef public bint verify cdef public int lagthreshold def copy(self): rhs = localconfig() + rhs.childmap = self.childmap rhs.nodemap = self.nodemap rhs.verify = self.verify rhs.lagthreshold = self.lagthreshold @@ -355,6 +444,7 @@ @classmethod def fromui(cls, ui): self = cls() + self.childmap = ui.configbool('clindex', 'childmap') self.nodemap = ui.configbool('clindex', 'nodemap') self.verify = ui.configbool('clindex', 'verify') self.lagthreshold = ui.configint('clindex', 'lagthreshold') diff --git a/setup.py b/setup.py --- a/setup.py +++ b/setup.py @@ -324,7 +324,7 @@ sources=['hgext3rd/clindex.pyx'], include_dirs=['hgext3rd'], extra_compile_args=filter(None, [ - STDC99, WALL, WEXTRA, WCONVERSION, PEDANTIC, + STDC99, WALL, WEXTRA, PEDANTIC, ]), ), ],