14#include <arrow/array/array_nested.h>
15#include <arrow/array/util.h>
16#include <arrow/buffer.h>
25std::shared_ptr<arrow::DataType> valueType(BranchInfo
const&
b)
27 switch (
b.leafClass) {
29 return arrow::boolean();
31 return b.isUnsigned ? arrow::uint8() :
arrow::int8();
33 return b.isUnsigned ? arrow::uint16() :
arrow::int16();
35 return b.isUnsigned ? arrow::uint32() :
arrow::int32();
37 return b.isUnsigned ? arrow::uint64() :
arrow::int64();
39 return arrow::float32();
41 return arrow::float64();
43 throw UnsupportedLayout(std::string(
"leaf class TLeaf") +
b.leafClass +
" of branch " +
b.name);
46BranchInfo
const& branchNamed(TreeInfo
const&
tree, std::string
const&
name)
48 for (
auto&
b :
tree.branches) {
53 throw FormatError(
"tree " +
tree.name +
" has no branch " +
name);
56std::string
format(
char const*
fmt,
auto... args)
58 auto n = std::snprintf(
nullptr, 0,
fmt, args...);
59 std::string
s(
n,
'\0');
60 std::snprintf(
s.data(),
n + 1,
fmt, args...);
67 std::vector<std::shared_ptr<arrow::Field>>
fields;
68 bool prevIsSize =
false;
69 for (
auto&
b :
tree.branches) {
70 if (prevIsSize &&
fields.back()->name() !=
b.name +
"_size") {
73 if (
b.name.ends_with(
"_size")) {
74 fields.push_back(std::make_shared<arrow::Field>(
b.name, arrow::int32()));
79 fields.push_back(std::make_shared<arrow::Field>(
b.name, arrow::list(valueType(
b))));
80 }
else if (!
b.countBranch.empty()) {
81 throw UnsupportedLayout(
"variable-length branch " +
b.name +
" is not preceded by its size branch");
82 }
else if (
b.fixedLen > 1) {
83 fields.push_back(std::make_shared<arrow::Field>(
b.name, arrow::fixed_size_list(valueType(
b),
b.fixedLen)));
85 fields.push_back(std::make_shared<arrow::Field>(
b.name, valueType(
b)));
89 if (!
fields.empty() &&
fields.back()->name().ends_with(
"_size")) {
92 return std::make_shared<arrow::Schema>(
fields);
95std::shared_ptr<arrow::Schema>
datasetSchema(std::shared_ptr<arrow::Schema>
const& physical)
97 std::vector<std::shared_ptr<arrow::Field>>
fields;
98 for (
auto&
f : physical->fields()) {
99 if (!
f->name().ends_with(
"_size")) {
103 return std::make_shared<arrow::Schema>(
fields);
109 std::vector<ColumnOp> ops;
111 for (
int fi = 0; fi <
schema->num_fields(); ++fi) {
112 auto const& field =
schema->field(fi);
113 auto const& branch = branchNamed(
tree, field->name());
114 auto type = field->type();
116 int64_t opRows = branch.entries;
117 if (
type->id() == arrow::Type::LIST) {
118 auto const& sizeBranch = branchNamed(
tree, branch.name +
"_size");
119 if (branch.entries != sizeBranch.entries) {
120 throw CorruptData(decoder.
url(), 0,
format(
"Branch %s has %lld entries, but its size branch %s has %lld.", branch.name.c_str(), (
long long)branch.entries, sizeBranch.name.c_str(), (
long long)sizeBranch.entries));
123 auto buffer = arrow::AllocateBuffer(
offsets.size() *
sizeof(int32_t), pool).ValueOrDie();
128 opRows = sizeBranch.entries;
129 }
else if (
type->id() == arrow::Type::BOOL) {
131 op.byteSize = (branch.entries + 7) / 8;
132 }
else if (
type->id() == arrow::Type::FIXED_SIZE_LIST &&
type->field(0)->type()->id() == arrow::Type::BOOL) {
135 op.byteSize = (branch.entries * branch.fixedLen) / 8 + 1;
137 op.byteSize = branch.entries * branch.fixedLen *
leafElementSize(branch.leafClass);
141 }
else if (
rows != opRows) {
142 throw CorruptData(decoder.
url(), 0,
format(
"Unmatching number of rows for branch %s. Expected %lli, found %lli", branch.name.c_str(), (
long long)
rows, (
long long)opRows));
144 ops.push_back(std::move(
op));
162 auto const*
offsets = (int32_t
const*)
op.offsets->data();
168std::shared_ptr<arrow::RecordBatch>
makeBatch(std::shared_ptr<arrow::Schema>
const&
schema, std::vector<ColumnOp>
const& ops,
171 std::vector<std::shared_ptr<arrow::Array>> columns;
172 for (
size_t i = 0;
i < ops.size(); ++
i) {
173 auto const&
op = ops[
i];
176 if (
type->id() == arrow::Type::FIXED_SIZE_LIST) {
177 auto vdata = std::make_shared<arrow::ArrayData>(
type->field(0)->type(),
entries *
op.branch->fixedLen,
178 std::vector<std::shared_ptr<arrow::Buffer>>{nullptr, buffers[i]});
179 columns.push_back(std::make_shared<arrow::FixedSizeListArray>(
type,
entries, arrow::MakeArray(vdata)));
180 }
else if (
op.kind == ColumnOp::Kind::VLA) {
181 auto total = ((int32_t
const*)
op.offsets->data())[
entries];
182 auto vdata = std::make_shared<arrow::ArrayData>(
type->field(0)->type(), total,
183 std::vector<std::shared_ptr<arrow::Buffer>>{nullptr, buffers[i]});
184 columns.push_back(std::make_shared<arrow::ListArray>(
type,
entries,
op.offsets, arrow::MakeArray(vdata)));
186 auto data = std::make_shared<arrow::ArrayData>(
type,
entries, std::vector<std::shared_ptr<arrow::Buffer>>{
nullptr,
buffers[
i]});
187 columns.push_back(arrow::MakeArray(
data));
190 return arrow::RecordBatch::Make(
schema,
rows, columns);
199 std::vector<std::shared_ptr<arrow::Buffer>>
buffers;
200 for (
auto&
op : ops) {
201 ARROW_ASSIGN_OR_RAISE(std::shared_ptr<arrow::Buffer>
buffer, arrow::AllocateBuffer(
op.byteSize, pool));
std::shared_ptr< arrow::Schema > schema
std::vector< std::shared_ptr< arrow::Field > > fields
std::vector< long > entries
void decodeVLAValues(BranchInfo const &branch, std::span< const int32_t > offsets, uint8_t *dst, size_t nbytes)
std::string const & url() const
std::vector< int32_t > decodeOffsets(BranchInfo const &sizeBranch)
Decodes a <col>_size branch into entries + 1 int32 offsets.
void decodeValues(BranchInfo const &branch, uint8_t *dst, size_t nbytes)
void decodeBooleans(BranchInfo const &branch, uint8_t *bitmap, size_t nbytes)
GLuint GLsizei const GLuint const GLintptr * offsets
GLuint const GLchar * name
GLboolean GLboolean GLboolean b
GLsizei GLsizei GLchar * source
GLint GLint GLsizei GLint GLenum GLenum type
GLint GLint GLsizei GLint GLenum format
std::shared_ptr< arrow::Schema > datasetSchema(std::shared_ptr< arrow::Schema > const &physical)
The physical schema without the *_size fields, as DataInputDirector derives it.
size_t leafElementSize(char leafClass)
Size in bytes of one value of a leaf class on disk.
std::shared_ptr< arrow::RecordBatch > makeBatch(std::shared_ptr< arrow::Schema > const &schema, std::vector< ColumnOp > const &ops, std::vector< std::shared_ptr< arrow::Buffer > > const &buffers, int64_t rows)
Wraps the buffers of ops (one per op, in order) into a record batch.
arrow::Result< std::shared_ptr< arrow::RecordBatch > > readTree(ByteSource &source, TreeInfo const &tree, Options const &options, arrow::MemoryPool *pool=arrow::default_memory_pool())
Reads a whole tree into heap memory: plan, execute, wrap.
std::shared_ptr< arrow::Schema > physicalSchema(TreeInfo const &tree)
std::vector< ColumnOp > planColumns(TreeInfo const &tree, std::shared_ptr< arrow::Schema > const &schema, BranchDecoder &decoder, arrow::MemoryPool *pool, int64_t &rows)
void executeColumnOp(BranchDecoder &decoder, ColumnOp const &op, uint8_t *dst)
Fills the buffer of op, which is exactly op.byteSize bytes.
std::unique_ptr< TTree > tree((TTree *) flIn.Get(std::string(o2::base::NameConf::CTFTREENAME).c_str()))
std::vector< ReadoutWindowData > rows