head-revs: add a native implementation of the `stop_rev` parameter
This does not add too much complexity to the native code and help with
branchmap v3 performance.
Note that the final conversion of the heads from native-code to Python is still
too costly, especially in Rust. In addition the current caching around headrevs
is too simple and fragile. However these are an unrelated problem.
### benchmark.name = hg.command.unbundle
# bin-env-vars.hg.py-re2-module = default
# benchmark.variants.issue6528 = disabled
# benchmark.variants.resource-usage = default
# benchmark.variants.reuse-external-delta-parent = yes
# benchmark.variants.revs = any-1-extra-rev
# benchmark.variants.source = unbundle
# benchmark.variants.validate = default
# benchmark.variants.verbosity = quiet
## data-env-vars.name = netbeans-2018-08-01-zstd-sparse-revlog
# bin-env-vars.hg.flavor = default
branch-v2: 0.233711 ~~~~~
branch-v3 before: 0.239857 (+2.63%, +0.01)
branch-v3 after: 0.239558 (+2.50%, +0.01)
# bin-env-vars.hg.flavor = rust
branch-v2: 0.235230 ~~~~~
branch-v3 before: 0.240972 (+2.44%, +0.01)
branch-v3 after: 0.239917 (+1.99%, +0.00)
## data-env-vars.name = netbeans-2018-08-01-ds2-pnm
# bin-env-vars.hg.flavor = rust
branch-v2: 0.255586 ~~~~~
branch-v3 before: 0.268560 (+5.08%, +0.01)
branch-v3 after: 0.262261 (+2.61%, +0.01)
## data-env-vars.name = mozilla-central-2024-03-22-zstd-sparse-revlog
# bin-env-vars.hg.flavor = default
branch-v2: 0.339010 ~~~~~
branch-v3 before: 0.349389 (+3.06%, +0.01)
branch-v3 after: 0.348247 (+2.72%, +0.01)
# bin-env-vars.hg.flavor = rust
branch-v2: 0.346525 ~~~~~
branch-v3 before: 0.355661 (+2.64%, +0.01)
branch-v3 after: 0.350906 (+1.26%, +0.00)
## data-env-vars.name = mozilla-central-2024-03-22-ds2-pnm
# bin-env-vars.hg.flavor = rust
branch-v2: 0.380202 ~~~~~
branch-v3 before: 0.408851 (+7.54%, +0.03)
branch-v3 after: 0.406511 (+6.92%, +0.03)
## data-env-vars.name = mozilla-unified-2024-03-22-zstd-sparse-revlog
# bin-env-vars.hg.flavor = default
branch-v2: 0.412165 ~~~~~
branch-v3 before: 0.427782 (+3.79%, +0.02)
branch-v3 after: 0.422595 (+2.53%, +0.01)
# bin-env-vars.hg.flavor = rust
branch-v2: 0.412397 ~~~~~
branch-v3 before: 0.422354 (+2.41%, +0.01)
branch-v3 after: 0.421079 (+2.11%, +0.01)
## data-env-vars.name = mozilla-unified-2024-03-22-ds2-pnm
# bin-env-vars.hg.flavor = rust
branch-v2: 0.429501 ~~~~~
branch-v3 before: 0.443197 (+3.19%, +0.01)
branch-v3 after: 0.449432 (+4.64%, +0.02)
## data-env-vars.name = mozilla-try-2024-03-26-zstd-sparse-revlog
# bin-env-vars.hg.flavor = default
branch-v2: 3.403171 ~~~~~
branch-v3 before: 3.819477 (+12.23%, +0.42)
branch-v3 after: 3.658482 (+7.50%, +0.26)
# bin-env-vars.hg.flavor = rust
branch-v2: 3.454876 ~~~~~
branch-v3 before: 3.590284 (+3.92%, +0.14)
branch-v3 after: 3.545843 (+2.63%, +0.09)
## data-env-vars.name = mozilla-try-2024-03-26-ds2-pnm
# bin-env-vars.hg.flavor = rust
branch-v2: 3.465435 ~~~~~
branch-v3 before: 3.633278 (+4.84%, +0.17)
branch-v3 after: 3.556074 (+2.62%, +0.09)
# revlogv0 - code related to revlog format "V0"
#
# Copyright 2005-2007 Olivia Mackall <olivia@selenic.com>
#
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2 or any later version.
from __future__ import annotations
from ..node import sha1nodeconstants
from .constants import (
INDEX_ENTRY_V0,
)
from ..i18n import _
from .. import (
error,
node,
revlogutils,
util,
)
from . import (
nodemap as nodemaputil,
)
def getoffset(q):
return int(q >> 16)
def gettype(q):
return int(q & 0xFFFF)
class revlogoldindex(list):
rust_ext_compat = 0
entry_size = INDEX_ENTRY_V0.size
null_item = revlogutils.entry(
data_offset=0,
data_compressed_length=0,
data_delta_base=node.nullrev,
link_rev=node.nullrev,
parent_rev_1=node.nullrev,
parent_rev_2=node.nullrev,
node_id=sha1nodeconstants.nullid,
)
@util.propertycache
def _nodemap(self):
nodemap = nodemaputil.NodeMap({sha1nodeconstants.nullid: node.nullrev})
for r in range(0, len(self)):
n = self[r][7]
nodemap[n] = r
return nodemap
def has_node(self, node):
"""return True if the node exist in the index"""
return node in self._nodemap
def rev(self, node):
"""return a revision for a node
If the node is unknown, raise a RevlogError"""
return self._nodemap[node]
def get_rev(self, node):
"""return a revision for a node
If the node is unknown, return None"""
return self._nodemap.get(node)
def append(self, tup):
self._nodemap[tup[7]] = len(self)
super(revlogoldindex, self).append(tup)
def __delitem__(self, i):
if not isinstance(i, slice) or not i.stop == -1 or i.step is not None:
raise ValueError(b"deleting slices only supports a:-1 with step 1")
for r in range(i.start, len(self)):
del self._nodemap[self[r][7]]
super(revlogoldindex, self).__delitem__(i)
def clearcaches(self):
self.__dict__.pop('_nodemap', None)
def __getitem__(self, i):
if i == -1:
return self.null_item
return list.__getitem__(self, i)
def pack_header(self, header):
"""pack header information in binary"""
return b''
def entry_binary(self, rev):
"""return the raw binary string representing a revision"""
entry = self[rev]
if gettype(entry[0]):
raise error.RevlogError(
_(b'index entry flags need revlog version 1')
)
e2 = (
getoffset(entry[0]),
entry[1],
entry[3],
entry[4],
self[entry[5]][7],
self[entry[6]][7],
entry[7],
)
return INDEX_ENTRY_V0.pack(*e2)
def headrevs(self, excluded_revs=None, stop_rev=None):
count = len(self)
if stop_rev is not None:
count = min(count, stop_rev)
if not count:
return [node.nullrev]
# we won't iter over filtered rev so nobody is a head at start
ishead = [0] * (count + 1)
revs = range(count)
if excluded_revs is not None:
revs = (r for r in revs if r not in excluded_revs)
for r in revs:
ishead[r] = 1 # I may be an head
e = self[r]
ishead[e[5]] = ishead[e[6]] = 0 # my parent are not
return [r for r, val in enumerate(ishead) if val]
def parse_index_v0(data, inline):
s = INDEX_ENTRY_V0.size
index = []
nodemap = nodemaputil.NodeMap({node.nullid: node.nullrev})
n = off = 0
l = len(data)
while off + s <= l:
cur = data[off : off + s]
off += s
e = INDEX_ENTRY_V0.unpack(cur)
# transform to revlogv1 format
e2 = revlogutils.entry(
data_offset=e[0],
data_compressed_length=e[1],
data_delta_base=e[2],
link_rev=e[3],
parent_rev_1=nodemap.get(e[4], node.nullrev),
parent_rev_2=nodemap.get(e[5], node.nullrev),
node_id=e[6],
)
index.append(e2)
nodemap[e[6]] = n
n += 1
index = revlogoldindex(index)
return index, None