From 5fb97b81d15f95b30aac107081e1317fb897d737 Mon Sep 17 00:00:00 2001
From: Max Kellermann <mk@cm4all.com>
Date: Wed, 11 May 2022 16:19:44 +0200
Subject: [PATCH] util/AllocatedArray: migrate from {Const,Writable}Buffer to
 std::span

---
 src/decoder/plugins/ModCommon.cxx             |   4 +-
 src/decoder/plugins/ModplugDecoderPlugin.cxx  |   2 +-
 src/decoder/plugins/OpenmptDecoderPlugin.cxx  |   4 +-
 src/lib/icu/CaseFold.cxx                      |   8 +-
 src/lib/icu/Converter.cxx                     |   4 +-
 src/lib/icu/Util.cxx                          |   2 +-
 src/output/plugins/AlsaOutputPlugin.cxx       |   2 +-
 .../plugins/httpd/HttpdOutputPlugin.cxx       |   2 +-
 .../plugins/httpd/IcyMetaDataServer.cxx       |   2 +-
 src/output/plugins/snapcast/Client.cxx        |  44 ++++----
 src/output/plugins/snapcast/Client.hxx        |   5 +-
 src/output/plugins/snapcast/Internal.hxx      |   5 +-
 .../plugins/snapcast/SnapcastOutputPlugin.cxx |  10 +-
 src/tag/Builder.cxx                           |   2 +-
 src/util/AllocatedArray.hxx                   | 100 ++++++++----------
 15 files changed, 93 insertions(+), 103 deletions(-)

diff --git a/src/decoder/plugins/ModCommon.cxx b/src/decoder/plugins/ModCommon.cxx
index 4adf9a272..187da1abe 100644
--- a/src/decoder/plugins/ModCommon.cxx
+++ b/src/decoder/plugins/ModCommon.cxx
@@ -51,8 +51,8 @@ mod_loadfile(const Domain *domain, DecoderClient *client, InputStream &is)
 
 	auto buffer = AllocatedArray<std::byte>(buffer_size);
 
