sapling/cstore/uniondatapackstore.cpp
Durham Goode 1734bf7dec cstore: implement UnionDatapackStore.get()
Summary:
This implements the get function of UnionDatapackStore which iterates over
deltas and produces a full text. Unfortunately the delta chains currently come
out in reverse order (full text last), so we have to read all of them before we
can produce the delta.

Since mpatch takes a char*, and we want to avoid copying the array into a string
array for every read, we event a ref counted string reference type which let's
us transfer ownership of the char* to the caller.

Test Plan: Added a test

Reviewers: #mercurial, simonfar

Reviewed By: simonfar

Subscribers: stash, simonfar, mjpieters

Differential Revision: https://phabricator.intern.facebook.com/D4556547

Signature: t1:4556547:1487200302:c73b8f464f6ce413f1c22c0125a91fbece701f08
2017-02-23 14:03:03 -08:00

135 lines
3.6 KiB
C++

// uniondatapackstore.cpp - implementation of a union datapack store
//
// Copyright 2017 Facebook, Inc.
//
// This software may be used and distributed according to the terms of the
// GNU General Public License version 2 or any later version.
//
// no-check-code
#include <algorithm>
#include <memory>
#include "uniondatapackstore.h"
extern "C" {
#include "mpatch.h"
}
UnionDatapackStore::UnionDatapackStore(std::vector<DatapackStore*> 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<delta_chain_link_t*> *links = (std::vector<delta_chain_link_t*>*)container;
if (index < 0 || (size_t)index >= links->size()) {
return NULL;
}
delta_chain_link_t *link = links->at(index);
struct mpatch_flist *res;
if ((mpatch_decode((const char*)link->delta, link->delta_sz, &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<delta_chain_link_t*> links;
delta_chain_link_t *link;
while ((link = chain.next()) != NULL) {
links.push_back(link);
}
delta_chain_link_t *fulltextLink = links.back();
links.pop_back();
// Short circuit and just return the full text if it's one long
if (links.size() == 0) {
char * finalText = new char[fulltextLink->delta_sz];
memcpy(finalText, fulltextLink->delta, fulltextLink->delta_sz);
return ConstantStringRef(finalText, fulltextLink->delta_sz);
}
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(fulltextLink->delta_sz, patch);
if (outlen < 0) {
mpatch_lfree(patch);
throw std::logic_error("mpatch failed to calculate size");
}
char *result= new char[outlen];
if (mpatch_apply(result, (const char*)fulltextLink->delta, fulltextLink->delta_sz, patch) < 0) {
delete[] result;
mpatch_lfree(patch);
throw std::logic_error("mpatch failed to apply patches");
}
mpatch_lfree(patch);
return ConstantStringRef(result, outlen);
}
delta_chain_t UnionDeltaChainIterator::getNextChain(const Key &key) {
for(std::vector<DatapackStore*>::iterator it = _store._stores.begin();
it != _store._stores.end();
it++) {
DatapackStore *substore = *it;
delta_chain_t chain = substore->getDeltaChainRaw(key);
if (chain.code == GET_DELTA_CHAIN_OK) {
return chain;
}
freedeltachain(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<DatapackStore*>::iterator it = _stores.begin();
it != _stores.end();
it++) {
DatapackStore *substore = *it;
if (substore->contains(key)) {
return true;
}
}
return false;
}
UnionDatapackStoreKeyIterator UnionDatapackStore::getMissing(KeyIterator &missing) {
return UnionDatapackStoreKeyIterator(*this, missing);
}