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 ParsingUtils.h
43  *  @brief Defines helper functions for text parsing
44  */
45 #pragma once
46 #ifndef AI_PARSING_UTILS_H_INC
47 #define AI_PARSING_UTILS_H_INC
48 
49 #ifdef __GNUC__
50 #pragma GCC system_header
51 #endif
52 
53 #include <assimp/StringComparison.h>
54 #include <assimp/StringUtils.h>
55 #include <assimp/defs.h>
56 
57 #include <vector>
58 #include <algorithm>
59 
60 namespace Assimp {
61 
62 // NOTE: the functions below are mostly intended as replacement for
63 // std::upper, std::lower, std::isupper, std::islower, std::isspace.
64 // we don't bother of locales. We don't want them. We want reliable
65 // (i.e. identical) results across all locales.
66 
67 // The functions below accept any character type, but know only
68 // about ASCII. However, UTF-32 is the only safe ASCII superset to
69 // use since it doesn't have multi-byte sequences.
70 
71 static const unsigned int BufferSize = 4096;
72 
73 
74 // ---------------------------------------------------------------------------------
75 template <class char_t>
IsUpper(char_t in)76 AI_FORCE_INLINE bool IsUpper(char_t in) {
77     return (in >= (char_t)'A' && in <= (char_t)'Z');
78 }
79 
80 // ---------------------------------------------------------------------------------
81 template <class char_t>
IsLower(char_t in)82 AI_FORCE_INLINE bool IsLower(char_t in) {
83     return (in >= (char_t)'a' && in <= (char_t)'z');
84 }
85 
86 // ---------------------------------------------------------------------------------
87 template <class char_t>
IsSpace(char_t in)88 AI_FORCE_INLINE bool IsSpace(char_t in) {
89     return (in == (char_t)' ' || in == (char_t)'\t');
90 }
91 
92 // ---------------------------------------------------------------------------------
93 template <class char_t>
IsLineEnd(char_t in)94 AI_FORCE_INLINE bool IsLineEnd(char_t in) {
95     return (in == (char_t)'\r' || in == (char_t)'\n' || in == (char_t)'\0' || in == (char_t)'\f');
96 }
97 
98 // ---------------------------------------------------------------------------------
99 template <class char_t>
IsSpaceOrNewLine(char_t in)100 AI_FORCE_INLINE bool IsSpaceOrNewLine(char_t in) {
101     return IsSpace<char_t>(in) || IsLineEnd<char_t>(in);
102 }
103 
104 // ---------------------------------------------------------------------------------
105 template <class char_t>
SkipSpaces(const char_t * in,const char_t ** out)106 AI_FORCE_INLINE bool SkipSpaces(const char_t *in, const char_t **out) {
107     while (*in == (char_t)' ' || *in == (char_t)'\t') {
108         ++in;
109     }
110     *out = in;
111     return !IsLineEnd<char_t>(*in);
112 }
113 
114 // ---------------------------------------------------------------------------------
115 template <class char_t>
SkipSpaces(const char_t ** inout)116 AI_FORCE_INLINE bool SkipSpaces(const char_t **inout) {
117     return SkipSpaces<char_t>(*inout, inout);
118 }
119 
120 // ---------------------------------------------------------------------------------
121 template <class char_t>
SkipLine(const char_t * in,const char_t ** out)122 AI_FORCE_INLINE bool SkipLine(const char_t *in, const char_t **out) {
123     while (*in != (char_t)'\r' && *in != (char_t)'\n' && *in != (char_t)'\0') {
124         ++in;
125     }
126 
127     // files are opened in binary mode. Ergo there are both NL and CR
128     while (*in == (char_t)'\r' || *in == (char_t)'\n') {
129         ++in;
130     }
131     *out = in;
132     return *in != (char_t)'\0';
133 }
134 
135 // ---------------------------------------------------------------------------------
136 template <class char_t>
SkipLine(const char_t ** inout)137 AI_FORCE_INLINE bool SkipLine(const char_t **inout) {
138     return SkipLine<char_t>(*inout, inout);
139 }
140 
141 // ---------------------------------------------------------------------------------
142 template <class char_t>
SkipSpacesAndLineEnd(const char_t * in,const char_t ** out)143 AI_FORCE_INLINE bool SkipSpacesAndLineEnd(const char_t *in, const char_t **out) {
144     while (*in == (char_t)' ' || *in == (char_t)'\t' || *in == (char_t)'\r' || *in == (char_t)'\n') {
145         ++in;
146     }
147     *out = in;
148     return *in != '\0';
149 }
150 
151 // ---------------------------------------------------------------------------------
152 template <class char_t>
SkipSpacesAndLineEnd(const char_t ** inout)153 AI_FORCE_INLINE bool SkipSpacesAndLineEnd(const char_t **inout) {
154     return SkipSpacesAndLineEnd<char_t>(*inout, inout);
155 }
156 
157 // ---------------------------------------------------------------------------------
158 template <class char_t>
GetNextLine(const char_t * & buffer,char_t out[BufferSize])159 AI_FORCE_INLINE bool GetNextLine(const char_t *&buffer, char_t out[BufferSize]) {
160     if ((char_t)'\0' == *buffer) {
161         return false;
162     }
163 
164     char *_out = out;
165     char *const end = _out + BufferSize;
166     while (!IsLineEnd(*buffer) && _out < end) {
167         *_out++ = *buffer++;
168     }
169     *_out = (char_t)'\0';
170 
171     while (IsLineEnd(*buffer) && '\0' != *buffer) {
172         ++buffer;
173     }
174 
175     return true;
176 }
177 
178 // ---------------------------------------------------------------------------------
179 template <class char_t>
IsNumeric(char_t in)180 AI_FORCE_INLINE bool IsNumeric(char_t in) {
181     return (in >= '0' && in <= '9') || '-' == in || '+' == in;
182 }
183 
184 // ---------------------------------------------------------------------------------
185 template <class char_t>
TokenMatch(char_t * & in,const char * token,unsigned int len)186 AI_FORCE_INLINE bool TokenMatch(char_t *&in, const char *token, unsigned int len) {
187     if (!::strncmp(token, in, len) && IsSpaceOrNewLine(in[len])) {
188         if (in[len] != '\0') {
189             in += len + 1;
190         } else {
191             // If EOF after the token make sure we don't go past end of buffer
192             in += len;
193         }
194         return true;
195     }
196 
197     return false;
198 }
199 // ---------------------------------------------------------------------------------
200 /** @brief Case-ignoring version of TokenMatch
201  *  @param in Input
202  *  @param token Token to check for
203  *  @param len Number of characters to check
204  */
TokenMatchI(const char * & in,const char * token,unsigned int len)205 AI_FORCE_INLINE bool TokenMatchI(const char *&in, const char *token, unsigned int len) {
206     if (!ASSIMP_strincmp(token, in, len) && IsSpaceOrNewLine(in[len])) {
207         in += len + 1;
208         return true;
209     }
210     return false;
211 }
212 
213 // ---------------------------------------------------------------------------------
SkipToken(const char * & in)214 AI_FORCE_INLINE void SkipToken(const char *&in) {
215     SkipSpaces(&in);
216     while (!IsSpaceOrNewLine(*in)) {
217         ++in;
218     }
219 }
220 
221 // ---------------------------------------------------------------------------------
GetNextToken(const char * & in)222 AI_FORCE_INLINE std::string GetNextToken(const char *&in) {
223     SkipSpacesAndLineEnd(&in);
224     const char *cur = in;
225     while (!IsSpaceOrNewLine(*in)) {
226         ++in;
227     }
228     return std::string(cur, (size_t)(in - cur));
229 }
230 
231 // ---------------------------------------------------------------------------------
232 /** @brief  Will perform a simple tokenize.
233  *  @param  str         String to tokenize.
234  *  @param  tokens      Array with tokens, will be empty if no token was found.
235  *  @param  delimiters  Delimiter for tokenize.
236  *  @return Number of found token.
237  */
238 template <class string_type>
tokenize(const string_type & str,std::vector<string_type> & tokens,const string_type & delimiters)239 AI_FORCE_INLINE unsigned int tokenize(const string_type &str, std::vector<string_type> &tokens,
240         const string_type &delimiters) {
241     // Skip delimiters at beginning.
242     typename string_type::size_type lastPos = str.find_first_not_of(delimiters, 0);
243 
244     // Find first "non-delimiter".
245     typename string_type::size_type pos = str.find_first_of(delimiters, lastPos);
246     while (string_type::npos != pos || string_type::npos != lastPos) {
247         // Found a token, add it to the vector.
248         string_type tmp = str.substr(lastPos, pos - lastPos);
249         if (!tmp.empty() && ' ' != tmp[0])
250             tokens.push_back(tmp);
251 
252         // Skip delimiters.  Note the "not_of"
253         lastPos = str.find_first_not_of(delimiters, pos);
254 
255         // Find next "non-delimiter"
256         pos = str.find_first_of(delimiters, lastPos);
257     }
258 
259     return static_cast<unsigned int>(tokens.size());
260 }
261 
ai_stdStrToLower(const std::string & str)262 inline std::string ai_stdStrToLower(const std::string &str) {
263     std::string out(str);
264     for (size_t i = 0; i < str.size(); ++i) {
265         out[i] = (char) tolower((unsigned char)out[i]);
266     }
267     return out;
268 }
269 
270 } // namespace Assimp
271 
272 #endif // ! AI_PARSING_UTILS_H_INC
273