Skip to content

Commit

Permalink
File: Split all String and Vector related functions out of FileDescri…
Browse files Browse the repository at this point in the history
…ptor

This allows dependent libraries to avoid needing Strings / Containers libraries
  • Loading branch information
Pagghiu committed Dec 31, 2024
1 parent 77d2166 commit a2f67be
Show file tree
Hide file tree
Showing 22 changed files with 495 additions and 404 deletions.
2 changes: 1 addition & 1 deletion Bindings/cpp/SC.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
#include "../../Libraries/AsyncStreams/AsyncStreams.cpp"
#include "../../Libraries/AsyncStreams/ZLibTransformStreams.cpp"
#include "../../Libraries/Build/Build.cpp"
#include "../../Libraries/File/FileDescriptor.cpp"
#include "../../Libraries/File/File.cpp"
#include "../../Libraries/FileSystem/FileSystem.cpp"
#include "../../Libraries/FileSystem/FileSystemDirectories.cpp"
#include "../../Libraries/FileSystem/Path.cpp"
Expand Down
7 changes: 5 additions & 2 deletions Documentation/Libraries/File.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,14 @@ File library allows synchronous I/O operations on files and pipes.
# Features
| SC::FileDescriptor | @copybrief SC::FileDescriptor |
|:----------------------------------|:----------------------------------------------|
| SC::FileDescriptor::open | @copybrief SC::FileDescriptor::open |
| SC::FileDescriptor::read | @copybrief SC::FileDescriptor::read |
| SC::FileDescriptor::write | @copybrief SC::FileDescriptor::write |
| SC::FileDescriptor::seek | @copybrief SC::FileDescriptor::seek |
| SC::FileDescriptor::readUntilEOF | @copybrief SC::FileDescriptor::readUntilEOF |

| SC::File | @copybrief SC::File |
|:----------------------------------|:----------------------------------------------|
| SC::File::open | @copybrief SC::File::open |
| SC::File::readUntilEOF | @copybrief SC::File::readUntilEOF |

| SC::PipeDescriptor | @copybrief SC::PipeDescriptor |
|:----------------------------------|:----------------------------------------------|
Expand Down
6 changes: 3 additions & 3 deletions Libraries/Async/Async.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@
#include "../Foundation/OpaqueObject.h"
#include "../Foundation/Span.h"
#include "../Threading/Atomic.h"
#include "../Threading/ThreadPool.h"
#include "../Time/Time.h"

// Descriptors
#include "../File/FileDescriptor.h"
#include "../Process/ProcessDescriptor.h"
#include "../Socket/SocketDescriptor.h"
#include "../Threading/ThreadPool.h"

