185 lines
5.9 KiB
C
185 lines
5.9 KiB
C
|
|
/*
|
||
|
|
* Copyright (C) 2022 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.
|
||
|
|
*/
|
||
|
|
|
||
|
|
#ifndef AAPT_FORMAT_BINARY_RESENTRY_SERIALIZER_H
|
||
|
|
#define AAPT_FORMAT_BINARY_RESENTRY_SERIALIZER_H
|
||
|
|
|
||
|
|
#include <unordered_map>
|
||
|
|
|
||
|
|
#include "ResourceTable.h"
|
||
|
|
#include "ValueVisitor.h"
|
||
|
|
#include "android-base/macros.h"
|
||
|
|
#include "androidfw/BigBuffer.h"
|
||
|
|
#include "androidfw/ResourceTypes.h"
|
||
|
|
|
||
|
|
namespace aapt {
|
||
|
|
|
||
|
|
using android::BigBuffer;
|
||
|
|
using android::Res_value;
|
||
|
|
using android::ResTable_entry;
|
||
|
|
using android::ResTable_map;
|
||
|
|
|
||
|
|
struct FlatEntry {
|
||
|
|
const ResourceTableEntryView* entry;
|
||
|
|
const Value* value;
|
||
|
|
|
||
|
|
// The entry string pool index to the entry's name.
|
||
|
|
uint32_t entry_key;
|
||
|
|
};
|
||
|
|
|
||
|
|
// Pair of ResTable_entry and Res_value. These pairs are stored sequentially in values buffer.
|
||
|
|
// We introduce this structure for ResEntryWriter to a have single allocation using
|
||
|
|
// BigBuffer::NextBlock which allows to return it back with BigBuffer::Backup.
|
||
|
|
struct ResEntryValuePair {
|
||
|
|
ResTable_entry entry;
|
||
|
|
Res_value value;
|
||
|
|
};
|
||
|
|
|
||
|
|
static_assert(sizeof(ResEntryValuePair) == sizeof(ResTable_entry) + sizeof(Res_value),
|
||
|
|
"ResEntryValuePair must not have padding between entry and value.");
|
||
|
|
|
||
|
|
template <bool compact>
|
||
|
|
using ResEntryValue = std::conditional_t<compact, ResTable_entry, ResEntryValuePair>;
|
||
|
|
|
||
|
|
// References ResEntryValue object stored in BigBuffer used as a key in std::unordered_map.
|
||
|
|
// Allows access to memory address where ResEntryValue is stored.
|
||
|
|
template <bool compact>
|
||
|
|
union ResEntryValueRef {
|
||
|
|
using T = ResEntryValue<compact>;
|
||
|
|
const std::reference_wrapper<const T> ref;
|
||
|
|
const u_char* ptr;
|
||
|
|
|
||
|
|
explicit ResEntryValueRef(const T& rev) : ref(rev) {
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
// Hasher which computes hash of ResEntryValue using its bytes representation in memory.
|
||
|
|
struct ResEntryValueContentHasher {
|
||
|
|
template <typename R>
|
||
|
|
std::size_t operator()(const R& ref) const {
|
||
|
|
return android::JenkinsHashMixBytes(0, ref.ptr, sizeof(typename R::T));
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
// Equaler which compares ResEntryValuePairs using theirs bytes representation in memory.
|
||
|
|
struct ResEntryValueContentEqualTo {
|
||
|
|
template <typename R>
|
||
|
|
bool operator()(const R& a, const R& b) const {
|
||
|
|
return std::memcmp(a.ptr, b.ptr, sizeof(typename R::T)) == 0;
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
// Base class that allows to write FlatEntries into entries_buffer.
|
||
|
|
class ResEntryWriter {
|
||
|
|
public:
|
||
|
|
virtual ~ResEntryWriter() = default;
|
||
|
|
|
||
|
|
// Writes resource table entry and its value into 'entries_buffer_' and returns offset
|
||
|
|
// in the buffer where entry was written.
|
||
|
|
int32_t Write(const FlatEntry* entry) {
|
||
|
|
if (ValueCast<Item>(entry->value) != nullptr) {
|
||
|
|
return WriteItem(entry);
|
||
|
|
} else {
|
||
|
|
return WriteMap(entry);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
protected:
|
||
|
|
ResEntryWriter(BigBuffer* entries_buffer) : entries_buffer_(entries_buffer) {
|
||
|
|
}
|
||
|
|
BigBuffer* entries_buffer_;
|
||
|
|
|
||
|
|
virtual int32_t WriteItem(const FlatEntry* entry) = 0;
|
||
|
|
|
||
|
|
virtual int32_t WriteMap(const FlatEntry* entry) = 0;
|
||
|
|
|
||
|
|
private:
|
||
|
|
DISALLOW_COPY_AND_ASSIGN(ResEntryWriter);
|
||
|
|
};
|
||
|
|
|
||
|
|
int32_t WriteMapToBuffer(const FlatEntry* map_entry, BigBuffer* buffer);
|
||
|
|
|
||
|
|
template <bool compact_entry, typename T=ResEntryValue<compact_entry>>
|
||
|
|
std::pair<int32_t, T*> WriteItemToBuffer(const FlatEntry* item_entry, BigBuffer* buffer);
|
||
|
|
|
||
|
|
// ResEntryWriter which writes FlatEntries sequentially into entries_buffer.
|
||
|
|
// Next entry is always written right after previous one in the buffer.
|
||
|
|
template <bool compact_entry = false>
|
||
|
|
class SequentialResEntryWriter : public ResEntryWriter {
|
||
|
|
public:
|
||
|
|
explicit SequentialResEntryWriter(BigBuffer* entries_buffer)
|
||
|
|
: ResEntryWriter(entries_buffer) {
|
||
|
|
}
|
||
|
|
~SequentialResEntryWriter() override = default;
|
||
|
|
|
||
|
|
int32_t WriteItem(const FlatEntry* entry) override {
|
||
|
|
auto result = WriteItemToBuffer<compact_entry>(entry, entries_buffer_);
|
||
|
|
return result.first;
|
||
|
|
}
|
||
|
|
|
||
|
|
int32_t WriteMap(const FlatEntry* entry) override {
|
||
|
|
return WriteMapToBuffer(entry, entries_buffer_);
|
||
|
|
}
|
||
|
|
|
||
|
|
private:
|
||
|
|
DISALLOW_COPY_AND_ASSIGN(SequentialResEntryWriter);
|
||
|
|
};
|
||
|
|
|
||
|
|
// ResEntryWriter that writes only unique entry and value pairs into entries_buffer.
|
||
|
|
// Next entry is written into buffer only if there is no entry with the same bytes representation
|
||
|
|
// in memory written before. Otherwise returns offset of already written entry.
|
||
|
|
template <bool compact_entry = false>
|
||
|
|
class DeduplicateItemsResEntryWriter : public ResEntryWriter {
|
||
|
|
public:
|
||
|
|
explicit DeduplicateItemsResEntryWriter(BigBuffer* entries_buffer)
|
||
|
|
: ResEntryWriter(entries_buffer) {
|
||
|
|
}
|
||
|
|
~DeduplicateItemsResEntryWriter() override = default;
|
||
|
|
|
||
|
|
int32_t WriteItem(const FlatEntry* entry) override {
|
||
|
|
const auto& [offset, out_entry] = WriteItemToBuffer<compact_entry>(entry, entries_buffer_);
|
||
|
|
|
||
|
|
auto [it, inserted] = entry_offsets.insert({Ref{*out_entry}, offset});
|
||
|
|
if (inserted) {
|
||
|
|
// If inserted just return a new offset as this is a first time we store
|
||
|
|
// this entry
|
||
|
|
return offset;
|
||
|
|
}
|
||
|
|
|
||
|
|
// If not inserted this means that this is a duplicate, backup allocated block to the buffer
|
||
|
|
// and return offset of previously stored entry
|
||
|
|
entries_buffer_->BackUp(sizeof(*out_entry));
|
||
|
|
return it->second;
|
||
|
|
}
|
||
|
|
|
||
|
|
int32_t WriteMap(const FlatEntry* entry) override {
|
||
|
|
return WriteMapToBuffer(entry, entries_buffer_);
|
||
|
|
}
|
||
|
|
|
||
|
|
private:
|
||
|
|
DISALLOW_COPY_AND_ASSIGN(DeduplicateItemsResEntryWriter);
|
||
|
|
|
||
|
|
using Ref = ResEntryValueRef<compact_entry>;
|
||
|
|
using Map = std::unordered_map<Ref, int32_t,
|
||
|
|
ResEntryValueContentHasher,
|
||
|
|
ResEntryValueContentEqualTo>;
|
||
|
|
Map entry_offsets;
|
||
|
|
};
|
||
|
|
|
||
|
|
} // namespace aapt
|
||
|
|
|
||
|
|
#endif
|