Skip to content

Commit 1c50e00

Browse files
committed
Refactoring
1. Remove recursion of operator++(), this leads to constant memory usage rather than filling the stack at some point 2. Extract subroutines from operator++()
1 parent b28fa19 commit 1c50e00

File tree

2 files changed

+118
-61
lines changed

2 files changed

+118
-61
lines changed

include/openPMD/ReadIterations.hpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,12 @@ class SeriesIterator
8989
m_currentIteration = *m_iterationsInCurrentStep.begin();
9090
return true;
9191
}
92+
93+
std::optional<SeriesIterator *> nextIterationInStep(Iteration &);
94+
95+
std::optional<SeriesIterator *> nextStep(Iteration &);
96+
97+
std::optional<SeriesIterator *> loopBody();
9298
};
9399

94100
/**

src/ReadIterations.cpp

Lines changed: 112 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -136,69 +136,57 @@ SeriesIterator::SeriesIterator(Series series) : m_series(std::move(series))
136136
}
137137
}
138138

139-
SeriesIterator &SeriesIterator::operator++()
139+
std::optional<SeriesIterator *>
140+
SeriesIterator::nextIterationInStep(Iteration &currentIteration)
140141
{
141-
if (!m_series.has_value())
142+
using ret_t = std::optional<SeriesIterator *>;
143+
144+
m_iterationsInCurrentStep.pop_front();
145+
if (m_iterationsInCurrentStep.empty())
142146
{
143-
*this = end();
144-
return *this;
147+
return ret_t{};
145148
}
146-
Series &series = m_series.value();
147-
auto &iterations = series.iterations;
148-
auto &currentIteration = iterations[m_currentIteration];
149149
auto oldIterationIndex = m_currentIteration;
150+
m_currentIteration = *m_iterationsInCurrentStep.begin();
151+
auto &series = m_series.value();
150152

151-
m_iterationsInCurrentStep.pop_front();
152-
if (!m_iterationsInCurrentStep.empty())
153+
switch (series.iterationEncoding())
153154
{
154-
m_currentIteration = *m_iterationsInCurrentStep.begin();
155-
switch (series.iterationEncoding())
155+
case IterationEncoding::groupBased:
156+
case IterationEncoding::variableBased: {
157+
if (!currentIteration.closed())
156158
{
157-
case IterationEncoding::groupBased:
158-
case IterationEncoding::variableBased: {
159-
if (!currentIteration.closed())
160-
{
161-
currentIteration.get().m_closed =
162-
internal::CloseStatus::ClosedInFrontend;
163-
}
164-
auto begin = series.iterations.find(oldIterationIndex);
165-
auto end = begin;
166-
++end;
167-
series.flush_impl(
168-
begin,
169-
end,
170-
FlushLevel::UserFlush,
171-
/* flushIOHandler = */ true);
172-
173-
series.iterations[m_currentIteration].open();
174-
return *this;
175-
}
176-
case IterationEncoding::fileBased:
177-
if (!currentIteration.closed())
178-
{
179-
currentIteration.close();
180-
}
181-
series.iterations[m_currentIteration].open();
182-
series.iterations[m_currentIteration].beginStep();
183-
return *this;
159+
currentIteration.get().m_closed =
160+
internal::CloseStatus::ClosedInFrontend;
184161
}
185-
throw std::runtime_error("Unreachable!");
186-
}
162+
auto begin = series.iterations.find(oldIterationIndex);
163+
auto end = begin;
164+
++end;
165+
series.flush_impl(
166+
begin,
167+
end,
168+
FlushLevel::UserFlush,
169+
/* flushIOHandler = */ true);
187170

