Commit fbd31eb8 authored by tommi@chromium.org's avatar tommi@chromium.org

Implementation of an STL compatible allocator for Courgette on Windows.

This is to better handle low memory situations when applying a differential
patch to a large Chrome setup.

For reading input files, I'm also switching to using memory mapped files
instead of ReadFileToString to reduce the load on the heap.

TEST=courgette.exe should succeed in applying a patch between two
  chrome 10.x archives on an XP machine with 180MB of physical memory and
  no page file.
BUG=72459,73209

Review URL: http://codereview.chromium.org/6597038

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@76320 0039d316-1c4b-4281-b951-d872f2087c98
parent 4b764ded
...@@ -595,7 +595,7 @@ class GraphAdjuster : public AdjustmentMethod { ...@@ -595,7 +595,7 @@ class GraphAdjuster : public AdjustmentMethod {
void CollectTraces(const AssemblyProgram* program, Trace* abs32, Trace* rel32, void CollectTraces(const AssemblyProgram* program, Trace* abs32, Trace* rel32,
bool is_model) { bool is_model) {
const std::vector<Instruction*>& instructions = program->instructions(); const InstructionVector& instructions = program->instructions();
for (size_t i = 0; i < instructions.size(); ++i) { for (size_t i = 0; i < instructions.size(); ++i) {
Instruction* instruction = instructions.at(i); Instruction* instruction = instructions.at(i);
if (Label* label = program->InstructionAbs32Label(instruction)) if (Label* label = program->InstructionAbs32Label(instruction))
......
...@@ -1252,7 +1252,7 @@ class Adjuster : public AdjustmentMethod { ...@@ -1252,7 +1252,7 @@ class Adjuster : public AdjustmentMethod {
void CollectTraces(const AssemblyProgram* program, Trace* abs32, Trace* rel32, void CollectTraces(const AssemblyProgram* program, Trace* abs32, Trace* rel32,
bool is_model) { bool is_model) {
label_info_maker_.ResetDebugLabel(); label_info_maker_.ResetDebugLabel();
const std::vector<Instruction*>& instructions = program->instructions(); const InstructionVector& instructions = program->instructions();
for (size_t i = 0; i < instructions.size(); ++i) { for (size_t i = 0; i < instructions.size(); ++i) {
Instruction* instruction = instructions.at(i); Instruction* instruction = instructions.at(i);
if (Label* label = program->InstructionAbs32Label(instruction)) if (Label* label = program->InstructionAbs32Label(instruction))
......
...@@ -12,12 +12,16 @@ ...@@ -12,12 +12,16 @@
#include "base/basictypes.h" #include "base/basictypes.h"
#include "courgette/image_info.h" #include "courgette/image_info.h"
#include "courgette/memory_allocator.h"
namespace courgette { namespace courgette {
class EncodedProgram; class EncodedProgram;
class Instruction; class Instruction;
typedef std::vector<Instruction*, MemoryAllocator<Instruction*> >
InstructionVector;
// A Label is a symbolic reference to an address. Unlike a conventional // A Label is a symbolic reference to an address. Unlike a conventional
// assembly language, we always know the address. The address will later be // assembly language, we always know the address. The address will later be
// stored in a table and the Label will be replaced with the index into the // stored in a table and the Label will be replaced with the index into the
...@@ -89,7 +93,7 @@ class AssemblyProgram { ...@@ -89,7 +93,7 @@ class AssemblyProgram {
EncodedProgram* Encode() const; EncodedProgram* Encode() const;
// Accessor for instruction list. // Accessor for instruction list.
const std::vector<Instruction*>& instructions() const { const InstructionVector& instructions() const {
return instructions_; return instructions_;
} }
...@@ -101,7 +105,6 @@ class AssemblyProgram { ...@@ -101,7 +105,6 @@ class AssemblyProgram {
// otherwise returns NULL. // otherwise returns NULL.
Label* InstructionRel32Label(const Instruction* instruction) const; Label* InstructionRel32Label(const Instruction* instruction) const;
private: private:
void Emit(Instruction* instruction) { instructions_.push_back(instruction); } void Emit(Instruction* instruction) { instructions_.push_back(instruction); }
...@@ -118,7 +121,7 @@ class AssemblyProgram { ...@@ -118,7 +121,7 @@ class AssemblyProgram {
uint64 image_base_; // Desired or mandated base address of image. uint64 image_base_; // Desired or mandated base address of image.
std::vector<Instruction*> instructions_; // All the instructions in program. InstructionVector instructions_; // All the instructions in program.
// These are lookup maps to find the label associated with a given address. // These are lookup maps to find the label associated with a given address.
// We have separate label spaces for addresses referenced by rel32 labels and // We have separate label spaces for addresses referenced by rel32 labels and
......
...@@ -30,6 +30,8 @@ ...@@ -30,6 +30,8 @@
'ensemble_create.cc', 'ensemble_create.cc',
'image_info.cc', 'image_info.cc',
'image_info.h', 'image_info.h',
'memory_allocator.cc',
'memory_allocator.h',
'region.h', 'region.h',
'simple_delta.cc', 'simple_delta.cc',
'simple_delta.h', 'simple_delta.h',
......
...@@ -39,18 +39,19 @@ EncodedProgram::EncodedProgram() : image_base_(0) {} ...@@ -39,18 +39,19 @@ EncodedProgram::EncodedProgram() : image_base_(0) {}
EncodedProgram::~EncodedProgram() {} EncodedProgram::~EncodedProgram() {}
// Serializes a vector of integral values using Varint32 coding. // Serializes a vector of integral values using Varint32 coding.
template<typename T> template<typename T, typename A>
void WriteVector(const std::vector<T>& items, SinkStream* buffer) { void WriteVector(const std::vector<T, A>& items, SinkStream* buffer) {
size_t count = items.size(); size_t count = items.size();
buffer->WriteSizeVarint32(count); buffer->WriteSizeVarint32(count);
for (size_t i = 0; i < count; ++i) { for (size_t i = 0; i < count; ++i) {
COMPILE_ASSERT(sizeof(T) <= sizeof(uint32), T_must_fit_in_uint32); COMPILE_ASSERT(sizeof(T) <= sizeof(uint32), // NOLINT
T_must_fit_in_uint32);
buffer->WriteSizeVarint32(items[i]); buffer->WriteSizeVarint32(items[i]);
} }
} }
template<typename T> template<typename T, typename A>
bool ReadVector(std::vector<T>* items, SourceStream* buffer) { bool ReadVector(std::vector<T, A>* items, SourceStream* buffer) {
uint32 count; uint32 count;
if (!buffer->ReadVarint32(&count)) if (!buffer->ReadVarint32(&count))
return false; return false;
...@@ -68,7 +69,8 @@ bool ReadVector(std::vector<T>* items, SourceStream* buffer) { ...@@ -68,7 +69,8 @@ bool ReadVector(std::vector<T>* items, SourceStream* buffer) {
} }
// Serializes a vector, using delta coding followed by Varint32 coding. // Serializes a vector, using delta coding followed by Varint32 coding.
void WriteU32Delta(const std::vector<uint32>& set, SinkStream* buffer) { template<typename A>
void WriteU32Delta(const std::vector<uint32, A>& set, SinkStream* buffer) {
size_t count = set.size(); size_t count = set.size();
buffer->WriteSizeVarint32(count); buffer->WriteSizeVarint32(count);
uint32 prev = 0; uint32 prev = 0;
...@@ -80,7 +82,8 @@ void WriteU32Delta(const std::vector<uint32>& set, SinkStream* buffer) { ...@@ -80,7 +82,8 @@ void WriteU32Delta(const std::vector<uint32>& set, SinkStream* buffer) {
} }
} }
static bool ReadU32Delta(std::vector<uint32>* set, SourceStream* buffer) { template <typename A>
static bool ReadU32Delta(std::vector<uint32, A>* set, SourceStream* buffer) {
uint32 count; uint32 count;
if (!buffer->ReadVarint32(&count)) if (!buffer->ReadVarint32(&count))
...@@ -109,8 +112,8 @@ static bool ReadU32Delta(std::vector<uint32>* set, SourceStream* buffer) { ...@@ -109,8 +112,8 @@ static bool ReadU32Delta(std::vector<uint32>* set, SourceStream* buffer) {
// the possibility of a greater size for experiments comparing Varint32 encoding // the possibility of a greater size for experiments comparing Varint32 encoding
// of a vector of larger integrals vs a plain form.) // of a vector of larger integrals vs a plain form.)
// //
template<typename T> template<typename T, typename A>
void WriteVectorU8(const std::vector<T>& items, SinkStream* buffer) { void WriteVectorU8(const std::vector<T, A>& items, SinkStream* buffer) {
size_t count = items.size(); size_t count = items.size();
buffer->WriteSizeVarint32(count); buffer->WriteSizeVarint32(count);
if (count != 0) { if (count != 0) {
...@@ -119,8 +122,8 @@ void WriteVectorU8(const std::vector<T>& items, SinkStream* buffer) { ...@@ -119,8 +122,8 @@ void WriteVectorU8(const std::vector<T>& items, SinkStream* buffer) {
} }
} }
template<typename T> template<typename T, typename A>
bool ReadVectorU8(std::vector<T>* items, SourceStream* buffer) { bool ReadVectorU8(std::vector<T, A>* items, SourceStream* buffer) {
uint32 count; uint32 count;
if (!buffer->ReadVarint32(&count)) if (!buffer->ReadVarint32(&count))
return false; return false;
...@@ -146,7 +149,7 @@ void EncodedProgram::DefineAbs32Label(int index, RVA value) { ...@@ -146,7 +149,7 @@ void EncodedProgram::DefineAbs32Label(int index, RVA value) {
static const RVA kUnassignedRVA = static_cast<RVA>(-1); static const RVA kUnassignedRVA = static_cast<RVA>(-1);
void EncodedProgram::DefineLabelCommon(std::vector<RVA>* rvas, void EncodedProgram::DefineLabelCommon(RvaVector* rvas,
int index, int index,
RVA rva) { RVA rva) {
if (static_cast<int>(rvas->size()) <= index) { if (static_cast<int>(rvas->size()) <= index) {
...@@ -163,7 +166,7 @@ void EncodedProgram::EndLabels() { ...@@ -163,7 +166,7 @@ void EncodedProgram::EndLabels() {
FinishLabelsCommon(&rel32_rva_); FinishLabelsCommon(&rel32_rva_);
} }
void EncodedProgram::FinishLabelsCommon(std::vector<RVA>* rvas) { void EncodedProgram::FinishLabelsCommon(RvaVector* rvas) {
// Replace all unassigned slots with the value at the previous index so they // Replace all unassigned slots with the value at the previous index so they
// delta-encode to zero. (There might be better values than zero. The way to // delta-encode to zero. (There might be better values than zero. The way to
// get that is have the higher level assembly program assign the unassigned // get that is have the higher level assembly program assign the unassigned
...@@ -183,7 +186,7 @@ void EncodedProgram::AddOrigin(RVA origin) { ...@@ -183,7 +186,7 @@ void EncodedProgram::AddOrigin(RVA origin) {
origins_.push_back(origin); origins_.push_back(origin);
} }
void EncodedProgram::AddCopy(int count, const void* bytes) { void EncodedProgram::AddCopy(uint32 count, const void* bytes) {
const uint8* source = static_cast<const uint8*>(bytes); const uint8* source = static_cast<const uint8*>(bytes);
// Fold adjacent COPY instructions into one. This nearly halves the size of // Fold adjacent COPY instructions into one. This nearly halves the size of
...@@ -199,7 +202,7 @@ void EncodedProgram::AddCopy(int count, const void* bytes) { ...@@ -199,7 +202,7 @@ void EncodedProgram::AddCopy(int count, const void* bytes) {
} }
if (ops_.back() == COPY) { if (ops_.back() == COPY) {
copy_counts_.back() += count; copy_counts_.back() += count;
for (int i = 0; i < count; ++i) { for (uint32 i = 0; i < count; ++i) {
copy_bytes_.push_back(source[i]); copy_bytes_.push_back(source[i]);
} }
return; return;
...@@ -212,7 +215,7 @@ void EncodedProgram::AddCopy(int count, const void* bytes) { ...@@ -212,7 +215,7 @@ void EncodedProgram::AddCopy(int count, const void* bytes) {
} else { } else {
ops_.push_back(COPY); ops_.push_back(COPY);
copy_counts_.push_back(count); copy_counts_.push_back(count);
for (int i = 0; i < count; ++i) { for (uint32 i = 0; i < count; ++i) {
copy_bytes_.push_back(source[i]); copy_bytes_.push_back(source[i]);
} }
} }
...@@ -349,8 +352,8 @@ bool EncodedProgram::ReadFrom(SourceStreamSet* streams) { ...@@ -349,8 +352,8 @@ bool EncodedProgram::ReadFrom(SourceStreamSet* streams) {
// Safe, non-throwing version of std::vector::at(). Returns 'true' for success, // Safe, non-throwing version of std::vector::at(). Returns 'true' for success,
// 'false' for out-of-bounds index error. // 'false' for out-of-bounds index error.
template<typename T> template<typename T, typename A>
bool VectorAt(const std::vector<T>& v, size_t index, T* output) { bool VectorAt(const std::vector<T, A>& v, size_t index, T* output) {
if (index >= v.size()) if (index >= v.size())
return false; return false;
*output = v[index]; *output = v[index];
...@@ -390,11 +393,11 @@ bool EncodedProgram::AssembleTo(SinkStream* final_buffer) { ...@@ -390,11 +393,11 @@ bool EncodedProgram::AssembleTo(SinkStream* final_buffer) {
} }
case COPY: { case COPY: {
int count; uint32 count;
if (!VectorAt(copy_counts_, ix_copy_counts, &count)) if (!VectorAt(copy_counts_, ix_copy_counts, &count))
return false; return false;
++ix_copy_counts; ++ix_copy_counts;
for (int i = 0; i < count; ++i) { for (uint32 i = 0; i < count; ++i) {
uint8 b; uint8 b;
if (!VectorAt(copy_bytes_, ix_copy_bytes, &b)) if (!VectorAt(copy_bytes_, ix_copy_bytes, &b))
return false; return false;
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include "base/basictypes.h" #include "base/basictypes.h"
#include "courgette/image_info.h" #include "courgette/image_info.h"
#include "courgette/memory_allocator.h"
namespace courgette { namespace courgette {
...@@ -37,7 +38,7 @@ class EncodedProgram { ...@@ -37,7 +38,7 @@ class EncodedProgram {
// (3) Add instructions in the order needed to generate bytes of file. // (3) Add instructions in the order needed to generate bytes of file.
void AddOrigin(RVA rva); void AddOrigin(RVA rva);
void AddCopy(int count, const void* bytes); void AddCopy(uint32 count, const void* bytes);
void AddRel32(int label_index); void AddRel32(int label_index);
void AddAbs32(int label_index); void AddAbs32(int label_index);
void AddMakeRelocs(); void AddMakeRelocs();
...@@ -67,25 +68,30 @@ class EncodedProgram { ...@@ -67,25 +68,30 @@ class EncodedProgram {
OP_LAST OP_LAST
}; };
typedef std::vector<RVA, MemoryAllocator<RVA> > RvaVector;
typedef std::vector<uint32, MemoryAllocator<uint32> > UInt32Vector;
typedef std::vector<uint8, MemoryAllocator<uint8> > UInt8Vector;
typedef std::vector<OP, MemoryAllocator<OP> > OPVector;
void DebuggingSummary(); void DebuggingSummary();
void GenerateBaseRelocations(SinkStream *buffer); void GenerateBaseRelocations(SinkStream *buffer);
void DefineLabelCommon(std::vector<RVA>*, int, RVA); void DefineLabelCommon(RvaVector*, int, RVA);
void FinishLabelsCommon(std::vector<RVA>* addresses); void FinishLabelsCommon(RvaVector* addresses);
// Binary assembly language tables. // Binary assembly language tables.
uint64 image_base_; uint64 image_base_;
std::vector<RVA> rel32_rva_; RvaVector rel32_rva_;
std::vector<RVA> abs32_rva_; RvaVector abs32_rva_;
std::vector<OP> ops_; OPVector ops_;
std::vector<RVA> origins_; RvaVector origins_;
std::vector<int> copy_counts_; UInt32Vector copy_counts_;
std::vector<uint8> copy_bytes_; UInt8Vector copy_bytes_;
std::vector<uint32> rel32_ix_; UInt32Vector rel32_ix_;
std::vector<uint32> abs32_ix_; UInt32Vector abs32_ix_;
// Table of the addresses containing abs32 relocations; computed during // Table of the addresses containing abs32 relocations; computed during
// assembly, used to generate base relocation table. // assembly, used to generate base relocation table.
std::vector<uint32> abs32_relocs_; UInt32Vector abs32_relocs_;
DISALLOW_COPY_AND_ASSIGN(EncodedProgram); DISALLOW_COPY_AND_ASSIGN(EncodedProgram);
}; };
......
...@@ -364,50 +364,32 @@ Status ApplyEnsemblePatch(SourceStream* base, ...@@ -364,50 +364,32 @@ Status ApplyEnsemblePatch(SourceStream* base,
Status ApplyEnsemblePatch(const FilePath::CharType* old_file_name, Status ApplyEnsemblePatch(const FilePath::CharType* old_file_name,
const FilePath::CharType* patch_file_name, const FilePath::CharType* patch_file_name,
const FilePath::CharType* new_file_name) { const FilePath::CharType* new_file_name) {
Status status;
// First read enough of the patch file to validate the header is well-formed. // First read enough of the patch file to validate the header is well-formed.
// A few varint32 numbers should fit in 100. // A few varint32 numbers should fit in 100.
FilePath patch_file_path(patch_file_name); FilePath patch_file_path(patch_file_name);
const int BIG_ENOUGH_FOR_HEADER = 100; file_util::MemoryMappedFile patch_file;
char buffer[BIG_ENOUGH_FOR_HEADER]; if (!patch_file.Initialize(patch_file_path))
int read_count =
file_util::ReadFile(patch_file_path, buffer, sizeof(buffer));
if (read_count < 0)
return C_READ_OPEN_ERROR; return C_READ_OPEN_ERROR;
// 'Dry-run' the first step of the patch process to validate format of header. // 'Dry-run' the first step of the patch process to validate format of header.
SourceStream patch_header_stream; SourceStream patch_header_stream;
patch_header_stream.Init(buffer, read_count); patch_header_stream.Init(patch_file.data(), patch_file.length());
EnsemblePatchApplication patch_process; EnsemblePatchApplication patch_process;
status = patch_process.ReadHeader(&patch_header_stream); Status status = patch_process.ReadHeader(&patch_header_stream);
if (status != C_OK) if (status != C_OK)
return status; return status;
// Header smells good so read the whole patch file for real.
int64 patch_file_size = 0;
if (!file_util::GetFileSize(patch_file_path, &patch_file_size))
return C_READ_ERROR;
std::string patch_file_buffer;
patch_file_buffer.reserve(static_cast<size_t>(patch_file_size));
if (!file_util::ReadFileToString(patch_file_path, &patch_file_buffer))
return C_READ_ERROR;
// Read the old_file. // Read the old_file.
FilePath old_file_path(old_file_name); FilePath old_file_path(old_file_name);
int64 old_file_size = 0; file_util::MemoryMappedFile old_file;
if (!file_util::GetFileSize(old_file_path, &old_file_size)) if (!old_file.Initialize(old_file_path))
return C_READ_ERROR;
std::string old_file_buffer;
old_file_buffer.reserve(static_cast<size_t>(old_file_size));
if (!file_util::ReadFileToString(old_file_path, &old_file_buffer))
return C_READ_ERROR; return C_READ_ERROR;
// Apply patch on streams. // Apply patch on streams.
SourceStream old_source_stream; SourceStream old_source_stream;
SourceStream patch_source_stream; SourceStream patch_source_stream;
old_source_stream.Init(old_file_buffer); old_source_stream.Init(old_file.data(), old_file.length());
patch_source_stream.Init(patch_file_buffer); patch_source_stream.Init(patch_file.data(), patch_file.length());
SinkStream new_sink_stream; SinkStream new_sink_stream;
status = ApplyEnsemblePatch(&old_source_stream, &patch_source_stream, status = ApplyEnsemblePatch(&old_source_stream, &patch_source_stream,
&new_sink_stream); &new_sink_stream);
......
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "courgette/memory_allocator.h"
#include <map>
#include "base/file_util.h"
#ifdef OS_WIN
namespace courgette {
// TempFile
TempFile::TempFile() : file_(base::kInvalidPlatformFileValue), size_(0) {
}
TempFile::~TempFile() {
Close();
}
void TempFile::Close() {
if (valid()) {
base::ClosePlatformFile(file_);
file_ = base::kInvalidPlatformFileValue;
size_ = 0;
}
}
bool TempFile::Create() {
DCHECK(file_ == base::kInvalidPlatformFileValue);
FilePath path;
if (file_util::CreateTemporaryFile(&path)) {
bool created = false;
base::PlatformFileError error_code = base::PLATFORM_FILE_OK;
int flags = base::PLATFORM_FILE_OPEN_ALWAYS | base::PLATFORM_FILE_READ |
base::PLATFORM_FILE_WRITE |
base::PLATFORM_FILE_DELETE_ON_CLOSE |
base::PLATFORM_FILE_TEMPORARY;
file_ = base::CreatePlatformFile(path, flags, &created, &error_code);
PLOG_IF(ERROR, file_ == base::kInvalidPlatformFileValue)
<< "CreatePlatformFile";
}
return valid();
}
bool TempFile::valid() const {
return file_ != base::kInvalidPlatformFileValue;
}
base::PlatformFile TempFile::handle() const {
return file_;
}
size_t TempFile::size() const {
return size_;
}
bool TempFile::SetSize(size_t size) {
bool ret = base::TruncatePlatformFile(file_, size);
if (ret)
size_ = size;
return ret;
}
// FileMapping
FileMapping::FileMapping() : mapping_(NULL), view_(NULL) {
}
FileMapping::~FileMapping() {
Close();
}
bool FileMapping::Create(HANDLE file, size_t size) {
DCHECK(file != INVALID_HANDLE_VALUE);
DCHECK(!valid());
mapping_ = ::CreateFileMapping(file, NULL, PAGE_READWRITE, 0, 0, NULL);
if (mapping_)
view_ = ::MapViewOfFile(mapping_, FILE_MAP_WRITE, 0, 0, size);
if (!valid()) {
PLOG(ERROR) << "Failed to map file";
Close();
}
return valid();
}
void FileMapping::Close() {
if (view_)
::UnmapViewOfFile(view_);
if (mapping_)
::CloseHandle(mapping_);
mapping_ = NULL;
view_ = NULL;
}
bool FileMapping::valid() const {
return view_ != NULL;
}
void* FileMapping::view() const {
return view_;
}
// TempMapping
TempMapping::TempMapping() {
}
TempMapping::~TempMapping() {
}
bool TempMapping::Initialize(size_t size) {
// TODO(tommi): The assumption here is that the alignment of pointers (this)
// is as strict or stricter than the alignment of the element type. This is
// not always true, e.g. __m128 has 16-byte alignment.
size += sizeof(this);
bool ret = file_.Create() && file_.SetSize(size) &&
mapping_.Create(file_.handle(), size);
if (ret) {
TempMapping** write = reinterpret_cast<TempMapping**>(mapping_.view());
write[0] = this;
}
return ret;
}
void* TempMapping::memory() const {
uint8* mem = reinterpret_cast<uint8*>(mapping_.view());
if (mem)
mem += sizeof(this);
DCHECK(mem);
return mem;
}
// static
TempMapping* TempMapping::GetMappingFromPtr(void* mem) {
TempMapping* ret = NULL;
if (mem) {
ret = reinterpret_cast<TempMapping**>(mem)[-1];
}
DCHECK(ret);
return ret;
}
} // namespace courgette
#endif // OS_WIN
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef COURGETTE_MEMORY_ALLOCATOR_H_
#define COURGETTE_MEMORY_ALLOCATOR_H_
#include <memory>
#include "base/basictypes.h"
#include "base/logging.h"
#include "base/platform_file.h"
namespace courgette {
#ifdef OS_WIN
// Manages a temporary file. The file is created in the %TEMP% folder and
// is deleted when the file handle is closed.
// NOTE: Since the file will be used as backing for a memory allocation,
// it will never be so big that size_t cannot represent its size.
class TempFile {
public:
TempFile();
~TempFile();
bool Create();
void Close();
bool SetSize(size_t size);
// Returns true iff the temp file is currently open.
bool valid() const;
// Returns the handle of the temporary file or INVALID_HANDLE_VALUE if
// a temp file has not been created.
base::PlatformFile handle() const;
// Returns the size of the temp file. If the temp file doesn't exist,
// the return value is 0.
size_t size() const;
protected:
base::PlatformFile file_;
size_t size_;
};
// Manages a read/write virtual mapping of a physical file.
class FileMapping {
public:
FileMapping();
~FileMapping();
// Map a file from beginning to |size|.
bool Create(HANDLE file, size_t size);
void Close();
// Returns true iff a mapping has been created.
bool valid() const;
// Returns a writable pointer to the beginning of the memory mapped file.
// If Create has not been called successfully, return value is NULL.
void* view() const;
protected:
HANDLE mapping_;
void* view_;
};
// Manages a temporary file and a memory mapping of the temporary file.
// The memory that this class manages holds a pointer back to the TempMapping
// object itself, so that given a memory pointer allocated by this class,
// you can get a pointer to the TempMapping instance that owns that memory.
class TempMapping {
public:
TempMapping();
~TempMapping();
// Creates a temporary file of size |size| and maps it into the current
// process' address space.
bool Initialize(size_t size);
// Returns a writable pointer to the reserved memory.
void* memory() const;
// Returns a pointer to the TempMapping instance that allocated the |mem|
// block of memory. It's the callers responsibility to make sure that
// the memory block was allocated by the TempMapping class.
static TempMapping* GetMappingFromPtr(void* mem);
protected:
TempFile file_;
FileMapping mapping_;
};
// An STL compatible memory allocator class that allocates memory either
// from the heap or via a temporary file. A file allocation will be made
// if either the requested memory size exceeds |kMaxHeapAllocationSize|
// or if a heap allocation fails.
// Allocating the memory as a mapping of a temporary file solves the problem
// that there might not be enough physical memory and pagefile to support the
// allocation. This can happen because these resources are too small, or
// already committed to other processes. Provided there is enough disk, the
// temporary file acts like a pagefile that other processes can't access.
template<class T>
class MemoryAllocator {
public:
typedef T value_type;
typedef value_type* pointer;
typedef value_type& reference;
typedef const value_type* const_pointer;
typedef const value_type& const_reference;
typedef size_t size_type;
typedef ptrdiff_t difference_type;
// Each allocation is tagged with a single byte so that we know how to
// deallocate it.
enum AllocationType {
HEAP_ALLOCATION,
FILE_ALLOCATION,
};
// 5MB is the maximum heap allocation size that we'll attempt.
// When applying a patch for Chrome 10.X we found that at this
// threshold there were 17 allocations higher than this threshold
// (largest at 136MB) 10 allocations just below the threshold and 6362
// smaller allocations.
static const size_t kMaxHeapAllocationSize = 1024 * 1024 * 5;
template<class OtherT>
struct rebind {
// convert an MemoryAllocator<T> to a MemoryAllocator<OtherT>
typedef MemoryAllocator<OtherT> other;
};
MemoryAllocator() _THROW0() {
}
explicit MemoryAllocator(const MemoryAllocator<T>& other) _THROW0() {
}
template<class OtherT>
explicit MemoryAllocator(const MemoryAllocator<OtherT>& other) _THROW0() {
}
~MemoryAllocator() {
}
void deallocate(pointer ptr, size_type size) {
uint8* mem = reinterpret_cast<uint8*>(ptr);
mem -= sizeof(T);
if (mem[0] == HEAP_ALLOCATION) {
delete [] mem;
} else {
DCHECK_EQ(static_cast<uint8>(FILE_ALLOCATION), mem[0]);
TempMapping* mapping = TempMapping::GetMappingFromPtr(mem);
delete mapping;
}
}
pointer allocate(size_type count) {
// We use the first byte of each allocation to mark the allocation type.
// However, so that the allocation is properly aligned, we allocate an
// extra element and then use the first byte of the first element
// to mark the allocation type.
count++;
if (count > max_size())
throw std::length_error("overflow");
size_type bytes = count * sizeof(T);
uint8* mem = NULL;
// First see if we can do this allocation on the heap.
if (count < kMaxHeapAllocationSize)
mem = new(std::nothrow) uint8[bytes];
if (mem != NULL) {
mem[0] = static_cast<uint8>(HEAP_ALLOCATION);
} else {
// If either the heap allocation failed or the request exceeds the
// max heap allocation threshold, we back the allocation with a temp file.
TempMapping* mapping = new TempMapping();
if (!mapping->Initialize(bytes)) {
delete mapping;
throw std::bad_alloc("TempMapping::Initialize");
}
mem = reinterpret_cast<uint8*>(mapping->memory());
mem[0] = static_cast<uint8>(FILE_ALLOCATION);
}
return reinterpret_cast<pointer>(mem + sizeof(T));
}
pointer allocate(size_type count, const void* hint) {
return allocate(count);
}
void construct(pointer ptr, const T& value) {
::new(ptr) T(value);
}
void destroy(pointer ptr) {
ptr->~T();
}
size_t max_size() const _THROW0() {
size_type count = static_cast<size_type>(-1) / sizeof(T);
return (0 < count ? count : 1);
}
};
#else // OS_WIN
// On Mac, Linux, we just use the default STL allocator.
template<class T>
class MemoryAllocator : public std::allocator<T> {
public:
};
#endif // OS_WIN
} // namespace courgette
#endif // COURGETTE_MEMORY_ALLOCATOR_H_
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
#include "base/basictypes.h" #include "base/basictypes.h"
#include "courgette/memory_allocator.h"
#include "courgette/region.h" #include "courgette/region.h"
namespace courgette { namespace courgette {
...@@ -151,7 +152,11 @@ class SinkStream { ...@@ -151,7 +152,11 @@ class SinkStream {
void Retire(); void Retire();
private: private:
std::string buffer_; // Use a string to manage the stream's memory. // Use a string to manage the stream's memory.
typedef std::basic_string<char,
std::char_traits<char>,
MemoryAllocator<char> > SinkBuffer;
SinkBuffer buffer_;
DISALLOW_COPY_AND_ASSIGN(SinkStream); DISALLOW_COPY_AND_ASSIGN(SinkStream);
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment