18#include <unordered_set>
26constexpr size_t kHeaderReadSize = 128;
27constexpr size_t kDirectoryReadSize = 64;
29constexpr uint64_t kMetadataGap = 4 * 1024;
31constexpr size_t kBasketHeaderTail = 19;
33constexpr size_t kMinKeySize = 29;
36constexpr uint32_t kMaxMetadataSize = 256 * 1024 * 1024;
38std::vector<uint8_t> readRange(ByteSource&
source, uint64_t off,
size_t len)
44 std::vector<uint8_t>
buf(
len);
54 auto buf = readRange(*mSource, 0, std::min<uint64_t>(kHeaderReadSize, mSource->size()));
56 auto magic =
c.bytes(4);
57 if (std::memcmp(magic.data(),
"root", 4) != 0) {
63 bool big =
h.version >= 1000000;
64 h.end = big ?
c.u64() :
c.u32();
65 h.seekFree = big ?
c.u64() :
c.u32();
66 h.nbytesFree =
c.i32();
68 h.nbytesName =
c.i32();
71 h.seekInfo = big ?
c.u64() :
c.u32();
72 h.nbytesInfo =
c.i32();
73 if (
h.begin <= 0 ||
h.nbytesName <= 0) {
74 c.fail(
"invalid file header");
78KeyInfo RootFile::parseKey(std::span<const uint8_t>
buf, uint64_t fileOffset)
95 c.fail(
"inconsistent key header for " + k.
name);
100DirectoryInfo RootFile::readDirectory(uint64_t
offset)
102 if (
offset >= mSource->size()) {
103 throw CorruptData(mUrl,
offset,
"directory record past the end of the file");
105 auto buf = readRange(*mSource,
offset, std::min<uint64_t>(kDirectoryReadSize, mSource->size() -
offset));
111 d.nbytesKeys =
c.i32();
112 d.nbytesName =
c.i32();
113 bool big = d.version > 1000;
114 d.seekDir = big ?
c.u64() :
c.u32();
115 d.seekParent = big ?
c.u64() :
c.u32();
116 d.seekKeys = big ?
c.u64() :
c.u32();
120std::vector<KeyInfo> RootFile::readKeyList(DirectoryInfo
const& dir)
122 if (dir.seekKeys == 0 || dir.nbytesKeys <= 0) {
125 auto buf = readRange(*mSource, dir.seekKeys, dir.nbytesKeys);
126 auto header = parseKey(
buf, dir.seekKeys);
127 Cursor
c{
buf, mUrl, dir.seekKeys};
129 auto nkeys =
c.i32();
130 if (nkeys < 0 || (
size_t)nkeys >
c.remaining() / kMinKeySize) {
133 std::vector<KeyInfo>
keys;
135 for (
int i = 0;
i < nkeys; ++
i) {
139 keys.push_back(parseKey(
c.buffer().subspan(
start), dir.seekKeys +
start));
148 auto dir = readDirectory((uint64_t)mHeader.
begin + mHeader.
nbytesName);
149 mTopKeys = readKeyList(dir);
156 std::vector<KeyInfo>
result;
157 for (
auto& k :
keys) {
158 auto it = std::find_if(
result.begin(),
result.end(), [&k](
KeyInfo const&
r) { return r.name == k.name; });
161 }
else if (k.
cycle > it->cycle) {
168std::optional<KeyInfo> RootFile::findTopKey(std::string
const&
name)
170 std::optional<KeyInfo>
result;
181 static const std::regex tfRegex(
"/?DF_([0-9]+)(|-.*)$");
182 std::vector<uint64_t> numbers;
183 std::unordered_set<uint64_t> seen;
186 if (std::regex_match(k.
name,
m, tfRegex)) {
187 auto n = std::stoull(
m[1].
str());
188 if (seen.insert(
n).second) {
189 numbers.push_back(
n);
193 std::sort(numbers.begin(), numbers.end());
195 if (!parents.empty()) {
198 auto parentOf = [&parents](uint64_t
n) -> std::string
const& {
199 static const std::string none;
201 return it == parents.end() ? none : it->second;
203 std::stable_sort(numbers.begin(), numbers.end(), [&](uint64_t
a, uint64_t
b) { return parentOf(a) < parentOf(b); });
205 std::vector<std::string>
result;
206 result.reserve(numbers.size());
207 for (
auto n : numbers) {
215 if (
auto it = mKeysOf.find(df); it != mKeysOf.end()) {
218 auto key = findTopKey(df);
221 if (k.
name.starts_with(df +
"-")) {
225 throw FormatError(
"no directory " + df +
" in " + mUrl);
227 if (
key->className !=
"TDirectoryFile" &&
key->className !=
"TDirectory") {
228 throw UnsupportedLayout(df +
" in " + mUrl +
" is a " +
key->className +
", not a TDirectoryFile");
230 if (
key->compressed()) {
231 throw UnsupportedLayout(
"compressed directory record for " + df +
" in " + mUrl);
233 auto dir = readDirectory(
key->seekKey +
key->keylen);
241 std::vector<Wanted> wanted;
242 for (
auto const& k :
keys) {
245 bool listed = mTreesToPrefetch.empty() || std::ranges::find(mTreesToPrefetch, k.
name) != mTreesToPrefetch.end();
247 if (mSource->canMap() || !listed || k.
className !=
"TTree" || k.
nbytes < k.
keylen || stored > kMaxMetadataSize ||
248 offset > mSource->size() || stored > mSource->size() -
offset) {
253 std::ranges::sort(wanted, {}, &Wanted::offset);
254 std::vector<std::pair<uint64_t, std::vector<uint8_t>>> spans;
255 for (
auto const&
w : wanted) {
256 if (!spans.empty()) {
259 if (
w.offset <=
end + kMetadataGap &&
w.offset +
w.length -
start <= kMaxMetadataSize) {
264 spans.emplace_back(
w.offset, std::vector<uint8_t>(
w.length));
266 std::vector<ReadRequest> requests;
271 if (!requests.empty()) {
272 mSource->readvInto(requests);
275 for (
auto const&
w : wanted) {
276 while (spans[span].
first + spans[span].second.size() <
w.offset +
w.length) {
279 auto const* at = spans[span].second.data() + (
w.offset - spans[span].first);
280 mPrefetched[
w.seekKey].assign(at, at +
w.length);
282 mKeysOf.emplace(df,
keys);
286std::vector<uint8_t> RootFile::readStored(
const KeyInfo&
key)
288 auto stored =
key.nbytes -
key.keylen;
289 if (
auto it = mPrefetched.find(
key.seekKey); it != mPrefetched.end() && it->second.size() == stored) {
290 auto bytes = std::move(it->second);
291 mPrefetched.erase(it);
294 return readRange(*mSource,
key.seekKey +
key.keylen, stored);
300 auto stored =
key.nbytes -
key.keylen;
301 if (
key.objlen > kMaxMetadataSize || stored > kMaxMetadataSize) {
304 if (!
key.compressed()) {
305 return readStored(
key);
307 auto compressed = readStored(
key);
315 if (
key.className !=
"TTree") {
322 for (
auto& branch :
tree.branches) {
323 for (
auto& basket : branch.baskets) {
324 if (basket.nEntries >= 0) {
327 auto buf = readRange(*mSource, basket.seek, std::min<uint64_t>(basket.nbytes, kHeaderReadSize * 4));
328 auto k = parseKey(
buf, basket.seek);
330 c.seek(k.
keylen - kBasketHeaderTail);
334 basket.nEntries =
c.i32();
342 if (
auto it = mStringMaps.find(
name); it != mStringMaps.end()) {
347 return mStringMaps[
name];
349 if (
key->className !=
"TMap") {
std::vector< std::string > keys
Class for time synchronization of RawReader instances.
RootFile(std::shared_ptr< ByteSource >)
const std::vector< KeyInfo > & topKeys()
All the keys of the top directory, every cycle, in on-disk order.
TreeInfo readTree(const KeyInfo &)
std::vector< std::string > dataFrames()
std::map< std::string, std::string > readStringMap(const std::string &key)
Empty map if the key does not exist. Read once per key.
FileHeader const & header() const
std::vector< uint8_t > readPayload(const KeyInfo &)
The uncompressed payload of a key.
std::vector< KeyInfo > keysOf(const std::string &df)
GLuint const GLchar * name
GLboolean GLboolean GLboolean b
GLsizei GLsizei GLchar * source
GLuint GLsizei GLsizei * length
GLenum GLenum GLsizei len
GLboolean GLboolean GLboolean GLboolean a
GLenum GLuint GLenum GLsizei const GLchar * buf
GLubyte GLubyte GLubyte GLubyte w
TreeInfo readTreePayload(std::span< const uint8_t > payload, uint16_t keylen, std::string const &file, uint64_t fileOffset)
size_t decompressBounded(std::span< const uint8_t > src, uint32_t objLen, uint8_t *dst, size_t maxOut, std::string const &file="<memory>", uint64_t fileOffset=0)
std::vector< KeyInfo > highestCycles(std::vector< KeyInfo > const &keys)
Keeps only the highest cycle of each name, preserving first-seen order.
std::map< std::string, std::string > readStringMapPayload(std::span< const uint8_t > payload, uint16_t keylen, std::string const &file, uint64_t fileOffset)
Deserializes a TMap of TObjString to TObjString (metaData, parentFiles).
std::string to_string(gsl::span< T, Size > span)
std::unique_ptr< TTree > tree((TTree *) flIn.Get(std::string(o2::base::NameConf::CTFTREENAME).c_str()))