From 0e203294c1e1cfbbcc3f3ece729f2a58126937cb Mon Sep 17 00:00:00 2001 From: Jakub Skowron Date: Wed, 17 Jan 2018 13:46:16 +0100 Subject: [PATCH] [Filesystem] Write file performace improvement (binary data through UTF-8) Improve performance of write, writeBytes, writeBase64 by pushing binary data through UTF-8 null-terminated string. Byte 0x00 is encoded as U+0100. Removed class FilesystemFile. [Verification] TCT pass rate is 100% tct-filesystem-tizen-tests Change-Id: Iee5983a429ed484eb0fd6a6d35ea794d292de8de Signed-off-by: Jakub Skowron --- src/filesystem/filesystem.gyp | 2 - src/filesystem/filesystem_file.cc | 290 ---------------------------------- src/filesystem/filesystem_file.h | 54 ------- src/filesystem/filesystem_instance.cc | 247 ++++++++++++++++++++++------- src/filesystem/filesystem_instance.h | 5 +- src/filesystem/filesystem_manager.cc | 29 ---- src/filesystem/filesystem_manager.h | 4 - src/filesystem/js/file_stream.js | 72 ++++----- 8 files changed, 229 insertions(+), 474 deletions(-) delete mode 100644 src/filesystem/filesystem_file.cc delete mode 100644 src/filesystem/filesystem_file.h diff --git a/src/filesystem/filesystem.gyp b/src/filesystem/filesystem.gyp index 9a3c737..6f3f46a 100644 --- a/src/filesystem/filesystem.gyp +++ b/src/filesystem/filesystem.gyp @@ -19,8 +19,6 @@ 'filesystem_api.js', 'filesystem_extension.cc', 'filesystem_extension.h', - 'filesystem_file.cc', - 'filesystem_file.h', 'filesystem_instance.cc', 'filesystem_instance.h', 'filesystem_manager.cc', diff --git a/src/filesystem/filesystem_file.cc b/src/filesystem/filesystem_file.cc deleted file mode 100644 index 1174747..0000000 --- a/src/filesystem/filesystem_file.cc +++ /dev/null @@ -1,290 +0,0 @@ -/* - * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "filesystem_file.h" -#include -#include -#include -#include - -namespace extension { -namespace filesystem { - -namespace { -uint8_t characterToNumber(char c) { - if (c == '+') { - return 62; - } - if (c == '/') { - return 63; - } - if (c <= '9') { - return c + 0x04; - } - if (c <= 'Z') { - return c - 0x41; - } - if (c <= 'z') { - return c - 0x47; - } - return 0; -} - -char numberToCharacter(uint8_t i) { - if (i <= 25) { - return 'A' + i; - } - if (i <= 51) { - return 'a' + i - 26; - } - if (i <= 61) { - return '0' + i - 52; - } - if (i == 62) { - return '+'; - } - if (i == 63) { - return '/'; - } - return 0; -} - -bool validateCharacter(char c) { - if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c == '=') || - (c == '+') || (c == '/')) { - return true; - } - return false; -} -} // namespace - -/** - * Data is encoded using Base64 encoding. - */ - -bool FilesystemBuffer::DecodeData(const std::string& data) { - ScopeLogger(); - if (data.length() % 4) { - LoggerE("Buffer has invalid length"); - return false; - } - - for (auto c : data) { - if (!validateCharacter(c)) { - LoggerE("Buffer has invalid character"); - return false; - } - } - - // Validate padding - for (size_t i = 0; i + 2 < data.length(); ++i) { - if (data[i] == '=') { - LoggerE("Unexpected padding character in string"); - return false; - } - } - - if (data[data.length() - 2] == '=' && data[data.length() - 1] != '=') { - LoggerE("Unexpected padding character in string"); - return false; - } - - clear(); - - if (data.length() == 0) { - return true; - } - - int padding = 0; - if (data[data.length() - 1] == '=') { - padding++; - } - - if (data[data.length() - 2] == '=') { - padding++; - } - - for (size_t i = 0; i < data.length(); i += 4) { - uint8_t part[] = {characterToNumber(data[i + 0]), characterToNumber(data[i + 1]), - characterToNumber(data[i + 2]), characterToNumber(data[i + 3])}; - push_back(uint8_t((part[0] << 2) | (part[1] >> 4))); - if ((data.length() - i != 4) || (padding < 2)) { - push_back(uint8_t((part[1] << 4) | (part[2] >> 2))); - } - if ((data.length() - i != 4) || (padding < 1)) { - push_back(uint8_t((part[2] << 6) | (part[3]))); - } - } - return true; -} - -std::string FilesystemBuffer::EncodeData() const { - ScopeLogger(); - std::string out; - - for (size_t i = 0; i < size(); i += 3) { - uint8_t part[] = {safe_get(i), safe_get(i + 1), safe_get(i + 2)}; - out.push_back(numberToCharacter(0x3F & (part[0] >> 2))); - out.push_back(numberToCharacter(0x3F & ((part[0] << 4) | (part[1] >> 4)))); - out.push_back(numberToCharacter(0x3F & ((part[1] << 2) | (part[2] >> 6)))); - out.push_back(numberToCharacter(0x3F & (part[2]))); - } - - if (out.size() == 0) return out; - - // Add padding - int fillup = (size() % 3); - if (fillup == 1) { - out[out.size() - 2] = '='; - } - - if (fillup == 1 || fillup == 2) { - out[out.size() - 1] = '='; - } - - return out; -} - -FilesystemFile::FilesystemFile(const std::string& path_) : path(path_) { - ScopeLogger(); -} - -bool FilesystemFile::Write(const FilesystemBuffer& data, size_t offset, bool rewrite) { - ScopeLogger(); - - FILE* file = nullptr; - if (rewrite) { - file = fopen(path.c_str(), "w"); - } else { - file = fopen(path.c_str(), "r+"); - } - - if (!file) { - LoggerE("Cannot open file %s to write!", path.c_str()); - return false; - } - - SCOPE_EXIT { - int status = fclose(file); - if (status) { - LoggerE("Cannot close file!"); - } - }; - - int status; - status = fseek(file, offset, SEEK_SET); - LoggerD("Offset is %u, writing %i bytes", offset, data.size()); - if (status) { - LoggerE("Cannot perform seek!"); - return false; - } - - size_t written = 0; - uint8_t* data_p = const_cast(data.data()); - size_t data_size = data.size(); - while (written < data.size()) { - size_t part = fwrite(data_p, 1, data_size, file); - - if (ferror(file)) { - LoggerE("Error during file write!"); - return false; - } - - written += part; - data_p += part; - data_size -= part; - } - - status = fflush(file); - if (status) { - LoggerE("Cannot flush file!"); - return false; - } - - status = fsync(fileno(file)); - if (status) { - LoggerE("Cannot sync file!"); - return false; - } - LoggerD("Written %u bytes", written); - - return true; -} - -bool FilesystemFile::WriteString(const std::string& data, size_t offset, bool rewrite) { - ScopeLogger(); - - FILE* file = nullptr; - if (rewrite) { - file = fopen(path.c_str(), "w"); - } else { - file = fopen(path.c_str(), "r+"); - } - - if (!file) { - LoggerE("Cannot open file %s to write!", path.c_str()); - return false; - } - - SCOPE_EXIT { - int status = fclose(file); - if (status) { - LoggerE("Cannot close file!"); - } - }; - - int status; - status = fseek(file, offset, SEEK_SET); - LoggerD("Offset is %u, writing %i bytes", offset, data.size()); - if (status) { - LoggerE("Cannot perform seek!"); - return false; - } - - size_t written = 0; - uint8_t* data_p = (uint8_t*)(data.c_str()); - size_t data_size = data.size(); - - while (written < data.size()) { - size_t part = fwrite(data_p, 1, data_size, file); - - if (ferror(file)) { - LoggerE("Error during file write!"); - return false; - } - - written += part; - data_p += part; - data_size -= part; - } - - status = fflush(file); - if (status) { - LoggerE("Cannot flush file!"); - return false; - } - - status = fsync(fileno(file)); - if (status) { - LoggerE("Cannot sync file!"); - return false; - } - LoggerD("Written %u bytes", written); - - return true; -} -} // namespace filesystem -} // namespace extension diff --git a/src/filesystem/filesystem_file.h b/src/filesystem/filesystem_file.h deleted file mode 100644 index e8dce33..0000000 --- a/src/filesystem/filesystem_file.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef FILESYSTEM_FILESYSTEM_FILE_H -#define FILESYSTEM_FILESYSTEM_FILE_H - -#include -#include -#include - -namespace extension { -namespace filesystem { - -class FilesystemBuffer : public std::vector { - public: - bool DecodeData(const std::string& data); - std::string EncodeData() const; - - private: - inline uint8_t safe_get(size_t i) const { - if (i >= size()) { - return 0; - } - return at(i); - } -}; - -class FilesystemFile { - const std::string path; - - public: - FilesystemFile(const std::string& path_); - - bool Write(const FilesystemBuffer& data, size_t offset, bool rewrite); - bool WriteString(const std::string& data, size_t offset, bool rewrite); -}; - -} // namespace filesystem -} // namespace extension - -#endif // FILESYSTEM_FILESYSTEM_FILE_H diff --git a/src/filesystem/filesystem_instance.cc b/src/filesystem/filesystem_instance.cc index 848c6dc..eaceadc 100644 --- a/src/filesystem/filesystem_instance.cc +++ b/src/filesystem/filesystem_instance.cc @@ -54,8 +54,9 @@ FilesystemInstance::FilesystemInstance() { REGISTER_SYNC("File_rename", FileRename); REGISTER_SYNC("File_readBytes", FileReadBytes); REGISTER_SYNC("File_readString", FileReadString); - REGISTER_SYNC("File_write", FileWrite); - REGISTER_SYNC("File_writeSync", FileWriteSync); + REGISTER_SYNC("File_writeBytes", FileWriteBytes); + REGISTER_SYNC("File_writeBase64", FileWriteBase64); + REGISTER_SYNC("File_writeString", FileWriteString); REGISTER_SYNC("Filesystem_fetchVirtualRoots", FilesystemFetchVirtualRoots); REGISTER_SYNC("FileSystemManager_addStorageStateChangeListener", StartListening); REGISTER_SYNC("FileSystemManager_removeStorageStateChangeListener", StopListening); @@ -136,6 +137,44 @@ void FilesystemInstance::FileRename(const picojson::value& args, picojson::objec std::bind(&FilesystemManager::Rename, &fsm, oldPath, newPath, onSuccess, onError)); } +/* str should be a reference to (possibly empty) std::string */ +static void encode_binary_in_string(const std::vector& buf, std::string& str) { + ScopeLogger(); + str.reserve(str.size() + buf.size()); + + for (std::uint8_t byte : buf) { + if (byte >= 128) { + str += 0xC0 | (byte >> 6); + str += 0x80 | (byte & 0x3F); + } else if (byte > 0) { + str += byte; + } else { + // zero as codepoint U+0100 + str += 0xC4; + str += 0x80; + } + } +} + +/* buf should be a reference to (possibly empty) vector */ +static void decode_binary_from_string(const std::string& str, std::vector& buf) { + ScopeLogger(); + buf.reserve(buf.size() + str.size()); + + const std::uint8_t* it = (std::uint8_t*)str.data(); + const std::uint8_t* end = it + str.size(); + while (it != end) { + if (*it < 128) { + buf.push_back(*it++); + continue; + } + auto b1 = *it++; + auto b2 = *it++; + unsigned int x = ((b1 & 0x1F) << 6) | (b2 & 0x3F); + buf.push_back(x != 0x100 ? x : 0); + } +} + static constexpr std::size_t NPOS = (std::size_t)(-1); /** * Returns a buffer. If length is NPOS, then it reads whole file, up to the end. @@ -188,24 +227,109 @@ static std::vector read_file(std::string path, std::size_t offset, return out_buf; } -/* str should be reference to (possibly empty) std::string */ -static void encode_binary_in_string(const std::vector& buf, std::string& str) { +/** + * On failure throws std::runtime_error + */ +void write_file(const std::uint8_t* data, std::size_t len, std::string path, std::size_t offset, + bool rewrite) { ScopeLogger(); - str.reserve(str.size() + buf.size()); - for (std::uint8_t byte : buf) { - if (byte >= 128) { - str += 0xC0 | (byte >> 6); - str += 0x80 | (byte & 0x3F); - } else if (byte > 0) { - str += byte; - } else { - // zero as codepoint U+0100 - str += 0xC4; - str += 0x80; + FILE* file = fopen(path.c_str(), rewrite ? "w" : "r+"); + + if (!file) { + throw std::runtime_error("cannot open file to write"); + } + + SCOPE_EXIT { + int status = std::fclose(file); + if (status) { + LoggerE("Cannot close file!"); } + }; + + if (std::fseek(file, offset, SEEK_SET) != 0) { + throw std::runtime_error("cannot perform seek"); + } + + const std::uint8_t* data_p = data; + const std::uint8_t* end_p = data + len; + while (data_p != end_p) { + data_p += fwrite(data_p, 1, end_p - data_p, file); + + if (std::ferror(file)) { + throw std::runtime_error("error during file write"); + } + } + + if (std::fflush(file)) { + throw std::runtime_error("error during file write"); + } +} + +namespace base64 { +static const char int_to_char[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; +static const std::uint8_t INV = 255; +static const std::uint8_t PAD = 254; +static const std::uint8_t char_to_int[] = { + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 62, 255, 255, 255, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, + 61, 255, 255, 255, 254, 255, 255, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, + 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 255, 255, 255, 255, + 255, 255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, + 43, 44, 45, 46, 47, 48, 49, 50, 51, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255}; + +/** + * On error throws std::runtime_error + */ +static std::vector decode(const char* str, std::size_t len) { + std::vector out; + if (len % 4 != 0) throw std::runtime_error("string length is not multiple of 4"); + if (len == 0) return out; + + const std::uint8_t* p = (std::uint8_t*)str; + const std::uint8_t* end = p + len - 4; // last four can have padding in it + std::uint8_t b1, b2, b3, b4; + out.reserve(len / 4 * 3); + while (p != end) { + b1 = char_to_int[*p++]; + b2 = char_to_int[*p++]; + b3 = char_to_int[*p++]; + b4 = char_to_int[*p++]; + + if (b1 > 63 || b2 > 63 || b3 > 63 || b4 > 63) throw std::runtime_error("invalid character"); + + out.push_back((b1 << 2) | (b2 >> 4)); + out.push_back((b2 << 4) | (b3 >> 2)); + out.push_back((b3 << 6) | b4); } + b1 = char_to_int[*p++]; + b2 = char_to_int[*p++]; + b3 = char_to_int[*p++]; + b4 = char_to_int[*p++]; + + if (b1 == PAD || b1 == INV || b2 == PAD || b2 == INV || b3 == INV || b4 == INV) + throw std::runtime_error("invalid character"); + + out.push_back((b1 << 2) | (b2 >> 4)); + if (b3 == PAD) { + if (b4 != PAD) throw std::runtime_error("invalid character"); + } else { + out.push_back((b2 << 4) | (b3 >> 2)); + if (b4 != PAD) out.push_back((b3 << 6) | b4); + } + + return out; } +} // namespace base64 void FilesystemInstance::FileReadString(const picojson::value& args, picojson::object& out) { ScopeLogger(); @@ -254,73 +378,82 @@ void FilesystemInstance::FileReadBytes(const picojson::value& args, picojson::ob } } -void FilesystemInstance::FileWrite(const picojson::value& args, picojson::object& out) { +void FilesystemInstance::FileWriteString(const picojson::value& args, picojson::object& out) { ScopeLogger(); - CHECK_EXIST(args, "callbackId", out) + CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemWrite, &out); CHECK_EXIST(args, "location", out) CHECK_EXIST(args, "data", out) CHECK_EXIST(args, "offset", out) CHECK_EXIST(args, "rewrite", out) - CHECK_EXIST(args, "isString", out) + CHECK_EXIST(args, "encoding", out) - double callback_id = args.get("callbackId").get(); const std::string& location = args.get("location").get(); - const std::string& data = args.get("data").get(); - size_t offset = static_cast(args.get("location").get()); + const std::string& str = args.get("data").get(); + size_t offset = static_cast(args.get("offset").get()); bool rewrite = static_cast(args.get("rewrite").get()); - bool is_string = static_cast(args.get("isString").get()); - auto onSuccess = [this, callback_id]() { - ScopeLogger("Entered into asynchronous function, onSuccess"); - picojson::value response = picojson::value(picojson::object()); - picojson::object& obj = response.get(); - obj["callbackId"] = picojson::value(callback_id); - ReportSuccess(obj); - PostMessage(response.serialize().c_str()); - }; + try { + write_file((const std::uint8_t*)str.c_str(), str.length(), location, offset, rewrite); + } catch (std::runtime_error& e) { + LoggerE("Cannot write to file %s, cause: %s", location.c_str(), e.what()); + PrepareError(FilesystemError::Other, out); + } + ReportSuccess(out); +} - auto onError = [this, callback_id](FilesystemError e) { - ScopeLogger("Entered into asynchronous function, onError"); - picojson::value response = picojson::value(picojson::object()); - picojson::object& obj = response.get(); - obj["callbackId"] = picojson::value(callback_id); - PrepareError(e, obj); - PostMessage(response.serialize().c_str()); - }; +void FilesystemInstance::FileWriteBytes(const picojson::value& args, picojson::object& out) { + ScopeLogger(); + CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemWrite, &out); + CHECK_EXIST(args, "location", out) + CHECK_EXIST(args, "data", out) + CHECK_EXIST(args, "offset", out) + CHECK_EXIST(args, "rewrite", out) - FilesystemManager& fsm = FilesystemManager::GetInstance(); - common::TaskQueue::GetInstance().Async(std::bind(&FilesystemManager::FileWrite, &fsm, location, - data, offset, rewrite, is_string, onSuccess, - onError)); + const std::string& location = args.get("location").get(); + const std::string& str = args.get("data").get(); + size_t offset = static_cast(args.get("offset").get()); + bool rewrite = static_cast(args.get("rewrite").get()); + + try { + std::vector data; + decode_binary_from_string(str, data); + write_file(data.data(), data.size(), location, offset, rewrite); + } catch (std::runtime_error& e) { + LoggerE("Cannot write to %s, cause: %s", location.c_str(), e.what()); + PrepareError(FilesystemError::Other, out); + } + ReportSuccess(out); } -void FilesystemInstance::FileWriteSync(const picojson::value& args, picojson::object& out) { +void FilesystemInstance::FileWriteBase64(const picojson::value& args, picojson::object& out) { ScopeLogger(); CHECK_PRIVILEGE_ACCESS(kPrivilegeFilesystemWrite, &out); CHECK_EXIST(args, "location", out) CHECK_EXIST(args, "data", out) CHECK_EXIST(args, "offset", out) CHECK_EXIST(args, "rewrite", out) - CHECK_EXIST(args, "isString", out) const std::string& location = args.get("location").get(); - const std::string& data = args.get("data").get(); + const std::string& str = args.get("data").get(); size_t offset = static_cast(args.get("offset").get()); bool rewrite = static_cast(args.get("rewrite").get()); - bool is_string = static_cast(args.get("isString").get()); - - auto onSuccess = [this, &out]() { - ScopeLogger("Entered into asynchronous function, onSuccess"); - ReportSuccess(out); - }; - auto onError = [this, &out](FilesystemError e) { - ScopeLogger("Entered into asynchronous function, onError"); - PrepareError(e, out); - }; + std::vector data; + try { + data = base64::decode(str.c_str(), str.length()); + } catch (std::runtime_error& e) { + LoggerE("Can't decode base64: %s", e.what()); + ReportError(InvalidValuesException(std::string("Can't decode base64: ") + e.what()), out); + return; + } - FilesystemManager::GetInstance().FileWrite(location, data, offset, rewrite, is_string, onSuccess, - onError); + try { + write_file(data.data(), data.size(), location, offset, rewrite); + ReportSuccess(picojson::value{(double)data.size()}, out); + } catch (std::runtime_error& e) { + LoggerE("Cannot write to %s, cause: %s", location.c_str(), e.what()); + PrepareError(FilesystemError::Other, out); + } } void FilesystemInstance::FileStat(const picojson::value& args, picojson::object& out) { diff --git a/src/filesystem/filesystem_instance.h b/src/filesystem/filesystem_instance.h index da21bc7..07fb8fa 100644 --- a/src/filesystem/filesystem_instance.h +++ b/src/filesystem/filesystem_instance.h @@ -37,8 +37,9 @@ class FilesystemInstance : public common::ParsedInstance, FilesystemStateChangeL void FileStatSync(const picojson::value& args, picojson::object& out); void FileReadString(const picojson::value& args, picojson::object& out); void FileReadBytes(const picojson::value& args, picojson::object& out); - void FileWrite(const picojson::value& args, picojson::object& out); - void FileWriteSync(const picojson::value& args, picojson::object& out); + void FileWriteBytes(const picojson::value& args, picojson::object& out); + void FileWriteBase64(const picojson::value& args, picojson::object& out); + void FileWriteString(const picojson::value& args, picojson::object& out); void FilesystemFetchVirtualRoots(const picojson::value& args, picojson::object& out); void FileSystemManagerFetchStorages(const picojson::value& args, picojson::object& out); void FileSystemManagerMakeDirectory(const picojson::value& args, picojson::object& out); diff --git a/src/filesystem/filesystem_manager.cc b/src/filesystem/filesystem_manager.cc index 0836c76..e6b373d 100644 --- a/src/filesystem/filesystem_manager.cc +++ b/src/filesystem/filesystem_manager.cc @@ -37,7 +37,6 @@ #include "common/logger.h" #include "common/scope_exit.h" #include "common/tools.h" -#include "filesystem/filesystem_file.h" namespace extension { namespace filesystem { @@ -397,34 +396,6 @@ void FilesystemManager::RemoveDirectory(const std::string& path, return; } -void FilesystemManager::FileWrite(const std::string& path, const std::string& data, size_t offset, - bool rewrite, bool is_string, - const std::function& success_cb, - const std::function& error_cb) { - ScopeLogger(); - FilesystemFile file(path); - FilesystemBuffer buffer; - // Decode buffer data if type is not string - if (!is_string) { - if (!buffer.DecodeData(data)) { - LoggerE("Cannot decode file data!"); - error_cb(FilesystemError::Other); - return; - } - if (file.Write(buffer, offset, rewrite)) { - success_cb(); - } else { - LoggerE("Cannot write to file %s!", path.c_str()); - error_cb(FilesystemError::Other); - } - } else if (file.WriteString(data, offset, rewrite)) { - success_cb(); - } else { - LoggerE("Cannot write to file %s!", path.c_str()); - error_cb(FilesystemError::Other); - } -} - void FilesystemManager::StartListening() { ScopeLogger(); auto set = std::bind(&FilesystemManager::OnStorageDeviceChanged, this, std::placeholders::_1, diff --git a/src/filesystem/filesystem_manager.h b/src/filesystem/filesystem_manager.h index 1f2fdbb..e0d2d12 100644 --- a/src/filesystem/filesystem_manager.h +++ b/src/filesystem/filesystem_manager.h @@ -83,10 +83,6 @@ class FilesystemManager { void RemoveDirectory(const std::string& path, const std::function& success_cb, const std::function& error_cb); - void FileWrite(const std::string& path, const std::string& data, size_t offset, bool rewrite, - bool is_string, const std::function& success_cb, - const std::function& error_cb); - void CopyTo(const std::string& originFilePath, const std::string& destinationFilePath, const bool overwrite, const std::function& success_cb, const std::function& error_cb); diff --git a/src/filesystem/js/file_stream.js b/src/filesystem/js/file_stream.js index 49f6bba..954ef49 100644 --- a/src/filesystem/js/file_stream.js +++ b/src/filesystem/js/file_stream.js @@ -103,6 +103,33 @@ function _checkWriteAccess(mode) { } } +/* returns array of numbers */ +function string_to_binary( str ) { + var output = []; + var len = str.length; + var c; + for( var i = 0; i < len; i++ ) { + // decode unicode codepoint U+0100 as zero byte + c = str.charCodeAt(i); + output.push( c == 0x100 ? 0 : c ); + } + return output; +} + +/* receives array of numbers, returns string */ +function binary_to_string( data ) { + var output = ""; + var len = data.length; + // endecode zero byte as unicode codepoint U+0100 + var zero = String.fromCharCode(0x100); + var b; + for( var i = 0; i < len; i++ ) { + b = data[i]; + output += b == 0 ? zero : String.fromCharCode(b); + } + return output; +} + function read() { var args = validator_.validateArgs(arguments, [ { @@ -160,17 +187,6 @@ FileStream.prototype.read = function() { return read.apply(this, arguments); }; -/* returns array of numbers */ -function string_to_binary( str ) { - var output = []; - var len = str.length; - for( var i = 0; i < len; i++ ) { - // decode unicode codepoint U+0100 as zero byte - output.push( str[i] == '\u0100' ? 0 : str.charCodeAt(i) ); - } - return output; -} - function readBytes() { var args = validator_.validateArgs(arguments, [ { @@ -239,13 +255,13 @@ function write() { var data = { location: commonFS_.toRealPath(this._file.fullPath), + encoding: this._encoding, offset: this.position, data: args.stringData, - rewrite: this._rewrite, - isString: true + rewrite: this._rewrite }; - var result = native_.callSync('File_writeSync', data); + var result = native_.callSync('File_writeString', data); if (native_.isFailure(result)) { throw new WebAPIException(WebAPIException.IO_ERR, 'Could not write'); @@ -280,12 +296,11 @@ function writeBytes() { var data = { location: commonFS_.toRealPath(this._file.fullPath), offset: this.position, - data: Base64.encode(args.byteData), + data: binary_to_string(args.byteData), rewrite: this._rewrite, - isString: false }; - var result = native_.callSync('File_writeSync', data); + var result = native_.callSync('File_writeBytes', data); if (native_.isFailure(result)) { throw new WebAPIException(WebAPIException.IO_ERR, 'Could not write'); @@ -300,11 +315,6 @@ FileStream.prototype.writeBytes = function() { writeBytes.apply(this, arguments); }; -function _isBase64(str) { - var base64 = new RegExp('^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$'); - return base64.test(str); -} - function writeBase64() { var args = validator_.validateArgs(arguments, [ { @@ -316,33 +326,23 @@ function writeBase64() { _checkClosed(this); _checkWriteAccess(this._mode); - if (!arguments.length) { - throw new WebAPIException(WebAPIException.TYPE_MISMATCH_ERR, - 'Argument "base64Data" missing'); - } - if (!args.base64Data.length || !_isBase64(args.base64Data)) { - throw new WebAPIException(WebAPIException.INVALID_VALUES_ERR, - 'Data is not base64'); - } - var data = { location: commonFS_.toRealPath(this._file.fullPath), offset: this.position, data: args.base64Data, rewrite: this._rewrite, - isString: false }; - var result = native_.callSync('File_writeSync', data); + var result = native_.callSync('File_writeBase64', data); if (native_.isFailure(result)) { - throw new WebAPIException(WebAPIException.IO_ERR, 'Could not write'); + throw native_.getErrorObject(result); } - var decoded = Base64.decode(args.base64Data); + var written_bytes = native_.getResultObject(result); can_change_size = true; - this.position += decoded.length; + this.position += written_bytes; can_change_size = false; this._rewrite = false; }; -- 2.7.4