}
/*}}}*/
-class FileFdPrivate { /*{{{*/
+struct APT_HIDDEN simple_buffer { /*{{{*/
+ static constexpr size_t buffersize_max = 4096;
+ unsigned long long bufferstart = 0;
+ unsigned long long bufferend = 0;
+ char buffer[buffersize_max];
+
+ const char *get() const { return buffer + bufferstart; }
+ char *get() { return buffer + bufferstart; }
+ bool empty() const { return bufferend <= bufferstart; }
+ bool full() const { return bufferend == buffersize_max; }
+ unsigned long long size() const { 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;
+ }
+ ssize_t write(const void *from, unsigned long long requested_size) APT_MUSTCHECK
+ {
+ if (buffersize_max - size() < requested_size)
+ requested_size = buffersize_max - size();
+ memcpy(buffer + bufferend, from, requested_size);
+ bufferend += requested_size;
+ if (bufferstart == bufferend)
+ bufferstart = bufferend = 0;
+ return requested_size;
+ }
+};
+ /*}}}*/
+
+class APT_HIDDEN FileFdPrivate { /*{{{*/
+ friend class BufferedWriteFileFdPrivate;
protected:
FileFd * const filefd;
- size_t buffersize_max = 0;
- std::unique_ptr<char[]> buffer;
- unsigned long long buffersize = 0;
-public:
+ simple_buffer buffer;
int compressed_fd;
pid_t compressor_pid;
bool is_pipe;
APT::Configuration::Compressor compressor;
unsigned int openmode;
unsigned long long seekpos;
- explicit FileFdPrivate(FileFd * const pfilefd) : filefd(pfilefd), buffer(nullptr),
+public:
+
+ explicit FileFdPrivate(FileFd * const pfilefd) : filefd(pfilefd),
compressed_fd(-1), compressor_pid(-1), is_pipe(false),
openmode(0), seekpos(0) {};
+ virtual APT::Configuration::Compressor get_compressor() const
+ {
+ return compressor;
+ }
+ virtual void set_compressor(APT::Configuration::Compressor const &compressor)
+ {
+ this->compressor = compressor;
+ }
+ virtual unsigned int get_openmode() const
+ {
+ return openmode;
+ }
+ virtual void set_openmode(unsigned int openmode)
+ {
+ this->openmode = openmode;
+ }
+ virtual bool get_is_pipe() const
+ {
+ return is_pipe;
+ }
+ virtual void set_is_pipe(bool is_pipe)
+ {
+ this->is_pipe = is_pipe;
+ }
+ virtual unsigned long long get_seekpos() const
+ {
+ return seekpos;
+ }
+ virtual void set_seekpos(unsigned long long seekpos)
+ {
+ this->seekpos = seekpos;
+ }
virtual bool InternalOpen(int const iFd, unsigned int const Mode) = 0;
ssize_t InternalRead(void * To, unsigned long long Size)
{
- unsigned long long tmp = 0;
- if (buffersize != 0)
+ // Drain the buffer if needed.
+ if (buffer.empty() == false)
{
- if (buffersize >= Size)
- {
- memcpy(To, buffer.get(), Size);
- if (buffersize == Size)
- {
- buffersize = 0;
- }
- else
- {
- buffersize -= Size;
- memmove(buffer.get(), buffer.get() + Size, buffersize);
- }
- return Size;
- }
- else
- {
- memcpy(To, buffer.get(), buffersize);
- Size -= buffersize;
- To = static_cast<char *>(To) + buffersize;
- tmp = buffersize;
- buffersize = 0;
- }
+ return buffer.read(To, Size);
}
- return InternalUnbufferedRead(To, Size) + tmp;
+ 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")); }
{
if (unlikely(Size == 0))
return nullptr;
+ // Read one byte less than buffer size to have space for trailing 0.
--Size;
- To[0] = '\0';
- if (unlikely(Size == 0))
- return To;
+
char * const InitialTo = To;
- do {
- if (buffersize == 0)
+ while (Size > 0) {
+ if (buffer.empty() == true)
{
- if (buffer.get() == nullptr)
- {
- buffer.reset(new char[Size]);
- buffersize_max = Size;
- }
+ buffer.reset();
unsigned long long actualread = 0;
- if (filefd->Read(buffer.get(), buffersize_max, &actualread) == false)
+ if (filefd->Read(buffer.get(), buffer.buffersize_max, &actualread) == false)
return nullptr;
- buffersize = actualread;
- if (buffersize == 0)
+ buffer.bufferend = actualread;
+ if (buffer.size() == 0)
{
- buffer.reset(nullptr);
- buffersize_max = 0;
if (To == InitialTo)
return nullptr;
break;
filefd->Flags &= ~FileFd::HitEof;
}
- unsigned long long const OutputSize = std::min(Size, buffersize);
+ 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)
- {
- size_t length = (newline - buffer.get());
- ++length;
- memcpy(To, buffer.get(), length);
- To += length;
- if (length < buffersize)
- {
- buffersize -= length;
- memmove(buffer.get(), buffer.get() + length, buffersize);
- }
- else
- buffersize = 0;
break;
- }
- else
- {
- memcpy(To, buffer.get(), OutputSize);
- To += OutputSize;
- Size -= OutputSize;
- buffersize -= OutputSize;
- memmove(buffer.get(), buffer.get() + OutputSize, buffersize);
- }
- } while (Size > 0);
+ }
*To = '\0';
return InitialTo;
}
+ virtual bool InternalFlush()
+ {
+ return true;
+ }
virtual ssize_t InternalWrite(void const * const From, unsigned long long const Size) = 0;
virtual bool InternalWriteError() { return filefd->FileFdErrno("write",_("Write error")); }
virtual bool InternalSeek(unsigned long long const To)
if (filefd->OpenInternDescriptor(openmode, compressor) == false)
return filefd->FileFdError("Seek on file %s because it couldn't be reopened", filefd->FileName.c_str());
- buffersize = 0;
+ 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 - buffersize;
+ return seekpos - buffer.size();
}
virtual unsigned long long InternalSize()
{
virtual ~FileFdPrivate() {}
};
/*}}}*/
-class GzipFileFdPrivate: public FileFdPrivate { /*{{{*/
+class APT_HIDDEN BufferedWriteFileFdPrivate : public FileFdPrivate { /*{{{*/
+protected:
+ FileFdPrivate *wrapped;
+ simple_buffer writebuffer;
+
+public:
+
+ explicit BufferedWriteFileFdPrivate(FileFdPrivate *Priv) :
+ FileFdPrivate(Priv->filefd), wrapped(Priv) {};
+
+ virtual APT::Configuration::Compressor get_compressor() const override
+ {
+ return wrapped->get_compressor();
+ }
+ virtual void set_compressor(APT::Configuration::Compressor const &compressor) override
+ {
+ return wrapped->set_compressor(compressor);
+ }
+ virtual unsigned int get_openmode() const override
+ {
+ return wrapped->get_openmode();
+ }
+ virtual void set_openmode(unsigned int openmode) override
+ {
+ return wrapped->set_openmode(openmode);
+ }
+ virtual bool get_is_pipe() const override
+ {
+ return wrapped->get_is_pipe();
+ }
+ virtual void set_is_pipe(bool is_pipe) override
+ {
+ FileFdPrivate::set_is_pipe(is_pipe);
+ wrapped->set_is_pipe(is_pipe);
+ }
+ virtual unsigned long long get_seekpos() const override
+ {
+ return wrapped->get_seekpos();
+ }
+ virtual void set_seekpos(unsigned long long seekpos) override
+ {
+ return wrapped->set_seekpos(seekpos);
+ }
+ virtual bool InternalOpen(int const iFd, unsigned int const Mode) override
+ {
+ if (InternalFlush() == false)
+ return false;
+ return wrapped->InternalOpen(iFd, Mode);
+ }
+ virtual ssize_t InternalUnbufferedRead(void * const To, unsigned long long const Size) override
+ {
+ if (InternalFlush() == false)
+ return -1;
+ return wrapped->InternalUnbufferedRead(To, Size);
+
+ }
+ virtual bool InternalReadError() override
+ {
+ return wrapped->InternalReadError();
+ }
+ virtual char * InternalReadLine(char * To, unsigned long long Size) override
+ {
+ if (InternalFlush() == false)
+ return nullptr;
+ return wrapped->InternalReadLine(To, Size);
+ }
+ virtual bool InternalFlush() override
+ {
+ while (writebuffer.empty() == false) {
+ auto written = wrapped->InternalWrite(writebuffer.get(),
+ writebuffer.size());
+ // Ignore interrupted syscalls
+ if (written < 0 && errno == EINTR)
+ continue;
+ if (written < 0)
+ return false;
+
+ writebuffer.bufferstart += written;
+ }
+
+ writebuffer.reset();
+ return true;
+ }
+ virtual ssize_t InternalWrite(void const * const From, unsigned long long const Size) override
+ {
+ size_t written = 0;
+
+ while (written < Size) {
+ auto buffered = writebuffer.write(static_cast<char const*>(From) + written, Size - written);
+
+ written += buffered;
+
+ if (writebuffer.full() && InternalFlush() == false)
+ return -1;
+ }
+
+ return written;
+ }
+ virtual bool InternalWriteError()
+ {
+ return wrapped->InternalWriteError();
+ }
+ virtual bool InternalSeek(unsigned long long const To)
+ {
+ if (InternalFlush() == false)
+ return false;
+ return wrapped->InternalSeek(To);
+ }
+ virtual bool InternalSkip(unsigned long long Over)
+ {
+ if (InternalFlush() == false)
+ return false;
+ return wrapped->InternalSkip(Over);
+ }
+ virtual bool InternalTruncate(unsigned long long const Size)
+ {
+ if (InternalFlush() == false)
+ return false;
+ return wrapped->InternalTruncate(Size);
+ }
+ virtual unsigned long long InternalTell()
+ {
+ if (InternalFlush() == false)
+ return -1;
+ return wrapped->InternalTell();
+ }
+ virtual unsigned long long InternalSize()
+ {
+ if (InternalFlush() == false)
+ return -1;
+ return wrapped->InternalSize();
+ }
+ virtual bool InternalClose(std::string const &FileName)
+ {
+ return wrapped->InternalClose(FileName);
+ }
+ virtual bool InternalAlwaysAutoClose() const
+ {
+ return wrapped->InternalAlwaysAutoClose();
+ }
+ virtual ~BufferedWriteFileFdPrivate()
+ {
+ delete wrapped;
+ }
+};
+ /*}}}*/
+class APT_HIDDEN GzipFileFdPrivate: public FileFdPrivate { /*{{{*/
#ifdef HAVE_ZLIB
public:
gzFile gz;
if (res != (off_t)To)
return filefd->FileFdError("Unable to seek to %llu", To);
seekpos = To;
- buffersize = 0;
+ buffer.reset();
return true;
}
virtual bool InternalSkip(unsigned long long Over) override
{
- if (Over >= buffersize)
+ if (Over >= buffer.size())
{
- Over -= buffersize;
- buffersize = 0;
+ Over -= buffer.size();
+ buffer.reset();
}
else
{
- buffersize -= Over;
- memmove(buffer.get(), buffer.get() + Over, buffersize);
+ buffer.bufferstart += Over;
return true;
}
if (Over == 0)
}
virtual unsigned long long InternalTell() override
{
- return gztell(gz) - buffersize;
+ return gztell(gz) - buffer.size();
}
virtual unsigned long long InternalSize() override
{
#endif
};
/*}}}*/
-class Bz2FileFdPrivate: public FileFdPrivate { /*{{{*/
+class APT_HIDDEN Bz2FileFdPrivate: public FileFdPrivate { /*{{{*/
#ifdef HAVE_BZ2
BZFILE* bz2;
public:
#endif
};
/*}}}*/
-class LzmaFileFdPrivate: public FileFdPrivate { /*{{{*/
+class APT_HIDDEN LzmaFileFdPrivate: public FileFdPrivate { /*{{{*/
#ifdef HAVE_LZMA
struct LZMAFILE {
FILE* file;
#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 */
{
SetCloseExec(Pipe[J],true);
compressed_fd = filefd->iFd;
- is_pipe = true;
+ set_is_pipe(true);
if (Comp == true)
filefd->iFd = Pipe[1];
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 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 (buffersize != 0)
+ if (buffer.size() != 0)
{
- lseek(filefd->iFd, -buffersize, SEEK_CUR);
- buffersize = 0;
+ lseek(filefd->iFd, -buffer.size(), SEEK_CUR);
+ buffer.reset();
}
return write(filefd->iFd, From, Size);
}
if (res != (off_t)To)
return filefd->FileFdError("Unable to seek to %llu", To);
seekpos = To;
- buffersize = 0;
+ buffer.reset();
return true;
}
virtual bool InternalSkip(unsigned long long Over) override
{
- if (Over >= buffersize)
+ if (Over >= buffer.size())
{
- Over -= buffersize;
- buffersize = 0;
+ Over -= buffer.size();
+ buffer.reset();
}
else
{
- buffersize -= Over;
- memmove(buffer.get(), buffer.get() + Over, buffersize);
+ buffer.bufferstart += Over;
return true;
}
if (Over == 0)
}
virtual bool InternalTruncate(unsigned long long const To) override
{
- if (buffersize != 0)
+ if (buffer.size() != 0)
{
unsigned long long const seekpos = lseek(filefd->iFd, 0, SEEK_CUR);
- if ((seekpos - buffersize) >= To)
- buffersize = 0;
+ if ((seekpos - buffer.size()) >= To)
+ buffer.reset();
else if (seekpos >= To)
- buffersize = (To - seekpos);
+ buffer.bufferend = (To - seekpos) + buffer.bufferstart;
else
- buffersize = 0;
+ buffer.reset();
}
if (ftruncate(filefd->iFd, To) != 0)
return filefd->FileFdError("Unable to truncate to %llu",To);
}
virtual unsigned long long InternalTell() override
{
- return lseek(filefd->iFd,0,SEEK_CUR) - buffersize;
+ return lseek(filefd->iFd,0,SEEK_CUR) - buffer.size();
}
virtual unsigned long long InternalSize() override
{
else
d = new PipedFileFdPrivate(this);
- d->openmode = Mode;
- d->compressor = compressor;
+ if (Mode & BufferedWrite)
+ d = new BufferedWriteFileFdPrivate(d);
+
+ d->set_openmode(Mode);
+ d->set_compressor(compressor);
if ((Flags & AutoClose) != AutoClose && d->InternalAlwaysAutoClose())
{
// Need to duplicate fd here or gz/bz2 close for cleanup will close the fd as well
To = (char *)To + Res;
Size -= Res;
if (d != NULL)
- d->seekpos += Res;
+ d->set_seekpos(d->get_seekpos() + Res);
if (Actual != 0)
*Actual += Res;
}
return d->InternalReadLine(To, Size);
}
/*}}}*/
+// FileFd::Flush - Flush the file /*{{{*/
+bool FileFd::Flush()
+{
+ if (d == nullptr)
+ return true;
+
+ return d->InternalFlush();
+}
+ /*}}}*/
// FileFd::Write - Write to the file /*{{{*/
bool FileFd::Write(const void *From,unsigned long long Size)
{
From = (char const *)From + Res;
Size -= Res;
if (d != NULL)
- d->seekpos += Res;
+ d->set_seekpos(d->get_seekpos() + Res);
}
if (Size == 0)
off_t const Res = d->InternalTell();
if (Res == (off_t)-1)
FileFdErrno("lseek","Failed to determine the current file position");
- d->seekpos = Res;
+ d->set_seekpos(Res);
return Res;
}
/*}}}*/
static bool StatFileFd(char const * const msg, int const iFd, std::string const &FileName, struct stat &Buf, FileFdPrivate * const d) /*{{{*/
{
- bool ispipe = (d != NULL && d->is_pipe == true);
+ bool ispipe = (d != NULL && d->get_is_pipe() == true);
if (ispipe == false)
{
if (fstat(iFd,&Buf) != 0)
// we set it here, too, as we get the info here for free
// in theory the Open-methods should take care of it already
if (d != NULL)
- d->is_pipe = true;
+ d->set_is_pipe(true);
if (stat(FileName.c_str(), &Buf) != 0)
return _error->Errno("fstat", "Unable to determine %s for file %s", msg, FileName.c_str());
}
/* */
bool FileFd::Close()
{
+ if (Flush() == false)
+ return false;
if (iFd == -1)
return true;