mirror of
https://boringssl.googlesource.com/boringssl
synced 2026-07-21 14:43:51 +00:00
bd6f41c308
... and then run clang-format on the changed files to reflow any comment blocks that exceeded line lengths. (Which generated a bunch of noise formatting changes, but probably that reduces noise in future CLs.) No semantic change to the code. Change-Id: I455da9faaaedda3e751ac91b5eb43cbc662d68a6 Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/97367 Reviewed-by: David Benjamin <davidben@google.com> Commit-Queue: Adam Langley <agl@google.com> Auto-Submit: Adam Langley <agl@google.com>
672 lines
19 KiB
C++
672 lines
19 KiB
C++
// Copyright 2025 The BoringSSL Authors
|
|
//
|
|
// 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
|
|
//
|
|
// https://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 OPENSSL_HEADER_CRYPTO_MEM_INTERNAL_H
|
|
#define OPENSSL_HEADER_CRYPTO_MEM_INTERNAL_H
|
|
|
|
#include <openssl/mem.h>
|
|
|
|
#include <algorithm>
|
|
#include <memory>
|
|
#include <type_traits>
|
|
#include <utility>
|
|
|
|
#include <openssl/err.h>
|
|
#include <openssl/span.h>
|
|
|
|
#include "internal.h"
|
|
|
|
|
|
BSSL_NAMESPACE_BEGIN
|
|
|
|
// Internal allocation-dependent functions.
|
|
//
|
|
// This header is separate from crypto/internal.h because there are some files
|
|
// which must avoid `OPENSSL_malloc`, to avoid a circular dependency, but
|
|
// need other support routines in crypto/internal.h. (See
|
|
// `_BORINGSSL_PROHIBIT_OPENSSL_MALLOC`.)
|
|
|
|
|
|
// Memory allocation.
|
|
|
|
// New behaves like `new` but uses `OPENSSL_malloc` for memory allocation. It
|
|
// returns nullptr on allocation error. It only implements single-object
|
|
// allocation and not new T[n].
|
|
//
|
|
// When called with no arguments, it performs value-initialization, not
|
|
// default-initialization. This means that, if it selects a non-user-provided
|
|
// constructor, the object will be zero-initialized. (As in any C++ type, once
|
|
// `T` gains a user-provided constructor, it is responsible for initializing
|
|
// all fields explicitly.)
|
|
//
|
|
// Note: unlike `new`, this does not support non-public constructors.
|
|
template <typename T, typename... Args>
|
|
T *New(Args &&...args) {
|
|
void *t = OPENSSL_malloc(sizeof(T));
|
|
if (t == nullptr) {
|
|
return nullptr;
|
|
}
|
|
return new (t) T(std::forward<Args>(args)...);
|
|
}
|
|
|
|
// Delete behaves like `delete` but uses `OPENSSL_free` to release memory.
|
|
//
|
|
// Note: unlike `delete` this does not support non-public destructors.
|
|
template <typename T>
|
|
void Delete(T *t) {
|
|
if (t != nullptr) {
|
|
t->~T();
|
|
OPENSSL_free(t);
|
|
}
|
|
}
|
|
|
|
namespace internal {
|
|
|
|
// All types with kAllowUniquePtr set may be used with UniquePtr. Other types
|
|
// may be C structs which require a `BORINGSSL_MAKE_DELETER` registration. Where
|
|
// an internal type cannot be annotated (e.g. an alias of std::variant), use
|
|
// `BORINGSSL_MAKE_DELETER(T, Delete)`.
|
|
template <typename T>
|
|
struct DeleterImpl<T, std::enable_if_t<T::kAllowUniquePtr>> {
|
|
static void Free(T *t) { Delete(t); }
|
|
};
|
|
|
|
// All types with kAllowRefCountedUniquePtr may be used with UniquePtr, which
|
|
// then will behave like std::shared_ptr.
|
|
template <typename T>
|
|
struct DeleterImpl<T, std::enable_if_t<T::kAllowRefCountedUniquePtr>> {
|
|
static void Free(T *t) { t->DecRefInternal(); }
|
|
};
|
|
|
|
} // namespace internal
|
|
|
|
// All types with kAllowRefCountedUniquePtr types also automatically get an
|
|
// UpRef function. Other types may be C structs which require a
|
|
// `BORINGSSL_MAKE_UP_REF` registration.
|
|
template <typename T, typename = std::enable_if_t<T::kAllowRefCountedUniquePtr>>
|
|
inline UniquePtr<T> UpRef(const T *v) {
|
|
if (v != nullptr) {
|
|
v->UpRefInternal();
|
|
}
|
|
return UniquePtr<T>(const_cast<T *>(v));
|
|
}
|
|
template <typename T, typename = std::enable_if_t<T::kAllowRefCountedUniquePtr>>
|
|
inline UniquePtr<T> UpRef(const UniquePtr<T> &ptr) {
|
|
return UpRef(ptr.get());
|
|
}
|
|
|
|
// MakeUnique behaves like `std::make_unique` but returns nullptr on allocation
|
|
// error.
|
|
template <typename T, typename... Args>
|
|
UniquePtr<T> MakeUnique(Args &&...args) {
|
|
return UniquePtr<T>(New<T>(std::forward<Args>(args)...));
|
|
}
|
|
|
|
|
|
// RefCounted is a common base for ref-counted types. This is an instance of the
|
|
// C++ curiously-recurring template pattern, so a type Foo must subclass
|
|
// RefCounted<Foo>. It additionally must friend RefCounted<Foo> to allow calling
|
|
// the destructor.
|
|
template <typename Derived>
|
|
class RefCounted {
|
|
public:
|
|
static constexpr bool kAllowRefCountedUniquePtr = true;
|
|
|
|
RefCounted(const RefCounted &) = delete;
|
|
RefCounted &operator=(const RefCounted &) = delete;
|
|
|
|
// These methods are intentionally named differently from `bssl::UpRef` to
|
|
// avoid a collision. Only the implementations of `FOO_up_ref` and `FOO_free`
|
|
// should call these. `DecRefInternal` returns true if the object was freed
|
|
// and false if there are still references.
|
|
void UpRefInternal() const {
|
|
// Safety: the following call does not mutate anything other than the atomic
|
|
// ref-count variable.
|
|
CRYPTO_refcount_inc(&references_);
|
|
}
|
|
bool DecRefInternal() {
|
|
if (CRYPTO_refcount_dec_and_test_zero(&references_)) {
|
|
Derived *d = static_cast<Derived *>(this);
|
|
d->~Derived();
|
|
OPENSSL_free(d);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
protected:
|
|
// Ensure that only `Derived`, which must inherit from `RefCounted<Derived>`,
|
|
// can call the constructor. This catches bugs where someone inherited from
|
|
// the wrong base.
|
|
class CheckSubClass {
|
|
private:
|
|
friend Derived;
|
|
CheckSubClass() = default;
|
|
};
|
|
RefCounted(CheckSubClass) {
|
|
static_assert(std::is_base_of_v<RefCounted, Derived>,
|
|
"Derived must subclass RefCounted<Derived>");
|
|
}
|
|
|
|
~RefCounted() { BSSL_CHECK(references_.load() == 0); }
|
|
|
|
private:
|
|
mutable CRYPTO_refcount_t references_ = 1;
|
|
};
|
|
|
|
|
|
// Containers.
|
|
|
|
// Array<T> is an owning array of elements of `T`.
|
|
template <typename T>
|
|
class Array {
|
|
public:
|
|
using value_type = std::remove_cv_t<T>;
|
|
|
|
// Array's default constructor creates an empty array.
|
|
Array() {}
|
|
Array(const Array &) = delete;
|
|
Array(Array &&other) { *this = std::move(other); }
|
|
|
|
~Array() { Reset(); }
|
|
|
|
Array &operator=(const Array &) = delete;
|
|
Array &operator=(Array &&other) {
|
|
Reset();
|
|
other.Release(&data_, &size_);
|
|
return *this;
|
|
}
|
|
|
|
const T *data() const { return data_; }
|
|
T *data() { return data_; }
|
|
size_t size() const { return size_; }
|
|
bool empty() const { return size_ == 0; }
|
|
|
|
const T &operator[](size_t i) const {
|
|
BSSL_CHECK(i < size_);
|
|
return data_[i];
|
|
}
|
|
T &operator[](size_t i) {
|
|
BSSL_CHECK(i < size_);
|
|
return data_[i];
|
|
}
|
|
|
|
T &front() {
|
|
BSSL_CHECK(size_ != 0);
|
|
return data_[0];
|
|
}
|
|
const T &front() const {
|
|
BSSL_CHECK(size_ != 0);
|
|
return data_[0];
|
|
}
|
|
T &back() {
|
|
BSSL_CHECK(size_ != 0);
|
|
return data_[size_ - 1];
|
|
}
|
|
const T &back() const {
|
|
BSSL_CHECK(size_ != 0);
|
|
return data_[size_ - 1];
|
|
}
|
|
|
|
T *begin() { return data_; }
|
|
const T *begin() const { return data_; }
|
|
T *end() { return data_ + size_; }
|
|
const T *end() const { return data_ + size_; }
|
|
|
|
void Reset() { Reset(nullptr, 0); }
|
|
|
|
// Reset releases the current contents of the array and takes ownership of the
|
|
// raw pointer supplied by the caller.
|
|
void Reset(T *new_data, size_t new_size) {
|
|
std::destroy_n(data_, size_);
|
|
OPENSSL_free(data_);
|
|
data_ = new_data;
|
|
size_ = new_size;
|
|
}
|
|
|
|
// Release releases ownership of the array to a raw pointer supplied by the
|
|
// caller.
|
|
void Release(T **out, size_t *out_size) {
|
|
*out = data_;
|
|
*out_size = size_;
|
|
data_ = nullptr;
|
|
size_ = 0;
|
|
}
|
|
|
|
// Init replaces the array with a newly-allocated array of `new_size`
|
|
// value-constructed copies of `T`. It returns true on success and false on
|
|
// error. If `T` is a primitive type like `uint8_t`, value-construction means
|
|
// it will be zero-initialized.
|
|
[[nodiscard]] bool Init(size_t new_size) {
|
|
if (!InitUninitialized(new_size)) {
|
|
return false;
|
|
}
|
|
std::uninitialized_value_construct_n(data_, size_);
|
|
return true;
|
|
}
|
|
|
|
// InitForOverwrite behaves like `Init` but it default-constructs each element
|
|
// instead. This means that, if `T` is a primitive type, the array will be
|
|
// uninitialized and thus must be filled in by the caller.
|
|
[[nodiscard]] bool InitForOverwrite(size_t new_size) {
|
|
if (!InitUninitialized(new_size)) {
|
|
return false;
|
|
}
|
|
std::uninitialized_default_construct_n(data_, size_);
|
|
return true;
|
|
}
|
|
|
|
// CopyFrom replaces the array with a newly-allocated copy of `in`. It returns
|
|
// true on success and false on error.
|
|
//
|
|
// `in` may not alias `this`.
|
|
[[nodiscard]] bool CopyFrom(Span<const T> in) {
|
|
BSSL_CHECK(!spans_alias(MakeConstSpan(*this), in));
|
|
if (!InitUninitialized(in.size())) {
|
|
return false;
|
|
}
|
|
std::uninitialized_copy(in.begin(), in.end(), data_);
|
|
return true;
|
|
}
|
|
|
|
// Shrink shrinks the stored size of the array to `new_size`. It crashes if
|
|
// the new size is larger. Note this does not shrink the allocation itself.
|
|
void Shrink(size_t new_size) {
|
|
if (new_size > size_) {
|
|
abort();
|
|
}
|
|
std::destroy_n(data_ + new_size, size_ - new_size);
|
|
size_ = new_size;
|
|
}
|
|
|
|
private:
|
|
// InitUninitialized replaces the array with a newly-allocated array of
|
|
// `new_size` elements, but whose constructor has not yet run. On success, the
|
|
// elements must be constructed before returning control to the caller.
|
|
bool InitUninitialized(size_t new_size) {
|
|
Reset();
|
|
if (new_size == 0) {
|
|
return true;
|
|
}
|
|
|
|
if (new_size > SIZE_MAX / sizeof(T)) {
|
|
OPENSSL_PUT_ERROR(CRYPTO, ERR_R_OVERFLOW);
|
|
return false;
|
|
}
|
|
data_ = reinterpret_cast<T *>(OPENSSL_malloc(new_size * sizeof(T)));
|
|
if (data_ == nullptr) {
|
|
return false;
|
|
}
|
|
size_ = new_size;
|
|
return true;
|
|
}
|
|
|
|
T *data_ = nullptr;
|
|
size_t size_ = 0;
|
|
};
|
|
|
|
// Vector<T> is a resizable array of elements of `T`.
|
|
template <typename T>
|
|
class Vector {
|
|
public:
|
|
Vector() = default;
|
|
Vector(const Vector &) = delete;
|
|
Vector(Vector &&other) { *this = std::move(other); }
|
|
~Vector() { clear(); }
|
|
|
|
Vector &operator=(const Vector &) = delete;
|
|
Vector &operator=(Vector &&other) {
|
|
clear();
|
|
std::swap(data_, other.data_);
|
|
std::swap(size_, other.size_);
|
|
std::swap(capacity_, other.capacity_);
|
|
return *this;
|
|
}
|
|
|
|
const T *data() const { return data_; }
|
|
T *data() { return data_; }
|
|
size_t size() const { return size_; }
|
|
bool empty() const { return size_ == 0; }
|
|
|
|
const T &operator[](size_t i) const {
|
|
BSSL_CHECK(i < size_);
|
|
return data_[i];
|
|
}
|
|
T &operator[](size_t i) {
|
|
BSSL_CHECK(i < size_);
|
|
return data_[i];
|
|
}
|
|
|
|
T &front() {
|
|
BSSL_CHECK(size_ != 0);
|
|
return data_[0];
|
|
}
|
|
const T &front() const {
|
|
BSSL_CHECK(size_ != 0);
|
|
return data_[0];
|
|
}
|
|
T &back() {
|
|
BSSL_CHECK(size_ != 0);
|
|
return data_[size_ - 1];
|
|
}
|
|
const T &back() const {
|
|
BSSL_CHECK(size_ != 0);
|
|
return data_[size_ - 1];
|
|
}
|
|
|
|
T *begin() { return data_; }
|
|
const T *begin() const { return data_; }
|
|
T *end() { return data_ + size_; }
|
|
const T *end() const { return data_ + size_; }
|
|
|
|
void clear() {
|
|
std::destroy_n(data_, size_);
|
|
OPENSSL_free(data_);
|
|
data_ = nullptr;
|
|
size_ = 0;
|
|
capacity_ = 0;
|
|
}
|
|
|
|
void pop_back() {
|
|
BSSL_CHECK(size_ != 0);
|
|
std::destroy_at(&data_[size_ - 1]);
|
|
size_--;
|
|
}
|
|
|
|
// Push adds `elem` at the end of the internal array, growing if necessary. It
|
|
// returns false when allocation fails.
|
|
[[nodiscard]] bool Push(T elem) {
|
|
if (!MaybeGrow(1)) {
|
|
return false;
|
|
}
|
|
new (&data_[size_]) T(std::move(elem));
|
|
size_++;
|
|
return true;
|
|
}
|
|
|
|
// CopyFrom replaces the contents of the array with a copy of `in`. It returns
|
|
// true on success and false on allocation error.
|
|
[[nodiscard]] bool CopyFrom(Span<const T> in) {
|
|
Array<T> copy;
|
|
if (!copy.CopyFrom(in)) {
|
|
return false;
|
|
}
|
|
|
|
clear();
|
|
copy.Release(&data_, &size_);
|
|
capacity_ = size_;
|
|
return true;
|
|
}
|
|
|
|
// Append appends the contents of `in` to the array. It returns true on
|
|
// success and false on allocation error.
|
|
[[nodiscard]] bool Append(Span<const T> in) {
|
|
if (!MaybeGrow(in.size())) {
|
|
return false;
|
|
}
|
|
std::uninitialized_copy(in.begin(), in.end(), data_ + size_);
|
|
size_ += in.size();
|
|
return true;
|
|
}
|
|
|
|
// AppendMove moves the contents of `in` and appends them to the array. It
|
|
// returns true on success and false on allocation error.
|
|
[[nodiscard]] bool AppendMove(Span<T> in) {
|
|
if (!MaybeGrow(in.size())) {
|
|
return false;
|
|
}
|
|
std::uninitialized_move(in.begin(), in.end(), data_ + size_);
|
|
size_ += in.size();
|
|
return true;
|
|
}
|
|
|
|
// EraseIf removes all elements that satisfy the predicate `pred`.
|
|
template <typename Pred>
|
|
void EraseIf(Pred pred) {
|
|
auto it = std::remove_if(begin(), end(), pred);
|
|
std::destroy(it, end());
|
|
size_ = it - begin();
|
|
}
|
|
|
|
private:
|
|
// If there is no room for `num` elements, creates a new backing array with
|
|
// double the size of the old one and copies elements over.
|
|
[[nodiscard]] bool MaybeGrow(size_t num) {
|
|
constexpr size_t kDefaultSize = 16;
|
|
constexpr size_t kMaxCapacity = SIZE_MAX / sizeof(T);
|
|
if (num > kMaxCapacity - size_) {
|
|
OPENSSL_PUT_ERROR(CRYPTO, ERR_R_OVERFLOW);
|
|
return false;
|
|
}
|
|
size_t new_capacity = size_ + num;
|
|
// No need to grow if we have room.
|
|
if (capacity_ >= new_capacity) {
|
|
return true;
|
|
}
|
|
// Always grow to at least kDefaultSize to avoid several small mallocs at
|
|
// the start.
|
|
new_capacity = std::max(new_capacity, std::min(kDefaultSize, kMaxCapacity));
|
|
// At least double the old capacity for linear amortized behavior.
|
|
if (capacity_ <= kMaxCapacity / 2) {
|
|
new_capacity = std::max(new_capacity, capacity_ * 2);
|
|
}
|
|
T *new_data =
|
|
reinterpret_cast<T *>(OPENSSL_malloc(new_capacity * sizeof(T)));
|
|
if (new_data == nullptr) {
|
|
return false;
|
|
}
|
|
size_t new_size = size_;
|
|
std::uninitialized_move(begin(), end(), new_data);
|
|
clear();
|
|
data_ = new_data;
|
|
size_ = new_size;
|
|
capacity_ = new_capacity;
|
|
return true;
|
|
}
|
|
|
|
// data_ is a pointer to `capacity_` objects of size `T`, the first `size_` of
|
|
// which are constructed.
|
|
T *data_ = nullptr;
|
|
// `size_` is the number of elements stored in this Vector.
|
|
size_t size_ = 0;
|
|
// `capacity_` is the number of elements allocated in this Vector.
|
|
size_t capacity_ = 0;
|
|
};
|
|
|
|
// A PackedSize is an integer that can store values from 0 to N, represented as
|
|
// a minimal-width integer.
|
|
template <size_t N>
|
|
using PackedSize = std::conditional_t<
|
|
N <= 0xff, uint8_t,
|
|
std::conditional_t<N <= 0xffff, uint16_t,
|
|
std::conditional_t<N <= 0xffffffff, uint32_t, size_t>>>;
|
|
|
|
// An InplaceVector is like a Vector, but stores up to N elements inline in the
|
|
// object. It is inspired by std::inplace_vector in C++26.
|
|
template <typename T, size_t N>
|
|
class InplaceVector {
|
|
public:
|
|
using value_type = std::remove_cv_t<T>;
|
|
|
|
InplaceVector() = default;
|
|
InplaceVector(const InplaceVector &other) { *this = other; }
|
|
InplaceVector(InplaceVector &&other) { *this = std::move(other); }
|
|
~InplaceVector() { clear(); }
|
|
InplaceVector &operator=(const InplaceVector &other) {
|
|
if (this != &other) {
|
|
CopyFrom(other);
|
|
}
|
|
return *this;
|
|
}
|
|
InplaceVector &operator=(InplaceVector &&other) {
|
|
clear();
|
|
std::uninitialized_move(other.begin(), other.end(), data());
|
|
size_ = other.size();
|
|
return *this;
|
|
}
|
|
|
|
const T *data() const { return reinterpret_cast<const T *>(storage_); }
|
|
T *data() { return reinterpret_cast<T *>(storage_); }
|
|
size_t size() const { return size_; }
|
|
static constexpr size_t capacity() { return N; }
|
|
bool empty() const { return size_ == 0; }
|
|
|
|
const T &operator[](size_t i) const {
|
|
BSSL_CHECK(i < size_);
|
|
return data()[i];
|
|
}
|
|
T &operator[](size_t i) {
|
|
BSSL_CHECK(i < size_);
|
|
return data()[i];
|
|
}
|
|
|
|
T &front() {
|
|
BSSL_CHECK(size_ != 0);
|
|
return data()[0];
|
|
}
|
|
const T &front() const {
|
|
BSSL_CHECK(size_ != 0);
|
|
return data()[0];
|
|
}
|
|
T &back() {
|
|
BSSL_CHECK(size_ != 0);
|
|
return data()[size_ - 1];
|
|
}
|
|
const T &back() const {
|
|
BSSL_CHECK(size_ != 0);
|
|
return data()[size_ - 1];
|
|
}
|
|
|
|
T *begin() { return data(); }
|
|
const T *begin() const { return data(); }
|
|
T *end() { return data() + size_; }
|
|
const T *end() const { return data() + size_; }
|
|
|
|
void clear() { Shrink(0); }
|
|
|
|
void pop_back() {
|
|
BSSL_CHECK(size_ != 0);
|
|
Shrink(size_ - 1);
|
|
}
|
|
|
|
// Shrink resizes the vector to `new_size`, which must not be larger than the
|
|
// current size. Unlike `Resize`, this can be called when `T` is not
|
|
// default-constructible.
|
|
void Shrink(size_t new_size) {
|
|
BSSL_CHECK(new_size <= size_);
|
|
std::destroy_n(data() + new_size, size_ - new_size);
|
|
size_ = static_cast<PackedSize<N>>(new_size);
|
|
}
|
|
|
|
// TryResize resizes the vector to `new_size` and returns true, or returns
|
|
// false if `new_size` is too large. Any newly-added elements are
|
|
// value-initialized.
|
|
[[nodiscard]] bool TryResize(size_t new_size) {
|
|
if (new_size <= size_) {
|
|
Shrink(new_size);
|
|
return true;
|
|
}
|
|
if (new_size > capacity()) {
|
|
return false;
|
|
}
|
|
std::uninitialized_value_construct_n(data() + size_, new_size - size_);
|
|
size_ = static_cast<PackedSize<N>>(new_size);
|
|
return true;
|
|
}
|
|
|
|
// TryResizeForOverwrite behaves like `TryResize`, but newly-added elements
|
|
// are default-initialized, so POD types may contain uninitialized values that
|
|
// the caller is responsible for filling in.
|
|
[[nodiscard]] bool TryResizeForOverwrite(size_t new_size) {
|
|
if (new_size <= size_) {
|
|
Shrink(new_size);
|
|
return true;
|
|
}
|
|
if (new_size > capacity()) {
|
|
return false;
|
|
}
|
|
std::uninitialized_default_construct_n(data() + size_, new_size - size_);
|
|
size_ = static_cast<PackedSize<N>>(new_size);
|
|
return true;
|
|
}
|
|
|
|
// TryCopyFrom sets the vector to a copy of `in` and returns true, or returns
|
|
// false if `in` is too large.
|
|
//
|
|
// `in` may not alias `this`.
|
|
[[nodiscard]] bool TryCopyFrom(Span<const T> in) {
|
|
BSSL_CHECK(!spans_alias(MakeConstSpan(*this), in));
|
|
if (in.size() > capacity()) {
|
|
return false;
|
|
}
|
|
clear();
|
|
std::uninitialized_copy(in.begin(), in.end(), data());
|
|
size_ = in.size();
|
|
return true;
|
|
}
|
|
|
|
// TryAppend appends the vector by a copy of `in` and returns true, or
|
|
// returns false if `in` is too large.
|
|
[[nodiscard]] bool TryAppend(Span<const T> in) {
|
|
if (in.size() > capacity() - size()) {
|
|
return false;
|
|
}
|
|
std::uninitialized_copy(in.begin(), in.end(), &data()[size_]);
|
|
size_ += in.size();
|
|
return true;
|
|
}
|
|
|
|
// TryPushBack appends `val` to the vector and returns a pointer to the
|
|
// newly-inserted value, or nullptr if the vector is at capacity.
|
|
[[nodiscard]] T *TryPushBack(T val) {
|
|
if (size() >= capacity()) {
|
|
return nullptr;
|
|
}
|
|
T *ret = &data()[size_];
|
|
new (ret) T(std::move(val));
|
|
size_++;
|
|
return ret;
|
|
}
|
|
|
|
// The following methods behave like their `Try*` counterparts, but abort the
|
|
// program on failure.
|
|
void Resize(size_t size) { BSSL_CHECK(TryResize(size)); }
|
|
void ResizeForOverwrite(size_t size) {
|
|
BSSL_CHECK(TryResizeForOverwrite(size));
|
|
}
|
|
void CopyFrom(Span<const T> in) { BSSL_CHECK(TryCopyFrom(in)); }
|
|
void Append(Span<const T> in) { BSSL_CHECK(TryAppend(in)); }
|
|
T &PushBack(T val) {
|
|
T *ret = TryPushBack(std::move(val));
|
|
BSSL_CHECK(ret != nullptr);
|
|
return *ret;
|
|
}
|
|
|
|
// EraseIf removes all elements that satisfy the predicate `pred`.
|
|
template <typename Pred>
|
|
void EraseIf(Pred pred) {
|
|
auto it = std::remove_if(begin(), end(), pred);
|
|
Shrink(it - begin());
|
|
}
|
|
|
|
private:
|
|
alignas(T) char storage_[sizeof(T[N])];
|
|
PackedSize<N> size_ = 0;
|
|
};
|
|
|
|
|
|
BSSL_NAMESPACE_END
|
|
|
|
#endif // OPENSSL_HEADER_CRYPTO_MEM_INTERNAL_H
|