1 /*
2 Open Asset Import Library (assimp)
3 ----------------------------------------------------------------------
4 
5 Copyright (c) 2006-2021, assimp team
6 
7 All rights reserved.
8 
9 Redistribution and use of this software in source and binary forms,
10 with or without modification, are permitted provided that the
11 following conditions are met:
12 
13 * Redistributions of source code must retain the above
14   copyright notice, this list of conditions and the
15   following disclaimer.
16 
17 * Redistributions in binary form must reproduce the above
18   copyright notice, this list of conditions and the
19   following disclaimer in the documentation and/or other
20   materials provided with the distribution.
21 
22 * Neither the name of the assimp team, nor the names of its
23   contributors may be used to endorse or promote products
24   derived from this software without specific prior
25   written permission of the assimp team.
26 
27 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
28 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
29 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
30 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
31 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
32 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
33 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
34 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
35 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
36 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
37 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38 
39 ----------------------------------------------------------------------
40 */
41 
42 /** @file  LineSplitter.h
43  *  @brief LineSplitter, a helper class to iterate through all lines
44  *    of a file easily. Works with StreamReader.
45  */
46 #pragma once
47 #ifndef INCLUDED_LINE_SPLITTER_H
48 #define INCLUDED_LINE_SPLITTER_H
49 
50 #ifdef __GNUC__
51 #   pragma GCC system_header
52 #endif
53 
54 #include <stdexcept>
55 #include <assimp/StreamReader.h>
56 #include <assimp/ParsingUtils.h>
57 
58 namespace Assimp {
59 
60 // ------------------------------------------------------------------------------------------------
61 /** Usage:
62 @code
63 for(LineSplitter splitter(stream);splitter;++splitter) {
64 
65     if (*splitter == "hi!") {
66        ...
67     }
68     else if (splitter->substr(0,5) == "hello") {
69        ...
70        // access the third token in the line (tokens are space-separated)
71        if (strtol(splitter[2]) > 5) { .. }
72     }
73 
74     ASSIMP_LOG_VERBOSE_DEBUG("Current line is: ", splitter.get_index());
75 }
76 @endcode
77 */
78 // ------------------------------------------------------------------------------------------------
79 class LineSplitter {
80 public:
81     typedef size_t line_idx;
82 
83     // -----------------------------------------
84     /** construct from existing stream reader
85     note: trim is *always* assumed true if skyp_empty_lines==true
86     */
87     LineSplitter(StreamReaderLE& stream, bool skip_empty_lines = true, bool trim = true);
88 
89     ~LineSplitter();
90 
91     // -----------------------------------------
92     /** pseudo-iterator increment */
93     LineSplitter& operator++();
94 
95     // -----------------------------------------
96     LineSplitter& operator++(int);
97 
98     // -----------------------------------------
99     /** get a pointer to the beginning of a particular token */
100     const char* operator[] (size_t idx) const;
101 
102     // -----------------------------------------
103     /** extract the start positions of N tokens from the current line*/
104     template <size_t N>
105     void get_tokens(const char* (&tokens)[N]) const;
106 
107     // -----------------------------------------
108     /** member access */
109     const std::string* operator -> () const;
110 
111     std::string operator* () const;
112 
113     // -----------------------------------------
114     /** boolean context */
115     operator bool() const;
116 
117     // -----------------------------------------
118     /** line indices are zero-based, empty lines are included */
119     operator line_idx() const;
120 
121     line_idx get_index() const;
122 
123     // -----------------------------------------
124     /** access the underlying stream object */
125     StreamReaderLE& get_stream();
126 
127     // -----------------------------------------
128     /** !strcmp((*this)->substr(0,strlen(check)),check) */
129     bool match_start(const char* check);
130 
131     // -----------------------------------------
132     /** swallow the next call to ++, return the previous value. */
133     void swallow_next_increment();
134 
135     LineSplitter( const LineSplitter & ) = delete;
136     LineSplitter(LineSplitter &&) = delete;
137     LineSplitter &operator = ( const LineSplitter & ) = delete;
138 
139 private:
140     line_idx mIdx;
141     std::string mCur;
142     StreamReaderLE& mStream;
143     bool mSwallow, mSkip_empty_lines, mTrim;
144 };
145 
LineSplitter(StreamReaderLE & stream,bool skip_empty_lines,bool trim)146 AI_FORCE_INLINE LineSplitter::LineSplitter(StreamReaderLE& stream, bool skip_empty_lines, bool trim ) :
147         mIdx(0),
148         mCur(),
149         mStream(stream),
150         mSwallow(),
151         mSkip_empty_lines(skip_empty_lines),
152         mTrim(trim) {
153     mCur.reserve(1024);
154     operator++();
155     mIdx = 0;
156 }
157 
~LineSplitter()158 AI_FORCE_INLINE LineSplitter::~LineSplitter() {
159     // empty
160 }
161 
162 AI_FORCE_INLINE LineSplitter& LineSplitter::operator++() {
163     if (mSwallow) {
164         mSwallow = false;
165         return *this;
166     }
167 
168     if (!*this) {
169         throw std::logic_error("End of file, no more lines to be retrieved.");
170     }
171 
172     char s;
173     mCur.clear();
174     while (mStream.GetRemainingSize() && (s = mStream.GetI1(), 1)) {
175         if (s == '\n' || s == '\r') {
176             if (mSkip_empty_lines) {
177                 while (mStream.GetRemainingSize() && ((s = mStream.GetI1()) == ' ' || s == '\r' || s == '\n'));
178                 if (mStream.GetRemainingSize()) {
179                     mStream.IncPtr(-1);
180                 }
181             } else {
182                 // skip both potential line terminators but don't read past this line.
183                 if (mStream.GetRemainingSize() && (s == '\r' && mStream.GetI1() != '\n')) {
184                     mStream.IncPtr(-1);
185                 }
186                 if (mTrim) {
187                     while (mStream.GetRemainingSize() && ((s = mStream.GetI1()) == ' ' || s == '\t'));
188                     if (mStream.GetRemainingSize()) {
189                         mStream.IncPtr(-1);
190                     }
191                 }
192             }
193             break;
194         }
195         mCur += s;
196     }
197     ++mIdx;
198 
199     return *this;
200 }
201 
202 AI_FORCE_INLINE LineSplitter &LineSplitter::operator++(int) {
203     return ++(*this);
204 }
205 
206 AI_FORCE_INLINE const char *LineSplitter::operator[] (size_t idx) const {
207     const char* s = operator->()->c_str();
208 
209     SkipSpaces(&s);
210     for (size_t i = 0; i < idx; ++i) {
211         for (; !IsSpace(*s); ++s) {
212             if (IsLineEnd(*s)) {
213                 throw std::range_error("Token index out of range, EOL reached");
214             }
215         }
216         SkipSpaces(&s);
217     }
218     return s;
219 }
220 
221 template <size_t N>
get_tokens(const char * (& tokens)[N])222 AI_FORCE_INLINE void LineSplitter::get_tokens(const char* (&tokens)[N]) const {
223     const char* s = operator->()->c_str();
224 
225     SkipSpaces(&s);
226     for (size_t i = 0; i < N; ++i) {
227         if (IsLineEnd(*s)) {
228             throw std::range_error("Token count out of range, EOL reached");
229         }
230         tokens[i] = s;
231 
232         for (; *s && !IsSpace(*s); ++s);
233         SkipSpaces(&s);
234     }
235 }
236 
237 AI_FORCE_INLINE const std::string* LineSplitter::operator -> () const {
238     return &mCur;
239 }
240 
241 AI_FORCE_INLINE std::string LineSplitter::operator* () const {
242     return mCur;
243 }
244 
245 AI_FORCE_INLINE LineSplitter::operator bool() const {
246     return mStream.GetRemainingSize() > 0;
247 }
248 
line_idx()249 AI_FORCE_INLINE LineSplitter::operator line_idx() const {
250     return mIdx;
251 }
252 
get_index()253 AI_FORCE_INLINE LineSplitter::line_idx LineSplitter::get_index() const {
254     return mIdx;
255 }
256 
get_stream()257 AI_FORCE_INLINE StreamReaderLE &LineSplitter::get_stream() {
258     return mStream;
259 }
260 
match_start(const char * check)261 AI_FORCE_INLINE bool LineSplitter::match_start(const char* check) {
262     const size_t len = ::strlen(check);
263 
264     return len <= mCur.length() && std::equal(check, check + len, mCur.begin());
265 }
266 
swallow_next_increment()267 AI_FORCE_INLINE void LineSplitter::swallow_next_increment() {
268     mSwallow = true;
269 }
270 
271 } // Namespace Assimp
272 
273 #endif // INCLUDED_LINE_SPLITTER_H
274