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; };