extlib: remove large parts of cstore/ctreemanifest

Summary:
This code is effectively unused. The only bit still relevant is that EdenFS
still depends on the Manifest class to parse a manifest.

Reviewed By: DurhamG

Differential Revision: D24037723

fbshipit-source-id: 901ae2ffc8960a95ec655a2e14d79afb8d32dcab
This commit is contained in:
Xavier Deguillard 2020-10-02 10:43:21 -07:00 committed by Facebook GitHub Bot
parent c16ab69c6a
commit 30ef9bdcf1
18 changed files with 1 additions and 4445 deletions

View File

@ -42,7 +42,7 @@
#include "eden/fs/utils/UnboundedQueueExecutor.h"
#include "edenscm/hgext/extlib/cstore/store.h" // @manual=//eden/scm:datapack
#include "edenscm/hgext/extlib/ctreemanifest/treemanifest.h" // @manual=//eden/scm:datapack
#include "edenscm/hgext/extlib/ctreemanifest/manifest.h" // @manual=//eden/scm:datapack
using folly::Future;
using folly::IOBuf;

View File

@ -6,14 +6,10 @@
add_library(
datapack
STATIC
cstore/datapackstore.cpp
cstore/deltachain.cpp
cstore/uniondatapackstore.cpp
ctreemanifest/manifest.cpp
ctreemanifest/manifest_entry.cpp
ctreemanifest/manifest_fetcher.cpp
ctreemanifest/manifest_ptr.cpp
ctreemanifest/treemanifest.cpp
)
target_link_libraries(
datapack

View File

@ -1,233 +0,0 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This software may be used and distributed according to the terms of the
* GNU General Public License version 2.
*/
// datapackstore.cpp - implementation of a datapack store
// no-check-code
#include "edenscm/hgext/extlib/cstore/datapackstore.h"
#include <stdlib.h>
#include <sys/types.h>
#include <stdexcept>
#include "edenscm/hgext/extlib/cstore/key.h"
#include "lib/clib/portability/dirent.h"
using std::chrono::steady_clock;
namespace {
// This deleter helps us be more exception safe without needing
// to add explicit try/catch statements
struct Deleter {
void operator()(DIR* dir) {
closedir(dir);
}
};
std::unordered_set<std::string> getAvailablePackFileNames(
const std::string& path) {
std::unordered_set<std::string> results;
std::string packpath(path);
if (!path.empty() && path[path.size() - 1] != '/') {
packpath.push_back('/');
}
size_t dirLength = packpath.size();
std::unique_ptr<DIR, Deleter> dirp(opendir(path.c_str()));
if (!dirp) {
return results;
}
dirent* entry;
while ((entry = readdir(dirp.get())) != nullptr) {
size_t fileLength = strlen(entry->d_name);
if (fileLength < PACKSUFFIXLEN) {
continue;
}
if (strcmp(entry->d_name + fileLength - PACKSUFFIXLEN, PACKSUFFIX) != 0) {
continue;
}
packpath.append(entry->d_name, fileLength - PACKSUFFIXLEN);
results.insert(packpath);
packpath.erase(dirLength);
}
return results;
}
} // namespace
DatapackStore::DatapackStore(
const std::string& path,
bool removeDeadPackFilesOnRefresh)
: path_(path), removeOnRefresh_(removeDeadPackFilesOnRefresh) {
// Find pack files in path
auto files = getAvailablePackFileNames(path);
for (const auto& packpath : files) {
addPack(packpath);
}
}
std::shared_ptr<datapack_handle_t> DatapackStore::addPack(
const std::string& path) {
std::string idxPath(path + INDEXSUFFIX);
std::string dataPath(path + PACKSUFFIX);
auto cpack = open_datapack(
(char*)idxPath.c_str(),
idxPath.size(),
(char*)dataPath.c_str(),
dataPath.size());
// open_datapack might fail and return a NULL handle;
if (!cpack) {
return nullptr;
}
// set up the shared_ptr Deleter to close the datapack
// when there are no more references
std::shared_ptr<datapack_handle_t> pack(cpack, close_datapack);
if (pack && pack->status == DATAPACK_HANDLE_OK) {
packs_[path] = pack;
return pack;
}
return nullptr;
}
DatapackStore::~DatapackStore() {}
DeltaChainIterator DatapackStore::getDeltaChain(const Key& key) {
std::shared_ptr<DeltaChain> chain = this->getDeltaChainRaw(key);
if (chain->status() == GET_DELTA_CHAIN_OK) {
return DeltaChainIterator(chain);
}
throw MissingKeyError("unable to find delta chain");
}
std::shared_ptr<DeltaChain> DatapackStore::getDeltaChainRaw(const Key& key) {
for (const auto& it : packs_) {
auto& pack = it.second;
auto chain = getdeltachain(pack.get(), (const uint8_t*)key.node);
if (chain.code == GET_DELTA_CHAIN_OOM) {
throw std::runtime_error("out of memory");
} else if (chain.code == GET_DELTA_CHAIN_NOT_FOUND) {
freedeltachain(chain);
continue;
} else if (chain.code != GET_DELTA_CHAIN_OK) {
freedeltachain(chain);
continue;
}
// Pass ownership of chain to CDeltaChain
return std::make_shared<CDeltaChain>(chain);
}
// Check if there are new packs available
auto rescanned = rescan();
for (const auto& pack : rescanned) {
auto chain = getdeltachain(pack.get(), (const uint8_t*)key.node);
if (chain.code == GET_DELTA_CHAIN_OOM) {
throw std::runtime_error("out of memory");
} else if (chain.code == GET_DELTA_CHAIN_NOT_FOUND) {
freedeltachain(chain);
continue;
} else if (chain.code != GET_DELTA_CHAIN_OK) {
freedeltachain(chain);
continue;
}
// Pass ownership of chain to CDeltaChain
return std::make_shared<CDeltaChain>(chain);
}
return std::make_shared<CDeltaChain>(GET_DELTA_CHAIN_NOT_FOUND);
}
Key* DatapackStoreKeyIterator::next() {
Key* key;
while ((key = _missing.next()) != NULL) {
if (!_store.contains(*key)) {
return key;
}
}
return NULL;
}
bool DatapackStore::contains(const Key& key) {
for (auto& it : packs_) {
auto& pack = it.second;
pack_index_entry_t packindex;
if (find(pack.get(), (uint8_t*)key.node, &packindex)) {
return true;
}
}
// Check if there are new packs available
auto rescanned = rescan();
for (auto& pack : rescanned) {
pack_index_entry_t packindex;
if (find(pack.get(), (uint8_t*)key.node, &packindex)) {
return true;
}
}
return false;
}
std::shared_ptr<KeyIterator> DatapackStore::getMissing(KeyIterator& missing) {
return std::make_shared<DatapackStoreKeyIterator>(*this, missing);
}
std::vector<std::shared_ptr<datapack_handle_t>> DatapackStore::rescan() {
constexpr auto PACK_REFRESH_RATE = std::chrono::milliseconds(100);
auto now = steady_clock::now();
std::vector<std::shared_ptr<datapack_handle_t>> newPacks;
if (nextRefresh_ <= now) {
auto availablePacks = getAvailablePackFileNames(path_);
// Garbage collect removed pack files
if (removeOnRefresh_) {
auto it = packs_.begin();
while (it != packs_.end()) {
if (availablePacks.find(it->first) == availablePacks.end()) {
// This pack file no longer exists, we
// can forget it
it = packs_.erase(it);
continue;
}
++it;
}
}
// Add any newly discovered files
for (const auto& packPath : availablePacks) {
if (packs_.find(packPath) == packs_.end()) {
// We haven't loaded this path yet, do so now
auto newPack = addPack(packPath);
if (newPack) {
newPacks.push_back(std::move(newPack));
}
}
}
nextRefresh_ = now + PACK_REFRESH_RATE;
}
return newPacks;
}
void DatapackStore::refresh() {
rescan();
}
void DatapackStore::markForRefresh() {
nextRefresh_ = steady_clock::time_point();
}

View File

@ -1,83 +0,0 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This software may be used and distributed according to the terms of the
* GNU General Public License version 2.
*/
// datapackstore.h - c++ declarations for a data pack store
// no-check-code
#ifndef FBHGEXT_DATAPACKSTORE_H
#define FBHGEXT_DATAPACKSTORE_H
extern "C" {
#include "lib/cdatapack/cdatapack.h"
}
#include <chrono>
#include <memory>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include "edenscm/hgext/extlib/cstore/datastore.h"
#include "edenscm/hgext/extlib/cstore/key.h"
#include "lib/clib/portability/portability.h"
class DatapackStore;
class DatapackStoreKeyIterator : public KeyIterator {
private:
DatapackStore& _store;
KeyIterator& _missing;
public:
DatapackStoreKeyIterator(DatapackStore& store, KeyIterator& missing)
: _store(store), _missing(missing) {}
Key* next() override;
};
/* Manages access to a directory of datapack files. */
class DatapackStore : public DataStore {
private:
std::string path_;
std::chrono::steady_clock::time_point nextRefresh_;
bool removeOnRefresh_{false};
std::unordered_map<std::string, std::shared_ptr<datapack_handle_t>> packs_;
std::shared_ptr<datapack_handle_t> addPack(const std::string& path);
std::vector<std::shared_ptr<datapack_handle_t>> rescan();
public:
~DatapackStore();
/** Initialize the store for the specified path.
* If removeDeadPackFilesOnRefresh is set to true (NOT the default),
* then the rescan() method can choose to unmap pack files that
* have been deleted. Since the DataStore API doesn't provide
* for propagating ownership out through the DeltaChain and DeltaChain
* iterator, it is not safe to removeDeadPackFilesOnRefresh if the calling
* code is keeping longlived references to those values; it is the
* responsibility of the calling code to ensure that the lifetime is
* managed correctly as it cannot be enforced automatically without
* restructing this API.
*/
explicit DatapackStore(
const std::string& path,
bool removeDeadPackFilesOnRefresh = false);
DeltaChainIterator getDeltaChain(const Key& key) override;
std::shared_ptr<KeyIterator> getMissing(KeyIterator& missing) override;
std::shared_ptr<DeltaChain> getDeltaChainRaw(const Key& key) override;
bool contains(const Key& key) override;
void markForRefresh() override;
void refresh() override;
};
#endif // FBHGEXT_DATAPACKSTORE_H

View File

@ -1,39 +0,0 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This software may be used and distributed according to the terms of the
* GNU General Public License version 2.
*/
// datastore.h - c++ declarations for a data store
// no-check-code
#ifndef FBHGEXT_DATASTORE_H
#define FBHGEXT_DATASTORE_H
#include <memory>
#include "edenscm/hgext/extlib/cstore/deltachain.h"
#include "edenscm/hgext/extlib/cstore/key.h"
class DataStore {
protected:
DataStore() {}
public:
virtual ~DataStore() {}
virtual DeltaChainIterator getDeltaChain(const Key& key) = 0;
virtual std::shared_ptr<DeltaChain> getDeltaChainRaw(const Key& key) = 0;
virtual std::shared_ptr<KeyIterator> getMissing(KeyIterator& missing) = 0;
virtual bool contains(const Key& key) = 0;
virtual void markForRefresh() = 0;
virtual void refresh() {}
};
#endif // FBHGEXT_DATASTORE_H

View File

@ -1,54 +0,0 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This software may be used and distributed according to the terms of the
* GNU General Public License version 2.
*/
// deltachain.cpp - c++ implementation of deltachain and related classes
// no-check-code
#include "edenscm/hgext/extlib/cstore/deltachain.h"
DeltaChainIterator::~DeltaChainIterator() {}
DeltaChainLink DeltaChainIterator::next() {
std::shared_ptr<DeltaChain> chain = _chains.back();
if (_index >= chain->linkcount()) {
// If we're not at the end, and we have a callback to fetch more, do the
// fetch.
bool refreshed = false;
if (chain->linkcount() > 0) {
DeltaChainLink result = chain->getlink(_index - 1);
const uint8_t* deltabasenode = result.deltabasenode();
if (memcmp(deltabasenode, NULLID, BIN_NODE_SIZE) != 0) {
Key key(
result.filename(),
result.filenamesz(),
(const char*)deltabasenode,
BIN_NODE_SIZE);
std::shared_ptr<DeltaChain> newChain = this->getNextChain(key);
if (newChain->status() == GET_DELTA_CHAIN_OK) {
// Do not free the old chain, since the iterator consumer may
// still be holding references to it.
_chains.push_back(newChain);
chain = _chains.back();
_index = 0;
refreshed = true;
}
}
}
if (!refreshed) {
return DeltaChainLink(NULL);
}
}
DeltaChainLink result = chain->getlink(_index);
_index++;
return result;
}

View File

@ -1,185 +0,0 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This software may be used and distributed according to the terms of the
* GNU General Public License version 2.
*/
// deltachain.h - c++ declaration of deltachain and related classes
// no-check-code
#ifndef FBHGEXT_DELTACHAIN_H
#define FBHGEXT_DELTACHAIN_H
extern "C" {
#include "lib/cdatapack/cdatapack.h"
}
#include <memory>
#include <vector>
#include "edenscm/hgext/extlib/cstore/key.h"
/*
* Wrapper around delta chain link, both C and Python
*/
class DeltaChainLink {
private:
const char *_filename, *_deltabasefilename;
const uint8_t *_node, *_deltabasenode, *_delta;
uint16_t _filenamesz, _deltabasefilenamesz;
uint64_t _deltasz;
public:
explicit DeltaChainLink(delta_chain_link_t* link) {
if (link) {
_filename = link->filename;
_deltabasefilename = link->filename;
_node = link->node;
_deltabasenode = link->deltabase_node;
_delta = link->delta;
_filenamesz = link->filename_sz;
_deltabasefilenamesz = link->filename_sz;
_deltasz = link->delta_sz;
} else {
_filename = NULL;
_deltabasefilename = NULL;
_node = NULL;
_deltabasenode = NULL;
_delta = NULL;
_filenamesz = 0;
_deltabasefilenamesz = 0;
_deltasz = 0;
}
}
DeltaChainLink(
const char* filename,
const char* deltabasefilename,
const uint8_t* node,
const uint8_t* deltabasenode,
const uint8_t* delta,
uint16_t filenamesz,
uint16_t deltabasefilenamesz,
uint64_t deltasz)
: _filename(filename),
_deltabasefilename(deltabasefilename),
_node(node),
_deltabasenode(deltabasenode),
_delta(delta),
_filenamesz(filenamesz),
_deltabasefilenamesz(deltabasefilenamesz),
_deltasz(deltasz) {}
~DeltaChainLink() = default;
const char* filename() const {
return _filename;
}
const char* deltabasefilename() const {
return _deltabasefilename;
}
const uint8_t* node() const {
return _node;
}
const uint8_t* deltabasenode() const {
return _deltabasenode;
}
const uint8_t* delta() const {
return _delta;
}
uint16_t filenamesz() const {
return _filenamesz;
}
uint16_t deltabasefilenamesz() const {
return _deltabasefilenamesz;
}
uint64_t deltasz() const {
return _deltasz;
}
bool isdone() const {
return (_filename == NULL);
}
};
/*
* Abstract delta chain class
*/
class DeltaChain {
protected:
DeltaChain() {}
public:
virtual ~DeltaChain() {}
virtual const DeltaChainLink getlink(const size_t) const = 0;
virtual size_t linkcount() const = 0;
virtual get_delta_chain_code_t status() const = 0;
};
/*
* Wrapper around C delta chain
* CDeltaChain takes ownership of delta_chain_t
*/
class CDeltaChain : public DeltaChain {
private:
delta_chain_t _chain;
public:
// The constructor does a shallow copy of the delta chain and since the
// ownership is taken by this class it is responsible for memory management
explicit CDeltaChain(delta_chain_t chain) : _chain(chain) {}
explicit CDeltaChain(get_delta_chain_code_t /*error*/)
: _chain{GET_DELTA_CHAIN_NOT_FOUND, nullptr, 0} {}
// Memory of _chain has to be deallocated because it is a C struct that
// contains an array of delta_chain_link_t's
~CDeltaChain() {
freedeltachain(_chain);
}
const DeltaChainLink getlink(const size_t idx) const override {
return DeltaChainLink(&(_chain.delta_chain_links[idx]));
}
size_t linkcount() const override {
return _chain.links_count;
}
get_delta_chain_code_t status() const override {
return _chain.code;
}
};
class DeltaChainIterator {
private:
size_t _index;
protected:
std::vector<std::shared_ptr<DeltaChain>> _chains;
DeltaChainIterator() : _index(0) {}
virtual std::shared_ptr<DeltaChain> getNextChain(const Key& /*key*/) {
return std::make_shared<CDeltaChain>(GET_DELTA_CHAIN_NOT_FOUND);
}
public:
explicit DeltaChainIterator(std::shared_ptr<DeltaChain> chain) : _index(0) {
_chains.push_back(chain);
}
virtual ~DeltaChainIterator();
DeltaChainLink next();
};
#endif // FBHGEXT_DELTACHAIN_H

View File

@ -1,37 +0,0 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This software may be used and distributed according to the terms of the
* GNU General Public License version 2.
*/
// match.h - c++ declarations for a data store
// no-check-code
#ifndef FBHGEXT_CSTORE_MATCH_H
#define FBHGEXT_CSTORE_MATCH_H
class Matcher {
public:
virtual ~Matcher() {}
virtual bool matches(const std::string& path) = 0;
virtual bool matches(const char* path, const size_t pathlen) = 0;
virtual bool visitdir(const std::string& path) = 0;
};
class AlwaysMatcher : public Matcher {
public:
AlwaysMatcher() {}
~AlwaysMatcher() override {}
bool matches(const std::string& /*path*/) override {
return true;
}
bool matches(const char* /*path*/, const size_t /*pathlen*/) override {
return true;
}
bool visitdir(const std::string& /*path*/) override {
return true;
}
};
#endif // FBHGEXT_CSTORE_MATCH_H

View File

@ -1,539 +0,0 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This software may be used and distributed according to the terms of the
* GNU General Public License version 2.
*/
// py-cstore.cpp - c++ implementation of a store
// no-check-code
#ifndef FBHGEXT_CSTORE_PY_DATAPACKSTORE_H
#define FBHGEXT_CSTORE_PY_DATAPACKSTORE_H
// The PY_SSIZE_T_CLEAN define must be defined before the Python.h include,
// as per the documentation.
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <memory>
#include <string>
extern "C" {
#include "lib/cdatapack/cdatapack.h"
}
#include "edenscm/hgext/extlib/cstore/datapackstore.h"
#include "edenscm/hgext/extlib/cstore/datastore.h"
#include "edenscm/hgext/extlib/cstore/key.h"
#include "edenscm/hgext/extlib/cstore/py-structs.h"
#include "edenscm/hgext/extlib/cstore/pythondatastore.h"
#include "edenscm/hgext/extlib/cstore/pythonkeyiterator.h"
#include "edenscm/hgext/extlib/cstore/pythonutil.h"
#include "edenscm/hgext/extlib/cstore/uniondatapackstore.h"
#include "edenscm/hgext/extlib/cstore/util.h"
// --------- DatapackStore Implementation ---------
/*
* Initializes the contents of a datapackstore
*/
static int datapackstore_init(py_datapackstore* self, PyObject* args) {
char* packdir;
Py_ssize_t packdirlen;
if (!PyArg_ParseTuple(args, "s#", &packdir, &packdirlen)) {
return -1;
}
// We have to manually call the member constructor, since the provided 'self'
// is just zerod out memory.
try {
std::string packdirstr(packdir, packdirlen);
new (&self->datapackstore) DatapackStore(packdirstr);
} catch (const std::exception& ex) {
PyErr_SetString(PyExc_RuntimeError, ex.what());
return -1;
}
return 0;
}
static void datapackstore_dealloc(py_datapackstore* self) {
// When py_datapackstore is free'd by python in the PyObject_Del below,
// its destructor is not called, so we need to manually destruct the members.
self->datapackstore.~DatapackStore();
PyObject_Del(self);
}
static PyObject* datapackstore_getdeltachain(
py_datapackstore* self,
PyObject* args) {
try {
char* name;
Py_ssize_t namelen;
char* node;
Py_ssize_t nodelen;
if (!PyArg_ParseTuple(args, "s#s#", &name, &namelen, &node, &nodelen)) {
return NULL;
}
Key key(name, namelen, node, nodelen);
DeltaChainIterator chain = self->datapackstore.getDeltaChain(key);
PythonObj resultChain = PyList_New(0);
size_t index = 0;
for (DeltaChainLink link = chain.next(); !link.isdone();
link = chain.next()) {
PythonObj name =
PyString_FromStringAndSize(link.filename(), link.filenamesz());
PythonObj retnode =
PyString_FromStringAndSize((const char*)link.node(), NODE_SZ);
PythonObj deltabasenode = PyString_FromStringAndSize(
(const char*)link.deltabasenode(), NODE_SZ);
PythonObj delta = PyString_FromStringAndSize(
(const char*)link.delta(), (Py_ssize_t)link.deltasz());
PythonObj tuple = PyTuple_Pack(
5,
(PyObject*)name,
(PyObject*)retnode,
(PyObject*)name,
(PyObject*)deltabasenode,
(PyObject*)delta);
if (PyList_Append((PyObject*)resultChain, tuple.returnval())) {
return NULL;
}
index++;
}
return resultChain.returnval();
} catch (const pyexception& ex) {
return NULL;
} catch (const MissingKeyError& ex) {
PyErr_SetString(PyExc_KeyError, ex.what());
return NULL;
} catch (const std::exception& ex) {
PyErr_SetString(PyExc_RuntimeError, ex.what());
return NULL;
}
}
static PyObject* datapackstore_getmissing(
py_datapackstore* self,
PyObject* keys) {
try {
PythonObj result = PyList_New(0);
PythonObj inputIterator = PyObject_GetIter(keys);
PythonKeyIterator keysIter(inputIterator);
std::shared_ptr<KeyIterator> missingIter =
self->datapackstore.getMissing(keysIter);
Key* key;
while ((key = missingIter->next()) != NULL) {
PythonObj missingKey = Py_BuildValue(
"(s#s#)", key->name.c_str(), key->name.size(), key->node, 20);
if (PyList_Append(result, (PyObject*)missingKey)) {
return NULL;
}
}
return result.returnval();
} catch (const pyexception& ex) {
return NULL;
} catch (const std::exception& ex) {
PyErr_SetString(PyExc_RuntimeError, ex.what());
return NULL;
}
}
static PyObject* datapackstore_markforrefresh(py_datapackstore* self) {
self->datapackstore.markForRefresh();
Py_RETURN_NONE;
}
// --------- DatapackStore Declaration ---------
static PyMethodDef datapackstore_methods[] = {
{"getdeltachain",
(PyCFunction)datapackstore_getdeltachain,
METH_VARARGS,
""},
{"getmissing", (PyCFunction)datapackstore_getmissing, METH_O, ""},
{"markforrefresh",
(PyCFunction)datapackstore_markforrefresh,
METH_NOARGS,
""},
{NULL, NULL}};
static PyTypeObject datapackstoreType = {
PyObject_HEAD_INIT(NULL) 0, /* ob_size */
"cstore.datapackstore", /* tp_name */
sizeof(py_datapackstore), /* tp_basicsize */
0, /* tp_itemsize */
(destructor)datapackstore_dealloc, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_compare */
0, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence - length/contains */
0, /* tp_as_mapping - getitem/setitem */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT, /* tp_flags */
"TODO", /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
datapackstore_methods, /* tp_methods */
0, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
(initproc)datapackstore_init, /* tp_init */
0, /* tp_alloc */
};
// --------- UnionDatapackStore Implementation ---------
static void addStore(py_uniondatapackstore* self, PythonObj store) {
PyObject* rawStore = (PyObject*)store;
// Record the substore references, so:
// A) We can decref them in case of an error.
// B) They don't get GC'd while the uniondatapackstore holds on to them.
int iscdatapack =
PyObject_IsInstance(rawStore, (PyObject*)&datapackstoreType);
switch (iscdatapack) {
case 1:
// Store is C datapack
{
self->cstores.push_back(store);
py_datapackstore* subStore = (py_datapackstore*)rawStore;
self->uniondatapackstore->addStore(&subStore->datapackstore);
}
break;
case 0:
// Store is PythonDataStore, it's memory management
// is performed by py_uniondatapackstore
{
std::shared_ptr<PythonDataStore> pystore =
std::make_shared<PythonDataStore>(store);
self->pystores.push_back(pystore);
self->uniondatapackstore->addStore(pystore.get());
}
break;
default:
// Error
throw std::logic_error("invalid store type passed to addStore");
}
}
/*
* Initializes the contents of a uniondatapackstore
*/
static int uniondatapackstore_init(
py_uniondatapackstore* self,
PyObject* args) {
PyObject* storeList;
if (!PyArg_ParseTuple(args, "O", &storeList)) {
return -1;
}
try {
// We have to manually call the member constructor, since the provided
// 'self' is just zerod out memory.
new (&self->uniondatapackstore)
std::shared_ptr<UnionDatapackStore>(new UnionDatapackStore());
new (&self->cstores) std::vector<PythonObj>();
new (&self->pystores) std::vector<std::shared_ptr<PythonDataStore>>();
PyObject* item;
PythonObj inputIterator = PyObject_GetIter(storeList);
while ((item = PyIter_Next(inputIterator)) != NULL) {
PythonObj store(item);
addStore(self, store);
}
} catch (const std::exception& ex) {
PyErr_SetString(PyExc_RuntimeError, ex.what());
return -1;
}
return 0;
}
static void uniondatapackstore_dealloc(py_uniondatapackstore* self) {
self->uniondatapackstore.~shared_ptr<UnionDatapackStore>();
self->cstores.~vector<PythonObj>();
self->pystores.~vector<std::shared_ptr<PythonDataStore>>();
PyObject_Del(self);
}
static PyObject* uniondatapackstore_addStore(
py_uniondatapackstore* self,
PyObject* storeObj) {
try {
PythonObj store(storeObj);
Py_INCREF(storeObj);
addStore(self, store);
Py_RETURN_NONE;
} catch (const pyexception& ex) {
return NULL;
} catch (const std::exception& ex) {
PyErr_SetString(PyExc_RuntimeError, ex.what());
return NULL;
}
}
static PyObject* uniondatapackstore_removeStore(
py_uniondatapackstore* self,
PyObject* storeObj) {
try {
PythonObj store(storeObj);
Py_INCREF(storeObj);
// Record the substore references, so:
// A) We can decref them in case of an error.
// B) They don't get GC'd while the uniondatapackstore holds on to them.
int iscdatapack =
PyObject_IsInstance(storeObj, (PyObject*)&datapackstoreType);
switch (iscdatapack) {
case 1:
// Store is C datapack
{
removeFromVector(self->cstores, store);
py_datapackstore* subStore = (py_datapackstore*)storeObj;
self->uniondatapackstore->removeStore(&subStore->datapackstore);
}
break;
case 0:
// Store is PythonDataStore, it's memory management
// is performed by py_uniondatapackstore
{
auto pystores = self->pystores;
for (auto it = pystores.begin(); it != pystores.end(); ++it) {
if ((*it)->getStore() == store) {
self->uniondatapackstore->removeStore((*it).get());
removeFromVector(self->pystores, *it);
break;
}
}
}
break;
default:
// Error
throw std::logic_error("invalid store type");
}
Py_RETURN_NONE;
} catch (const pyexception& ex) {
return NULL;
} catch (const std::exception& ex) {
PyErr_SetString(PyExc_RuntimeError, ex.what());
return NULL;
}
}
static PyObject* uniondatapackstore_get(
py_uniondatapackstore* self,
PyObject* args) {
try {
char* name;
Py_ssize_t namelen;
char* node;
Py_ssize_t nodelen;
if (!PyArg_ParseTuple(args, "s#s#", &name, &namelen, &node, &nodelen)) {
return NULL;
}
Key key(name, namelen, node, nodelen);
ConstantStringRef fulltext = self->uniondatapackstore->get(key);
return PyString_FromStringAndSize(fulltext.content(), fulltext.size());
} catch (const pyexception& ex) {
return NULL;
} catch (const MissingKeyError& ex) {
PyErr_SetString(PyExc_KeyError, ex.what());
return NULL;
} catch (const std::exception& ex) {
PyErr_SetString(PyExc_RuntimeError, ex.what());
return NULL;
}
}
static PyObject* uniondatapackstore_getdeltachain(
py_uniondatapackstore* self,
PyObject* args) {
try {
char* name;
Py_ssize_t namelen;
char* node;
Py_ssize_t nodelen;
if (!PyArg_ParseTuple(args, "s#s#", &name, &namelen, &node, &nodelen)) {
return NULL;
}
Key key(name, namelen, node, nodelen);
UnionDeltaChainIterator chain =
self->uniondatapackstore->getDeltaChain(key);
PythonObj resultChain = PyList_New(0);
for (DeltaChainLink link = chain.next(); !link.isdone();
link = chain.next()) {
PythonObj name =
PyString_FromStringAndSize(link.filename(), link.filenamesz());
PythonObj retnode =
PyString_FromStringAndSize((const char*)link.node(), NODE_SZ);
PythonObj deltabasenode = PyString_FromStringAndSize(
(const char*)link.deltabasenode(), NODE_SZ);
PythonObj delta = PyString_FromStringAndSize(
(const char*)link.delta(), (Py_ssize_t)link.deltasz());
PythonObj tuple = PyTuple_Pack(
5,
(PyObject*)name,
(PyObject*)retnode,
(PyObject*)name,
(PyObject*)deltabasenode,
(PyObject*)delta);
if (PyList_Append((PyObject*)resultChain, tuple.returnval())) {
return NULL;
}
}
return resultChain.returnval();
} catch (const pyexception& ex) {
return NULL;
} catch (const MissingKeyError& ex) {
PyErr_SetString(PyExc_KeyError, ex.what());
return NULL;
} catch (const std::exception& ex) {
PyErr_SetString(PyExc_RuntimeError, ex.what());
return NULL;
}
}
static PyObject* uniondatapackstore_getmissing(
py_uniondatapackstore* self,
PyObject* keys) {
try {
PythonObj result = PyList_New(0);
PythonObj inputIterator = PyObject_GetIter(keys);
PythonKeyIterator keysIter((PyObject*)inputIterator);
UnionDatapackStoreKeyIterator missingIter =
self->uniondatapackstore->getMissing(keysIter);
Key* key;
while ((key = missingIter.next()) != NULL) {
PythonObj missingKey = Py_BuildValue(
"(s#s#)", key->name.c_str(), key->name.size(), key->node, 20);
if (PyList_Append(result, (PyObject*)missingKey)) {
return NULL;
}
}
return result.returnval();
} catch (const pyexception& ex) {
return NULL;
} catch (const std::exception& ex) {
PyErr_SetString(PyExc_RuntimeError, ex.what());
return NULL;
}
}
static PyObject* uniondatapackstore_markforrefresh(
py_uniondatapackstore* self) {
self->uniondatapackstore->markForRefresh();
Py_RETURN_NONE;
}
static PyObject* uniondatapackstore_getmetrics(
py_uniondatapackstore* /*self*/) {
return PyDict_New();
}
// --------- UnionDatapackStore Declaration ---------
static PyMethodDef uniondatapackstore_methods[] = {
{"get", (PyCFunction)uniondatapackstore_get, METH_VARARGS, ""},
{"addstore", (PyCFunction)uniondatapackstore_addStore, METH_O, ""},
{"removestore", (PyCFunction)uniondatapackstore_removeStore, METH_O, ""},
{"getdeltachain",
(PyCFunction)uniondatapackstore_getdeltachain,
METH_VARARGS,
""},
{"getmissing", (PyCFunction)uniondatapackstore_getmissing, METH_O, ""},
{"markforrefresh",
(PyCFunction)uniondatapackstore_markforrefresh,
METH_NOARGS,
""},
{"getmetrics", (PyCFunction)uniondatapackstore_getmetrics, METH_NOARGS, ""},
{NULL, NULL}};
static PyTypeObject uniondatapackstoreType = {
PyObject_HEAD_INIT(NULL) 0, /* ob_size */
"cstore.uniondatapackstore", /* tp_name */
sizeof(py_uniondatapackstore), /* tp_basicsize */
0, /* tp_itemsize */
(destructor)uniondatapackstore_dealloc, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_compare */
0, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence - length/contains */
0, /* tp_as_mapping - getitem/setitem */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT, /* tp_flags */
"TODO", /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
uniondatapackstore_methods, /* tp_methods */
0, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
(initproc)uniondatapackstore_init, /* tp_init */
0, /* tp_alloc */
};
#endif /* FBHGEXT_CSTORE_PY_DATAPACKSTORE_H */

View File

@ -1,45 +0,0 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This software may be used and distributed according to the terms of the
* GNU General Public License version 2.
*/
// py-structs.h - c++ headers for store python objects
// no-check-code
#ifndef FBHGEXT_CSTORE_PY_STRUCTS_H
#define FBHGEXT_CSTORE_PY_STRUCTS_H
#include <memory>
#include "edenscm/hgext/extlib/cstore/datapackstore.h"
#include "edenscm/hgext/extlib/cstore/pythondatastore.h"
#include "edenscm/hgext/extlib/cstore/pythonutil.h"
#include "edenscm/hgext/extlib/cstore/uniondatapackstore.h"
// clang-format off
// clang thinks that PyObject_HEAD should be on the same line as the next line
// since there is no semicolong after it. There is no semicolon because
// PyObject_HEAD macro already contains one and MSVC does not support
// extra semicolons.
struct py_datapackstore {
PyObject_HEAD
DatapackStore datapackstore;
};
// clang-format on
// clang-format off
struct py_uniondatapackstore {
PyObject_HEAD
std::shared_ptr<UnionDatapackStore> uniondatapackstore;
// Keep a reference to the python objects so we can decref them later.
std::vector<PythonObj> cstores;
std::vector<std::shared_ptr<PythonDataStore>> pystores;
};
// clang-format on
#endif // FBHGEXT_CSTORE_PY_STRUCTS_H

File diff suppressed because it is too large Load Diff

View File

@ -1,45 +0,0 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This software may be used and distributed according to the terms of the
* GNU General Public License version 2.
*/
// pythonkeyiterator.h - c++ implementation of python key iterator
// no-check-code
#ifndef FBHGEXT_PYTHONKEYITERATOR_H
#define FBHGEXT_PYTHONKEYITERATOR_H
#include "edenscm/hgext/extlib/cstore/pythonutil.h"
class PythonKeyIterator : public KeyIterator {
private:
PythonObj _input;
Key _current;
public:
PythonKeyIterator(PythonObj input) : _input(input) {}
Key* next() {
PyObject* item;
while ((item = PyIter_Next((PyObject*)_input)) != NULL) {
PythonObj itemObj(item);
char* name;
Py_ssize_t namelen;
char* node;
Py_ssize_t nodelen;
if (!PyArg_ParseTuple(item, "s#s#", &name, &namelen, &node, &nodelen)) {
throw pyexception();
}
_current = Key(name, namelen, node, nodelen);
return &_current;
}
return NULL;
}
};
#endif // FBHGEXT_PYTHONKEYITERATOR_H

View File

@ -1,165 +0,0 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This software may be used and distributed according to the terms of the
* GNU General Public License version 2.
*/
// uniondatapackstore.cpp - implementation of a union datapack store
// no-check-code
#include "edenscm/hgext/extlib/cstore/uniondatapackstore.h"
#include "edenscm/hgext/extlib/cstore/util.h"
#include <algorithm>
#include <memory>
extern "C" {
#include "eden/scm/edenscm/mercurial/mpatch.h"
}
UnionDatapackStore::UnionDatapackStore() {}
UnionDatapackStore::UnionDatapackStore(std::vector<DataStore*>& stores)
: _stores(stores) {}
UnionDatapackStore::~UnionDatapackStore() {
// TODO: we should manage the substore lifetimes here, but because they are
// also controlled by Python, we need to let python handle it and manage the
// refcount in the py_uniondatapackstore type.
}
mpatch_flist* getNextLink(void* container, ssize_t index) {
std::vector<DeltaChainLink>* links = (std::vector<DeltaChainLink>*)container;
if (index < 0 || (size_t)index >= links->size()) {
return NULL;
}
DeltaChainLink link = links->at(index);
struct mpatch_flist* res;
if ((mpatch_decode(
(const char*)link.delta(), (ssize_t)link.deltasz(), &res)) < 0) {
throw std::logic_error("invalid patch during patch application");
}
return res;
}
ConstantStringRef UnionDatapackStore::get(const Key& key) {
UnionDeltaChainIterator chain = this->getDeltaChain(key);
std::vector<DeltaChainLink> links;
for (DeltaChainLink link = chain.next(); !link.isdone();
link = chain.next()) {
links.push_back(link);
}
DeltaChainLink fulltextLink = links.back();
links.pop_back();
// Short circuit and just return the full text if it's one long
if (links.size() == 0) {
return ConstantStringRef(
(const char*)fulltextLink.delta(), (size_t)fulltextLink.deltasz());
}
std::reverse(links.begin(), links.end());
mpatch_flist* patch = mpatch_fold(&links, getNextLink, 0, links.size());
if (!patch) { /* error already set or memory error */
throw std::logic_error("mpatch failed to fold patches");
}
ssize_t outlen = mpatch_calcsize((ssize_t)fulltextLink.deltasz(), patch);
if (outlen < 0) {
mpatch_lfree(patch);
throw std::logic_error("mpatch failed to calculate size");
}
auto result = std::make_shared<std::string>(outlen, '\0');
if (mpatch_apply(
&(*result)[0],
(const char*)fulltextLink.delta(),
(ssize_t)fulltextLink.deltasz(),
patch) < 0) {
mpatch_lfree(patch);
throw std::logic_error("mpatch failed to apply patches");
}
mpatch_lfree(patch);
return ConstantStringRef(result);
}
std::shared_ptr<DeltaChain> UnionDeltaChainIterator::getNextChain(
const Key& key) {
for (std::vector<DataStore*>::iterator it = _store._stores.begin();
it != _store._stores.end();
it++) {
DataStore* substore = *it;
std::shared_ptr<DeltaChain> chain = substore->getDeltaChainRaw(key);
if (chain->status() == GET_DELTA_CHAIN_OK) {
return chain;
}
}
throw MissingKeyError("unable to find delta chain");
}
UnionDeltaChainIterator UnionDatapackStore::getDeltaChain(const Key& key) {
return UnionDeltaChainIterator(*this, key);
}
Key* UnionDatapackStoreKeyIterator::next() {
Key* key;
while ((key = _missing.next()) != NULL) {
if (!_store.contains(*key)) {
return key;
}
}
return NULL;
}
bool UnionDatapackStore::contains(const Key& key) {
for (std::vector<DataStore*>::iterator it = _stores.begin();
it != _stores.end();
it++) {
DataStore* substore = *it;
if (substore->contains(key)) {
return true;
}
}
return false;
}
UnionDatapackStoreKeyIterator UnionDatapackStore::getMissing(
KeyIterator& missing) {
return UnionDatapackStoreKeyIterator(*this, missing);
}
void UnionDatapackStore::markForRefresh() {
for (std::vector<DataStore*>::iterator it = _stores.begin();
it != _stores.end();
it++) {
DataStore* substore = *it;
substore->markForRefresh();
}
}
void UnionDatapackStore::addStore(DataStore* store) {
_stores.push_back(store);
}
void UnionDatapackStore::removeStore(DataStore* store) {
removeFromVector<DataStore*>(_stores, store);
}
void UnionDatapackStore::refresh() {
for (auto* store : _stores) {
store->refresh();
}
}

View File

@ -1,79 +0,0 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This software may be used and distributed according to the terms of the
* GNU General Public License version 2.
*/
// uniondatapackstore.h - c++ declarations for a union datapack store
// no-check-code
#ifndef FBHGEXT_CSTORE_UNIONDATAPACKSTORE_H
#define FBHGEXT_CSTORE_UNIONDATAPACKSTORE_H
#include <cstring>
#include <stdexcept>
#include <vector>
extern "C" {
#include "lib/cdatapack/cdatapack.h"
}
#include "edenscm/hgext/extlib/cstore/datapackstore.h"
#include "edenscm/hgext/extlib/cstore/key.h"
#include "edenscm/hgext/extlib/cstore/store.h"
class UnionDatapackStore;
class UnionDatapackStoreKeyIterator : public KeyIterator {
private:
UnionDatapackStore& _store;
KeyIterator& _missing;
public:
UnionDatapackStoreKeyIterator(UnionDatapackStore& store, KeyIterator& missing)
: _store(store), _missing(missing) {}
Key* next() override;
};
class UnionDeltaChainIterator : public DeltaChainIterator {
private:
UnionDatapackStore& _store;
protected:
std::shared_ptr<DeltaChain> getNextChain(const Key& key) override;
public:
UnionDeltaChainIterator(UnionDatapackStore& store, const Key& key)
: DeltaChainIterator(), _store(store) {
_chains.push_back(this->getNextChain(key));
}
};
class UnionDatapackStore : public Store {
public:
std::vector<DataStore*> _stores;
UnionDatapackStore();
UnionDatapackStore(std::vector<DataStore*>& stores);
~UnionDatapackStore() override;
ConstantStringRef get(const Key& key) override;
UnionDeltaChainIterator getDeltaChain(const Key& key);
bool contains(const Key& key);
UnionDatapackStoreKeyIterator getMissing(KeyIterator& missing);
void markForRefresh();
void addStore(DataStore* store);
void removeStore(DataStore* store);
void refresh();
};
#endif // FBHGEXT_CSTORE_UNIONDATAPACKSTORE_H

View File

@ -1,16 +0,0 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This software may be used and distributed according to the terms of the
* GNU General Public License version 2.
*/
template <class T>
void removeFromVector(std::vector<T>& vec, T& item) {
for (auto it = vec.begin(); it != vec.end(); ++it) {
if (*it == item) {
vec.erase(it);
break;
}
}
}

View File

@ -1,755 +0,0 @@
// Copyright (c) 2004-present, Facebook, Inc.
// All Rights Reserved.
//
// This software may be used and distributed according to the terms of the
// GNU General Public License version 2 or any later version.
// treemanifest.cpp - c++ implementation of a tree manifest
// no-check-code
#include "edenscm/hgext/extlib/ctreemanifest/treemanifest.h"
#include <cassert>
#include <limits>
/**
* Helper function that performs the actual recursion on the tree entries.
*/
void treemanifest_diffrecurse(
Manifest* selfmf,
Manifest* othermf,
std::string& path,
DiffResult& diff,
const ManifestFetcher& fetcher,
bool clean,
Matcher& matcher) {
ManifestIterator selfiter;
ManifestIterator otheriter;
if (selfmf != NULL) {
selfiter = selfmf->getIterator();
}
if (othermf != NULL) {
otheriter = othermf->getIterator();
}
// Iterate through both directory contents
while (!selfiter.isfinished() || !otheriter.isfinished()) {
int cmp = 0;
ManifestEntry* selfentry = NULL;
std::string selfbinnode;
if (!selfiter.isfinished()) {
cmp--;
selfentry = selfiter.currentvalue();
if (selfentry->hasNode()) {
selfbinnode = binfromhex(selfentry->get_node());
} else {
assert(selfentry->isdirectory());
}
}
ManifestEntry* otherentry = NULL;
std::string otherbinnode;
if (!otheriter.isfinished()) {
cmp++;
otherentry = otheriter.currentvalue();
if (otherentry->hasNode()) {
otherbinnode = binfromhex(otherentry->get_node());
} else {
assert(otherentry->isdirectory());
}
}
// If both sides are present, cmp == 0, so do a filename comparison
if (cmp == 0) {
cmp = strcmp(selfentry->filename, otherentry->filename);
}
size_t originalpathsize = path.size();
if (cmp < 0) {
// selfentry should be processed first and only exists in self
selfentry->appendtopath(path);
if (selfentry->isdirectory()) {
if (matcher.visitdir(path)) {
Manifest* selfchildmanifest =
selfentry->get_manifest(fetcher, path.c_str(), path.size());
treemanifest_diffrecurse(
selfchildmanifest, NULL, path, diff, fetcher, clean, matcher);
}
} else if (matcher.matches(path)) {
diff.add(path, selfbinnode.c_str(), selfentry->flag, NULL, NULL);
}
selfiter.next();
} else if (cmp > 0) {
// otherentry should be processed first and only exists in other
otherentry->appendtopath(path);
if (otherentry->isdirectory()) {
if (matcher.visitdir(path)) {
Manifest* otherchildmanifest =
otherentry->get_manifest(fetcher, path.c_str(), path.size());
treemanifest_diffrecurse(
NULL, otherchildmanifest, path, diff, fetcher, clean, matcher);
}
} else if (matcher.matches(path)) {
diff.add(path, NULL, NULL, otherbinnode.c_str(), otherentry->flag);
}
otheriter.next();
} else {
// Append the non-directory form to the path when possible
if (!selfentry->isdirectory()) {
selfentry->appendtopath(path);
} else {
otherentry->appendtopath(path);
}
// Filenames match - now compare directory vs file
if (selfentry->isdirectory() && otherentry->isdirectory()) {
// Both are directories - recurse
if (matcher.visitdir(path) &&
(selfbinnode != otherbinnode || clean || selfbinnode.size() == 0)) {
Manifest* selfchildmanifest =
selfentry->get_manifest(fetcher, path.c_str(), path.size());
Manifest* otherchildmanifest =
otherentry->get_manifest(fetcher, path.c_str(), path.size());
treemanifest_diffrecurse(
selfchildmanifest,
otherchildmanifest,
path,
diff,
fetcher,
clean,
matcher);
}
} else if (selfentry->isdirectory() && !otherentry->isdirectory()) {
if (matcher.matches(path)) {
// self is directory, other is not - process other then self
diff.add(path, NULL, NULL, otherbinnode.c_str(), otherentry->flag);
}
if (matcher.visitdir(path)) {
path.append(1, '/');
Manifest* selfchildmanifest =
selfentry->get_manifest(fetcher, path.c_str(), path.size());
treemanifest_diffrecurse(
selfchildmanifest, NULL, path, diff, fetcher, clean, matcher);
}
} else if (!selfentry->isdirectory() && otherentry->isdirectory()) {
if (matcher.matches(path)) {
// self is not directory, other is - process self then other
diff.add(path, selfbinnode.c_str(), selfentry->flag, NULL, NULL);
}
if (matcher.visitdir(path)) {
path.append(1, '/');
Manifest* otherchildmanifest =
otherentry->get_manifest(fetcher, path.c_str(), path.size());
treemanifest_diffrecurse(
NULL, otherchildmanifest, path, diff, fetcher, clean, matcher);
}
} else {
// both are files
if (matcher.matches(path)) {
bool flagsdiffer =
((selfentry->flag && otherentry->flag &&
*selfentry->flag != *otherentry->flag) ||
((bool)selfentry->flag != (bool)otherentry->flag));
if (selfbinnode != otherbinnode || flagsdiffer) {
diff.add(
path,
selfbinnode.c_str(),
selfentry->flag,
otherbinnode.c_str(),
otherentry->flag);
} else if (clean) {
diff.addclean(path);
}
}
}
selfiter.next();
otheriter.next();
}
path.erase(originalpathsize);
}
}
FindResult treemanifest::find(
ManifestEntry* manifestentry,
PathIterator& path,
FindMode findMode,
FindContext* findContext,
FindResult (*callback)(
Manifest* manifest,
const char* filename,
size_t filenamelen,
FindContext* findContext,
ManifestPtr* resultManifest),
ManifestPtr* resultManifest) {
if (manifestentry->resolved.isnull()) {
const char* pathstart;
size_t pathlen;
path.getPathToPosition(&pathstart, &pathlen);
// Chop off the trailing slash before fetching
// TODO: we should get rid of this code and just call
// manifestentry->get_manifest(). The only benefit here is avoiding the
// extra string allocation.
pathlen = pathlen > 0 ? pathlen - 1 : 0;
findContext->nodebuffer.erase();
appendbinfromhex(manifestentry->get_node(), findContext->nodebuffer);
manifestentry->resolved =
this->fetcher.get(pathstart, pathlen, findContext->nodebuffer);
}
ManifestPtr manifest = manifestentry->resolved;
*resultManifest = manifest;
FindResult result;
const char* word = "";
size_t wordlen = 0;
path.next(&word, &wordlen);
if (path.isfinished()) {
// time to execute the callback.
result = callback(manifest, word, wordlen, findContext, resultManifest);
} else {
// position the iterator at the right location
bool exacthit;
std::list<ManifestEntry>::iterator iterator =
manifest->findChild(word, wordlen, RESULT_DIRECTORY, &exacthit);
ManifestEntry* entry;
if (!exacthit) {
// do we create the intermediate node?
if (findMode != CREATE_IF_MISSING) {
return FIND_PATH_NOT_FOUND;
}
if (!manifest->isMutable()) {
manifest = ManifestPtr(manifest->copy());
iterator =
manifest->findChild(word, wordlen, RESULT_DIRECTORY, &exacthit);
}
// create the intermediate node...
entry = manifest->addChild(
iterator, word, wordlen, NULL, MANIFEST_DIRECTORY_FLAGPTR);
} else {
entry = &(*iterator);
}
// now find the next subdir
ManifestPtr newChildManifest;
result =
find(entry, path, findMode, findContext, callback, &newChildManifest);
// If the child was changed, apply it to this manifest
if (!entry->resolved->isMutable() && newChildManifest->isMutable()) {
// If we're not mutable, copy ourselves
if (!manifest->isMutable()) {
manifest = ManifestPtr(manifest->copy());
// Refind the entry in the new iterator
iterator =
manifest->findChild(word, wordlen, RESULT_DIRECTORY, &exacthit);
entry = &(*iterator);
}
// Replace the reference to the child
entry->resolved = newChildManifest;
}
// if the child manifest has 0 entries, we may want to prune it, if the mode
// indicates that we should.
if (findMode == REMOVE_EMPTY_IMPLICIT_NODES) {
if (newChildManifest->children() == 0) {
if (!manifest->isMutable()) {
manifest = ManifestPtr(manifest->copy());
iterator =
manifest->findChild(word, wordlen, RESULT_DIRECTORY, &exacthit);
}
manifest->removeChild(iterator);
// entry is invalid now
entry = nullptr;
}
}
*resultManifest = manifest;
if (entry && findContext->invalidate_checksums) {
if (!manifest->isMutable()) {
throw std::logic_error("attempting to null node on immutable manifest");
}
entry->reset_node();
}
}
return result;
}
struct GetResult {
ManifestFetcher& fetcher;
const std::string& filename;
std::string* resultnode;
const char** resultflag;
FindResultType resulttype;
ManifestPtr* resultmanifest;
};
static FindResult get_callback(
Manifest* manifest,
const char* filename,
size_t filenamelen,
FindContext* context,
ManifestPtr* /*resultManifest*/) {
GetResult* result = (GetResult*)context->extras;
// position the iterator at the right location
bool exacthit;
std::list<ManifestEntry>::iterator iterator =
manifest->findChild(filename, filenamelen, result->resulttype, &exacthit);
if (!exacthit) {
// TODO: not found. :( :(
return FIND_PATH_NOT_FOUND;
}
ManifestEntry& entry = *iterator;
result->resultnode->erase();
if (entry.hasNode()) {
appendbinfromhex(entry.get_node(), *result->resultnode);
}
*result->resultflag = entry.flag;
if (result->resultmanifest && entry.flag &&
*entry.flag == MANIFEST_DIRECTORY_FLAG) {
*result->resultmanifest = entry.get_manifest(
result->fetcher, result->filename.c_str(), result->filename.size());
}
return FIND_PATH_OK;
}
bool treemanifest::get(
const std::string& filename,
std::string* resultnode,
const char** resultflag,
FindResultType resulttype,
ManifestPtr* resultmanifest) {
getRootManifest();
GetResult extras = {
fetcher, filename, resultnode, resultflag, resulttype, resultmanifest};
PathIterator pathiter(filename);
FindContext changes;
changes.nodebuffer.reserve(BIN_NODE_SIZE);
changes.extras = &extras;
ManifestPtr newManifest;
FindResult result = this->find(
&this->root, pathiter, BASIC_WALK, &changes, get_callback, &newManifest);
return result == FIND_PATH_OK;
}
struct SetParams {
const std::string& resultnode;
const char* resultflag;
};
static FindResult set_callback(
Manifest* manifest,
const char* filename,
size_t filenamelen,
FindContext* context,
ManifestPtr* resultManifest) {
SetParams* params = (SetParams*)context->extras;
if (!manifest->isMutable()) {
*resultManifest = ManifestPtr(manifest->copy());
manifest = *resultManifest;
}
// position the iterator at the right location
bool exacthit;
std::list<ManifestEntry>::iterator iterator =
manifest->findChild(filename, filenamelen, RESULT_FILE, &exacthit);
if (!exacthit) {
// create the entry, insert it.
manifest->addChild(
iterator,
filename,
filenamelen,
params->resultnode.c_str(),
params->resultflag);
} else {
ManifestEntry* entry = &(*iterator);
entry->updatehexnode(params->resultnode.c_str(), params->resultflag);
}
context->invalidate_checksums = true;
return FIND_PATH_OK;
}
SetResult treemanifest::set(
const std::string& filename,
const std::string& resultnode,
const char* resultflag) {
SetParams extras = {resultnode, resultflag};
PathIterator pathiter(filename);
FindContext changes;
changes.nodebuffer.reserve(BIN_NODE_SIZE);
changes.extras = &extras;
ManifestPtr resultManifest;
FindResult result = this->find(
&this->root,
pathiter,
CREATE_IF_MISSING,
&changes,
set_callback,
&resultManifest);
this->root.resolved = resultManifest;
if (changes.invalidate_checksums) {
this->root.reset_node();
}
switch (result) {
case FIND_PATH_OK:
return SET_OK;
case FIND_PATH_CONFLICT:
return SET_CONFLICT;
default:
return SET_WTF;
}
}
struct RemoveResult {
bool found;
};
static FindResult remove_callback(
Manifest* manifest,
const char* filename,
size_t filenamelen,
FindContext* context,
ManifestPtr* resultManifest) {
RemoveResult* params = (RemoveResult*)context->extras;
// position the iterator at the right location
bool exacthit;
std::list<ManifestEntry>::iterator iterator =
manifest->findChild(filename, filenamelen, RESULT_FILE, &exacthit);
if (exacthit) {
if (!manifest->isMutable()) {
*resultManifest = ManifestPtr(manifest->copy());
manifest = *resultManifest;
iterator =
manifest->findChild(filename, filenamelen, RESULT_FILE, &exacthit);
}
manifest->removeChild(iterator);
params->found = true;
context->invalidate_checksums = true;
}
return FIND_PATH_OK;
}
bool treemanifest::remove(const std::string& filename) {
RemoveResult extras = {false};
PathIterator pathiter(filename);
FindContext changes;
changes.nodebuffer.reserve(BIN_NODE_SIZE);
changes.extras = &extras;
ManifestPtr resultManifest;
FindResult result = this->find(
&this->root,
pathiter,
REMOVE_EMPTY_IMPLICIT_NODES,
&changes,
remove_callback,
&resultManifest);
this->root.resolved = resultManifest;
if (changes.invalidate_checksums) {
this->root.reset_node();
}
return (result == FIND_PATH_OK) && extras.found;
}
SubtreeIterator::SubtreeIterator(
std::string path,
ManifestPtr mainRoot,
const std::vector<const char*>& cmpNodes,
const std::vector<ManifestPtr>& cmpRoots,
const ManifestFetcher& fetcher,
const int maxDepth)
: cmpNodes(cmpNodes),
path(path),
fetcher(fetcher),
firstRun(true),
maxDepth(maxDepth),
depth(1) {
this->mainStack.push_back(stackframe(mainRoot, false));
if (cmpRoots.size() > 2) {
throw std::logic_error(
"Tree comparison only supports 2 comparisons at "
"once for now");
}
for (size_t i = 0; i < cmpRoots.size(); i++) {
ManifestPtr cmpRoot = cmpRoots[i];
std::vector<stackframe> stack;
stack.push_back(stackframe(cmpRoot, false));
this->cmpStacks.push_back(stack);
}
if (this->path.size() > 0 && this->path.back() != '/') {
this->path.append(1, '/');
}
}
void SubtreeIterator::popResult(
std::string** path,
ManifestPtr* result,
ManifestPtr* p1,
ManifestPtr* p2) {
stackframe& mainFrame = this->mainStack.back();
ManifestPtr mainManifest = mainFrame.manifest;
// Record the comparison manifests of the level we're processing.
ManifestPtr cmpManifests[2]{ManifestPtr(), ManifestPtr()};
for (size_t i = 0; i < cmpStacks.size(); i++) {
// If a cmpstack is at the same level as the main stack, it represents
// the same diretory and should be inspected.
if (this->mainStack.size() == cmpStacks[i].size()) {
std::vector<stackframe>& cmpStack = cmpStacks[i];
cmpManifests[i] = cmpStack.back().manifest;
cmpStack.pop_back();
}
}
this->mainStack.pop_back();
*path = &this->path;
*result = mainManifest;
*p1 = cmpManifests[0];
*p2 = cmpManifests[1];
}
bool SubtreeIterator::processDirectory(ManifestEntry* mainEntry) {
// mainEntry is a new entry we need to compare against each cmpEntry, and
// then push if it is different from all of them.
// First move all the cmp iterators forward to the same name as mainEntry.
bool alreadyExists = false;
std::vector<std::vector<stackframe>*> requirePush;
for (size_t i = 0; i < cmpStacks.size(); i++) {
std::vector<stackframe>& cmpStack = cmpStacks[i];
// If the cmpStack is at a different level, it is not at the same
// location as main, so don't bother searching it.
if (cmpStack.size() < mainStack.size()) {
continue;
}
stackframe& cmpFrame = cmpStack.back();
// Move cmp iterator forward until we match or pass the current
// mainEntry filename.
while (!cmpFrame.isfinished()) {
ManifestEntry* cmpEntry = cmpFrame.currentvalue();
if (!cmpEntry->isdirectory()) {
cmpFrame.next();
continue;
}
int cmp = ManifestEntry::compareName(cmpEntry, mainEntry);
if (cmp >= 0) {
// If the directory names match...
if (cmp == 0) {
// And the nodes match...
if (!alreadyExists &&
(mainEntry->hasNode() &&
memcmp(
mainEntry->get_node(),
cmpEntry->get_node(),
HEX_NODE_SIZE) == 0)) {
// Skip this entry
alreadyExists = true;
}
// Remember this stack so we can push to it later
requirePush.push_back(&cmpStack);
}
break;
}
cmpFrame.next();
}
}
// If mainEntry matched any of the cmpEntries, we should skip mainEntry.
if (alreadyExists) {
assert(mainEntry->hasNode());
return false;
}
// Otherwise, push to the main stack
mainEntry->appendtopath(this->path);
this->depth++;
ManifestPtr mainManifest = mainEntry->get_manifest(
this->fetcher, this->path.c_str(), this->path.size());
this->mainStack.push_back(stackframe(mainManifest, false));
// And push all cmp stacks we remembered that have the same directory.
for (size_t i = 0; i < requirePush.size(); i++) {
std::vector<stackframe>* cmpStack = requirePush[i];
ManifestEntry* cmpEntry = cmpStack->back().currentvalue();
ManifestPtr cmpManifest = cmpEntry->get_manifest(
this->fetcher, this->path.c_str(), this->path.size());
cmpStack->push_back(stackframe(cmpManifest, false));
}
return true;
}
bool SubtreeIterator::next(
std::string** path,
ManifestPtr* result,
ManifestPtr* p1,
ManifestPtr* p2) {
if (!this->firstRun) {
// Pop the last returned directory off the path
size_t slashoffset = this->path.find_last_of('/', this->path.size() - 1);
if (slashoffset == std::string::npos) {
this->path.erase();
} else {
this->path.erase(slashoffset + 1);
}
this->depth--;
} else {
this->firstRun = false;
}
*result = ManifestPtr();
*p1 = ManifestPtr();
*p2 = ManifestPtr();
while (true) {
if (this->mainStack.empty()) {
return false;
}
stackframe& mainFrame = this->mainStack.back();
// If we've reached the end of this manifest, we've processed all the
// children, so we can now return it.
if (mainFrame.isfinished()) {
// This can return false if this manifest ended up being equivalent to
// a cmp parent manifest, which means we should skip it.
this->popResult(path, result, p1, p2);
if (this->mainStack.size() > 0) {
this->mainStack.back().next();
}
if (this->path.size() > 0) {
this->path.erase(this->path.size() - 1);
}
return true;
} else {
// Use currentvalue instead of next so that the stack of frames match the
// actual current filepath.
ManifestEntry* mainEntry = mainFrame.currentvalue();
if (mainEntry->isdirectory()) {
// If we're at a directory, process it, either by pushing it on the
// stack, or by skipping it if it already matches a cmp parent.
if (this->depth >= this->maxDepth ||
!this->processDirectory(mainEntry)) {
mainFrame.next();
}
} else {
mainFrame.next();
}
}
}
}
FinalizeIterator::FinalizeIterator(
ManifestPtr mainRoot,
const std::vector<const char*>& cmpNodes,
const std::vector<ManifestPtr>& cmpRoots,
const ManifestFetcher& fetcher)
: _iterator(
std::string(""),
mainRoot,
cmpNodes,
cmpRoots,
fetcher,
std::numeric_limits<int>::max()) {}
bool FinalizeIterator::next(
std::string** path,
ManifestPtr* result,
ManifestPtr* p1,
ManifestPtr* p2) {
while (_iterator.next(path, result, p1, p2)) {
std::string* realPath = *path;
ManifestPtr realResult = *result;
ManifestPtr realP1 = *p1;
ManifestPtr realP2 = *p2;
// If it's mutable, mark it permanent and check it against parents.
if (realResult->isMutable()) {
const char* p1Node = realP1 ? realP1->node() : NULLID;
const char* p2Node = realP2 ? realP2->node() : NULLID;
// If mutable root node, always give it new parents and return it
if (realPath->length() == 0) {
realResult->markPermanent(p1Node, p2Node);
// If mutable child has parents, compare with parent contents and return
// only if different.
} else if (p1 || p2) {
std::string mainRaw, parentRaw;
realResult->serialize(mainRaw);
bool parentMatch = false;
ManifestPtr parents[2]{realP1, realP2};
for (int i = 0; i < 2; ++i) {
Manifest* p = parents[i];
if (p) {
p->serialize(parentRaw);
if (mainRaw == parentRaw) {
realResult->markPermanent(p->node());
parentMatch = true;
break;
}
}
}
if (parentMatch) {
continue;
}
realResult->markPermanent(p1Node, p2Node);
// If mutable child has no parents, always return
} else {
realResult->markPermanent(p1Node, p2Node);
}
}
return true;
}
return false;
}

View File

@ -1,367 +0,0 @@
// Copyright (c) 2004-present, Facebook, Inc.
// All Rights Reserved.
//
// This software may be used and distributed according to the terms of the
// GNU General Public License version 2 or any later version.
// treemanifest.h - c++ declarations of a tree manifest
// no-check-code
#ifndef FBHGEXT_CTREEMANIFEST_TREEMANIFEST_H
#define FBHGEXT_CTREEMANIFEST_TREEMANIFEST_H
#include <memory>
#include <string>
#include <vector>
#include "edenscm/hgext/extlib/cstore/match.h"
#include "edenscm/hgext/extlib/ctreemanifest/manifest.h"
#include "edenscm/hgext/extlib/ctreemanifest/manifest_entry.h"
#include "edenscm/hgext/extlib/ctreemanifest/manifest_fetcher.h"
enum FindResult {
FIND_PATH_OK,
FIND_PATH_NOT_FOUND,
FIND_PATH_CONFLICT,
FIND_PATH_WTF,
};
enum SetResult {
SET_OK,
SET_CONFLICT,
SET_WTF,
};
enum FindMode {
// walks the tree and searches for a leaf node. if the path cannot be found,
// exit with `FIND_PATH_NOT_FOUND`.
BASIC_WALK,
// walks the tree. if the intermediate paths cannot be found, create them.
// if a leaf node exists where an intermediate path node needs to be
// created, then return `FIND_PATH_CONFLICT`.
CREATE_IF_MISSING,
// walks the tree. if the path cannot be found, exit with
// `FIND_PATH_NOT_FOUND`. if the operation is successful, then check
// intermediate nodes to ensure that they still have children. any nodes
// that do not should be removed.
REMOVE_EMPTY_IMPLICIT_NODES,
};
struct FindContext {
bool invalidate_checksums;
int32_t num_leaf_node_changes;
FindMode mode;
// reuse this space when fetching manifests.
std::string nodebuffer;
// any extra data the callback needs to complete the operation.
void* extras;
FindContext()
: invalidate_checksums(false),
num_leaf_node_changes(0),
mode(BASIC_WALK),
extras(NULL) {}
};
class PathIterator {
private:
std::string path;
size_t position;
public:
PathIterator(std::string path) {
this->path = path;
this->position = 0;
}
bool next(char const** word, size_t* wordlen) {
if (this->isfinished()) {
return false;
}
*word = this->path.c_str() + this->position;
size_t slashoffset = this->path.find('/', this->position);
if (slashoffset == std::string::npos) {
*wordlen = this->path.length() - this->position;
} else {
*wordlen = slashoffset - this->position;
}
this->position += *wordlen + 1;
return true;
}
bool isfinished() {
return this->position >= this->path.length();
}
void getPathToPosition(const char** word, size_t* wordlen) {
*word = path.c_str();
*wordlen = this->position;
}
};
/**
* A single instance of a treemanifest.
*/
struct treemanifest {
// Fetcher for the manifests.
ManifestFetcher fetcher;
ManifestEntry root;
treemanifest(std::shared_ptr<Store> store, std::string rootNode)
: fetcher(store) {
std::string hexnode;
hexnode.reserve(HEX_NODE_SIZE);
hexfrombin(rootNode.c_str(), hexnode);
root.initialize(NULL, 0, hexnode.c_str(), MANIFEST_DIRECTORY_FLAGPTR);
// ManifestEntry.initialize will create a blank manifest in .resolved.
// however, we actually want the resolution to happen through
// manifestfetcher. therefore, let's clear it.
root.resolved = ManifestPtr();
}
treemanifest(std::shared_ptr<Store> store) : fetcher(store) {
root.initialize(NULL, 0, HEXNULLID, MANIFEST_DIRECTORY_FLAGPTR);
}
treemanifest(treemanifest& other) : fetcher(other.fetcher) {
root.initialize(&other.root);
}
bool get(
const std::string& filename,
std::string* resultnode,
const char** resultflag,
FindResultType resulttype = RESULT_FILE,
ManifestPtr* resultmanifest = nullptr);
SetResult set(
const std::string& filename,
const std::string& resultnode,
const char* resultflag);
/**
* Removes a file from the treemanifest. Returns true iff the file was
* found and removed.
*/
bool remove(const std::string& filename);
ManifestPtr getRootManifest() {
if (this->root.resolved.isnull()) {
std::string binnode;
binnode.reserve(BIN_NODE_SIZE);
appendbinfromhex(this->root.get_node(), binnode);
this->root.resolved = this->fetcher.get("", 0, binnode);
}
return this->root.resolved;
}
private:
/**
* Basic mechanism to traverse a tree. Once the deepest directory in the
* path has been located, the supplied callback is executed. That callback
* is called with the manifest of the deepest directory and the leaf node's
* filename.
*
* For instance, if treemanifest_find is called on /abc/def/ghi, then the
* callback is executed with the manifest of /abc/def, and the filename
* passed in will be "ghi".
*/
FindResult find(
ManifestEntry* manifestentry,
PathIterator& path,
FindMode findMode,
FindContext* findContext,
FindResult (*callback)(
Manifest* manifest,
const char* filename,
size_t filenamelen,
FindContext* findContext,
ManifestPtr* resultManifest),
ManifestPtr* resultManifest);
};
/**
* Represents a single stack frame in an iteration of the contents of the tree.
*/
struct stackframe {
private:
ManifestIterator iterator;
SortedManifestIterator sortedIterator;
public:
ManifestPtr manifest;
bool sorted;
stackframe(ManifestPtr manifest, bool sorted)
: manifest(manifest), sorted(sorted) {
if (sorted) {
sortedIterator = manifest->getSortedIterator();
} else {
iterator = manifest->getIterator();
}
}
ManifestEntry* next() {
if (sorted) {
return sortedIterator.next();
} else {
return iterator.next();
}
}
ManifestEntry* currentvalue() const {
if (sorted) {
return sortedIterator.currentvalue();
} else {
return iterator.currentvalue();
}
}
bool isfinished() const {
if (sorted) {
return sortedIterator.isfinished();
} else {
return iterator.isfinished();
}
}
};
/**
* An iterator that takes a main treemanifest and a vector of comparison
* treemanifests and iterates over the Manifests that only exist in the main
* treemanifest.
*/
class SubtreeIterator {
private:
std::vector<stackframe> mainStack;
std::vector<const char*> cmpNodes;
std::vector<std::vector<stackframe>> cmpStacks;
std::string path;
ManifestFetcher fetcher;
bool firstRun;
int maxDepth;
int depth;
public:
SubtreeIterator(
std::string path,
ManifestPtr mainRoot,
const std::vector<const char*>& cmpNodes,
const std::vector<ManifestPtr>& cmpRoots,
const ManifestFetcher& fetcher,
const int depth);
/**
* Outputs the next new Manifest and its corresponding path and node.
*
* `resultEntry` contains the ManifestEntry that points at the result. This
* is useful for updating the ManifestEntry hash if the caller decides to
* make the result permanent.
*
* Return true if a manifest was returned, or false if we've reached the
* end.
*/
bool next(
std::string** path,
ManifestPtr* result,
ManifestPtr* p1,
ManifestPtr* p2);
private:
/**
* Pops the current Manifest, populating the output values and returning true
* if the current Manifest is different from all comparison manifests.
*/
void popResult(
std::string** path,
ManifestPtr* result,
ManifestPtr* p1,
ManifestPtr* p2);
/** Pushes the given Manifest onto the stacks. If the given Manifest equals
* one of the comparison Manifests, the function does nothing.
*/
bool processDirectory(ManifestEntry* mainEntry);
};
class FinalizeIterator {
private:
SubtreeIterator _iterator;
public:
FinalizeIterator(
ManifestPtr mainRoot,
const std::vector<const char*>& cmpNodes,
const std::vector<ManifestPtr>& cmpRoots,
const ManifestFetcher& fetcher);
bool next(
std::string** path,
ManifestPtr* result,
ManifestPtr* p1,
ManifestPtr* p2);
};
/**
* A helper struct representing the state of an iterator recursing over a tree.
*/
struct fileiter {
ManifestFetcher fetcher; // Instance to fetch tree content
std::vector<stackframe> frames;
std::string path; // The fullpath for the top entry in the stack.
bool sorted; // enable mercurial sorting?
// If provided, the given matcher filters the results by path
std::shared_ptr<Matcher> matcher;
fileiter(treemanifest& tm, bool sorted)
: fetcher(tm.fetcher), sorted(sorted) {
this->frames.push_back(stackframe(tm.getRootManifest(), this->sorted));
this->path.reserve(1024);
}
fileiter(const fileiter& old)
: fetcher(old.fetcher), frames(old.frames), path(old.path) {}
fileiter& operator=(const fileiter& other) {
this->fetcher = other.fetcher;
this->frames = other.frames;
this->path = other.path;
return *this;
}
};
struct DiffResult {
virtual ~DiffResult() {}
virtual void add(
const std::string& path,
const char* beforeNode,
const char* beforeFlag,
const char* afterNode,
const char* afterFlag) = 0;
virtual void addclean(const std::string& path) = 0;
};
extern void treemanifest_diffrecurse(
Manifest* selfmf,
Manifest* othermf,
std::string& path,
DiffResult& diff,
const ManifestFetcher& fetcher,
bool clean,
Matcher& matcher);
#endif // FBHGEXT_CTREEMANIFEST_TREEMANIFEST_H

View File

@ -13,20 +13,8 @@ New errors are not allowed. Warnings are strongly discouraged.
$ NPROC=`hg debugsh -c 'import multiprocessing; ui.write(str(multiprocessing.cpu_count()))'`
$ cat $TESTTMP/files.txt | PYTHONPATH= xargs -n64 -P $NPROC contrib/check-code.py --warnings --per-file=0 | LC_ALL=C sort
Skipping edenscm/hgext/extlib/cstore/datapackstore.cpp it has no-che?k-code (glob)
Skipping edenscm/hgext/extlib/cstore/datapackstore.h it has no-che?k-code (glob)
Skipping edenscm/hgext/extlib/cstore/datastore.h it has no-che?k-code (glob)
Skipping edenscm/hgext/extlib/cstore/deltachain.cpp it has no-che?k-code (glob)
Skipping edenscm/hgext/extlib/cstore/deltachain.h it has no-che?k-code (glob)
Skipping edenscm/hgext/extlib/cstore/key.h it has no-che?k-code (glob)
Skipping edenscm/hgext/extlib/cstore/match.h it has no-che?k-code (glob)
Skipping edenscm/hgext/extlib/cstore/py-datapackstore.h it has no-che?k-code (glob)
Skipping edenscm/hgext/extlib/cstore/py-structs.h it has no-che?k-code (glob)
Skipping edenscm/hgext/extlib/cstore/py-treemanifest.h it has no-che?k-code (glob)
Skipping edenscm/hgext/extlib/cstore/pythonkeyiterator.h it has no-che?k-code (glob)
Skipping edenscm/hgext/extlib/cstore/store.h it has no-che?k-code (glob)
Skipping edenscm/hgext/extlib/cstore/uniondatapackstore.cpp it has no-che?k-code (glob)
Skipping edenscm/hgext/extlib/cstore/uniondatapackstore.h it has no-che?k-code (glob)
Skipping edenscm/hgext/extlib/ctreemanifest/manifest.cpp it has no-che?k-code (glob)
Skipping edenscm/hgext/extlib/ctreemanifest/manifest.h it has no-che?k-code (glob)
Skipping edenscm/hgext/extlib/ctreemanifest/manifest_entry.cpp it has no-che?k-code (glob)
@ -35,8 +23,6 @@ New errors are not allowed. Warnings are strongly discouraged.
Skipping edenscm/hgext/extlib/ctreemanifest/manifest_fetcher.h it has no-che?k-code (glob)
Skipping edenscm/hgext/extlib/ctreemanifest/manifest_ptr.cpp it has no-che?k-code (glob)
Skipping edenscm/hgext/extlib/ctreemanifest/manifest_ptr.h it has no-che?k-code (glob)
Skipping edenscm/hgext/extlib/ctreemanifest/treemanifest.cpp it has no-che?k-code (glob)
Skipping edenscm/hgext/extlib/ctreemanifest/treemanifest.h it has no-che?k-code (glob)
Skipping edenscm/hgext/globalrevs.py it has no-che?k-code (glob)
Skipping edenscm/hgext/hgsql.py it has no-che?k-code (glob)
Skipping edenscm/mercurial/commands/eden.py it has no-che?k-code (glob)