STLAsciiParseManual.C
Go to the documentation of this file.
1/*--------------------------------*- C++ -*----------------------------------*\
2 ========= |
3 \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
4 \\ / O peration |
5 \\ / A nd | www.openfoam.com
6 \\/ M anipulation |
7-------------------------------------------------------------------------------
8 Copyright (C) 2018 OpenCFD Ltd.
9-------------------------------------------------------------------------------
10License
11 This file is part of OpenFOAM.
12
13 OpenFOAM is free software: you can redistribute it and/or modify it
14 under the terms of the GNU General Public License as published by
15 the Free Software Foundation, either version 3 of the License, or
16 (at your option) any later version.
17
18 OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
19 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
20 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
21 for more details.
22
23 You should have received a copy of the GNU General Public License
24 along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
25
26Description
27 Hand-written parsing of STL ASCII format
28
29\*---------------------------------------------------------------------------*/
30
31#include "STLAsciiParse.H"
32#include "STLReader.H"
33#include "OSspecific.H"
34#include "stringOps.H"
35
36// * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * //
37
38static inline std::string perrorEOF(std::string expected)
39{
40 return "Premature EOF while reading '" + expected + "'";
41}
42
43
44static inline std::string perrorParse(std::string expected, std::string found)
45{
46 return "Parse error. Expecting '" + expected + "' found '" + found + "'";
47}
48
49
50/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
51
52namespace Foam
53{
54namespace Detail
55{
56
57//- A lexer for parsing STL ASCII files.
58// Returns DynamicList(s) of points and facets (zoneIds).
59// The facets are within a solid/endsolid grouping
61:
63{
64 enum scanState
65 {
66 scanSolid = 0,
67 scanFacet,
68 scanLoop,
69 scanVerts,
70 scanEndLoop,
71 scanEndFacet,
72 scanEndSolid
73 };
74
75 scanState state_;
76
77 std::string errMsg_;
78
79 //- Like std:csub_match
80 typedef std::pair<const char*, const char*> tokenType;
81
82 // Tokenized line
84
85 //- Tokenize
86 inline std::string::size_type tokenize(const char *p, const char *pe)
87 {
88 const char* start = p;
89 tokens_.clear();
90
91 // Find not space
92 while (p < pe && isspace(*p))
93 {
94 if (*p == '\n' && lineNum_)
95 {
96 ++lineNum_;
97 }
98 ++p;
99 }
100
101 while (p != pe)
102 {
103 const char* beg = p;
104
105 // Find space
106 while (p < pe && !isspace(*p))
107 {
108 ++p;
109 }
110 tokens_.append(tokenType(beg, p));
111
112 // Find next
113 while (p < pe && isspace(*p))
114 {
115 if (*p == '\n')
116 {
117 ++lineNum_;
118 return (p - start);
119 }
120 ++p;
121 }
122 }
123
124 return (p - start);
125 }
126
127
128public:
129
130 //- From input stream and the approximate number of vertices in the STL
131 STLAsciiParseManual(const label approxNpoints)
132 :
133 Detail::STLAsciiParse(approxNpoints)
134 {}
135
136 //- Execute parser
137 void execute(std::istream& is);
138};
139
140} // End namespace Detail
141} // End namespace Foam
142
143
144/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
145
146// Length of the input read buffer
147#define INBUFLEN 16384
148
150{
151 if (!is)
152 {
153 return;
154 }
155
156 // Buffering
157 char inbuf[INBUFLEN];
158 std::streamsize pending = 0;
159
160 lineNum_ = 0;
161
162 state_ = scanSolid;
163 errMsg_.clear();
164
165 // Line-oriented processing loop
166 while (is)
167 {
168 if (pending >= INBUFLEN)
169 {
170 // We overfilled the buffer while trying to scan a token...
172 << "buffer full while scanning near line " << lineNum_ << nl;
173 break;
174 }
175
176 char *data = inbuf + pending; // current data buffer
177 const std::streamsize buflen = INBUFLEN - pending; // space in buffer
178
179 is.read(data, buflen);
180 const std::streamsize gcount = is.gcount();
181
182 if (!gcount)
183 {
184 // EOF
185 // If scanning for next "solid" this is a valid way to exit, but
186 // an error if scanning for the initial "solid" or any other token
187
188 switch (state_)
189 {
190 case scanSolid:
191 {
192 if (!lineNum_) errMsg_ = perrorEOF("solid");
193 break;
194 }
195 case scanFacet: { errMsg_ = perrorEOF("facet"); break; }
196 case scanLoop: { errMsg_ = perrorEOF("outer loop"); break; }
197 case scanVerts: { errMsg_ = perrorEOF("vertex"); break; }
198 case scanEndLoop: { errMsg_ = perrorEOF("endloop"); break; }
199 case scanEndFacet: { errMsg_ = perrorEOF("endfacet"); break; }
200 case scanEndSolid: { errMsg_ = perrorEOF("endsolid"); break; }
201 }
202
203 // Terminate the parsing loop
204 break;
205 }
206
207 // p,pe = Ragel parsing point and parsing end (default naming)
208 // eof = Ragel EOF point (default naming)
209
210 char *p = inbuf;
211 char *pe = data + gcount;
212
213 // Line-oriented: search backwards to find last newline
214 {
215 --pe;
216 while (*pe != '\n' && pe >= inbuf)
217 {
218 --pe;
219 }
220 ++pe;
221 }
222
223 std::string cmd;
224 do
225 {
226 // Tokenize
227 const auto parsedLen = tokenize(p, pe);
228 p += parsedLen;
229 if (!parsedLen || tokens_.empty())
230 {
231 break;
232 }
233
234 // Ensure consistent case on the first token
235 cmd.assign(tokens_[0].first, tokens_[0].second);
236 stringOps::lower(cmd);
237
238 // Handle all expected parse states
239 switch (state_)
240 {
241 case scanSolid:
242 {
243 if (cmd == "solid")
244 {
245 if (tokens_.empty())
246 {
248 }
249 else
250 {
252 (
253 word::validate(tokens_[1].first, tokens_[1].second)
254 );
255 }
256
257 state_ = scanFacet; // Next state
258 }
259 else
260 {
261 errMsg_ = perrorParse("solid", cmd);
262 }
263 break;
264 }
265 case scanFacet:
266 {
267 if (cmd == "color")
268 {
269 // Optional 'color' entry (after solid)
270 // - continue looking for 'facet'
271 continue;
272 }
273 else if (cmd == "facet")
274 {
275 beginFacet();
276 state_ = scanLoop; // Next state
277 }
278 else if (cmd == "endsolid")
279 {
280 // Finished with 'endsolid' - find next solid
281 state_ = scanSolid;
282 }
283 else
284 {
285 errMsg_ = perrorParse("facet", cmd);
286 }
287 break;
288 }
289 case scanLoop:
290 {
291 if (cmd == "outer")
292 {
293 // More pedantic would with (tokens_[1] == "loop") too
294 state_ = scanVerts; // Next state
295 }
296 else
297 {
298 errMsg_ = perrorParse("outer loop", cmd);
299 }
300 break;
301 }
302 case scanVerts:
303 {
304 if (cmd == "vertex")
305 {
306 if (tokens_.size() > 3)
307 {
308 // Although tokens are not nul-terminated,
309 // they are space delimited and thus good enough for atof()
310 addVertexComponent(tokens_[1].first);
311 addVertexComponent(tokens_[2].first);
312 addVertexComponent(tokens_[3].first);
313 }
314 else
315 {
316 errMsg_ = "Error parsing vertex value";
317 }
318 }
319 else if (cmd == "endloop")
320 {
321 state_ = scanEndFacet; // Next state
322 }
323 else
324 {
325 errMsg_ = perrorParse("vertex", cmd);
326 }
327 break;
328 }
329 case scanEndLoop:
330 {
331 if (cmd == "endloop")
332 {
333 state_ = scanEndFacet; // Next state
334 }
335 else
336 {
337 errMsg_ = perrorParse("endloop", cmd);
338 }
339 break;
340 }
341 case scanEndFacet:
342 {
343 if (cmd == "endfacet")
344 {
345 endFacet();
346 state_ = scanFacet; // Next facet, or endsolid
347 }
348 else
349 {
350 errMsg_ = perrorParse("endfacet", cmd);
351 }
352 break;
353 }
354 case scanEndSolid:
355 {
356 if (cmd == "endsolid")
357 {
358 state_ = scanSolid; // Start over again
359 }
360 else
361 {
362 errMsg_ = perrorParse("endsolid", cmd);
363 }
364 break;
365 }
366 }
367 }
368 while (errMsg_.empty());
369
370 // How much still in the buffer?
371 pending = data + gcount - pe;
372
373 if (pending)
374 {
375 memmove(inbuf, pe, pending);
376 }
377
378 if (gcount < buflen)
379 {
380 break; // done
381 }
382
383 if (!errMsg_.empty())
384 {
385 break;
386 }
387 }
388
389 if (!errMsg_.empty())
390 {
392 << errMsg_ << nl;
393 }
394}
395
396
397/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
398
399//
400// Member Function
401//
402bool Foam::fileFormats::STLReader::readAsciiManual
403(
404 const fileName& filename
405)
406{
407 IFstream is(filename);
408 if (!is)
409 {
411 << "file " << filename << " not found"
412 << exit(FatalError);
413 }
414
415 // Create with the approximate number of vertices in the STL from file size
416 Detail::STLAsciiParseManual lexer(Foam::fileSize(filename)/400);
417 lexer.execute(is.stdStream());
418
419 transfer(lexer);
420
421 return true;
422}
423
424
425// ************************************************************************* //
Functions used by OpenFOAM that are specific to POSIX compliant operating systems and need to be repl...
#define INBUFLEN
static std::string perrorParse(std::string expected, std::string found)
static std::string perrorEOF(std::string expected)
bool found
A lexer for parsing STL ASCII files.
void execute(std::istream &is)
Execute parser.
STLAsciiParseManual(const label approxNpoints)
From input stream and the approximate number of vertices in the STL.
Internal class used when parsing STL ASCII format.
Definition: STLAsciiParse.H:55
void beginFacet()
Action when entering 'facet'.
void beginSolid(word solidName)
Action when entering 'solid'.
bool addVertexComponent(float val)
Add next vertex component. On each third call, adds the point.
void endFacet()
Action on 'endfacet'.
A 1D vector of objects of type <T> that resizes itself as necessary to accept the new objects.
Definition: DynamicList.H:72
void clear() noexcept
Clear the addressed list, i.e. set the size to zero.
Definition: DynamicListI.H:391
void append(const T &val)
Copy append an element to the end of this list.
Definition: DynamicListI.H:503
Input from file stream, using an ISstream.
Definition: IFstream.H:57
void append(const T &val)
Append an element at the end of the list.
Definition: ListI.H:175
void clear()
Clear the list, i.e. set size to zero.
Definition: ListI.H:116
Database for solution data, solver performance and other reduced data.
Definition: data.H:58
A class for handling file names.
Definition: fileName.H:76
virtual bool execute()
Calculate the output fields.
virtual void validate()
Validate the turbulence fields after construction.
Definition: kkLOmega.C:597
volScalarField & p
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:453
string lower(const std::string &s)
Return string copy transformed with std::tolower on each character.
Definition: stringOps.C:1184
Namespace for OpenFOAM.
bool isspace(char c) noexcept
Test for whitespace (C-locale)
Definition: char.H:65
off_t fileSize(const fileName &name, const bool followLink=true)
Return size of file or -1 on failure (normally follows symbolic links).
Definition: MSwindows.C:684
error FatalError
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:130
constexpr char nl
The newline '\n' character (0x0a)
Definition: Ostream.H:53