From a72878c5b9b1ff5dc3fc87767ce46a43e764e3bc Mon Sep 17 00:00:00 2001
From: Max Kellermann <mk@cm4all.com>
Date: Mon, 18 Nov 2019 21:25:04 +0100
Subject: [PATCH] io/FileDescriptor: add method FullRead()

---
 src/system/FileDescriptor.cxx | 22 +++++++++++++++++++++-
 src/system/FileDescriptor.hxx |  8 +++++++-
 test/run_filter.cxx           | 17 +----------------
 3 files changed, 29 insertions(+), 18 deletions(-)

diff --git a/src/system/FileDescriptor.cxx b/src/system/FileDescriptor.cxx
index 1f6bf0808..2e53c8086 100644
--- a/src/system/FileDescriptor.cxx
+++ b/src/system/FileDescriptor.cxx
@@ -1,5 +1,5 @@
 /*
- * Copyright 2012-2018 Max Kellermann <max.kellermann@gmail.com>
+ * Copyright 2012-2019 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
@@ -28,8 +28,10 @@
  */
 
 #include "FileDescriptor.hxx"
+#include "system/Error.hxx"
 
 #include <assert.h>
+#include <stdint.h>
 #include <sys/stat.h>
 #include <fcntl.h>
 
@@ -285,6 +287,24 @@ FileDescriptor::GetSize() const noexcept
 		: -1;
 }
 
+void
+FileDescriptor::FullRead(void *_buffer, size_t length)
+{
+	uint8_t *buffer = (uint8_t *)_buffer;
+
+	while (length > 0) {
+		ssize_t nbytes = Read(buffer, length);
+		if (nbytes <= 0) {
+			if (nbytes < 0)
+				throw MakeErrno("Failed to read");
+			throw std::runtime_error("Unexpected end of file");
+		}
+
+		buffer += nbytes;
+		length -= nbytes;
+	}
+}
+
 #ifndef _WIN32
 
 int
diff --git a/src/system/FileDescriptor.hxx b/src/system/FileDescriptor.hxx
index 92cdddce6..5fabd17dd 100644
--- a/src/system/FileDescriptor.hxx
+++ b/src/system/FileDescriptor.hxx
@@ -1,5 +1,5 @@
 /*
- * Copyright 2012-2018 Max Kellermann <max.kellermann@gmail.com>
+ * Copyright 2012-2019 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
@@ -233,6 +233,12 @@ public:
 		return ::read(fd, buffer, length);
 	}
 
+	/**
+	 * Read until all of the given buffer has been filled.  Throws
+	 * on error.
+	 */
+	void FullRead(void *buffer, size_t length);
+
 	ssize_t Write(const void *buffer, size_t length) noexcept {
 		return ::write(fd, buffer, length);
 	}
diff --git a/test/run_filter.cxx b/test/run_filter.cxx
index 0515b1b4e..116907cee 100644
--- a/test/run_filter.cxx
+++ b/test/run_filter.cxx
@@ -79,21 +79,6 @@ WriteOrThrow(FileDescriptor fd, const void *buffer, size_t size)
 	return nbytes;
 }
 
-static void
-FullRead(FileDescriptor fd, void *_buffer, size_t size)
-{
-	auto buffer = (uint8_t *)_buffer;
-
-	while (size > 0) {
-		size_t nbytes = ReadOrThrow(fd, buffer, size);
-		if (nbytes == 0)
-			throw std::runtime_error("Premature end of input");
-
-		buffer += nbytes;
-		size -= nbytes;
-	}
-}
-
 static void
 FullWrite(FileDescriptor fd, ConstBuffer<uint8_t> src)
 {
@@ -124,7 +109,7 @@ ReadFrames(FileDescriptor fd, void *_buffer, size_t size, size_t frame_size)
 	const size_t modulo = nbytes % frame_size;
 	if (modulo > 0) {
 		size_t rest = frame_size - modulo;
-		FullRead(fd, buffer + nbytes, rest);
+		fd.FullRead(buffer + nbytes, rest);
 		nbytes += rest;
 	}