From b37c031fd10985e3c0df4a2e24396c119acf017a Mon Sep 17 00:00:00 2001
From: Max Kellermann <max@musicpd.org>
Date: Fri, 20 May 2022 10:17:20 +0200
Subject: [PATCH] util/{HugeAllocator,SparseBuffer}: use std::span

---
 src/MusicBuffer.hxx                |  1 +
 src/input/BufferingInputStream.cxx | 14 ++++----
 src/util/HugeAllocator.cxx         |  8 ++---
 src/util/HugeAllocator.hxx         | 51 ++++++++++++++----------------
 src/util/SpanCast.hxx              | 49 ++++++++++++++++++++++++++++
 src/util/SparseBuffer.hxx          | 13 +++-----
 6 files changed, 89 insertions(+), 47 deletions(-)
 create mode 100644 src/util/SpanCast.hxx

diff --git a/src/MusicBuffer.hxx b/src/MusicBuffer.hxx
index 0d1aedd2b..f2a8d4470 100644
--- a/src/MusicBuffer.hxx
+++ b/src/MusicBuffer.hxx
@@ -20,6 +20,7 @@
 #ifndef MPD_MUSIC_BUFFER_HXX
 #define MPD_MUSIC_BUFFER_HXX
 
+#include "MusicChunk.hxx"
 #include "MusicChunkPtr.hxx"
 #include "util/SliceBuffer.hxx"
 #include "thread/Mutex.hxx"
diff --git a/src/input/BufferingInputStream.cxx b/src/input/BufferingInputStream.cxx
index e1cd0b304..172ee2b65 100644
--- a/src/input/BufferingInputStream.cxx
+++ b/src/input/BufferingInputStream.cxx
@@ -84,8 +84,8 @@ BufferingInputStream::Read(std::unique_lock<Mutex> &lock, size_t offset,
 		auto r = buffer.Read(offset);
 		if (r.HasData()) {
 			/* yay, we have some data */
-			size_t nbytes = std::min(s, r.defined_buffer.size);
-			memcpy(ptr, r.defined_buffer.data, nbytes);
+			size_t nbytes = std::min(s, r.defined_buffer.size());
+			memcpy(ptr, r.defined_buffer.data(), nbytes);
 			return nbytes;
 		}
 
@@ -107,9 +107,9 @@ BufferingInputStream::FindFirstHole() const noexcept
 		/* a hole at the beginning */
 		return 0;
 
-	if (r.defined_buffer.size < size())
+	if (r.defined_buffer.size() < size())
 		/* a hole in the middle */
-		return r.defined_buffer.size;
+		return r.defined_buffer.size();
 
 	/* the file has been read completely */
 	return INVALID_OFFSET;
@@ -163,10 +163,10 @@ BufferingInputStream::RunThreadLocked(std::unique_lock<Mutex> &lock)
 			   hard disk, instead of returning when "some"
 			   data has been read */
 			constexpr size_t MAX_READ = 64 * 1024;
-			if (w.size > MAX_READ)
-				w.size = MAX_READ;
 
-			size_t nbytes = input->Read(lock, w.data, w.size);
+			size_t nbytes = input->Read(lock, w.data(),
+						    std::min(w.size(),
+							     MAX_READ));
 			buffer.Commit(read_offset, read_offset + nbytes);
 
 			client_cond.notify_all();
diff --git a/src/util/HugeAllocator.cxx b/src/util/HugeAllocator.cxx
index 48cdba720..7dd0b61b8 100644
--- a/src/util/HugeAllocator.cxx
+++ b/src/util/HugeAllocator.cxx
@@ -56,7 +56,7 @@ AlignToPageSize(size_t size) noexcept
 	return (size + ps - 1) / ps * ps;
 }
 
