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 
149 void Foam::Detail::STLAsciiParseManual::execute(std::istream& is)
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 // ************************************************************************* //
static word validate(const std::string &s, const bool prefix=false)
Construct validated word (no invalid characters).
Definition: word.C:38
void size(const label n)
Older name for setAddressableSize.
Definition: UList.H:118
#define INBUFLEN
Internal class used when parsing STL ASCII format.
Definition: STLAsciiParse.H:50
off_t fileSize(const fileName &name, const bool followLink=true)
Return size of file or -1 on failure (normally follows symbolic links).
Definition: POSIX.C:858
void beginSolid(word solidName)
Action when entering &#39;solid&#39;.
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:125
static std::string perrorParse(std::string expected, std::string found)
error FatalError
Error stream (stdout output on all processes), with additional &#39;FOAM FATAL ERROR&#39; header text and sta...
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:578
void beginFacet()
Action when entering &#39;facet&#39;.
constexpr char nl
The newline &#39;\n&#39; character (0x0a)
Definition: Ostream.H:49
bool empty() const noexcept
True if the UList is empty (ie, size() is zero)
Definition: UListI.H:420
A lexer for parsing STL ASCII files.
bool addVertexComponent(float val)
Add next vertex component. On each third call, adds the point.
Functions used by OpenFOAM that are specific to POSIX compliant operating systems and need to be repl...
static std::string perrorEOF(std::string expected)
static const word null
An empty word.
Definition: word.H:84
STLAsciiParseManual(const label approxNpoints)
From input stream and the approximate number of vertices in the STL.
void append(const T &val)
Copy append an element to the end of this list.
Definition: DynamicList.H:558
graph_traits< Graph >::vertices_size_type size_type
Definition: SloanRenumber.C:68
void execute(std::istream &is)
Execute parser.
Database for solution data, solver performance and other reduced data.
Definition: data.H:51
void clear() noexcept
Clear the addressed list, i.e. set the size to zero.
Definition: DynamicListI.H:384
string lower(const std::string &s)
Return string copy transformed with std::tolower on each character.
Definition: stringOps.C:1171
bool isspace(char c) noexcept
Test for whitespace (C-locale)
Definition: char.H:71
void endFacet()
Action on &#39;endfacet&#39;.
volScalarField & p
bool found
Namespace for OpenFOAM.