1*adcb0a62SAndroid Build Coastguard Worker /*
2*adcb0a62SAndroid Build Coastguard Worker * Copyright (C) 2020 The Android Open Source Project
3*adcb0a62SAndroid Build Coastguard Worker *
4*adcb0a62SAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License");
5*adcb0a62SAndroid Build Coastguard Worker * you may not use this file except in compliance with the License.
6*adcb0a62SAndroid Build Coastguard Worker * You may obtain a copy of the License at
7*adcb0a62SAndroid Build Coastguard Worker *
8*adcb0a62SAndroid Build Coastguard Worker * http://www.apache.org/licenses/LICENSE-2.0
9*adcb0a62SAndroid Build Coastguard Worker *
10*adcb0a62SAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software
11*adcb0a62SAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS,
12*adcb0a62SAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*adcb0a62SAndroid Build Coastguard Worker * See the License for the specific language governing permissions and
14*adcb0a62SAndroid Build Coastguard Worker * limitations under the License.
15*adcb0a62SAndroid Build Coastguard Worker */
16*adcb0a62SAndroid Build Coastguard Worker
17*adcb0a62SAndroid Build Coastguard Worker #include "zip_cd_entry_map.h"
18*adcb0a62SAndroid Build Coastguard Worker
ComputeHash(std::string_view name)19*adcb0a62SAndroid Build Coastguard Worker static uint32_t ComputeHash(std::string_view name) {
20*adcb0a62SAndroid Build Coastguard Worker return static_cast<uint32_t>(std::hash<std::string_view>{}(name));
21*adcb0a62SAndroid Build Coastguard Worker }
22*adcb0a62SAndroid Build Coastguard Worker
23*adcb0a62SAndroid Build Coastguard Worker template <typename ZipStringOffset>
ToStringView(ZipStringOffset & entry,const uint8_t * start)24*adcb0a62SAndroid Build Coastguard Worker const std::string_view ToStringView(ZipStringOffset& entry, const uint8_t *start) {
25*adcb0a62SAndroid Build Coastguard Worker auto name = reinterpret_cast<const char*>(start + entry.name_offset);
26*adcb0a62SAndroid Build Coastguard Worker return std::string_view{name, entry.name_length};
27*adcb0a62SAndroid Build Coastguard Worker }
28*adcb0a62SAndroid Build Coastguard Worker
29*adcb0a62SAndroid Build Coastguard Worker // Convert a ZipEntry to a hash table index, verifying that it's in a valid range.
30*adcb0a62SAndroid Build Coastguard Worker template <typename ZipStringOffset>
GetCdEntryOffset(std::string_view name,const uint8_t * start) const31*adcb0a62SAndroid Build Coastguard Worker std::pair<ZipError, uint64_t> CdEntryMapZip32<ZipStringOffset>::GetCdEntryOffset(
32*adcb0a62SAndroid Build Coastguard Worker std::string_view name, const uint8_t* start) const {
33*adcb0a62SAndroid Build Coastguard Worker const uint32_t hash = ComputeHash(name);
34*adcb0a62SAndroid Build Coastguard Worker
35*adcb0a62SAndroid Build Coastguard Worker // NOTE: (hash_table_size - 1) is guaranteed to be non-negative.
36*adcb0a62SAndroid Build Coastguard Worker uint32_t ent = hash & (hash_table_size_ - 1);
37*adcb0a62SAndroid Build Coastguard Worker while (hash_table_[ent].name_offset != 0) {
38*adcb0a62SAndroid Build Coastguard Worker if (ToStringView(hash_table_[ent], start) == name) {
39*adcb0a62SAndroid Build Coastguard Worker return {kSuccess, static_cast<uint64_t>(hash_table_[ent].name_offset)};
40*adcb0a62SAndroid Build Coastguard Worker }
41*adcb0a62SAndroid Build Coastguard Worker ent = (ent + 1) & (hash_table_size_ - 1);
42*adcb0a62SAndroid Build Coastguard Worker }
43*adcb0a62SAndroid Build Coastguard Worker
44*adcb0a62SAndroid Build Coastguard Worker ALOGV("Zip: Unable to find entry %.*s", static_cast<int>(name.size()), name.data());
45*adcb0a62SAndroid Build Coastguard Worker return {kEntryNotFound, 0};
46*adcb0a62SAndroid Build Coastguard Worker }
47*adcb0a62SAndroid Build Coastguard Worker
48*adcb0a62SAndroid Build Coastguard Worker template <typename ZipStringOffset>
AddToMap(std::string_view name,const uint8_t * start)49*adcb0a62SAndroid Build Coastguard Worker ZipError CdEntryMapZip32<ZipStringOffset>::AddToMap(std::string_view name, const uint8_t* start) {
50*adcb0a62SAndroid Build Coastguard Worker const uint64_t hash = ComputeHash(name);
51*adcb0a62SAndroid Build Coastguard Worker uint32_t ent = hash & (hash_table_size_ - 1);
52*adcb0a62SAndroid Build Coastguard Worker
53*adcb0a62SAndroid Build Coastguard Worker /*
54*adcb0a62SAndroid Build Coastguard Worker * We over-allocated the table, so we're guaranteed to find an empty slot.
55*adcb0a62SAndroid Build Coastguard Worker * Further, we guarantee that the hashtable size is not 0.
56*adcb0a62SAndroid Build Coastguard Worker */
57*adcb0a62SAndroid Build Coastguard Worker while (hash_table_[ent].name_offset != 0) {
58*adcb0a62SAndroid Build Coastguard Worker if (ToStringView(hash_table_[ent], start) == name) {
59*adcb0a62SAndroid Build Coastguard Worker // We've found a duplicate entry. We don't accept duplicates.
60*adcb0a62SAndroid Build Coastguard Worker ALOGW("Zip: Found duplicate entry %.*s", static_cast<int>(name.size()), name.data());
61*adcb0a62SAndroid Build Coastguard Worker return kDuplicateEntry;
62*adcb0a62SAndroid Build Coastguard Worker }
63*adcb0a62SAndroid Build Coastguard Worker ent = (ent + 1) & (hash_table_size_ - 1);
64*adcb0a62SAndroid Build Coastguard Worker }
65*adcb0a62SAndroid Build Coastguard Worker
66*adcb0a62SAndroid Build Coastguard Worker // `name` has already been validated before entry.
67*adcb0a62SAndroid Build Coastguard Worker const char* start_char = reinterpret_cast<const char*>(start);
68*adcb0a62SAndroid Build Coastguard Worker hash_table_[ent].name_offset = static_cast<uint32_t>(name.data() - start_char);
69*adcb0a62SAndroid Build Coastguard Worker hash_table_[ent].name_length = static_cast<uint16_t>(name.size());
70*adcb0a62SAndroid Build Coastguard Worker return kSuccess;
71*adcb0a62SAndroid Build Coastguard Worker }
72*adcb0a62SAndroid Build Coastguard Worker
73*adcb0a62SAndroid Build Coastguard Worker template <typename ZipStringOffset>
ResetIteration()74*adcb0a62SAndroid Build Coastguard Worker void CdEntryMapZip32<ZipStringOffset>::ResetIteration() {
75*adcb0a62SAndroid Build Coastguard Worker current_position_ = 0;
76*adcb0a62SAndroid Build Coastguard Worker }
77*adcb0a62SAndroid Build Coastguard Worker
78*adcb0a62SAndroid Build Coastguard Worker template <typename ZipStringOffset>
Next(const uint8_t * cd_start)79*adcb0a62SAndroid Build Coastguard Worker std::pair<std::string_view, uint64_t> CdEntryMapZip32<ZipStringOffset>::Next(
80*adcb0a62SAndroid Build Coastguard Worker const uint8_t* cd_start) {
81*adcb0a62SAndroid Build Coastguard Worker while (current_position_ < hash_table_size_) {
82*adcb0a62SAndroid Build Coastguard Worker const auto& entry = hash_table_[current_position_];
83*adcb0a62SAndroid Build Coastguard Worker current_position_ += 1;
84*adcb0a62SAndroid Build Coastguard Worker
85*adcb0a62SAndroid Build Coastguard Worker if (entry.name_offset != 0) {
86*adcb0a62SAndroid Build Coastguard Worker return {ToStringView(entry, cd_start), static_cast<uint64_t>(entry.name_offset)};
87*adcb0a62SAndroid Build Coastguard Worker }
88*adcb0a62SAndroid Build Coastguard Worker }
89*adcb0a62SAndroid Build Coastguard Worker // We have reached the end of the hash table.
90*adcb0a62SAndroid Build Coastguard Worker return {};
91*adcb0a62SAndroid Build Coastguard Worker }
92*adcb0a62SAndroid Build Coastguard Worker
AddToMap(std::string_view name,const uint8_t * start)93*adcb0a62SAndroid Build Coastguard Worker ZipError CdEntryMapZip64::AddToMap(std::string_view name, const uint8_t* start) {
94*adcb0a62SAndroid Build Coastguard Worker const auto [it, added] =
95*adcb0a62SAndroid Build Coastguard Worker entry_table_.insert({name, name.data() - reinterpret_cast<const char*>(start)});
96*adcb0a62SAndroid Build Coastguard Worker if (!added) {
97*adcb0a62SAndroid Build Coastguard Worker ALOGW("Zip: Found duplicate entry %.*s", static_cast<int>(name.size()), name.data());
98*adcb0a62SAndroid Build Coastguard Worker return kDuplicateEntry;
99*adcb0a62SAndroid Build Coastguard Worker }
100*adcb0a62SAndroid Build Coastguard Worker return kSuccess;
101*adcb0a62SAndroid Build Coastguard Worker }
102*adcb0a62SAndroid Build Coastguard Worker
GetCdEntryOffset(std::string_view name,const uint8_t *) const103*adcb0a62SAndroid Build Coastguard Worker std::pair<ZipError, uint64_t> CdEntryMapZip64::GetCdEntryOffset(std::string_view name,
104*adcb0a62SAndroid Build Coastguard Worker const uint8_t* /*cd_start*/) const {
105*adcb0a62SAndroid Build Coastguard Worker const auto it = entry_table_.find(name);
106*adcb0a62SAndroid Build Coastguard Worker if (it == entry_table_.end()) {
107*adcb0a62SAndroid Build Coastguard Worker ALOGV("Zip: Could not find entry %.*s", static_cast<int>(name.size()), name.data());
108*adcb0a62SAndroid Build Coastguard Worker return {kEntryNotFound, 0};
109*adcb0a62SAndroid Build Coastguard Worker }
110*adcb0a62SAndroid Build Coastguard Worker
111*adcb0a62SAndroid Build Coastguard Worker return {kSuccess, it->second};
112*adcb0a62SAndroid Build Coastguard Worker }
113*adcb0a62SAndroid Build Coastguard Worker
ResetIteration()114*adcb0a62SAndroid Build Coastguard Worker void CdEntryMapZip64::ResetIteration() {
115*adcb0a62SAndroid Build Coastguard Worker iterator_ = entry_table_.begin();
116*adcb0a62SAndroid Build Coastguard Worker }
117*adcb0a62SAndroid Build Coastguard Worker
Next(const uint8_t *)118*adcb0a62SAndroid Build Coastguard Worker std::pair<std::string_view, uint64_t> CdEntryMapZip64::Next(const uint8_t* /*cd_start*/) {
119*adcb0a62SAndroid Build Coastguard Worker if (iterator_ == entry_table_.end()) {
120*adcb0a62SAndroid Build Coastguard Worker return {};
121*adcb0a62SAndroid Build Coastguard Worker }
122*adcb0a62SAndroid Build Coastguard Worker
123*adcb0a62SAndroid Build Coastguard Worker return *iterator_++;
124*adcb0a62SAndroid Build Coastguard Worker }
125*adcb0a62SAndroid Build Coastguard Worker
Create(uint64_t num_entries,size_t cd_length,uint16_t max_file_name_length)126*adcb0a62SAndroid Build Coastguard Worker std::unique_ptr<CdEntryMapInterface> CdEntryMapInterface::Create(uint64_t num_entries,
127*adcb0a62SAndroid Build Coastguard Worker size_t cd_length, uint16_t max_file_name_length) {
128*adcb0a62SAndroid Build Coastguard Worker using T = std::unique_ptr<CdEntryMapInterface>;
129*adcb0a62SAndroid Build Coastguard Worker if (num_entries > UINT16_MAX)
130*adcb0a62SAndroid Build Coastguard Worker return T(new CdEntryMapZip64());
131*adcb0a62SAndroid Build Coastguard Worker
132*adcb0a62SAndroid Build Coastguard Worker uint16_t num_entries_ = static_cast<uint16_t>(num_entries);
133*adcb0a62SAndroid Build Coastguard Worker if (cd_length > ZipStringOffset20::offset_max ||
134*adcb0a62SAndroid Build Coastguard Worker max_file_name_length > ZipStringOffset20::length_max) {
135*adcb0a62SAndroid Build Coastguard Worker return T(new CdEntryMapZip32<ZipStringOffset32>(num_entries_));
136*adcb0a62SAndroid Build Coastguard Worker }
137*adcb0a62SAndroid Build Coastguard Worker return T(new CdEntryMapZip32<ZipStringOffset20>(num_entries_));
138*adcb0a62SAndroid Build Coastguard Worker }
139