24constexpr uint32_t kByteCountMask = 0x40000000;
25constexpr uint32_t kClassMask = 0x80000000;
26constexpr uint32_t kNewClassTag = 0xFFFFFFFF;
27constexpr uint16_t kStreamedMemberWise = 0x4000;
28constexpr uint32_t kIsReferenced = 1 << 4;
29constexpr int64_t kMapOffset = 2;
31constexpr size_t kBasketHeaderTail = 19;
41struct EmbeddedBasket {
46using Object = std::variant<std::monostate,
47 std::shared_ptr<BranchInfo>,
48 std::shared_ptr<LeafInfo>,
49 std::shared_ptr<EmbeddedBasket>,
50 std::shared_ptr<std::string>>;
53std::shared_ptr<T>
as(Object
const& o)
55 auto*
p = std::get_if<std::shared_ptr<T>>(&o);
56 return p ? *
p :
nullptr;
65 explicit ObjectReader(Cursor&
c) : mC(
c) {}
68 std::map<std::string, std::string> stringMap();
80 void skipObject(
char const*
className,
bool checkVersion =
true);
81 std::vector<Object> objArray();
84 std::shared_ptr<BranchInfo> tbranch(
int64_t mapAt);
85 std::shared_ptr<LeafInfo> tleaf(std::string
const&
className,
int64_t mapAt);
86 std::shared_ptr<EmbeddedBasket> tbasket(
int64_t mapAt);
87 std::shared_ptr<std::string> tobjstring(
int64_t mapAt);
90 std::map<int64_t, std::string> mClasses;
91 std::map<int64_t, Object> mObjects;
98 if (it == table.end() || std::find(it->second.begin(), it->second.end(),
version) == it->second.end()) {
103ObjectReader::Versioned ObjectReader::header(
char const*
className)
106 auto bcnt = mC.
u32();
107 if (!(bcnt & kByteCountMask)) {
108 throw UnsupportedLayout(std::string(
"object of class ") +
className +
" without byte count");
110 size_t end =
start + 4 + (bcnt & ~kByteCountMask);
112 mC.
fail(std::string(
"byte count of ") +
className +
" past the end of the buffer");
115 if (
version & kStreamedMemberWise) {
116 throw UnsupportedLayout(std::string(
"memberwise streaming of ") +
className);
122void ObjectReader::tobject()
125 if (
version & kStreamedMemberWise) {
130 checkVersion(
"TObject",
version);
133 if (
bits & kIsReferenced) {
138std::string ObjectReader::tnamed()
151 auto bcnt = mC.
u32();
152 if (!(bcnt & kByteCountMask)) {
153 throw UnsupportedLayout(std::string(
"object of class ") +
className +
" without byte count");
160 mC.
skip(bcnt & ~kByteCountMask);
163std::vector<Object> ObjectReader::objArray()
171 mC.
fail(
"negative TObjArray size");
173 std::vector<Object>
result;
174 for (
int i = 0;
i <
size; ++
i) {
175 result.push_back(anyObject());
183Object ObjectReader::anyObject()
186 auto begPos = mC.
pos();
187 auto bcnt = mC.
u32();
188 if (!(bcnt & kByteCountMask) || bcnt == kNewClassTag) {
193 if (tag & kClassMask) {
194 throw UnsupportedLayout(
"new object without byte count");
200 throw UnsupportedLayout(
"reference to the parent object");
202 auto it = mObjects.find(tag);
203 if (it == mObjects.end()) {
208 size_t end = begPos + 4 + (bcnt & ~kByteCountMask);
210 mC.
fail(
"object byte count past the end of the buffer");
215 if (!(tag & kClassMask)) {
216 throw UnsupportedLayout(
"object reference with byte count");
217 }
else if (tag == kNewClassTag) {
219 mClasses[tagAt + kMapOffset] =
className;
222 auto it = mClasses.find(tag & ~kClassMask);
223 if (it == mClasses.end()) {
226 result = readByClass(it->second, beg + kMapOffset);
232Object ObjectReader::readByClass(std::string
const&
className,
int64_t mapAt)
235 return tbranch(mapAt);
237 if (
className.size() == 6 &&
className.starts_with(
"TLeaf") && std::string_view(
"OBSILFD").find(
className[5]) != std::string_view::npos) {
241 return tbasket(mapAt);
244 return tobjstring(mapAt);
246 throw UnsupportedLayout(
"objects of class " +
className);
249std::shared_ptr<LeafInfo> ObjectReader::tleaf(std::string
const&
className,
int64_t mapAt)
252 auto leaf = std::make_shared<LeafInfo>();
253 mObjects[mapAt] = leaf;
255 auto vl =
header(
"TLeaf");
257 leaf->name = tnamed();
258 leaf->len = mC.
i32();
262 leaf->isUnsigned = mC.
u8() != 0;
263 auto count = anyObject();
266 }
else if (!std::holds_alternative<std::monostate>(
count)) {
267 throw UnsupportedLayout(
"fLeafCount of " + leaf->name +
" is not a leaf");
275std::shared_ptr<EmbeddedBasket> ObjectReader::tbasket(
int64_t mapAt)
278 auto basket = std::make_shared<EmbeddedBasket>();
279 mObjects[mapAt] = basket;
285 auto keylen = mC.
u16();
287 if (keylen < kBasketHeaderTail) {
290 mC.
seek(
start + keylen - kBasketHeaderTail);
291 checkVersion(
"TBasket", mC.
u16() % 1000);
294 auto nevbuf = mC.
i32();
297 if (nevbuf < 0 || last < keylen) {
298 mC.
fail(
"inconsistent embedded basket header");
300 basket->nEntries = nevbuf;
305 bool displacements = flag > 40;
309 bool hasBuffer = flag > 10;
313 if (flag == 1 && nevbuf > 0) {
315 mC.
skip(4 + 4 * (
size_t)nevbuf);
317 mC.
skip(4 + 4 * (
size_t)nevbuf);
324 basket->data.assign(
data.begin(),
data.end());
329std::shared_ptr<std::string> ObjectReader::tobjstring(
int64_t mapAt)
331 auto s = std::make_shared<std::string>();
340std::shared_ptr<BranchInfo> ObjectReader::tbranch(
int64_t mapAt)
342 auto b = std::make_shared<BranchInfo>();
346 skipObject(
"TAttFill",
false);
350 auto writeBasket = mC.
i32();
352 skipObject(
"ROOT::TIOFeatures",
false);
354 auto maxBaskets = mC.
i32();
356 b->entries = mC.
i64();
358 b->totBytes = mC.
i64();
359 b->zipBytes = mC.
i64();
360 if (maxBaskets < 0 || writeBasket < 0 || b->
entries < 0) {
361 mC.
fail(
"inconsistent TBranch header for " +
b->name);
364 if ((
size_t)maxBaskets > mC.
remaining() / 20) {
365 mC.
fail(
"fMaxBaskets " +
std::to_string(maxBaskets) +
" of branch " +
b->name +
" exceeds the buffer");
368 if (!objArray().
empty()) {
369 throw UnsupportedLayout(
"branch " +
b->name +
" has sub-branches");
371 auto leaves = objArray();
372 if (leaves.size() != 1 || !as<LeafInfo>(leaves[0])) {
373 throw UnsupportedLayout(
"branch " +
b->name +
" does not have exactly one leaf");
375 auto baskets = objArray();
377 auto readArray = [
this, maxBaskets](
auto reader) {
379 std::vector<int64_t>
values(maxBaskets);
385 auto basketBytes = readArray([
this] {
return (
int64_t)mC.
i32(); });
386 auto basketEntry = readArray([
this] {
return mC.
i64(); });
387 auto basketSeek = readArray([
this] {
return mC.
i64(); });
388 if (!mC.
string().empty()) {
389 throw UnsupportedLayout(
"branch " +
b->name +
" stores its baskets in another file");
393 auto leaf = as<LeafInfo>(leaves[0]);
394 b->leafClass = leaf->className[5];
395 b->isUnsigned = leaf->isUnsigned;
396 b->fixedLen = leaf->len;
397 b->countBranch = leaf->countLeaf;
401 while (nNormal < maxBaskets && nNormal < writeBasket && basketSeek[nNormal] != 0) {
404 for (
int i = 0;
i < nNormal; ++
i) {
405 if (basketBytes[
i] <= 0 || basketSeek[
i] < 0) {
406 mC.
fail(
"invalid basket location in branch " +
b->name);
410 bool hasNext =
i + 1 < maxBaskets;
411 if (basketEntry[
i] < 0 || (hasNext && basketEntry[
i + 1] < basketEntry[
i])) {
412 mC.
fail(
"invalid basket entries in branch " +
b->name);
414 int64_t n = hasNext ? basketEntry[
i + 1] - basketEntry[
i] : -1;
416 mC.
fail(
"invalid basket entries in branch " +
b->name);
418 b->baskets.push_back(BasketRef{(uint64_t)basketSeek[
i], (uint32_t)basketBytes[
i], basketEntry[
i], (int32_t)
n,
false, {}});
421 bool knownNormal = nNormal < maxBaskets;
422 int64_t normalEntries = nNormal == 0 ? 0 : (knownNormal ? basketEntry[nNormal] : -1);
423 if (normalEntries !=
b->entries) {
425 for (
auto& o : baskets) {
426 auto e = as<EmbeddedBasket>(o);
427 if (!e || (!knownNormal && e->nEntries == 0)) {
430 b->baskets.push_back(BasketRef{0, 0,
first, e->nEntries,
true, std::move(e->data)});
431 first += e->nEntries;
438 if (nNormal > 0 &&
b->baskets[nNormal - 1].nEntries < 0) {
439 int64_t rest =
b->entries -
b->baskets[nNormal - 1].firstEntry;
440 for (
size_t i = nNormal;
i <
b->baskets.size(); ++
i) {
441 rest -=
b->baskets[
i].nEntries;
443 if (rest >= 0 && rest <= INT32_MAX) {
444 b->baskets[nNormal - 1].nEntries = (int32_t)rest;
450TreeInfo ObjectReader::tree()
455 skipObject(
"TAttLine",
false);
456 skipObject(
"TAttFill",
false);
457 skipObject(
"TAttMarker",
false);
458 t.entries = mC.
i64();
459 t.totBytes = mC.
i64();
460 t.zipBytes = mC.
i64();
464 auto nClusterRange = mC.
u32();
467 mC.
skip(8 * (
size_t)nClusterRange);
469 mC.
skip(8 * (
size_t)nClusterRange);
470 skipObject(
"ROOT::TIOFeatures",
false);
471 for (
auto& o : objArray()) {
472 auto b = as<BranchInfo>(o);
474 throw UnsupportedLayout(
"tree " + t.name +
" has a branch which is not a TBranch");
476 t.branches.push_back(std::move(*
b));
483std::map<std::string, std::string> ObjectReader::stringMap()
485 std::map<std::string, std::string>
result;
490 for (
int i = 0;
i <
n; ++
i) {
491 auto key = as<std::string>(anyObject());
492 auto value = as<std::string>(anyObject());
494 throw UnsupportedLayout(
"TMap entry which is not a TObjString pair");
523 static const std::map<std::string, std::vector<int>> table{
546 Cursor c{payload, file, fileOffset, keylen};
547 return ObjectReader{
c}.tree();
550std::map<std::string, std::string>
readStringMapPayload(std::span<const uint8_t> payload, uint16_t keylen, std::string
const& file, uint64_t fileOffset)
552 Cursor c{payload, file, fileOffset, keylen};
553 return ObjectReader{
c}.stringMap();
std::vector< std::string > header
std::vector< long > entries
std::span< const uint8_t > buffer() const
std::span< const uint8_t > bytes(size_t n)
std::string const & file() const
int64_t displacement() const
std::string string()
TString: 1-byte length, or 0xFF followed by a 4-byte length.
void fail(std::string const &what) const
std::string cstring()
NUL-terminated string, as used for class names after kNewClassTag.
GLuint const GLchar * name
GLboolean GLboolean GLboolean b
GLsizei const GLfloat * value
GLenum GLsizei GLsizei GLint * values
GLenum GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const void * bits
GLenum GLenum GLsizei len
TreeInfo readTreePayload(std::span< const uint8_t > payload, uint16_t keylen, std::string const &file, uint64_t fileOffset)
size_t leafElementSize(char leafClass)
Size in bytes of one value of a leaf class on disk.
std::map< std::string, std::vector< int > > const & supportedClassVersions()
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).
PlaceholderNode as(Configurable< T > const &v)
void check(const std::vector< std::string > &arguments, const std::vector< ConfigParamSpec > &workflowOptions, const std::vector< DeviceSpec > &deviceSpecs, CheckMatrix &matrix)
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()))