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