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;
401 arrow::Result<arrow::fs::FileInfo>
GetFileInfo(std::string
const&
path)
override;
410 return std::make_shared<RootObjectHandler>((
void*)mTree.get(), std::make_shared<TTreeFileFormat>(mTotCompressedSize, mTotUncompressedSize));
413 std::unique_ptr<TTree>&
GetTree(arrow::dataset::FileSource)
override
420 size_t mTotUncompressedSize;
421 size_t mTotCompressedSize;
422 std::unique_ptr<TTree> mTree;
427 arrow::dataset::FileSource
source(
path, shared_from_this());
428 arrow::fs::FileInfo
result;
430 result.set_type(arrow::fs::FileType::File);
439 std::shared_ptr<arrow::dataset::FileFormat>
format,
440 arrow::compute::Expression partition_expression,
441 std::shared_ptr<arrow::Schema> physical_schema)
442 : FileFragment(
source,
format,
std::move(partition_expression), physical_schema)
444 auto rootFS = std::dynamic_pointer_cast<VirtualRootFileSystemBase>(this->
source().filesystem());
445 if (rootFS.get() ==
nullptr) {
447 source.filesystem()->type_name().c_str(),
source.path().c_str());
449 auto objectHandler = rootFS->GetObjectHandler(
source);
450 if (!objectHandler->format->Equals(*
format)) {
451 throw runtime_error_f(
"Cannot read source %s with format %s to pupulate a TTreeFileFragment.",
452 source.path().c_str(), objectHandler->format->type_name().c_str());
454 mTree = objectHandler->GetObjectAsOwner<TTree>();
462 std::vector<ReadOps>&
ops()
471 return std::make_shared<arrow::Buffer>((uint8_t*)(mOps.size() - 1),
size);
475 std::unique_ptr<TTree> mTree;
476 std::vector<ReadOps> mOps;
488 arrow::Status
Close()
override;
490 arrow::Result<int64_t>
Tell()
const override;
492 arrow::Status
Write(
const void*
data, int64_t nbytes)
override;
494 bool closed()
const override;
496 TBranch*
CreateBranch(
char const* branchName,
char const* sizeBranch);
505 std::string mBranchPrefix;
513 mBranchPrefix(
std::move(branchPrefix))
519 if (mTree->GetCurrentFile() ==
nullptr) {
520 return arrow::Status::Invalid(
"Cannot close a tree not attached to a file");
522 mTree->GetCurrentFile()->Close();
523 return arrow::Status::OK();
528 return arrow::Result<int64_t>(arrow::Status::NotImplemented(
"Cannot move"));
533 return arrow::Status::NotImplemented(
"Cannot write raw bytes to a TTree");
539 if (mTree->GetCurrentFile() ==
nullptr) {
542 return mTree->GetCurrentFile()->IsOpen() ==
false;
547 if (mBranchPrefix.empty() ==
true) {
548 return mTree->Branch(branchName, (
char*)
nullptr, sizeBranch);
550 return mTree->Branch((mBranchPrefix +
"/" + branchName).c_str(), (
char*)
nullptr, (mBranchPrefix + sizeBranch).c_str());
556 std::shared_ptr<o2::framework::TTreeFileFormat>
format =
nullptr;
563 context->
format = std::make_shared<o2::framework::TTreeFileFormat>(context->totalCompressedSize, context->totalUncompressedSize);
565 .
options = [context]() {
return context->format->DefaultWriteOptions(); },
566 .format = [context]() {
return context->format; },
567 .deferredOutputStreamer = [](std::shared_ptr<arrow::dataset::FileFragment> fragment,
const std::shared_ptr<arrow::ResizableBuffer>&
buffer) -> std::shared_ptr<arrow::io::OutputStream> {
568 auto treeFragment = std::dynamic_pointer_cast<TTreeFileFragment>(fragment);
569 return std::make_shared<TTreeDeferredReadOutputStream>(treeFragment->ops(),
buffer);
585 auto* tPtrOffset =
reinterpret_cast<int*
>(
op.targetBuffer->mutable_data());
586 offsets = std::span<int>{tPtrOffset, tPtrOffset +
op.rootBranchEntries + 1};
590 while (readEntries <
op.rootBranchEntries) {
591 auto readLast =
op.branch->GetBulkRead().GetEntriesSerialized(readEntries,
rootBuffer);
592 if (readLast == -1) {
595 readEntries += readLast;
596 for (
auto i = 0;
i < readLast; ++
i) {
598 uint32_t
raw =
reinterpret_cast<uint32_t*
>(
rootBuffer.GetCurrent())[
i];
599 offset += (std::endian::native == std::endian::little) ? __builtin_bswap32(
raw) :
raw;
607 const std::shared_ptr<arrow::dataset::ScanOptions>& options,
608 const std::shared_ptr<arrow::dataset::FileFragment>& fragment)
const
610 assert(options->dataset_schema !=
nullptr);
612 auto dataset_schema = options->dataset_schema;
613 auto treeFragment = std::dynamic_pointer_cast<TTreeFileFragment>(fragment);
614 if (treeFragment.get() ==
nullptr) {
615 return {arrow::Status::NotImplemented(
"Not a ttree fragment")};
618 auto generator = [pool = options->pool, treeFragment, dataset_schema, &totalCompressedSize = mTotCompressedSize,
619 &totalUncompressedSize = mTotUncompressedSize]() -> arrow::Future<std::shared_ptr<arrow::RecordBatch>> {
621 O2_SIGNPOST_START(root_arrow_fs, tid,
"Generator",
"Creating batch for tree %{public}s", treeFragment->GetTree()->GetName());
622 std::vector<std::shared_ptr<arrow::Array>> columns;
623 std::vector<std::shared_ptr<arrow::Field>>
fields = dataset_schema->fields();
624 auto physical_schema = *treeFragment->ReadPhysicalSchema();
626 if (dataset_schema->num_fields() > physical_schema->num_fields()) {
627 throw runtime_error_f(
"One TTree must have all the fields requested in a table");
631 std::vector<BranchFieldMapping> mappings;
635 for (
int fi = 0; fi < dataset_schema->num_fields(); ++fi) {
636 auto dataset_field = dataset_schema->field(fi);
639 O2_SIGNPOST_EVENT_EMIT(root_arrow_fs, tid,
"Generator",
"Processing dataset field %{public}s.", dataset_field->name().c_str());
640 int physicalFieldIdx = physical_schema->GetFieldIndex(dataset_field->name());
642 if (physicalFieldIdx < 0) {
643 throw runtime_error_f(
"Cannot find physical field associated to %s. Possible fields: %s",
644 dataset_field->name().c_str(), physical_schema->ToString().c_str());
646 if (physicalFieldIdx > 0 && physical_schema->field(physicalFieldIdx - 1)->name().ends_with(
"_size")) {
647 O2_SIGNPOST_EVENT_EMIT(root_arrow_fs, tid,
"Generator",
"Field %{public}s has sizes in %{public}s.", dataset_field->name().c_str(),
648 physical_schema->field(physicalFieldIdx - 1)->name().c_str());
649 mappings.push_back({physicalFieldIdx, physicalFieldIdx - 1, fi});
652 if (physicalFieldIdx > 0) {
653 O2_SIGNPOST_EVENT_EMIT(root_arrow_fs, tid,
"Generator",
"Field %{public}s previous field is %{public}s.", dataset_field->name().c_str(),
654 physical_schema->field(physicalFieldIdx - 1)->name().c_str());
656 mappings.push_back({physicalFieldIdx, -1, fi});
661 auto*
tree = treeFragment->GetTree();
662 auto branches =
tree->GetListOfBranches();
663 size_t totalTreeSize = 0;
664 std::vector<TBranch*> selectedBranches;
665 for (
auto& mapping : mappings) {
666 selectedBranches.push_back((TBranch*)branches->At(mapping.mainBranchIdx));
667 O2_SIGNPOST_EVENT_EMIT(root_arrow_fs, tid,
"Generator",
"Adding branch %{public}s to stream.", selectedBranches.back()->GetName());
668 totalTreeSize += selectedBranches.back()->GetTotalSize();
669 if (mapping.vlaIdx != -1) {
670 selectedBranches.push_back((TBranch*)branches->At(mapping.vlaIdx));
671 O2_SIGNPOST_EVENT_EMIT(root_arrow_fs, tid,
"Generator",
"Adding branch %{public}s to stream.", selectedBranches.back()->GetName());
672 totalTreeSize += selectedBranches.back()->GetTotalSize();
676 size_t cacheSize = std::max(std::min(totalTreeSize, 25000000UL), 1000000UL);
678 tree->SetCacheSize(cacheSize);
679 for (
auto* branch : selectedBranches) {
680 tree->AddBranchToCache(branch,
false);
682 tree->StopCacheLearningPhase();
685 std::vector<ReadOps>& ops = treeFragment->ops();
687 ops.reserve(opsCount);
688 for (
size_t mi = 0; mi < mappings.size(); ++mi) {
691 auto datasetField = dataset_schema->field(mapping.datasetFieldIdx);
692 auto physicalField = physical_schema->field(mapping.mainBranchIdx);
694 if (mapping.vlaIdx != -1) {
695 auto* branch = (TBranch*)branches->At(mapping.vlaIdx);
698 .rootBranchEntries = branch->GetEntries(),
703 auto&
op = ops.back();
704 ARROW_ASSIGN_OR_RAISE(
op.targetBuffer, arrow::AllocateBuffer((
op.rootBranchEntries + 1) *
op.typeSize, pool));
709 auto& valueOp = ops.back();
710 valueOp.branch = (TBranch*)branches->At(mapping.mainBranchIdx);
711 valueOp.rootBranchEntries = valueOp.branch->GetEntries();
714 auto listType = std::dynamic_pointer_cast<arrow::FixedSizeListType>(datasetField->type());
715 valueOp.typeSize = physicalField->type()->byte_width();
718 if ((datasetField->type() == arrow::boolean())) {
720 valueOp.listSize = 1;
721 valueOp.targetBuffer = treeFragment->GetPlaceholderForOp((valueOp.rootBranchEntries + 7) / 8);
722 }
else if (listType && datasetField->type()->field(0)->type() == arrow::boolean()) {
723 valueOp.typeSize = physicalField->type()->field(0)->type()->byte_width();
724 valueOp.listSize = listType->list_size();
726 valueOp.targetBuffer = treeFragment->GetPlaceholderForOp((valueOp.rootBranchEntries * valueOp.listSize) / 8 + 1);
727 }
else if (mapping.vlaIdx != -1) {
728 valueOp.typeSize = physicalField->type()->field(0)->type()->byte_width();
729 valueOp.listSize = -1;
732 valueOp.targetBuffer = treeFragment->GetPlaceholderForOp(ops[ops.size() - 2].offsetCount * valueOp.typeSize);
733 }
else if (listType) {
735 valueOp.listSize = listType->list_size();
736 valueOp.typeSize = physicalField->type()->field(0)->type()->byte_width();
737 valueOp.targetBuffer = treeFragment->GetPlaceholderForOp(valueOp.rootBranchEntries * valueOp.typeSize * valueOp.listSize);
739 valueOp.typeSize = physicalField->type()->byte_width();
741 valueOp.listSize = 1;
742 valueOp.targetBuffer = treeFragment->GetPlaceholderForOp(valueOp.rootBranchEntries * valueOp.typeSize);
744 arrow::Status status;
745 std::shared_ptr<arrow::Array>
array;
748 auto vdata = std::make_shared<arrow::ArrayData>(datasetField->type()->field(0)->type(), valueOp.rootBranchEntries * valueOp.listSize,
749 std::vector<std::shared_ptr<arrow::Buffer>>{nullptr, valueOp.targetBuffer});
750 array = std::make_shared<arrow::FixedSizeListArray>(datasetField->type(), valueOp.rootBranchEntries, arrow::MakeArray(vdata));
752 O2_SIGNPOST_EVENT_EMIT(root_arrow_fs, tid,
"Op",
"Created op for branch %{public}s with %lli entries, size of the buffer %lli.",
753 valueOp.branch->GetName(),
754 valueOp.rootBranchEntries,
755 valueOp.targetBuffer->size());
756 }
else if (mapping.vlaIdx != -1) {
757 auto& offsetOp = ops[ops.size() - 2];
758 auto vdata = std::make_shared<arrow::ArrayData>(datasetField->type()->field(0)->type(), offsetOp.offsetCount,
759 std::vector<std::shared_ptr<arrow::Buffer>>{nullptr, valueOp.targetBuffer});
761 array = std::make_shared<arrow::ListArray>(datasetField->type(), offsetOp.rootBranchEntries, offsetOp.targetBuffer, arrow::MakeArray(vdata));
762 O2_SIGNPOST_EVENT_EMIT(root_arrow_fs, tid,
"Op",
"Created op for branch %{public}s with %lli entries, size of the buffer %lli.",
763 offsetOp.branch->GetName(), offsetOp.rootBranchEntries, offsetOp.targetBuffer->size());
764 O2_SIGNPOST_EVENT_EMIT(root_arrow_fs, tid,
"Op",
"Created op for branch %{public}s with %lli entries, size of the buffer %lli.",
765 valueOp.branch->GetName(),
766 offsetOp.offsetCount,
767 valueOp.targetBuffer->size());
769 auto data = std::make_shared<arrow::ArrayData>(datasetField->type(), valueOp.rootBranchEntries,
770 std::vector<std::shared_ptr<arrow::Buffer>>{nullptr, valueOp.targetBuffer});
772 O2_SIGNPOST_EVENT_EMIT(root_arrow_fs, tid,
"Op",
"Created op for branch %{public}s with %lli entries, size of the buffer %lli.",
773 valueOp.branch->GetName(),
774 valueOp.rootBranchEntries,
775 valueOp.targetBuffer->size());
778 columns.push_back(
array);
784 for (
size_t i = 0;
i < ops.size(); ++
i) {
787 rows =
op.rootBranchEntries;
790 auto& offsetOp = ops[
i - 1];
791 rows = offsetOp.rootBranchEntries;
794 throw runtime_error_f(
"Unmatching number of rows for branch %s. Expected %lli, found %lli",
op.branch->GetName(),
rows,
op.rootBranchEntries);
797 throw runtime_error_f(
"Unmatching number of rows for branch %s. Expected %lli, found %lli",
op.branch->GetName(),
rows, ops[
i - 1].offsetCount);
801 auto batch = arrow::RecordBatch::Make(dataset_schema,
rows, columns);
802 totalCompressedSize +=
tree->GetZipBytes();
803 totalUncompressedSize +=
tree->GetTotBytes();
804 O2_SIGNPOST_END(root_arrow_fs, tid,
"Generator",
"Done creating batch compressed:%zu uncompressed:%zu", totalCompressedSize, totalUncompressedSize);
813 case arrow::Type::BOOL:
815 case arrow::Type::UINT8:
817 case arrow::Type::UINT16:
819 case arrow::Type::UINT32:
821 case arrow::Type::UINT64:
823 case arrow::Type::INT8:
825 case arrow::Type::INT16:
827 case arrow::Type::INT32:
829 case arrow::Type::INT64:
831 case arrow::Type::FLOAT:
833 case arrow::Type::DOUBLE:
841 const std::string&
path,
842 const std::shared_ptr<const arrow::KeyValueMetadata>& metadata)
844 arrow::dataset::FileSource
source{
path, shared_from_this()};
845 auto prefix = metadata->Get(
"branch_prefix");
847 return std::make_shared<TTreeOutputStream>(
GetTree(
source).get(), *prefix);
849 return std::make_shared<TTreeOutputStream>(
GetTree(
source).get(),
"");
863 auto typeGenerator = [](std::shared_ptr<arrow::DataType>
const&
type,
int size) -> std::shared_ptr<arrow::DataType> {
866 return arrow::list(
type);
868 return std::move(
type);
870 return arrow::fixed_size_list(
type,
size);
875 case EDataType::kBool_t:
876 return typeGenerator(arrow::boolean(),
size);
877 case EDataType::kUChar_t:
878 return typeGenerator(arrow::uint8(),
size);
879 case EDataType::kUShort_t:
880 return typeGenerator(arrow::uint16(),
size);
881 case EDataType::kUInt_t:
882 return typeGenerator(arrow::uint32(),
size);
883 case EDataType::kULong64_t:
884 return typeGenerator(arrow::uint64(),
size);
885 case EDataType::kChar_t:
886 return typeGenerator(arrow::int8(),
size);
887 case EDataType::kShort_t:
888 return typeGenerator(arrow::int16(),
size);
889 case EDataType::kInt_t:
890 return typeGenerator(arrow::int32(),
size);
891 case EDataType::kLong64_t:
892 return typeGenerator(arrow::int64(),
size);
893 case EDataType::kFloat_t:
894 return typeGenerator(arrow::float32(),
size);
895 case EDataType::kDouble_t:
896 return typeGenerator(arrow::float64(),
size);
908 auto fs = std::dynamic_pointer_cast<VirtualRootFileSystemBase>(
source.filesystem());
913 auto objectHandler = fs->GetObjectHandler(
source);
915 if (!objectHandler->format->Equals(*
this)) {
921 auto tree = objectHandler->GetObjectAsOwner<TTree>().release();
923 auto branches =
tree->GetListOfBranches();
924 auto n = branches->GetEntries();
926 std::vector<std::shared_ptr<arrow::Field>>
fields;
928 bool prevIsSize =
false;
929 for (
auto i = 0;
i <
n; ++
i) {
930 auto branch =
static_cast<TBranch*
>(branches->At(
i));
931 std::string
name = branch->GetName();
932 if (prevIsSize &&
fields.back()->name() !=
name +
"_size") {
933 throw runtime_error_f(
"Unexpected layout for VLA container %s.", branch->GetName());
936 if (
name.ends_with(
"_size")) {
937 fields.emplace_back(std::make_shared<arrow::Field>(
name, arrow::int32()));
942 branch->GetExpectedType(cls,
type);
947 auto listSize =
static_cast<TLeaf*
>(branch->GetListOfLeaves()->At(0))->GetLenStatic();
954 if (
fields.back()->name().ends_with(
"_size")) {
957 return std::make_shared<arrow::Schema>(
fields);
962 arrow::dataset::FileSource
source, arrow::compute::Expression partition_expression,
963 std::shared_ptr<arrow::Schema> physical_schema)
966 return std::make_shared<TTreeFileFragment>(
source, std::dynamic_pointer_cast<arrow::dataset::FileFormat>(shared_from_this()),
967 std::move(partition_expression),
973 std::vector<TBranch*> branches;
974 std::vector<TBranch*> sizesBranches;
975 std::vector<std::shared_ptr<arrow::Array>> valueArrays;
976 std::vector<std::shared_ptr<arrow::Array>> sizeArrays;
977 std::vector<std::shared_ptr<arrow::DataType>> valueTypes;
979 std::vector<int64_t> valuesIdealBasketSize;
980 std::vector<int64_t> sizeIdealBasketSize;
982 std::vector<int64_t> typeSizes;
983 std::vector<int64_t> listSizes;
984 bool firstBasket =
true;
987 void finaliseBasketSize(std::shared_ptr<arrow::RecordBatch> firstBatch)
990 O2_SIGNPOST_START(root_arrow_fs, sid,
"finaliseBasketSize",
"First batch with %lli rows received and %zu columns",
991 firstBatch->num_rows(), firstBatch->columns().size());
992 for (
size_t i = 0;
i < branches.size();
i++) {
993 auto* branch = branches[
i];
994 auto* sizeBranch = sizesBranches[
i];
996 int valueSize = valueTypes[
i]->byte_width();
997 if (listSizes[
i] == 1) {
998 O2_SIGNPOST_EVENT_EMIT(root_arrow_fs, sid,
"finaliseBasketSize",
"Branch %s exists and uses %d bytes per entry for %lli entries.",
999 branch->GetName(), valueSize, firstBatch->num_rows());
1000 assert(sizeBranch ==
nullptr);
1001 branch->SetBasketSize(1024 + firstBatch->num_rows() * valueSize);
1002 }
else if (listSizes[
i] == -1) {
1003 O2_SIGNPOST_EVENT_EMIT(root_arrow_fs, sid,
"finaliseBasketSize",
"Branch %s exists and uses %d bytes per entry.",
1004 branch->GetName(), valueSize);
1006 auto column = firstBatch->GetColumnByName(schema_->field(
i)->name());
1007 auto list = std::static_pointer_cast<arrow::ListArray>(column);
1008 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.",
1009 branch->GetName(), sizeBranch->GetName(), list->length(), valueSize);
1010 branch->SetBasketSize(1024 + firstBatch->num_rows() * valueSize * list->length());
1011 sizeBranch->SetBasketSize(1024 + firstBatch->num_rows() * 4);
1013 O2_SIGNPOST_EVENT_EMIT(root_arrow_fs, sid,
"finaliseBasketSize",
"Branch %s needed. There are %lli entries per array of size %d in that list.",
1014 branch->GetName(), listSizes[
i], valueSize);
1015 assert(sizeBranch ==
nullptr);
1016 branch->SetBasketSize(1024 + firstBatch->num_rows() * valueSize * listSizes[
i]);
1019 auto field = firstBatch->schema()->field(
i);
1020 if (field->name().starts_with(
"fIndexArray")) {
1022 int idealBasketSize = 4 * firstBatch->num_rows() + 1024 + field->type()->byte_width() * firstBatch->num_rows();
1023 int basketSize = std::max(32000, idealBasketSize);
1024 sizeBranch->SetBasketSize(basketSize);
1025 branch->SetBasketSize(basketSize);
1035 std::shared_ptr<arrow::io::OutputStream> destination,
1036 arrow::fs::FileLocator destination_locator)
1037 : FileWriter(
schema, options, destination, destination_locator)
1040 auto directoryStream = std::dynamic_pointer_cast<TDirectoryFileOutputStream>(destination_);
1041 auto treeStream = std::dynamic_pointer_cast<TTreeOutputStream>(destination_);
1043 if (directoryStream.get()) {
1044 TDirectoryFile* dir = directoryStream->GetDirectory();
1046 auto*
tree =
new TTree(destination_locator_.path.c_str(),
"");
1047 treeStream = std::make_shared<TTreeOutputStream>(
tree,
"");
1048 }
else if (treeStream.get()) {
1052 auto*
tree = treeStream->GetTree();
1053 treeStream = std::make_shared<TTreeOutputStream>(
tree, destination_locator_.path);
1056 throw std::runtime_error(
"Unsupported backend.");
1060 auto& field =
schema->field(
i);
1061 listSizes.push_back(1);
1063 int valuesIdealBasketSize = 0;
1065 switch (field->type()->id()) {
1066 case arrow::Type::FIXED_SIZE_LIST: {
1067 listSizes.back() = std::static_pointer_cast<arrow::FixedSizeListType>(field->type())->list_size();
1068 valuesIdealBasketSize = 1024 + valueTypes.back()->byte_width() * listSizes.back();
1069 valueTypes.push_back(field->type()->field(0)->type());
1070 sizesBranches.push_back(
nullptr);
1071 std::string leafList = fmt::format(
"{}[{}]{}", field->name(), listSizes.back(),
rootSuffixFromArrow(valueTypes.back()->id()));
1072 branches.push_back(treeStream->CreateBranch(field->name().c_str(), leafList.c_str()));
1074 case arrow::Type::LIST: {
1075 valueTypes.push_back(field->type()->field(0)->type());
1076 std::string leafList = fmt::format(
"{}[{}_size]{}", field->name(), field->name(),
rootSuffixFromArrow(valueTypes.back()->id()));
1077 listSizes.back() = -1;
1078 std::string sizeLeafList = field->name() +
"_size/I";
1079 sizesBranches.push_back(treeStream->CreateBranch((field->name() +
"_size").c_str(), sizeLeafList.c_str()));
1080 branches.push_back(treeStream->CreateBranch(field->name().c_str(), leafList.c_str()));
1085 valueTypes.push_back(field->type());
1087 sizesBranches.push_back(
nullptr);
1088 branches.push_back(treeStream->CreateBranch(field->name().c_str(), leafList.c_str()));
1095 arrow::Status
Write(
const std::shared_ptr<arrow::RecordBatch>& batch)
override
1098 firstBasket =
false;
1099 finaliseBasketSize(batch);
1103 if (batch->columns().empty() || batch->num_rows() == 0) {
1104 return arrow::Status::OK();
1108 auto directoryStream = std::dynamic_pointer_cast<TDirectoryFileOutputStream>(destination_);
1109 TTree*
tree =
nullptr;
1110 if (directoryStream.get()) {
1111 TDirectoryFile* dir = directoryStream->GetDirectory();
1112 tree = (TTree*)dir->Get(destination_locator_.path.c_str());
1114 auto treeStream = std::dynamic_pointer_cast<TTreeOutputStream>(destination_);
1118 throw std::runtime_error(
"Unsupported backend.");
1121 for (
auto i = 0u;
i < batch->columns().
size(); ++
i) {
1122 auto column = batch->column(
i);
1123 auto& field = batch->schema()->field(
i);
1125 valueArrays.push_back(
nullptr);
1127 switch (field->type()->id()) {
1128 case arrow::Type::FIXED_SIZE_LIST: {
1129 auto list = std::static_pointer_cast<arrow::FixedSizeListArray>(column);
1130 if (list->list_type()->field(0)->type()->id() == arrow::Type::BOOL) {
1131 int64_t
length = list->length() * list->list_type()->list_size();
1132 arrow::UInt8Builder builder;
1133 auto ok = builder.Reserve(
length);
1136 auto boolArray = std::static_pointer_cast<arrow::BooleanArray>(list->values());
1138 if (boolArray->IsValid(
i)) {
1140 uint8_t
value = boolArray->Value(
i) ? 1 : 0;
1141 auto ok = builder.Append(
value);
1144 auto ok = builder.AppendNull();
1147 valueArrays.back() = *builder.Finish();
1149 valueArrays.back() = list->values();
1152 case arrow::Type::LIST: {
1153 auto list = std::static_pointer_cast<arrow::ListArray>(column);
1154 valueArrays.back() = list->values();
1156 case arrow::Type::BOOL: {
1159 auto boolArray = std::static_pointer_cast<arrow::BooleanArray>(column);
1161 int64_t
length = boolArray->length();
1162 arrow::UInt8Builder builder;
1163 auto ok = builder.Reserve(
length);
1166 if (boolArray->IsValid(
i)) {
1168 uint8_t
value = boolArray->Value(
i) ? 1 : 0;
1169 auto ok = builder.Append(
value);
1172 auto ok = builder.AppendNull();
1175 valueArrays.back() = *builder.Finish();
1178 valueArrays.back() = column;
1183 while (pos < batch->num_rows()) {
1184 for (
size_t bi = 0; bi < branches.size(); ++bi) {
1185 auto* branch = branches[bi];
1186 auto* sizeBranch = sizesBranches[bi];
1187 auto array = batch->column(bi);
1188 auto& field = batch->schema()->field(bi);
1189 auto& listSize = listSizes[bi];
1190 auto valueType = valueTypes[bi];
1191 auto valueArray = valueArrays[bi];
1193 switch (field->type()->id()) {
1194 case arrow::Type::LIST: {
1195 auto list = std::static_pointer_cast<arrow::ListArray>(
array);
1196 listSize = list->value_length(
pos);
1197 uint8_t
const*
buffer = std::static_pointer_cast<arrow::PrimitiveArray>(valueArray)->values()->data() +
array->offset() + list->value_offset(
pos) * valueType->byte_width();
1198 branch->SetAddress((
void*)
buffer);
1199 sizeBranch->SetAddress(&listSize);
1201 case arrow::Type::FIXED_SIZE_LIST:
1204 auto byteWidth = valueType->byte_width() ? valueType->byte_width() : 1;
1205 uint8_t
const*
buffer = std::static_pointer_cast<arrow::PrimitiveArray>(valueArray)->values()->data() +
array->offset() +
pos * listSize * byteWidth;
1206 branch->SetAddress((
void*)
buffer);
1213 return arrow::Status::OK();
1218 auto treeStream = std::dynamic_pointer_cast<TTreeOutputStream>(destination_);
1219 auto*
tree = treeStream->GetTree();
1220 tree->Write(
"", TObject::kOverwrite);
1221 tree->SetDirectory(
nullptr);
1226arrow::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
1228 auto writer = std::make_shared<TTreeFileWriter>(
schema, options, destination, destination_locator);
1229 return std::dynamic_pointer_cast<arrow::dataset::FileWriter>(writer);
1234 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)
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