|
34 | 34 |
|
35 | 35 | #include "PyArray.hpp" |
36 | 36 | #include <pdal/io/MemoryViewReader.hpp> |
37 | | -#include <numpy/arrayobject.h> |
38 | 37 |
|
39 | 38 | namespace pdal |
40 | 39 | { |
@@ -95,7 +94,8 @@ std::string pyObjectToString(PyObject *pname) |
95 | 94 | #define PyDataType_NAMES(descr) ((descr)->names) |
96 | 95 | #endif |
97 | 96 |
|
98 | | -Array::Array(PyArrayObject* array) : m_array(array), m_rowMajor(true) |
| 97 | +Array::Array(PyArrayObject* array, std::shared_ptr<ArrayStreamHandler> stream_handler) |
| 98 | + : m_array(array), m_rowMajor(true), m_stream_handler(std::move(stream_handler)) |
99 | 99 | { |
100 | 100 | Py_XINCREF(array); |
101 | 101 |
|
@@ -164,51 +164,93 @@ Array::~Array() |
164 | 164 | Py_XDECREF(m_array); |
165 | 165 | } |
166 | 166 |
|
167 | | - |
168 | | -ArrayIter& Array::iterator() |
| 167 | +std::shared_ptr<ArrayIter> Array::iterator() |
169 | 168 | { |
170 | | - ArrayIter *it = new ArrayIter(m_array); |
171 | | - m_iterators.emplace_back((it)); |
172 | | - return *it; |
| 169 | + return std::make_shared<ArrayIter>(m_array, m_stream_handler); |
173 | 170 | } |
174 | 171 |
|
175 | | - |
176 | | -ArrayIter::ArrayIter(PyArrayObject* np_array) |
| 172 | +ArrayIter::ArrayIter(PyArrayObject* np_array, std::shared_ptr<ArrayStreamHandler> stream_handler) |
| 173 | + : m_stream_handler(std::move(stream_handler)) |
177 | 174 | { |
| 175 | + // Create iterator |
178 | 176 | m_iter = NpyIter_New(np_array, |
179 | | - NPY_ITER_EXTERNAL_LOOP | NPY_ITER_READONLY | NPY_ITER_REFS_OK, |
180 | | - NPY_KEEPORDER, NPY_NO_CASTING, NULL); |
| 177 | + NPY_ITER_EXTERNAL_LOOP | NPY_ITER_READONLY | NPY_ITER_REFS_OK, |
| 178 | + NPY_KEEPORDER, NPY_NO_CASTING, NULL); |
181 | 179 | if (!m_iter) |
182 | 180 | throw pdal_error("Unable to create numpy iterator."); |
183 | 181 |
|
| 182 | + initIterator(); |
| 183 | +} |
| 184 | + |
| 185 | +void ArrayIter::initIterator() |
| 186 | +{ |
| 187 | + // For a stream handler, first execute it to get the buffer populated and know the size of the data to iterate |
| 188 | + int64_t stream_chunk_size = 0; |
| 189 | + if (m_stream_handler) { |
| 190 | + stream_chunk_size = (*m_stream_handler)(); |
| 191 | + if (!stream_chunk_size) { |
| 192 | + m_done = true; |
| 193 | + return; |
| 194 | + } |
| 195 | + } |
| 196 | + |
| 197 | + // Initialize the iterator function |
184 | 198 | char *itererr; |
185 | 199 | m_iterNext = NpyIter_GetIterNext(m_iter, &itererr); |
186 | 200 | if (!m_iterNext) |
187 | 201 | { |
188 | 202 | NpyIter_Deallocate(m_iter); |
189 | | - throw pdal_error(std::string("Unable to create numpy iterator: ") + |
190 | | - itererr); |
| 203 | + m_iter = nullptr; |
| 204 | + throw pdal_error(std::string("Unable to retrieve iteration function from numpy iterator: ") + itererr); |
191 | 205 | } |
192 | 206 | m_data = NpyIter_GetDataPtrArray(m_iter); |
193 | | - m_stride = NpyIter_GetInnerStrideArray(m_iter); |
194 | | - m_size = NpyIter_GetInnerLoopSizePtr(m_iter); |
| 207 | + m_stride = *NpyIter_GetInnerStrideArray(m_iter); |
| 208 | + m_size = *NpyIter_GetInnerLoopSizePtr(m_iter); |
| 209 | + if (stream_chunk_size) { |
| 210 | + // Ensure chunk size is valid and then limit iteration accordingly |
| 211 | + if (0 < stream_chunk_size && stream_chunk_size <= m_size) { |
| 212 | + m_size = stream_chunk_size; |
| 213 | + } else { |
| 214 | + throw pdal_error(std::string("Stream chunk size not in the range of array length: ") + |
| 215 | + std::to_string(stream_chunk_size)); |
| 216 | + } |
| 217 | + } |
195 | 218 | m_done = false; |
196 | 219 | } |
197 | 220 |
|
| 221 | +void ArrayIter::resetIterator() |
| 222 | +{ |
| 223 | + // Reset the iterator to the initial state |
| 224 | + if (NpyIter_Reset(m_iter, NULL) != NPY_SUCCEED) { |
| 225 | + NpyIter_Deallocate(m_iter); |
| 226 | + m_iter = nullptr; |
| 227 | + throw pdal_error("Unable to reset numpy iterator."); |
| 228 | + } |
| 229 | + |
| 230 | + initIterator(); |
| 231 | +} |
| 232 | + |
198 | 233 | ArrayIter::~ArrayIter() |
199 | 234 | { |
200 | | - NpyIter_Deallocate(m_iter); |
| 235 | + if (m_iter != nullptr) { |
| 236 | + NpyIter_Deallocate(m_iter); |
| 237 | + } |
201 | 238 | } |
202 | 239 |
|
203 | 240 | ArrayIter& ArrayIter::operator++() |
204 | 241 | { |
205 | 242 | if (m_done) |
206 | 243 | return *this; |
207 | 244 |
|
208 | | - if (--(*m_size)) |
209 | | - *m_data += *m_stride; |
210 | | - else if (!m_iterNext(m_iter)) |
211 | | - m_done = true; |
| 245 | + if (--m_size) { |
| 246 | + *m_data += m_stride; |
| 247 | + } else if (!m_iterNext(m_iter)) { |
| 248 | + if (m_stream_handler) { |
| 249 | + resetIterator(); |
| 250 | + } else { |
| 251 | + m_done = true; |
| 252 | + } |
| 253 | + } |
212 | 254 | return *this; |
213 | 255 | } |
214 | 256 |
|
|
0 commit comments