Project
Loading...
Searching...
No Matches
ReadPlan.cxx
Go to the documentation of this file.
1// Copyright 2019-2026 CERN and copyright holders of ALICE O2.
2// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
3// All rights not expressly granted are reserved.
4//
5// This software is distributed under the terms of the GNU General Public
6// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
7//
8// In applying this license CERN does not waive the privileges and immunities
9// granted to it by virtue of its status as an Intergovernmental Organization
10// or submit itself to any jurisdiction.
11
12#include "AODNative/ReadPlan.h"
13#include <algorithm>
14#include <cstring>
15
16namespace o2::aod::native
17{
18
20{
21 std::lock_guard lock{mMutex};
22 auto best = mFree.end();
23 for (auto it = mFree.begin(); it != mFree.end(); ++it) {
24 if (it->capacity >= size && (best == mFree.end() || it->capacity < best->capacity)) {
25 best = it;
26 }
27 }
28 if (best != mFree.end()) {
29 auto buffer = std::move(*best);
30 mFree.erase(best);
31 return buffer;
32 }
33 // All the free buffers are too small: give them back rather than keep them
34 // resident next to the new one. Some headroom, so that the next DF, a bit
35 // larger, still fits.
36 mFree.clear();
37 auto capacity = size + size / 8;
38 return {std::make_unique_for_overwrite<uint8_t[]>(capacity), capacity};
39}
40
42{
43 std::lock_guard lock{mMutex};
44 mFree.push_back(std::move(buffer));
45 if (mFree.size() > kMaxFree) {
46 mFree.erase(std::ranges::min_element(mFree, {}, &Buffer::capacity));
47 }
48}
49
50ReadPlan::ReadPlan(std::shared_ptr<ByteSource> source, Options const& options, std::shared_ptr<ReadBufferPool> pool)
51 : mSource(std::move(source)), mPool(pool ? std::move(pool) : std::make_shared<ReadBufferPool>()), mCoalesceGap(options.coalesceGap)
52{
53}
54
56{
57 for (auto& buffer : mBuffers) {
58 mPool->put(std::move(buffer));
59 }
60}
61
62void ReadPlan::add(uint64_t offset, size_t length)
63{
64 if (length == 0 || offset > mSource->size() || length > mSource->size() - offset) {
65 // Left to the decoder, which reports it with the branch it belongs to.
66 return;
67 }
68 std::lock_guard lock{mMutex};
69 mPending.emplace_back(offset, length);
70}
71
73{
74 for (auto const& branch : tree.branches) {
75 for (auto const& basket : branch.baskets) {
76 if (!basket.embedded) {
77 add(basket.seek, basket.nbytes);
78 }
79 }
80 }
81}
82
83std::span<const uint8_t> ReadPlan::lookup(uint64_t offset, size_t length) const
84{
85 auto it = std::upper_bound(mChunks.begin(), mChunks.end(), offset, [](uint64_t off, Chunk const& c) { return off < c.offset; });
86 if (it == mChunks.begin()) {
87 return {};
88 }
89 --it;
90 if (offset - it->offset + length > it->size) {
91 return {};
92 }
93 return {it->data + (offset - it->offset), length};
94}
95
96void ReadPlan::fetchPending()
97{
98 std::ranges::sort(mPending);
99 std::vector<Chunk> fetched;
100 for (auto [offset, length] : mPending) {
101 if (!lookup(offset, length).empty()) {
102 continue;
103 }
104 auto end = offset + length;
105 if (!fetched.empty()) {
106 auto& last = fetched.back();
107 auto lastEnd = last.offset + last.size;
108 auto next = std::lower_bound(mChunks.begin(), mChunks.end(), lastEnd, [](Chunk const& c, uint64_t off) { return c.offset < off; });
109 bool bridgesFetched = next != mChunks.end() && next->offset < offset;
110 if (offset <= lastEnd + mCoalesceGap && !bridgesFetched) {
111 if (end > lastEnd) {
112 last.size = end - last.offset;
113 }
114 continue;
115 }
116 }
117 fetched.push_back({offset, nullptr, length});
118 }
119 mPending.clear();
120 if (fetched.empty()) {
121 return;
122 }
123 // One buffer for all the chunks of this fetch. Earlier buffers stay where
124 // they are, so spans handed out before remain valid.
125 size_t total = 0;
126 for (auto& chunk : fetched) {
127 total += chunk.size;
128 }
129 auto buffer = mPool->get(total);
130 std::vector<ReadRequest> requests;
131 auto* at = buffer.data.get();
132 for (auto& chunk : fetched) {
133 chunk.data = at;
134 at += chunk.size;
135 requests.push_back({chunk.offset, chunk.size, chunk.data});
136 }
137 mBuffers.push_back(std::move(buffer));
138 mSource->readvInto(requests);
139 mChunks.insert(mChunks.end(), fetched.begin(), fetched.end());
140 std::ranges::sort(mChunks, {}, &Chunk::offset);
141}
142
143std::span<const uint8_t> ReadPlan::find(uint64_t offset, size_t length)
144{
145 std::lock_guard lock{mMutex};
146 auto bytes = lookup(offset, length);
147 if (bytes.empty() && !mPending.empty()) {
148 fetchPending();
149 bytes = lookup(offset, length);
150 }
151 return bytes;
152}
153
155{
156 std::lock_guard lock{mMutex};
157 if (!mPending.empty()) {
158 fetchPending();
159 }
160}
161
163{
164 std::lock_guard lock{mMutex};
165 size_t total = 0;
166 for (auto const& chunk : mChunks) {
167 total += chunk.size;
168 }
169 return total;
170}
171
172PlannedByteSource::PlannedByteSource(std::shared_ptr<ByteSource> source, std::shared_ptr<ReadPlan> plan)
173 : mSource(std::move(source)), mPlan(std::move(plan))
174{
175}
176
177std::span<const uint8_t> PlannedByteSource::tryMap(uint64_t off, size_t len)
178{
179 auto bytes = mSource->tryMap(off, len);
180 return bytes.empty() ? mPlan->find(off, len) : bytes;
181}
182
183void PlannedByteSource::readInto(uint64_t off, size_t len, void* dst)
184{
185 auto bytes = mPlan->find(off, len);
186 if (bytes.empty()) {
187 mSource->readInto(off, len, dst);
188 return;
189 }
190 std::memcpy(dst, bytes.data(), len);
191}
192
193void PlannedByteSource::readvInto(std::span<ReadRequest> requests)
194{
195 mSource->readvInto(requests);
196}
197
198} // namespace o2::aod::native
uint32_t c
Definition RawData.h:2
size_t bytes
void readInto(uint64_t off, size_t len, void *dst) override
Throws IoError, or CorruptData when the range is past the end of the file.
Definition ReadPlan.cxx:183
void readvInto(std::span< ReadRequest > requests) override
Definition ReadPlan.cxx:193
PlannedByteSource(std::shared_ptr< ByteSource > source, std::shared_ptr< ReadPlan > plan)
Definition ReadPlan.cxx:172
std::span< const uint8_t > tryMap(uint64_t off, size_t len) override
A view of the bytes without copying them, if the backend can provide it.
Definition ReadPlan.cxx:177
void put(Buffer buffer)
Definition ReadPlan.cxx:41
Buffer get(size_t size)
The smallest free buffer of at least size bytes, or a new one.
Definition ReadPlan.cxx:19
void fetch()
Fetches the pending ranges now, rather than on the first find().
Definition ReadPlan.cxx:154
std::span< const uint8_t > find(uint64_t offset, size_t length)
Definition ReadPlan.cxx:143
ReadPlan(std::shared_ptr< ByteSource > source, Options const &options, std::shared_ptr< ReadBufferPool > pool=nullptr)
Without a pool, the plan allocates its own buffers.
Definition ReadPlan.cxx:50
void add(uint64_t offset, size_t length)
Definition ReadPlan.cxx:62
size_t bytesHeld() const
Definition ReadPlan.cxx:162
GLuint buffer
Definition glcorearb.h:655
GLsizeiptr size
Definition glcorearb.h:659
GLuint GLuint end
Definition glcorearb.h:469
GLsizei GLsizei GLchar * source
Definition glcorearb.h:798
GLenum GLenum dst
Definition glcorearb.h:1767
GLintptr offset
Definition glcorearb.h:660
GLuint GLsizei GLsizei * length
Definition glcorearb.h:790
GLenum GLenum GLsizei len
Definition glcorearb.h:4232
void empty(int)
std::unique_ptr< TTree > tree((TTree *) flIn.Get(std::string(o2::base::NameConf::CTFTREENAME).c_str()))