Use the STL helpers for managing uninitialized buffers in bssl::Array
Prior to C++17, we're missing a few of them, but they're easy to fill
in. In doing so, switch default-construct + copy-assign in
Array::CopyFrom to copy-construct.
These will also be used for a bssl::InplaceVector next.
Change-Id: I4172a94382a23bd9f05d35e3c62c81c8c2c06948
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/71827
Reviewed-by: Adam Langley <agl@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
diff --git a/ssl/internal.h b/ssl/internal.h
index 43ea1c2..247f8c6 100644
--- a/ssl/internal.h
+++ b/ssl/internal.h
@@ -189,6 +189,32 @@
// C++ utilities.
+// Fill-ins for various functions in C++17.
+// TODO(crbug.com/42290600): Replace these with the standard ones when we
+// require C++17.
+
+template <typename ForwardIt>
+ForwardIt cxx17_uninitialized_default_construct_n(ForwardIt first, size_t n) {
+ using T = typename std::iterator_traits<ForwardIt>::value_type;
+ while (n > 0) {
+ new (std::addressof(*first)) T;
+ first++;
+ n--;
+ }
+ return first;
+}
+
+template <typename ForwardIt>
+ForwardIt cxx17_destroy_n(ForwardIt first, size_t n) {
+ using T = typename std::iterator_traits<ForwardIt>::value_type;
+ while (n > 0) {
+ first->~T();
+ first++;
+ n--;
+ }
+ return first;
+}
+
// 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].
@@ -266,9 +292,7 @@
// 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) {
- for (size_t i = 0; i < size_; i++) {
- data_[i].~T();
- }
+ cxx17_destroy_n(data_, size_);
OPENSSL_free(data_);
data_ = new_data;
size_ = new_size;
@@ -289,6 +313,38 @@
//
// Note that if |T| is a primitive type like |uint8_t|, it is uninitialized.
bool Init(size_t new_size) {
+ if (!InitUninitialized(new_size)) {
+ return false;
+ }
+ cxx17_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.
+ bool CopyFrom(Span<const T> 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();
+ }
+ cxx17_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;
@@ -303,35 +359,9 @@
return false;
}
size_ = new_size;
- for (size_t i = 0; i < size_; i++) {
- new (&data_[i]) T;
- }
return true;
}
- // CopyFrom replaces the array with a newly-allocated copy of |in|. It returns
- // true on success and false on error.
- bool CopyFrom(Span<const T> in) {
- if (!Init(in.size())) {
- return false;
- }
- std::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();
- }
- for (size_t i = new_size; i < size_; i++) {
- data_[i].~T();
- }
- size_ = new_size;
- }
-
- private:
T *data_ = nullptr;
size_t size_ = 0;
};