Skip to content

Commit caf49e8

Browse files
JasMehta08jblomer
authored andcommitted
[ntuple] Add RNTupleLocatorMulti class for kTypeMulti locator
1 parent c6eb7fd commit caf49e8

5 files changed

Lines changed: 169 additions & 11 deletions

File tree

tree/ntuple/inc/ROOT/RNTupleTypes.hxx

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,28 @@ public:
201201
std::uint64_t GetLocation() const { return fLocation; }
202202
};
203203

204+
/// RNTupleLocator payload for the kTypeMulti locator (type 0x03). Used by storage
205+
/// backends that pack multiple pages into shared objects (e.g., S3 Mode A). The two
206+
/// 32-bit fields are written to disk as separate integers, so the on-disk layout is
207+
/// directly interpretable without any bit unpacking.
208+
class RNTupleLocatorMulti {
209+
private:
210+
std::uint32_t fObjectId = 0;
211+
std::uint32_t fOffset = 0;
212+
213+
public:
214+
RNTupleLocatorMulti() = default;
215+
RNTupleLocatorMulti(std::uint32_t objectId, std::uint32_t offset) : fObjectId(objectId), fOffset(offset) {}
216+
217+
bool operator==(const RNTupleLocatorMulti &other) const
218+
{
219+
return fObjectId == other.fObjectId && fOffset == other.fOffset;
220+
}
221+
222+
std::uint32_t GetObjectId() const { return fObjectId; }
223+
std::uint32_t GetOffset() const { return fOffset; }
224+
};
225+
204226
// Workaround missing return type overloading
205227
class RNTupleLocator;
206228
namespace Internal {
@@ -216,6 +238,11 @@ template <>
216238
struct RNTupleLocatorHelper<RNTupleLocatorObject64> {
217239
static RNTupleLocatorObject64 Get(const RNTupleLocator &loc);
218240
};
241+
242+
template <>
243+
struct RNTupleLocatorHelper<RNTupleLocatorMulti> {
244+
static RNTupleLocatorMulti Get(const RNTupleLocator &loc);
245+
};
219246
} // namespace Internal
220247

