mirror of
https://github.com/facebook/sapling.git
synced 2024-10-09 16:31:02 +03:00
e8554fc3d9
This adds the concept of mutable and immutable Manifests. During a treemanifest copy, any sub-manifests that are immutable (such as ones that had been loaded from a store, or those that are in memory but have been mark immutable), do not need to be copied. This dramatically reduces the amount of memory allocation happening when copying trees during automatic tree creation during hg pull.
239 lines
6.4 KiB
C++
239 lines
6.4 KiB
C++
// manifest_entry.cpp - c++ implementation of a single manifest entry
|
|
//
|
|
// Copyright 2016 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 "manifest_entry.h"
|
|
|
|
ManifestEntry::ManifestEntry() {
|
|
this->filename = NULL;
|
|
this->filenamelen = 0;
|
|
this->node = NULL;
|
|
this->flag = NULL;
|
|
this->ownedmemory = NULL;
|
|
}
|
|
|
|
void ManifestEntry::initialize(
|
|
const char *filename, const size_t filenamelen,
|
|
const char *node,
|
|
const char *flag) {
|
|
if (flag != NULL && *flag == MANIFEST_DIRECTORY_FLAG) {
|
|
this->resolved = ManifestPtr(new Manifest());
|
|
}
|
|
this->ownedmemory = new char[
|
|
filenamelen +
|
|
1 + // null character
|
|
HEX_NODE_SIZE + // node hash
|
|
1 + // flag
|
|
1 // NL
|
|
];
|
|
|
|
// set up the pointers.
|
|
this->filename = this->ownedmemory;
|
|
if (node == NULL) {
|
|
this->node = NULL;
|
|
} else {
|
|
this->node = this->filename + filenamelen + 1;
|
|
}
|
|
|
|
// set up the null character and NL.
|
|
this->filename[filenamelen] = '\0';
|
|
*(this->filename + filenamelen + 1 + HEX_NODE_SIZE + 1) = '\n';
|
|
|
|
// set up filenamelen
|
|
this->filenamelen = filenamelen;
|
|
|
|
// write the memory.
|
|
memcpy(this->filename, filename, filenamelen);
|
|
if (node != NULL) {
|
|
memcpy(this->node, node, HEX_NODE_SIZE);
|
|
}
|
|
if (flag == NULL) {
|
|
*(this->filename + filenamelen + 1 + HEX_NODE_SIZE) = '\n';
|
|
this->flag = NULL;
|
|
} else {
|
|
this->flag = this->filename + filenamelen + 1 + HEX_NODE_SIZE;
|
|
*this->flag = *flag;
|
|
}
|
|
}
|
|
|
|
char *ManifestEntry::initialize(char *entrystart) {
|
|
// Each entry is of the format:
|
|
//
|
|
// <filename>\0<40-byte hash><optional 1 byte flag>\n
|
|
//
|
|
// Where flags can be 't' to represent a sub directory
|
|
this->filename = entrystart;
|
|
char *nulldelimiter = strchr(entrystart, '\0');
|
|
this->filenamelen = nulldelimiter - entrystart;
|
|
|
|
this->node = nulldelimiter + 1;
|
|
|
|
this->flag = nulldelimiter + 41;
|
|
char *nextpointer;
|
|
if (*this->flag != '\n') {
|
|
nextpointer = this->flag + 2;
|
|
} else {
|
|
// No flag
|
|
nextpointer = this->flag + 1;
|
|
this->flag = NULL;
|
|
}
|
|
this->resolved = ManifestPtr();
|
|
this->ownedmemory = NULL;
|
|
|
|
return nextpointer;
|
|
}
|
|
|
|
void ManifestEntry::initialize(ManifestEntry *other) {
|
|
if (other->ownedmemory) {
|
|
this->initialize(other->filename, other->filenamelen,
|
|
other->node, other->flag);
|
|
if (other->resolved.isnull()) {
|
|
this->resolved = ManifestPtr();
|
|
}
|
|
} else {
|
|
// Else it points at a piece of memory owned by something else
|
|
this->initialize(other->filename);
|
|
}
|
|
|
|
if (!other->resolved.isnull()) {
|
|
if (other->resolved->isMutable()) {
|
|
this->resolved = other->resolved->copy();
|
|
} else {
|
|
this->resolved = other->resolved;
|
|
}
|
|
}
|
|
}
|
|
|
|
ManifestEntry::~ManifestEntry() {
|
|
if (this->ownedmemory != NULL) {
|
|
delete [] this->ownedmemory;
|
|
}
|
|
}
|
|
|
|
bool ManifestEntry::isdirectory() const {
|
|
return this->flag && *this->flag == MANIFEST_DIRECTORY_FLAG;
|
|
}
|
|
|
|
void ManifestEntry::appendtopath(std::string &path) {
|
|
path.append(this->filename, this->filenamelen);
|
|
if (this->isdirectory()) {
|
|
path.append(1, '/');
|
|
}
|
|
}
|
|
|
|
ManifestPtr ManifestEntry::get_manifest(
|
|
ManifestFetcher fetcher, const char *path, size_t pathlen) {
|
|
if (this->resolved.isnull()) {
|
|
std::string binnode = binfromhex(node);
|
|
this->resolved = fetcher.get(path, pathlen, binnode);
|
|
}
|
|
|
|
return this->resolved;
|
|
}
|
|
|
|
void ManifestEntry::update(const char *node, const char *flag) {
|
|
// we cannot flip between file and directory.
|
|
bool wasdir = this->flag != NULL && *this->flag == MANIFEST_DIRECTORY_FLAG;
|
|
bool willbedir = flag != NULL && *flag == MANIFEST_DIRECTORY_FLAG;
|
|
|
|
if (wasdir != willbedir) {
|
|
throw std::logic_error("changing to/from directory is not permitted");
|
|
}
|
|
|
|
// if we didn't previously own the memory, we should now.
|
|
if (this->ownedmemory == NULL) {
|
|
ManifestPtr oldresolved = this->resolved;
|
|
this->initialize(this->filename, this->filenamelen, node, flag);
|
|
this->resolved = oldresolved;
|
|
return;
|
|
}
|
|
|
|
// initialize node if it's not already done.
|
|
if (this->node == NULL) {
|
|
this->node = this->filename + this->filenamelen + 1;
|
|
}
|
|
memcpy(this->node, node, HEX_NODE_SIZE);
|
|
|
|
if (flag == NULL) {
|
|
*(this->filename + this->filenamelen + 1 + HEX_NODE_SIZE) = '\n';
|
|
this->flag = NULL;
|
|
} else {
|
|
this->flag = this->filename + filenamelen + 1 + HEX_NODE_SIZE;
|
|
*this->flag = *flag;
|
|
}
|
|
}
|
|
|
|
static size_t mercurialOrderFilenameLength(const ManifestEntry &entry) {
|
|
return entry.filenamelen +
|
|
((entry.flag != NULL && *entry.flag == MANIFEST_DIRECTORY_FLAG) ?
|
|
1 : 0);
|
|
}
|
|
|
|
static char mercurialOrderFilenameCharAt(
|
|
const ManifestEntry &entry, size_t offset) {
|
|
if (offset < entry.filenamelen) {
|
|
return entry.filename[offset];
|
|
} else if (offset == entry.filenamelen &&
|
|
(entry.flag != NULL && *entry.flag == MANIFEST_DIRECTORY_FLAG)) {
|
|
return '/';
|
|
}
|
|
|
|
throw std::out_of_range("Illegal index for manifest entry");
|
|
}
|
|
|
|
bool ManifestEntry::compareMercurialOrder(
|
|
ManifestEntry * const &left,
|
|
ManifestEntry * const &right) {
|
|
size_t leftlen = mercurialOrderFilenameLength(*left);
|
|
size_t rightlen = mercurialOrderFilenameLength(*right);
|
|
size_t minlen = (leftlen < rightlen) ? leftlen : rightlen;
|
|
|
|
for (size_t ix = 0; ix < minlen; ix ++) {
|
|
unsigned char leftchar = mercurialOrderFilenameCharAt(*left, ix);
|
|
unsigned char rightchar = mercurialOrderFilenameCharAt(*right, ix);
|
|
|
|
if (leftchar < rightchar) {
|
|
return true;
|
|
} else if (leftchar > rightchar) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// same up to minlen.
|
|
if (leftlen < rightlen) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
int ManifestEntry::compareName(ManifestEntry *left, ManifestEntry *right) {
|
|
assert(left || right);
|
|
|
|
// If left is empty, then it is greater than right. This makes this function
|
|
// useful for iterating right after left has already finished.
|
|
if (!left) {
|
|
return 1;
|
|
}
|
|
else if (!right) {
|
|
return -1;
|
|
}
|
|
|
|
size_t minlen = left->filenamelen < right->filenamelen ?
|
|
left->filenamelen : right->filenamelen;
|
|
int cmp = strncmp(left->filename, right->filename, minlen);
|
|
if (cmp == 0 && left->filenamelen == right->filenamelen) {
|
|
return 0;
|
|
} else if (cmp > 0 ||
|
|
(cmp == 0 && left->filenamelen > right->filenamelen)) {
|
|
return 1;
|
|
} else {
|
|
return -1;
|
|
}
|
|
}
|