diff options
-rw-r--r-- | include/cru/common/io/FileStream.h | 18 | ||||
-rw-r--r-- | include/cru/common/platform/web/WebFileStream.h | 1 | ||||
-rw-r--r-- | src/common/platform/web/WebFileStream.cpp | 100 | ||||
-rw-r--r-- | test/common/CMakeLists.txt | 2 | ||||
-rw-r--r-- | test/platform/CMakeLists.txt | 2 |
5 files changed, 110 insertions, 13 deletions
diff --git a/include/cru/common/io/FileStream.h b/include/cru/common/io/FileStream.h index 333b6d64..9176739a 100644 --- a/include/cru/common/io/FileStream.h +++ b/include/cru/common/io/FileStream.h @@ -2,7 +2,7 @@ * Here are some notes about FileStream: * * 1. FileStream is currently implemented as a typedef of the corresponding - * specific XxxFileStream class implemented on each platform and controled with + * specific XxxFileStream class implemented on each platform and controlled with * preprocessor commands. There might be some other ways like proxy pattern but * I do this way for simplicity. So your duty to implement a new platform is to * define a new class and ensure it implements all the required interface. And @@ -11,15 +11,8 @@ * * 2. Since each platform defines their own way to open a file, especially the * flags to open a file, we have to define a common interface. I decide to - * mimic the C++ std stream open flags - * (https://en.cppreference.com/w/cpp/io/basic_filebuf/open) so on platforms - * where there is no direct support on certain flags we try our best to - * simulate it and make a note for users. The difference between cru design and - * std design is: - * 1. cru deletes `binary` flag because Stream does not have a so-called - * _text_ mode and it is always byte based. - * 2. cru adds `OpenFileFlags::Create` and `OpenFileFlags::Exclusive` flags - * mimicking Linux behavior which does not exist in std. + * mimic Linux flags so on platforms where there is no direct support on certain + * flags we try our best to simulate it and make a note for users. * * (TODO: Currently the problem is that when I implemented for Windows and UNIX * I didn't take this into consideration so I have to fix this inconsistency @@ -40,4 +33,9 @@ using FileStream = platform::unix::UnixFileStream; namespace cru::io { using FileStream = platform::win::Win32FileStream; } +#elif CRU_PLATFORM_EMSCRIPTEN +#include "../platform/web/WebFileStream.h" +namespace cru::io { +using FileStream = platform::web::WebFileStream; +} #endif diff --git a/include/cru/common/platform/web/WebFileStream.h b/include/cru/common/platform/web/WebFileStream.h index 171df495..76f3fa84 100644 --- a/include/cru/common/platform/web/WebFileStream.h +++ b/include/cru/common/platform/web/WebFileStream.h @@ -1,6 +1,5 @@ #pragma once -#include <_stdio.h> #include "../../PreConfig.h" #ifdef CRU_PLATFORM_EMSCRIPTEN diff --git a/src/common/platform/web/WebFileStream.cpp b/src/common/platform/web/WebFileStream.cpp index dd44acdf..036be9a6 100644 --- a/src/common/platform/web/WebFileStream.cpp +++ b/src/common/platform/web/WebFileStream.cpp @@ -1,11 +1,111 @@ #include "cru/common/platform/web/WebFileStream.h" #include "cru/common/io/OpenFileFlag.h" +#include "cru/common/platform/web/WebException.h" #include <cstdio> +#include <stdexcept> namespace cru::platform::web { WebFileStream::WebFileStream(String path, io::OpenFileFlag flags) : path_(std::move(path)), flags_(flags) { + // 1. Check all invalid flag combinations. + + using io::OpenFileFlags; + + if (!(flags & OpenFileFlags::Read) && !(flags & OpenFileFlags::Write)) { + throw std::invalid_argument( + "At least one of Read and Write flag must be specified when opening a " + "file."); + } + + if (!(flags & OpenFileFlags::Write) && + (flags & OpenFileFlags::Truncate || flags & OpenFileFlags::Append)) { + throw std::invalid_argument( + "Truncate and Append flag can only be used when opening a file with " + "Write flag."); + } + + if (flags & OpenFileFlags::Truncate && flags & OpenFileFlags::Append) { + throw std::invalid_argument( + "Truncate and Append flag can not be used together."); + } + + if (!(flags & OpenFileFlags::Create) && flags & OpenFileFlags::Exclusive) { + // Although linux doc says that this is an undefined behavior, but let's + // make it an error so you won't get some unexpected flags without noticing. + throw std::invalid_argument("Exclusive flag must be set with Create flag."); + } + + // 2. Handle with OpenFileFlags::Create and OpenFileFlags::Exclusive. + const auto utf8_path = path.ToUtf8(); + + if (!(flags & OpenFileFlags::Create)) { + auto file = std::fopen(utf8_path.c_str(), "r"); + if (file == NULL) { + // Note: Is there any other possible reason why fopen fails? + throw WebException(u"File does not exist."); + } else { + if (!(flags & OpenFileFlags::Write)) { + // We are not going to write the file, so we can just use this handle. + file_ = file; + return; + } else { + // We are going to write the file, so we have to close this handle and + // create a new one. + std::fclose(file); + } + } + } else { + if (flags & OpenFileFlags::Exclusive) { + auto file = std::fopen(utf8_path.c_str(), "r"); + if (file != NULL) { + std::fclose(file); + throw WebException(u"File already exists."); + } + } + } + + // 3. Now everything is ok, we can open the file with correct flags. + // There is only one edge case for C fopen: if you open a file read-only and + // specify Create flag, fopen won't create the file for you. + + if (flags & OpenFileFlags::Write) { + if (flags & OpenFileFlags::Read) { + if (flags & OpenFileFlags::Append) { + file_ = std::fopen(utf8_path.c_str(), "a+"); + } else { + file_ = std::fopen(utf8_path.c_str(), "w+"); + } + } else { + if (flags & OpenFileFlags::Append) { + file_ = std::fopen(utf8_path.c_str(), "a"); + } else { + file_ = std::fopen(utf8_path.c_str(), "w"); + } + } + } else { + // Open file read-only. + auto file = std::fopen(utf8_path.c_str(), "r"); + if (file == NULL) { + file = std::fopen(utf8_path.c_str(), "w"); + if (file == NULL) { + throw WebException(u"Failed to create file."); + } + std::fclose(file); + // Open it again. + file = std::fopen(utf8_path.c_str(), "r"); + } + file_ = file; + } + + if (file_ == NULL) { + throw WebException(u"Failed to open file."); + } } +WebFileStream::~WebFileStream() { + if (file_ != NULL) { + std::fclose(file_); + } +} } // namespace cru::platform::web diff --git a/test/common/CMakeLists.txt b/test/common/CMakeLists.txt index b3436105..85049357 100644 --- a/test/common/CMakeLists.txt +++ b/test/common/CMakeLists.txt @@ -7,7 +7,7 @@ add_executable(CruBaseTest ) target_link_libraries(CruBaseTest PRIVATE CruBase CruTestBase) -if (UNIX) +if (UNIX AND NOT EMSCRIPTEN) target_sources(CruBaseTest PRIVATE platform/unix/UnixFileStreamTest.cpp ) diff --git a/test/platform/CMakeLists.txt b/test/platform/CMakeLists.txt index 01f6966b..6a26214b 100644 --- a/test/platform/CMakeLists.txt +++ b/test/platform/CMakeLists.txt @@ -8,7 +8,7 @@ if (WIN32) add_subdirectory(graphics/direct2d) endif() -if (UNIX) +if (UNIX AND NOT EMSCRIPTEN) add_subdirectory(graphics/cairo) endif() |