mirror of
https://github.com/facebook/sapling.git
synced 2024-10-10 08:47:12 +03:00
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:
parent
c16ab69c6a
commit
30ef9bdcf1
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
}
|
@ -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
|
@ -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
|
@ -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;
|
||||
}
|
@ -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
|
@ -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
|
@ -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 */
|
@ -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
@ -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
|
@ -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();
|
||||
}
|
||||
}
|
@ -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
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
@ -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
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user