//! @defgroup group_async Async
//! @copybrief library_async (see @ref library_async for more details)
Expand Down Expand Up @@ -701,7 +701,7 @@ struct AsyncSocketClose : public AsyncRequest
/// should instead avoid using the `Task` parameter for best performance.
///
/// When not using the `Task` remember to:
/// - Open the file descriptor for non-blocking IO (SC::FileDescriptor::OpenOptions::blocking == `false`)
/// - Open the file descriptor for non-blocking IO (SC::File::OpenOptions::blocking == `false`)
/// - Call SC::AsyncEventLoop::associateExternallyCreatedFileDescriptor on the file descriptor
///
/// Additional notes:
Expand Down Expand Up @@ -784,7 +784,7 @@ struct AsyncFileRead : public AsyncRequest
/// should instead avoid using the `Task` parameter for best performance.
///
/// When not using the `Task` remember to:
/// - Open the file descriptor for non-blocking IO (SC::FileDescriptor::OpenOptions::blocking == `false`)
/// - Open the file descriptor for non-blocking IO (SC::File::OpenOptions::blocking == `false`)
/// - Call SC::AsyncEventLoop::associateExternallyCreatedFileDescriptor on the file descriptor
///
/// \snippet Libraries/Async/Tests/AsyncTest.cpp AsyncFileWriteSnippet
Expand Down
7 changes: 3 additions & 4 deletions Libraries/Async/Internal/AsyncWindows.inl
Original file line number Diff line number Diff line change
Expand Up @@ -535,11 +535,10 @@ struct SC::AsyncEventLoop::Internal::KernelEvents
// In the sync case (threadpool) we try a regular sync call to support files opened with async == false.
if (not synchronous or func(fileDescriptor, async.buffer.data(), bufSize, &numBytes, nullptr) == FALSE)
{
// File must have FileDescriptor::OpenOptions::async == true +
// File must have File::OpenOptions::async == true +
// associateExternallyCreatedFileDescriptor
return Result::Error(
"ReadFile/WriteFile failed (forgot setting FileDescriptor::OpenOptions::async = true or "
"AsyncEventLoop::associateExternallyCreatedFileDescriptor?)");
return Result::Error("ReadFile/WriteFile failed (forgot setting File::OpenOptions::async = true or "
"AsyncEventLoop::associateExternallyCreatedFileDescriptor?)");
}
}
}
Expand Down
30 changes: 16 additions & 14 deletions Libraries/Async/Tests/AsyncTest.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) Stefano Cristiano
// SPDX-License-Identifier: MIT
#include "../Async.h"
#include "../../File/File.h"
#include "../../FileSystem/FileSystem.h"
#include "../../FileSystem/Path.h"
#include "../../Process/Process.h"
Expand Down Expand Up @@ -858,11 +859,12 @@ void SC::AsyncTest::fileReadWrite(bool useThreadPool)
SC_TEST_EXPECT(fs.makeDirectoryIfNotExists(name));

// 4. Open the destination file and associate it with the event loop
FileDescriptor::OpenOptions openOptions;
File::OpenOptions openOptions;
openOptions.blocking = useThreadPool;

