@@ -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,70 @@ 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+ // SetPosition(Object64) no longer accepts kTypeMulti after the split.
486+ locator = RNTupleLocator{};
487+ locator.SetType (RNTupleLocator::kTypeMulti );
488+ EXPECT_THROW (locator.SetPosition (RNTupleLocatorObject64{1 }), ROOT ::RException);
489+
490+ // Correct usage round-trip.
491+ locator.SetPosition (RNTupleLocatorMulti (1 , 2 ));
492+ auto m = locator.GetPosition <RNTupleLocatorMulti>();
493+ EXPECT_EQ (1U , m.GetObjectId ());
494+ EXPECT_EQ (2U , m.GetOffset ());
495+
496+ // GetPosition<Object64>() no longer accepts kTypeMulti after the split.
497+ EXPECT_THROW (locator.GetPosition <RNTupleLocatorObject64>(), ROOT ::RException);
498+
499+ // GetPosition<Multi>() rejects non-Multi types.
500+ locator = RNTupleLocator{};
501+ locator.SetType (RNTupleLocator::kTypeObject64 );
502+ locator.SetPosition (RNTupleLocatorObject64{1 });
503+ EXPECT_THROW (locator.GetPosition <RNTupleLocatorMulti>(), ROOT ::RException);
504+ }
505+
438506TEST (RNTuple, SerializeEnvelopeLink)
439507{
440508 RNTupleSerializer::REnvelopeLink link;
0 commit comments