aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/cru/common/io/FileStream.h18
-rw-r--r--include/cru/common/platform/web/WebFileStream.h1
-rw-r--r--src/common/platform/web/WebFileStream.cpp100
-rw-r--r--test/common/CMakeLists.txt2
-rw-r--r--test/platform/CMakeLists.txt2
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()