FileDescriptor fd;
SC_TEST_EXPECT(fd.open(filePath.view(), FileDescriptor::WriteCreateTruncate, openOptions));
File file(fd);
SC_TEST_EXPECT(file.open(filePath.view(), File::WriteCreateTruncate, openOptions));
if (not useThreadPool)
{
SC_TEST_EXPECT(eventLoop.associateExternallyCreatedFileDescriptor(fd));
Expand Down Expand Up @@ -895,7 +897,7 @@ void SC::AsyncTest::fileReadWrite(bool useThreadPool)
SC_TEST_EXPECT(fd.close());

// 7. Open the file for read now
SC_TEST_EXPECT(fd.open(filePath.view(), FileDescriptor::ReadOnly, openOptions));
SC_TEST_EXPECT(file.open(filePath.view(), File::ReadOnly, openOptions));
if (not useThreadPool)
{
SC_TEST_EXPECT(eventLoop.associateExternallyCreatedFileDescriptor(fd));
Expand Down Expand Up @@ -986,12 +988,12 @@ void SC::AsyncTest::fileEndOfFile(bool useThreadPool)
SC_TEST_EXPECT(fs.write(fileName, {data, sizeof(data)}));
}

FileDescriptor::OpenOptions openOptions;
File::OpenOptions openOptions;
openOptions.blocking = useThreadPool;

FileDescriptor::Handle handle = FileDescriptor::Invalid;
FileDescriptor fd;
SC_TEST_EXPECT(fd.open(filePath.view(), FileDescriptor::ReadOnly, openOptions));
SC_TEST_EXPECT(File(fd).open(filePath.view(), File::ReadOnly, openOptions));
if (not useThreadPool)
{
SC_TEST_EXPECT(eventLoop.associateExternallyCreatedFileDescriptor(fd));
Expand Down Expand Up @@ -1071,11 +1073,11 @@ void SC::AsyncTest::fileClose()
SC_TEST_EXPECT(fs.makeDirectoryIfNotExists(name));
SC_TEST_EXPECT(fs.write(filePath.view(), "test"));

FileDescriptor::OpenOptions openOptions;
File::OpenOptions openOptions;
openOptions.blocking = false;

FileDescriptor fd;
SC_TEST_EXPECT(fd.open(filePath.view(), FileDescriptor::WriteCreateTruncate, openOptions));
SC_TEST_EXPECT(File(fd).open(filePath.view(), File::WriteCreateTruncate, openOptions));
SC_TEST_EXPECT(eventLoop.associateExternallyCreatedFileDescriptor(fd));

FileDescriptor::Handle handle = FileDescriptor::Invalid;
Expand All @@ -1089,7 +1091,7 @@ void SC::AsyncTest::fileClose()
SC_TEST_EXPECT(fs.removeFile(fileName));
SC_TEST_EXPECT(fs.changeDirectory(report.applicationRootDirectory));
SC_TEST_EXPECT(fs.removeEmptyDirectory(name));
// fd.close() will fail as the file was already closed but it also throws a Win32 exception that will
// file.close() will fail as the file was already closed but it also throws a Win32 exception that will
// stop the debugger by default. Opting for a .detach()
// SC_TEST_EXPECT(not fd.close());
fd.detach();
Expand Down Expand Up @@ -1370,9 +1372,9 @@ SC_TRY(threadPool.create(4));

// Open the file
FileDescriptor fd;
FileDescriptor::OpenOptions options;
File::OpenOptions options;
options.blocking = true; // AsyncFileRead::Task enables using regular blocking file descriptors
SC_TRY(fd.open("MyFile.txt", FileDescriptor::ReadOnly, options));
SC_TRY(File(fd).open("MyFile.txt", File::ReadOnly, options));

// Create the async file read request and async task
AsyncFileRead asyncReadFile;
Expand Down Expand Up @@ -1437,10 +1439,10 @@ SC_TRY(threadPool.create(4));
// ...

// Open the file (for write)
FileDescriptor::OpenOptions options;
File::OpenOptions options;
options.blocking = true; // AsyncFileWrite::Task enables using regular blocking file descriptors
FileDescriptor fd;
SC_TRY(fd.open("MyFile.txt", FileDescriptor::WriteCreateTruncate, options));
SC_TRY(File(fd).open("MyFile.txt", File::WriteCreateTruncate, options));

// Create the async file write request
AsyncFileWrite asyncWriteFile;
Expand Down Expand Up @@ -1478,9 +1480,9 @@ SC::Result snippetForFileClose(AsyncEventLoop& eventLoop, Console& console)

// Open a file and associated it with event loop
FileDescriptor fd;
FileDescriptor::OpenOptions options;
File::OpenOptions options;
options.blocking = false;
SC_TRY(fd.open("MyFile.txt", FileDescriptor::WriteCreateTruncate, options));
SC_TRY(File(fd).open("MyFile.txt", File::WriteCreateTruncate, options));
SC_TRY(eventLoop.associateExternallyCreatedFileDescriptor(fd));

// Create the file close request
Expand Down
71 changes: 40 additions & 31 deletions Libraries/AsyncStreams/AsyncRequestStreams.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright (c) Stefano Cristiano
// SPDX-License-Identifier: MIT
#include "AsyncRequestStreams.h"

#include "../Foundation/Assert.h"
//-------------------------------------------------------------------------------------------------------
// AsyncRequestReadableStream
//-------------------------------------------------------------------------------------------------------
Expand All @@ -22,6 +22,15 @@ struct SC::AsyncRequestReadableStream<AsyncReadRequest>::Internal
{
return detail::SocketDescriptorDefinition::releaseHandle(async.handle);
}

template <typename DescriptorType>
static Result init(AsyncRequestReadableStream& self, AsyncBuffersPool& buffersPool, Span<Request> requests,
AsyncEventLoop& loop, const DescriptorType& descriptor)
{
self.request.cacheInternalEventLoop(loop);
SC_TRY(descriptor.get(Internal::getDescriptor(self.request), Result::Error("Missing descriptor")));
return self.AsyncReadableStream::init(buffersPool, requests);
}
};

template <typename AsyncReadRequest>
Expand All @@ -30,17 +39,6 @@ SC::AsyncRequestReadableStream<AsyncReadRequest>::AsyncRequestReadableStream()
AsyncReadableStream::asyncRead.bind<AsyncRequestReadableStream, &AsyncRequestReadableStream::read>(*this);
}

