blob: 38e7cb15fda4bff91e3fe6a3713693f0c7213e0f [file] [log] [blame]
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// DataPack represents a read-only view onto an on-disk file that contains
// (key, value) pairs of data. It's used to store static resources like
// translation strings and images.
#ifndef UI_BASE_RESOURCE_DATA_PACK_H_
#define UI_BASE_RESOURCE_DATA_PACK_H_
#include <stddef.h>
#include <stdint.h>
#include <map>
#include <memory>
#include <optional>
#include <string_view>
#include <vector>
#include "base/compiler_specific.h"
#include "base/component_export.h"
#include "base/files/file.h"
#include "base/files/memory_mapped_file.h"
#include "base/memory/raw_ptr.h"
#include "base/types/expected.h"
#include "build/build_config.h"
#include "ui/base/resource/resource_handle.h"
#if BUILDFLAG(IS_WIN)
#include "base/win/windows_types.h"
#endif
namespace base {
class FilePath;
class RefCountedStaticMemory;
}
namespace ui {
enum ResourceScaleFactor : int;
class COMPONENT_EXPORT(UI_DATA_PACK) DataPack : public ResourceHandle {
public:
explicit DataPack(ResourceScaleFactor resource_scale_factor);
DataPack(const DataPack&) = delete;
DataPack& operator=(const DataPack&) = delete;
~DataPack() override;
// Pack Entry and Alias. This removes padding between fields, and alignment
// requirements, which makes the structs usable for aliasing into the input
// buffer directly.
//
// TODO(davidben): Ideally we would load these structures through memcpy, or
// a little-endian variant of base/big_endian.h, rather than type-punning
// pointers. This code currently depends on Chromium disabling strict aliasing.
#pragma pack(push, 1)
struct Entry {
static int CompareById(const void* void_key, const void* void_entry);
// ID corresponding with each resources.
uint16_t resource_id;
// The offset of the resource in .pak file.
uint32_t file_offset;
};
struct Alias {
static int CompareById(const void* void_key, const void* void_entry);
// ID corresponding with each resources.
uint16_t resource_id;
// The index of the entry which has the same resource to `resource_id`'s
// resource.
uint16_t entry_index;
};
#pragma pack(pop)
// Pair of resource id and string view data.
struct ResourceData {
explicit ResourceData(uint16_t id, std::string_view data)
: id(id), data(data) {}
// Resource ID.
uint16_t id;
// Resource data.
std::string_view data;
};
// Iterator for ResourceData in `resource_table_`.
// Note that this Iterator doesn't include Alias members in `alias_table_`.
class Iterator {
public:
Iterator() = default;
~Iterator() = default;
Iterator(const Iterator&) = default;
Iterator& operator=(const Iterator&) = default;
const ResourceData& operator*() { return *resource_data_; }
Iterator& operator++() {
UNSAFE_TODO(++entry_); // Should be UNSAFE_BUFFER_USAGE, too.
UpdateResourceData();
return *this;
}
bool operator==(const Iterator& other) const {
return entry_ == other.entry_;
}
private:
friend class DataPack;
explicit Iterator(const uint8_t* data_source, const Entry* entry)
: data_source_(data_source), entry_(entry) {
UpdateResourceData();
}
void UpdateResourceData();
raw_ptr<const uint8_t> data_source_;
raw_ptr<ResourceData> resource_data_;
raw_ptr<const Entry, AllowPtrArithmetic> entry_;
};
Iterator begin() const;
Iterator end() const;
// Abstraction of a data source (memory mapped file or in-memory buffer).
class DataSource {
public:
virtual ~DataSource() = default;
virtual size_t GetLength() const = 0;
virtual const uint8_t* GetData() const = 0;
};
// Load a pack file from |path|, returning false on error. If the final
// extension of |path| is .gz, the file will be uncompressed and stored in
// memory owned by |data_source_|. Otherwise the file will be mapped to
// memory, with the mapping owned by |data_source_|.
bool LoadFromPath(const base::FilePath& path);
enum class FailureReason {
kOpenFile,
kMapFile,
kUnzip,
kIncompleteHeader,
kBadPakVersion,
kBadEncodingType,
kTooShort,
kBoundsExceeded,
kOrderingViolation,
kAliasTableCorrupt,
};
struct ErrorState {
FailureReason reason;
#if BUILDFLAG(IS_WIN)
DWORD error;
#else
int error;
#endif
base::File::Error file_error;
friend bool operator==(const ErrorState& lhs,
const ErrorState& rhs) = default;
};
// As LoadFromPath, but returns an ErrorState on failure.
base::expected<void, DataPack::ErrorState> LoadFromPathWithError(
const base::FilePath& path);
// The static part of the implementation in LoadFromPath().
static base::expected<std::unique_ptr<DataPack::DataSource>,
DataPack::ErrorState>
LoadFromPathInternal(const base::FilePath& path);
// Invokes LoadFromFileRegion with the entire contents of |file|. Compressed
// files are not supported.
bool LoadFromFile(base::File file);
// Loads a pack file from |region| of |file|, returning false on error.
// The file region will be mapped to memory with the mapping owned by
// |data_source_|.
bool LoadFromFileRegion(base::File file,
const base::MemoryMappedFile::Region& region);
// Loads a pack file from |buffer|, returning false on error.
// Data is not copied, |buffer| should stay alive during |DataPack| lifetime.
bool LoadFromBuffer(base::span<const uint8_t> buffer);
// Writes a pack file containing |resources| to |path|. If there are any
// text resources to be written, their encoding must already agree to the
// |textEncodingType| specified. If no text resources are present, please
// indicate BINARY.
static bool WritePack(const base::FilePath& path,
const std::map<uint16_t, std::string_view>& resources,
TextEncodingType textEncodingType);
// ResourceHandle implementation:
bool HasResource(uint16_t resource_id) const override;
std::optional<std::string_view> GetStringView(
uint16_t resource_id) const override;
base::RefCountedStaticMemory* GetStaticMemory(
uint16_t resource_id) const override;
TextEncodingType GetTextEncodingType() const override;
ResourceScaleFactor GetResourceScaleFactor() const override;
#if DCHECK_IS_ON()
// Checks to see if any resource in this DataPack already exists in the list
// of resources.
void CheckForDuplicateResources(
const std::vector<std::unique_ptr<ResourceHandle>>& packs) override;
#endif
// Return Entry or Alias by index of resource or alias table.
const Entry* GetEntryByResourceTableIndex(size_t index) const {
return UNSAFE_TODO(&resource_table_[index]);
}
const Alias* GetAliasByAliasTableIndex(size_t index) const {
return UNSAFE_TODO(&alias_table_[index]);
}
// Return the size of the alias table.
size_t GetAliasTableSize() const { return alias_count_; }
// Return the size of the resource Should only be used for unit-testing
// (more specifically checking that alias table generation removes entries
// for the resources table), as this is an implementation detail.
size_t GetResourceTableSizeForTesting() const { return resource_count_; }
private:
class BufferDataSource;
class MemoryMappedDataSource;
class StringDataSource;
// Does the actual loading of a pack file.
// Called by Load and LoadFromFile and LoadFromBuffer.
base::expected<void, DataPack::FailureReason> LoadImpl(
std::unique_ptr<DataSource> data_source);
const Entry* LookupEntryById(uint16_t resource_id) const;
// Sanity check the file. If it passed the check, register `resource_table_`
// and `alias_table_`.
// `margin_to_skip` represents the size of the margin in bytes before
// resource_table information starts.
// If there is no extra data in data pack, `margin_to_skip` is equal to the
// length of file header. Returns std::nullopt on success or a FailureReason
// on failure.
base::expected<void, DataPack::FailureReason>
SanityCheckFileAndRegisterResources(size_t margin_to_skip,
const uint8_t* data,
size_t data_length);
// Returns the string between `target_offset` and `next_offset` in data pack.
static std::string_view GetStringViewFromOffset(uint32_t target_offset,
uint32_t next_offset,
const uint8_t* data_source);
std::unique_ptr<DataSource> data_source_;
raw_ptr<const Entry, AllowPtrArithmetic> resource_table_;
size_t resource_count_;
raw_ptr<const Alias, AllowPtrArithmetic> alias_table_;
size_t alias_count_;
// Type of encoding for text resources.
TextEncodingType text_encoding_type_;
// The scale of the image in this resource pack relative to images in the 1x
// resource pak.
ResourceScaleFactor resource_scale_factor_;
};
} // namespace ui
#endif // UI_BASE_RESOURCE_DATA_PACK_H_