diff --git a/src/io/BufferedReader.cxx b/src/io/BufferedReader.cxx
index 5808cd419..95f48e73e 100644
--- a/src/io/BufferedReader.cxx
+++ b/src/io/BufferedReader.cxx
@@ -1,5 +1,5 @@
 /*
- * Copyright 2014-2019 Max Kellermann <max.kellermann@gmail.com>
+ * Copyright 2014-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
@@ -35,8 +35,6 @@
 #include <cstdint>
 #include <stdexcept>
 
-#include <string.h>
-
 bool
 BufferedReader::Fill(bool need_more)
 {
@@ -68,8 +66,8 @@ BufferedReader::ReadFull(std::size_t size)
 {
 	while (true) {
 		auto r = Read();
-		if (r.size >= size)
-			return r.data;
+		if (r.size() >= size)
+			return r.data();
 
 		if (!Fill(true))
 			throw std::runtime_error("Premature end of file");
@@ -77,25 +75,22 @@ BufferedReader::ReadFull(std::size_t size)
 }
 
 std::size_t
-BufferedReader::ReadFromBuffer(WritableBuffer<void> dest) noexcept
+BufferedReader::ReadFromBuffer(std::span<std::byte> dest) noexcept
 {
-	auto src = Read();
-	std::size_t nbytes = std::min(src.size, dest.size);
-	memcpy(dest.data, src.data, nbytes);
+	const auto src = Read();
+	std::size_t nbytes = std::min(src.size(), dest.size());
+	std::copy_n(src.data(), nbytes, dest.data());
 	Consume(nbytes);
 	return nbytes;
 }
 
 void
-BufferedReader::ReadFull(WritableBuffer<void> _dest)
+BufferedReader::ReadFull(std::span<std::byte> dest)
 {
-	auto dest = WritableBuffer<uint8_t>::FromVoid(_dest);
-	assert(dest.size == _dest.size);
-
 	while (true) {
-		std::size_t nbytes = ReadFromBuffer(dest.ToVoid());
-		dest.skip_front(nbytes);
-		if (dest.size == 0)
+		std::size_t nbytes = ReadFromBuffer(dest);
+		dest = dest.subspan(nbytes);
+		if (dest.empty())
 			break;
 
 		if (!Fill(true))
diff --git a/src/io/BufferedReader.hxx b/src/io/BufferedReader.hxx
index 4ecc7f8cb..035ac8d8e 100644
--- a/src/io/BufferedReader.hxx
+++ b/src/io/BufferedReader.hxx
@@ -1,5 +1,5 @@
 /*
- * Copyright 2014-2019 Max Kellermann <max.kellermann@gmail.com>
+ * Copyright 2014-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,12 @@
  * OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-#ifndef BUFFERED_READER_HXX
-#define BUFFERED_READER_HXX
+#pragma once
 
 #include "util/DynamicFifoBuffer.hxx"
 
 #include <cstddef>
+#include <span>
 
 class Reader;
 
@@ -64,7 +64,7 @@ public:
 	bool Fill(bool need_more);
 
 	[[gnu::pure]]
-	WritableBuffer<void> Read() const noexcept {
+	std::span<std::byte> Read() const noexcept {
 		return buffer.Read().ToVoid();
 	}
 
@@ -83,14 +83,14 @@ public:
 	 * Read (and consume) data from the input buffer into the
 	 * given buffer.  Does not attempt to refill the buffer.
 	 */
-	std::size_t ReadFromBuffer(WritableBuffer<void> dest) noexcept;
+	std::size_t ReadFromBuffer(std::span<std::byte> dest) noexcept;
 
 	/**
 	 * Read data into the given buffer and consume it from our
 	 * buffer.  Throw an exception if the request cannot be
 	 * forfilled.
 	 */
-	void ReadFull(WritableBuffer<void> dest);
+	void ReadFull(std::span<std::byte> dest);
 
 	char *ReadLine();
 
@@ -98,5 +98,3 @@ public:
 		return line_number;
 	}
 };
-
-#endif