stevemarple / IniFile

Arduino library to parse ini files.
GNU Lesser General Public License v2.1
87 stars 45 forks source link

buffer size optimization idea #5

Closed dzzie closed 3 years ago

dzzie commented 8 years ago

Hi, just a thought in regards to:

The user-supplied buffer must be large enough to accomodate the longest line in the file.

This can be optimized a bit in the readLine scanner. If it detects that a line is a comment, it can scan ahead until the next new line, incrementing pos, but not being bound by the current buffer length limitation. By ignoring comment line length, you can trim allot off of your buffer sizes to only the longest variable line now.

I just did a quick hack to make it so, but it saves allot of memory and allows you to include verbose comments without having to be careful.

IniFile::error_t IniFile::readUntilNewLine(File &file, char *buffer, size_t len, uint32_t &pos){

    char *b=buffer;
    while(1){
          size_t bytesRead = file.read(b, len);
          if (!bytesRead) {
            buffer[0] = '\0';
            return errorEndOfFile;
          }

          for (size_t i = 0; i < bytesRead && i < len-1; ++i) {
            // Test for '\n' with optional '\r' too
            // if (endOfLineTest(buffer, len, i, '\n', '\r')

            if (buffer[i] == '\n' || buffer[i] == '\r') {
              char match = buffer[i];
              char otherNewline = (match == '\n' ? '\r' : '\n'); 
              // end of line, discard any trailing character of the other sort
              // of newline
              buffer[i] = '\0';

              if (buffer[i+1] == otherNewline)
            ++i;
              pos += (i + 1); // skip past newline(s)
              //return (i+1 == bytesRead && !file.available());
              return errorNoError;
            }
          }
          if (!file.available()) {
            // end of file without a newline
            buffer[bytesRead] = '\0';
            // return 1; //done
            return errorEndOfFile;
          }
          *b+=len;
    }

}

IniFile::error_t IniFile::readLine(File &file, char *buffer, size_t len, uint32_t &pos)
{
...
//does not account for whitespace before comment char lazy..
if (isCommentChar(buffer[0]))return readUntilNewLine(file, buffer,len, pos);
stevemarple commented 8 years ago

That's a neat optimisation that doesn't appear to have any negative impacts. Can you please generate a pull request so that I can merge the changes?

dzzie commented 8 years ago

sure, I will do a few more tests on it too then

dzzie commented 8 years ago

one other addition I made that you might be interested in was to include the error messages directly into the class. sucks up a bit more memory with the strings but puts it all in one place. maybe wrap it in a #ifdef withErrorMsgs


char* s_errorNoError="no error";
char* s_errorFileNotFound="file not found";
char* s_errorFileNotOpen="file not open";
char* s_errorBufferTooSmall="buffer too small";
char* s_errorSeekError="seek error";
char* s_errorSectionNotFound="section not found";
char* s_errorKeyNotFound="key not found";
char* s_errorEndOfFile="end of file";
char* s_errorUnknownError="unknown error";
char* s_errorUnknownValue="unknown err value";

char* IniFile::getErrorText()
{
  switch (_error) {
      case IniFile::errorNoError: return IniFile::s_errorNoError;
      case IniFile::errorFileNotFound: return IniFile::s_errorFileNotFound;
      case IniFile::errorFileNotOpen: return IniFile::s_errorFileNotOpen;
      case IniFile::errorBufferTooSmall: return IniFile::s_errorBufferTooSmall;
      case IniFile::errorSeekError: return IniFile::s_errorSeekError;
      case IniFile::errorSectionNotFound: return IniFile::s_errorSectionNotFound;
      case IniFile::errorKeyNotFound: return IniFile::s_errorKeyNotFound;
      case IniFile::errorEndOfFile: return IniFile::s_errorEndOfFile;
      case IniFile::errorUnknownError: return IniFile::s_errorUnknownError;
      default: return IniFile::s_errorUnknownValue;
  }
}