-	std::byte *const end = buffer.end();
-	std::byte *p = buffer.begin();
+	std::byte *p = buffer.data();
+	std::byte *const end = p + buffer.size();
 
 	while (true) {
 		size_t ret = decoder_read(client, is, p, end - p);
diff --git a/src/decoder/plugins/ModplugDecoderPlugin.cxx b/src/decoder/plugins/ModplugDecoderPlugin.cxx
index cd31602af..f6e017888 100644
--- a/src/decoder/plugins/ModplugDecoderPlugin.cxx
+++ b/src/decoder/plugins/ModplugDecoderPlugin.cxx
@@ -73,7 +73,7 @@ static ModPlugFile *
 LoadModPlugFile(DecoderClient *client, InputStream &is)
 {
 	const auto buffer = mod_loadfile(&modplug_domain, client, is);
-	if (buffer.IsNull()) {
+	if (buffer == nullptr) {
 		LogWarning(modplug_domain, "could not load stream");
 		return nullptr;
 	}
diff --git a/src/decoder/plugins/OpenmptDecoderPlugin.cxx b/src/decoder/plugins/OpenmptDecoderPlugin.cxx
index 1e8547d1f..12508d2c3 100644
--- a/src/decoder/plugins/OpenmptDecoderPlugin.cxx
+++ b/src/decoder/plugins/OpenmptDecoderPlugin.cxx
@@ -73,7 +73,7 @@ mod_decode(DecoderClient &client, InputStream &is)
 	char audio_buffer[OPENMPT_FRAME_SIZE];
 
 	const auto buffer = mod_loadfile(&openmpt_domain, &client, is);
-	if (buffer.IsNull()) {
+	if (buffer == nullptr) {
 		LogWarning(openmpt_domain, "could not load stream");
 		return;
 	}
@@ -128,7 +128,7 @@ static bool
 openmpt_scan_stream(InputStream &is, TagHandler &handler) noexcept
 try {
 	const auto buffer = mod_loadfile(&openmpt_domain, nullptr, is);
-	if (buffer.IsNull()) {
+	if (buffer == nullptr) {
 		LogWarning(openmpt_domain, "could not load stream");
 		return false;
 	}
diff --git a/src/lib/icu/CaseFold.cxx b/src/lib/icu/CaseFold.cxx
index d085b3cf6..c6b06c546 100644
--- a/src/lib/icu/CaseFold.cxx
+++ b/src/lib/icu/CaseFold.cxx
@@ -43,21 +43,21 @@ IcuCaseFold(std::string_view src) noexcept
 try {
 #ifdef HAVE_ICU
 	const auto u = UCharFromUTF8(src);
-	if (u.IsNull())
+	if (u.data() == nullptr)
 		return {src};
 
 	AllocatedArray<UChar> folded(u.size() * 2U);
 
 	UErrorCode error_code = U_ZERO_ERROR;
-	size_t folded_length = u_strFoldCase(folded.begin(), folded.size(),
-					     u.begin(), u.size(),
+	size_t folded_length = u_strFoldCase(folded.data(), folded.size(),
+					     u.data(), u.size(),
 					     U_FOLD_CASE_DEFAULT,
 					     &error_code);
 	if (folded_length == 0 || error_code != U_ZERO_ERROR)
 		return {src};
 
 	folded.SetSize(folded_length);
-	return UCharToUTF8({folded.begin(), folded.size()});
+	return UCharToUTF8(std::basic_string_view{folded.data(), folded.size()});
 
 #else
 #error not implemented
diff --git a/src/lib/icu/Converter.cxx b/src/lib/icu/Converter.cxx
index 9fc15c728..9335fe4a7 100644
--- a/src/lib/icu/Converter.cxx
+++ b/src/lib/icu/Converter.cxx
@@ -141,11 +141,11 @@ IcuConverter::FromUTF8(std::string_view s) const
 
 	// TODO: dynamic buffer?
 	char buffer[4096], *target = buffer;
-	const UChar *source = u.begin();
+	const UChar *source = u.data();
 	UErrorCode code = U_ZERO_ERROR;
 
 	ucnv_fromUnicode(converter, &target, buffer + std::size(buffer),
-			 &source, u.end(),
+			 &source, u.data() + u.size(),
 			 nullptr, true, &code);
 
 	if (code != U_ZERO_ERROR)
diff --git a/src/lib/icu/Util.cxx b/src/lib/icu/Util.cxx
index 4739c3f45..084a25def 100644
--- a/src/lib/icu/Util.cxx
+++ b/src/lib/icu/Util.cxx
@@ -38,7 +38,7 @@ UCharFromUTF8(std::string_view src)
 
 	UErrorCode error_code = U_ZERO_ERROR;
 	int32_t dest_length;
-	u_strFromUTF8(dest.begin(), dest_capacity, &dest_length,
+	u_strFromUTF8(dest.data(), dest_capacity, &dest_length,
 		      src.data(), src.size(),
 		      &error_code);
 	if (U_FAILURE(error_code))
diff --git a/src/output/plugins/AlsaOutputPlugin.cxx b/src/output/plugins/AlsaOutputPlugin.cxx
index eebd47540..25df4c920 100644
--- a/src/output/plugins/AlsaOutputPlugin.cxx
+++ b/src/output/plugins/AlsaOutputPlugin.cxx
@@ -760,7 +760,7 @@ Play_44_1_Silence(snd_pcm_t *pcm)
 		throw Alsa::MakeError(err, "snd_pcm_prepare() failed");
 
 	AllocatedArray<int16_t> buffer{channels * period_size};
-	buffer = {};
+	buffer = std::span<const int16_t>{};
 
 	/* play at least 250ms of silence */
 	for (snd_pcm_uframes_t remaining_frames = rate / 4;;) {
diff --git a/src/output/plugins/httpd/HttpdOutputPlugin.cxx b/src/output/plugins/httpd/HttpdOutputPlugin.cxx
index de58286ca..9af3897ac 100644
--- a/src/output/plugins/httpd/HttpdOutputPlugin.cxx
+++ b/src/output/plugins/httpd/HttpdOutputPlugin.cxx
@@ -172,7 +172,7 @@ HttpdOutput::ReadPage()
 	if (size == 0)
 		return nullptr;
 
-	return std::make_shared<Page>(ConstBuffer{buffer, size});
+	return std::make_shared<Page>(std::span{buffer, size});
 }
 
 inline void
diff --git a/src/output/plugins/httpd/IcyMetaDataServer.cxx b/src/output/plugins/httpd/IcyMetaDataServer.cxx
index 96c8bba12..29260608e 100644
--- a/src/output/plugins/httpd/IcyMetaDataServer.cxx
+++ b/src/output/plugins/httpd/IcyMetaDataServer.cxx
@@ -114,5 +114,5 @@ icy_server_metadata_page(const Tag &tag, const TagType *types) noexcept
 	if (icy_string == 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});
 }
diff --git a/src/output/plugins/snapcast/Client.cxx b/src/output/plugins/snapcast/Client.cxx
index 4b4a7601e..0f7e29c1c 100644
--- a/src/output/plugins/snapcast/Client.cxx
+++ b/src/output/plugins/snapcast/Client.cxx
@@ -95,8 +95,8 @@ SnapcastClient::OnSocketReady(unsigned flags) noexcept
 				/* discard old chunks */
 				continue;
 
-			const ConstBuffer<std::byte> payload = chunk->payload;
-			if (!SendWireChunk(payload.ToVoid(), chunk->time)) {
+			const std::span payload = chunk->payload;
+			if (!SendWireChunk(payload, chunk->time)) {
 				// TODO: handle EAGAIN
 				LockClose();
 				return;
@@ -110,23 +110,23 @@ SnapcastClient::OnSocketReady(unsigned flags) noexcept
 }
 
 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);
-	return nbytes == ssize_t(buffer.size);
+	auto nbytes = s.Write(buffer.data(), buffer.size());
+	return nbytes == ssize_t(buffer.size());
 }
 
 template<typename T>
 static bool
 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
-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
@@ -158,10 +158,10 @@ static bool
 SendCodecHeader(SocketDescriptor s, const PackedBE16 id,
 		const SnapcastBase &request,
 		const StringView codec,
-		const ConstBuffer<void> payload) noexcept
+		const std::span<const std::byte> payload) noexcept
 {
 	const PackedLE32 codec_size = codec.size;
-	const PackedLE32 payload_size = payload.size;
+	const PackedLE32 payload_size = payload.size();
 
 	SnapcastBase base{};
 	base.type = uint16_t(SnapcastMessageType::CODEC_HEADER);
@@ -169,7 +169,7 @@ SendCodecHeader(SocketDescriptor s, const PackedBE16 id,
 	base.refers_to = request.id;
 	base.sent = ToSnapcastTimestamp(std::chrono::steady_clock::now());
 	base.size = sizeof(codec_size) + codec.size +
-		sizeof(payload_size) + payload.size;
+		sizeof(payload_size) + payload.size();
 
 	return SendT(s, base) &&
 		SendT(s, codec_size) && Send(s, codec) &&
@@ -212,25 +212,25 @@ SnapcastClient::SendTime(const SnapcastBase &request_header,
 
 static bool
 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
 {
 	SnapcastWireChunk hdr{};
 	hdr.timestamp = ToSnapcastTimestamp(t);
-	hdr.size = payload.size;
+	hdr.size = payload.size();
 
 	SnapcastBase base{};
 	base.type = uint16_t(SnapcastMessageType::WIRE_CHUNK);
 	base.id = id;
 	base.sent = ToSnapcastTimestamp(std::chrono::steady_clock::now());
-	base.size = sizeof(hdr) + payload.size;
+	base.size = sizeof(hdr) + payload.size();
 
 	// TODO: no blocking send()
 	return SendT(s, base) && SendT(s, hdr) && Send(s, payload);
 }
 
 bool
-SnapcastClient::SendWireChunk(ConstBuffer<void> payload,
+SnapcastClient::SendWireChunk(std::span<const std::byte> payload,
 			      std::chrono::steady_clock::time_point t) noexcept
 {
 	return ::SendWireChunk(GetSocket(), next_id++, payload, t);
@@ -238,21 +238,21 @@ SnapcastClient::SendWireChunk(ConstBuffer<void> payload,
 
 static bool
 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{};
 	base.type = uint16_t(SnapcastMessageType::STREAM_TAGS);
 	base.id = id;
 	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);
 }
 
 void
-SnapcastClient::SendStreamTags(ConstBuffer<void> payload) noexcept
+SnapcastClient::SendStreamTags(std::span<const std::byte> payload) noexcept
 {
 	::SendStreamTags(GetSocket(), next_id++, payload);
 }
@@ -270,7 +270,7 @@ SnapcastClient::OnSocketInput(void *data, size_t length) noexcept
 
 	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))) {
 	case SnapcastMessageType::HELLO:
@@ -284,8 +284,8 @@ SnapcastClient::OnSocketInput(void *data, size_t length) noexcept
 		break;
 
 	case SnapcastMessageType::TIME:
-		if (payload.size >= sizeof(SnapcastTime))
-			SendTime(base, *(const SnapcastTime *)payload.data);
+		if (payload.size() >= sizeof(SnapcastTime))
+			SendTime(base, *(const SnapcastTime *)(const void *)payload.data());
 		break;
 
 	default:
diff --git a/src/output/plugins/snapcast/Client.hxx b/src/output/plugins/snapcast/Client.hxx
index d104d4ae8..7e4f19db8 100644
--- a/src/output/plugins/snapcast/Client.hxx
+++ b/src/output/plugins/snapcast/Client.hxx
@@ -26,6 +26,7 @@
 
 #include <chrono>
 #include <cstdint>
+#include <span>
 
 struct SnapcastBase;
 struct SnapcastTime;
@@ -60,7 +61,7 @@ public:
 
 	void LockClose() noexcept;
 
-	void SendStreamTags(ConstBuffer<void> payload) noexcept;
+	void SendStreamTags(std::span<const std::byte> payload) noexcept;
 
 	/**
 	 * Caller must lock the mutex.
@@ -84,7 +85,7 @@ public:
 private:
 	SnapcastChunkPtr LockPopQueue() noexcept;
 
-	bool SendWireChunk(ConstBuffer<void> payload,
+	bool SendWireChunk(std::span<const std::byte> payload,
 			   std::chrono::steady_clock::time_point t) noexcept;
 
 	bool SendServerSettings(const SnapcastBase &request) noexcept;
diff --git a/src/output/plugins/snapcast/Internal.hxx b/src/output/plugins/snapcast/Internal.hxx
index 837b568a5..54fca7430 100644
--- a/src/output/plugins/snapcast/Internal.hxx
+++ b/src/output/plugins/snapcast/Internal.hxx
@@ -161,9 +161,8 @@ public:
 		return "pcm";
 	}
 
-	ConstBuffer<void> GetCodecHeader() const noexcept {
-		ConstBuffer<std::byte> result(codec_header);
-		return result.ToVoid();
+	std::span<const std::byte> GetCodecHeader() const noexcept {
+		return codec_header;
 	}
 
 	/* virtual methods from class AudioOutput */
diff --git a/src/output/plugins/snapcast/SnapcastOutputPlugin.cxx b/src/output/plugins/snapcast/SnapcastOutputPlugin.cxx
index 1abe2a45a..fd5a6fee6 100644
--- a/src/output/plugins/snapcast/SnapcastOutputPlugin.cxx
+++ b/src/output/plugins/snapcast/SnapcastOutputPlugin.cxx
@@ -127,7 +127,7 @@ ReadEncoder(Encoder &encoder)
 	std::byte buffer[4096];
 
 	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};
 }
 
@@ -181,7 +181,7 @@ SnapcastOutput::Close() noexcept
 
 	ClearQueue(chunks);
 
-	codec_header = nullptr;
+	codec_header = std::span<const std::byte>{};
 	delete encoder;
 }
 
@@ -294,12 +294,12 @@ SnapcastOutput::SendTag(const Tag &tag)
 	if (json.empty())
 		return;
 
-	const ConstBuffer payload(json.data(), json.size());
+	const auto payload = std::as_bytes(std::span{json});
 
 	const std::scoped_lock<Mutex> protect(mutex);
 	// TODO: enqueue StreamTags, don't send directly
 	for (auto &client : clients)
-		client.SendStreamTags(payload.ToVoid());
+		client.SendStreamTags(payload);
 #else
 	(void)tag;
 #endif
@@ -348,7 +348,7 @@ SnapcastOutput::Play(const void *chunk, size_t size)
 		if (chunks.empty())
 			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}));
 	}
 
