Codebase list mozc / debian/2.23.2815.102+dfsg-1 src / base / pepper_file_system_mock.cc
debian/2.23.2815.102+dfsg-1

Tree @debian/2.23.2815.102+dfsg-1 (Download .tar.gz)

pepper_file_system_mock.cc @debian/2.23.2815.102+dfsg-1raw · history · blame

// Copyright 2010-2018, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//     * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#ifdef OS_NACL

#include "base/pepper_file_system_mock.h"

#include <algorithm>
#include <string>

#include "base/file_util.h"
#include "base/logging.h"
#include "base/mmap_sync_interface.h"
#include "base/mutex.h"
#include "base/util.h"

using std::unique_ptr;
using mozc::internal::MockFileNode;

namespace mozc {
namespace {
const char *kFileDelimiter = "/";
}  // namespace

namespace internal {

MockFileNode::MockFileNode() : is_directory_(false) {}

MockFileNode::MockFileNode(MockFileNode *parent_node, const string &name,
                           bool is_directory)
    : parent_node_(parent_node), name_(name), is_directory_(is_directory) {}

MockFileNode::~MockFileNode() {}

bool MockFileNode::FileExists(const string &filename) const {
  return child_nodes_.find(filename) != child_nodes_.end();
}

bool MockFileNode::DirectoryExists(const string &dirname) const {
  auto iter = child_nodes_.find(dirname);
  return iter != child_nodes_.end() && iter->second->is_directory_;
}

bool MockFileNode::IsDirectory() const {
  return is_directory_;
}

bool MockFileNode::GetFileContent(string *buffer) const {
  if (is_directory_) {
    return false;
  }
  *buffer = content_;
  return true;
}

bool MockFileNode::SetFile(const string &filename, const string &content) {
  if (!is_directory_ || DirectoryExists(filename)) {
    return false;
  }
  MockFileNode *node = new MockFileNode(this, filename, false);
  node->content_ = content;
  child_nodes_[filename].reset(node);
  return true;
}

bool MockFileNode::AddDirectory(const string &dirname) {
  if (!is_directory_ || FileExists(dirname)) {
    return false;
  }
  child_nodes_[dirname] =
      unique_ptr<MockFileNode>(new MockFileNode(this, dirname, true));
  return true;
}

bool MockFileNode::Rename(const string &filename, MockFileNode *parent_node) {
  if (!parent_node || !parent_node->is_directory_) {
    LOG(ERROR) << "Invalid parent node.";
    return false;
  }
  if (filename == name_ && parent_node == parent_node_) {
    return true;
  }
  if (parent_node->DirectoryExists(filename) ||
      (parent_node->FileExists(filename) && is_directory_)) {
    LOG(ERROR) << "Cannot overwrite the destination file or directory.";
    return false;
  }

  auto iter = parent_node_->child_nodes_.find(name_);
  CHECK_EQ(iter->second.get(), this);
  parent_node->child_nodes_[filename].reset(iter->second.release());
  parent_node_->child_nodes_.erase(iter);
  name_ = filename;
  parent_node_ = parent_node;
  return true;
}

bool MockFileNode::Delete() {
  if (!parent_node_) {
    LOG(ERROR) << "Cannot delete a root directory.";
    return false;
  }
  return parent_node_->child_nodes_.erase(name_) > 0;
}

bool MockFileNode::Query(PP_FileInfo *file_info) const {
  file_info->size = content_.size();
  file_info->type = is_directory_ ? PP_FILETYPE_DIRECTORY : PP_FILETYPE_REGULAR;
  file_info->system_type = PP_FILESYSTEMTYPE_ISOLATED;
  // Fill dummy value for time stamp.
  file_info->creation_time = content_.size() + 1;
  file_info->last_access_time = content_.size() + 1;
  file_info->last_modified_time = content_.size() + 1;
  return true;
}

MockFileNode *MockFileNode::GetNode(const string &path) {
  if (path.empty() || path == kFileDelimiter) {
    return this;
  }
  MockFileNode *current_node = this;
  for (SplitIterator<SingleDelimiter> iter(path, kFileDelimiter);
       !iter.Done(); iter.Next()) {
    const string &name = iter.Get().as_string();
    auto child_node_iter = current_node->child_nodes_.find(name);
    if (child_node_iter == child_nodes_.end()) {
      return nullptr;
    }
    current_node = child_node_iter->second.get();
  }
  return current_node;
}

string MockFileNode::DebugMessage() const {
  string message = "\n";

  string path;
  {
    std::vector<string> nodes;
    const MockFileNode *node = this;
    while (node != nullptr) {
      nodes.push_back(node->name_);
      node = node->parent_node_;
    }
    reverse(nodes.begin(), nodes.end());
    Util::JoinStrings(nodes, "/", &path);
  }

  std::vector<const MockFileNode*> sub_directories;
  if (is_directory_) {
    message += Util::StringPrintf("directory: %s\n", path.c_str());
    for (const auto &elem : child_nodes_) {
      const MockFileNode *child_node = elem.second.get();
      const char node_type = child_node->is_directory_ ? 'D' : 'F';
      message += Util::StringPrintf(
          "  %c %s\n", node_type, child_node->name_.c_str());
      sub_directories.push_back(child_node);
    }
  } else {
    message += Util::StringPrintf("file: %s\n", path.c_str());
    message += Util::StringPrintf(
        "  size: %d\n", static_cast<int>(content_.size()));
    message += "  content: [" + content_ + "]\n";
  }
  if (!sub_directories.empty()) {
    for (const MockFileNode *node : sub_directories) {
      message += node->DebugMessage();
    }
  }
  return message;
}

}  // namespace internal

PepperFileSystemMock::PepperFileSystemMock()
    : root_directory_(nullptr, "", true) {}

PepperFileSystemMock::~PepperFileSystemMock() {}

bool PepperFileSystemMock::Open(pp::Instance *instance, int64 expected_size) {
  scoped_lock l(&mutex_);
  return true;
}

bool PepperFileSystemMock::FileExists(const string &path) {
  scoped_lock l(&mutex_);
  MockFileNode *node = root_directory_.GetNode(FileUtil::Dirname(path));
  return node && node->FileExists(FileUtil::Basename(path));
}

bool PepperFileSystemMock::DirectoryExists(const string &path) {
  scoped_lock l(&mutex_);
  MockFileNode *node = root_directory_.GetNode(FileUtil::Dirname(path));
  const string &basename = FileUtil::Basename(path);
  return node && (basename.empty() || node->DirectoryExists(basename));
}

bool PepperFileSystemMock::ReadBinaryFile(const string &path, string *buffer) {
  scoped_lock l(&mutex_);
  MockFileNode *node = root_directory_.GetNode(path);
  return node && node->GetFileContent(buffer);
}

bool PepperFileSystemMock::WriteBinaryFile(const string &path,
                                           const string &buffer) {
  scoped_lock l(&mutex_);
  const string &filename = FileUtil::Basename(path);
  MockFileNode *node = root_directory_.GetNode(FileUtil::Dirname(path));
  bool result = node && node->SetFile(filename, buffer);
  return result;
}

bool PepperFileSystemMock::CreateDirectory(const string &path) {
  scoped_lock l(&mutex_);
  MockFileNode *node = root_directory_.GetNode(FileUtil::Dirname(path));
  return node && node->AddDirectory(FileUtil::Basename(path));
}

bool PepperFileSystemMock::Delete(const string &path) {
  scoped_lock l(&mutex_);
  MockFileNode *node = root_directory_.GetNode(path);
  return node && node->Delete();
}

bool PepperFileSystemMock::Rename(const string &from, const string &to) {
  scoped_lock l(&mutex_);
  MockFileNode *from_file = root_directory_.GetNode(from);
  MockFileNode *to_directory = root_directory_.GetNode(FileUtil::Dirname(to));
  return from_file && from_file->Rename(FileUtil::Basename(to), to_directory);
}

bool PepperFileSystemMock::RegisterMmap(MmapSyncInterface *mmap) {
  scoped_lock l(&mutex_);
  return mmap_set_.insert(mmap).second;
}

bool PepperFileSystemMock::UnRegisterMmap(MmapSyncInterface *mmap) {
  scoped_lock l(&mutex_);
  return mmap_set_.erase(mmap);
}

bool PepperFileSystemMock::SyncMmapToFile() {
  scoped_lock l(&mutex_);
  for (std::set<MmapSyncInterface*>::iterator it = mmap_set_.begin();
       it != mmap_set_.end(); ++it) {
    (*it)->SyncToFile();
  }
  return true;
}

bool PepperFileSystemMock::Query(const string &path, PP_FileInfo *file_info) {
  scoped_lock l(&mutex_);
  MockFileNode *node = root_directory_.GetNode(path);
  return node && node->Query(file_info);
}

}  // namespace mozc

#endif  // OS_NACL