2009-09-14 01:07:32 +00:00
|
|
|
/**
|
2013-05-03 06:02:03 +00:00
|
|
|
* Copyright (c) 2007-2012, Timothy Stack
|
|
|
|
*
|
|
|
|
* All rights reserved.
|
2013-05-28 04:35:00 +00:00
|
|
|
*
|
2013-05-03 06:02:03 +00:00
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions are met:
|
2013-05-28 04:35:00 +00:00
|
|
|
*
|
2013-05-03 06:02:03 +00:00
|
|
|
* * Redistributions of source code must retain the above copyright notice, this
|
|
|
|
* list of conditions and the following disclaimer.
|
|
|
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
|
|
|
* this list of conditions and the following disclaimer in the documentation
|
|
|
|
* and/or other materials provided with the distribution.
|
|
|
|
* * Neither the name of Timothy Stack nor the names of its contributors
|
|
|
|
* may be used to endorse or promote products derived from this software
|
|
|
|
* without specific prior written permission.
|
2013-05-28 04:35:00 +00:00
|
|
|
*
|
2013-05-03 06:02:03 +00:00
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY
|
|
|
|
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
|
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
|
|
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
|
|
|
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
|
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
|
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
|
|
|
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
|
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
*
|
2009-09-14 01:07:32 +00:00
|
|
|
* @file line_buffer.hh
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifndef __line_buffer_hh
|
|
|
|
#define __line_buffer_hh
|
|
|
|
|
|
|
|
#include <errno.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <zlib.h>
|
|
|
|
|
|
|
|
#include <exception>
|
|
|
|
|
2014-03-06 14:58:49 +00:00
|
|
|
#include "lnav_log.hh"
|
2009-09-14 01:07:32 +00:00
|
|
|
#include "auto_fd.hh"
|
|
|
|
#include "auto_mem.hh"
|
2014-02-01 14:41:11 +00:00
|
|
|
#include "shared_buffer.hh"
|
2009-09-14 01:07:32 +00:00
|
|
|
|
2014-03-15 11:40:58 +00:00
|
|
|
struct line_value {
|
|
|
|
char *lv_start;
|
|
|
|
size_t lv_len;
|
|
|
|
bool lv_partial;
|
|
|
|
|
|
|
|
void terminate() {
|
|
|
|
this->lv_start[this->lv_len] = '\0';
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
2009-09-14 01:07:32 +00:00
|
|
|
/**
|
|
|
|
* Buffer for reading whole lines out of file descriptors. The class presents
|
|
|
|
* a stateless interface, callers specify the offset where a line starts and
|
|
|
|
* the class takes care of caching the surrounding range and locating the
|
|
|
|
* delimiter.
|
|
|
|
*
|
|
|
|
* XXX A bit of a wheel reinvention, but I'm not sure how well the libraries
|
|
|
|
* handle non-blocking I/O...
|
|
|
|
*/
|
|
|
|
class line_buffer {
|
|
|
|
public:
|
2014-11-12 05:18:55 +00:00
|
|
|
static const size_t DEFAULT_LINE_BUFFER_SIZE = 256 * 1024;
|
2015-11-30 04:35:13 +00:00
|
|
|
static const size_t MAX_LINE_BUFFER_SIZE = 4 * 4 * DEFAULT_LINE_BUFFER_SIZE;
|
2009-09-14 01:07:32 +00:00
|
|
|
class error
|
2013-05-28 04:35:00 +00:00
|
|
|
: public std::exception {
|
2009-09-14 01:07:32 +00:00
|
|
|
public:
|
2013-05-28 04:35:00 +00:00
|
|
|
error(int err)
|
|
|
|
: e_err(err) { };
|
2009-09-14 01:07:32 +00:00
|
|
|
|
2013-05-28 04:35:00 +00:00
|
|
|
int e_err;
|
2009-09-14 01:07:32 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/** Construct an empty line_buffer. */
|
|
|
|
line_buffer();
|
|
|
|
|
|
|
|
virtual ~line_buffer();
|
|
|
|
|
|
|
|
/** @param fd The file descriptor that data should be pulled from. */
|
|
|
|
void set_fd(auto_fd &fd) throw (error);
|
|
|
|
|
|
|
|
/** @return The file descriptor that data should be pulled from. */
|
2013-04-17 16:27:12 +00:00
|
|
|
int get_fd() const { return this->lb_fd; };
|
2009-09-14 01:07:32 +00:00
|
|
|
|
2013-06-29 18:00:34 +00:00
|
|
|
time_t get_file_time() const { return this->lb_file_time; };
|
|
|
|
|
2009-09-14 01:07:32 +00:00
|
|
|
/**
|
|
|
|
* @return The size of the file or the amount of data pulled from a pipe.
|
|
|
|
*/
|
2014-02-19 13:58:31 +00:00
|
|
|
ssize_t get_file_size() const { return this->lb_file_size; };
|
2009-09-14 01:07:32 +00:00
|
|
|
|
2015-08-20 04:02:26 +00:00
|
|
|
bool is_pipe() const {
|
|
|
|
return !this->lb_seekable;
|
|
|
|
};
|
|
|
|
|
2015-04-22 05:25:54 +00:00
|
|
|
bool is_pipe_closed() const {
|
|
|
|
return !this->lb_seekable && (this->lb_file_size != -1);
|
|
|
|
};
|
|
|
|
|
2014-03-15 11:40:58 +00:00
|
|
|
bool is_compressed() const {
|
|
|
|
return this->lb_gz_file != NULL || this->lb_bz_file;
|
|
|
|
};
|
|
|
|
|
2013-05-28 04:35:00 +00:00
|
|
|
off_t get_read_offset(off_t off) const
|
|
|
|
{
|
|
|
|
if (this->lb_gz_file) {
|
|
|
|
return this->lb_gz_offset;
|
|
|
|
}
|
|
|
|
else{
|
|
|
|
return off;
|
|
|
|
}
|
2010-01-10 19:40:38 +00:00
|
|
|
};
|
|
|
|
|
2009-09-14 01:07:32 +00:00
|
|
|
/**
|
|
|
|
* Read up to the end of file or a given delimiter.
|
|
|
|
*
|
|
|
|
* @param offset_inout The offset in the file to start reading from. On
|
|
|
|
* return, it contains the offset where the next line should start or one
|
|
|
|
* past the size of the file.
|
|
|
|
* @param len_out On return, contains the length of the line, not including
|
|
|
|
* the delimiter.
|
|
|
|
* @param delim The character that splits lines in the input, defaults to a
|
|
|
|
* line feed.
|
|
|
|
* @return The address in the internal buffer where the line starts. The
|
2013-05-01 04:48:16 +00:00
|
|
|
* line is not NULL-terminated, but this method ensures there is room to NULL
|
2009-09-14 01:07:32 +00:00
|
|
|
* terminate the line. If any modifications are made to the line, such as
|
|
|
|
* NULL termination, the invalidate() must be called before re-reading the
|
|
|
|
* line to refresh the buffer.
|
|
|
|
*/
|
2014-03-15 11:40:58 +00:00
|
|
|
bool read_line(off_t &offset_inout, line_value &lv,
|
2014-02-19 13:58:31 +00:00
|
|
|
bool include_delim = false)
|
2014-02-01 14:41:11 +00:00
|
|
|
throw (error);
|
|
|
|
|
2014-10-20 05:16:40 +00:00
|
|
|
bool read_line(off_t &offset_inout, shared_buffer_ref &sbr, line_value *lv = NULL)
|
2014-02-01 14:41:11 +00:00
|
|
|
throw (error);
|
|
|
|
|
|
|
|
bool read_range(off_t offset, size_t len, shared_buffer_ref &sbr)
|
|
|
|
throw (error);
|
2009-09-14 01:07:32 +00:00
|
|
|
|
2015-12-23 06:44:42 +00:00
|
|
|
void clear()
|
2009-09-14 01:07:32 +00:00
|
|
|
{
|
2013-05-28 04:35:00 +00:00
|
|
|
this->lb_buffer_size = 0;
|
2009-09-14 01:07:32 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/** Release any resources held by this object. */
|
|
|
|
void reset()
|
|
|
|
{
|
2013-05-28 04:35:00 +00:00
|
|
|
this->lb_fd.reset();
|
2009-09-14 01:07:32 +00:00
|
|
|
|
2013-05-28 04:35:00 +00:00
|
|
|
this->lb_file_offset = 0;
|
|
|
|
this->lb_file_size = (size_t)-1;
|
|
|
|
this->lb_buffer_size = 0;
|
2012-09-22 23:15:15 +00:00
|
|
|
this->lb_last_line_offset = -1;
|
2009-09-14 01:07:32 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/** Check the invariants for this object. */
|
|
|
|
bool invariant(void)
|
|
|
|
{
|
2014-03-06 14:58:49 +00:00
|
|
|
require(this->lb_buffer != NULL);
|
|
|
|
require(this->lb_buffer_size <= this->lb_buffer_max);
|
2009-09-14 01:07:32 +00:00
|
|
|
|
2013-05-28 04:35:00 +00:00
|
|
|
return true;
|
2009-09-14 01:07:32 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param off The file offset to check for in the buffer.
|
|
|
|
* @return True if the given offset is cached in the buffer.
|
|
|
|
*/
|
2013-04-17 16:27:12 +00:00
|
|
|
bool in_range(off_t off) const
|
2009-09-14 01:07:32 +00:00
|
|
|
{
|
2013-05-28 04:35:00 +00:00
|
|
|
return this->lb_file_offset <= off &&
|
|
|
|
off < (int)(this->lb_file_offset + this->lb_buffer_size);
|
2009-09-14 01:07:32 +00:00
|
|
|
};
|
|
|
|
|
2011-05-14 01:27:33 +00:00
|
|
|
void resize_buffer(size_t new_max) throw (error);
|
|
|
|
|
2009-09-14 01:07:32 +00:00
|
|
|
/**
|
|
|
|
* Ensure there is enough room in the buffer to cache a range of data from
|
|
|
|
* the file. First, this method will check to see if there is enough room
|
|
|
|
* from where 'start' begins in the buffer to the maximum buffer size. If
|
|
|
|
* this is not enough, the currently cached data at 'start' will be moved
|
|
|
|
* to the beginning of the buffer, overwriting any cached data earlier in
|
|
|
|
* the file. Finally, if this is still not enough, the buffer will be
|
|
|
|
* reallocated to make more room.
|
|
|
|
*
|
|
|
|
* @param start The file offset of the start of the line.
|
|
|
|
* @param max_length The amount of data to be cached in the buffer.
|
|
|
|
*/
|
|
|
|
void ensure_available(off_t start, size_t max_length) throw (error);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Fill the buffer with the given range of data from the file.
|
|
|
|
*
|
|
|
|
* @param start The file offset where data should start to be read from the
|
|
|
|
* file.
|
|
|
|
* @param max_length The maximum amount of data to read from the file.
|
|
|
|
* @return True if any data was read from the file.
|
|
|
|
*/
|
|
|
|
bool fill_range(off_t start, size_t max_length) throw (error);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* After a successful fill, the cached data can be retrieved with this
|
|
|
|
* method.
|
|
|
|
*
|
|
|
|
* @param start The file offset to retrieve cached data for.
|
|
|
|
* @param avail_out On return, the amount of data currently cached at the
|
|
|
|
* given offset.
|
|
|
|
* @return A pointer to the start of the cached data in the internal
|
|
|
|
* buffer.
|
|
|
|
*/
|
|
|
|
char *get_range(off_t start, size_t &avail_out)
|
|
|
|
{
|
2013-05-28 04:35:00 +00:00
|
|
|
off_t buffer_offset = start - this->lb_file_offset;
|
|
|
|
char *retval;
|
2009-09-14 01:07:32 +00:00
|
|
|
|
2014-03-06 14:58:49 +00:00
|
|
|
require(buffer_offset >= 0);
|
2009-09-14 01:07:32 +00:00
|
|
|
|
2013-05-28 04:35:00 +00:00
|
|
|
retval = &this->lb_buffer[buffer_offset];
|
|
|
|
avail_out = this->lb_buffer_size - buffer_offset;
|
2009-09-14 01:07:32 +00:00
|
|
|
|
2013-05-28 04:35:00 +00:00
|
|
|
return retval;
|
2009-09-14 01:07:32 +00:00
|
|
|
};
|
|
|
|
|
2014-02-01 14:41:11 +00:00
|
|
|
shared_buffer lb_share_manager;
|
|
|
|
|
2009-09-14 01:07:32 +00:00
|
|
|
auto_fd lb_fd; /*< The file to read data from. */
|
2013-05-28 04:35:00 +00:00
|
|
|
gzFile lb_gz_file; /*< File handle for gzipped files. */
|
|
|
|
bool lb_bz_file; /*< Flag set for bzip2 compressed files. */
|
|
|
|
off_t lb_gz_offset; /*< The offset into the compressed file. */
|
|
|
|
|
2009-09-14 01:07:32 +00:00
|
|
|
auto_mem<char> lb_buffer; /*< The internal buffer where data is cached */
|
|
|
|
|
2014-02-19 13:58:31 +00:00
|
|
|
ssize_t lb_file_size; /*<
|
2013-05-28 04:35:00 +00:00
|
|
|
* The size of the file. When lb_fd refers to
|
|
|
|
* a pipe, this is set to the amount of data
|
|
|
|
* read from the pipe when EOF is reached.
|
|
|
|
*/
|
|
|
|
off_t lb_file_offset; /*<
|
|
|
|
* Data cached in the buffer comes from this
|
|
|
|
* offset in the file.
|
|
|
|
*/
|
2013-06-29 18:00:34 +00:00
|
|
|
time_t lb_file_time;
|
2014-02-21 04:00:51 +00:00
|
|
|
ssize_t lb_buffer_size; /*< The amount of cached data in the buffer. */
|
2014-02-19 13:58:31 +00:00
|
|
|
ssize_t lb_buffer_max; /*< The size of the buffer memory. */
|
2012-06-05 20:18:59 +00:00
|
|
|
bool lb_seekable; /*< Flag set for seekable file descriptors. */
|
2012-09-22 23:15:15 +00:00
|
|
|
off_t lb_last_line_offset; /*< */
|
2009-09-14 01:07:32 +00:00
|
|
|
};
|
|
|
|
#endif
|