-WritableBuffer<void>
+std::span<std::byte>
 HugeAllocate(size_t size)
 {
 	size = AlignToPageSize(size);
@@ -74,7 +74,7 @@ HugeAllocate(size_t size)
 	madvise(p, size, MADV_HUGEPAGE);
 #endif
 
-	return {p, size};
+	return {(std::byte *)p, size};
 }
 
 void
@@ -108,7 +108,7 @@ HugeDiscard(void *p, size_t size) noexcept
 
 #elif defined(_WIN32)
 
-WritableBuffer<void>
+std::span<std::byte>
 HugeAllocate(size_t size)
 {
 	// TODO: use MEM_LARGE_PAGES
@@ -119,7 +119,7 @@ HugeAllocate(size_t size)
 		throw std::bad_alloc();
 
 	// TODO: round size up to the page size
-	return {p, size};
+	return {(std::byte *)p, size};
 }
 
 #endif
diff --git a/src/util/HugeAllocator.hxx b/src/util/HugeAllocator.hxx
index d87517e92..1169a176b 100644
--- a/src/util/HugeAllocator.hxx
+++ b/src/util/HugeAllocator.hxx
@@ -27,12 +27,12 @@
  * OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-#ifndef HUGE_ALLOCATOR_HXX
-#define HUGE_ALLOCATOR_HXX
+#pragma once
 
-#include "WritableBuffer.hxx"
+#include "SpanCast.hxx"
 
 #include <cstddef>
+#include <span>
 #include <utility>
 
 #ifdef __linux__
@@ -48,7 +48,7 @@
  * (to the next page size), so callers can take advantage of this
  * allocation overhead
  */