221248
/// Generic information about the physical location of data. Values depend on the concrete storage type. E.g.,
@@ -226,6 +253,7 @@ struct RNTupleLocatorHelper<RNTupleLocatorObject64> {
226253
class RNTupleLocator {
227254
friend struct Internal::RNTupleLocatorHelper<std::uint64_t>;
228255
friend struct Internal::RNTupleLocatorHelper<RNTupleLocatorObject64>;
256+
friend struct Internal::RNTupleLocatorHelper<RNTupleLocatorMulti>;
229257

230258
public:
231259
/// Values for the _Type_ field in non-disk locators. Serializable types must have the MSb == 0; see
@@ -277,7 +305,8 @@ public:
277305
void SetType(ELocatorType type);
278306
void SetReserved(std::uint8_t reserved);
279307

280-
/// Note that for GetPosition() / SetPosition(), the locator type must correspond (kTypeFile, kTypeObject64).
308+
/// Note that for GetPosition() / SetPosition(), the locator type must correspond
309+
/// (kTypeFile, kTypeObject64, ...).
281310

282311
template <typename T>
283312
T GetPosition() const
@@ -287,6 +316,7 @@ public:
287316

288317
void SetPosition(std::uint64_t position);
289318
void SetPosition(RNTupleLocatorObject64 position);
319+
void SetPosition(RNTupleLocatorMulti position);
290320
};
291321

292322
namespace Internal {

tree/ntuple/src/RNTupleSerialize.cxx

Lines changed: 54 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -511,6 +511,49 @@ ROOT::RResult<void> DeserializeLocatorPayloadObject64(const unsigned char *buffe
511511
return ROOT::RResult<void>::Success();
512512
}
513513

514+
std::uint32_t SerializeLocatorPayloadMulti(const ROOT::RNTupleLocator &locator, unsigned char *buffer)
515+
{
516+
const auto &data = locator.GetPosition<ROOT::RNTupleLocatorMulti>();
517+
518+
void *bufferVoid = buffer;
519+
auto base = buffer;
520+
auto pos = base;
521+
void **where = (buffer == nullptr) ? &bufferVoid : reinterpret_cast<void **>(&pos);
522+
523+
if (locator.GetNBytesOnStorage() > std::numeric_limits<std::uint32_t>::max()) {
524+
pos += RNTupleSerializer::SerializeUInt64(locator.GetNBytesOnStorage(), *where);
525+
} else {
526+
pos += RNTupleSerializer::SerializeUInt32(locator.GetNBytesOnStorage(), *where);
527+
}
528+
pos += RNTupleSerializer::SerializeUInt32(data.GetObjectId(), *where);
529+
pos += RNTupleSerializer::SerializeUInt32(data.GetOffset(), *where);
530+
531+
return pos - base;
532+
}
533+
534+
ROOT::RResult<void> DeserializeLocatorPayloadMulti(const unsigned char *buffer, std::uint32_t sizeofLocatorPayload,
535+
ROOT::RNTupleLocator &locator)
536+
{
537+
const unsigned char *pos = buffer;
538+
if (sizeofLocatorPayload == 12) {
539+
std::uint32_t nBytesOnStorage;
540+
pos += RNTupleSerializer::DeserializeUInt32(pos, nBytesOnStorage);
541+
locator.SetNBytesOnStorage(nBytesOnStorage);
542+
} else if (sizeofLocatorPayload == 16) {
543+
std::uint64_t nBytesOnStorage;
544+
pos += RNTupleSerializer::DeserializeUInt64(pos, nBytesOnStorage);
545+
locator.SetNBytesOnStorage(nBytesOnStorage);
546+
} else {
547+
return R__FAIL("invalid Multi locator payload size: " + std::to_string(sizeofLocatorPayload));
548+
}
549+
std::uint32_t objectId;
550+
std::uint32_t offset;
551+
pos += RNTupleSerializer::DeserializeUInt32(pos, objectId);
552+
RNTupleSerializer::DeserializeUInt32(pos, offset);
553+
locator.SetPosition(ROOT::RNTupleLocatorMulti(objectId, offset));
554+
return ROOT::RResult<void>::Success();
555+
}
556+
514557
std::uint32_t SerializeAliasColumn(const ROOT::RColumnDescriptor &columnDesc,
515558
const ROOT::Internal::RNTupleSerializer::RContext &context, void *buffer)
516559
{
@@ -1091,7 +1134,7 @@ ROOT::Internal::RNTupleSerializer::SerializeLocator(const RNTupleLocator &locato
10911134
locatorType = 0x02;
10921135
break;
10931136
case RNTupleLocator::kTypeMulti:
1094-
size += SerializeLocatorPayloadObject64(locator, payloadp);
1137+
size += SerializeLocatorPayloadMulti(locator, payloadp);
10951138
locatorType = 0x03;
10961139
break;
10971140
default:
@@ -1138,14 +1181,20 @@ ROOT::RResult<std::uint32_t> ROOT::Internal::RNTupleSerializer::DeserializeLocat
11381181
locator.SetType(RNTupleLocator::kTypeFile);
11391182
DeserializeLocatorPayloadLarge(bytes, locator);
11401183
break;
1141-
case 0x02:
1184+
case 0x02: {
11421185
locator.SetType(RNTupleLocator::kTypeObject64);
1143-
DeserializeLocatorPayloadObject64(bytes, payloadSize, locator);
1186+
auto res = DeserializeLocatorPayloadObject64(bytes, payloadSize, locator);
1187+
if (!res)
1188+
return R__FORWARD_ERROR(res);
11441189
break;
1145-
case 0x03:
1190+
}
1191+
case 0x03: {
11461192
locator.SetType(RNTupleLocator::kTypeMulti);
1147-
DeserializeLocatorPayloadObject64(bytes, payloadSize, locator);
1193+
auto res = DeserializeLocatorPayloadMulti(bytes, payloadSize, locator);
1194+
if (!res)
1195+
return R__FORWARD_ERROR(res);
11481196
break;
1197+
}
11491198
default: locator.SetType(RNTupleLocator::kTypeUnknown);
11501199
}
11511200
bytes += payloadSize;

tree/ntuple/src/RNTupleTypes.cxx

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,11 +80,18 @@ void ROOT::RNTupleLocator::SetPosition(std::uint64_t position)
8080

8181
void ROOT::RNTupleLocator::SetPosition(RNTupleLocatorObject64 position)
8282
{
83-
if (GetType() != kTypeObject64 && GetType() != kTypeMulti)
83+
if (GetType() != kTypeObject64)
8484
throw RException(R__FAIL("cannot set position as 64bit object for type " + std::to_string(GetType())));
8585
fPosition = position.GetLocation();
8686
}
8787

88+
void ROOT::RNTupleLocator::SetPosition(RNTupleLocatorMulti position)
89+
{
90+
if (GetType() != kTypeMulti)
91+
throw RException(R__FAIL("cannot set position as Multi locator for type " + std::to_string(GetType())));
92+
fPosition = (static_cast<std::uint64_t>(position.GetObjectId()) << 32) | position.GetOffset();
93+
}
94+
8895
std::uint64_t ROOT::Internal::RNTupleLocatorHelper<std::uint64_t>::Get(const RNTupleLocator &loc)
8996
{
9097
if (loc.GetType() != ROOT::RNTupleLocator::kTypeFile)
@@ -95,7 +102,16 @@ std::uint64_t ROOT::Internal::RNTupleLocatorHelper<std::uint64_t>::Get(const RNT
95102
ROOT::RNTupleLocatorObject64
96103
ROOT::Internal::RNTupleLocatorHelper<ROOT::RNTupleLocatorObject64>::Get(const RNTupleLocator &loc)
97104
{
98-
if (loc.GetType() != ROOT::RNTupleLocator::kTypeObject64 && loc.GetType() != ROOT::RNTupleLocator::kTypeMulti)
105+
if (loc.GetType() != ROOT::RNTupleLocator::kTypeObject64)
99106
throw RException(R__FAIL("cannot retrieve position as 64bit object for type " + std::to_string(loc.GetType())));
100107
return RNTupleLocatorObject64{loc.fPosition};
101108
}
109+
110+
ROOT::RNTupleLocatorMulti
111+
ROOT::Internal::RNTupleLocatorHelper<ROOT::RNTupleLocatorMulti>::Get(const RNTupleLocator &loc)
112+
{
113+
if (loc.GetType() != ROOT::RNTupleLocator::kTypeMulti)
114+
throw RException(R__FAIL("cannot retrieve position as Multi locator for type " + std::to_string(loc.GetType())));
115+
return RNTupleLocatorMulti(static_cast<std::uint32_t>(loc.fPosition >> 32),
116+
static_cast<std::uint32_t>(loc.fPosition));
117+
}

tree/ntuple/test/ntuple_serialize.cxx

Lines changed: 65 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -402,7 +402,7 @@ TEST(RNTuple, SerializeLocator)
402402
// Multi locator round-trip with 32-bit nBytesOnStorage
403403
locator = RNTupleLocator{};
404404
locator.SetType(RNTupleLocator::kTypeMulti);
405-
locator.SetPosition(RNTupleLocatorObject64{42U});
405+
locator.SetPosition(RNTupleLocatorMulti{7, 1024});
406406
locator.SetNBytesOnStorage(1024U);
407407
locator.SetReserved(0);
408408
EXPECT_EQ(16u, RNTupleSerializer::SerializeLocator(locator, buffer).Unwrap());
@@ -411,7 +411,9 @@ TEST(RNTuple, SerializeLocator)
411411
EXPECT_EQ(locator.GetType(), RNTupleLocator::kTypeMulti);
412412
EXPECT_EQ(locator.GetNBytesOnStorage(), 1024U);
413413
EXPECT_EQ(locator.GetReserved(), 0);
414-
EXPECT_EQ(42U, locator.GetPosition<RNTupleLocatorObject64>().GetLocation());
414+
auto multi = locator.GetPosition<RNTupleLocatorMulti>();
415+
EXPECT_EQ(7U, multi.GetObjectId());
416+
EXPECT_EQ(1024U, multi.GetOffset());
415417

416418
// Multi locator round-trip with 64-bit nBytesOnStorage and reserved bit
417419
locator.SetNBytesOnStorage(static_cast<std::uint64_t>(std::numeric_limits<std::uint32_t>::max()) + 1);
@@ -422,7 +424,9 @@ TEST(RNTuple, SerializeLocator)
422424
EXPECT_EQ(locator.GetType(), RNTupleLocator::kTypeMulti);
423425
EXPECT_EQ(locator.GetNBytesOnStorage(), static_cast<std::uint64_t>(std::numeric_limits<std::uint32_t>::max()) + 1);
424426
EXPECT_EQ(locator.GetReserved(), 1);
425-
EXPECT_EQ(42U, locator.GetPosition<RNTupleLocatorObject64>().GetLocation());
427+
multi = locator.GetPosition<RNTupleLocatorMulti>();
428+
EXPECT_EQ(7U, multi.GetObjectId());
429+
EXPECT_EQ(1024U, multi.GetOffset());
426430

427431
std::int32_t *head = reinterpret_cast<std::int32_t *>(buffer);
428432
#ifndef R__BYTESWAP
@@ -435,6 +439,64 @@ TEST(RNTuple, SerializeLocator)
435439
EXPECT_EQ(locator.GetType(), RNTupleLocator::kTypeUnknown);
436440
}
437441

442+
TEST(RNTuple, RNTupleLocatorMultiFields)
443+
{
444+
// Default-constructed: both fields zero
445+
RNTupleLocatorMulti zero;
446+
EXPECT_EQ(0U, zero.GetObjectId());
447+
EXPECT_EQ(0U, zero.GetOffset());
448+
449+
// Non-trivial values
450+
RNTupleLocatorMulti m(0x12345, 0xABCDE);
451+
EXPECT_EQ(0x12345U, m.GetObjectId());
452+
EXPECT_EQ(0xABCDEU, m.GetOffset());
453+
454+
// Max 32-bit values: both fields use the full uint32 range
455+
RNTupleLocatorMulti maxVals(0xFFFFFFFFU, 0xFFFFFFFFU);
456+
EXPECT_EQ(0xFFFFFFFFU, maxVals.GetObjectId());
457+
EXPECT_EQ(0xFFFFFFFFU, maxVals.GetOffset());
458+
459+
// Object id only: no leak into offset
460+
RNTupleLocatorMulti idOnly(0xFFFFFFFFU, 0U);
461+
EXPECT_EQ(0xFFFFFFFFU, idOnly.GetObjectId());
462+
EXPECT_EQ(0U, idOnly.GetOffset());
463+
464+
// Offset only: no leak into object id
465+
RNTupleLocatorMulti offsetOnly(0U, 0xFFFFFFFFU);
466+
EXPECT_EQ(0U, offsetOnly.GetObjectId());
467+
EXPECT_EQ(0xFFFFFFFFU, offsetOnly.GetOffset());
468+
469+
// Equality semantics
470+
EXPECT_EQ(RNTupleLocatorMulti(1, 2), RNTupleLocatorMulti(1, 2));
471+
EXPECT_FALSE(RNTupleLocatorMulti(1, 2) == RNTupleLocatorMulti(1, 3));
472+
EXPECT_FALSE(RNTupleLocatorMulti(1, 2) == RNTupleLocatorMulti(2, 2));
473+
}
474+
475+
TEST(RNTuple, RNTupleLocatorMultiTypeEnforcement)
476+
{
477+
RNTupleLocator locator;
478+
479+
// SetPosition(Multi) requires kTypeMulti.
480+
locator.SetType(RNTupleLocator::kTypeObject64);
481+
EXPECT_THROW(locator.SetPosition(RNTupleLocatorMulti(1, 2)), ROOT::RException);
482+
locator.SetType(RNTupleLocator::kTypeFile);
483+
EXPECT_THROW(locator.SetPosition(RNTupleLocatorMulti(1, 2)), ROOT::RException);
484+
485+
// Correct usage round-trip.
486+
locator = RNTupleLocator{};
487+
locator.SetType(RNTupleLocator::kTypeMulti);
488+
locator.SetPosition(RNTupleLocatorMulti(1, 2));
489+
auto m = locator.GetPosition<RNTupleLocatorMulti>();
490+
EXPECT_EQ(1U, m.GetObjectId());
491+
EXPECT_EQ(2U, m.GetOffset());
492+
493+
// GetPosition<Multi>() rejects non-Multi types.
494+
locator = RNTupleLocator{};
495+
locator.SetType(RNTupleLocator::kTypeObject64);
496+
locator.SetPosition(RNTupleLocatorObject64{1});
497+
EXPECT_THROW(locator.GetPosition<RNTupleLocatorMulti>(), ROOT::RException);
498+
}
499+
438500
TEST(RNTuple, SerializeEnvelopeLink)
439501
{
440502
RNTupleSerializer::REnvelopeLink link;

tree/ntuple/test/ntuple_test.hxx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
using ROOT::EExtraTypeInfoIds;
5858
using ROOT::RNTupleLocalIndex;
5959
using ROOT::RNTupleLocator;
60+
using ROOT::RNTupleLocatorMulti;
6061
using ROOT::RNTupleLocatorObject64;
6162
using ROOT::Internal::RColumnIndex;
6263
using RClusterDescriptor = ROOT::RClusterDescriptor;

0 commit comments

Comments
 (0)