/** Find an arbitrary number of delimited strings at the end of a file. This * function provides the core of our last-lines-loader implementation. * * @param fd File descriptor to operate on. This must be opened for reading. * * @param bytes Delimiter sequence. * * @param bytes_length Number of significant bytes in @p bytes. * * @param count Number of tail sequences to find. * * @param out Destination string array. */ static inline size_t find_tail_sequences(int fd, const char* bytes, size_t bytes_length, size_t count, wxArrayString& out) { size_t count_added ( 0 ); /* We overlap the file reads a little to avoid splitting (and thus missing) the delimiter sequence. */ const size_t read_overlap ( bytes_length - 1 ); const size_t read_size ( BUFSIZ ); const off_t log_length ( lseek(fd, 0, SEEK_END) ); bool have_last_pos ( false ); char buf[read_size]; off_t last_found_pos ( 0 ); off_t last_read_position ( log_length + read_overlap ); /* We read `read_size'-byte blocks of the file, starting at the end and working backwards. */ while ( count_added < count && last_read_position > 0 ) { off_t read_position ( next_read_position(last_read_position, read_size, read_overlap) ); size_t bytes_read ( pread(fd, buf, std::min(static_cast<off_t>(read_size), last_read_position - read_position), read_position) ); /* In each block, we search for `bytes', starting at the end. */ for ( ssize_t i = bytes_read - read_overlap - 1; i >= 0; i-- ) { if ( !strncmp((buf + i), bytes, bytes_length) ) { off_t this_found_pos ( read_position + i ); if ( have_last_pos && count_added < count ) { size_t line_length ( last_found_pos - this_found_pos - bytes_length ); if ( line_length > 0 ) { char* source ( NULL ); if ( last_found_pos >= read_position + (off_t) bytes_read ) { source = new char[ line_length + 1]; memset(source, 0, line_length + 1); if ( pread(fd, source, line_length, this_found_pos + bytes_length) < (ssize_t) line_length ) { wxLogWarning(_T("ChatLog::find_tail_sequences: Read-byte count less than expected")); } } else { source = buf + i + bytes_length; } if ( strncmp(source, "##", 2) != 0 ) { out.Insert(wxString(L'\0', 0), 0); wxLogMessage(_T("ChatLog::find_tail_sequences: fetching write buffer for %lu bytes"), sizeof(wxChar) * (line_length + 1)); #if !defined(HAVE_WX28) || defined(SL_QT_MODE) wxStringBufferLength outzero_buf(out[0], sizeof(wxChar) * (line_length + 1)); wxConvUTF8.ToWChar(outzero_buf, line_length, source); outzero_buf.SetLength(line_length); #else wxConvUTF8.MB2WC(out[0].GetWriteBuf(sizeof(wxChar) * (line_length + 1)), source, line_length); out[0].UngetWriteBuf(line_length); #endif ++count_added; } if ( last_found_pos >= read_position + (off_t) bytes_read ) delete[] source; if ( count_added >= count ) i = -1; /* short-circuit the `for' loop. */ } } last_found_pos = this_found_pos; have_last_pos = true; i -= bytes_length - 1; } } last_read_position = read_position; } return count_added; }
/** Find an arbitrary number of delimited strings at the end of a file. This * function provides the core of our last-lines-loader implementation. * * @param fd File descriptor to operate on. This must be opened for reading. * * @param bytes Delimiter sequence. * * @param bytes_length Number of significant bytes in @p bytes. * * @param count Number of tail sequences to find. * * @param out Destination string array. */ static size_t find_tail_sequences(wxFile& fd, const char* bytes, size_t bytes_length, size_t count, wxArrayString& out) { size_t count_added(0); /* We overlap the file reads a little to avoid splitting (and thus missing) the delimiter sequence. */ const size_t read_overlap = bytes_length - 1; const size_t read_size = BUFSIZ; const off_t log_length = fd.SeekEnd(); bool have_last_pos = false; char buf[read_size]; off_t last_found_pos = 0; off_t last_read_position = log_length + read_overlap; /* We read `read_size'-byte blocks of the file, starting at the end and working backwards. */ while (count_added < count && last_read_position > 0) { const off_t read_position = next_read_position(last_read_position, read_size, read_overlap); const size_t bytes_read = readblock(fd, buf, std::min(static_cast<off_t>(read_size), last_read_position - read_position), read_position); /* In each block, we search for `bytes', starting at the end. */ for (ssize_t i = bytes_read - read_overlap - 1; i >= 0; i--) { if (strncmp((buf + i), bytes, bytes_length) == 0) { const off_t this_found_pos = read_position + i; if (have_last_pos) { const size_t line_length = last_found_pos - this_found_pos - bytes_length; if (line_length > 0) { char* source = NULL; if (last_found_pos >= read_position + (off_t)bytes_read) { source = new char[line_length + 1]; memset(source, 0, line_length + 1); if (readblock(fd, source, line_length, this_found_pos + bytes_length) < (ssize_t)line_length) { wxLogWarning(_T("ChatLog::find_tail_sequences: Read-byte count less than expected")); } } else { source = buf + i + bytes_length; } source[line_length] = 0; wxString tmp = wxString::FromUTF8(source); out.Insert(tmp, 0); if (last_found_pos >= read_position + (off_t)bytes_read) delete[] source; count_added++; if (count_added >= count) break; } } last_found_pos = this_found_pos; have_last_pos = true; i -= bytes_length - 1; } } last_read_position = read_position; } return count_added; }