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 -------------------------------------------------------------------------------
10 License
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 
26 Description
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 
38 static inline std::string perrorEOF(std::string expected)
39 {
40  return "Premature EOF while reading '" + expected + "'";
41 }
42 
43 
44 static inline std::string perrorParse(std::string expected, std::string found)
45 {
46  return "Parse error. Expecting '" + expected + "' found '" + found + "'";
47 }
48 
49 
50 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
51 
52 namespace Foam
53 {
54 namespace 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 
128 public:
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  {
251  beginSolid
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 //
402 bool 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 // ************************************************************************* //
OSspecific.H
Functions used by OpenFOAM that are specific to POSIX compliant operating systems and need to be repl...
p
volScalarField & p
Definition: createFieldRefs.H:8
STLReader.H
Foam::fileName
A class for handling file names.
Definition: fileName.H:73
INBUFLEN
#define INBUFLEN
Definition: STLAsciiParseManual.C:147
Foam::isspace
bool isspace(char c) noexcept
Test for whitespace (C-locale)
Definition: char.H:65
Foam::IFstream
Input from file stream, using an ISstream.
Definition: IFstream.H:53
Foam::DynamicList< tokenType, 16 >
Foam::Detail::STLAsciiParse::endFacet
void endFacet()
Action on 'endfacet'.
Definition: STLAsciiParseI.H:107
Foam::word::validate
static word validate(const std::string &s, const bool prefix=false)
Construct validated word (no invalid characters).
Definition: word.C:45
Foam::Detail::STLAsciiParse
Internal class used when parsing STL ASCII format.
Definition: STLAsciiParse.H:54
Foam::DynamicList::clear
void clear() noexcept
Clear the addressed list, i.e. set the size to zero.
Definition: DynamicListI.H:391
perrorEOF
static std::string perrorEOF(std::string expected)
Definition: STLAsciiParseManual.C:38
Foam::Detail::STLAsciiParse::lineNum_
label lineNum_
Definition: STLAsciiParse.H:62
Foam::stringOps::lower
string lower(const std::string &s)
Return string copy transformed with std::tolower on each character.
Definition: stringOps.C:1184
Foam::fileSize
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:676
Foam::DynamicList::append
DynamicList< T, SizeMin > & append(const T &val)
Append an element to the end of this list.
Definition: DynamicListI.H:511
Foam::Detail::STLAsciiParseManual
A lexer for parsing STL ASCII files.
Definition: STLAsciiParseManual.C:60
size_type
graph_traits< Graph >::vertices_size_type size_type
Definition: SloanRenumber.C:76
Foam::FatalError
error FatalError
Foam
Namespace for OpenFOAM.
Definition: atmBoundaryLayer.C:33
Foam::exit
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:130
found
bool found
Definition: TABSMDCalcMethod2.H:32
Foam::Detail::STLAsciiParse::addVertexComponent
bool addVertexComponent(float val)
Add next vertex component. On each third call, adds the point.
Definition: STLAsciiParseI.H:76
Foam::Detail::STLAsciiParseManual::execute
void execute(std::istream &is)
Execute parser.
Definition: STLAsciiParseManual.C:149
FatalErrorInFunction
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:453
Foam::nl
constexpr char nl
Definition: Ostream.H:404
Foam::word::null
static const word null
An empty word.
Definition: word.H:80
Foam::Detail::STLAsciiParseManual::STLAsciiParseManual
STLAsciiParseManual(const label approxNpoints)
From input stream and the approximate number of vertices in the STL.
Definition: STLAsciiParseManual.C:131
stringOps.H
Foam::Detail::STLAsciiParse::beginSolid
void beginSolid(word solidName)
Action when entering 'solid'.
Definition: STLAsciiParseI.H:30
STLAsciiParse.H
Foam::data
Database for solution data, solver performance and other reduced data.
Definition: data.H:55
perrorParse
static std::string perrorParse(std::string expected, std::string found)
Definition: STLAsciiParseManual.C:44
Foam::Detail::STLAsciiParse::beginFacet
void beginFacet()
Action when entering 'facet'.
Definition: STLAsciiParseI.H:63