201 lines
6.2 KiB
C
201 lines
6.2 KiB
C
|
// Copyright 2016 The Crashpad Authors. All rights reserved.
|
||
|
//
|
||
|
// 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 CRASHPAD_CLIENT_SIMPLE_ADDRESS_RANGE_BAG_H_
|
||
|
#define CRASHPAD_CLIENT_SIMPLE_ADDRESS_RANGE_BAG_H_
|
||
|
|
||
|
#include <stdint.h>
|
||
|
#include <string.h>
|
||
|
|
||
|
#include <type_traits>
|
||
|
|
||
|
#include "base/check_op.h"
|
||
|
#include "base/logging.h"
|
||
|
#include "base/numerics/safe_conversions.h"
|
||
|
#include "util/misc/from_pointer_cast.h"
|
||
|
#include "util/numeric/checked_range.h"
|
||
|
|
||
|
namespace crashpad {
|
||
|
|
||
|
//! \brief A bag implementation using a fixed amount of storage, so that it does
|
||
|
//! not perform any dynamic allocations for its operations.
|
||
|
//!
|
||
|
//! The actual bag storage (TSimpleAddressRangeBag::Entry) is POD, so that it
|
||
|
//! can be transmitted over various IPC mechanisms.
|
||
|
template <size_t NumEntries = 64>
|
||
|
class TSimpleAddressRangeBag {
|
||
|
public:
|
||
|
//! Constant and publicly accessible version of the template parameter.
|
||
|
static const size_t num_entries = NumEntries;
|
||
|
|
||
|
//! \brief A single entry in the bag.
|
||
|
struct Entry {
|
||
|
//! \brief The base address of the range.
|
||
|
uint64_t base;
|
||
|
|
||
|
//! \brief The size of the range in bytes.
|
||
|
uint64_t size;
|
||
|
|
||
|
//! \brief Returns the validity of the entry.
|
||
|
//!
|
||
|
//! If #base and #size are both zero, the entry is considered inactive, and
|
||
|
//! this method returns `false`. Otherwise, returns `true`.
|
||
|
bool is_active() const {
|
||
|
return base != 0 || size != 0;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
//! \brief An iterator to traverse all of the active entries in a
|
||
|
//! TSimpleAddressRangeBag.
|
||
|
class Iterator {
|
||
|
public:
|
||
|
explicit Iterator(const TSimpleAddressRangeBag& bag)
|
||
|
: bag_(bag),
|
||
|
current_(0) {
|
||
|
}
|
||
|
|
||
|
Iterator(const Iterator&) = delete;
|
||
|
Iterator& operator=(const Iterator&) = delete;
|
||
|
|
||
|
//! \brief Returns the next entry in the bag, or `nullptr` if at the end of
|
||
|
//! the collection.
|
||
|
const Entry* Next() {
|
||
|
while (current_ < bag_.num_entries) {
|
||
|
const Entry* entry = &bag_.entries_[current_++];
|
||
|
if (entry->is_active()) {
|
||
|
return entry;
|
||
|
}
|
||
|
}
|
||
|
return nullptr;
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
const TSimpleAddressRangeBag& bag_;
|
||
|
size_t current_;
|
||
|
};
|
||
|
|
||
|
TSimpleAddressRangeBag()
|
||
|
: entries_() {
|
||
|
}
|
||
|
|
||
|
TSimpleAddressRangeBag(const TSimpleAddressRangeBag& other) {
|
||
|
*this = other;
|
||
|
}
|
||
|
|
||
|
TSimpleAddressRangeBag& operator=(const TSimpleAddressRangeBag& other) {
|
||
|
memcpy(entries_, other.entries_, sizeof(entries_));
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
//! \brief Returns the number of active entries. The upper limit for this is
|
||
|
//! \a NumEntries.
|
||
|
size_t GetCount() const {
|
||
|
size_t count = 0;
|
||
|
for (size_t i = 0; i < num_entries; ++i) {
|
||
|
if (entries_[i].is_active()) {
|
||
|
++count;
|
||
|
}
|
||
|
}
|
||
|
return count;
|
||
|
}
|
||
|
|
||
|
//! \brief Inserts the given range into the bag. Duplicates and overlapping
|
||
|
//! ranges are supported and allowed, but not coalesced.
|
||
|
//!
|
||
|
//! \param[in] range The range to be inserted. The range must have either a
|
||
|
//! non-zero base address or size.
|
||
|
//!
|
||
|
//! \return `true` if there was space to insert the range into the bag,
|
||
|
//! otherwise `false` with an error logged.
|
||
|
bool Insert(CheckedRange<uint64_t> range) {
|
||
|
DCHECK(range.base() != 0 || range.size() != 0);
|
||
|
|
||
|
for (size_t i = 0; i < num_entries; ++i) {
|
||
|
if (!entries_[i].is_active()) {
|
||
|
entries_[i].base = range.base();
|
||
|
entries_[i].size = range.size();
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
LOG(ERROR) << "no space available to insert range";
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
//! \brief Inserts the given range into the bag. Duplicates and overlapping
|
||
|
//! ranges are supported and allowed, but not coalesced.
|
||
|
//!
|
||
|
//! \param[in] base The base of the range to be inserted. May not be null.
|
||
|
//! \param[in] size The size of the range to be inserted. May not be zero.
|
||
|
//!
|
||
|
//! \return `true` if there was space to insert the range into the bag,
|
||
|
//! otherwise `false` with an error logged.
|
||
|
bool Insert(void* base, size_t size) {
|
||
|
DCHECK(base != nullptr);
|
||
|
DCHECK_NE(0u, size);
|
||
|
return Insert(CheckedRange<uint64_t>(FromPointerCast<uint64_t>(base),
|
||
|
base::checked_cast<uint64_t>(size)));
|
||
|
}
|
||
|
|
||
|
//! \brief Removes the given range from the bag.
|
||
|
//!
|
||
|
//! \param[in] range The range to be removed. The range must have either a
|
||
|
//! non-zero base address or size.
|
||
|
//!
|
||
|
//! \return `true` if the range was found and removed, otherwise `false` with
|
||
|
//! an error logged.
|
||
|
bool Remove(CheckedRange<uint64_t> range) {
|
||
|
DCHECK(range.base() != 0 || range.size() != 0);
|
||
|
|
||
|
for (size_t i = 0; i < num_entries; ++i) {
|
||
|
if (entries_[i].base == range.base() &&
|
||
|
entries_[i].size == range.size()) {
|
||
|
entries_[i].base = entries_[i].size = 0;
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
LOG(ERROR) << "did not find range to remove";
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
//! \brief Removes the given range from the bag.
|
||
|
//!
|
||
|
//! \param[in] base The base of the range to be removed. May not be null.
|
||
|
//! \param[in] size The size of the range to be removed. May not be zero.
|
||
|
//!
|
||
|
//! \return `true` if the range was found and removed, otherwise `false` with
|
||
|
//! an error logged.
|
||
|
bool Remove(void* base, size_t size) {
|
||
|
DCHECK(base != nullptr);
|
||
|
DCHECK_NE(0u, size);
|
||
|
return Remove(CheckedRange<uint64_t>(FromPointerCast<uint64_t>(base),
|
||
|
base::checked_cast<uint64_t>(size)));
|
||
|
}
|
||
|
|
||
|
|
||
|
private:
|
||
|
Entry entries_[NumEntries];
|
||
|
};
|
||
|
|
||
|
//! \brief A TSimpleAddressRangeBag with default template parameters.
|
||
|
using SimpleAddressRangeBag = TSimpleAddressRangeBag<64>;
|
||
|
|
||
|
static_assert(std::is_standard_layout<SimpleAddressRangeBag>::value,
|
||
|
"SimpleAddressRangeBag must be standard layout");
|
||
|
|
||
|
} // namespace crashpad
|
||
|
|
||
|
#endif // CRASHPAD_CLIENT_SIMPLE_ADDRESS_RANGE_BAG_H_
|