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
Please add newer entries at the top, list the date and your name with
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>
================================================================================
++gerbview:
......
......@@ -86,11 +86,10 @@ void DSNLEXER::PushReader( LINE_READER* aLineReader )
{
readerStack.push_back( aLineReader );
reader = aLineReader;
start = (char*) (*aLineReader);
// force a new readLine() as first thing.
limit = start;
next = start;
limit = start();
next = start();
}
......@@ -102,12 +101,10 @@ bool DSNLEXER::PopReader()
readerStack.pop_back();
reader = &readerStack.back();
start = (char*) (*reader);
// force a new readLine() as first thing.
limit = start;
next = start;
limit = start();
next = start();
return true;
}
return false;
......@@ -334,7 +331,7 @@ L_read:
goto exit;
}
cur = start;
cur = start();
// skip leading whitespace
while( cur<limit && isSpace(*cur) )
......@@ -348,8 +345,8 @@ L_read:
{
// save the entire line, including new line as the current token.
// the '#' character may not be at offset zero.
curText = start; // entire line is the token
cur = start; // ensure a good curOffset below
curText = start(); // entire line is the token
cur = start(); // ensure a good curOffset below
curTok = DSN_COMMENT;
head = limit; // do a readLine() on next call in here.
goto exit;
......@@ -417,7 +414,7 @@ L_read:
like: U2-14 or "U2"-"14"
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 = '-';
curTok = DSN_DASH;
......@@ -538,7 +535,7 @@ L_read:
exit: // single point of exit, no returns elsewhere please.
curOffset = cur - start;
curOffset = cur - start();
next = head;
......
......@@ -37,10 +37,18 @@
LINE_READER::LINE_READER( unsigned aMaxLineLength )
{
lineNum = 0;
if( aMaxLineLength == 0 ) // caller is goofed up.
aMaxLineLength = LINE_READER_LINE_DEFAULT_MAX;
maxLineLength = aMaxLineLength;
// the real capacity is 10 bytes larger than requested.
capacity = aMaxLineLength + 10;
// start at the INITIAL size, expand as needed up to the MAX size in maxLineLength
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];
......@@ -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 ) :
LINE_READER( aMaxLineLength ),
fp( aFile )
......@@ -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 )
{
line[0] = 0;
length = 0;
}
else
// fgets always put a terminating nul at end of its read.
while( fgets( line + length, capacity - length, fp ) )
{
length = strlen( line );
length += strlen( line + length );
if( length > maxLineLength )
if( length == maxLineLength )
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;
}
int STRING_LINE_READER::ReadLine() throw (IOError)
unsigned STRING_LINE_READER::ReadLine() throw (IOError)
{
size_t nlOffset = lines.find( '\n', ndx );
size_t advance;
if( nlOffset == std::string::npos )
advance = lines.length() - ndx;
length = lines.length() - ndx;
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") );
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;
ndx += advance;
ndx += length;
}
length = advance;
line[advance] = 0;
line[length] = 0;
return advance;
return length;
}
......
......@@ -79,7 +79,6 @@ enum DSN_SYNTAX_T {
class DSNLEXER
{
char* next;
char* start;
char* limit;
typedef boost::ptr_vector<LINE_READER> READER_STACK;
......@@ -100,14 +99,21 @@ class DSNLEXER
const KEYWORD* keywords;
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();
int readLine() throw (IOError)
{
int len = reader->ReadLine();
unsigned len = reader->ReadLine();
next = start;
limit = start + len;
// set next and limit to start() and 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;
}
......@@ -187,12 +193,12 @@ public:
* 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
* 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
* possible if there are at least 2 LINE_READERs on the stack, since popping
* 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.
*/
bool PopReader();
......
......@@ -61,6 +61,8 @@ struct IOError
}
};
#define LINE_READER_LINE_DEFAULT_MAX 100000
#define LINE_READER_LINE_INITIAL_SIZE 5000
/**
* Class LINE_READER
......@@ -70,12 +72,17 @@ struct IOError
class LINE_READER
{
protected:
unsigned length;
int lineNum;
char* line;
unsigned maxLineLength;
unsigned capacity;
wxString source; ///< origin of text lines, e.g. filename or "clipboard"
unsigned length; ///< no. bytes in line before trailing nul.
int lineNum;
char* line; ///< the read line of UTF8 text
unsigned capacity; ///< no. bytes allocated for line.
unsigned maxLineLength; ///< maximum allowed capacity using resizing.
wxString source; ///< origin of text lines, e.g. filename or "clipboard"
void expandCapacity( unsigned newsize );
public:
......@@ -84,7 +91,7 @@ public:
* builds a line reader and fixes the length of the maximum supported
* line length to @a aMaxLineLength.
*/
LINE_READER( unsigned aMaxLineLength );
LINE_READER( unsigned aMaxLineLength = LINE_READER_LINE_DEFAULT_MAX );
virtual ~LINE_READER()
{
......@@ -96,10 +103,10 @@ public:
* 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
* 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.
*/
virtual int ReadLine() throw (IOError) = 0;
virtual unsigned ReadLine() throw (IOError) = 0;
/**
* Function GetSource
......@@ -118,7 +125,7 @@ public:
* is a casting operator that returns a char* pointer to the start of the
* line buffer.
*/
operator char* ()
operator char* () const
{
return line;
}
......@@ -166,7 +173,7 @@ public:
* @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.
*/
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()
{
......@@ -179,10 +186,10 @@ public:
* 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
* 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.
*/
int ReadLine() throw (IOError);
unsigned ReadLine() throw (IOError);
/**
* Function Rewind
......@@ -234,10 +241,10 @@ public:
* 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
* 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.
*/
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