16#include <TBufferFile.h>
18#include <arrow/buffer.h>
19#include <arrow/dataset/file_base.h>
20#include <arrow/extension_type.h>
21#include <arrow/memory_pool.h>
22#include <arrow/status.h>
23#include <arrow/type.h>
24#include <arrow/type_fwd.h>
25#include <arrow/util/key_value_metadata.h>
26#include <arrow/array/array_nested.h>
27#include <arrow/array/array_primitive.h>
28#include <arrow/array/builder_nested.h>
29#include <arrow/array/builder_primitive.h>
30#include <arrow/array/util.h>
31#include <arrow/record_batch.h>
74 const std::shared_ptr<arrow::ResizableBuffer>&
buffer);
82 static arrow::Result<std::shared_ptr<TTreeDeferredReadOutputStream>>
Create(
83 std::vector<ReadOps>& ops,
84 int64_t initial_capacity = 4096,
85 arrow::MemoryPool* pool = arrow::default_memory_pool());
95 arrow::Status
Close()
override;
96 [[nodiscard]]
bool closed()
const override;
97 [[nodiscard]] arrow::Result<int64_t>
Tell()
const override;
98 arrow::Status
Write(
const void*
data, int64_t nbytes)
override;
101 using OutputStream::Write;
105 arrow::Result<std::shared_ptr<arrow::Buffer>>
Finish();
112 arrow::Status
Reset(std::vector<ReadOps> ops,
113 int64_t initial_capacity, arrow::MemoryPool* pool);
115 [[nodiscard]] int64_t
capacity()
const {
return capacity_; }
119 std::vector<ReadOps> ops_;
122 arrow::Status Reserve(int64_t nbytes);
124 std::shared_ptr<arrow::ResizableBuffer> buffer_;
128 uint8_t* mutable_data_;
131static constexpr int64_t kBufferMinimumSize = 256;
133TTreeDeferredReadOutputStream::TTreeDeferredReadOutputStream()
134 : is_open_(false), capacity_(0), position_(0), mutable_data_(nullptr) {}
136TTreeDeferredReadOutputStream::TTreeDeferredReadOutputStream(std::vector<ReadOps>& ops,
137 const std::shared_ptr<arrow::ResizableBuffer>&
buffer)
143 mutable_data_(
buffer->mutable_data()) {}
146 std::vector<ReadOps>& ops,
147 int64_t initial_capacity, arrow::MemoryPool* pool)
151 RETURN_NOT_OK(
ptr->Reset(ops, initial_capacity, pool));
156 int64_t initial_capacity, arrow::MemoryPool* pool)
158 ARROW_ASSIGN_OR_RAISE(buffer_, AllocateResizableBuffer(initial_capacity, pool));
161 capacity_ = initial_capacity;
163 mutable_data_ = buffer_->mutable_data();
164 return arrow::Status::OK();
171 if (position_ < capacity_) {
172 RETURN_NOT_OK(buffer_->Resize(position_,
false));
175 return arrow::Status::OK();
182 RETURN_NOT_OK(
Close());
183 buffer_->ZeroPadding();
185 return std::move(buffer_);
193 while (readEntries <
op.rootBranchEntries) {
194 auto readLast =
op.branch->GetBulkRead().GetEntriesSerialized(readEntries,
rootBuffer);
196 throw runtime_error_f(
"Error while reading branch %s starting from %zu.",
op.branch->GetName(), readEntries);
198 int size = readLast *
op.listSize;
199 readEntries += readLast;
209 memset(
target, 0,
op.targetBuffer->size());
211 while (readEntries <
op.rootBranchEntries) {
212 auto beginValue = readEntries;
213 readLast =
op.branch->GetBulkRead().GetBulkEntries(readEntries,
rootBuffer);
215 throw runtime_error_f(
"Error while reading branch %s starting from %d.",
op.branch->GetName(), readEntries);
217 int size = readLast *
op.listSize;
218 readEntries += readLast;
219 for (
int i = beginValue;
i < beginValue +
size; ++
i) {
220 auto value =
static_cast<uint8_t
>(
rootBuffer.GetCurrent()[
i - beginValue] << (
i % 8));
228 auto* tPtrOffset =
reinterpret_cast<const int*
>(offsetOp.targetBuffer->data());
229 std::span<int const>
const offsets{tPtrOffset, tPtrOffset + offsetOp.rootBranchEntries + 1};
232 while (readEntries <
op.rootBranchEntries) {
233 auto readLast =
op.branch->GetBulkRead().GetEntriesSerialized(readEntries,
rootBuffer);
235 throw runtime_error_f(
"Error while reading branch %s starting from %d.",
op.branch->GetName(), readEntries);
237 if (readEntries + readLast >
op.rootBranchEntries) {
238 throw runtime_error_f(
"Invalid read range for branch %s: starting from %d, read %d entries, total entries %lld.",
239 op.branch->GetName(), readEntries, readLast,
static_cast<long long>(
op.rootBranchEntries));
243 throw runtime_error_f(
"Invalid offset range for branch %s: offsets[%d]=%d, offsets[%d]=%d.",
244 op.branch->GetName(), readEntries,
offsets[readEntries], readEntries + readLast,
offsets[readEntries + readLast]);
246 readEntries += readLast;
261 if (ARROW_PREDICT_FALSE(!is_open_)) {
262 return arrow::Status::IOError(
"OutputStream is closed");
264 if (ARROW_PREDICT_TRUE(nbytes == 0)) {
265 return arrow::Status::OK();
267 if (ARROW_PREDICT_FALSE(position_ + nbytes >= capacity_)) {
268 RETURN_NOT_OK(Reserve(nbytes));
272 if (
ref >= ops_.size()) {
273 memcpy(mutable_data_ + position_,
data, nbytes);
275 return arrow::Status::OK();
277 auto&
op = ops_[
ref];
296 op.branch->SetStatus(
false);
297 op.branch->DropBaskets(
"all");
299 op.branch->GetTransientBuffer(0)->Expand(0);
302 return arrow::Status::OK();
305arrow::Status TTreeDeferredReadOutputStream::Reserve(int64_t nbytes)
311 int64_t new_capacity = std::max(kBufferMinimumSize, capacity_);
312 new_capacity = position_ + nbytes;
313 if (new_capacity > capacity_) {
314 RETURN_NOT_OK(buffer_->Resize(new_capacity));
315 capacity_ = new_capacity;
316 mutable_data_ = buffer_->mutable_data();
318 return arrow::Status::OK();
325 : FileWriteOptions(
format)
337 const std::string&
path,
338 const std::shared_ptr<const arrow::KeyValueMetadata>& metadata)
override;
340 virtual std::unique_ptr<TTree>&
GetTree(arrow::dataset::FileSource
source) = 0;
345 size_t& mTotCompressedSize;
346 size_t& mTotUncompressedSize;
351 mTotCompressedSize(totalCompressedSize),
352 mTotUncompressedSize(totalUncompressedSize)
370 auto fs = std::dynamic_pointer_cast<VirtualRootFileSystemBase>(
source.filesystem());
374 return fs->CheckSupport(
source);
377 arrow::Result<std::shared_ptr<arrow::Schema>>
Inspect(
const arrow::dataset::FileSource&
source)
const override;
379 arrow::Result<std::shared_ptr<arrow::dataset::FileFragment>>
MakeFragment(
380 arrow::dataset::FileSource
source, arrow::compute::Expression partition_expression,
381 std::shared_ptr<arrow::Schema> physical_schema)
override;
383 arrow::Result<std::shared_ptr<arrow::dataset::FileWriter>>
MakeWriter(std::shared_ptr<arrow::io::OutputStream> destination, std::shared_ptr<arrow::Schema>
schema, std::shared_ptr<arrow::dataset::FileWriteOptions> options, arrow::fs::FileLocator destination_locator)
const override;
388 const std::shared_ptr<arrow::dataset::ScanOptions>& options,
389 const std::shared_ptr<arrow::dataset::FileFragment>& fragment)
const override;
397 mTotUncompressedSize(totalUncompressedSize),
398 mTotCompressedSize(totalCompressedSize),
403 arrow::Result<arrow::fs::FileInfo>
GetFileInfo(std::string
const&
path)
override;
412 return std::make_shared<RootObjectHandler>((
void*)mTree.get(), std::make_shared<TTreeFileFormat>(mTotCompressedSize, mTotUncompressedSize));
415 std::unique_ptr<TTree>&
GetTree(arrow::dataset::FileSource)
override
425 size_t& mTotUncompressedSize;
426 size_t& mTotCompressedSize;
427 std::unique_ptr<TTree> mTree;
432 arrow::dataset::FileSource
source(
path, shared_from_this());
433 arrow::fs::FileInfo
result;
435 result.set_type(arrow::fs::FileType::File);
444 std::shared_ptr<arrow::dataset::FileFormat>
format,
445 arrow::compute::Expression partition_expression,
446 std::shared_ptr<arrow::Schema> physical_schema)
447 : FileFragment(
source,
format,
std::move(partition_expression), physical_schema)
449 auto rootFS = std::dynamic_pointer_cast<VirtualRootFileSystemBase>(this->
source().filesystem());
450 if (rootFS.get() ==
nullptr) {
452 source.filesystem()->type_name().c_str(),
source.path().c_str());
454 auto objectHandler = rootFS->GetObjectHandler(
source);
455 if (!objectHandler->format->Equals(*
format)) {
456 throw runtime_error_f(
"Cannot read source %s with format %s to pupulate a TTreeFileFragment.",
457 source.path().c_str(), objectHandler->format->type_name().c_str());
459 mTree = objectHandler->GetObjectAsOwner<TTree>();
467 std::vector<ReadOps>&
ops()
476 return std::make_shared<arrow::Buffer>((uint8_t*)(mOps.size() - 1),
size);
480 std::unique_ptr<TTree> mTree;
481 std::vector<ReadOps> mOps;
493 arrow::Status
Close()
override;
495 arrow::Result<int64_t>
Tell()
const override;
497 arrow::Status
Write(
const void*
data, int64_t nbytes)
override;
499 bool closed()
const override;
501 TBranch*
CreateBranch(
char const* branchName,
char const* sizeBranch);
510 std::string mBranchPrefix;
518 mBranchPrefix(
std::move(branchPrefix))
524 if (mTree->GetCurrentFile() ==
nullptr) {
525 return arrow::Status::Invalid(
"Cannot close a tree not attached to a file");
527 mTree->GetCurrentFile()->Close();
528 return arrow::Status::OK();
533 return arrow::Result<int64_t>(arrow::Status::NotImplemented(
"Cannot move"));
538 return arrow::Status::NotImplemented(
"Cannot write raw bytes to a TTree");
544 if (mTree->GetCurrentFile() ==
nullptr) {
547 return mTree->GetCurrentFile()->IsOpen() ==
false;
552 if (mBranchPrefix.empty() ==
true) {
553 return mTree->Branch(branchName, (
char*)
nullptr, sizeBranch);
555 return mTree->Branch((mBranchPrefix +
"/" + branchName).c_str(), (
char*)
nullptr, (mBranchPrefix + sizeBranch).c_str());
561 std::shared_ptr<o2::framework::TTreeFileFormat>
format =
nullptr;
568 context->
format = std::make_shared<o2::framework::TTreeFileFormat>(context->totalCompressedSize, context->totalUncompressedSize);
570 .
options = [context]() {
return context->format->DefaultWriteOptions(); },
571 .format = [context]() {
return context->format; },
572 .deferredOutputStreamer = [](std::shared_ptr<arrow::dataset::FileFragment> fragment,
const std::shared_ptr<arrow::ResizableBuffer>&
buffer) -> std::shared_ptr<arrow::io::OutputStream> {
573 auto treeFragment = std::dynamic_pointer_cast<TTreeFileFragment>(fragment);
574 return std::make_shared<TTreeDeferredReadOutputStream>(treeFragment->ops(),
buffer);
590 auto* tPtrOffset =
reinterpret_cast<int*
>(
op.targetBuffer->mutable_data());
591 offsets = std::span<int>{tPtrOffset, tPtrOffset +
op.rootBranchEntries + 1};
595 while (readEntries <
op.rootBranchEntries) {
596 auto readLast =
op.branch->GetBulkRead().GetEntriesSerialized(readEntries,
rootBuffer);
597 if (readLast == -1) {
600 readEntries += readLast;
601 for (
auto i = 0;
i < readLast; ++
i) {
603 uint32_t
raw =
reinterpret_cast<uint32_t*
>(
rootBuffer.GetCurrent())[
i];
604 offset += (std::endian::native == std::endian::little) ? __builtin_bswap32(
raw) :
raw;
612 const std::shared_ptr<arrow::dataset::ScanOptions>& options,
613 const std::shared_ptr<arrow::dataset::FileFragment>& fragment)
const
615 assert(options->dataset_schema !=
nullptr);
617 auto dataset_schema = options->dataset_schema;
618 auto treeFragment = std::dynamic_pointer_cast<TTreeFileFragment>(fragment);
619 if (treeFragment.get() ==
nullptr) {
620 return {arrow::Status::NotImplemented(
"Not a ttree fragment")};
623 auto generator = [pool = options->pool, treeFragment, dataset_schema, &totalCompressedSize = mTotCompressedSize,
624 &totalUncompressedSize = mTotUncompressedSize]() -> arrow::Future<std::shared_ptr<arrow::RecordBatch>> {
626 O2_SIGNPOST_START(root_arrow_fs, tid,
"Generator",
"Creating batch for tree %{public}s", treeFragment->GetTree()->GetName());
627 std::vector<std::shared_ptr<arrow::Array>> columns;
628 std::vector<std::shared_ptr<arrow::Field>>
fields = dataset_schema->fields();
629 auto physical_schema = *treeFragment->ReadPhysicalSchema();
631 if (dataset_schema->num_fields() > physical_schema->num_fields()) {
632 throw runtime_error_f(
"One TTree must have all the fields requested in a table");
636 std::vector<BranchFieldMapping> mappings;
640 for (
int fi = 0; fi < dataset_schema->num_fields(); ++fi) {
641 auto dataset_field = dataset_schema->field(fi);
644 O2_SIGNPOST_EVENT_EMIT(root_arrow_fs, tid,
"Generator",
"Processing dataset field %{public}s.", dataset_field->name().c_str());
645 int physicalFieldIdx = physical_schema->GetFieldIndex(dataset_field->name());
647 if (physicalFieldIdx < 0) {
648 throw runtime_error_f(
"Cannot find physical field associated to %s. Possible fields: %s",
649 dataset_field->name().c_str(), physical_schema->ToString().c_str());
651 if (physicalFieldIdx > 0 && physical_schema->field(physicalFieldIdx - 1)->name().ends_with(
"_size")) {
652 O2_SIGNPOST_EVENT_EMIT(root_arrow_fs, tid,
"Generator",
"Field %{public}s has sizes in %{public}s.", dataset_field->name().c_str(),
653 physical_schema->field(physicalFieldIdx - 1)->name().c_str());
654 mappings.push_back({physicalFieldIdx, physicalFieldIdx - 1, fi});
657 if (physicalFieldIdx > 0) {
658 O2_SIGNPOST_EVENT_EMIT(root_arrow_fs, tid,
"Generator",
"Field %{public}s previous field is %{public}s.", dataset_field->name().c_str(),
659 physical_schema->field(physicalFieldIdx - 1)->name().c_str());
661 mappings.push_back({physicalFieldIdx, -1, fi});
666 auto*
tree = treeFragment->GetTree();
667 auto branches =
tree->GetListOfBranches();
668 size_t totalTreeSize = 0;
669 std::vector<TBranch*> selectedBranches;
670 for (
auto& mapping : mappings) {
671 selectedBranches.push_back((TBranch*)branches->At(mapping.mainBranchIdx));
672 O2_SIGNPOST_EVENT_EMIT(root_arrow_fs, tid,
"Generator",
"Adding branch %{public}s to stream.", selectedBranches.back()->GetName());
673 totalTreeSize += selectedBranches.back()->GetTotalSize();
674 if (mapping.vlaIdx != -1) {
675 selectedBranches.push_back((TBranch*)branches->At(mapping.vlaIdx));
676 O2_SIGNPOST_EVENT_EMIT(root_arrow_fs, tid,
"Generator",
"Adding branch %{public}s to stream.", selectedBranches.back()->GetName());
677 totalTreeSize += selectedBranches.back()->GetTotalSize();
681 size_t cacheSize = std::max(std::min(totalTreeSize, 25000000UL), 1000000UL);
683 tree->SetCacheSize(cacheSize);
684 for (
auto* branch : selectedBranches) {
685 tree->AddBranchToCache(branch,
false);
687 tree->StopCacheLearningPhase();
690 std::vector<ReadOps>& ops = treeFragment->ops();
692 ops.reserve(opsCount);
693 for (
size_t mi = 0; mi < mappings.size(); ++mi) {
696 auto datasetField = dataset_schema->field(mapping.datasetFieldIdx);
697 auto physicalField = physical_schema->field(mapping.mainBranchIdx);
699 if (mapping.vlaIdx != -1) {
700 auto* branch = (TBranch*)branches->At(mapping.vlaIdx);
703 .rootBranchEntries = branch->GetEntries(),
708 auto&
op = ops.back();
709 ARROW_ASSIGN_OR_RAISE(
op.targetBuffer, arrow::AllocateBuffer((
op.rootBranchEntries + 1) *
op.typeSize, pool));
714 auto& valueOp = ops.back();
715 valueOp.branch = (TBranch*)branches->At(mapping.mainBranchIdx);
716 valueOp.rootBranchEntries = valueOp.branch->GetEntries();
719 auto listType = std::dynamic_pointer_cast<arrow::FixedSizeListType>(datasetField->type());
720 valueOp.typeSize = physicalField->type()->byte_width();
723 if ((datasetField->type() == arrow::boolean())) {
725 valueOp.listSize = 1;
726 valueOp.targetBuffer = treeFragment->GetPlaceholderForOp((valueOp.rootBranchEntries + 7) / 8);
727 }
else if (listType && datasetField->type()->field(0)->type() == arrow::boolean()) {
728 valueOp.typeSize = physicalField->type()->field(0)->type()->byte_width();
729 valueOp.listSize = listType->list_size();
731 valueOp.targetBuffer = treeFragment->GetPlaceholderForOp((valueOp.rootBranchEntries * valueOp.listSize) / 8 + 1);
732 }
else if (mapping.vlaIdx != -1) {
733 valueOp.typeSize = physicalField->type()->field(0)->type()->byte_width();
734 valueOp.listSize = -1;
737 valueOp.targetBuffer = treeFragment->GetPlaceholderForOp(ops[ops.size() - 2].offsetCount * valueOp.typeSize);
738 }
else if (listType) {
740 valueOp.listSize = listType->list_size();
741 valueOp.typeSize = physicalField->type()->field(0)->type()->byte_width();
742 valueOp.targetBuffer = treeFragment->GetPlaceholderForOp(valueOp.rootBranchEntries * valueOp.typeSize * valueOp.listSize);
744 valueOp.typeSize = physicalField->type()->byte_width();
746 valueOp.listSize = 1;
747 valueOp.targetBuffer = treeFragment->GetPlaceholderForOp(valueOp.rootBranchEntries * valueOp.typeSize);
749 arrow::Status status;
750 std::shared_ptr<arrow::Array>
array;
753 auto vdata = std::make_shared<arrow::ArrayData>(datasetField->type()->field(0)->type(), valueOp.rootBranchEntries * valueOp.listSize,
754 std::vector<std::shared_ptr<arrow::Buffer>>{nullptr, valueOp.targetBuffer});
755 array = std::make_shared<arrow::FixedSizeListArray>(datasetField->type(), valueOp.rootBranchEntries, arrow::MakeArray(vdata));
757 O2_SIGNPOST_EVENT_EMIT(root_arrow_fs, tid,
"Op",
"Created op for branch %{public}s with %lli entries, size of the buffer %lli.",
758 valueOp.branch->GetName(),
759 valueOp.rootBranchEntries,
760 valueOp.targetBuffer->size());
761 }
else if (mapping.vlaIdx != -1) {
762 auto& offsetOp = ops[ops.size() - 2];
763 auto vdata = std::make_shared<arrow::ArrayData>(datasetField->type()->field(0)->type(), offsetOp.offsetCount,
764 std::vector<std::shared_ptr<arrow::Buffer>>{nullptr, valueOp.targetBuffer});
766 array = std::make_shared<arrow::ListArray>(datasetField->type(), offsetOp.rootBranchEntries, offsetOp.targetBuffer, arrow::MakeArray(vdata));
767 O2_SIGNPOST_EVENT_EMIT(root_arrow_fs, tid,
"Op",
"Created op for branch %{public}s with %lli entries, size of the buffer %lli.",
768 offsetOp.branch->GetName(), offsetOp.rootBranchEntries, offsetOp.targetBuffer->size());
769 O2_SIGNPOST_EVENT_EMIT(root_arrow_fs, tid,
"Op",
"Created op for branch %{public}s with %lli entries, size of the buffer %lli.",
770 valueOp.branch->GetName(),
771 offsetOp.offsetCount,
772 valueOp.targetBuffer->size());
774 auto data = std::make_shared<arrow::ArrayData>(datasetField->type(), valueOp.rootBranchEntries,
775 std::vector<std::shared_ptr<arrow::Buffer>>{nullptr, valueOp.targetBuffer});
777 O2_SIGNPOST_EVENT_EMIT(root_arrow_fs, tid,
"Op",
"Created op for branch %{public}s with %lli entries, size of the buffer %lli.",
778 valueOp.branch->GetName(),
779 valueOp.rootBranchEntries,
780 valueOp.targetBuffer->size());
783 columns.push_back(
array);
789 for (
size_t i = 0;
i < ops.size(); ++
i) {
792 rows =
op.rootBranchEntries;
795 auto& offsetOp = ops[
i - 1];
796 rows = offsetOp.rootBranchEntries;
799 throw runtime_error_f(
"Unmatching number of rows for branch %s. Expected %lli, found %lli",
op.branch->GetName(),
rows,
op.rootBranchEntries);
802 throw runtime_error_f(
"Unmatching number of rows for branch %s. Expected %lli, found %lli",
op.branch->GetName(),
rows, ops[
i - 1].offsetCount);
806 auto batch = arrow::RecordBatch::Make(dataset_schema,
rows, columns);
807 totalCompressedSize +=
tree->GetZipBytes();
808 totalUncompressedSize +=
tree->GetTotBytes();
809 O2_SIGNPOST_END(root_arrow_fs, tid,
"Generator",
"Done creating batch compressed:%zu uncompressed:%zu", totalCompressedSize, totalUncompressedSize);
818 case arrow::Type::BOOL:
820 case arrow::Type::UINT8:
822 case arrow::Type::UINT16:
824 case arrow::Type::UINT32:
826 case arrow::Type::UINT64:
828 case arrow::Type::INT8:
830 case arrow::Type::INT16:
832 case arrow::Type::INT32:
834 case arrow::Type::INT64:
836 case arrow::Type::FLOAT:
838 case arrow::Type::DOUBLE:
846 const std::string&
path,
847 const std::shared_ptr<const arrow::KeyValueMetadata>& metadata)
849 arrow::dataset::FileSource
source{
path, shared_from_this()};
850 auto prefix = metadata->Get(
"branch_prefix");
852 return std::make_shared<TTreeOutputStream>(
GetTree(
source).get(), *prefix);
854 return std::make_shared<TTreeOutputStream>(
GetTree(
source).get(),
"");
868 auto typeGenerator = [](std::shared_ptr<arrow::DataType>
const&
type,
int size) -> std::shared_ptr<arrow::DataType> {
871 return arrow::list(
type);
873 return std::move(
type);
875 return arrow::fixed_size_list(
type,
size);
880 case EDataType::kBool_t:
881 return typeGenerator(arrow::boolean(),
size);
882 case EDataType::kUChar_t:
883 return typeGenerator(arrow::uint8(),
size);
884 case EDataType::kUShort_t:
885 return typeGenerator(arrow::uint16(),
size);
886 case EDataType::kUInt_t:
887 return typeGenerator(arrow::uint32(),
size);
888 case EDataType::kULong64_t:
889 return typeGenerator(arrow::uint64(),
size);
890 case EDataType::kChar_t:
891 return typeGenerator(arrow::int8(),
size);
892 case EDataType::kShort_t:
893 return typeGenerator(arrow::int16(),
size);
894 case EDataType::kInt_t:
895 return typeGenerator(arrow::int32(),
size);
896 case EDataType::kLong64_t:
897 return typeGenerator(arrow::int64(),
size);
898 case EDataType::kFloat_t:
899 return typeGenerator(arrow::float32(),
size);
900 case EDataType::kDouble_t:
901 return typeGenerator(arrow::float64(),
size);
913 auto fs = std::dynamic_pointer_cast<VirtualRootFileSystemBase>(
source.filesystem());
918 auto objectHandler = fs->GetObjectHandler(
source);
920 if (!objectHandler->format->Equals(*
this)) {
926 auto tree = objectHandler->GetObjectAsOwner<TTree>().release();
928 auto branches =
tree->GetListOfBranches();
929 auto n = branches->GetEntries();
931 std::vector<std::shared_ptr<arrow::Field>>
fields;
933 bool prevIsSize =
false;
934 for (
auto i = 0;
i <
n; ++
i) {
935 auto branch =
static_cast<TBranch*
>(branches->At(
i));
936 std::string
name = branch->GetName();
937 if (prevIsSize &&
fields.back()->name() !=
name +
"_size") {
938 throw runtime_error_f(
"Unexpected layout for VLA container %s.", branch->GetName());
941 if (
name.ends_with(
"_size")) {
942 fields.emplace_back(std::make_shared<arrow::Field>(
name, arrow::int32()));
947 branch->GetExpectedType(cls,
type);
952 auto listSize =
static_cast<TLeaf*
>(branch->GetListOfLeaves()->At(0))->GetLenStatic();
959 if (
fields.back()->name().ends_with(
"_size")) {
962 return std::make_shared<arrow::Schema>(
fields);
967 arrow::dataset::FileSource
source, arrow::compute::Expression partition_expression,
968 std::shared_ptr<arrow::Schema> physical_schema)
971 return std::make_shared<TTreeFileFragment>(
source, std::dynamic_pointer_cast<arrow::dataset::FileFormat>(shared_from_this()),
972 std::move(partition_expression),
978 std::vector<TBranch*> branches;
979 std::vector<TBranch*> sizesBranches;
980 std::vector<std::shared_ptr<arrow::Array>> valueArrays;
981 std::vector<std::shared_ptr<arrow::Array>> sizeArrays;
982 std::vector<std::shared_ptr<arrow::DataType>> valueTypes;
984 std::vector<int64_t> valuesIdealBasketSize;
985 std::vector<int64_t> sizeIdealBasketSize;
987 std::vector<int64_t> typeSizes;
988 std::vector<int64_t> listSizes;
989 bool firstBasket =
true;
992 void finaliseBasketSize(std::shared_ptr<arrow::RecordBatch> firstBatch)
995 O2_SIGNPOST_START(root_arrow_fs, sid,
"finaliseBasketSize",
"First batch with %lli rows received and %zu columns",
996 firstBatch->num_rows(), firstBatch->columns().size());
997 for (
size_t i = 0;
i < branches.size();
i++) {
998 auto* branch = branches[
i];
999 auto* sizeBranch = sizesBranches[
i];
1001 int valueSize = valueTypes[
i]->byte_width();
1002 if (listSizes[
i] == 1) {
1003 O2_SIGNPOST_EVENT_EMIT(root_arrow_fs, sid,
"finaliseBasketSize",
"Branch %s exists and uses %d bytes per entry for %lli entries.",
1004 branch->GetName(), valueSize, firstBatch->num_rows());
1005 assert(sizeBranch ==
nullptr);
1006 branch->SetBasketSize(1024 + firstBatch->num_rows() * valueSize);
1007 }
else if (listSizes[
i] == -1) {
1008 O2_SIGNPOST_EVENT_EMIT(root_arrow_fs, sid,
"finaliseBasketSize",
"Branch %s exists and uses %d bytes per entry.",
1009 branch->GetName(), valueSize);
1011 auto column = firstBatch->GetColumnByName(schema_->field(
i)->name());
1012 auto list = std::static_pointer_cast<arrow::ListArray>(column);
1013 O2_SIGNPOST_EVENT_EMIT(root_arrow_fs, sid,
"finaliseBasketSize",
"Branch %s needed. Associated size branch %s and there are %lli entries of size %d in that list.",
1014 branch->GetName(), sizeBranch->GetName(), list->length(), valueSize);
1015 branch->SetBasketSize(1024 + firstBatch->num_rows() * valueSize * list->length());
1016 sizeBranch->SetBasketSize(1024 + firstBatch->num_rows() * 4);
1018 O2_SIGNPOST_EVENT_EMIT(root_arrow_fs, sid,
"finaliseBasketSize",
"Branch %s needed. There are %lli entries per array of size %d in that list.",
1019 branch->GetName(), listSizes[
i], valueSize);
1020 assert(sizeBranch ==
nullptr);
1021 branch->SetBasketSize(1024 + firstBatch->num_rows() * valueSize * listSizes[
i]);
1024 auto field = firstBatch->schema()->field(
i);
1025 if (field->name().starts_with(
"fIndexArray")) {
1027 int idealBasketSize = 4 * firstBatch->num_rows() + 1024 + field->type()->byte_width() * firstBatch->num_rows();
1028 int basketSize = std::max(32000, idealBasketSize);
1029 sizeBranch->SetBasketSize(basketSize);
1030 branch->SetBasketSize(basketSize);
1040 std::shared_ptr<arrow::io::OutputStream> destination,
1041 arrow::fs::FileLocator destination_locator)
1042 : FileWriter(
schema, options, destination, destination_locator)
1045 auto directoryStream = std::dynamic_pointer_cast<TDirectoryFileOutputStream>(destination_);
1046 auto treeStream = std::dynamic_pointer_cast<TTreeOutputStream>(destination_);
1048 if (directoryStream.get()) {
1049 TDirectoryFile* dir = directoryStream->GetDirectory();
1051 auto*
tree =
new TTree(destination_locator_.path.c_str(),
"");
1052 treeStream = std::make_shared<TTreeOutputStream>(
tree,
"");
1053 }
else if (treeStream.get()) {
1057 auto*
tree = treeStream->GetTree();
1058 treeStream = std::make_shared<TTreeOutputStream>(
tree, destination_locator_.path);
1061 throw std::runtime_error(
"Unsupported backend.");
1065 auto& field =
schema->field(
i);
1066 listSizes.push_back(1);
1068 int valuesIdealBasketSize = 0;
1070 switch (field->type()->id()) {
1071 case arrow::Type::FIXED_SIZE_LIST: {
1072 listSizes.back() = std::static_pointer_cast<arrow::FixedSizeListType>(field->type())->list_size();
1073 valuesIdealBasketSize = 1024 + valueTypes.back()->byte_width() * listSizes.back();
1074 valueTypes.push_back(field->type()->field(0)->type());
1075 sizesBranches.push_back(
nullptr);
1076 std::string leafList = fmt::format(
"{}[{}]{}", field->name(), listSizes.back(),
rootSuffixFromArrow(valueTypes.back()->id()));
1077 branches.push_back(treeStream->CreateBranch(field->name().c_str(), leafList.c_str()));
1079 case arrow::Type::LIST: {
1080 valueTypes.push_back(field->type()->field(0)->type());
1081 std::string leafList = fmt::format(
"{}[{}_size]{}", field->name(), field->name(),
rootSuffixFromArrow(valueTypes.back()->id()));
1082 listSizes.back() = -1;
1083 std::string sizeLeafList = field->name() +
"_size/I";
1084 sizesBranches.push_back(treeStream->CreateBranch((field->name() +
"_size").c_str(), sizeLeafList.c_str()));
1085 branches.push_back(treeStream->CreateBranch(field->name().c_str(), leafList.c_str()));
1090 valueTypes.push_back(field->type());
1092 sizesBranches.push_back(
nullptr);
1093 branches.push_back(treeStream->CreateBranch(field->name().c_str(), leafList.c_str()));
1100 arrow::Status
Write(
const std::shared_ptr<arrow::RecordBatch>& batch)
override
1103 firstBasket =
false;
1104 finaliseBasketSize(batch);
1108 if (batch->columns().empty() || batch->num_rows() == 0) {
1109 return arrow::Status::OK();
1113 auto directoryStream = std::dynamic_pointer_cast<TDirectoryFileOutputStream>(destination_);
1114 TTree*
tree =
nullptr;
1115 if (directoryStream.get()) {
1116 TDirectoryFile* dir = directoryStream->GetDirectory();
1117 tree = (TTree*)dir->Get(destination_locator_.path.c_str());
1119 auto treeStream = std::dynamic_pointer_cast<TTreeOutputStream>(destination_);
1123 throw std::runtime_error(
"Unsupported backend.");
1126 for (
auto i = 0u;
i < batch->columns().
size(); ++
i) {
1127 auto column = batch->column(
i);
1128 auto& field = batch->schema()->field(
i);
1130 valueArrays.push_back(
nullptr);
1132 switch (field->type()->id()) {
1133 case arrow::Type::FIXED_SIZE_LIST: {
1134 auto list = std::static_pointer_cast<arrow::FixedSizeListArray>(column);
1135 if (list->list_type()->field(0)->type()->id() == arrow::Type::BOOL) {
1136 int64_t
length = list->length() * list->list_type()->list_size();
1137 arrow::UInt8Builder builder;
1138 auto ok = builder.Reserve(
length);
1141 auto boolArray = std::static_pointer_cast<arrow::BooleanArray>(list->values());
1143 if (boolArray->IsValid(
i)) {
1145 uint8_t
value = boolArray->Value(
i) ? 1 : 0;
1146 auto ok = builder.Append(
value);
1149 auto ok = builder.AppendNull();
1152 valueArrays.back() = *builder.Finish();
1154 valueArrays.back() = list->values();
1157 case arrow::Type::LIST: {
1158 auto list = std::static_pointer_cast<arrow::ListArray>(column);
1159 valueArrays.back() = list->values();
1161 case arrow::Type::BOOL: {
1164 auto boolArray = std::static_pointer_cast<arrow::BooleanArray>(column);
1166 int64_t
length = boolArray->length();
1167 arrow::UInt8Builder builder;
1168 auto ok = builder.Reserve(
length);
1171 if (boolArray->IsValid(
i)) {
1173 uint8_t
value = boolArray->Value(
i) ? 1 : 0;
1174 auto ok = builder.Append(
value);
1177 auto ok = builder.AppendNull();
1180 valueArrays.back() = *builder.Finish();
1183 valueArrays.back() = column;
1188 while (pos < batch->num_rows()) {
1189 for (
size_t bi = 0; bi < branches.size(); ++bi) {
1190 auto* branch = branches[bi];
1191 auto* sizeBranch = sizesBranches[bi];
1192 auto array = batch->column(bi);
1193 auto& field = batch->schema()->field(bi);
1194 auto& listSize = listSizes[bi];
1195 auto valueType = valueTypes[bi];
1196 auto valueArray = valueArrays[bi];
1198 switch (field->type()->id()) {
1199 case arrow::Type::LIST: {
1200 auto list = std::static_pointer_cast<arrow::ListArray>(
array);
1201 listSize = list->value_length(
pos);
1202 uint8_t
const*
buffer = std::static_pointer_cast<arrow::PrimitiveArray>(valueArray)->values()->data() +
array->offset() + list->value_offset(
pos) * valueType->byte_width();
1203 branch->SetAddress((
void*)
buffer);
1204 sizeBranch->SetAddress(&listSize);
1206 case arrow::Type::FIXED_SIZE_LIST:
1209 auto byteWidth = valueType->byte_width() ? valueType->byte_width() : 1;
1210 uint8_t
const*
buffer = std::static_pointer_cast<arrow::PrimitiveArray>(valueArray)->values()->data() +
array->offset() +
pos * listSize * byteWidth;
1211 branch->SetAddress((
void*)
buffer);
1218 return arrow::Status::OK();
1223 auto treeStream = std::dynamic_pointer_cast<TTreeOutputStream>(destination_);
1224 auto*
tree = treeStream->GetTree();
1225 tree->Write(
"", TObject::kOverwrite);
1226 tree->SetDirectory(
nullptr);
1231arrow::Result<std::shared_ptr<arrow::dataset::FileWriter>>
TTreeFileFormat::MakeWriter(std::shared_ptr<arrow::io::OutputStream> destination, std::shared_ptr<arrow::Schema>
schema, std::shared_ptr<arrow::dataset::FileWriteOptions> options, arrow::fs::FileLocator destination_locator)
const
1233 auto writer = std::make_shared<TTreeFileWriter>(
schema, options, destination, destination_locator);
1234 return std::dynamic_pointer_cast<arrow::dataset::FileWriter>(writer);
1239 std::shared_ptr<TTreeFileWriteOptions> options(
std::shared_ptr< arrow::Schema > schema
std::vector< std::shared_ptr< arrow::Field > > fields
o2::raw::RawFileWriter * raw
#define DEFINE_DPL_PLUGIN_INSTANCE(NAME, KIND)
#define DEFINE_DPL_PLUGINS_END
#define DEFINE_DPL_PLUGINS_BEGIN
#define O2_DECLARE_DYNAMIC_LOG(name)
#define O2_SIGNPOST_ID_FROM_POINTER(name, log, pointer)
#define O2_SIGNPOST_END(log, id, name, format,...)
#define O2_SIGNPOST_EVENT_EMIT(log, id, name, format,...)
#define O2_SIGNPOST_START(log, id, name, format,...)
std::string type_name() const override
SingleTreeFileSystem(TTree *tree, size_t &totalCompressedSize, size_t &totalUncompressedSize)
arrow::Result< arrow::fs::FileInfo > GetFileInfo(std::string const &path) override
std::shared_ptr< RootObjectHandler > GetObjectHandler(arrow::dataset::FileSource source) override
std::unique_ptr< TTree > & GetTree(arrow::dataset::FileSource) override
~TTreeDeferredReadOutputStream() override=default
arrow::Status Reset(std::vector< ReadOps > ops, int64_t initial_capacity, arrow::MemoryPool *pool)
Initialize state of OutputStream with newly allocated memory and set position to 0.
arrow::Status Write(const void *data, int64_t nbytes) override
arrow::Result< std::shared_ptr< arrow::Buffer > > Finish()
Close the stream and return the buffer.
arrow::Result< int64_t > Tell() const override
static arrow::Result< std::shared_ptr< TTreeDeferredReadOutputStream > > Create(std::vector< ReadOps > &ops, int64_t initial_capacity=4096, arrow::MemoryPool *pool=arrow::default_memory_pool())
Create in-memory output stream with indicated capacity using a memory pool.
arrow::Status Close() override
Close the stream, preserving the buffer (retrieve it with Finish()).
bool closed() const override
TTreeFileFragment(arrow::dataset::FileSource source, std::shared_ptr< arrow::dataset::FileFormat > format, arrow::compute::Expression partition_expression, std::shared_ptr< arrow::Schema > physical_schema)
std::vector< ReadOps > & ops()
std::shared_ptr< arrow::Buffer > GetPlaceholderForOp(size_t size)
arrow::Result< std::shared_ptr< arrow::io::OutputStream > > OpenOutputStream(const std::string &path, const std::shared_ptr< const arrow::KeyValueMetadata > &metadata) override
~TTreeFileSystem() override
virtual std::unique_ptr< TTree > & GetTree(arrow::dataset::FileSource source)=0
TTreeFileWriteOptions(std::shared_ptr< arrow::dataset::FileFormat > format)
arrow::Status Write(const std::shared_ptr< arrow::RecordBatch > &batch) override
arrow::Future FinishInternal() override
TTreeFileWriter(std::shared_ptr< arrow::Schema > schema, std::shared_ptr< arrow::dataset::FileWriteOptions > options, std::shared_ptr< arrow::io::OutputStream > destination, arrow::fs::FileLocator destination_locator)
arrow::Status Write(const void *data, int64_t nbytes) override
arrow::Result< int64_t > Tell() const override
arrow::Status Close() override
bool closed() const override
TBranch * CreateBranch(char const *branchName, char const *sizeBranch)
TTreeOutputStream(TTree *, std::string branchPrefix)
GLuint GLsizei const GLuint const GLintptr * offsets
GLuint const GLchar * name
GLsizei GLsizei GLchar * source
GLsizei const GLfloat * value
GLint GLint GLsizei GLint GLenum GLenum type
GLuint GLsizei GLsizei * length
GLsizei const GLchar *const * path
GLint GLint GLsizei GLint GLenum format
Defining ITS Vertex explicitly as messageable.
RuntimeErrorRef runtime_error(const char *)
TBufferFile & rootBuffer()
@ RootObjectReadingImplementation
char const * rootSuffixFromArrow(arrow::Type::type id)
auto arrowTypeFromROOT(EDataType type, int size)
void bigEndianCopy(void *dest, const void *src, int count, size_t typeSize)
RuntimeErrorRef runtime_error_f(const char *,...)
int64_t rootBranchEntries
std::shared_ptr< arrow::Buffer > targetBuffer
std::function< std::shared_ptr< arrow::dataset::FileWriteOptions >()> options
RootArrowFactory * create() override
std::shared_ptr< o2::framework::TTreeFileFormat > format
size_t totalCompressedSize
size_t totalUncompressedSize
VectorOfTObjectPtrs other
std::unique_ptr< TTree > tree((TTree *) flIn.Get(std::string(o2::base::NameConf::CTFTREENAME).c_str()))
std::vector< ReadoutWindowData > rows