1 /******************************************************************************
2  * $Id$
3  *
4  * Project:  libLAS - http://liblas.org - A BSD library for LAS format data.
5  * Purpose:  LAS 1.0 reader implementation for C++ libLAS
6  * Author:   Mateusz Loskot, mateusz@loskot.net
7  *
8  ******************************************************************************
9  * Copyright (c) 2008, Mateusz Loskot
10  *
11  * All rights reserved.
12  *
13  * Redistribution and use in source and binary forms, with or without
14  * modification, are permitted provided that the following
15  * conditions are met:
16  *
17  *     * Redistributions of source code must retain the above copyright
18  *       notice, this list of conditions and the following disclaimer.
19  *     * Redistributions in binary form must reproduce the above copyright
20  *       notice, this list of conditions and the following disclaimer in
21  *       the documentation and/or other materials provided
22  *       with the distribution.
23  *     * Neither the name of the Martin Isenburg or Iowa Department
24  *       of Natural Resources nor the names of its contributors may be
25  *       used to endorse or promote products derived from this software
26  *       without specific prior written permission.
27  *
28  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
29  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
30  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
31  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
32  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
33  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
34  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
35  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
36  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
37  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
38  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
39  * OF SUCH DAMAGE.
40  ****************************************************************************/
41 
42 #include <liblas/liblas.hpp>
43 #include <liblas/detail/reader/reader.hpp>
44 #include <liblas/detail/reader/cachedreader.hpp>
45 #include <liblas/detail/private_utility.hpp>
46 // boost
47 #include <boost/cstdint.hpp>
48 // std
49 #include <fstream>
50 #include <istream>
51 #include <iostream>
52 #include <stdexcept>
53 #include <cstddef> // std::size_t
54 #include <cstdlib> // std::free
55 #include <cassert>
56 
57 using namespace boost;
58 
59 namespace liblas { namespace detail {
60 
61 
CachedReaderImpl(std::istream & ifs,std::size_t size)62 CachedReaderImpl::CachedReaderImpl(std::istream& ifs , std::size_t size)
63     : ReaderImpl(ifs)
64     , m_cache_size(size)
65     , m_cache_start_position(0)
66     , m_cache_read_position(0)
67     , m_cache_initialized(false)
68 {
69 }
70 
ReadHeader()71 void CachedReaderImpl::ReadHeader()
72 {
73     ReaderImpl::ReadHeader();
74     HeaderPtr hptr(new liblas::Header(ReaderImpl::GetHeader()));
75 
76     // If we were given no cache size, try to cache the whole thing
77     if (m_cache_size == 0) {
78         m_cache_size = hptr->GetPointRecordsCount();
79     }
80 
81     if (m_cache_size > hptr->GetPointRecordsCount()) {
82         m_cache_size = hptr->GetPointRecordsCount();
83     }
84     // // FIXME: Note, vector::resize never shrinks the container and frees memory! Are we aware of this fact here? --mloskot
85     // m_cache.resize(m_cache_size);
86     //
87     // // Mark all positions as uncached and build up the mask
88     // // to the size of the number of points in the file
89     // uint8_t const uncached_mask = 0;
90     // cache_mask_type(hptr->GetPointRecordsCount(), uncached_mask).swap(m_mask);
91 
92     m_header = hptr;
93 }
94 
CacheData(uint32_t position)95 void CachedReaderImpl::CacheData(uint32_t position)
96 {
97     cache_mask_type::size_type old_cache_start_position = m_cache_start_position;
98     m_cache_start_position = position;
99 
100     cache_mask_type::size_type header_size = static_cast<cache_mask_type::size_type>(m_header->GetPointRecordsCount());
101     cache_mask_type::size_type left_to_cache = (std::min)(m_cache_size, header_size - m_cache_start_position);
102 
103     cache_mask_type::size_type to_mark = (std::min)(m_cache_size, header_size - old_cache_start_position);
104 
105     for (uint32_t i = 0; i < to_mark; ++i)
106     {
107         m_mask[old_cache_start_position + i] = 0;
108     }
109 
110     // if these aren't equal, we've hopped around with ReadPointAt
111     // and we need to seek to the proper position.
112     if (m_current != position) {
113         CachedReaderImpl::Seek(position);
114         m_current = position;
115     }
116     m_cache_read_position =  position;
117 
118     for (uint32_t i = 0; i < left_to_cache; ++i)
119     {
120         try {
121             m_mask[m_current] = 1;
122             ReadNextUncachedPoint();
123             m_cache[i] = new liblas::Point(ReaderImpl::GetPoint());
124         } catch (std::out_of_range&) {
125             // cached to the end
126             break;
127         }
128     }
129 }
130 
ReadNextUncachedPoint()131 void CachedReaderImpl::ReadNextUncachedPoint()
132 {
133     if (0 == m_current)
134     {
135         m_ifs.clear();
136         m_ifs.seekg(m_header->GetDataOffset(), std::ios::beg);
137     }
138 
139     if (m_current >= m_size ){
140         throw std::out_of_range("ReadNextPoint: file has no more points to read, end of file reached");
141     }
142 
143     // m_point_reader->read();
144     detail::read_n(m_point->GetData().front(), m_ifs, m_record_size);
145     ++m_current;
146     // *m_point = m_point_reader->GetPoint();
147 
148 
149     if (!m_transforms.empty())
150     {
151         ReaderImpl::TransformPoint(*m_point);
152     }
153 
154 }
155 
ReadCachedPoint(uint32_t position)156 void CachedReaderImpl::ReadCachedPoint(uint32_t position) {
157 
158     int32_t cache_position = position - m_cache_start_position ;
159 
160     // std::cout << "MASK: ";
161     // std::vector<bool>::iterator it;
162     // for (it = m_mask.begin(); it != m_mask.end(); ++it) {
163     //     std::cout << *it << ",";
164     // }
165     // std::cout << std::endl;
166 
167     // If our point cache and mask have not yet been initialized, we
168     // should do so before tyring to read any points.  We don't want to do
169     // this in ::Reset or ::ReadHeader, as these functions may be called
170     // multiple times and screw up our assumptions.
171     if (!m_cache_initialized)
172     {
173         m_cache = cache_type(m_cache_size);
174 
175         // Mark all positions as uncached and build up the mask
176         // to the size of the number of points in the file
177         uint8_t const uncached_mask = 0;
178         cache_mask_type(m_header->GetPointRecordsCount(), uncached_mask).swap(m_mask);
179 
180         m_cache_initialized = true;
181     }
182     if (m_mask[position] == 1) {
183         m_cache_read_position = position;
184         *m_point =  *m_cache[cache_position];
185         return;
186     } else {
187 
188         CacheData(position);
189 
190         // At this point, we can't have a negative cache position.
191         // If we do, it's a big error or we'll segfault.
192         cache_position = position - m_cache_start_position ;
193         if (cache_position < 0) {
194             std::ostringstream msg;
195             msg  << "ReadCachedPoint:: cache position: "
196                  << cache_position
197                  << " is negative. position or m_cache_start_position is invalid "
198                  << "position: " << position << " m_cache_start_position: "
199                  << m_cache_start_position;
200             throw std::runtime_error(msg.str());
201         }
202 
203         if (m_mask[position] == 1) {
204             if (static_cast<uint32_t>(cache_position) > m_cache.size()) {
205                 std::ostringstream msg;
206                 msg << "ReadCachedPoint:: cache position: "
207                     << position
208                     << " greater than cache size: " << m_cache.size() ;
209                 throw std::runtime_error(msg.str());
210             }
211             *m_point = *m_cache[cache_position];
212             return;
213         } else {
214             std::ostringstream msg;
215             msg << "ReadCachedPoint:: unable to obtain cached point"
216                 << " at position: " << position
217                 << " cache_position was " << cache_position ;
218             std::string out(msg.str());
219 
220             throw std::runtime_error(out);
221         }
222     }
223 }
224 
ReadNextPoint()225 void CachedReaderImpl::ReadNextPoint()
226 {
227     if (m_cache_read_position == m_size )
228     {
229         throw std::out_of_range("file has no more points to read, end of file reached");
230     }
231 
232     // PointPtr ptr = ReadCachedPoint(m_cache_read_position, header);
233     ReadCachedPoint(m_cache_read_position);
234     ++m_cache_read_position;
235 
236     // Filter the points and continue reading until we either find
237     // one to keep or throw an exception.
238 
239     bool bLastPoint = false;
240     if (!FilterPoint(*m_point))
241     {
242         ReadCachedPoint(m_cache_read_position);
243         ++m_cache_read_position;
244 
245         while (!FilterPoint(*m_point))
246         {
247             ReadCachedPoint(m_cache_read_position);
248             ++m_cache_read_position;
249             if (m_current == m_size)
250             {
251                 bLastPoint = true;
252                 break;
253             }
254         }
255     }
256 
257     if (bLastPoint)
258         throw std::out_of_range("ReadNextPoint: file has no more points to read, end of file reached");
259 
260 }
261 
ReadPointAt(std::size_t n)262 liblas::Point const& CachedReaderImpl::ReadPointAt(std::size_t n)
263 {
264 
265     if (n >= m_size ){
266         throw std::out_of_range("file has no more points to read, end of file reached");
267 
268     } else if (m_size < n) {
269         std::ostringstream output;
270         output << "ReadPointAt:: Inputted value: "
271                << n << " is greater than the number of points: "
272                << m_size;
273         std::string out(output.str());
274         throw std::runtime_error(out);
275     }
276 
277     ReadCachedPoint(n);
278     m_cache_read_position = n;
279     return *m_point;
280 }
281 
Reset()282 void CachedReaderImpl::Reset()
283 {
284     if (m_mask.empty())
285     {
286         ReaderImpl::Reset();
287         return;
288     }
289 
290     typedef cache_mask_type::size_type size_type;
291     size_type old_cache_start_position = m_cache_start_position;
292     size_type header_size = static_cast<size_type>(m_header->GetPointRecordsCount());
293     size_type to_mark = (std::min)(m_cache_size, header_size - old_cache_start_position);
294 
295     for (uint32_t i = 0; i < to_mark; ++i) {
296 
297         size_type const mark_pos = m_cache_start_position + i;
298         assert(mark_pos < m_mask.size());
299 
300         m_mask[mark_pos] = 0;
301     }
302 
303     m_cache_start_position = 0;
304     m_cache_read_position = 0;
305     m_cache_initialized = false;
306 
307     ReaderImpl::Reset();
308 }
309 
Seek(std::size_t n)310 void CachedReaderImpl::Seek(std::size_t n)
311 {
312 
313    if (n < 1) {
314        CachedReaderImpl::Reset();
315    }
316 
317    m_cache_read_position = n;
318    ReaderImpl::Seek(n);
319 }
320 
SetFilters(std::vector<liblas::FilterPtr> const & filters)321 void CachedReaderImpl::SetFilters(std::vector<liblas::FilterPtr> const& filters)
322 {
323     Reset();
324     ReaderImpl::SetFilters(filters);
325 }
GetFilters() const326 std::vector<liblas::FilterPtr>  CachedReaderImpl::GetFilters() const
327 {
328     return ReaderImpl::GetFilters();
329 }
330 
SetTransforms(std::vector<liblas::TransformPtr> const & transforms)331 void CachedReaderImpl::SetTransforms(std::vector<liblas::TransformPtr> const& transforms)
332 {
333     // Setting transforms means the cache is invalid
334     Reset();
335     ReaderImpl::SetTransforms(transforms);
336 
337 }
338 
GetTransforms() const339 std::vector<liblas::TransformPtr>  CachedReaderImpl::GetTransforms() const
340 {
341     return ReaderImpl::GetTransforms();
342 }
343 }} // namespace liblas::detail
344 
345