188-
// The currently active iterations have been exhausted.
189-
// Now see if there are further iterations to be found.
190-
191-
if (series.iterationEncoding() == IterationEncoding::fileBased)
192-
{
193-
// this one is handled above, stream is over once it proceeds to here
194-
*this = end();
195-
return *this;
171+
series.iterations[m_currentIteration].open();
172+
return {this};
196173
}
197-
198-
if (!currentIteration.closed())
199-
{
200-
currentIteration.close();
174+
case IterationEncoding::fileBased:
175+
if (!currentIteration.closed())
176+
{
177+
currentIteration.close();
178+
}
179+
series.iterations[m_currentIteration].open();
180+
series.iterations[m_currentIteration].beginStep();
181+
return {this};
201182
}
183+
throw std::runtime_error("Unreachable!");
184+
}
185+
186+
std::optional<SeriesIterator *>
187+
SeriesIterator::nextStep(Iteration &currentIteration)
188+
{
189+
using ret_t = std::optional<SeriesIterator *>;
202190

203191
// since we are in group-based iteration layout, it does not
204192
// matter which iteration we begin a step upon
@@ -217,18 +205,19 @@ SeriesIterator &SeriesIterator::operator++()
217205
* Fallback implementation: Assume that each step corresponds
218206
* with an iteration in ascending order.
219207
*/
220-
auto it = iterations.find(m_currentIteration);
221-
auto itEnd = iterations.end();
208+
auto &series = m_series.value();
209+
auto it = series.iterations.find(m_currentIteration);
210+
auto itEnd = series.iterations.end();
222211
if (it == itEnd)
223212
{
224213
*this = end();
225-
return *this;
214+
return {this};
226215
}
227216
++it;
228217
if (it == itEnd)
229218
{
230219
*this = end();
231-
return *this;
220+
return {this};
232221
}
233222
m_iterationsInCurrentStep = {it->first};
234223
}
@@ -237,8 +226,51 @@ SeriesIterator &SeriesIterator::operator++()
237226
if (status == AdvanceStatus::OVER)
238227
{
239228
*this = end();
240-
return *this;
229+
return {this};
230+
}
231+
232+
return ret_t{};
233+
}
234+
235+
std::optional<SeriesIterator *> SeriesIterator::loopBody()
236+
{
237+
using ret_t = std::optional<SeriesIterator *>;
238+
239+
Series &series = m_series.value();
240+
auto &iterations = series.iterations;
241+
auto &currentIteration = iterations[m_currentIteration];
242+
243+
{
244+
auto option = nextIterationInStep(currentIteration);
245+
if (option.has_value())
246+
{
247+
return option;
248+
}
241249
}
250+
251+
// The currently active iterations have been exhausted.
252+
// Now see if there are further iterations to be found.
253+
254+
if (series.iterationEncoding() == IterationEncoding::fileBased)
255+
{
256+
// this one is handled above, stream is over once it proceeds to here
257+
*this = end();
258+
return {this};
259+
}
260+
261+
if (!currentIteration.closed())
262+
{
263+
currentIteration.close();
264+
}
265+
266+
{
267+
auto option = nextStep(currentIteration);
268+
if (option.has_value())
269+
{
270+
return option;
271+
}
272+
}
273+
242274
currentIteration.setStepStatus(StepStatus::DuringStep);
243275

244276
auto iteration = iterations.at(m_currentIteration);
@@ -248,12 +280,31 @@ SeriesIterator &SeriesIterator::operator++()
248280
}
249281
else
250282
{
251-
// we had this one already, skip it
252-
// @todo remove recursive call
283+
// we had this iteration already, skip it
253284
iteration.endStep();
254-
return operator++();
285+
return ret_t{}; // empty, go into next iteration
286+
}
287+
return {this};
288+
}
289+
290+
SeriesIterator &SeriesIterator::operator++()
291+
{
292+
if (!m_series.has_value())
293+
{
294+
*this = end();
295+
return *this;
255296
}
256-
return *this;
297+
std::optional<SeriesIterator *> res;
298+
/*
299+
* loopBody() might return an empty option to indicate a skipped iteration.
300+
* Loop until it returns something real for us.
301+
*/
302+
do
303+
{
304+
res = loopBody();
305+
} while (!res.has_value());
306+
307+
return *res.value();
257308
}
258309

259310
IndexedIteration SeriesIterator::operator*()

0 commit comments

Comments
 (0)