[Filesystem] Write file performace improvement (binary data through UTF-8) 85/167585/1
authorJakub Skowron <j.skowron@samsung.com>
Wed, 17 Jan 2018 12:46:16 +0000 (13:46 +0100)
committerPiotr Kosko <p.kosko@samsung.com>
Thu, 18 Jan 2018 07:30:39 +0000 (08:30 +0100)
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 <j.skowron@samsung.com>
src/filesystem/filesystem.gyp
src/filesystem/filesystem_file.cc [deleted file]
src/filesystem/filesystem_file.h [deleted file]
src/filesystem/filesystem_instance.cc
src/filesystem/filesystem_instance.h
src/filesystem/filesystem_manager.cc
src/filesystem/filesystem_manager.h
src/filesystem/js/file_stream.js

index 9a3c737..6f3f46a 100644 (file)
@@ -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 (file)
index 1174747..0000000
+++ /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 <common/logger.h>
-#include <common/scope_exit.h>
-#include <stdio.h>
-#include <unistd.h>
-
-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<uint8_t*>(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 (file)
index e8dce33..0000000
+++ /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 <stdint.h>
-#include <string>
-#include <vector>
-
-namespace extension {
-namespace filesystem {
-
-class FilesystemBuffer : public std::vector<uint8_t> {
- 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
index 848c6dc..eaceadc 100644 (file)
@@ -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<std::uint8_t>& 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<std::uint8_t>& 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<std::uint8_t> 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<std::uint8_t>& 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<std::uint8_t> decode(const char* str, std::size_t len) {
+  std::vector<std::uint8_t> 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<double>();
   const std::string& location = args.get("location").get<std::string>();
-  const std::string& data = args.get("data").get<std::string>();
-  size_t offset = static_cast<size_t>(args.get("location").get<double>());
+  const std::string& str = args.get("data").get<std::string>();
+  size_t offset = static_cast<size_t>(args.get("offset").get<double>());
   bool rewrite = static_cast<bool>(args.get("rewrite").get<bool>());
-  bool is_string = static_cast<bool>(args.get("isString").get<bool>());
 
-  auto onSuccess = [this, callback_id]() {
-    ScopeLogger("Entered into asynchronous function, onSuccess");
-    picojson::value response = picojson::value(picojson::object());
-    picojson::object& obj = response.get<picojson::object>();
-    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<picojson::object>();
-    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<std::string>();
+  const std::string& str = args.get("data").get<std::string>();
+  size_t offset = static_cast<size_t>(args.get("offset").get<double>());
+  bool rewrite = static_cast<bool>(args.get("rewrite").get<bool>());
+
+  try {
+    std::vector<std::uint8_t> 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<std::string>();
-  const std::string& data = args.get("data").get<std::string>();
+  const std::string& str = args.get("data").get<std::string>();
   size_t offset = static_cast<size_t>(args.get("offset").get<double>());
   bool rewrite = static_cast<bool>(args.get("rewrite").get<bool>());
-  bool is_string = static_cast<bool>(args.get("isString").get<bool>());
-
-  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<std::uint8_t> 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) {
index da21bc7..07fb8fa 100644 (file)
@@ -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);
index 0836c76..e6b373d 100644 (file)
@@ -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<void()>& success_cb,
-                                  const std::function<void(FilesystemError)>& 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,
index 1f2fdbb..e0d2d12 100644 (file)
@@ -83,10 +83,6 @@ class FilesystemManager {
   void RemoveDirectory(const std::string& path, const std::function<void()>& success_cb,
                        const std::function<void(FilesystemError)>& error_cb);
 
-  void FileWrite(const std::string& path, const std::string& data, size_t offset, bool rewrite,
-                 bool is_string, const std::function<void()>& success_cb,
-                 const std::function<void(FilesystemError)>& error_cb);
-
   void CopyTo(const std::string& originFilePath, const std::string& destinationFilePath,
               const bool overwrite, const std::function<void()>& success_cb,
               const std::function<void(FilesystemError)>& error_cb);
index 49f6bba..954ef49 100644 (file)
@@ -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;
 };