util/AllocatedArray: migrate from {Const,Writable}Buffer to std::span

This commit is contained in:
Max Kellermann 2022-05-11 16:19:44 +02:00 committed by Max Kellermann
parent 23dd613ff9
commit 5fb97b81d1
15 changed files with 93 additions and 103 deletions

View File

@ -51,8 +51,8 @@ mod_loadfile(const Domain *domain, DecoderClient *client, InputStream &is)
auto buffer = AllocatedArray<std::byte>(buffer_size); auto buffer = AllocatedArray<std::byte>(buffer_size);
std::byte *const end = buffer.end(); std::byte *p = buffer.data();
std::byte *p = buffer.begin(); std::byte *const end = p + buffer.size();
while (true) { while (true) {
size_t ret = decoder_read(client, is, p, end - p); size_t ret = decoder_read(client, is, p, end - p);

View File

@ -73,7 +73,7 @@ static ModPlugFile *
LoadModPlugFile(DecoderClient *client, InputStream &is) LoadModPlugFile(DecoderClient *client, InputStream &is)
{ {
const auto buffer = mod_loadfile(&modplug_domain, client, is); const auto buffer = mod_loadfile(&modplug_domain, client, is);
if (buffer.IsNull()) { if (buffer == nullptr) {
LogWarning(modplug_domain, "could not load stream"); LogWarning(modplug_domain, "could not load stream");
return nullptr; return nullptr;
} }

View File

@ -73,7 +73,7 @@ mod_decode(DecoderClient &client, InputStream &is)
char audio_buffer[OPENMPT_FRAME_SIZE]; char audio_buffer[OPENMPT_FRAME_SIZE];
const auto buffer = mod_loadfile(&openmpt_domain, &client, is); const auto buffer = mod_loadfile(&openmpt_domain, &client, is);
if (buffer.IsNull()) { if (buffer == nullptr) {
LogWarning(openmpt_domain, "could not load stream"); LogWarning(openmpt_domain, "could not load stream");
return; return;
} }
@ -128,7 +128,7 @@ static bool
openmpt_scan_stream(InputStream &is, TagHandler &handler) noexcept openmpt_scan_stream(InputStream &is, TagHandler &handler) noexcept
try { try {
const auto buffer = mod_loadfile(&openmpt_domain, nullptr, is); const auto buffer = mod_loadfile(&openmpt_domain, nullptr, is);
if (buffer.IsNull()) { if (buffer == nullptr) {
LogWarning(openmpt_domain, "could not load stream"); LogWarning(openmpt_domain, "could not load stream");
return false; return false;
} }

View File

@ -43,21 +43,21 @@ IcuCaseFold(std::string_view src) noexcept
try { try {
#ifdef HAVE_ICU #ifdef HAVE_ICU
const auto u = UCharFromUTF8(src); const auto u = UCharFromUTF8(src);
if (u.IsNull()) if (u.data() == nullptr)
return {src}; return {src};
AllocatedArray<UChar> folded(u.size() * 2U); AllocatedArray<UChar> folded(u.size() * 2U);
UErrorCode error_code = U_ZERO_ERROR; UErrorCode error_code = U_ZERO_ERROR;
size_t folded_length = u_strFoldCase(folded.begin(), folded.size(), size_t folded_length = u_strFoldCase(folded.data(), folded.size(),
u.begin(), u.size(), u.data(), u.size(),
U_FOLD_CASE_DEFAULT, U_FOLD_CASE_DEFAULT,
&error_code); &error_code);
if (folded_length == 0 || error_code != U_ZERO_ERROR) if (folded_length == 0 || error_code != U_ZERO_ERROR)
return {src}; return {src};
folded.SetSize(folded_length); folded.SetSize(folded_length);
return UCharToUTF8({folded.begin(), folded.size()}); return UCharToUTF8(std::basic_string_view{folded.data(), folded.size()});
#else #else
#error not implemented #error not implemented

View File

@ -141,11 +141,11 @@ IcuConverter::FromUTF8(std::string_view s) const
// TODO: dynamic buffer? // TODO: dynamic buffer?
char buffer[4096], *target = buffer; char buffer[4096], *target = buffer;
const UChar *source = u.begin(); const UChar *source = u.data();
UErrorCode code = U_ZERO_ERROR; UErrorCode code = U_ZERO_ERROR;
ucnv_fromUnicode(converter, &target, buffer + std::size(buffer), ucnv_fromUnicode(converter, &target, buffer + std::size(buffer),
&source, u.end(), &source, u.data() + u.size(),
nullptr, true, &code); nullptr, true, &code);
if (code != U_ZERO_ERROR) if (code != U_ZERO_ERROR)

View File

@ -38,7 +38,7 @@ UCharFromUTF8(std::string_view src)
UErrorCode error_code = U_ZERO_ERROR; UErrorCode error_code = U_ZERO_ERROR;
int32_t dest_length; int32_t dest_length;
u_strFromUTF8(dest.begin(), dest_capacity, &dest_length, u_strFromUTF8(dest.data(), dest_capacity, &dest_length,
src.data(), src.size(), src.data(), src.size(),
&error_code); &error_code);
if (U_FAILURE(error_code)) if (U_FAILURE(error_code))

View File

@ -760,7 +760,7 @@ Play_44_1_Silence(snd_pcm_t *pcm)
throw Alsa::MakeError(err, "snd_pcm_prepare() failed"); throw Alsa::MakeError(err, "snd_pcm_prepare() failed");
AllocatedArray<int16_t> buffer{channels * period_size}; AllocatedArray<int16_t> buffer{channels * period_size};
buffer = {}; buffer = std::span<const int16_t>{};
/* play at least 250ms of silence */ /* play at least 250ms of silence */
for (snd_pcm_uframes_t remaining_frames = rate / 4;;) { for (snd_pcm_uframes_t remaining_frames = rate / 4;;) {

View File

@ -172,7 +172,7 @@ HttpdOutput::ReadPage()
if (size == 0) if (size == 0)
return nullptr; return nullptr;
return std::make_shared<Page>(ConstBuffer{buffer, size}); return std::make_shared<Page>(std::span{buffer, size});
} }
inline void inline void

View File

@ -114,5 +114,5 @@ icy_server_metadata_page(const Tag &tag, const TagType *types) noexcept
if (icy_string == nullptr) if (icy_string == nullptr)
return nullptr; return nullptr;
return std::make_shared<Page>(ConstBuffer<std::byte>{(const std::byte *)icy_string.c_str(), uint8_t(icy_string[0]) * 16U + 1U}); return std::make_shared<Page>(std::span{(const std::byte *)icy_string.c_str(), uint8_t(icy_string[0]) * 16U + 1U});
} }

View File

@ -95,8 +95,8 @@ SnapcastClient::OnSocketReady(unsigned flags) noexcept
/* discard old chunks */ /* discard old chunks */
continue; continue;
const ConstBuffer<std::byte> payload = chunk->payload; const std::span payload = chunk->payload;
if (!SendWireChunk(payload.ToVoid(), chunk->time)) { if (!SendWireChunk(payload, chunk->time)) {
// TODO: handle EAGAIN // TODO: handle EAGAIN
LockClose(); LockClose();
return; return;
@ -110,23 +110,23 @@ SnapcastClient::OnSocketReady(unsigned flags) noexcept
} }
static bool static bool
Send(SocketDescriptor s, ConstBuffer<void> buffer) noexcept Send(SocketDescriptor s, std::span<const std::byte> buffer) noexcept
{ {
auto nbytes = s.Write(buffer.data, buffer.size); auto nbytes = s.Write(buffer.data(), buffer.size());
return nbytes == ssize_t(buffer.size); return nbytes == ssize_t(buffer.size());
} }
template<typename T> template<typename T>
static bool static bool
SendT(SocketDescriptor s, const T &buffer) noexcept SendT(SocketDescriptor s, const T &buffer) noexcept
{ {
return Send(s, ConstBuffer<T>{&buffer, 1}.ToVoid()); return Send(s, std::as_bytes(std::span{&buffer, 1}));
} }
static bool static bool
Send(SocketDescriptor s, StringView buffer) noexcept Send(SocketDescriptor s, std::string_view buffer) noexcept
{ {
return Send(s, buffer.ToVoid()); return Send(s, std::as_bytes(std::span{buffer}));
} }
static bool static bool
@ -158,10 +158,10 @@ static bool
SendCodecHeader(SocketDescriptor s, const PackedBE16 id, SendCodecHeader(SocketDescriptor s, const PackedBE16 id,
const SnapcastBase &request, const SnapcastBase &request,
const StringView codec, const StringView codec,
const ConstBuffer<void> payload) noexcept const std::span<const std::byte> payload) noexcept
{ {
const PackedLE32 codec_size = codec.size; const PackedLE32 codec_size = codec.size;
const PackedLE32 payload_size = payload.size; const PackedLE32 payload_size = payload.size();
SnapcastBase base{}; SnapcastBase base{};
base.type = uint16_t(SnapcastMessageType::CODEC_HEADER); base.type = uint16_t(SnapcastMessageType::CODEC_HEADER);
@ -169,7 +169,7 @@ SendCodecHeader(SocketDescriptor s, const PackedBE16 id,
base.refers_to = request.id; base.refers_to = request.id;
base.sent = ToSnapcastTimestamp(std::chrono::steady_clock::now()); base.sent = ToSnapcastTimestamp(std::chrono::steady_clock::now());
base.size = sizeof(codec_size) + codec.size + base.size = sizeof(codec_size) + codec.size +
sizeof(payload_size) + payload.size; sizeof(payload_size) + payload.size();
return SendT(s, base) && return SendT(s, base) &&
SendT(s, codec_size) && Send(s, codec) && SendT(s, codec_size) && Send(s, codec) &&
@ -212,25 +212,25 @@ SnapcastClient::SendTime(const SnapcastBase &request_header,
static bool static bool
SendWireChunk(SocketDescriptor s, const PackedBE16 id, SendWireChunk(SocketDescriptor s, const PackedBE16 id,
const ConstBuffer<void> payload, const std::span<const std::byte> payload,
std::chrono::steady_clock::time_point t) noexcept std::chrono::steady_clock::time_point t) noexcept
{ {
SnapcastWireChunk hdr{}; SnapcastWireChunk hdr{};
hdr.timestamp = ToSnapcastTimestamp(t); hdr.timestamp = ToSnapcastTimestamp(t);
hdr.size = payload.size; hdr.size = payload.size();
SnapcastBase base{}; SnapcastBase base{};
base.type = uint16_t(SnapcastMessageType::WIRE_CHUNK); base.type = uint16_t(SnapcastMessageType::WIRE_CHUNK);
base.id = id; base.id = id;
base.sent = ToSnapcastTimestamp(std::chrono::steady_clock::now()); base.sent = ToSnapcastTimestamp(std::chrono::steady_clock::now());
base.size = sizeof(hdr) + payload.size; base.size = sizeof(hdr) + payload.size();
// TODO: no blocking send() // TODO: no blocking send()
return SendT(s, base) && SendT(s, hdr) && Send(s, payload); return SendT(s, base) && SendT(s, hdr) && Send(s, payload);
} }
bool bool
SnapcastClient::SendWireChunk(ConstBuffer<void> payload, SnapcastClient::SendWireChunk(std::span<const std::byte> payload,
std::chrono::steady_clock::time_point t) noexcept std::chrono::steady_clock::time_point t) noexcept
{ {
return ::SendWireChunk(GetSocket(), next_id++, payload, t); return ::SendWireChunk(GetSocket(), next_id++, payload, t);
@ -238,21 +238,21 @@ SnapcastClient::SendWireChunk(ConstBuffer<void> payload,
static bool static bool
SendStreamTags(SocketDescriptor s, const PackedBE16 id, SendStreamTags(SocketDescriptor s, const PackedBE16 id,
const ConstBuffer<void> payload) noexcept const std::span<const std::byte> payload) noexcept
{ {
const PackedLE32 payload_size = payload.size; const PackedLE32 payload_size = payload.size();
SnapcastBase base{}; SnapcastBase base{};
base.type = uint16_t(SnapcastMessageType::STREAM_TAGS); base.type = uint16_t(SnapcastMessageType::STREAM_TAGS);
base.id = id; base.id = id;
base.sent = ToSnapcastTimestamp(std::chrono::steady_clock::now()); base.sent = ToSnapcastTimestamp(std::chrono::steady_clock::now());
base.size = sizeof(payload_size) + payload.size; base.size = sizeof(payload_size) + payload.size();
return SendT(s, base) && SendT(s, payload_size) && Send(s, payload); return SendT(s, base) && SendT(s, payload_size) && Send(s, payload);
} }
void void
SnapcastClient::SendStreamTags(ConstBuffer<void> payload) noexcept SnapcastClient::SendStreamTags(std::span<const std::byte> payload) noexcept
{ {
::SendStreamTags(GetSocket(), next_id++, payload); ::SendStreamTags(GetSocket(), next_id++, payload);
} }
@ -270,7 +270,7 @@ SnapcastClient::OnSocketInput(void *data, size_t length) noexcept
ConsumeInput(sizeof(base) + base.size); ConsumeInput(sizeof(base) + base.size);
const ConstBuffer<void> payload{&base + 1, base.size}; const std::span<const std::byte> payload{(const std::byte *)(&base + 1), base.size};
switch (SnapcastMessageType(uint16_t(base.type))) { switch (SnapcastMessageType(uint16_t(base.type))) {
case SnapcastMessageType::HELLO: case SnapcastMessageType::HELLO:
@ -284,8 +284,8 @@ SnapcastClient::OnSocketInput(void *data, size_t length) noexcept
break; break;
case SnapcastMessageType::TIME: case SnapcastMessageType::TIME:
if (payload.size >= sizeof(SnapcastTime)) if (payload.size() >= sizeof(SnapcastTime))
SendTime(base, *(const SnapcastTime *)payload.data); SendTime(base, *(const SnapcastTime *)(const void *)payload.data());
break; break;
default: default:

View File

@ -26,6 +26,7 @@
#include <chrono> #include <chrono>
#include <cstdint> #include <cstdint>
#include <span>
struct SnapcastBase; struct SnapcastBase;
struct SnapcastTime; struct SnapcastTime;
@ -60,7 +61,7 @@ public:
void LockClose() noexcept; void LockClose() noexcept;
void SendStreamTags(ConstBuffer<void> payload) noexcept; void SendStreamTags(std::span<const std::byte> payload) noexcept;
/** /**
* Caller must lock the mutex. * Caller must lock the mutex.
@ -84,7 +85,7 @@ public:
private: private:
SnapcastChunkPtr LockPopQueue() noexcept; SnapcastChunkPtr LockPopQueue() noexcept;
bool SendWireChunk(ConstBuffer<void> payload, bool SendWireChunk(std::span<const std::byte> payload,
std::chrono::steady_clock::time_point t) noexcept; std::chrono::steady_clock::time_point t) noexcept;
bool SendServerSettings(const SnapcastBase &request) noexcept; bool SendServerSettings(const SnapcastBase &request) noexcept;

View File

@ -161,9 +161,8 @@ public:
return "pcm"; return "pcm";
} }
ConstBuffer<void> GetCodecHeader() const noexcept { std::span<const std::byte> GetCodecHeader() const noexcept {
ConstBuffer<std::byte> result(codec_header); return codec_header;
return result.ToVoid();
} }
/* virtual methods from class AudioOutput */ /* virtual methods from class AudioOutput */

View File

@ -127,7 +127,7 @@ ReadEncoder(Encoder &encoder)
std::byte buffer[4096]; std::byte buffer[4096];
size_t nbytes = encoder.Read(buffer, sizeof(buffer)); size_t nbytes = encoder.Read(buffer, sizeof(buffer));
const ConstBuffer<std::byte> src(buffer, nbytes); const std::span<const std::byte> src{buffer, nbytes};
return AllocatedArray<std::byte>{src}; return AllocatedArray<std::byte>{src};
} }
@ -181,7 +181,7 @@ SnapcastOutput::Close() noexcept
ClearQueue(chunks); ClearQueue(chunks);
codec_header = nullptr; codec_header = std::span<const std::byte>{};
delete encoder; delete encoder;
} }
@ -294,12 +294,12 @@ SnapcastOutput::SendTag(const Tag &tag)
if (json.empty()) if (json.empty())
return; return;
const ConstBuffer payload(json.data(), json.size()); const auto payload = std::as_bytes(std::span{json});
const std::scoped_lock<Mutex> protect(mutex); const std::scoped_lock<Mutex> protect(mutex);
// TODO: enqueue StreamTags, don't send directly // TODO: enqueue StreamTags, don't send directly
for (auto &client : clients) for (auto &client : clients)
client.SendStreamTags(payload.ToVoid()); client.SendStreamTags(payload);
#else #else
(void)tag; (void)tag;
#endif #endif
@ -348,7 +348,7 @@ SnapcastOutput::Play(const void *chunk, size_t size)
if (chunks.empty()) if (chunks.empty())
inject_event.Schedule(); inject_event.Schedule();
const ConstBuffer payload{buffer, nbytes}; const std::span<const std::byte> payload{buffer, nbytes};
chunks.push(std::make_shared<SnapcastChunk>(now, AllocatedArray{payload})); chunks.push(std::make_shared<SnapcastChunk>(now, AllocatedArray{payload}));
} }

View File

@ -216,7 +216,7 @@ TagBuilder::AddItemInternal(TagType type, StringView value) noexcept
assert(!value.empty()); assert(!value.empty());
auto f = FixTagString(value); auto f = FixTagString(value);
if (!f.IsNull()) if (f != nullptr)
value = { f.data(), f.size() }; value = { f.data(), f.size() };
AddItemUnchecked(type, value); AddItemUnchecked(type, value);

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2010-2019 Max Kellermann <max.kellermann@gmail.com> * Copyright 2010-2022 Max Kellermann <max.kellermann@gmail.com>
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions * modification, are permitted provided that the following conditions
@ -27,14 +27,11 @@
* OF THE POSSIBILITY OF SUCH DAMAGE. * OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
#ifndef ALLOCATED_ARRAY_HXX #pragma once
#define ALLOCATED_ARRAY_HXX
#include "ConstBuffer.hxx"
#include "WritableBuffer.hxx"
#include <algorithm> #include <algorithm>
#include <cassert> #include <cassert>
#include <span>
#include <utility> #include <utility>
/** /**
@ -42,7 +39,7 @@
*/ */
template<class T> template<class T>
class AllocatedArray { class AllocatedArray {
typedef WritableBuffer<T> Buffer; using Buffer = std::span<T>;
public: public:
using size_type = typename Buffer::size_type; using size_type = typename Buffer::size_type;
@ -51,10 +48,10 @@ public:
using pointer = typename Buffer::pointer; using pointer = typename Buffer::pointer;
using const_pointer = typename Buffer::const_pointer; using const_pointer = typename Buffer::const_pointer;
using iterator = typename Buffer::iterator; using iterator = typename Buffer::iterator;
using const_iterator = typename Buffer::const_iterator; using const_iterator = typename Buffer::iterator;
protected: protected:
Buffer buffer{nullptr}; Buffer buffer{};
public: public:
constexpr AllocatedArray() = default; constexpr AllocatedArray() = default;
@ -62,16 +59,15 @@ public:
explicit AllocatedArray(size_type _size) noexcept explicit AllocatedArray(size_type _size) noexcept
:buffer{new T[_size], _size} {} :buffer{new T[_size], _size} {}
explicit AllocatedArray(ConstBuffer<T> src) noexcept { explicit AllocatedArray(std::span<const T> src) noexcept {
if (src == nullptr) if (src.data() == nullptr)
return; return;
buffer = {new T[src.size], src.size}; buffer = {new T[src.size()], src.size()};
std::copy_n(src.data, src.size, buffer.data); std::copy(src.begin(), src.end(), buffer.begin());
} }
AllocatedArray(std::nullptr_t n) noexcept AllocatedArray(std::nullptr_t) noexcept {}
:buffer(n) {}
explicit AllocatedArray(const AllocatedArray &other) noexcept explicit AllocatedArray(const AllocatedArray &other) noexcept
:AllocatedArray(other.buffer) {} :AllocatedArray(other.buffer) {}
@ -80,27 +76,28 @@ public:
:buffer(other.release()) {} :buffer(other.release()) {}
~AllocatedArray() noexcept { ~AllocatedArray() noexcept {
delete[] buffer.data; delete[] buffer.data();
} }
AllocatedArray &operator=(ConstBuffer<T> src) noexcept { AllocatedArray &operator=(std::span<const T> src) noexcept {
assert(size() == 0 || buffer.data != nullptr); assert(empty() || buffer.data() != nullptr);
assert(src.size == 0 || src.data != nullptr); assert(src.empty() || src.data() != nullptr);
ResizeDiscard(src.size); ResizeDiscard(src.size());
std::copy_n(src.data, src.size, buffer.data); std::copy(src.begin(), src.end(), buffer.begin());
return *this; return *this;
} }
AllocatedArray &operator=(const AllocatedArray &other) noexcept { AllocatedArray &operator=(const AllocatedArray &other) noexcept {
assert(size() == 0 || buffer.data != nullptr); assert(empty() || buffer.data() != nullptr);
assert(other.size() == 0 || other.buffer.data != nullptr); assert(other.empty() || other.buffer.data() != nullptr);
if (&other == this) if (&other == this)
return *this; return *this;
ResizeDiscard(other.size()); ResizeDiscard(other.size());
std::copy_n(other.buffer.data, other.buffer.size, buffer.data); std::copy_n(other.buffer.begin(), other.buffer.end(),
buffer.begin());
return *this; return *this;
} }
@ -111,29 +108,25 @@ public:
} }
AllocatedArray &operator=(std::nullptr_t n) noexcept { AllocatedArray &operator=(std::nullptr_t n) noexcept {
delete[] buffer.data; delete[] buffer.data();
buffer = n; buffer = {};
return *this; return *this;
} }
operator ConstBuffer<T>() const noexcept { operator std::span<const T>() const noexcept {
return buffer; return buffer;
} }
operator WritableBuffer<T>() noexcept { operator std::span<T>() noexcept {
return buffer; return buffer;
} }
constexpr bool IsNull() const noexcept {
return buffer.IsNull();
}
constexpr bool operator==(std::nullptr_t) const noexcept { constexpr bool operator==(std::nullptr_t) const noexcept {
return buffer == nullptr; return buffer.data() == nullptr;
} }
constexpr bool operator!=(std::nullptr_t) const noexcept { constexpr bool operator!=(std::nullptr_t) const noexcept {
return buffer != nullptr; return buffer.data() != nullptr;
} }
/** /**
@ -147,22 +140,22 @@ public:
* Returns the number of allocated elements. * Returns the number of allocated elements.
*/ */
constexpr size_type size() const noexcept { constexpr size_type size() const noexcept {
return buffer.size; return buffer.size();
} }
/** /**
* Returns the number of allocated elements. * Returns the number of allocated elements.
*/ */
constexpr size_type capacity() const noexcept { constexpr size_type capacity() const noexcept {
return buffer.size; return buffer.size();
} }
pointer data() noexcept { pointer data() noexcept {
return buffer.data; return buffer.data();
} }
const_pointer data() const noexcept { const_pointer data() const noexcept {
return buffer.data; return buffer.data();
} }
reference front() noexcept { reference front() noexcept {
@ -200,7 +193,7 @@ public:
} }
constexpr const_iterator begin() const noexcept { constexpr const_iterator begin() const noexcept {
return buffer.cbegin(); return buffer.begin();
} }
iterator end() noexcept { iterator end() noexcept {
@ -208,19 +201,18 @@ public:
} }
constexpr const_iterator end() const noexcept { constexpr const_iterator end() const noexcept {
return buffer.cend(); return buffer.end();
} }
/** /**
* Resizes the array, discarding old data. * Resizes the array, discarding old data.
*/ */
void ResizeDiscard(size_type _size) noexcept { void ResizeDiscard(size_type _size) noexcept {
if (_size == buffer.size) if (_size == buffer.size())
return; return;
delete[] buffer.data; delete[] buffer.data();
buffer.size = _size; buffer = {new T[_size], _size};
buffer.data = new T[buffer.size];
} }
/** /**
@ -229,7 +221,7 @@ public:
* avoid expensive heap operations. * avoid expensive heap operations.
*/ */
void GrowDiscard(size_type _size) noexcept { void GrowDiscard(size_type _size) noexcept {
if (_size > buffer.size) if (_size > buffer.size())
ResizeDiscard(_size); ResizeDiscard(_size);
} }
@ -238,16 +230,16 @@ public:
* range of elements, starting from the beginning. * range of elements, starting from the beginning.
*/ */
void GrowPreserve(size_type _size, size_type preserve) noexcept { void GrowPreserve(size_type _size, size_type preserve) noexcept {
if (_size <= buffer.size) if (_size <= buffer.size())
return; return;
T *new_data = new T[_size]; T *new_data = new T[_size];
std::move(buffer.data, buffer.data + preserve, new_data); std::move(buffer.begin(), std::next(buffer.begin(), preserve),
new_data);
delete[] buffer.data; delete[] buffer.data();
buffer.data = new_data; buffer = {new_data, _size};
buffer.size = _size;
} }
/** /**
@ -256,17 +248,15 @@ public:
* they are still allocated). * they are still allocated).
*/ */
void SetSize(size_type _size) noexcept { void SetSize(size_type _size) noexcept {
assert(_size <= buffer.size); assert(_size <= buffer.size());
buffer.size = _size; buffer = buffer.first(_size);
} }
/** /**
* Give up ownership of the allocated buffer and return it. * Give up ownership of the allocated buffer and return it.
*/ */
Buffer release() noexcept { Buffer release() noexcept {
return std::exchange(buffer, nullptr); return std::exchange(buffer, std::span<T>{});
} }
}; };
#endif