}
/*}}}*/
-class FileFdPrivate { /*{{{*/
+class APT_HIDDEN FileFdPrivate { /*{{{*/
protected:
FileFd * const filefd;
+ struct simple_buffer {
+ static constexpr size_t buffersize_max = 4096;
+ unsigned long long bufferstart = 0;
+ unsigned long long bufferend = 0;
+ char buffer[buffersize_max];
+
+ char *get() { return buffer + bufferstart; }
+ bool empty() { return bufferend <= bufferstart; }
+ unsigned long long size() { return bufferend-bufferstart; }
+ void reset() { bufferend = bufferstart = 0; }
+ ssize_t read(void *to, unsigned long long requested_size) APT_MUSTCHECK
+ {
+ if (size() < requested_size)
+ requested_size = size();
+ memcpy(to, buffer + bufferstart, requested_size);
+ bufferstart += requested_size;
+ if (bufferstart == bufferend)
+ bufferstart = bufferend = 0;
+ return requested_size;
+ }
+ } buffer;
public:
int compressed_fd;
pid_t compressor_pid;
APT::Configuration::Compressor compressor;
unsigned int openmode;
unsigned long long seekpos;
- FileFdPrivate(FileFd * const pfilefd) : filefd(pfilefd),
+ explicit FileFdPrivate(FileFd * const pfilefd) : filefd(pfilefd),
compressed_fd(-1), compressor_pid(-1), is_pipe(false),
openmode(0), seekpos(0) {};
virtual bool InternalOpen(int const iFd, unsigned int const Mode) = 0;
- virtual ssize_t InternalRead(void * const To, unsigned long long const Size) = 0;
- virtual bool InternalReadError() { return filefd->FileFdErrno("read",_("Read error")); }
- virtual char * InternalReadLine(char * const To, unsigned long long const Size)
+ ssize_t InternalRead(void * To, unsigned long long Size)
{
- unsigned long long read = 0;
- while ((Size - 1) != read)
+ // Drain the buffer if needed.
+ if (buffer.empty() == false)
{
- unsigned long long done = 0;
- if (filefd->Read(To + read, 1, &done) == false)
- return NULL;
- if (done == 0)
- break;
- if (To[read++] == '\n')
- break;
+ return buffer.read(To, Size);
}
- if (read == 0)
- return NULL;
- To[read] = '\0';
- return To;
+ return InternalUnbufferedRead(To, Size);
+ }
+ virtual ssize_t InternalUnbufferedRead(void * const To, unsigned long long const Size) = 0;
+ virtual bool InternalReadError() { return filefd->FileFdErrno("read",_("Read error")); }
+ virtual char * InternalReadLine(char * To, unsigned long long Size)
+ {
+ if (unlikely(Size == 0))
+ return nullptr;
+ --Size;
+ To[0] = '\0';
+ if (unlikely(Size == 0))
+ return To;
+ char * const InitialTo = To;
+
+ do {
+ if (buffer.empty() == true)
+ {
+ buffer.reset();
+ unsigned long long actualread = 0;
+ if (filefd->Read(buffer.get(), buffer.buffersize_max, &actualread) == false)
+ return nullptr;
+ buffer.bufferend = actualread;
+ if (buffer.size() == 0)
+ {
+ if (To == InitialTo)
+ return nullptr;
+ break;
+ }
+ filefd->Flags &= ~FileFd::HitEof;
+ }
+
+ unsigned long long const OutputSize = std::min(Size, buffer.size());
+ char const * const newline = static_cast<char const * const>(memchr(buffer.get(), '\n', OutputSize));
+ // Read until end of line or up to Size bytes from the buffer.
+ unsigned long long actualread = buffer.read(To,
+ (newline != nullptr)
+ ? (newline - buffer.get()) + 1
+ : OutputSize);
+ To += actualread;
+ Size -= actualread;
+ if (newline != nullptr)
+ break;
+ } while (Size > 0);
+ *To = '\0';
+ return InitialTo;
}
virtual ssize_t InternalWrite(void const * const From, unsigned long long const Size) = 0;
virtual bool InternalWriteError() { return filefd->FileFdErrno("write",_("Write error")); }
if (filefd->OpenInternDescriptor(openmode, compressor) == false)
return filefd->FileFdError("Seek on file %s because it couldn't be reopened", filefd->FileName.c_str());
+ buffer.reset();
if (To != 0)
return filefd->Skip(To);
// seeking around, but not all users of FileFd use always Seek() and co
// so d->seekpos isn't always true and we can just use it as a hint if
// we have nothing else, but not always as an authority…
- return seekpos;
+ return seekpos - buffer.size();
}
virtual unsigned long long InternalSize()
{
virtual ~FileFdPrivate() {}
};
/*}}}*/
-class GzipFileFdPrivate: public FileFdPrivate { /*{{{*/
+class APT_HIDDEN GzipFileFdPrivate: public FileFdPrivate { /*{{{*/
#ifdef HAVE_ZLIB
public:
gzFile gz;
filefd->Flags |= FileFd::Compressed;
return gz != nullptr;
}
- virtual ssize_t InternalRead(void * const To, unsigned long long const Size) override
+ virtual ssize_t InternalUnbufferedRead(void * const To, unsigned long long const Size) override
{
return gzread(gz, To, Size);
}
return filefd->FileFdError("gzread: %s (%d: %s)", _("Read error"), err, errmsg);
return FileFdPrivate::InternalReadError();
}
- virtual char * InternalReadLine(char * const To, unsigned long long const Size) override
+ virtual char * InternalReadLine(char * To, unsigned long long Size) override
{
return gzgets(gz, To, Size);
}
off_t const res = gzseek(gz, To, SEEK_SET);
if (res != (off_t)To)
return filefd->FileFdError("Unable to seek to %llu", To);
-
seekpos = To;
+ buffer.reset();
return true;
}
virtual bool InternalSkip(unsigned long long Over) override
{
+ if (Over >= buffer.size())
+ {
+ Over -= buffer.size();
+ buffer.reset();
+ }
+ else
+ {
+ buffer.bufferstart += Over;
+ return true;
+ }
+ if (Over == 0)
+ return true;
off_t const res = gzseek(gz, Over, SEEK_CUR);
if (res < 0)
return filefd->FileFdError("Unable to seek ahead %llu",Over);
}
virtual unsigned long long InternalTell() override
{
- return gztell(gz);
+ return gztell(gz) - buffer.size();
}
virtual unsigned long long InternalSize() override
{
return true;
}
- GzipFileFdPrivate(FileFd * const filefd) : FileFdPrivate(filefd), gz(nullptr) {}
+ explicit GzipFileFdPrivate(FileFd * const filefd) : FileFdPrivate(filefd), gz(nullptr) {}
virtual ~GzipFileFdPrivate() { InternalClose(""); }
#endif
};
/*}}}*/
-class Bz2FileFdPrivate: public FileFdPrivate { /*{{{*/
+class APT_HIDDEN Bz2FileFdPrivate: public FileFdPrivate { /*{{{*/
#ifdef HAVE_BZ2
BZFILE* bz2;
public:
filefd->Flags |= FileFd::Compressed;
return bz2 != nullptr;
}
- virtual ssize_t InternalRead(void * const To, unsigned long long const Size) override
+ virtual ssize_t InternalUnbufferedRead(void * const To, unsigned long long const Size) override
{
return BZ2_bzread(bz2, To, Size);
}
return true;
}
- Bz2FileFdPrivate(FileFd * const filefd) : FileFdPrivate(filefd), bz2(nullptr) {}
+ explicit Bz2FileFdPrivate(FileFd * const filefd) : FileFdPrivate(filefd), bz2(nullptr) {}
virtual ~Bz2FileFdPrivate() { InternalClose(""); }
#endif
};
/*}}}*/
-class LzmaFileFdPrivate: public FileFdPrivate { /*{{{*/
+class APT_HIDDEN LzmaFileFdPrivate: public FileFdPrivate { /*{{{*/
#ifdef HAVE_LZMA
struct LZMAFILE {
FILE* file;
}
return true;
}
- virtual ssize_t InternalRead(void * const To, unsigned long long const Size) override
+ virtual ssize_t InternalUnbufferedRead(void * const To, unsigned long long const Size) override
{
ssize_t Res;
if (lzma->eof == true)
return true;
}
- LzmaFileFdPrivate(FileFd * const filefd) : FileFdPrivate(filefd), lzma(nullptr) {}
+ explicit LzmaFileFdPrivate(FileFd * const filefd) : FileFdPrivate(filefd), lzma(nullptr) {}
virtual ~LzmaFileFdPrivate() { InternalClose(""); }
#endif
};
/*}}}*/
-class PipedFileFdPrivate: public FileFdPrivate /*{{{*/
+class APT_HIDDEN PipedFileFdPrivate: public FileFdPrivate /*{{{*/
/* if we don't have a specific class dealing with library calls, we (un)compress
by executing a specified binary and pipe in/out what we need */
{
return true;
}
- virtual ssize_t InternalRead(void * const To, unsigned long long const Size) override
+ virtual ssize_t InternalUnbufferedRead(void * const To, unsigned long long const Size) override
{
return read(filefd->iFd, To, Size);
}
compressor_pid = -1;
return Ret;
}
- PipedFileFdPrivate(FileFd * const filefd) : FileFdPrivate(filefd) {}
+ explicit PipedFileFdPrivate(FileFd * const filefd) : FileFdPrivate(filefd) {}
virtual ~PipedFileFdPrivate() { InternalClose(""); }
};
/*}}}*/
-class DirectFileFdPrivate: public FileFdPrivate /*{{{*/
+class APT_HIDDEN DirectFileFdPrivate: public FileFdPrivate /*{{{*/
{
public:
virtual bool InternalOpen(int const, unsigned int const) override { return true; }
- virtual ssize_t InternalRead(void * const To, unsigned long long const Size) override
+ virtual ssize_t InternalUnbufferedRead(void * const To, unsigned long long const Size) override
{
return read(filefd->iFd, To, Size);
}
-
virtual ssize_t InternalWrite(void const * const From, unsigned long long const Size) override
{
+ // files opened read+write are strange and only really "supported" for direct files
+ if (buffer.size() != 0)
+ {
+ lseek(filefd->iFd, -buffer.size(), SEEK_CUR);
+ buffer.reset();
+ }
return write(filefd->iFd, From, Size);
}
virtual bool InternalSeek(unsigned long long const To) override
if (res != (off_t)To)
return filefd->FileFdError("Unable to seek to %llu", To);
seekpos = To;
+ buffer.reset();
return true;
}
virtual bool InternalSkip(unsigned long long Over) override
{
+ if (Over >= buffer.size())
+ {
+ Over -= buffer.size();
+ buffer.reset();
+ }
+ else
+ {
+ buffer.bufferstart += Over;
+ return true;
+ }
+ if (Over == 0)
+ return true;
off_t const res = lseek(filefd->iFd, Over, SEEK_CUR);
if (res < 0)
return filefd->FileFdError("Unable to seek ahead %llu",Over);
}
virtual bool InternalTruncate(unsigned long long const To) override
{
+ if (buffer.size() != 0)
+ {
+ unsigned long long const seekpos = lseek(filefd->iFd, 0, SEEK_CUR);
+ if ((seekpos - buffer.size()) >= To)
+ buffer.reset();
+ else if (seekpos >= To)
+ buffer.bufferend = (To - seekpos) + buffer.bufferstart;
+ else
+ buffer.reset();
+ }
if (ftruncate(filefd->iFd, To) != 0)
return filefd->FileFdError("Unable to truncate to %llu",To);
return true;
}
virtual unsigned long long InternalTell() override
{
- return lseek(filefd->iFd,0,SEEK_CUR);
+ return lseek(filefd->iFd,0,SEEK_CUR) - buffer.size();
}
virtual unsigned long long InternalSize() override
{
virtual bool InternalClose(std::string const &) override { return true; }
virtual bool InternalAlwaysAutoClose() const override { return false; }
- DirectFileFdPrivate(FileFd * const filefd) : FileFdPrivate(filefd) {}
+ explicit DirectFileFdPrivate(FileFd * const filefd) : FileFdPrivate(filefd) {}
virtual ~DirectFileFdPrivate() { InternalClose(""); }
};
/*}}}*/