Skip to content

Commit 45bbd5c

Browse files
committed
Writing RNTuple with no data now works
1 parent 89223b8 commit 45bbd5c

File tree

3 files changed

+209
-194
lines changed

3 files changed

+209
-194
lines changed

src/uproot/models/RNTuple.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -270,7 +270,7 @@ def read_members(self, chunk, cursor, context, file):
270270
-_rntuple_anchor_format.size
271271
- _rntuple_anchor_checksum_format.size : -_rntuple_anchor_checksum_format.size
272272
]
273-
)
273+
), "Anchor checksum does not match! File is corrupted or incompatible."
274274
cursor.skip(-_rntuple_anchor_checksum_format.size)
275275

276276
self._header_chunk_ready = False

src/uproot/writing/_cascade.py

Lines changed: 42 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import uuid
2828

2929
import numpy
30+
import xxhash
3031

3132
import uproot.compression
3233
import uproot.const
@@ -1730,58 +1731,62 @@ def add_tree(
17301731
def add_rntuple(self, sink, name, title, akform):
17311732
import uproot.writing._cascadentuple
17321733

1734+
rntuple_spec_version_epoch = 1
1735+
rntuple_spec_version_major = 0
1736+
rntuple_spec_version_minor = 0
1737+
rntuple_spec_version_patch = 0
1738+
17331739
anchor = uproot.writing._cascadentuple.NTuple_Anchor(
1734-
None, 0, 0, 48, None, None, None, None, None, None, 0
1740+
None,
1741+
rntuple_spec_version_epoch,
1742+
rntuple_spec_version_major,
1743+
rntuple_spec_version_minor,
1744+
rntuple_spec_version_patch,
1745+
None,
1746+
None,
1747+
None,
1748+
None,
1749+
None,
1750+
None,
1751+
0, # TODO: Fix this
17351752
)
17361753

17371754
header = uproot.writing._cascadentuple.NTuple_Header(None, name, "", akform)
17381755

17391756
footer = uproot.writing._cascadentuple.NTuple_Footer(
1740-
None, 0, header._crc32, akform
1757+
None, 0, header._checksum, akform
17411758
)
17421759

1743-
# the empty page list is hard-coded bytes which represents:
1744-
# 0 1 2 3
1745-
# 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
1746-
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1747-
# | Envelope Version | Minimum Version |
1748-
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1749-
# | Size |T|
1750-
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1751-
# | Number of Items (for list frames) |Reserv.|
1752-
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1753-
# | FRAME PAYLOAD |
1754-
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1755-
# | CRC32 |
1756-
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1757-
#
1758-
# - Envelope Version = 1 (0x0100)
1759-
# - Minimum Version = 1 (0x0100)
1760-
# - Size = -8 (0xf8ffffff) [value is negative because this is a list]
1761-
# - Number of Items = 0 (0x00000000) [empty list]
1762-
# - FRAME PAYLOAD = empty [because number of items is 0]
1763-
# - CRC32 = 2678769841
1764-
# manually calculate CRC32:
1765-
1766-
# In [1]: zlib.crc32(b'\x01\x00\x01\x00\xf8\xff\xff\xff\00\00\00\00')
1767-
# Out[1]: 2678769841
1768-
# In [2]: np.array([177, 200, 170, 159], dtype=np.uint8).view("uint32")
1769-
# Out[2]: array([2678769841], dtype=uint32)
1770-
1771-
empty_page_list_bytes = numpy.array(
1772-
[1, 0, 1, 0, 248, 255, 255, 255, 0, 0, 0, 0, 177, 200, 170, 159],
1773-
dtype=numpy.uint8,
1760+
empty_page_list_headerbytes = (
1761+
uproot.writing._cascadentuple._serialize_envelope_header(
1762+
uproot.const.RNTupleEnvelopeType.PAGELIST, 48
1763+
)
17741764
)
1775-
offset = self._freesegments.allocate(16)
1776-
footer.cluster_group_record_frames[0].page_list_envlink.locator = (
1777-
uproot.writing._cascadentuple.NTuple_Locator(16, offset)
1765+
header.serialize() # so that checksum is computed
1766+
empty_page_list_payloadbytes = (
1767+
uproot.models.RNTuple._rntuple_checksum_format.pack(header._checksum)
1768+
)
1769+
empty_page_list_payloadbytes += (
1770+
uproot.writing._cascadentuple._serialize_rntuple_list_frame([])
1771+
) # cluster summaries
1772+
empty_page_list_payloadbytes += (
1773+
uproot.writing._cascadentuple._serialize_rntuple_list_frame([])
1774+
) # page locations
1775+
empty_page_list_bytes = (
1776+
empty_page_list_headerbytes + empty_page_list_payloadbytes
1777+
)
1778+
1779+
empty_page_checksum = xxhash.xxh3_64_intdigest(empty_page_list_bytes)
1780+
checksum_bytes = uproot.models.RNTuple._rntuple_checksum_format.pack(
1781+
empty_page_checksum
17781782
)
1783+
empty_page_list_bytes += checksum_bytes
17791784

17801785
ntuple = uproot.writing._cascadentuple.NTuple(
17811786
self, name, title, akform, self._freesegments, header, footer, [], anchor
17821787
)
17831788

1784-
sink.write(offset, empty_page_list_bytes)
1789+
# sink.write(offset, empty_page_list_bytes)
17851790
ntuple.write(sink)
17861791
sink.flush()
17871792
return ntuple

0 commit comments

Comments
 (0)