diff --git a/src/tag/Builder.cxx b/src/tag/Builder.cxx
index 468d3d676..28241d3ef 100644
--- a/src/tag/Builder.cxx
+++ b/src/tag/Builder.cxx
@@ -216,7 +216,7 @@ TagBuilder::AddItemInternal(TagType type, StringView value) noexcept
 	assert(!value.empty());
 
 	auto f = FixTagString(value);
-	if (!f.IsNull())
+	if (f != nullptr)
 		value = { f.data(), f.size() };
 
 	AddItemUnchecked(type, value);
diff --git a/src/util/AllocatedArray.hxx b/src/util/AllocatedArray.hxx
index 476997f00..86a703511 100644
--- a/src/util/AllocatedArray.hxx
+++ b/src/util/AllocatedArray.hxx
@@ -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
  * modification, are permitted provided that the following conditions
@@ -27,14 +27,11 @@
  * OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-#ifndef ALLOCATED_ARRAY_HXX
-#define ALLOCATED_ARRAY_HXX
-
-#include "ConstBuffer.hxx"
-#include "WritableBuffer.hxx"
+#pragma once
 
 #include <algorithm>
 #include <cassert>
+#include <span>
 #include <utility>
 
 /**
@@ -42,7 +39,7 @@
  */
 template<class T>
 class AllocatedArray {
-	typedef WritableBuffer<T> Buffer;
+	using Buffer = std::span<T>;
 
 public:
 	using size_type = typename Buffer::size_type;
@@ -51,10 +48,10 @@ public:
 	using pointer = typename Buffer::pointer;
 	using const_pointer = typename Buffer::const_pointer;
 	using iterator = typename Buffer::iterator;
-	using const_iterator = typename Buffer::const_iterator;
+	using const_iterator = typename Buffer::iterator;
 
 protected:
-	Buffer buffer{nullptr};
+	Buffer buffer{};
 
 public:
 	constexpr AllocatedArray() = default;
@@ -62,16 +59,15 @@ public:
 	explicit AllocatedArray(size_type _size) noexcept
 		:buffer{new T[_size], _size} {}
 
-	explicit AllocatedArray(ConstBuffer<T> src) noexcept {
-		if (src == nullptr)
+	explicit AllocatedArray(std::span<const T> src) noexcept {
+		if (src.data() == nullptr)
 			return;
 
-		buffer = {new T[src.size], src.size};
-		std::copy_n(src.data, src.size, buffer.data);
+		buffer = {new T[src.size()], src.size()};
+		std::copy(src.begin(), src.end(), buffer.begin());
 	}
 
-	AllocatedArray(std::nullptr_t n) noexcept
-		:buffer(n) {}
+	AllocatedArray(std::nullptr_t) noexcept {}
 
 	explicit AllocatedArray(const AllocatedArray &other) noexcept
 		:AllocatedArray(other.buffer) {}
@@ -80,27 +76,28 @@ public:
 		:buffer(other.release()) {}
 
 	~AllocatedArray() noexcept {
-		delete[] buffer.data;
+		delete[] buffer.data();
 	}
 
-	AllocatedArray &operator=(ConstBuffer<T> src) noexcept {
-		assert(size() == 0 || buffer.data != nullptr);
-		assert(src.size == 0 || src.data != nullptr);
+	AllocatedArray &operator=(std::span<const T> src) noexcept {
+		assert(empty() || buffer.data() != nullptr);
+		assert(src.empty() || src.data() != nullptr);
 
-		ResizeDiscard(src.size);
-		std::copy_n(src.data, src.size, buffer.data);
+		ResizeDiscard(src.size());
+		std::copy(src.begin(), src.end(), buffer.begin());
 		return *this;
 	}
 
 	AllocatedArray &operator=(const AllocatedArray &other) noexcept {
-		assert(size() == 0 || buffer.data != nullptr);
-		assert(other.size() == 0 || other.buffer.data != nullptr);
+		assert(empty() || buffer.data() != nullptr);
+		assert(other.empty() || other.buffer.data() != nullptr);
 
 		if (&other == this)
 			return *this;
 
 		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;
 	}
 
@@ -111,29 +108,25 @@ public:
 	}
 
 	AllocatedArray &operator=(std::nullptr_t n) noexcept {
-		delete[] buffer.data;
-		buffer = n;
+		delete[] buffer.data();
+		buffer = {};
 		return *this;
 	}
 
-	operator ConstBuffer<T>() const noexcept {
+	operator std::span<const T>() const noexcept {
 		return buffer;
 	}
 
-	operator WritableBuffer<T>() noexcept {
+	operator std::span<T>() noexcept {
 		return buffer;
 	}
 
-	constexpr bool IsNull() const noexcept {
-		return buffer.IsNull();
-	}
-
 	constexpr bool operator==(std::nullptr_t) const noexcept {
-		return buffer == nullptr;
+		return buffer.data() == nullptr;
 	}
 
 	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.
 	 */
 	constexpr size_type size() const noexcept {
-		return buffer.size;
+		return buffer.size();
 	}
 
 	/**
 	 * Returns the number of allocated elements.
 	 */
 	constexpr size_type capacity() const noexcept {
-		return buffer.size;
+		return buffer.size();
 	}
 
 	pointer data() noexcept {
-		return buffer.data;
+		return buffer.data();
 	}
 
 	const_pointer data() const noexcept {
-		return buffer.data;
+		return buffer.data();
 	}
 
 	reference front() noexcept {
@@ -200,7 +193,7 @@ public:
 	}
 
 	constexpr const_iterator begin() const noexcept {
-		return buffer.cbegin();
+		return buffer.begin();
 	}
 
 	iterator end() noexcept {
@@ -208,19 +201,18 @@ public:
 	}
 
 	constexpr const_iterator end() const noexcept {
-		return buffer.cend();
+		return buffer.end();
 	}
 
 	/**
 	 * Resizes the array, discarding old data.
 	 */
 	void ResizeDiscard(size_type _size) noexcept {
-		if (_size == buffer.size)
+		if (_size == buffer.size())
 			return;
 
-		delete[] buffer.data;
-		buffer.size = _size;
-		buffer.data = new T[buffer.size];
+		delete[] buffer.data();
+		buffer = {new T[_size], _size};
 	}
 
 	/**
@@ -229,7 +221,7 @@ public:
 	 * avoid expensive heap operations.
 	 */
 	void GrowDiscard(size_type _size) noexcept {
-		if (_size > buffer.size)
+		if (_size > buffer.size())
 			ResizeDiscard(_size);
 	}
 
@@ -238,16 +230,16 @@ public:
 	 * range of elements, starting from the beginning.
 	 */
 	void GrowPreserve(size_type _size, size_type preserve) noexcept {
-		if (_size <= buffer.size)
+		if (_size <= buffer.size())
 			return;
 
 		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;
-		buffer.data = new_data;
-		buffer.size = _size;
+		delete[] buffer.data();
+		buffer = {new_data, _size};
 	}
 
 	/**
@@ -256,17 +248,15 @@ public:
 	 * they are still allocated).
 	 */
 	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.
 	 */
 	Buffer release() noexcept {
-		return std::exchange(buffer, nullptr);
+		return std::exchange(buffer, std::span<T>{});
 	}
 };
-
-#endif