ByteStream: Move routines from FileSystem to ByteStream

This commit is contained in:
Connor McLaughlin
2022-07-08 20:11:31 +10:00
parent d81e156a29
commit b5bf3593c4
13 changed files with 155 additions and 357 deletions

View File

@ -118,7 +118,8 @@ add_library(common
target_include_directories(common PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/..")
target_include_directories(common PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/..")
target_link_libraries(common PRIVATE glad stb Threads::Threads libchdr glslang vulkan-loader zlib minizip samplerate)
target_link_libraries(common PUBLIC fmt Threads::Threads)
target_link_libraries(common PRIVATE glad stb libchdr glslang vulkan-loader zlib minizip samplerate)
if(WIN32)
target_sources(common PRIVATE

View File

@ -896,9 +896,7 @@ void GrowableMemoryByteStream::Grow(u32 MinimumGrowth)
ResizeMemory(NewSize);
}
#if defined(_MSC_VER)
std::unique_ptr<ByteStream> ByteStream_OpenFileStream(const char* fileName, u32 openMode)
std::unique_ptr<ByteStream> ByteStream::OpenFile(const char* fileName, u32 openMode)
{
if ((openMode & (BYTESTREAM_OPEN_CREATE | BYTESTREAM_OPEN_WRITE)) == BYTESTREAM_OPEN_WRITE)
{
@ -945,80 +943,10 @@ std::unique_ptr<ByteStream> ByteStream_OpenFileStream(const char* fileName, u32
modeString[modeStringLength] = 0;
if (openMode & BYTESTREAM_OPEN_CREATE_PATH)
{
u32 i;
u32 fileNameLength = static_cast<u32>(std::strlen(fileName));
char* tempStr = (char*)alloca(fileNameLength + 1);
// check if it starts with a drive letter. if so, skip ahead
if (fileNameLength >= 2 && fileName[1] == ':')
{
if (fileNameLength <= 3)
{
// create a file called driveletter: or driveletter:\ ? you must be crazy
i = fileNameLength;
}
else
{
std::memcpy(tempStr, fileName, 3);
i = 3;
}
}
else
{
// start at beginning
i = 0;
}
// step through each path component, create folders as necessary
for (; i < fileNameLength; i++)
{
if (i > 0 && (fileName[i] == '\\' || fileName[i] == '/'))
{
// terminate the string
tempStr[i] = '\0';
// check if it exists
struct stat s;
if (stat(tempStr, &s) < 0)
{
if (errno == ENOENT)
{
// try creating it
if (_mkdir(tempStr) < 0)
{
// no point trying any further down the chain
break;
}
}
else // if (errno == ENOTDIR)
{
// well.. someone's trying to open a fucking weird path that is comprised of both directories and files...
// I aint sticking around here to find out what disaster awaits... let fopen deal with it
break;
}
}
// append platform path seperator
#if defined(_WIN32)
tempStr[i] = '\\';
#else
tempStr[i] = '/';
#endif
}
else
{
// append character to temp string
tempStr[i] = fileName[i];
}
}
}
if (openMode & BYTESTREAM_OPEN_ATOMIC_UPDATE)
{
DebugAssert(openMode & (BYTESTREAM_OPEN_CREATE | BYTESTREAM_OPEN_WRITE));
#ifdef _WIN32
#ifndef _UWP
// generate the temporary file name
u32 fileNameLength = static_cast<u32>(std::strlen(fileName));
@ -1116,131 +1044,7 @@ std::unique_ptr<ByteStream> ByteStream_OpenFileStream(const char* fileName, u32
// return pointer
return pStream;
}
else
{
// forward through
FILE* pFile = FileSystem::OpenCFile(fileName, modeString);
if (!pFile)
return nullptr;
return std::make_unique<FileByteStream>(pFile);
}
}
#else
std::unique_ptr<ByteStream> ByteStream_OpenFileStream(const char* fileName, u32 openMode)
{
if ((openMode & (BYTESTREAM_OPEN_CREATE | BYTESTREAM_OPEN_WRITE)) == BYTESTREAM_OPEN_WRITE)
{
// if opening with write but not create, the path must exist.
struct stat s;
if (stat(fileName, &s) < 0)
return nullptr;
}
char modeString[16];
u32 modeStringLength = 0;
if (openMode & BYTESTREAM_OPEN_WRITE)
{
if (openMode & BYTESTREAM_OPEN_TRUNCATE)
modeString[modeStringLength++] = 'w';
else
modeString[modeStringLength++] = 'a';
modeString[modeStringLength++] = 'b';
if (openMode & BYTESTREAM_OPEN_READ)
modeString[modeStringLength++] = '+';
}
else if (openMode & BYTESTREAM_OPEN_READ)
{
modeString[modeStringLength++] = 'r';
modeString[modeStringLength++] = 'b';
}
modeString[modeStringLength] = 0;
if (openMode & BYTESTREAM_OPEN_CREATE_PATH)
{
u32 i;
const u32 fileNameLength = static_cast<u32>(std::strlen(fileName));
char* tempStr = (char*)alloca(fileNameLength + 1);
#if defined(_WIN32)
// check if it starts with a drive letter. if so, skip ahead
if (fileNameLength >= 2 && fileName[1] == ':')
{
if (fileNameLength <= 3)
{
// create a file called driveletter: or driveletter:\ ? you must be crazy
i = fileNameLength;
}
else
{
std::memcpy(tempStr, fileName, 3);
i = 3;
}
}
else
{
// start at beginning
i = 0;
}
#endif
// step through each path component, create folders as necessary
for (i = 0; i < fileNameLength; i++)
{
if (i > 0 && (fileName[i] == '\\' || fileName[i] == '/') && fileName[i] != ':')
{
// terminate the string
tempStr[i] = '\0';
// check if it exists
struct stat s;
if (stat(tempStr, &s) < 0)
{
if (errno == ENOENT)
{
// try creating it
#if defined(_WIN32)
if (mkdir(tempStr) < 0)
#else
if (mkdir(tempStr, 0777) < 0)
#endif
{
// no point trying any further down the chain
break;
}
}
else // if (errno == ENOTDIR)
{
// well.. someone's trying to open a fucking weird path that is comprised of both directories and
// files... I aint sticking around here to find out what disaster awaits... let fopen deal with it
break;
}
}
// append platform path seperator
#if defined(_WIN32)
tempStr[i] = '\\';
#else
tempStr[i] = '/';
#endif
}
else
{
// append character to temp string
tempStr[i] = fileName[i];
}
}
}
if (openMode & BYTESTREAM_OPEN_ATOMIC_UPDATE)
{
DebugAssert(openMode & (BYTESTREAM_OPEN_CREATE | BYTESTREAM_OPEN_WRITE));
// generate the temporary file name
@ -1256,7 +1060,7 @@ std::unique_ptr<ByteStream> ByteStream_OpenFileStream(const char* fileName, u32
#endif
// open the file
std::FILE* pTemporaryFile = std::fopen(temporaryFileName, modeString);
std::FILE* pTemporaryFile = FileSystem::OpenCFile(temporaryFileName, modeString);
if (pTemporaryFile == nullptr)
return nullptr;
@ -1267,7 +1071,7 @@ std::unique_ptr<ByteStream> ByteStream_OpenFileStream(const char* fileName, u32
// do we need to copy the existing file into this one?
if (!(openMode & BYTESTREAM_OPEN_TRUNCATE))
{
std::FILE* pOriginalFile = std::fopen(fileName, "rb");
std::FILE* pOriginalFile = FileSystem::OpenCFile(fileName, "rb");
if (!pOriginalFile)
{
// this will delete the temporary file
@ -1297,10 +1101,12 @@ std::unique_ptr<ByteStream> ByteStream_OpenFileStream(const char* fileName, u32
// return pointer
return pStream;
#endif
}
else
{
std::FILE* pFile = std::fopen(fileName, modeString);
// forward through
std::FILE* pFile = FileSystem::OpenCFile(fileName, modeString);
if (!pFile)
return nullptr;
@ -1308,36 +1114,34 @@ std::unique_ptr<ByteStream> ByteStream_OpenFileStream(const char* fileName, u32
}
}
#endif
std::unique_ptr<MemoryByteStream> ByteStream_CreateMemoryStream(void* pMemory, u32 Size)
std::unique_ptr<MemoryByteStream> ByteStream::CreateMemoryStream(void* pMemory, u32 Size)
{
DebugAssert(pMemory != nullptr && Size > 0);
return std::make_unique<MemoryByteStream>(pMemory, Size);
}
std::unique_ptr<ReadOnlyMemoryByteStream> ByteStream_CreateReadOnlyMemoryStream(const void* pMemory, u32 Size)
std::unique_ptr<ReadOnlyMemoryByteStream> ByteStream::CreateReadOnlyMemoryStream(const void* pMemory, u32 Size)
{
DebugAssert(pMemory != nullptr && Size > 0);
return std::make_unique<ReadOnlyMemoryByteStream>(pMemory, Size);
}
std::unique_ptr<NullByteStream> ByteStream_CreateNullStream()
std::unique_ptr<NullByteStream> ByteStream::CreateNullStream()
{
return std::make_unique<NullByteStream>();
}
std::unique_ptr<GrowableMemoryByteStream> ByteStream_CreateGrowableMemoryStream(void* pInitialMemory, u32 InitialSize)
std::unique_ptr<GrowableMemoryByteStream> ByteStream::CreateGrowableMemoryStream(void* pInitialMemory, u32 InitialSize)
{
return std::make_unique<GrowableMemoryByteStream>(pInitialMemory, InitialSize);
}
std::unique_ptr<GrowableMemoryByteStream> ByteStream_CreateGrowableMemoryStream()
std::unique_ptr<GrowableMemoryByteStream> ByteStream::CreateGrowableMemoryStream()
{
return std::make_unique<GrowableMemoryByteStream>(nullptr, 0);
}
bool ByteStream_CopyStream(ByteStream* pDestinationStream, ByteStream* pSourceStream)
bool ByteStream::CopyStream(ByteStream* pDestinationStream, ByteStream* pSourceStream)
{
const u32 chunkSize = 4096;
u8 chunkData[chunkSize];
@ -1363,7 +1167,7 @@ bool ByteStream_CopyStream(ByteStream* pDestinationStream, ByteStream* pSourceSt
return (pSourceStream->SeekAbsolute(oldSourcePosition) && success);
}
bool ByteStream_AppendStream(ByteStream* pSourceStream, ByteStream* pDestinationStream)
bool ByteStream::AppendStream(ByteStream* pSourceStream, ByteStream* pDestinationStream)
{
const u32 chunkSize = 4096;
u8 chunkData[chunkSize];
@ -1389,7 +1193,7 @@ bool ByteStream_AppendStream(ByteStream* pSourceStream, ByteStream* pDestination
return (pSourceStream->SeekAbsolute(oldSourcePosition) && success);
}
u32 ByteStream_CopyBytes(ByteStream* pSourceStream, u32 byteCount, ByteStream* pDestinationStream)
u32 ByteStream::CopyBytes(ByteStream* pSourceStream, u32 byteCount, ByteStream* pDestinationStream)
{
const u32 chunkSize = 4096;
u8 chunkData[chunkSize];
@ -1411,3 +1215,69 @@ u32 ByteStream_CopyBytes(ByteStream* pSourceStream, u32 byteCount, ByteStream* p
return byteCount - remaining;
}
std::string ByteStream::ReadStreamToString(ByteStream* stream, bool seek_to_start /* = true */)
{
u64 pos = stream->GetPosition();
u64 size = stream->GetSize();
if (pos > 0 && seek_to_start)
{
if (!stream->SeekAbsolute(0))
return {};
pos = 0;
}
Assert(size >= pos);
size -= pos;
if (size == 0 || size > std::numeric_limits<u32>::max())
return {};
std::string ret;
ret.resize(static_cast<size_t>(size));
if (!stream->Read2(ret.data(), static_cast<u32>(size)))
return {};
return ret;
}
bool ByteStream::WriteStreamToString(const std::string_view& sv, ByteStream* stream)
{
if (sv.size() > std::numeric_limits<u32>::max())
return false;
return stream->Write2(sv.data(), static_cast<u32>(sv.size()));
}
std::vector<u8> ByteStream::ReadBinaryStream(ByteStream* stream, bool seek_to_start /*= true*/)
{
u64 pos = stream->GetPosition();
u64 size = stream->GetSize();
if (pos > 0 && seek_to_start)
{
if (!stream->SeekAbsolute(0))
return {};
pos = 0;
}
Assert(size >= pos);
size -= pos;
if (size == 0 || size > std::numeric_limits<u32>::max())
return {};
std::vector<u8> ret;
ret.resize(static_cast<size_t>(size));
if (!stream->Read2(ret.data(), static_cast<u32>(size)))
return {};
return ret;
}
bool ByteStream::WriteBinaryToStream(ByteStream* stream, const void* data, size_t data_length)
{
if (data_length > std::numeric_limits<u32>::max())
return false;
return stream->Write2(data, static_cast<u32>(data_length));
}

View File

@ -1,6 +1,8 @@
#pragma once
#include "types.h"
#include <memory>
#include <string>
#include <vector>
// base byte stream creation functions
enum BYTESTREAM_OPEN_MODE
@ -10,12 +12,18 @@ enum BYTESTREAM_OPEN_MODE
BYTESTREAM_OPEN_APPEND = 4, // seek to the end
BYTESTREAM_OPEN_TRUNCATE = 8, // truncate the file, seek to start
BYTESTREAM_OPEN_CREATE = 16, // if the file does not exist, create it
BYTESTREAM_OPEN_CREATE_PATH = 32, // if the file parent directories don't exist, create them
BYTESTREAM_OPEN_ATOMIC_UPDATE = 64, //
BYTESTREAM_OPEN_SEEKABLE = 128,
BYTESTREAM_OPEN_STREAMED = 256,
};
// forward declarations for implemented classes
class ByteStream;
class MemoryByteStream;
class GrowableMemoryByteStream;
class ReadOnlyMemoryByteStream;
class NullByteStream;
// interface class used by readers, writers, etc.
class ByteStream
{
@ -68,6 +76,42 @@ public:
inline void SetErrorState() { m_errorState = true; }
inline void ClearErrorState() { m_errorState = false; }
// base byte stream creation functions
// opens a local file-based stream. fills in error if passed, and returns false if the file cannot be opened.
static std::unique_ptr<ByteStream> OpenFile(const char* FileName, u32 OpenMode);
// memory byte stream, caller is responsible for management, therefore it can be located on either the stack or on the
// heap.
static std::unique_ptr<MemoryByteStream> CreateMemoryStream(void* pMemory, u32 Size);
// a growable memory byte stream will automatically allocate its own memory if the provided memory is overflowed.
// a "pure heap" buffer, i.e. a buffer completely managed by this implementation, can be created by supplying a NULL
// pointer and initialSize of zero.
static std::unique_ptr<GrowableMemoryByteStream> CreateGrowableMemoryStream(void* pInitialMemory, u32 InitialSize);
static std::unique_ptr<GrowableMemoryByteStream> CreateGrowableMemoryStream();
// readable memory stream
static std::unique_ptr<ReadOnlyMemoryByteStream> CreateReadOnlyMemoryStream(const void* pMemory, u32 Size);
// null memory stream
static std::unique_ptr<NullByteStream> CreateNullStream();
// copies one stream's contents to another. rewinds source streams automatically, and returns it back to its old
// position.
static bool CopyStream(ByteStream* pDestinationStream, ByteStream* pSourceStream);
// appends one stream's contents to another.
static bool AppendStream(ByteStream* pSourceStream, ByteStream* pDestinationStream);
// copies a number of bytes from one to another
static u32 CopyBytes(ByteStream* pSourceStream, u32 byteCount, ByteStream* pDestinationStream);
static std::string ReadStreamToString(ByteStream* stream, bool seek_to_start = true);
static bool WriteStreamToString(const std::string_view& sv, ByteStream* stream);
static std::vector<u8> ReadBinaryStream(ByteStream* stream, bool seek_to_start = true);
static bool WriteBinaryToStream(ByteStream* stream, const void* data, size_t data_length);
protected:
ByteStream() : m_errorState(false) {}
@ -199,33 +243,3 @@ private:
u32 m_iSize;
u32 m_iMemorySize;
};
// base byte stream creation functions
// opens a local file-based stream. fills in error if passed, and returns false if the file cannot be opened.
std::unique_ptr<ByteStream> ByteStream_OpenFileStream(const char* FileName, u32 OpenMode);
// memory byte stream, caller is responsible for management, therefore it can be located on either the stack or on the
// heap.
std::unique_ptr<MemoryByteStream> ByteStream_CreateMemoryStream(void* pMemory, u32 Size);
// a growable memory byte stream will automatically allocate its own memory if the provided memory is overflowed.
// a "pure heap" buffer, i.e. a buffer completely managed by this implementation, can be created by supplying a NULL
// pointer and initialSize of zero.
std::unique_ptr<GrowableMemoryByteStream> ByteStream_CreateGrowableMemoryStream(void* pInitialMemory, u32 InitialSize);
std::unique_ptr<GrowableMemoryByteStream> ByteStream_CreateGrowableMemoryStream();
// readable memory stream
std::unique_ptr<ReadOnlyMemoryByteStream> ByteStream_CreateReadOnlyMemoryStream(const void* pMemory, u32 Size);
// null memory stream
std::unique_ptr<NullByteStream> ByteStream_CreateNullStream();
// copies one stream's contents to another. rewinds source streams automatically, and returns it back to its old
// position.
bool ByteStream_CopyStream(ByteStream* pDestinationStream, ByteStream* pSourceStream);
// appends one stream's contents to another.
bool ByteStream_AppendStream(ByteStream* pSourceStream, ByteStream* pDestinationStream);
// copies a number of bytes from one to another
u32 ByteStream_CopyBytes(ByteStream* pSourceStream, u32 byteCount, ByteStream* pDestinationStream);

View File

@ -844,18 +844,6 @@ std::string BuildRelativePath(const std::string_view& filename, const std::strin
return new_string;
}
std::unique_ptr<ByteStream> OpenFile(const char* FileName, u32 Flags)
{
// has a path
if (FileName[0] == '\0')
return nullptr;
// TODO: Handle Android content URIs here.
// forward to local file wrapper
return ByteStream_OpenFileStream(FileName, Flags);
}
FileSystem::ManagedCFilePtr OpenManagedCFile(const char* filename, const char* mode)
{
return ManagedCFilePtr(OpenCFile(filename, mode), [](std::FILE* fp) { std::fclose(fp); });
@ -1116,72 +1104,6 @@ bool WriteFileToString(const char* filename, const std::string_view& sv)
return true;
}
std::string ReadStreamToString(ByteStream* stream, bool seek_to_start /* = true */)
{
u64 pos = stream->GetPosition();
u64 size = stream->GetSize();
if (pos > 0 && seek_to_start)
{
if (!stream->SeekAbsolute(0))
return {};
pos = 0;
}
Assert(size >= pos);
size -= pos;
if (size == 0 || size > std::numeric_limits<u32>::max())
return {};
std::string ret;
ret.resize(static_cast<size_t>(size));
if (!stream->Read2(ret.data(), static_cast<u32>(size)))
return {};
return ret;
}
bool WriteStreamToString(const std::string_view& sv, ByteStream* stream)
{
if (sv.size() > std::numeric_limits<u32>::max())
return false;
return stream->Write2(sv.data(), static_cast<u32>(sv.size()));
}
std::vector<u8> ReadBinaryStream(ByteStream* stream, bool seek_to_start /*= true*/)
{
u64 pos = stream->GetPosition();
u64 size = stream->GetSize();
if (pos > 0 && seek_to_start)
{
if (!stream->SeekAbsolute(0))
return {};
pos = 0;
}
Assert(size >= pos);
size -= pos;
if (size == 0 || size > std::numeric_limits<u32>::max())
return {};
std::vector<u8> ret;
ret.resize(static_cast<size_t>(size));
if (!stream->Read2(ret.data(), static_cast<u32>(size)))
return {};
return ret;
}
bool WriteBinaryToSTream(ByteStream* stream, const void* data, size_t data_length)
{
if (data_length > std::numeric_limits<u32>::max())
return false;
return stream->Write2(data, static_cast<u32>(data_length));
}
void BuildOSPath(char* Destination, u32 cbDestination, const char* Path)
{
u32 i;

View File

@ -2,12 +2,12 @@
#include "timestamp.h"
#include "types.h"
#include <cstdio>
#include <ctime>
#include <memory>
#include <optional>
#include <string>
#include <vector>
class ByteStream;
#include <sys/stat.h>
#ifdef _WIN32
#define FS_OSPATH_SEPARATOR_CHARACTER '\\'
@ -185,9 +185,6 @@ bool DeleteFile(const char* Path);
// rename file
bool RenamePath(const char* OldPath, const char* NewPath);
// open files
std::unique_ptr<ByteStream> OpenFile(const char* FileName, u32 Flags);
using ManagedCFilePtr = std::unique_ptr<std::FILE, void (*)(std::FILE*)>;
ManagedCFilePtr OpenManagedCFile(const char* filename, const char* mode);
std::FILE* OpenCFile(const char* filename, const char* mode);
@ -201,12 +198,6 @@ std::optional<std::string> ReadFileToString(std::FILE* fp);
bool WriteBinaryFile(const char* filename, const void* data, size_t data_length);
bool WriteFileToString(const char* filename, const std::string_view& sv);
std::string ReadStreamToString(ByteStream* stream, bool seek_to_start = true);
bool WriteStreamToString(const std::string_view& sv, ByteStream* stream);
std::vector<u8> ReadBinaryStream(ByteStream* stream, bool seek_to_start = true);
bool WriteBinaryToSTream(ByteStream* stream, const void* data, size_t data_length);
// creates a directory in the local filesystem
// if the directory already exists, the return value will be true.
// if Recursive is specified, all parent directories will be created