template <typename AsyncReadRequest>
template <typename DescriptorType>
SC::Result SC::AsyncRequestReadableStream<AsyncReadRequest>::init(AsyncBuffersPool& buffersPool, Span<Request> requests,
AsyncEventLoop& loop,
const DescriptorType& descriptor)
{
request.cacheInternalEventLoop(loop);
SC_TRY(descriptor.get(Internal::getDescriptor(request), Result::Error("Missing descriptor")));
return AsyncReadableStream::init(buffersPool, requests);
}

template <typename AsyncReadRequest>
void SC::AsyncRequestReadableStream<AsyncReadRequest>::onEndCloseDescriptor()
{
Expand Down Expand Up @@ -137,6 +135,15 @@ struct SC::AsyncRequestWritableStream<AsyncWriteRequest>::Internal
{
return detail::SocketDescriptorDefinition::releaseHandle(async.handle);
}

template <typename DescriptorType>
static Result init(AsyncRequestWritableStream& self, AsyncBuffersPool& buffersPool, Span<Request> requests,
AsyncEventLoop& loop, const DescriptorType& descriptor)
{
self.request.cacheInternalEventLoop(loop);
SC_TRY(descriptor.get(Internal::getDescriptor(self.request), Result::Error("Missing descriptor")));
return self.AsyncWritableStream::init(buffersPool, requests);
}
};

template <typename AsyncWriteRequest>
Expand All @@ -145,17 +152,6 @@ SC::AsyncRequestWritableStream<AsyncWriteRequest>::AsyncRequestWritableStream()
AsyncWritableStream::asyncWrite.bind<AsyncRequestWritableStream, &AsyncRequestWritableStream::write>(*this);
}

template <typename AsyncWriteRequest>
template <typename DescriptorType>
SC::Result SC::AsyncRequestWritableStream<AsyncWriteRequest>::init(AsyncBuffersPool& buffersPool,
Span<Request> requests, AsyncEventLoop& loop,
const DescriptorType& descriptor)
{
request.cacheInternalEventLoop(loop);
SC_TRY(descriptor.get(Internal::getDescriptor(request), Result::Error("Missing descriptor")));
return AsyncWritableStream::init(buffersPool, requests);
}

template <typename AsyncWriteRequest>
void SC::AsyncRequestWritableStream<AsyncWriteRequest>::onEndCloseDescriptor()
{
Expand Down Expand Up @@ -201,20 +197,33 @@ SC::Result SC::AsyncRequestWritableStream<AsyncWriteRequest>::write(AsyncBufferV
}
return res;
}

