sapling/cstore/datapackstore.cpp
Mihails Smolins 38c4cde5a3 cstore: extend and refactor deltachain class
Summary:
Extended DeltaChain with CDeltaChain and PyDeltaChain which are wrappers around
c and python delta chains respectively. The declaration and implementation
of c and python delta chains as well as DeltaChainLink were put in a different
file.

Test Plan: * Ensure that unit tests pass

Reviewers: ryanmce, durham, simonfar, #fbhgext

Reviewed By: ryanmce, durham, #fbhgext

Differential Revision: https://phab.mercurial-scm.org/D630
2017-09-07 08:07:30 -07:00

227 lines
5.8 KiB
C++

// 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.
// datapackstore.cpp - implementation of a datapack store
// no-check-code
#include "cstore/datapackstore.h"
#include <sys/types.h>
#include <dirent.h>
#include <stdexcept>
#include <stdlib.h>
#include "cstore/key.h"
std::vector<std::string> getAvailablePackFiles(const std::string &path) {
std::vector<std::string> results;
std::string packpath(path);
if (!path.empty() && path[path.size() - 1] != '/') {
packpath.push_back('/');
}
size_t dirLength = packpath.size();
DIR *dirp = opendir(path.c_str());
if (!dirp) {
return results;
}
try {
dirent *entry;
while ((entry = readdir(dirp)) != NULL) {
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.push_back(packpath);
packpath.erase(dirLength);
}
closedir(dirp);
} catch (const std::exception &ex) {
closedir(dirp);
throw;
}
return results;
}
DatapackStore::DatapackStore(const std::string &path) :
_path(path),
_lastRefresh(0) {
// Find pack files in path
std::vector<std::string> files = getAvailablePackFiles(path);
for(std::vector<std::string>::iterator it = files.begin();
it != files.end();
it++) {
std::string &packpath = *it;
addPack(packpath);
}
}
datapack_handle_t *DatapackStore::addPack(const std::string &path) {
std::string idx_path(path + INDEXSUFFIX);
std::string data_path(path + PACKSUFFIX);
datapack_handle_t *pack = open_datapack(
(char*)idx_path.c_str(), idx_path.size(),
(char*)data_path.c_str(), data_path.size());
if (pack == NULL) {
return NULL;
}
if (pack->status == DATAPACK_HANDLE_OK) {
_packs.push_back(pack);
_packPaths.insert(path);
return pack;
} else {
free(pack);
return NULL;
}
}
DatapackStore::~DatapackStore() {
for(std::vector<datapack_handle_t*>::iterator it = _packs.begin();
it != _packs.end();
it++) {
close_datapack(*it);
}
}
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(std::vector<datapack_handle_t*>::iterator it = _packs.begin();
it != _packs.end();
it++) {
datapack_handle_t *pack = *it;
delta_chain_t chain = getdeltachain(pack, (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
std::vector<datapack_handle_t*> refreshed = refresh();
for(std::vector<datapack_handle_t*>::iterator it = refreshed.begin();
it != refreshed.end();
it++) {
datapack_handle_t *pack = *it;
delta_chain_t chain = getdeltachain(pack, (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(std::vector<datapack_handle_t*>::iterator it = _packs.begin();
it != _packs.end();
it++) {
datapack_handle_t *pack = *it;
pack_index_entry_t packindex;
if (find(pack, (uint8_t*)key.node, &packindex)) {
return true;
}
}
// Check if there are new packs available
std::vector<datapack_handle_t*> refreshed = refresh();
for(std::vector<datapack_handle_t*>::iterator it = refreshed.begin();
it != refreshed.end();
it++) {
datapack_handle_t *pack = *it;
pack_index_entry_t packindex;
if (find(pack, (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<datapack_handle_t*> DatapackStore::refresh() {
clock_t now = clock();
std::vector<datapack_handle_t*> newPacks;
if (now - _lastRefresh > PACK_REFRESH_RATE) {
std::vector<std::string> availablePacks = getAvailablePackFiles(_path);
for(std::vector<std::string>::iterator it = availablePacks.begin();
it != availablePacks.end();
it++) {
std::string &packPath = *it;
if (_packPaths.find(packPath) == _packPaths.end()) {
datapack_handle_t *newPack = addPack(packPath);
if (newPack) {
newPacks.push_back(newPack);
}
}
}
_lastRefresh = now;
}
return newPacks;
}
void DatapackStore::markForRefresh() {
_lastRefresh = 0;
}