diff --git a/Builds/VisualStudio2012/beast.vcxproj b/Builds/VisualStudio2012/beast.vcxproj
index 8309bb27d2..6cc039c10c 100644
--- a/Builds/VisualStudio2012/beast.vcxproj
+++ b/Builds/VisualStudio2012/beast.vcxproj
@@ -140,6 +140,7 @@
+
@@ -437,6 +438,12 @@
true
true
+
+ true
+ true
+ true
+ true
+
true
true
diff --git a/Builds/VisualStudio2012/beast.vcxproj.filters b/Builds/VisualStudio2012/beast.vcxproj.filters
index 7b777f5085..c70a09c151 100644
--- a/Builds/VisualStudio2012/beast.vcxproj.filters
+++ b/Builds/VisualStudio2012/beast.vcxproj.filters
@@ -623,6 +623,9 @@
beast_core\containers
+
+ beast_core\files
+
@@ -967,6 +970,9 @@
beast_crypto\math
+
+ beast_core\files
+
diff --git a/modules/beast_core/beast_core.cpp b/modules/beast_core/beast_core.cpp
index 82966a182e..cb3662d5d8 100644
--- a/modules/beast_core/beast_core.cpp
+++ b/modules/beast_core/beast_core.cpp
@@ -155,6 +155,7 @@ namespace beast
#include "files/beast_FileInputStream.cpp"
#include "files/beast_FileOutputStream.cpp"
#include "files/beast_FileSearchPath.cpp"
+#include "files/beast_RandomAccessFile.cpp"
#include "files/beast_TemporaryFile.cpp"
#include "json/beast_JSON.cpp"
diff --git a/modules/beast_core/beast_core.h b/modules/beast_core/beast_core.h
index a0d9a3042f..8abaf273ea 100644
--- a/modules/beast_core/beast_core.h
+++ b/modules/beast_core/beast_core.h
@@ -252,6 +252,7 @@ namespace beast
#include "files/beast_FileOutputStream.h"
#include "files/beast_FileSearchPath.h"
#include "files/beast_MemoryMappedFile.h"
+#include "files/beast_RandomAccessFile.h"
#include "files/beast_TemporaryFile.h"
#include "json/beast_JSON.h"
#include "logging/beast_FileLogger.h"
diff --git a/modules/beast_core/files/beast_RandomAccessFile.cpp b/modules/beast_core/files/beast_RandomAccessFile.cpp
new file mode 100644
index 0000000000..e9588a7382
--- /dev/null
+++ b/modules/beast_core/files/beast_RandomAccessFile.cpp
@@ -0,0 +1,214 @@
+//------------------------------------------------------------------------------
+/*
+ This file is part of Beast: https://github.com/vinniefalco/Beast
+ Copyright 2013, Vinnie Falco
+
+ Portions of this file are from JUCE.
+ Copyright (c) 2013 - Raw Material Software Ltd.
+ Please visit http://www.juce.com
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+//==============================================================================
+
+RandomAccessFile::RandomAccessFile (int bufferSizeToUse) noexcept
+ : fileHandle (nullptr)
+ , currentPosition (0)
+ , bufferSize (bufferSizeToUse)
+ , bytesInBuffer (0)
+ , writeBuffer (bmax (bufferSizeToUse, 16)) // enforce minimum size of 16
+{
+}
+
+RandomAccessFile::~RandomAccessFile ()
+{
+ close ();
+}
+
+Result RandomAccessFile::open (File const& path, Mode mode)
+{
+ close ();
+
+ return nativeOpen (path, mode);
+}
+
+void RandomAccessFile::close ()
+{
+ if (isOpen ())
+ {
+ flushBuffer ();
+ nativeFlush ();
+ nativeClose ();
+ }
+}
+
+Result RandomAccessFile::setPosition (FileOffset newPosition)
+{
+ Result result (Result::ok ());
+
+ if (newPosition != currentPosition)
+ {
+ flushBuffer ();
+
+ result = nativeSetPosition (newPosition);
+ }
+
+ return result;
+}
+
+Result RandomAccessFile::read (void* buffer, ByteCount numBytes, ByteCount* pActualAmount)
+{
+ return nativeRead (buffer, numBytes, pActualAmount);
+}
+
+Result RandomAccessFile::write (const void* data, ByteCount numBytes, ByteCount* pActualAmount)
+{
+ bassert (data != nullptr && ((ssize_t) numBytes) >= 0);
+
+ Result result (Result::ok ());
+
+ ByteCount amountWritten = 0;
+
+ if (bytesInBuffer + numBytes < bufferSize)
+ {
+ memcpy (writeBuffer + bytesInBuffer, data, numBytes);
+ bytesInBuffer += numBytes;
+ currentPosition += numBytes;
+ }
+ else
+ {
+ result = flushBuffer ();
+
+ if (result.wasOk ())
+ {
+ if (numBytes < bufferSize)
+ {
+ bassert (bytesInBuffer == 0);
+
+ memcpy (writeBuffer + bytesInBuffer, data, numBytes);
+ bytesInBuffer += numBytes;
+ currentPosition += numBytes;
+ }
+ else
+ {
+ ByteCount bytesWritten;
+
+ result = nativeWrite (data, numBytes, &bytesWritten);
+
+ if (result.wasOk ())
+ currentPosition += bytesWritten;
+ }
+ }
+ }
+
+ if (pActualAmount != nullptr)
+ *pActualAmount = amountWritten;
+
+ return result;
+}
+
+Result RandomAccessFile::truncate ()
+{
+ Result result = flush ();
+
+ if (result.wasOk ())
+ result = nativeTruncate ();
+
+ return result;
+}
+
+Result RandomAccessFile::flush ()
+{
+ Result result = flushBuffer ();
+
+ if (result.wasOk ())
+ result = nativeFlush ();
+
+ return result;
+}
+
+Result RandomAccessFile::flushBuffer ()
+{
+ bassert (isOpen ());
+
+ Result result (Result::ok ());
+
+ if (bytesInBuffer > 0)
+ {
+ result = nativeWrite (writeBuffer, bytesInBuffer);
+ bytesInBuffer = 0;
+ }
+
+ return result;
+}
+
+//------------------------------------------------------------------------------
+
+class RandomAccessFileTests : public UnitTest
+{
+public:
+ RandomAccessFileTests () : UnitTest ("RandomAccessFile")
+ {
+ }
+
+ struct Payload
+ {
+ Payload (int maxBytes)
+ : bufferSize (maxBytes)
+ , data (maxBytes)
+ {
+ }
+
+ // Create a pseudo-random payload
+ void generate (int64 seedValue) noexcept
+ {
+ Random r (seedValue);
+
+ bytes = 1 + r.nextInt (bufferSize);
+
+ bassert (bytes >= 1 && bytes <= bufferSize);
+
+ for (int i = 0; i < bytes; ++i)
+ data [i] = static_cast (r.nextInt ());
+ }
+
+ bool operator== (Payload const& other) const noexcept
+ {
+ if (bytes == other.bytes)
+ {
+ return memcmp (data.getData (), other.data.getData (), bytes) == 0;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ int const bufferSize;
+ int bytes;
+ HeapBlock data;
+ };
+
+
+ void runTest ()
+ {
+ Result result = file.open (File::createTempFile ("tests"), RandomAccessFile::readWrite);
+
+ expect (result.wasOk (), "Should be ok");
+ }
+
+private:
+ RandomAccessFile file;
+};
+
+static RandomAccessFileTests randomAccessFileTests;
diff --git a/modules/beast_core/files/beast_RandomAccessFile.h b/modules/beast_core/files/beast_RandomAccessFile.h
new file mode 100644
index 0000000000..b97e17dcb1
--- /dev/null
+++ b/modules/beast_core/files/beast_RandomAccessFile.h
@@ -0,0 +1,242 @@
+//------------------------------------------------------------------------------
+/*
+ This file is part of Beast: https://github.com/vinniefalco/Beast
+ Copyright 2013, Vinnie Falco
+
+ Portions of this file are from JUCE.
+ Copyright (c) 2013 - Raw Material Software Ltd.
+ Please visit http://www.juce.com
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+//==============================================================================
+
+#ifndef BEAST_RANDOMACCESSFILE_H_INCLUDED
+#define BEAST_RANDOMACCESSFILE_H_INCLUDED
+
+#include "../misc/beast_Result.h"
+
+/** Provides random access reading and writing to an operating system file.
+
+ This class wraps the underlying native operating system routines for
+ opening and closing a file for reading and/or writing, seeking within
+ the file, and performing read and write operations. There are also methods
+ provided for obtaining an input or output stream which will work with
+ the file.
+
+ Writes are batched using an internal buffer. The buffer is flushed when
+ it fills, the current position is manually changed, or the file
+ is closed. It is also possible to explicitly flush the buffer.
+
+ @note All files are opened in binary mode. No text newline conversions
+ are performed.
+
+ @see FileInputStream, FileOutputStream
+*/
+class BEAST_API RandomAccessFile : Uncopyable, LeakChecked
+{
+public:
+ /** The type of an FileOffset.
+
+ This can be useful when writing templates.
+ */
+ typedef int64 FileOffset;
+
+ /** The type of a byte count.
+
+ This can be useful when writing templates.
+ */
+ typedef size_t ByteCount;
+
+ /** The access mode.
+
+ @see open
+ */
+ enum Mode
+ {
+ readOnly,
+ readWrite
+ };
+
+ //==============================================================================
+ /** Creates an unopened file object.
+
+ @see open, isOpen
+ */
+ explicit RandomAccessFile (int bufferSizeToUse = 16384) noexcept;
+
+ /** Destroy the file object.
+
+ If the operating system file is open it will be closed.
+ */
+ ~RandomAccessFile ();
+
+ /** Determine if a file is open.
+
+ @return `true` if the operating system file is open.
+ */
+ bool isOpen () const noexcept { return fileHandle != nullptr; }
+
+ /** Opens a file object.
+
+ The file is opened with the specified permissions. The initial
+ position is set to the beginning of the file.
+
+ @note If a file is already open, it will be closed first.
+
+ @param path The path to the file
+ @param mode The access permissions
+ @return An indication of the success of the operation.
+
+ @see Mode
+ */
+ Result open (File const& path, Mode mode);
+
+ /** Closes the file object.
+
+ Any data that needs to be flushed will be written before the file is closed.
+
+ @note If no file is opened, this call does nothing.
+ */
+ void close ();
+
+ /** Retrieve the @ref File associated with this object.
+
+ @return The associated @ref File.
+ */
+ File const& getFile () const noexcept { return file; }
+
+ /** Get the current position.
+
+ The next read or write will take place from here.
+
+ @return The current position, as an absolute byte FileOffset from the begining.
+ */
+ FileOffset getPosition () const noexcept { return currentPosition; }
+
+ /** Set the current position.
+
+ The next read or write will take place at this location.
+
+ @param newPosition The byte FileOffset from the beginning of the file to move to.
+
+ @return `true` if the operation was successful.
+ */
+ Result setPosition (FileOffset newPosition);
+
+ /** Read data at the current position.
+
+ The caller is responsible for making sure that the memory pointed to
+ by `buffer` is at least as large as `bytesToRead`.
+
+ @note The file must have been opened with read permission.
+
+ @param buffer The memory to store the incoming data
+ @param numBytes The number of bytes to read.
+ @param pActualAmount Pointer to store the actual amount read, or `nullptr`.
+
+ @return `true` if all the bytes were read.
+ */
+ Result read (void* buffer, ByteCount numBytes, ByteCount* pActualAmount = 0);
+
+ /** Write data at the current position.
+
+ The current position is advanced past the data written. If data is
+ written past the end of the file, the file size is increased on disk.
+
+ The caller is responsible for making sure that the memory pointed to
+ by `buffer` is at least as large as `bytesToWrite`.
+
+ @note The file must have been opened with write permission.
+
+ @param data A pointer to the data buffer to write to the file.
+ @param numBytes The number of bytes to write.
+ @param pActualAmount Pointer to store the actual amount written, or `nullptr`.
+
+ @return `true` if all the data was written.
+ */
+ Result write (const void* data, ByteCount numBytes, ByteCount* pActualAmount = 0);
+
+ /** Truncate the file at the current position.
+ */
+ Result truncate ();
+
+ /** Flush the output buffers.
+
+ This calls the operating system to make sure all data has been written.
+ */
+ Result flush();
+
+ //==============================================================================
+private:
+ Result flushBuffer ();
+
+ // Some of these these methods are implemented natively on
+ // the corresponding platform.
+ //
+ // See beast_posix_SharedCode.h and beast_win32_Files.cpp
+ Result nativeOpen (File const& path, Mode mode);
+ void nativeClose ();
+ Result nativeSetPosition (FileOffset newPosition);
+ Result nativeRead (void* buffer, ByteCount numBytes, ByteCount* pActualAmount = 0);
+ Result nativeWrite (const void* data, ByteCount numBytes, ByteCount* pActualAmount = 0);
+ Result nativeTruncate ();
+ Result nativeFlush ();
+
+private:
+ File file;
+ void* fileHandle;
+ FileOffset currentPosition;
+ ByteCount const bufferSize;
+ ByteCount bytesInBuffer;
+ HeapBlock writeBuffer;
+};
+
+class BEAST_API RandomAccessFileInputStream : public InputStream
+{
+public:
+ explicit RandomAccessFileInputStream (RandomAccessFile& file) : m_file (file) { }
+
+ int64 getTotalLength() { return m_file.getFile ().getSize (); }
+ bool isExhausted() { return getPosition () == getTotalLength (); }
+ int read (void* destBuffer, int maxBytesToRead)
+ {
+ size_t actualBytes = 0;
+ m_file.read (destBuffer, maxBytesToRead, &actualBytes);
+ return actualBytes;
+ }
+
+ int64 getPosition() { return m_file.getPosition (); }
+ bool setPosition (int64 newPosition) { return m_file.setPosition (newPosition); }
+ void skipNextBytes (int64 numBytesToSkip) { m_file.setPosition (getPosition () + numBytesToSkip); }
+
+private:
+ RandomAccessFile& m_file;
+};
+
+class BEAST_API RandomAccessFileOutputStream : public OutputStream
+{
+public:
+ explicit RandomAccessFileOutputStream (RandomAccessFile& file) : m_file (file) { }
+
+ void flush() { m_file.flush (); }
+ int64 getPosition() { return m_file.getPosition (); }
+ bool setPosition (int64 newPosition) { return m_file.setPosition (newPosition); }
+ bool write (const void* dataToWrite, size_t numberOfBytes) { return m_file.write (dataToWrite, numberOfBytes); }
+
+private:
+ RandomAccessFile& m_file;
+};
+
+#endif
+
diff --git a/modules/beast_core/memory/beast_Uncopyable.h b/modules/beast_core/memory/beast_Uncopyable.h
index e1f1a614b1..349dde0a10 100644
--- a/modules/beast_core/memory/beast_Uncopyable.h
+++ b/modules/beast_core/memory/beast_Uncopyable.h
@@ -45,13 +45,16 @@
@code
- class MyClass : Uncopyable
+ class MyClass : public Uncopyable
{
public:
//...
};
@endcode
+
+ @note The derivation should be public or else child classes which
+ also derive from Uncopyable may not compile.
*/
class Uncopyable
{
diff --git a/modules/beast_core/native/beast_posix_SharedCode.h b/modules/beast_core/native/beast_posix_SharedCode.h
index 936be35f40..7de9d0ebbd 100644
--- a/modules/beast_core/native/beast_posix_SharedCode.h
+++ b/modules/beast_core/native/beast_posix_SharedCode.h
@@ -504,6 +504,176 @@ Result FileOutputStream::truncate()
return getResultForReturnValue (ftruncate (getFD (fileHandle), (off_t) currentPosition));
}
+//==============================================================================
+RandomAccessFile::RandomAccessFile (int bufferSizeToUse) noexcept
+ : fileHandle (nullptr)
+ , currentPosition (0)
+ , writeBuffer (bufferSizeToUse)
+{
+}
+
+RandomAccessFile::~RandomAccessFile ()
+{
+ close ();
+}
+
+Result RandomAccessFile::open (File const& path, Mode mode)
+{
+ close ();
+
+ Result result (Result::ok ());
+
+ if (path.exists())
+ {
+ int oflag;
+ switch (mode)
+ {
+ case readOnly:
+ oflag = O_RDONLY;
+ break;
+
+ default:
+ case readWRite:
+ oflag = O_RDWR;
+ break;
+ };
+
+ const int f = ::open (path.getFullPathName().toUTF8(), oflag, 00644);
+
+ if (f != -1)
+ {
+ currentPosition = lseek (f, 0, SEEK_SET);
+
+ if (currentPosition >= 0)
+ {
+ file = path;
+ fileHandle = fdToVoidPointer (f);
+ }
+ else
+ {
+ result = getResultForErrno();
+ ::close (f);
+ }
+ }
+ else
+ {
+ result = getResultForErrno();
+ }
+ }
+ else if (mode == readWrite)
+ {
+ const int f = open (file.getFullPathName().toUTF8(), O_RDWR + O_CREAT, 00644);
+
+ if (f != -1)
+ {
+ file = path;
+ fileHandle = fdToVoidPointer (f);
+ }
+ else
+ {
+ result = getResultForErrno();
+ }
+ }
+ else
+ {
+ // file doesn't exist and we're opening read-only
+ Result::fail (String (strerror (ENOENT)));
+ }
+
+ return result;
+}
+
+void RandomAccessFile::close ()
+{
+ if (fileHandle != nullptr)
+ {
+ file = File::nonexistent ();
+ ::close (getFD (fileHandle));
+ fileHandle = nullptr;
+ }
+}
+
+Result RandomAccessFile::setPosition (Offset newPosition)
+{
+ bassert (isOpen ());
+
+ Result result (Result::ok ());
+
+ off_t const actual = lseek (getFD (fileHandle), newPosition, SEEK_SET);
+
+ if (actual != newPosition)
+ result = getResultForErrno();
+
+ return result;
+}
+
+Result RandomAccessFile::read (void* buffer, ByteCount numBytes, ByteCount* pActualAmount )
+{
+ bassert (isOpen ());
+
+ Result result (Result::ok ());
+
+ ssize_t amount = ::read (getFD (fileHandle), buffer, numBytes);
+
+ if (amount < 0)
+ {
+ result = getResultForErrno();
+ amount = 0;
+ }
+
+ if (pActualAmount != nullptr)
+ *pActualAmount = amount;
+
+ return (size_t) result;
+}
+
+Result RandomAccessFile::write (void const* data, ByteCount numBytes, size_t* pActualAmount)
+{
+ bassert (isOpen ());
+
+ Result result (Result::ok ());
+
+ ssize_t const actual = ::write (getFD (fileHandle), data, numBytes);
+
+ if (actual == -1)
+ {
+ status = getResultForErrno();
+ actual = 0;
+ }
+
+ if (pActualAmount != nullptr)
+ *pActualAmount = actual;
+
+ return result;
+}
+
+Result RandomAccessFile::truncate ()
+{
+ flush();
+
+ return getResultForReturnValue (ftruncate (getFD (fileHandle), (off_t) currentPosition));
+}
+
+void RandomAccessFile::flush ()
+{
+ bassert (isOpen ());
+
+ if (fileHandle != nullptr)
+ {
+ if (fsync (getFD (fileHandle)) == -1)
+ status = getResultForErrno();
+
+ #if BEAST_ANDROID
+ // This stuff tells the OS to asynchronously update the metadata
+ // that the OS has cached aboud the file - this metadata is used
+ // when the device is acting as a USB drive, and unless it's explicitly
+ // refreshed, it'll get out of step with the real file.
+ const LocalRef t (javaString (file.getFullPathName()));
+ android.activity.callVoidMethod (BeastAppActivity.scanFile, t.get());
+ #endif
+ }
+}
+
//==============================================================================
String SystemStats::getEnvironmentVariable (const String& name, const String& defaultValue)
{
diff --git a/modules/beast_core/native/beast_win32_Files.cpp b/modules/beast_core/native/beast_win32_Files.cpp
index 444bc51c3e..cb5933a69b 100644
--- a/modules/beast_core/native/beast_win32_Files.cpp
+++ b/modules/beast_core/native/beast_win32_Files.cpp
@@ -307,6 +307,161 @@ Result FileOutputStream::truncate()
: WindowsFileHelpers::getResultForLastError();
}
+//==============================================================================
+
+Result RandomAccessFile::nativeOpen (File const& path, Mode mode)
+{
+ bassert (! isOpen ());
+
+ Result result (Result::ok ());
+
+ DWORD dwDesiredAccess;
+ switch (mode)
+ {
+ case readOnly:
+ dwDesiredAccess = GENERIC_READ;
+ break;
+
+ default:
+ case readWrite:
+ dwDesiredAccess = GENERIC_READ | GENERIC_WRITE;
+ break;
+ };
+
+ DWORD dwCreationDisposition;
+ switch (mode)
+ {
+ case readOnly:
+ dwCreationDisposition = OPEN_EXISTING;
+ break;
+
+ default:
+ case readWrite:
+ dwCreationDisposition = OPEN_ALWAYS;
+ break;
+ };
+
+ HANDLE h = CreateFile (path.getFullPathName().toWideCharPointer(),
+ dwDesiredAccess,
+ FILE_SHARE_READ,
+ 0,
+ dwCreationDisposition,
+ FILE_ATTRIBUTE_NORMAL,
+ 0);
+
+ if (h != INVALID_HANDLE_VALUE)
+ {
+ file = path;
+ fileHandle = h;
+
+ result = setPosition (0);
+
+ if (result.failed ())
+ nativeClose ();
+ }
+ else
+ {
+ result = WindowsFileHelpers::getResultForLastError();
+ }
+
+ return result;
+}
+
+void RandomAccessFile::nativeClose ()
+{
+ bassert (isOpen ());
+
+ CloseHandle ((HANDLE) fileHandle);
+
+ file = File::nonexistent ();
+ fileHandle = nullptr;
+ currentPosition = 0;
+}
+
+Result RandomAccessFile::nativeSetPosition (FileOffset newPosition)
+{
+ bassert (isOpen ());
+
+ Result result (Result::ok ());
+
+ LARGE_INTEGER li;
+ li.QuadPart = newPosition;
+ li.LowPart = SetFilePointer ((HANDLE) fileHandle,
+ (LONG) li.LowPart,
+ &li.HighPart,
+ FILE_BEGIN);
+
+ if (li.LowPart != INVALID_SET_FILE_POINTER)
+ {
+ currentPosition = li.QuadPart;
+ }
+ else
+ {
+ result = WindowsFileHelpers::getResultForLastError();
+ }
+
+ return result;
+}
+
+Result RandomAccessFile::nativeRead (void* buffer, ByteCount numBytes, ByteCount* pActualAmount )
+{
+ bassert (isOpen ());
+
+ Result result (Result::ok ());
+
+ DWORD actualNum = 0;
+
+ if (! ReadFile ((HANDLE) fileHandle, buffer, (DWORD) numBytes, &actualNum, 0))
+ result = WindowsFileHelpers::getResultForLastError();
+
+ if (pActualAmount != nullptr)
+ *pActualAmount = actualNum;
+
+ return result;
+}
+
+Result RandomAccessFile::nativeWrite (void const* data, ByteCount numBytes, size_t* pActualAmount)
+{
+ bassert (isOpen ());
+
+ Result result (Result::ok ());
+
+ DWORD actualNum = 0;
+
+ if (! WriteFile ((HANDLE) fileHandle, data, (DWORD) numBytes, &actualNum, 0))
+ result = WindowsFileHelpers::getResultForLastError();
+
+ if (pActualAmount != nullptr)
+ *pActualAmount = actualNum;
+
+ return result;
+}
+
+Result RandomAccessFile::nativeTruncate ()
+{
+ bassert (isOpen ());
+
+ Result result (Result::ok ());
+
+ if (! SetEndOfFile ((HANDLE) fileHandle))
+ result = WindowsFileHelpers::getResultForLastError();
+
+ return result;
+}
+
+Result RandomAccessFile::nativeFlush ()
+{
+ bassert (isOpen ());
+
+ Result result (Result::ok ());
+
+ if (! FlushFileBuffers ((HANDLE) fileHandle))
+ result = WindowsFileHelpers::getResultForLastError();
+
+ return result;
+}
+
+
//==============================================================================
void MemoryMappedFile::openInternal (const File& file, AccessMode mode)
{
diff --git a/modules/beast_core/streams/beast_MemoryOutputStream.h b/modules/beast_core/streams/beast_MemoryOutputStream.h
index c93f1ba0f2..f2e8f7ad8c 100644
--- a/modules/beast_core/streams/beast_MemoryOutputStream.h
+++ b/modules/beast_core/streams/beast_MemoryOutputStream.h
@@ -38,7 +38,6 @@
class BEAST_API MemoryOutputStream
: public OutputStream
, LeakChecked
- , Uncopyable
{
public:
//==============================================================================
diff --git a/modules/beast_core/streams/beast_OutputStream.h b/modules/beast_core/streams/beast_OutputStream.h
index 33b744a7d6..0528f0fcac 100644
--- a/modules/beast_core/streams/beast_OutputStream.h
+++ b/modules/beast_core/streams/beast_OutputStream.h
@@ -40,7 +40,7 @@ class File;
@see InputStream, MemoryOutputStream, FileOutputStream
*/
-class BEAST_API OutputStream
+class BEAST_API OutputStream : public Uncopyable
{
protected:
//==============================================================================