-WritableBuffer<void>
+std::span<std::byte>
 HugeAllocate(size_t size);
 
 /**
@@ -87,7 +87,7 @@ HugeDiscard(void *p, size_t size) noexcept;
 #elif defined(_WIN32)
 #include <memoryapi.h>
 
-WritableBuffer<void>
+std::span<std::byte>
 HugeAllocate(size_t size);
 
 static inline void
@@ -118,16 +118,16 @@ HugeDiscard(void *p, size_t size) noexcept
 
 #include <cstdint>
 
-static inline WritableBuffer<void>
+static inline std::span<std::byte>
 HugeAllocate(size_t size)
 {
-	return {new uint8_t[size], size};
+	return {new std::byte[size], size};
 }
 
 static inline void
 HugeFree(void *_p, size_t) noexcept
 {
-	auto *p = (uint8_t *)_p;
+	auto *p = (std::byte *)_p;
 	delete[] p;
 }
 
@@ -153,7 +153,7 @@ HugeDiscard(void *, size_t) noexcept
  */
 template<typename T>
 class HugeArray {
-	typedef WritableBuffer<T> Buffer;
+	using Buffer = std::span<T>;
 	Buffer buffer{nullptr};
 
 public:
@@ -162,20 +162,19 @@ public:
 	typedef typename Buffer::reference reference;
 	typedef typename Buffer::const_reference const_reference;
 	typedef typename Buffer::iterator iterator;
-	typedef typename Buffer::const_iterator const_iterator;
 
 	constexpr HugeArray() = default;
 
 	explicit HugeArray(size_type _size)
-		:buffer(Buffer::FromVoidFloor(HugeAllocate(sizeof(value_type) * _size))) {}
+		:buffer(FromBytesFloor<value_type>(HugeAllocate(sizeof(value_type) * _size))) {}
 
 	constexpr HugeArray(HugeArray &&other) noexcept
 		:buffer(std::exchange(other.buffer, nullptr)) {}
 
 	~HugeArray() noexcept {
-		if (buffer != nullptr) {
-			auto v = buffer.ToVoid();
-			HugeFree(v.data, v.size);
+		if (!buffer.empty()) {
+			auto v = std::as_writable_bytes(buffer);
+			HugeFree(v.data(), v.size());
 		}
 	}
 
@@ -186,18 +185,18 @@ public:
 	}
 
 	void SetName(const char *name) noexcept {
-		const auto v = buffer.ToVoid();
-		HugeSetName(v.data, v.size, name);
+		const auto v = std::as_writable_bytes(buffer);
+		HugeSetName(v.data(), v.size(), name);
 	}
 
 	void ForkCow(bool enable) noexcept {
-		auto v = buffer.ToVoid();
-		HugeForkCow(v.data, v.size, enable);
+		const auto v = std::as_writable_bytes(buffer);
+		HugeForkCow(v.data(), v.size(), enable);
 	}
 
 	void Discard() noexcept {
-		auto v = buffer.ToVoid();
-		HugeDiscard(v.data, v.size);
+		const auto v = std::as_writable_bytes(buffer);
+		HugeDiscard(v.data(), v.size());
 	}
 
 	constexpr bool operator==(std::nullptr_t) const noexcept {
@@ -212,7 +211,7 @@ public:
 	 * Returns the number of allocated elements.
 	 */
 	constexpr size_type size() const noexcept {
-		return buffer.size;
+		return buffer.size();
 	}
 
 	reference front() noexcept {
@@ -249,17 +248,15 @@ public:
 		return buffer.begin();
 	}
 
-	constexpr const_iterator begin() const noexcept {
-		return buffer.cbegin();
+	constexpr auto begin() const noexcept {
+		return buffer.begin();
 	}
 
 	iterator end() noexcept {
 		return buffer.end();
 	}
 
-	constexpr const_iterator end() const noexcept {
-		return buffer.cend();
+	constexpr auto end() const noexcept {
+		return buffer.end();
 	}
 };
-
-#endif
diff --git a/src/util/SpanCast.hxx b/src/util/SpanCast.hxx
new file mode 100644
index 000000000..a2cb624ce
--- /dev/null
+++ b/src/util/SpanCast.hxx
@@ -0,0 +1,49 @@
+/*
+ * Copyright 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
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
+ * FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <cstddef>
+#include <span>
+
+/**
+ * Cast a std::span<std::byte> to a std::span<T>, rounding down to the
+ * next multiple of T's size.
+ */
+template<typename T>
+constexpr std::span<T>
+FromBytesFloor(std::span<std::byte> other) noexcept
+{
+	static_assert(sizeof(T) > 0, "Empty base type");
+
+	return {
+		reinterpret_cast<T *>(other.data()),
+		other.size() / sizeof(T),
+	};
+}
diff --git a/src/util/SparseBuffer.hxx b/src/util/SparseBuffer.hxx
index 7232609c3..9d16ee4fb 100644
--- a/src/util/SparseBuffer.hxx
+++ b/src/util/SparseBuffer.hxx
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013-2018 Max Kellermann <max.kellermann@gmail.com>
+ * Copyright 2013-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,12 +27,9 @@
  * OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-#ifndef SPARSE_BUFFER_HXX
-#define SPARSE_BUFFER_HXX
+#pragma once
 
 #include "HugeAllocator.hxx"
-#include "ConstBuffer.hxx"
-#include "WritableBuffer.hxx"
 
 #include <cassert>
 #include <map>
@@ -118,7 +115,7 @@ public:
 
 	struct ReadResult {
 		size_type undefined_size;
-		ConstBuffer<T> defined_buffer;
+		std::span<const T> defined_buffer;
 
 		constexpr bool HasData() const noexcept {
 			return undefined_size == 0 &&
@@ -131,7 +128,7 @@ public:
 		return {c.undefined_size, {&buffer.front() + offset + c.undefined_size, c.defined_size}};
 	}
 
-	WritableBuffer<T> Write(size_type offset) noexcept {
+	std::span<T> Write(size_type offset) noexcept {
 		auto c = map.Check(offset);
 		return {&buffer.front() + offset, c.undefined_size};
 	}
@@ -140,5 +137,3 @@ public:
 		map.Commit(start_offset, end_offset);
 	}
 };
-
-#endif