]> git.saurik.com Git - apt.git/blobdiff - apt-pkg/contrib/fileutl.cc
simple_buffer::write: Use free() instead of maxsize - size()
[apt.git] / apt-pkg / contrib / fileutl.cc
index 7f3ed673a13144e7563a7c8e136d57371c98415a..004df3fe8d33ffc8e793543570723ba75b3198c0 100644 (file)
@@ -63,6 +63,9 @@
 #ifdef HAVE_LZMA
        #include <lzma.h>
 #endif
+#ifdef HAVE_LZ4
+       #include <lz4frame.h>
+#endif
 #include <endian.h>
 #include <stdint.h>
 
@@ -75,6 +78,9 @@
 
 using namespace std;
 
+/* Should be a multiple of the common page size (4096) */
+static constexpr unsigned long long APT_BUFFER_SIZE = 64 * 1024;
+
 // RunScripts - Run a set of scripts from a configuration subtree      /*{{{*/
 // ---------------------------------------------------------------------
 /* */
@@ -160,7 +166,7 @@ bool CopyFile(FileFd &From,FileFd &To)
       return false;
 
    // Buffered copy between fds
-   constexpr size_t BufSize = 64000;
+   constexpr size_t BufSize = APT_BUFFER_SIZE;
    std::unique_ptr<unsigned char[]> Buf(new unsigned char[BufSize]);
    unsigned long long ToRead = 0;
    do {
@@ -920,16 +926,35 @@ bool ChangeOwnerAndPermissionOfFile(char const * const requester, char const * c
                                                                        /*}}}*/
 
 struct APT_HIDDEN simple_buffer {                                                      /*{{{*/
-   static constexpr size_t buffersize_max = 4096;
+   size_t buffersize_max = 0;
    unsigned long long bufferstart = 0;
    unsigned long long bufferend = 0;
-   char buffer[buffersize_max];
+   char *buffer = nullptr;
+
+   simple_buffer() {
+      reset(4096);
+   }
+   ~simple_buffer() {
+      delete[] buffer;
+   }
 
    const char *get() const { return buffer + bufferstart; }
    char *get() { return buffer + bufferstart; }
+   const char *getend() const { return buffer + bufferend; }
+   char *getend() { return buffer + bufferend; }
    bool empty() const { return bufferend <= bufferstart; }
    bool full() const { return bufferend == buffersize_max; }
+   unsigned long long free() const { return buffersize_max - bufferend; }
    unsigned long long size() const { return bufferend-bufferstart; }
+   void reset(size_t size)
+   {
+      if (size > buffersize_max) {
+        delete[] buffer;
+        buffersize_max = size;
+        buffer = new char[size];
+      }
+      reset();
+   }
    void reset() { bufferend = bufferstart = 0; }
    ssize_t read(void *to, unsigned long long requested_size) APT_MUSTCHECK
    {
@@ -943,9 +968,9 @@ struct APT_HIDDEN simple_buffer {                                                   /*{{{*/
    }
    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);
+      if (free() < requested_size)
+        requested_size = free();
+      memcpy(getend(), from, requested_size);
       bufferend += requested_size;
       if (bufferstart == bufferend)
         bufferstart = bufferend = 0;
@@ -1475,6 +1500,157 @@ public:
    explicit Bz2FileFdPrivate(FileFd * const filefd) : FileFdPrivate(filefd), bz2(nullptr) {}
    virtual ~Bz2FileFdPrivate() { InternalClose(""); }
 #endif
+};
+                                                                       /*}}}*/
+class APT_HIDDEN Lz4FileFdPrivate: public FileFdPrivate {                              /*{{{*/
+   static constexpr unsigned long long LZ4_HEADER_SIZE = 19;
+   static constexpr unsigned long long LZ4_FOOTER_SIZE = 4;
+#ifdef HAVE_LZ4
+   LZ4F_decompressionContext_t dctx;
+   LZ4F_compressionContext_t cctx;
+   LZ4F_errorCode_t res;
+   FileFd backend;
+   simple_buffer lz4_buffer;
+   // Count of bytes that the decompressor expects to read next, or buffer size.
+   size_t next_to_load = APT_BUFFER_SIZE;
+public:
+   virtual bool InternalOpen(int const iFd, unsigned int const Mode) override
+   {
+      if ((Mode & FileFd::ReadWrite) == FileFd::ReadWrite)
+        return _error->Error("lz4 only supports write or read mode");
+
+      if ((Mode & FileFd::WriteOnly) == FileFd::WriteOnly) {
+        res = LZ4F_createCompressionContext(&cctx, LZ4F_VERSION);
+        lz4_buffer.reset(LZ4F_compressBound(APT_BUFFER_SIZE, nullptr)
+                         + LZ4_HEADER_SIZE + LZ4_FOOTER_SIZE);
+      } else {
+        res = LZ4F_createDecompressionContext(&dctx, LZ4F_VERSION);
+        lz4_buffer.reset(APT_BUFFER_SIZE);
+      }
+
+      filefd->Flags |= FileFd::Compressed;
+
+      if (LZ4F_isError(res))
+        return false;
+
+      unsigned int flags = (Mode & (FileFd::WriteOnly|FileFd::ReadOnly));
+      if (backend.OpenDescriptor(iFd, flags) == false)
+        return false;
+
+      // Write the file header
+      if ((Mode & FileFd::WriteOnly) == FileFd::WriteOnly)
+      {
+        res = LZ4F_compressBegin(cctx, lz4_buffer.buffer, lz4_buffer.buffersize_max, nullptr);
+        if (LZ4F_isError(res) || backend.Write(lz4_buffer.buffer, res) == false)
+           return false;
+      }
+
+      return true;
+   }
+   virtual ssize_t InternalUnbufferedRead(void * const To, unsigned long long const Size) override
+   {
+      /* Keep reading as long as the compressor still wants to read */
+      while (next_to_load) {
+        // Fill compressed buffer;
+        if (lz4_buffer.empty()) {
+           unsigned long long read;
+           /* Reset - if LZ4 decompressor wants to read more, allocate more */
+           lz4_buffer.reset(next_to_load);
+           if (backend.Read(lz4_buffer.getend(), lz4_buffer.free(), &read) == false)
+              return -1;
+           lz4_buffer.bufferend += read;
+
+           /* Expected EOF */
+           if (read == 0) {
+              res = -1;
+              return filefd->FileFdError("LZ4F: %s %s",
+                                         filefd->FileName.c_str(),
+                                         _("Unexpected end of file")), -1;
+           }
+        }
+        // Drain compressed buffer as far as possible.
+        size_t in = lz4_buffer.size();
+        size_t out = Size;
+
+        res = LZ4F_decompress(dctx, To, &out, lz4_buffer.get(), &in, nullptr);
+        if (LZ4F_isError(res))
+              return -1;
+
+        next_to_load = res;
+        lz4_buffer.bufferstart += in;
+
+        if (out != 0)
+           return out;
+      }
+
+      return 0;
+   }
+   virtual bool InternalReadError() override
+   {
+      char const * const errmsg = LZ4F_getErrorName(res);
+
+      return filefd->FileFdError("LZ4F: %s %s (%zu: %s)", filefd->FileName.c_str(), _("Read error"), res, errmsg);
+   }
+   virtual ssize_t InternalWrite(void const * const From, unsigned long long const Size) override
+   {
+      unsigned long long const towrite = std::min(APT_BUFFER_SIZE, Size);
+
+      res = LZ4F_compressUpdate(cctx,
+                               lz4_buffer.buffer, lz4_buffer.buffersize_max,
+                               From, towrite, nullptr);
+
+      if (LZ4F_isError(res) || backend.Write(lz4_buffer.buffer, res) == false)
+        return -1;
+
+      return towrite;
+   }
+   virtual bool InternalWriteError() override
+   {
+      char const * const errmsg = LZ4F_getErrorName(res);
+
+      return filefd->FileFdError("LZ4F: %s %s (%zu: %s)", filefd->FileName.c_str(), _("Write error"), res, errmsg);
+   }
+   virtual bool InternalStream() const override { return true; }
+
+   virtual bool InternalFlush() override
+   {
+      return backend.Flush();
+   }
+
+   virtual bool InternalClose(std::string const &) override
+   {
+      /* Reset variables */
+      res = 0;
+      next_to_load = APT_BUFFER_SIZE;
+
+      if (cctx != nullptr)
+      {
+        res = LZ4F_compressEnd(cctx, lz4_buffer.buffer, lz4_buffer.buffersize_max, nullptr);
+        if (LZ4F_isError(res) || backend.Write(lz4_buffer.buffer, res) == false)
+           return false;
+        if (!backend.Flush())
+           return false;
+        if (!backend.Close())
+           return false;
+
+        res = LZ4F_freeCompressionContext(cctx);
+        cctx = nullptr;
+      }
+
+      if (dctx != nullptr)
+      {
+        res = LZ4F_freeDecompressionContext(dctx);
+        dctx = nullptr;
+      }
+
+      return LZ4F_isError(res) == false;
+   }
+
+   explicit Lz4FileFdPrivate(FileFd * const filefd) : FileFdPrivate(filefd), dctx(nullptr), cctx(nullptr) {}
+   virtual ~Lz4FileFdPrivate() {
+      InternalClose("");
+   }
+#endif
 };
                                                                        /*}}}*/
 class APT_HIDDEN LzmaFileFdPrivate: public FileFdPrivate {                             /*{{{*/
@@ -1949,6 +2125,7 @@ bool FileFd::Open(string FileName,unsigned int const Mode,CompressMode Compress,
       case Bzip2: name = "bzip2"; break;
       case Lzma: name = "lzma"; break;
       case Xz: name = "xz"; break;
+      case Lz4: name = "lz4"; break;
       case Auto:
       case Extension:
         // Unreachable
@@ -2065,6 +2242,7 @@ bool FileFd::OpenDescriptor(int Fd, unsigned int const Mode, CompressMode Compre
    case Bzip2: name = "bzip2"; break;
    case Lzma: name = "lzma"; break;
    case Xz: name = "xz"; break;
+   case Lz4: name = "lz4"; break;
    case Auto:
    case Extension:
       if (AutoClose == true && Fd != -1)
@@ -2126,6 +2304,9 @@ bool FileFd::OpenInternDescriptor(unsigned int const Mode, APT::Configuration::C
       APT_COMPRESS_INIT("xz", LzmaFileFdPrivate);
       APT_COMPRESS_INIT("lzma", LzmaFileFdPrivate);
 #endif
+#ifdef HAVE_LZ4
+      APT_COMPRESS_INIT("lz4", Lz4FileFdPrivate);
+#endif
 #undef APT_COMPRESS_INIT
       else if (compressor.Name == "." || compressor.Binary.empty() == true)
         d = new DirectFileFdPrivate(this);