/* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include #include #include #include #include "zip_error.h" // This class is the interface of the central directory entries map. The map // helps to locate a particular cd entry based on the filename. class CdEntryMapInterface { public: virtual ~CdEntryMapInterface() = default; // Adds an entry to the map. The |name| should internally points to the // filename field of a cd entry. And |start| points to the beginning of the // central directory. Returns 0 on success. virtual ZipError AddToMap(std::string_view name, const uint8_t* start) = 0; // For the zip entry |entryName|, finds the offset of its filename field in // the central directory. Returns a pair of [status, offset]. The value of // the status is 0 on success. virtual std::pair GetCdEntryOffset(std::string_view name, const uint8_t* cd_start) const = 0; // Resets the iterator to the beginning of the map. virtual void ResetIteration() = 0; // Returns the [name, cd offset] of the current element. Also increments the // iterator to points to the next element. Returns an empty pair we have read // past boundary. virtual std::pair Next(const uint8_t* cd_start) = 0; }; /** * More space efficient string representation of strings in an mmaped zipped * file than std::string_view. Using std::string_view as an entry in the * ZipArchive hash table wastes space. std::string_view stores a pointer to a * string (on 64 bit, 8 bytes) and the length to read from that pointer, * 2 bytes. Because of alignment, the structure consumes 16 bytes, wasting * 6 bytes. * * ZipStringOffset stores a 4 byte offset from a fixed location in the memory * mapped file instead of the entire address, consuming 8 bytes with alignment. */ struct ZipStringOffset { uint32_t name_offset; uint16_t name_length; const std::string_view ToStringView(const uint8_t* start) const { return std::string_view{reinterpret_cast(start + name_offset), name_length}; } }; // This implementation of CdEntryMap uses an array hash table. It uses less // memory than std::map; and it's used as the default implementation for zip // archives without zip64 extension. class CdEntryMapZip32 : public CdEntryMapInterface { public: static std::unique_ptr Create(uint16_t num_entries); ZipError AddToMap(std::string_view name, const uint8_t* start) override; std::pair GetCdEntryOffset(std::string_view name, const uint8_t* cd_start) const override; void ResetIteration() override; std::pair Next(const uint8_t* cd_start) override; private: explicit CdEntryMapZip32(uint16_t num_entries); // We know how many entries are in the Zip archive, so we can have a // fixed-size hash table. We define a load factor of 0.75 and over // allocate so the maximum number entries can never be higher than // ((4 * UINT16_MAX) / 3 + 1) which can safely fit into a uint32_t. uint32_t hash_table_size_{0}; std::unique_ptr hash_table_{nullptr, free}; // The position of element for the current iteration. uint32_t current_position_{0}; }; // This implementation of CdEntryMap uses a std::map class CdEntryMapZip64 : public CdEntryMapInterface { public: static std::unique_ptr Create(); ZipError AddToMap(std::string_view name, const uint8_t* start) override; std::pair GetCdEntryOffset(std::string_view name, const uint8_t* cd_start) const override; void ResetIteration() override; std::pair Next(const uint8_t* cd_start) override; private: CdEntryMapZip64() = default; std::map entry_table_; std::map::iterator iterator_; };