Commit 8b4cad8a authored by Dick Hollenbeck's avatar Dick Hollenbeck

make LINE_READERs have dynamic buffer sizes

parent 285d9b64
...@@ -4,6 +4,14 @@ KiCad ChangeLog 2010 ...@@ -4,6 +4,14 @@ KiCad ChangeLog 2010
Please add newer entries at the top, list the date and your name with Please add newer entries at the top, list the date and your name with
email address. email address.
2010-Oct-20 UPDATE Dick Hollenbeck <dick@softplc.com>
================================================================================
++richio:
LINE_READERs will now allocate a smaller initial size buffer, say 5000 bytes,
and then resize their buffers up to some provided maximum, after which an
exception is thrown should a line exceed that maximum line length.
2010-oct-15, UPDATE Jean-Pierre Charras <jean-pierre.charras@gipsa-lab.inpg.fr> 2010-oct-15, UPDATE Jean-Pierre Charras <jean-pierre.charras@gipsa-lab.inpg.fr>
================================================================================ ================================================================================
++gerbview: ++gerbview:
......
...@@ -86,11 +86,10 @@ void DSNLEXER::PushReader( LINE_READER* aLineReader ) ...@@ -86,11 +86,10 @@ void DSNLEXER::PushReader( LINE_READER* aLineReader )
{ {
readerStack.push_back( aLineReader ); readerStack.push_back( aLineReader );
reader = aLineReader; reader = aLineReader;
start = (char*) (*aLineReader);
// force a new readLine() as first thing. // force a new readLine() as first thing.
limit = start; limit = start();
next = start; next = start();
} }
...@@ -102,12 +101,10 @@ bool DSNLEXER::PopReader() ...@@ -102,12 +101,10 @@ bool DSNLEXER::PopReader()
readerStack.pop_back(); readerStack.pop_back();
reader = &readerStack.back(); reader = &readerStack.back();
start = (char*) (*reader);
// force a new readLine() as first thing. // force a new readLine() as first thing.
limit = start; limit = start();
next = start; next = start();
return true; return true;
} }
return false; return false;
...@@ -334,7 +331,7 @@ L_read: ...@@ -334,7 +331,7 @@ L_read:
goto exit; goto exit;
} }
cur = start; cur = start();
// skip leading whitespace // skip leading whitespace
while( cur<limit && isSpace(*cur) ) while( cur<limit && isSpace(*cur) )
...@@ -348,8 +345,8 @@ L_read: ...@@ -348,8 +345,8 @@ L_read:
{ {
// save the entire line, including new line as the current token. // save the entire line, including new line as the current token.
// the '#' character may not be at offset zero. // the '#' character may not be at offset zero.
curText = start; // entire line is the token curText = start(); // entire line is the token
cur = start; // ensure a good curOffset below cur = start(); // ensure a good curOffset below
curTok = DSN_COMMENT; curTok = DSN_COMMENT;
head = limit; // do a readLine() on next call in here. head = limit; // do a readLine() on next call in here.
goto exit; goto exit;
...@@ -417,7 +414,7 @@ L_read: ...@@ -417,7 +414,7 @@ L_read:
like: U2-14 or "U2"-"14" like: U2-14 or "U2"-"14"
This is detectable by a non-space immediately preceeding the dash. This is detectable by a non-space immediately preceeding the dash.
*/ */
if( *cur == '-' && cur>start && !isSpace( cur[-1] ) ) if( *cur == '-' && cur>start() && !isSpace( cur[-1] ) )
{ {
curText = '-'; curText = '-';
curTok = DSN_DASH; curTok = DSN_DASH;
...@@ -538,7 +535,7 @@ L_read: ...@@ -538,7 +535,7 @@ L_read:
exit: // single point of exit, no returns elsewhere please. exit: // single point of exit, no returns elsewhere please.
curOffset = cur - start; curOffset = cur - start();
next = head; next = head;
......
...@@ -37,10 +37,18 @@ ...@@ -37,10 +37,18 @@
LINE_READER::LINE_READER( unsigned aMaxLineLength ) LINE_READER::LINE_READER( unsigned aMaxLineLength )
{ {
lineNum = 0; lineNum = 0;
if( aMaxLineLength == 0 ) // caller is goofed up.
aMaxLineLength = LINE_READER_LINE_DEFAULT_MAX;
maxLineLength = aMaxLineLength; maxLineLength = aMaxLineLength;
// the real capacity is 10 bytes larger than requested. // start at the INITIAL size, expand as needed up to the MAX size in maxLineLength
capacity = aMaxLineLength + 10; capacity = LINE_READER_LINE_INITIAL_SIZE;
// but never go above user's aMaxLineLength, and leave space for trailing nul
if( capacity > aMaxLineLength-1 )
capacity = aMaxLineLength-1;
line = new char[capacity]; line = new char[capacity];
...@@ -49,6 +57,28 @@ LINE_READER::LINE_READER( unsigned aMaxLineLength ) ...@@ -49,6 +57,28 @@ LINE_READER::LINE_READER( unsigned aMaxLineLength )
} }
void LINE_READER::expandCapacity( unsigned newsize )
{
// length can equal maxLineLength and nothing breaks, there's room for
// the terminating nul. cannot go over this.
if( newsize > maxLineLength+1 )
newsize = maxLineLength+1;
if( newsize > capacity )
{
capacity = newsize;
// resize the buffer, and copy the original data
char* bigger = new char[capacity];
memcpy( bigger, line, length );
delete line;
line = bigger;
}
}
FILE_LINE_READER::FILE_LINE_READER( FILE* aFile, const wxString& aFileName, unsigned aMaxLineLength ) : FILE_LINE_READER::FILE_LINE_READER( FILE* aFile, const wxString& aFileName, unsigned aMaxLineLength ) :
LINE_READER( aMaxLineLength ), LINE_READER( aMaxLineLength ),
fp( aFile ) fp( aFile )
...@@ -57,56 +87,61 @@ FILE_LINE_READER::FILE_LINE_READER( FILE* aFile, const wxString& aFileName, unsi ...@@ -57,56 +87,61 @@ FILE_LINE_READER::FILE_LINE_READER( FILE* aFile, const wxString& aFileName, unsi
} }
int FILE_LINE_READER::ReadLine() throw (IOError) unsigned FILE_LINE_READER::ReadLine() throw (IOError)
{ {
const char* p = fgets( line, capacity, fp ); length = 0;
line[0] = 0;
if( !p ) // fgets always put a terminating nul at end of its read.
{ while( fgets( line + length, capacity - length, fp ) )
line[0] = 0;
length = 0;
}
else
{ {
length = strlen( line ); length += strlen( line + length );
if( length > maxLineLength ) if( length == maxLineLength )
throw IOError( _("Line length exceeded") ); throw IOError( _("Line length exceeded") );
++lineNum; // a normal line breaks here, once through
if( length < capacity-1 || line[length-1] == '\n' )
break;
expandCapacity( capacity * 2 );
} }
if( length )
++lineNum;
return length; return length;
} }
int STRING_LINE_READER::ReadLine() throw (IOError) unsigned STRING_LINE_READER::ReadLine() throw (IOError)
{ {
size_t nlOffset = lines.find( '\n', ndx ); size_t nlOffset = lines.find( '\n', ndx );
size_t advance;
if( nlOffset == std::string::npos ) if( nlOffset == std::string::npos )
advance = lines.length() - ndx; length = lines.length() - ndx;
else else
advance = nlOffset - ndx + 1; // include the newline, so +1 length = nlOffset - ndx + 1; // include the newline, so +1
if( advance ) if( length )
{ {
if( advance > maxLineLength ) if( length >= maxLineLength )
throw IOError( _("Line length exceeded") ); throw IOError( _("Line length exceeded") );
wxASSERT( ndx + advance <= lines.length() ); if( length > capacity )
expandCapacity( length );
memcpy( line, &source[ndx], advance ); wxASSERT( ndx + length <= lines.length() );
memcpy( line, &source[ndx], length );
++lineNum; ++lineNum;
ndx += advance; ndx += length;
} }
length = advance; line[length] = 0;
line[advance] = 0;
return advance; return length;
} }
......
...@@ -79,7 +79,6 @@ enum DSN_SYNTAX_T { ...@@ -79,7 +79,6 @@ enum DSN_SYNTAX_T {
class DSNLEXER class DSNLEXER
{ {
char* next; char* next;
char* start;
char* limit; char* limit;
typedef boost::ptr_vector<LINE_READER> READER_STACK; typedef boost::ptr_vector<LINE_READER> READER_STACK;
...@@ -100,14 +99,21 @@ class DSNLEXER ...@@ -100,14 +99,21 @@ class DSNLEXER
const KEYWORD* keywords; const KEYWORD* keywords;
unsigned keywordCount; unsigned keywordCount;
/// Use casting char* operator to get start of line, which is dynamic since reader
/// can be resizing its buffer at each reader->ReadLine() only.
char* start() const { return (char*) (*reader); }
void init(); void init();
int readLine() throw (IOError) int readLine() throw (IOError)
{ {
int len = reader->ReadLine(); unsigned len = reader->ReadLine();
next = start; // set next and limit to start() and start() + len.
limit = start + len; // start() is constant until the next ReadLine(), which could resize and
// relocate reader's line buffer.
next = start();
limit = next + len;
return len; return len;
} }
...@@ -187,12 +193,12 @@ public: ...@@ -187,12 +193,12 @@ public:
* in the case of FILE_LINE_READER this means the associated FILE is closed. * in the case of FILE_LINE_READER this means the associated FILE is closed.
* The most recently used former LINE_READER on the stack becomes the * The most recently used former LINE_READER on the stack becomes the
* current LINE_READER and its previous position in its input stream and the * current LINE_READER and its previous position in its input stream and the
* its latest line number should pertain. PopReader always starts reading * its latest line number should pertain. PopReader always starts reading
* from a new line upon returning to the previous LINE_READER. A pop is only * from a new line upon returning to the previous LINE_READER. A pop is only
* possible if there are at least 2 LINE_READERs on the stack, since popping * possible if there are at least 2 LINE_READERs on the stack, since popping
* the last one is not supported. * the last one is not supported.
* *
* @return bool - true if there was at least two readers on the stack and * @return bool - true if there was at least two readers on the stack and
* therefore the pop succeeded, else false and the pop failed. * therefore the pop succeeded, else false and the pop failed.
*/ */
bool PopReader(); bool PopReader();
......
...@@ -61,6 +61,8 @@ struct IOError ...@@ -61,6 +61,8 @@ struct IOError
} }
}; };
#define LINE_READER_LINE_DEFAULT_MAX 100000
#define LINE_READER_LINE_INITIAL_SIZE 5000
/** /**
* Class LINE_READER * Class LINE_READER
...@@ -70,12 +72,17 @@ struct IOError ...@@ -70,12 +72,17 @@ struct IOError
class LINE_READER class LINE_READER
{ {
protected: protected:
unsigned length; unsigned length; ///< no. bytes in line before trailing nul.
int lineNum; int lineNum;
char* line;
unsigned maxLineLength; char* line; ///< the read line of UTF8 text
unsigned capacity; unsigned capacity; ///< no. bytes allocated for line.
wxString source; ///< origin of text lines, e.g. filename or "clipboard"
unsigned maxLineLength; ///< maximum allowed capacity using resizing.
wxString source; ///< origin of text lines, e.g. filename or "clipboard"
void expandCapacity( unsigned newsize );
public: public:
...@@ -84,7 +91,7 @@ public: ...@@ -84,7 +91,7 @@ public:
* builds a line reader and fixes the length of the maximum supported * builds a line reader and fixes the length of the maximum supported
* line length to @a aMaxLineLength. * line length to @a aMaxLineLength.
*/ */
LINE_READER( unsigned aMaxLineLength ); LINE_READER( unsigned aMaxLineLength = LINE_READER_LINE_DEFAULT_MAX );
virtual ~LINE_READER() virtual ~LINE_READER()
{ {
...@@ -96,10 +103,10 @@ public: ...@@ -96,10 +103,10 @@ public:
* reads a line of text into the buffer and increments the line number * reads a line of text into the buffer and increments the line number
* counter. If the line is larger than the buffer size, then an exception * counter. If the line is larger than the buffer size, then an exception
* is thrown. * is thrown.
* @return int - The number of bytes read, 0 at end of file. * @return unsigned - The number of bytes read, 0 at end of file.
* @throw IOError only when a line is too long. * @throw IOError only when a line is too long.
*/ */
virtual int ReadLine() throw (IOError) = 0; virtual unsigned ReadLine() throw (IOError) = 0;
/** /**
* Function GetSource * Function GetSource
...@@ -118,7 +125,7 @@ public: ...@@ -118,7 +125,7 @@ public:
* is a casting operator that returns a char* pointer to the start of the * is a casting operator that returns a char* pointer to the start of the
* line buffer. * line buffer.
*/ */
operator char* () operator char* () const
{ {
return line; return line;
} }
...@@ -166,7 +173,7 @@ public: ...@@ -166,7 +173,7 @@ public:
* @param aFileName is the name of the file for error reporting purposes. * @param aFileName is the name of the file for error reporting purposes.
* @param aMaxLineLength is the number of bytes to use in the line buffer. * @param aMaxLineLength is the number of bytes to use in the line buffer.
*/ */
FILE_LINE_READER( FILE* aFile, const wxString& aFileName, unsigned aMaxLineLength ); FILE_LINE_READER( FILE* aFile, const wxString& aFileName, unsigned aMaxLineLength = LINE_READER_LINE_DEFAULT_MAX );
~FILE_LINE_READER() ~FILE_LINE_READER()
{ {
...@@ -179,10 +186,10 @@ public: ...@@ -179,10 +186,10 @@ public:
* reads a line of text into the buffer and increments the line number * reads a line of text into the buffer and increments the line number
* counter. If the line is larger than the buffer size, then an exception * counter. If the line is larger than the buffer size, then an exception
* is thrown. * is thrown.
* @return int - The number of bytes read, 0 at end of file. * @return unsigned - The number of bytes read, 0 at end of file.
* @throw IOError only when a line is too long. * @throw IOError only when a line is too long.
*/ */
int ReadLine() throw (IOError); unsigned ReadLine() throw (IOError);
/** /**
* Function Rewind * Function Rewind
...@@ -234,10 +241,10 @@ public: ...@@ -234,10 +241,10 @@ public:
* reads a line of text into the buffer and increments the line number * reads a line of text into the buffer and increments the line number
* counter. If the line is larger than the buffer size, then an exception * counter. If the line is larger than the buffer size, then an exception
* is thrown. * is thrown.
* @return int - The number of bytes read, 0 at end of file. * @return unsigned - The number of bytes read, 0 at end of file.
* @throw IOError only when a line is too long. * @throw IOError only when a line is too long.
*/ */
int ReadLine() throw (IOError); unsigned ReadLine() throw (IOError);
}; };
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment