bindings: add revlogindex

Summary:
This module is inteneded to have native paths for some operations that need to
scan the whole changelog. It allows us to experiment some breaking changes,
namely, head-based visibility without "filtered revs", head-based phases on
the revlog format, before the more advanced structure taking over.

This diff adds a revlog index reader that can answer do simple queries like
"length", "parents".

Reviewed By: sfilipco

Differential Revision: D17199837

fbshipit-source-id: 2574f64c980419fa966200fd52fa5ddf873baae4
This commit is contained in:
Jun Wu 2019-09-17 18:12:07 -07:00 committed by Facebook Github Bot
parent 493a96a006
commit d2fbb0164e
5 changed files with 136 additions and 1 deletions

View File

@ -28,6 +28,7 @@ pymutationstore = { path = "modules/pymutationstore" }
pynodemap = { path = "modules/pynodemap" }
pypathmatcher = { path = "modules/pypathmatcher" }
pyrevisionstore = { path = "modules/pyrevisionstore" }
pyrevlogindex = { path = "modules/pyrevlogindex" }
pystackdesc = { path = "modules/pystackdesc" }
pytreestate = { path = "modules/pytreestate" }
pyvlq = { path = "modules/pyvlq" }

View File

@ -0,0 +1,8 @@
[package]
name = "pyrevlogindex"
version = "0.1.0"
edition = "2018"
[dependencies]
cpython = { version = "0.3", features = ["python27-sys"], default-features = false }
cpython-ext = { path = "../../../../lib/cpython-ext" }

View File

@ -0,0 +1,125 @@
// Copyright 2019 Facebook, Inc.
//
// This software may be used and distributed according to the terms of the
// GNU General Public License version 2 or any later version.
#![allow(non_camel_case_types)]
use cpython::*;
use cpython_ext::SimplePyBuf;
use std::cell::RefCell;
// XXX: The revlogindex is a temporary solution before migrating to
// segmented changelog. It is here to experiment breaking changes with
// revlog, incluing:
//
// - Redefine "head()" to only return remotenames and tracked draft heads.
// - Get rid of "filtered revs" and "repo view" layer entirely.
// - Switch phases to be defined by heads (remotenames), instead of roots.
pub fn init_module(py: Python, package: &str) -> PyResult<PyModule> {
let name = [package, "revlogindex"].join(".");
let m = PyModule::new(py, &name)?;
m.add_class::<revlogindex>(py)?;
Ok(m)
}
py_class!(class revlogindex |py| {
data changelogi: RevlogIndex;
def __new__(_cls, changelogi: &PyObject) -> PyResult<Self> {
let changelogi = RevlogIndex {
data: SimplePyBuf::new(py, changelogi),
inserted: RefCell::new(Vec::new()),
};
Self::create_instance(py, changelogi)
}
/// Get parent revisions.
def parentrevs(&self, rev: u32) -> PyResult<Vec<u32>> {
let revlog = self.changelogi(py);
Ok(revlog.parents(rev))
}
/// Insert a new revision that hasn't been written to disk.
/// Used by revlog._addrevision.
def insert(&self, parents: Vec<u32>) -> PyResult<PyObject> {
let revlog = self.changelogi(py);
revlog.insert(parents);
Ok(py.None())
}
def __len__(&self) -> PyResult<usize> {
let revlog = self.changelogi(py);
Ok(revlog.len())
}
});
/// Minimal code to read the DAG (i.e. parents) stored in non-inlined revlog.
struct RevlogIndex {
// Content of revlog-name.i (ex. 00changelog.i).
data: SimplePyBuf<RevlogEntry>,
// Inserted entries that are not flushed to disk.
inserted: RefCell<Vec<Vec<u32>>>,
}
/// Revlog entry. See "# index ng" in revlog.py.
#[allow(dead_code)]
#[repr(packed)]
#[derive(Copy, Clone)]
struct RevlogEntry {
offset_flags: u64,
compressed: i32,
len: i32,
base: i32,
link: i32,
p1: i32,
p2: i32,
node: [u8; 32],
}
impl RevlogIndex {
/// Revisions in total.
fn len(&self) -> usize {
let inserted = self.inserted.borrow();
self.data_len() + inserted.len()
}
/// Revisions stored in the original revlog index.
fn data_len(&self) -> usize {
self.data.as_ref().len()
}
/// Get parent revisions.
fn parents(&self, rev: u32) -> Vec<u32> {
let data_len = self.data_len();
if rev >= data_len as u32 {
let inserted = self.inserted.borrow();
return inserted[rev as usize - data_len].clone();
}
let data = self.data.as_ref();
let p1 = i32::from_be(data[rev as usize].p1);
let p2 = i32::from_be(data[rev as usize].p2);
if p1 == -1 {
// p1 == -1 but p2 != -1 is illegal for changelog (but possible
// for filelog with copy information).
assert!(p2 == -1);
Vec::new()
} else if p2 == -1 {
assert!((p1 as u32) < rev);
vec![p1 as u32]
} else {
assert!((p1 as u32) < rev);
assert!((p2 as u32) < rev);
vec![p1 as u32, p2 as u32]
}
}
/// Insert a new revision with given parents at the end.
fn insert(&self, parents: Vec<u32>) {
let mut inserted = self.inserted.borrow_mut();
inserted.push(parents);
}
}

View File

@ -37,6 +37,7 @@ py_module_initializer!(bindings, initbindings, PyInit_bindings, |py, m| {
"revisionstore",
pyrevisionstore::init_module(py, &name)?,
)?;
m.add(py, "revlogindex", pyrevlogindex::init_module(py, &name)?)?;
m.add(py, "stackdesc", pystackdesc::init_module(py, &name)?)?;
m.add(py, "treestate", pytreestate::init_module(py, &name)?)?;
m.add(py, "vlq", pyvlq::init_module(py, &name)?)?;

View File

@ -15,7 +15,7 @@ use std::collections::BinaryHeap;
use std::fmt::{self, Debug};
use std::ops::{Bound, RangeBounds, RangeInclusive};
type Id = u64;
pub type Id = u64;
/// Range `low..=high`. `low` must be <= `high`.
#[derive(Copy, Clone, Debug, Eq)]