LCOV - code coverage report
Current view: top level - core/zip - SPZip.h (source / functions) Hit Total Coverage
Test: coverage.info Lines: 57 89 64.0 %
Date: 2024-05-12 00:16:13 Functions: 7 7 100.0 %

          Line data    Source code
       1             : /**
       2             : Copyright (c) 2022 Roman Katuntsev <sbkarr@stappler.org>
       3             : Copyright (c) 2023 Stappler LLC <admin@stappler.dev>
       4             : 
       5             : Permission is hereby granted, free of charge, to any person obtaining a copy
       6             : of this software and associated documentation files (the "Software"), to deal
       7             : in the Software without restriction, including without limitation the rights
       8             : to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
       9             : copies of the Software, and to permit persons to whom the Software is
      10             : furnished to do so, subject to the following conditions:
      11             : 
      12             : The above copyright notice and this permission notice shall be included in
      13             : all copies or substantial portions of the Software.
      14             : 
      15             : THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
      16             : IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      17             : FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
      18             : AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      19             : LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
      20             : OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
      21             : THE SOFTWARE.
      22             : **/
      23             : 
      24             : #ifndef STAPPLER_ZIP_SPZIP_H_
      25             : #define STAPPLER_ZIP_SPZIP_H_
      26             : 
      27             : #include "SPBytesView.h"
      28             : #include "SPBuffer.h"
      29             : #include "SPTime.h"
      30             : #include "SPLog.h"
      31             : #include "zip.h"
      32             : 
      33             : namespace STAPPLER_VERSIONIZED stappler {
      34             : 
      35             : template <typename Interface>
      36             : class ZipArchive : public Interface::AllocBaseType {
      37             : public:
      38             :         using Bytes = typename Interface::BytesType;
      39             :         using Buffer = BufferTemplate<Interface>;
      40             : 
      41             :         ZipArchive();
      42             :         ZipArchive(BytesView);
      43             :         ZipArchive(FILE *, bool readonly);
      44             : 
      45             :         ~ZipArchive();
      46             : 
      47             :         bool addDir(StringView name);
      48             :         bool addFile(StringView name, BytesView data, bool uncompressed = false);
      49             :         bool addFile(StringView name, StringView data, bool uncompressed = false);
      50             : 
      51             :         Buffer save();
      52             : 
      53             :         explicit operator bool () { return _handle != nullptr; }
      54             : 
      55             :         size_t size(bool original = false) const;
      56             :         StringView getName(size_t idx, bool original = false) const;
      57             : 
      58             :         void ftw(const Callback<void(StringView path, size_t size, Time time)> &, bool original = false) const;
      59             : 
      60             : protected:
      61             :         Buffer _data;
      62             :         Buffer _buffer;
      63             : 
      64             :         zip_t *_handle = nullptr;
      65             : };
      66             : 
      67             : template <typename Interface>
      68          25 : ZipArchive<Interface>::ZipArchive() : ZipArchive(BytesView()) { }
      69             : 
      70             : template <typename Interface>
      71          25 : ZipArchive<Interface>::ZipArchive(BytesView b) {
      72          25 :         if (!b.empty()) {
      73           0 :                 _data.put(b.data(), b.size());
      74             :         }
      75          25 :         auto source = zip_source_function_create([] (void *ud, void *data, zip_uint64_t size, zip_source_cmd_t cmd) -> zip_int64_t {
      76        1275 :                 auto d = (ZipArchive<Interface> *)ud;
      77        1275 :                 switch (cmd) {
      78          75 :                 case ZIP_SOURCE_REMOVE:
      79             :                 case ZIP_SOURCE_OPEN:
      80             :                 case ZIP_SOURCE_CLOSE:
      81             :                 case ZIP_SOURCE_FREE:
      82             :                         /* do nothing */
      83          75 :                         return 0;
      84             :                         break;
      85           0 :                 case ZIP_SOURCE_READ: {
      86           0 :                         auto v = d->_data.read(size);
      87           0 :                         memcpy(data, v.data(), v.size());
      88           0 :                         return v.size();
      89             :                         break;
      90             :                 }
      91          25 :                 case ZIP_SOURCE_STAT: {
      92          25 :                         zip_stat_t *stat = (zip_stat_t *)data;
      93          25 :                         zip_stat_init(stat);
      94          25 :                         stat->valid = ZIP_STAT_SIZE;
      95          25 :                         stat->size = d->_data.input();
      96          25 :                         return sizeof(struct zip_stat);
      97             :                         break; /* get meta information */
      98             :                 }
      99           0 :                 case ZIP_SOURCE_ERROR: {
     100           0 :                         int * errdata = (int *)data;
     101           0 :                         errdata[0] = ZIP_ER_INTERNAL;
     102           0 :                         errdata[1] = EINVAL;
     103           0 :                         break; /* get error information */
     104             :                 }
     105         150 :                 case ZIP_SOURCE_SEEK_WRITE:
     106         150 :                         d->_buffer.seek(zip_source_seek_compute_offset(d->_buffer.size(), d->_buffer.input(), data, size, nullptr));
     107         150 :                         return 0;
     108             :                         break; /* get write position */
     109           0 :                 case ZIP_SOURCE_SEEK:
     110           0 :                         d->_data.seek(zip_source_seek_compute_offset(d->_data.size(), d->_data.input(), data, size, nullptr));
     111           0 :                         return 0;
     112             :                         break;  /* set position for reading */
     113         400 :                 case ZIP_SOURCE_TELL_WRITE:
     114         400 :                         return d->_buffer.size();
     115             :                         break; /* get write position */
     116           0 :                 case ZIP_SOURCE_TELL:
     117           0 :                         return d->_data.size();
     118             :                         break; /* get read position */
     119          25 :                 case ZIP_SOURCE_SUPPORTS: {
     120          25 :                         auto supports = zip_source_make_command_bitmap(ZIP_SOURCE_OPEN, ZIP_SOURCE_READ, ZIP_SOURCE_CLOSE, ZIP_SOURCE_STAT, ZIP_SOURCE_ERROR,
     121             :                                         ZIP_SOURCE_FREE, ZIP_SOURCE_SEEK, ZIP_SOURCE_TELL, ZIP_SOURCE_SUPPORTS, ZIP_SOURCE_BEGIN_WRITE, ZIP_SOURCE_COMMIT_WRITE,
     122             :                                         ZIP_SOURCE_ROLLBACK_WRITE, ZIP_SOURCE_SEEK_WRITE, ZIP_SOURCE_TELL_WRITE, ZIP_SOURCE_REMOVE, ZIP_SOURCE_WRITE);
     123          25 :                         return supports;
     124             :                         break;   /* check whether source supports command */
     125             :                 }
     126          25 :                 case ZIP_SOURCE_BEGIN_WRITE:
     127          25 :                         d->_buffer.clear();
     128          25 :                         d->_buffer = d->_data;
     129          25 :                         return 0;
     130             :                         break; /* prepare for writing */
     131          25 :                 case ZIP_SOURCE_COMMIT_WRITE:
     132          25 :                         d->_data = move(d->_buffer);
     133          25 :                         d->_buffer.clear();
     134          25 :                         return 0;
     135             :                         break; /* writing is done */
     136           0 :                 case ZIP_SOURCE_ROLLBACK_WRITE:
     137           0 :                         d->_buffer.clear();
     138           0 :                         return 0;
     139             :                         break; /* discard written changes */
     140         550 :                 case ZIP_SOURCE_WRITE:
     141         550 :                         return d->_buffer.put((const uint8_t *)data, size);
     142             :                         break; /* write data */
     143           0 :                 default:
     144           0 :                         break;
     145             :                 }
     146           0 :                 return -1;
     147             :         }, this, nullptr);
     148             : 
     149             :         zip_error_t err;
     150             : 
     151          25 :         _handle = zip_open_from_source(source, ZIP_CREATE | ZIP_TRUNCATE, &err);
     152          25 :         if (!_handle) {
     153           0 :                 log::warn("ZipArchive", "Fail to create archive: ", err.str);
     154             :         }
     155          25 : }
     156             : 
     157             : template <typename Interface>
     158             : ZipArchive<Interface>::ZipArchive(FILE *file, bool readonly) {
     159             :         auto source = zip_source_filep_create(file, 0, -1, nullptr);
     160             : 
     161             :         _handle = zip_open_from_source(source, readonly ? ZIP_RDONLY : 0, nullptr);
     162             : }
     163             : 
     164             : template <typename Interface>
     165          25 : ZipArchive<Interface>::~ZipArchive() {
     166          25 :         if (_handle) {
     167           0 :                 zip_discard(_handle);
     168           0 :                 _handle = nullptr;
     169             :         }
     170          25 : }
     171             : 
     172             : template <typename Interface>
     173             : bool ZipArchive<Interface>::addDir(StringView name) {
     174             :         return zip_dir_add(_handle, name.terminated() ? name.data() : name.str<Interface>().data(), ZIP_FL_ENC_UTF_8) >= 0;
     175             : }
     176             : 
     177             : template <typename Interface>
     178          75 : bool ZipArchive<Interface>::addFile(StringView name, BytesView data, bool uncompressed) {
     179          75 :         zip_source_t *source = nullptr;
     180          75 :         uint8_t *buf = nullptr;
     181             : 
     182             :         if constexpr (std::is_same<Interface, memory::PoolInterface>::value) {
     183             :                 buf = (uint8_t *)memory::pool::palloc(memory::pool::acquire(), data.size());
     184             :                 memcpy(buf, data.data(), data.size());
     185             :                 source = zip_source_buffer(_handle, buf, data.size(), 0);
     186             :         } else {
     187          75 :                 buf = new uint8_t[data.size()];
     188          75 :                 memcpy(buf, data.data(), data.size());
     189          75 :                 source = zip_source_buffer(_handle, buf, data.size(), 1);
     190             :         }
     191          75 :         if (source) {
     192          75 :                 auto idx = zip_file_add(_handle, name.terminated() ? name.data() : name.str<Interface>().data(), source, ZIP_FL_ENC_UTF_8);
     193          75 :                 if (idx < 0) {
     194           0 :                         auto err = zip_get_error(_handle);
     195           0 :                         if (err) {
     196           0 :                                 std::cout << "ZIP error: " << zip_error_strerror(err) << "\n";
     197             :                         }
     198             : 
     199           0 :                         zip_source_free(source);
     200           0 :                         return false;
     201             :                 }
     202             : 
     203          75 :                 if (uncompressed) {
     204          50 :                         zip_set_file_compression(_handle, idx, ZIP_CM_STORE, 0);
     205             :                 }
     206             : 
     207          75 :                 return true;
     208             :         }
     209           0 :         return false;
     210             : }
     211             : 
     212             : template <typename Interface>
     213          25 : bool ZipArchive<Interface>::addFile(StringView name, StringView data, bool uncompressed) {
     214          25 :         return addFile(name, BytesView((const uint8_t *)data.data(), data.size()), uncompressed);
     215             : }
     216             : 
     217             : template <typename Interface>
     218          25 : auto ZipArchive<Interface>::save() -> ZipArchive<Interface>::Buffer {
     219          25 :         auto err = zip_close(_handle);
     220          25 :         if (err < 0) {
     221           0 :                 zip_discard(_handle);
     222           0 :                 return Buffer();
     223             :         }
     224          25 :         _handle = nullptr;
     225          25 :         return move(_data);
     226             : }
     227             : 
     228             : template <typename Interface>
     229             : size_t ZipArchive<Interface>::size(bool original) const {
     230             :         return zip_get_num_entries(_handle, original ? ZIP_FL_UNCHANGED : 0);
     231             : }
     232             : 
     233             : template <typename Interface>
     234             : StringView ZipArchive<Interface>::getName(size_t idx, bool original) const {
     235             :         return zip_get_name(_handle, idx, original ? ZIP_FL_UNCHANGED | ZIP_FL_ENC_GUESS : ZIP_FL_ENC_GUESS);
     236             : }
     237             : 
     238             : template <typename Interface>
     239             : void ZipArchive<Interface>::ftw(const Callback<void(StringView path, size_t size, Time time)> &cb, bool original) const {
     240             :         zip_stat_t stat;
     241             :         for (size_t i = 0; i < size(original); ++ i) {
     242             :                 zip_stat_index(_handle, i, ZIP_STAT_SIZE | ZIP_STAT_MTIME | ZIP_STAT_NAME, &stat);
     243             :                 cb(stat.name, stat.size, Time::seconds(stat.mtime));
     244             :         }
     245             : }
     246             : 
     247             : }
     248             : 
     249             : #endif /* STAPPLER_ZIP_SPZIP_H_ */

Generated by: LCOV version 1.14