| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591 |
- // Copyright (C) 2011 Michael McMaster <michael@codesrc.com>
- //
- // This file is part of libzipper.
- //
- // libzipper is free software: you can redistribute it and/or modify
- // it under the terms of the GNU General Public License as published by
- // the Free Software Foundation, either version 3 of the License, or
- // (at your option) any later version.
- //
- // libzipper is distributed in the hope that it will be useful,
- // but WITHOUT ANY WARRANTY; without even the implied warranty of
- // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- // GNU General Public License for more details.
- //
- // You should have received a copy of the GNU General Public License
- // along with libzipper. If not, see <http://www.gnu.org/licenses/>.
- #ifndef zipper_hh
- #define zipper_hh
- #include <stdexcept>
- #include <memory>
- #include <string>
- #include <vector>
- #include <cstdint>
- #include <sys/stat.h> // For mode_t
- #include <sys/time.h> // For timeval
- /**
- \mainpage libzipper C++ (de)compression library
- \section intro Introduction
- libzipper offers a flexible C++ interface for reading compressed files
- in multiple formats.
- <a href="http://www.codesrc.com/src/libzipper">Homepage</a>
- libzipper aims to provide applications a transparent method of accessing
- compressed data. eg. libzipper is suited to reading XML config files that
- are compressed to save space.
- libzipper is not a general-purpose archive management library, as it
- does not provide access to the filesystem attributes of each file.
- (ie. libzipper does not support the concepts of file owner, group or
- permissions.
- \section formats Supported Formats
- <ul>
- <li>gzip</li>
- <li>zip</li>
- </ul>
- \section example_read Reading a compressed file into memory
- \code
- #include <zipper.hh>
- #include <algorithm>
- #include <vector>
- class MemWriter : public zipper::Writer
- {
- public:
- std::vector<uint8_t> data;
- virtual void writeData(
- zsize_t offset, zsize_t bytes, const uint8_t* inData)
- {
- data.resize(std::max(offset + bytes, data.size()));
- std::copy(inData, inData + bytes, &data[offset]);
- }
- virtual zsize_t getSize() const { return data.size(); }
- };
- std::vector<uint8_t> readSavedGame(const std::string& filename)
- {
- // open the compressed input file. FileReader will throw an
- // exception if an IO error occurs.
- zipper::FileReader reader(filename);
- MemWriter writer;
- zipper::Decompressor decomp(reader);
- std::vector<zipper::CompressedFilePtr> entries(decomp.getEntries());
- if (!entries.empty())
- {
- // Uncompress the first file. Will pass-though data as-is if the
- // file is not compressed.
- entries.front()->decompress(writer);
- }
- return writer.data;
- }
- \endcode
- \section example_write Writing compressed files.
- \code
- #include <zipper.hh>
- #include <algorithm>
- #include <vector>
- class MemReader : public zipper::Reader
- {
- public:
- MemReader(const vector<uint8_t>& data) : m_data(data) {}
- virtual const std::string& getSourceName() const
- {
- static std::string Name("savedGame.dat");
- return Name;
- }
- virtual const timeval& getModTime() const
- {
- return zipper::s_now;
- }
- virtual zsize_t getSize() const { return m_data.size(); }
- virtual void readData(zsize_t offset, zsize_t bytes, uint8_t* dest) const
- {
- std::copy(&m_data[offset], &m_data[offset + bytes], dest);
- }
- private:
- std::vector<uint8_t> m_data;
- };
- void writeSavedGame(
- const std::string& filename, const std::vector<uint8_t>& gameData)
- {
- zipper::FileWriter writer(filename);
- zipper::Compressor comp(zipper::Container_zip, writer);
- comp.addFile(MemReader(gameData));
- }
- \endcode
- */
- /// \namespace zipper
- /// \brief The zipper namespace contains the libzipper public API.
- namespace zipper
- {
- /// \typedef zsize_t
- /// zsize_t should be used exclusively when dealing with file offsets
- /// and sizes to support large files (>4Gb).
- ///
- /// Unlike size_t on some systems, zsize_t will be 64bit when compiling for
- /// a 32bit target.
- typedef uint64_t zsize_t;
- /// \enum ContainerFormat
- /// ContainerFormat enumerates the compressed archive formats supported
- /// by libzipper.
- ///
- /// An application can determine the supported formats by iterating
- /// over the Container_begin to Container_end range. eg.
- /// \code
- /// for (int i = Container_begin; i < Container_end; ++i)
- /// {
- /// const Container& container(getContainer(ContainerFormat(i)));
- /// }
- /// \endcode
- enum ContainerFormat
- {
- /// Iteration marker
- Container_begin = 0,
- /// No container (eg. plain text)
- Container_none = 0,
- /// ZIP
- Container_zip,
- /// gzip.
- Container_gzip,
- /// Iteration marker
- Container_end
- };
- /// \struct Container
- /// Provides libzipper capability details for a compressed archive
- /// format.
- /// \see getContainer
- struct Container
- {
- /// \enum CapabilityBits allows a bitmask to be specified with a
- /// combination of boolean flags.
- enum CapabilityBits
- {
- /// Compression bit is set if the format is usable with Compressor
- Compression = 1,
- /// Decompression bit is set if the format is usable with
- /// Decompressor
- Decompression = 2,
- /// EmbeddedFilenames bit is set if CompressedFile::getPath() is
- /// supported
- EmbeddedFilenames = 4,
- /// Archive bit is set if multiple compressed files may exist in
- /// a single container.
- Archive = 8,
- /// FileSize bit is set if the uncompressed size for each
- /// compressed file is recorded in the container.
- FileSize = 16
- };
- /// %Container Type
- ContainerFormat format;
- /// %Container Internet Media Type (aka MIME type).
- /// eg. "application/zip"
- std::string mediaType;
- /// Bitmask comprised of CapabilityBits enum values.
- uint32_t capabilities;
- };
- /// \brief When passed as a method parameter, it requests that the
- /// current time be used instead.
- extern const timeval s_now;
- /// \brief Returns the capability details of the given format.
- const Container& getContainer(ContainerFormat format);
- /// \brief Base class for all exceptions thrown by libzipper
- class Exception : public std::runtime_error
- {
- public:
- /// Exception ctor
- /// \param what A description of the error encountered.
- Exception(const std::string& what);
- };
- /// \brief Exception thrown when the input data does not match
- /// the expected Container format.
- class FormatException : public Exception
- {
- public:
- /// FormatException ctor
- /// \param what A description of the error encountered.
- FormatException(const std::string& what);
- };
- /// \brief Exception thrown when a Reader or Writer instance is unable
- /// to satisfy an IO request due to an external error.
- class IOException : public Exception
- {
- public:
- /// IOException ctor
- /// \param what A description of the error encountered.
- IOException(const std::string& what);
- };
- /// \brief Exception thrown when an operation is requested on a compressed
- /// archive that libzipper does not implement.
- ///
- /// This exception may be thrown even if libzipper advertises general
- /// support for the Container format. eg. libzipper supports most
- /// ZIP files, but an UnsupportedException will be thrown if given an
- /// encrypted ZIP file.
- class UnsupportedException : public Exception
- {
- public:
- /// UnsupportedException ctor
- /// \param what A description of the error encountered.
- UnsupportedException(const std::string& what);
- };
- /// \brief Reader supplies input data to the compression/decompression
- /// functions.
- ///
- /// Normally, an application using libzipper provides the Reader
- /// implementation. The implementation could supply data from files,
- /// in-memory buffers, or it could be generated on-the-fly.
- ///
- /// The Reader implementation must support random access, and must
- /// determine at creation time the number of bytes available. The
- /// Reader interface is not suitable for use with streaming data.
- class Reader
- {
- public:
- /// Reader dtor
- virtual ~Reader();
- /// Returns a name for this source of the data.
- ///
- /// For file-based Reader implementations, this would normally be
- /// the input filename.
- virtual const std::string& getSourceName() const = 0;
- /// Return the last-modified timestamp of the data.
- /// If the special s_now value is returned, the current time should be
- /// used instead.
- virtual const timeval& getModTime() const = 0;
- /// Returns the number of bytes available via readData()
- ///
- /// \invariant getSize() is stable throughout the lifetime
- /// of the Reader instance.
- virtual zsize_t getSize() const = 0;
- /// Copies data into the dest buffer
- ///
- /// An exception must be thrown if it is not possible to copy the
- /// requested data into the supplied buffer (eg. file IO error).
- ///
- /// \pre offset + bytes <= getSize()
- ///
- /// \param offset Number of bytes to skip at the front of the data
- /// source.
- /// \param bytes Number of bytes to copy
- /// \param dest Destination buffer.
- ///
- virtual void readData(
- zsize_t offset, zsize_t bytes, uint8_t* dest
- ) const = 0;
- };
- /// \brief FileReader is a file-based implementation of the Reader
- /// interface.
- class FileReader : public Reader
- {
- public:
- /// Read data from the supplied file.
- FileReader(const std::string& filename);
- /// Read data from the supplied file.
- ///
- /// \param filename The value used by getSourceName(). This name
- /// is arbitary, and does not need to be related to fd.
- ///
- /// \param fd The descriptor to source data from. The descriptor
- /// must be open for reading, blocking, and seekable (ie. lseek(2)).
- ///
- /// \param closeFd If true, fd will be closed by this object
- /// when it is no longer needed.
- FileReader(const std::string& filename, int fd, bool closeFd);
- /// FileReader dtor
- virtual ~FileReader();
- /// Inherited from Reader
- virtual const std::string& getSourceName() const;
- /// Inherited from Reader
- virtual const timeval& getModTime() const;
- /// Inherited from Reader
- virtual zsize_t getSize() const;
- /// Inherited from Reader
- virtual void readData(
- zsize_t offset, zsize_t bytes, uint8_t* dest
- ) const;
- private:
- FileReader(const FileReader&);
- FileReader& operator=(const FileReader&);
- class FileReaderImpl;
- FileReaderImpl* m_impl;
- };
- /// \typedef ReaderPtr
- /// A shared pointer to a Reader
- typedef std::shared_ptr<Reader> ReaderPtr;
- /// \brief Writer accepts output data from the compression/decompression
- /// functions.
- ///
- /// Normally, an application using libzipper provides the Writer
- /// implementation. The implementation could write data to files,
- /// in-memory buffers, or it could be simply discarded.
- ///
- /// The Writer implementation needs only to support sequential access.
- class Writer
- {
- public:
- /// Writer dtor
- virtual ~Writer();
- /// Returns the size of the written data.
- virtual zsize_t getSize() const = 0;
- /// Accepts output from libzipper
- ///
- /// An exception must be thrown if it is not possible to accept
- /// given data. (eg. file IO error).
- ///
- /// \param offset Number of bytes to skip at the front of the data
- /// source. Skipped bytes will contain null characters if not already
- /// assigned a value.
- /// \param bytes Number of bytes in data
- /// \param data Output from libzipper.
- ///
- virtual void writeData(
- zsize_t offset, zsize_t bytes, const uint8_t* data
- ) = 0;
- };
- /// \typedef WriterPtr
- /// A shared pointer to a Writer
- typedef std::shared_ptr<Writer> WriterPtr;
- /// \brief FileWrter is a file-based implementation of the Writer
- /// interface.
- class FileWriter : public Writer
- {
- public:
- /// Write data to the supplied file.
- /// If the file already exists, it will be truncated.
- /// If the file does not exist, it will be created with the
- /// given permissions.
- ///
- /// \param filename The file to open for writing.
- ///
- /// \param createPermissions The permissions set on the file if it is to
- /// be created.
- ///
- /// \param modTime Set a specific modification time on the created file.
- /// If the special s_now value is provided, the current time will be
- /// used.
- ///
- FileWriter(
- const std::string& filename,
- mode_t createPermissions = 0664,
- const timeval& modTime = s_now);
- /// Write data to the supplied file.
- ///
- /// \param filename The filename reported in any exception error
- /// messages. This name is arbitary, and does not need to be
- /// related to fd.
- ///
- /// \param fd The descriptor to write data to. The descriptor
- /// must be open for writing in blocking mode.
- ///
- /// \param closeFd If true, fd will be closed by this object
- /// when it is no longer needed.
- FileWriter(const std::string& filename, int fd, bool closeFd);
- /// FileWriter dtor
- virtual ~FileWriter();
- /// Inherited from Writer
- virtual zsize_t getSize() const;
- /// Inherited from Writer
- virtual void writeData(
- zsize_t offset, zsize_t bytes, const uint8_t* data
- );
- private:
- FileWriter(const FileWriter&);
- FileWriter& operator=(const FileWriter&);
- class FileWriterImpl;
- FileWriterImpl* m_impl;
- };
- /// \brief CompressedFile represents an entry within a compressed archive.
- ///
- /// CompressedFile instances are created by Decompressor, and allow
- /// selectively extracting the contents of an archive.
- class CompressedFile
- {
- public:
- /// CompressedFile dtor
- virtual ~CompressedFile();
- /// Return true if decompress is likely to succeed.
- ///
- /// isDecompressSupported may return false if libzipper doesn't know
- /// how to deal with the compressed data. eg. encrypted files,
- /// or ZIP files compressed with non-standard schemes.
- virtual bool isDecompressSupported() const = 0;
- /// Decompress the file, and store the results via the given
- /// writer object.
- virtual void decompress(Writer& writer) = 0;
- /// Return the file path of the compressed file.
- ///
- /// Unix-style path separaters ('/') are returned, even if the
- /// archive was created under an alternative OS.
- virtual const std::string& getPath() const = 0;
- /// Return the compressed size of the file
- ///
- /// getCompressedSize() will return -1 of the FileSize capability
- /// bit of the container is false.
- virtual zsize_t getCompressedSize() const = 0;
- /// Return the uncompressed size of the file
- ///
- /// The decompress method will pass exactly this number of bytes
- /// to the Writer.
- ///
- /// getUncompressedSize() will return -1 of the FileSize capability
- /// bit of the container is false.
- virtual zsize_t getUncompressedSize() const = 0;
- /// Return the modification time of the original file
- virtual const timeval& getModificationTime() const = 0;
- };
- /// \typedef CompressedFilePtr
- /// A shared pointer to a CompressedFile
- typedef std::shared_ptr<CompressedFile> CompressedFilePtr;
- /// \brief Decompressor detects the compressed archive type of the data,
- /// and creates suitable CompressedFile instances to access the compressed
- /// data.
- class Decompressor
- {
- public:
- /// Create a decompressor from the data made available by reader.
- Decompressor(const ReaderPtr& reader);
- /// Create a decompressor from the data made available by reader.
- ///
- /// \param reader must remain in scope for the lifetime of the
- /// Decompressor, and lifetime of any CompressedFile objects returned
- /// from getEntries()
- Decompressor(Reader& reader);
- /// Decompressor dtor
- ~Decompressor();
- /// Return the detected Container type of the compressed archive.
- ContainerFormat getContainerFormat() const;
- /// Return CompressedFile entries to represent the file entries within
- /// a compressed archive.
- std::vector<CompressedFilePtr> getEntries() const;
- private:
- Decompressor(const Decompressor&);
- Decompressor& operator=(const Decompressor&);
- class DecompressorImpl;
- DecompressorImpl* m_decompressor;
- };
- /// \brief Compressor creates a compressed archive from the supplied
- /// Reader objects.
- /// data.
- class Compressor
- {
- public:
- /// Create a Compressor to output the given compressed archived format
- /// to writer.
- /// \param writer destination of the compressed data
- /// \param format determines the output archive file type to
- /// create.
- Compressor(ContainerFormat format, const WriterPtr& writer);
- /// Create a Compressor to output the given compressed archived format
- /// to writer.
- ///
- /// \param writer is the destination of the compressed data. writer
- /// must remain in scope for the lifetime of the Compressor.
- /// \param format determines the output archive file type to
- /// create.
- Compressor(ContainerFormat format, Writer& writer);
- /// \brief Compressor dtor
- ///
- /// Additional data may be passed to writer (given in ctor) to close
- /// the compressed archive.
- ~Compressor();
- /// Compress the data given by reader, and add it to the compressed
- /// archive.
- void addFile(const Reader& reader);
- class CompressorImpl;
- private:
- Compressor(const Compressor&);
- Compressor& operator=(const Compressor&);
- CompressorImpl* m_compressor;
- };
- }
- #endif
|