namespace SC
{
SC_COMPILER_EXTERN template struct AsyncRequestReadableStream<AsyncSocketReceive>;
SC_COMPILER_EXTERN template struct AsyncRequestReadableStream<AsyncFileRead>;
SC_COMPILER_EXTERN template struct AsyncRequestWritableStream<AsyncFileWrite>;
SC_COMPILER_EXTERN template struct AsyncRequestWritableStream<AsyncSocketSend>;

SC_COMPILER_EXTERN template SC::Result SC::AsyncRequestReadableStream<AsyncSocketReceive>::init(
AsyncBuffersPool& buffersPool, Span<Request> requests, AsyncEventLoop& loop, const SocketDescriptor& descriptor);
SC_COMPILER_EXTERN template SC::Result SC::AsyncRequestWritableStream<AsyncSocketSend>::init(
AsyncBuffersPool& buffersPool, Span<Request> requests, AsyncEventLoop& loop, const SocketDescriptor& descriptor);
SC_COMPILER_EXTERN template SC::Result SC::AsyncRequestReadableStream<AsyncFileRead>::init(
AsyncBuffersPool& buffersPool, Span<Request> requests, AsyncEventLoop& loop, const FileDescriptor& descriptor);
SC_COMPILER_EXTERN template SC::Result SC::AsyncRequestWritableStream<AsyncFileWrite>::init(
AsyncBuffersPool& buffersPool, Span<Request> requests, AsyncEventLoop& loop, const FileDescriptor& descriptor);
Result ReadableSocketStream::init(AsyncBuffersPool& buffersPool, Span<Request> requests, AsyncEventLoop& loop,
const SocketDescriptor& descriptor)
{
return Internal::init(*this, buffersPool, requests, loop, descriptor);
}
Result WritableSocketStream::init(AsyncBuffersPool& buffersPool, Span<Request> requests, AsyncEventLoop& loop,
const SocketDescriptor& descriptor)
{
return Internal::init(*this, buffersPool, requests, loop, descriptor);
}
Result ReadableFileStream::init(AsyncBuffersPool& buffersPool, Span<Request> requests, AsyncEventLoop& loop,
const FileDescriptor& descriptor)
{
return Internal::init(*this, buffersPool, requests, loop, descriptor);
}
Result WritableFileStream::init(AsyncBuffersPool& buffersPool, Span<Request> requests, AsyncEventLoop& loop,
const FileDescriptor& descriptor)
{
return Internal::init(*this, buffersPool, requests, loop, descriptor);
}

} // namespace SC
33 changes: 23 additions & 10 deletions Libraries/AsyncStreams/AsyncRequestStreams.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,6 @@ struct AsyncRequestReadableStream : public AsyncReadableStream
{
AsyncRequestReadableStream();

template <typename DescriptorType>
Result init(AsyncBuffersPool& buffersPool, Span<Request> requests, AsyncEventLoop& loop,
const DescriptorType& descriptor);

/// @brief Registers or unregisters a listener to AsyncReadableStream::eventEnd to close descriptor
Result registerAutoCloseDescriptor(bool value);

Expand Down Expand Up @@ -55,16 +51,33 @@ struct AsyncRequestWritableStream : public AsyncWritableStream
void onEndCloseDescriptor();
};

// clang-format off
/// @brief Uses an SC::AsyncFileRead to stream data from a file
struct ReadableFileStream : public AsyncRequestReadableStream<AsyncFileRead>{};
struct ReadableFileStream : public AsyncRequestReadableStream<AsyncFileRead>
{
Result init(AsyncBuffersPool& buffersPool, Span<Request> requests, AsyncEventLoop& loop,
const FileDescriptor& descriptor);
};

/// @brief Uses an SC::AsyncFileWrite to stream data to a file
struct WritableFileStream : public AsyncRequestWritableStream<AsyncFileWrite>{};
struct WritableFileStream : public AsyncRequestWritableStream<AsyncFileWrite>
{
Result init(AsyncBuffersPool& buffersPool, Span<Request> requests, AsyncEventLoop& loop,
const FileDescriptor& descriptor);
};

/// @brief Uses an SC::AsyncFileWrite to stream data from a socket
struct ReadableSocketStream : public AsyncRequestReadableStream<AsyncSocketReceive>{};
struct ReadableSocketStream : public AsyncRequestReadableStream<AsyncSocketReceive>
{
Result init(AsyncBuffersPool& buffersPool, Span<Request> requests, AsyncEventLoop& loop,
const SocketDescriptor& descriptor);
};

/// @brief Uses an SC::AsyncFileWrite to stream data to a socket
struct WritableSocketStream : public AsyncRequestWritableStream<AsyncSocketSend>{};
// clang-format on
struct WritableSocketStream : public AsyncRequestWritableStream<AsyncSocketSend>
{
Result init(AsyncBuffersPool& buffersPool, Span<Request> requests, AsyncEventLoop& loop,
const SocketDescriptor& descriptor);
};

} // namespace SC
//! @}
Loading

0 comments on commit a2f67be

Please sign in to comment.