sapling/eden/fs/model/git/GitTree.cpp
Xavier Deguillard 2e9cce82d5 store: make BackingStore return shared_ptr instead of unique_ptr
Summary:
In both the ObjectStore and in the hg BackingStore, copies of the unique_ptr
were being made. For large blobs this is particularly inefficient as
potentially several MB (if not more) of data needs to be copied. Let's fix this
by changing the BackingStore API to return a shared_ptr.

In order to make the code easier to read and write, also define 3 types:
TreePtr, BlobPtr and BlobMetadataPtr and use them in the BackingStore code.
Future changes should be done at a later point to convert the whole codebase to
using these.

Reviewed By: chadaustin

Differential Revision: D45967102

fbshipit-source-id: 6086f95456232db48a5cbec47b7cf8b14e4424ed
2023-05-18 12:30:08 -07:00

104 lines
3.2 KiB
C++

/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This software may be used and distributed according to the terms of the
* GNU General Public License version 2.
*/
#include "eden/fs/model/git/GitTree.h"
#include <fmt/format.h>
#include <folly/io/Cursor.h>
#include <cstdio>
#include <cstring>
#include "eden/fs/model/Hash.h"
#include "eden/fs/model/Tree.h"
#include "eden/fs/model/TreeEntry.h"
#include "eden/fs/utils/Throw.h"
using folly::IOBuf;
using std::invalid_argument;
using std::vector;
namespace facebook::eden {
enum GitModeMask {
DIRECTORY = 040000,
GIT_LINK = 0160000,
REGULAR_EXECUTABLE_FILE = 0100755,
REGULAR_FILE = 0100644,
SYMLINK = 0120000,
};
TreePtr deserializeGitTree(const ObjectId& hash, const IOBuf* treeData) {
folly::io::Cursor cursor(treeData);
// Find the end of the header and extract the size.
if (cursor.readFixedString(5) != "tree ") {
throw invalid_argument("Contents did not start with expected header.");
}
// 25 characters is long enough to represent any legitimate length
size_t maxSizeLength = 25;
auto sizeStr = cursor.readTerminatedString('\0', maxSizeLength);
auto contentSize = folly::to<unsigned int>(sizeStr);
if (contentSize != cursor.length()) {
throw invalid_argument("Size in header should match contents");
}
// Scan the data and populate entries, as appropriate.
Tree::container entries{kPathMapDefaultCaseSensitive};
while (!cursor.isAtEnd()) {
// Extract the mode.
// This should only be 6 or 7 characters.
// Stop scanning if we haven't seen a space in 10 characters
size_t maxModeLength = 10;
auto modeStr = cursor.readTerminatedString(' ', maxModeLength);
size_t modeEndIndex;
auto mode = std::stoi(modeStr, &modeEndIndex, /* base */ 8);
if (modeEndIndex != modeStr.size()) {
throw invalid_argument("Did not parse expected number of octal chars.");
}
// Extract the name.
auto name = cursor.readTerminatedString();
// Extract the hash.
Hash20::Storage hashBytes;
cursor.pull(hashBytes.data(), hashBytes.size());
// Determine the individual fields from the mode.
TreeEntryType fileType;
if (mode == GitModeMask::DIRECTORY) {
fileType = TreeEntryType::TREE;
} else if (mode == GitModeMask::REGULAR_FILE) {
fileType = TreeEntryType::REGULAR_FILE;
} else if (mode == GitModeMask::REGULAR_EXECUTABLE_FILE) {
fileType = TreeEntryType::EXECUTABLE_FILE;
} else if (mode == GitModeMask::SYMLINK) {
fileType = TreeEntryType::SYMLINK;
} else if (mode == GitModeMask::GIT_LINK) {
throwf<std::domain_error>(
"Gitlinks are not currently supported: {:o} in object {}",
mode,
hash);
} else {
throw invalid_argument(
fmt::format("Unrecognized mode: {:o} in object {}", mode, hash));
}
auto pathName = PathComponentPiece{name};
entries.emplace(pathName, ObjectId(hashBytes), fileType);
}
return std::make_shared<TreePtr::element_type>(std::move(entries), hash);
}
// Convenience wrapper which accepts a ByteRange
TreePtr deserializeGitTree(const ObjectId& hash, folly::ByteRange treeData) {
IOBuf buf(IOBuf::WRAP_BUFFER, treeData);
return deserializeGitTree(hash, &buf);
}
} // namespace facebook::eden