<commit_before>//
// Copyright (c) 2010 Advanced Micro Devices, Inc. All rights reserved.
//

#ifndef VERSIONS_HPP_
#define VERSIONS_HPP_

#include "utils/macros.hpp"

#ifndef AMD_PLATFORM_NAME
# define AMD_PLATFORM_NAME "AMD Accelerated Parallel Processing"
#endif // AMD_PLATFORM_NAME

#ifndef AMD_PLATFORM_BUILD_NUMBER
# define AMD_PLATFORM_BUILD_NUMBER 2335
#endif // AMD_PLATFORM_BUILD_NUMBER

#ifndef AMD_PLATFORM_REVISION_NUMBER
# define AMD_PLATFORM_REVISION_NUMBER 0
#endif // AMD_PLATFORM_REVISION_NUMBER

#ifndef AMD_PLATFORM_RELEASE_INFO
# define AMD_PLATFORM_RELEASE_INFO
#endif // AMD_PLATFORM_RELEASE_INFO

#define AMD_BUILD_STRING XSTR(AMD_PLATFORM_BUILD_NUMBER) \
    "." XSTR(AMD_PLATFORM_REVISION_NUMBER)

#ifndef AMD_PLATFORM_INFO
# define AMD_PLATFORM_INFO "AMD-APP" AMD_PLATFORM_RELEASE_INFO \
    DEBUG_ONLY("." IF(IS_OPTIMIZED,"opt","dbg")) " (" AMD_BUILD_STRING ")"
#endif // ATI_PLATFORM_INFO

#endif // VERSIONS_HPP_
<commit_msg>P4 to Git Change 1361988 by johtaylo@johtaylo-jtincrementor-increment on 2017/01/15 03:00:07<commit_after>//
// Copyright (c) 2010 Advanced Micro Devices, Inc. All rights reserved.
//

#ifndef VERSIONS_HPP_
#define VERSIONS_HPP_

#include "utils/macros.hpp"

#ifndef AMD_PLATFORM_NAME
# define AMD_PLATFORM_NAME "AMD Accelerated Parallel Processing"
#endif // AMD_PLATFORM_NAME

#ifndef AMD_PLATFORM_BUILD_NUMBER
# define AMD_PLATFORM_BUILD_NUMBER 2336
#endif // AMD_PLATFORM_BUILD_NUMBER

#ifndef AMD_PLATFORM_REVISION_NUMBER
# define AMD_PLATFORM_REVISION_NUMBER 0
#endif // AMD_PLATFORM_REVISION_NUMBER

#ifndef AMD_PLATFORM_RELEASE_INFO
# define AMD_PLATFORM_RELEASE_INFO
#endif // AMD_PLATFORM_RELEASE_INFO

#define AMD_BUILD_STRING XSTR(AMD_PLATFORM_BUILD_NUMBER) \
    "." XSTR(AMD_PLATFORM_REVISION_NUMBER)

#ifndef AMD_PLATFORM_INFO
# define AMD_PLATFORM_INFO "AMD-APP" AMD_PLATFORM_RELEASE_INFO \
    DEBUG_ONLY("." IF(IS_OPTIMIZED,"opt","dbg")) " (" AMD_BUILD_STRING ")"
#endif // ATI_PLATFORM_INFO

#endif // VERSIONS_HPP_
<|endoftext|>


<commit_before>//
// Copyright (c) 2010 Advanced Micro Devices, Inc. All rights reserved.
//

#ifndef VERSIONS_HPP_
#define VERSIONS_HPP_

#include "utils/macros.hpp"

#ifndef AMD_PLATFORM_NAME
#define AMD_PLATFORM_NAME "AMD Accelerated Parallel Processing"
#endif  // AMD_PLATFORM_NAME

#ifndef AMD_PLATFORM_BUILD_NUMBER
#define AMD_PLATFORM_BUILD_NUMBER 2459
#endif  // AMD_PLATFORM_BUILD_NUMBER

#ifndef AMD_PLATFORM_REVISION_NUMBER
#define AMD_PLATFORM_REVISION_NUMBER 0
#endif  // AMD_PLATFORM_REVISION_NUMBER

#ifndef AMD_PLATFORM_RELEASE_INFO
#define AMD_PLATFORM_RELEASE_INFO
#endif  // AMD_PLATFORM_RELEASE_INFO

#define AMD_BUILD_STRING                                                                           \
  XSTR(AMD_PLATFORM_BUILD_NUMBER)                                                                  \
  "." XSTR(AMD_PLATFORM_REVISION_NUMBER)

#ifndef AMD_PLATFORM_INFO
#define AMD_PLATFORM_INFO                                                                          \
  "AMD-APP" AMD_PLATFORM_RELEASE_INFO DEBUG_ONLY(                                                  \
      "." IF(IS_OPTIMIZED, "opt", "dbg")) " (" AMD_BUILD_STRING ")"
#endif  // ATI_PLATFORM_INFO

#endif  // VERSIONS_HPP_
<commit_msg>P4 to Git Change 1437971 by johtaylo@johtaylo-jtincrementor-increment on 2017/07/22 03:00:04<commit_after>//
// Copyright (c) 2010 Advanced Micro Devices, Inc. All rights reserved.
//

#ifndef VERSIONS_HPP_
#define VERSIONS_HPP_

#include "utils/macros.hpp"

#ifndef AMD_PLATFORM_NAME
#define AMD_PLATFORM_NAME "AMD Accelerated Parallel Processing"
#endif  // AMD_PLATFORM_NAME

#ifndef AMD_PLATFORM_BUILD_NUMBER
#define AMD_PLATFORM_BUILD_NUMBER 2460
#endif  // AMD_PLATFORM_BUILD_NUMBER

#ifndef AMD_PLATFORM_REVISION_NUMBER
#define AMD_PLATFORM_REVISION_NUMBER 0
#endif  // AMD_PLATFORM_REVISION_NUMBER

#ifndef AMD_PLATFORM_RELEASE_INFO
#define AMD_PLATFORM_RELEASE_INFO
#endif  // AMD_PLATFORM_RELEASE_INFO

#define AMD_BUILD_STRING                                                                           \
  XSTR(AMD_PLATFORM_BUILD_NUMBER)                                                                  \
  "." XSTR(AMD_PLATFORM_REVISION_NUMBER)

#ifndef AMD_PLATFORM_INFO
#define AMD_PLATFORM_INFO                                                                          \
  "AMD-APP" AMD_PLATFORM_RELEASE_INFO DEBUG_ONLY(                                                  \
      "." IF(IS_OPTIMIZED, "opt", "dbg")) " (" AMD_BUILD_STRING ")"
#endif  // ATI_PLATFORM_INFO

#endif  // VERSIONS_HPP_
<|endoftext|>


<commit_before>//
// Copyright (c) 2010 Advanced Micro Devices, Inc. All rights reserved.
//

#ifndef VERSIONS_HPP_
#define VERSIONS_HPP_

#include "utils/macros.hpp"

#ifndef AMD_PLATFORM_NAME
#define AMD_PLATFORM_NAME "AMD Accelerated Parallel Processing"
#endif  // AMD_PLATFORM_NAME

#ifndef AMD_PLATFORM_BUILD_NUMBER
#define AMD_PLATFORM_BUILD_NUMBER 2975
#endif  // AMD_PLATFORM_BUILD_NUMBER

#ifndef AMD_PLATFORM_REVISION_NUMBER
#define AMD_PLATFORM_REVISION_NUMBER 0
#endif  // AMD_PLATFORM_REVISION_NUMBER

#ifndef AMD_PLATFORM_RELEASE_INFO
#define AMD_PLATFORM_RELEASE_INFO
#endif  // AMD_PLATFORM_RELEASE_INFO

#define AMD_BUILD_STRING                                                                           \
  XSTR(AMD_PLATFORM_BUILD_NUMBER)                                                                  \
  "." XSTR(AMD_PLATFORM_REVISION_NUMBER)

#ifndef AMD_PLATFORM_INFO
#define AMD_PLATFORM_INFO                                                                          \
  "AMD-APP" AMD_PLATFORM_RELEASE_INFO DEBUG_ONLY(                                                  \
      "." IF(IS_OPTIMIZED, "opt", "dbg")) " (" AMD_BUILD_STRING ")"
#endif  // ATI_PLATFORM_INFO

#endif  // VERSIONS_HPP_
<commit_msg>P4 to Git Change 1984843 by chui@ocl-promo-incrementor on 2019/08/18 03:00:25<commit_after>//
// Copyright (c) 2010 Advanced Micro Devices, Inc. All rights reserved.
//

#ifndef VERSIONS_HPP_
#define VERSIONS_HPP_

#include "utils/macros.hpp"

#ifndef AMD_PLATFORM_NAME
#define AMD_PLATFORM_NAME "AMD Accelerated Parallel Processing"
#endif  // AMD_PLATFORM_NAME

#ifndef AMD_PLATFORM_BUILD_NUMBER
#define AMD_PLATFORM_BUILD_NUMBER 2976
#endif  // AMD_PLATFORM_BUILD_NUMBER

#ifndef AMD_PLATFORM_REVISION_NUMBER
#define AMD_PLATFORM_REVISION_NUMBER 0
#endif  // AMD_PLATFORM_REVISION_NUMBER

#ifndef AMD_PLATFORM_RELEASE_INFO
#define AMD_PLATFORM_RELEASE_INFO
#endif  // AMD_PLATFORM_RELEASE_INFO

#define AMD_BUILD_STRING                                                                           \
  XSTR(AMD_PLATFORM_BUILD_NUMBER)                                                                  \
  "." XSTR(AMD_PLATFORM_REVISION_NUMBER)

#ifndef AMD_PLATFORM_INFO
#define AMD_PLATFORM_INFO                                                                          \
  "AMD-APP" AMD_PLATFORM_RELEASE_INFO DEBUG_ONLY(                                                  \
      "." IF(IS_OPTIMIZED, "opt", "dbg")) " (" AMD_BUILD_STRING ")"
#endif  // ATI_PLATFORM_INFO

#endif  // VERSIONS_HPP_
<|endoftext|>


<commit_before>//
// Copyright (c) 2010 Advanced Micro Devices, Inc. All rights reserved.
//

#ifndef VERSIONS_HPP_
#define VERSIONS_HPP_

#include "utils/macros.hpp"

#ifndef AMD_PLATFORM_NAME
#define AMD_PLATFORM_NAME "AMD Accelerated Parallel Processing"
#endif  // AMD_PLATFORM_NAME

#ifndef AMD_PLATFORM_BUILD_NUMBER
#define AMD_PLATFORM_BUILD_NUMBER 2467
#endif  // AMD_PLATFORM_BUILD_NUMBER

#ifndef AMD_PLATFORM_REVISION_NUMBER
#define AMD_PLATFORM_REVISION_NUMBER 0
#endif  // AMD_PLATFORM_REVISION_NUMBER

#ifndef AMD_PLATFORM_RELEASE_INFO
#define AMD_PLATFORM_RELEASE_INFO
#endif  // AMD_PLATFORM_RELEASE_INFO

#define AMD_BUILD_STRING                                                                           \
  XSTR(AMD_PLATFORM_BUILD_NUMBER)                                                                  \
  "." XSTR(AMD_PLATFORM_REVISION_NUMBER)

#ifndef AMD_PLATFORM_INFO
#define AMD_PLATFORM_INFO                                                                          \
  "AMD-APP" AMD_PLATFORM_RELEASE_INFO DEBUG_ONLY(                                                  \
      "." IF(IS_OPTIMIZED, "opt", "dbg")) " (" AMD_BUILD_STRING ")"
#endif  // ATI_PLATFORM_INFO

#endif  // VERSIONS_HPP_
<commit_msg>P4 to Git Change 1442964 by johtaylo@johtaylo-jtincrementor-increment on 2017/08/03 03:00:04<commit_after>//
// Copyright (c) 2010 Advanced Micro Devices, Inc. All rights reserved.
//

#ifndef VERSIONS_HPP_
#define VERSIONS_HPP_

#include "utils/macros.hpp"

#ifndef AMD_PLATFORM_NAME
#define AMD_PLATFORM_NAME "AMD Accelerated Parallel Processing"
#endif  // AMD_PLATFORM_NAME

#ifndef AMD_PLATFORM_BUILD_NUMBER
#define AMD_PLATFORM_BUILD_NUMBER 2468
#endif  // AMD_PLATFORM_BUILD_NUMBER

#ifndef AMD_PLATFORM_REVISION_NUMBER
#define AMD_PLATFORM_REVISION_NUMBER 0
#endif  // AMD_PLATFORM_REVISION_NUMBER

#ifndef AMD_PLATFORM_RELEASE_INFO
#define AMD_PLATFORM_RELEASE_INFO
#endif  // AMD_PLATFORM_RELEASE_INFO

#define AMD_BUILD_STRING                                                                           \
  XSTR(AMD_PLATFORM_BUILD_NUMBER)                                                                  \
  "." XSTR(AMD_PLATFORM_REVISION_NUMBER)

#ifndef AMD_PLATFORM_INFO
#define AMD_PLATFORM_INFO                                                                          \
  "AMD-APP" AMD_PLATFORM_RELEASE_INFO DEBUG_ONLY(                                                  \
      "." IF(IS_OPTIMIZED, "opt", "dbg")) " (" AMD_BUILD_STRING ")"
#endif  // ATI_PLATFORM_INFO

#endif  // VERSIONS_HPP_
<|endoftext|>


<commit_before>// Copyright 2017-2020 The Verible Authors.
//
// 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 "verilog/analysis/verilog_project.h"

#include <iostream>
#include <string>
#include <vector>

#include "absl/memory/memory.h"
#include "absl/status/status.h"
#include "absl/strings/ascii.h"
#include "absl/strings/match.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/strip.h"
#include "absl/strings/str_join.h"
#include "common/text/text_structure.h"
#include "common/util/file_util.h"
#include "common/util/logging.h"
#include "verilog/analysis/verilog_analyzer.h"

namespace verilog {

VerilogSourceFile::VerilogSourceFile(absl::string_view referenced_path,
                                     const absl::Status& status)
    : referenced_path_(referenced_path), status_(status) {}

absl::Status VerilogSourceFile::Open() {
  // Don't re-open.  analyzed_structure_ should be set/written once only.
  if (state_ != State::kInitialized) return status_;

  // Load file contents.
  std::string content;
  status_ = verible::file::GetContents(ResolvedPath(), &content);
  if (!status_.ok()) return status_;

  // TODO(fangism): std::move or memory-map to avoid a short-term copy.
  analyzed_structure_ =
      absl::make_unique<VerilogAnalyzer>(content, ResolvedPath());
  state_ = State::kOpened;
  // status_ is Ok here.
  return status_;
}

absl::Status VerilogSourceFile::Parse() {
  // Parsed state is cached.
  if (state_ == State::kParsed) return status_;

  // Open file and load contents if not already done.
  status_ = Open();
  if (!status_.ok()) return status_;

  // Lex, parse, populate underlying TextStructureView.
  status_ = analyzed_structure_->Analyze();
  state_ = State::kParsed;
  return status_;
}

const verible::TextStructureView* VerilogSourceFile::GetTextStructure() const {
  if (analyzed_structure_ == nullptr) return nullptr;
  return &analyzed_structure_->Data();
}

std::ostream& operator<<(std::ostream& stream,
                         const VerilogSourceFile& source) {
  stream << "referenced path: " << source.ReferencedPath() << std::endl;
  stream << "resolved path: " << source.ResolvedPath() << std::endl;
  stream << "corpus: " << source.Corpus() << std::endl;
  const auto status = source.Status();
  stream << "status: " << (status.ok() ? "ok" : status.message()) << std::endl;
  const auto* text_structure = source.GetTextStructure();
  stream << "have text structure? "
         << ((text_structure != nullptr) ? "yes" : "no") << std::endl;
  return stream;
}

absl::Status InMemoryVerilogSourceFile::Open() {
  analyzed_structure_ = ABSL_DIE_IF_NULL(
      absl::make_unique<VerilogAnalyzer>(contents_for_open_, ResolvedPath()));
  state_ = State::kOpened;
  status_ = absl::OkStatus();
  return status_;
}

absl::Status ParsedVerilogSourceFile::Open() {
  state_ = State::kOpened;
  status_ = absl::OkStatus();
  return status_;
}

absl::Status ParsedVerilogSourceFile::Parse() {
  state_ = State::kParsed;
  status_ = absl::OkStatus();
  return status_;
}

const verible::TextStructureView* ParsedVerilogSourceFile::GetTextStructure()
    const {
  return text_structure_;
}

absl::StatusOr<VerilogSourceFile*> VerilogProject::OpenFile(
    absl::string_view referenced_filename, absl::string_view resolved_filename,
    absl::string_view corpus) {
  const auto inserted = files_.emplace(
      referenced_filename, absl::make_unique<VerilogSourceFile>(
                               referenced_filename, resolved_filename, corpus));
  CHECK(inserted.second);  // otherwise, would have already returned above
  const auto file_iter = inserted.first;
  VerilogSourceFile& file(*file_iter->second);

  // Read the file's contents.
  const absl::Status status = file.Open();
  if (!status.ok()) return status;

  const absl::string_view contents(file.GetTextStructure()->Contents());

  // Register the file's contents range in string_view_map_.
  string_view_map_.must_emplace(contents);

  // Map the start of the file's contents to its corresponding owner
  // VerilogSourceFile.
  const auto map_inserted =
      buffer_to_analyzer_map_.emplace(contents.begin(), file_iter);
  CHECK(map_inserted.second);

  return &file;
}

absl::StatusOr<VerilogSourceFile*> VerilogProject::OpenTranslationUnit(
    absl::string_view referenced_filename) {
  // Check for a pre-existing entry to avoid duplicate files.
  {
    const auto found = files_.find(referenced_filename);
    if (found != files_.end()) {
      const auto status = found->second->Status();
      if (!status.ok()) return status;
      return found->second.get();
    }
  }

  // Locate the file among the base paths.
  const std::string resolved_filename =
      verible::file::JoinPath(TranslationUnitRoot(), referenced_filename);

  return OpenFile(referenced_filename, resolved_filename, Corpus());
}

absl::Status VerilogProject::IncludeFileNotFoundError(
    absl::string_view referenced_filename) const {
  return absl::NotFoundError(absl::StrCat(
      "Unable to find '", referenced_filename,
      "' among the included paths: ", absl::StrJoin(include_paths_, ", ")));
}

absl::StatusOr<VerilogSourceFile*> VerilogProject::OpenIncludedFile(
    absl::string_view referenced_filename) {
  VLOG(1) << __FUNCTION__ << ", referenced: " << referenced_filename;
  // Check for a pre-existing entry to avoid duplicate files.
  {
    const auto found = files_.find(referenced_filename);
    if (found != files_.end()) {
      const auto status = found->second->Status();
      if (!status.ok()) return status;
      return found->second.get();
    }
  }

  // Locate the file among the base paths.
  for (const auto& include_path : include_paths_) {
    const std::string resolved_filename =
        verible::file::JoinPath(include_path, referenced_filename);
    if (verible::file::FileExists(resolved_filename).ok()) {
      VLOG(2) << "File'" << resolved_filename << "' exists.";
      return OpenFile(referenced_filename, resolved_filename, Corpus());
    }
    VLOG(2) << "Checked for file'" << resolved_filename << "', but not found.";
  }

  // Not found in any path.  Cache this status.
  const auto inserted = files_.emplace(
      referenced_filename,
      absl::make_unique<VerilogSourceFile>(
          referenced_filename, IncludeFileNotFoundError(referenced_filename)));
  CHECK(inserted.second) << "Not-found file should have been recorded as such.";
  return inserted.first->second->Status();
}

void VerilogProject::AddVirtualFile(absl::string_view resolved_filename,
                                    absl::string_view content) {
  std::string referenced_filename(resolved_filename);
  // Make the virtual file relative to the include directory.
  for (absl::string_view include_path : include_paths_) {
    if (absl::StartsWith(resolved_filename, include_path)) {
      referenced_filename =
          absl::StripPrefix(resolved_filename, absl::StrCat(include_path, "/"));
      break;
    }
  }

  const auto inserted = files_.emplace(
      referenced_filename,
      absl::make_unique<InMemoryVerilogSourceFile>(
          referenced_filename, resolved_filename, content, /*corpus=*/""));
  CHECK(inserted.second);
  const auto file_iter = inserted.first;

  // Register the file's contents range in string_view_map_.
  string_view_map_.must_emplace(content);

  // Map the start of the file's contents to its corresponding owner
  // VerilogSourceFile.
  const auto map_inserted =
      buffer_to_analyzer_map_.emplace(content.begin(), file_iter);
  CHECK(map_inserted.second);
}

std::vector<absl::Status> VerilogProject::GetErrorStatuses() const {
  std::vector<absl::Status> statuses;
  for (const auto& file : files_) {
    const auto status = file.second->Status();
    if (!status.ok()) {
      statuses.push_back(status);
    }
  }
  return statuses;
}

const VerilogSourceFile* VerilogProject::LookupFileOrigin(
    absl::string_view content_substring) const {
  // Look for corresponding source text (superstring) buffer start.
  const auto found_superstring = string_view_map_.find(content_substring);
  if (found_superstring == string_view_map_.end()) return nullptr;
  const absl::string_view::const_iterator buffer_start =
      found_superstring->first;

  // Reverse-lookup originating file based on buffer start.
  const auto found_file = buffer_to_analyzer_map_.find(buffer_start);
  if (found_file == buffer_to_analyzer_map_.end()) return nullptr;

  const VerilogSourceFile* file = found_file->second->second.get();
  return file;
}

FileList ParseSourceFileList(absl::string_view file_list_path,
                             const std::string& file_list_content) {
  constexpr absl::string_view kIncludeDirPrefix = "+incdir+";
  FileList file_list_out;
  file_list_out.file_list_path = file_list_path;
  file_list_out.include_dirs.push_back(".");
  std::string file_path;
  std::istringstream stream(file_list_content);
  while (std::getline(stream, file_path)) {
    absl::RemoveExtraAsciiWhitespace(&file_path);
    // Ignore blank lines
    if (file_path.empty()) continue;
    // Ignore "# ..." comments
    if (file_path.front() == '#') continue;
    // Ignore "// ..." comments
    if (absl::StartsWith(file_path, "//")) continue;

    if (absl::StartsWith(file_path, kIncludeDirPrefix)) {
      // Handle includes
      file_list_out.include_dirs.emplace_back(
          absl::StripPrefix(file_path, kIncludeDirPrefix));
    } else {
      // A regular file
      file_list_out.file_paths.push_back(file_path);
    }
  }
  return file_list_out;
}

absl::StatusOr<FileList> ParseSourceFileListFromFile(
    absl::string_view file_list_file) {
  std::string content;
  const auto read_status = verible::file::GetContents(file_list_file, &content);
  if (!read_status.ok()) return read_status;
  return ParseSourceFileList(file_list_file, content);
}

}  // namespace verilog
<commit_msg>Fix string_view to string assignment<commit_after>// Copyright 2017-2020 The Verible Authors.
//
// 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 "verilog/analysis/verilog_project.h"

#include <iostream>
#include <string>
#include <vector>

#include "absl/memory/memory.h"
#include "absl/status/status.h"
#include "absl/strings/ascii.h"
#include "absl/strings/match.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/strip.h"
#include "absl/strings/str_join.h"
#include "common/text/text_structure.h"
#include "common/util/file_util.h"
#include "common/util/logging.h"
#include "verilog/analysis/verilog_analyzer.h"

namespace verilog {

VerilogSourceFile::VerilogSourceFile(absl::string_view referenced_path,
                                     const absl::Status& status)
    : referenced_path_(referenced_path), status_(status) {}

absl::Status VerilogSourceFile::Open() {
  // Don't re-open.  analyzed_structure_ should be set/written once only.
  if (state_ != State::kInitialized) return status_;

  // Load file contents.
  std::string content;
  status_ = verible::file::GetContents(ResolvedPath(), &content);
  if (!status_.ok()) return status_;

  // TODO(fangism): std::move or memory-map to avoid a short-term copy.
  analyzed_structure_ =
      absl::make_unique<VerilogAnalyzer>(content, ResolvedPath());
  state_ = State::kOpened;
  // status_ is Ok here.
  return status_;
}

absl::Status VerilogSourceFile::Parse() {
  // Parsed state is cached.
  if (state_ == State::kParsed) return status_;

  // Open file and load contents if not already done.
  status_ = Open();
  if (!status_.ok()) return status_;

  // Lex, parse, populate underlying TextStructureView.
  status_ = analyzed_structure_->Analyze();
  state_ = State::kParsed;
  return status_;
}

const verible::TextStructureView* VerilogSourceFile::GetTextStructure() const {
  if (analyzed_structure_ == nullptr) return nullptr;
  return &analyzed_structure_->Data();
}

std::ostream& operator<<(std::ostream& stream,
                         const VerilogSourceFile& source) {
  stream << "referenced path: " << source.ReferencedPath() << std::endl;
  stream << "resolved path: " << source.ResolvedPath() << std::endl;
  stream << "corpus: " << source.Corpus() << std::endl;
  const auto status = source.Status();
  stream << "status: " << (status.ok() ? "ok" : status.message()) << std::endl;
  const auto* text_structure = source.GetTextStructure();
  stream << "have text structure? "
         << ((text_structure != nullptr) ? "yes" : "no") << std::endl;
  return stream;
}

absl::Status InMemoryVerilogSourceFile::Open() {
  analyzed_structure_ = ABSL_DIE_IF_NULL(
      absl::make_unique<VerilogAnalyzer>(contents_for_open_, ResolvedPath()));
  state_ = State::kOpened;
  status_ = absl::OkStatus();
  return status_;
}

absl::Status ParsedVerilogSourceFile::Open() {
  state_ = State::kOpened;
  status_ = absl::OkStatus();
  return status_;
}

absl::Status ParsedVerilogSourceFile::Parse() {
  state_ = State::kParsed;
  status_ = absl::OkStatus();
  return status_;
}

const verible::TextStructureView* ParsedVerilogSourceFile::GetTextStructure()
    const {
  return text_structure_;
}

absl::StatusOr<VerilogSourceFile*> VerilogProject::OpenFile(
    absl::string_view referenced_filename, absl::string_view resolved_filename,
    absl::string_view corpus) {
  const auto inserted = files_.emplace(
      referenced_filename, absl::make_unique<VerilogSourceFile>(
                               referenced_filename, resolved_filename, corpus));
  CHECK(inserted.second);  // otherwise, would have already returned above
  const auto file_iter = inserted.first;
  VerilogSourceFile& file(*file_iter->second);

  // Read the file's contents.
  const absl::Status status = file.Open();
  if (!status.ok()) return status;

  const absl::string_view contents(file.GetTextStructure()->Contents());

  // Register the file's contents range in string_view_map_.
  string_view_map_.must_emplace(contents);

  // Map the start of the file's contents to its corresponding owner
  // VerilogSourceFile.
  const auto map_inserted =
      buffer_to_analyzer_map_.emplace(contents.begin(), file_iter);
  CHECK(map_inserted.second);

  return &file;
}

absl::StatusOr<VerilogSourceFile*> VerilogProject::OpenTranslationUnit(
    absl::string_view referenced_filename) {
  // Check for a pre-existing entry to avoid duplicate files.
  {
    const auto found = files_.find(referenced_filename);
    if (found != files_.end()) {
      const auto status = found->second->Status();
      if (!status.ok()) return status;
      return found->second.get();
    }
  }

  // Locate the file among the base paths.
  const std::string resolved_filename =
      verible::file::JoinPath(TranslationUnitRoot(), referenced_filename);

  return OpenFile(referenced_filename, resolved_filename, Corpus());
}

absl::Status VerilogProject::IncludeFileNotFoundError(
    absl::string_view referenced_filename) const {
  return absl::NotFoundError(absl::StrCat(
      "Unable to find '", referenced_filename,
      "' among the included paths: ", absl::StrJoin(include_paths_, ", ")));
}

absl::StatusOr<VerilogSourceFile*> VerilogProject::OpenIncludedFile(
    absl::string_view referenced_filename) {
  VLOG(1) << __FUNCTION__ << ", referenced: " << referenced_filename;
  // Check for a pre-existing entry to avoid duplicate files.
  {
    const auto found = files_.find(referenced_filename);
    if (found != files_.end()) {
      const auto status = found->second->Status();
      if (!status.ok()) return status;
      return found->second.get();
    }
  }

  // Locate the file among the base paths.
  for (const auto& include_path : include_paths_) {
    const std::string resolved_filename =
        verible::file::JoinPath(include_path, referenced_filename);
    if (verible::file::FileExists(resolved_filename).ok()) {
      VLOG(2) << "File'" << resolved_filename << "' exists.";
      return OpenFile(referenced_filename, resolved_filename, Corpus());
    }
    VLOG(2) << "Checked for file'" << resolved_filename << "', but not found.";
  }

  // Not found in any path.  Cache this status.
  const auto inserted = files_.emplace(
      referenced_filename,
      absl::make_unique<VerilogSourceFile>(
          referenced_filename, IncludeFileNotFoundError(referenced_filename)));
  CHECK(inserted.second) << "Not-found file should have been recorded as such.";
  return inserted.first->second->Status();
}

void VerilogProject::AddVirtualFile(absl::string_view resolved_filename,
                                    absl::string_view content) {
  std::string referenced_filename(resolved_filename);
  // Make the virtual file relative to the include directory.
  for (absl::string_view include_path : include_paths_) {
    if (absl::StartsWith(resolved_filename, include_path)) {
      referenced_filename =
          absl::StripPrefix(resolved_filename, absl::StrCat(include_path, "/"));
      break;
    }
  }

  const auto inserted = files_.emplace(
      referenced_filename,
      absl::make_unique<InMemoryVerilogSourceFile>(
          referenced_filename, resolved_filename, content, /*corpus=*/""));
  CHECK(inserted.second);
  const auto file_iter = inserted.first;

  // Register the file's contents range in string_view_map_.
  string_view_map_.must_emplace(content);

  // Map the start of the file's contents to its corresponding owner
  // VerilogSourceFile.
  const auto map_inserted =
      buffer_to_analyzer_map_.emplace(content.begin(), file_iter);
  CHECK(map_inserted.second);
}

std::vector<absl::Status> VerilogProject::GetErrorStatuses() const {
  std::vector<absl::Status> statuses;
  for (const auto& file : files_) {
    const auto status = file.second->Status();
    if (!status.ok()) {
      statuses.push_back(status);
    }
  }
  return statuses;
}

const VerilogSourceFile* VerilogProject::LookupFileOrigin(
    absl::string_view content_substring) const {
  // Look for corresponding source text (superstring) buffer start.
  const auto found_superstring = string_view_map_.find(content_substring);
  if (found_superstring == string_view_map_.end()) return nullptr;
  const absl::string_view::const_iterator buffer_start =
      found_superstring->first;

  // Reverse-lookup originating file based on buffer start.
  const auto found_file = buffer_to_analyzer_map_.find(buffer_start);
  if (found_file == buffer_to_analyzer_map_.end()) return nullptr;

  const VerilogSourceFile* file = found_file->second->second.get();
  return file;
}

FileList ParseSourceFileList(absl::string_view file_list_path,
                             const std::string& file_list_content) {
  constexpr absl::string_view kIncludeDirPrefix = "+incdir+";
  FileList file_list_out;
  file_list_out.file_list_path = std::string(file_list_path);
  file_list_out.include_dirs.push_back(".");
  std::string file_path;
  std::istringstream stream(file_list_content);
  while (std::getline(stream, file_path)) {
    absl::RemoveExtraAsciiWhitespace(&file_path);
    // Ignore blank lines
    if (file_path.empty()) continue;
    // Ignore "# ..." comments
    if (file_path.front() == '#') continue;
    // Ignore "// ..." comments
    if (absl::StartsWith(file_path, "//")) continue;

    if (absl::StartsWith(file_path, kIncludeDirPrefix)) {
      // Handle includes
      file_list_out.include_dirs.emplace_back(
          absl::StripPrefix(file_path, kIncludeDirPrefix));
    } else {
      // A regular file
      file_list_out.file_paths.push_back(file_path);
    }
  }
  return file_list_out;
}

absl::StatusOr<FileList> ParseSourceFileListFromFile(
    absl::string_view file_list_file) {
  std::string content;
  const auto read_status = verible::file::GetContents(file_list_file, &content);
  if (!read_status.ok()) return read_status;
  return ParseSourceFileList(file_list_file, content);
}

}  // namespace verilog
<|endoftext|>


<commit_before>// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "views/controls/single_split_view.h"

#if defined(OS_LINUX)
#include <gdk/gdk.h>
#endif

#include "app/gfx/canvas.h"
#include "skia/ext/skia_utils_win.h"
#include "views/background.h"

namespace views {

// Size of the divider in pixels.
static const int kDividerSize = 4;

SingleSplitView::SingleSplitView(View* leading,
                                 View* trailing,
                                 Orientation orientation)
    : is_horizontal_(orientation == HORIZONTAL_SPLIT),
      divider_offset_(-1) {
  AddChildView(leading);
  AddChildView(trailing);
  set_background(
      views::Background::CreateSolidBackground(
          skia::COLORREFToSkColor(GetSysColor(COLOR_3DFACE))));
}

void SingleSplitView::Layout() {
  if (GetChildViewCount() != 2)
    return;

  View* leading = GetChildViewAt(0);
  View* trailing = GetChildViewAt(1);
  if (divider_offset_ < 0)
    divider_offset_ = (GetPrimaryAxisSize() - kDividerSize) / 2;
  else
    divider_offset_ = std::min(divider_offset_,
                               GetPrimaryAxisSize() - kDividerSize);

  if (is_horizontal_) {
    leading->SetBounds(0, 0, divider_offset_, height());
    trailing->SetBounds(divider_offset_ + kDividerSize, 0,
                        width() - divider_offset_ - kDividerSize, height());
  } else {
    leading->SetBounds(0, 0, width(), divider_offset_);
    trailing->SetBounds(0, divider_offset_ + kDividerSize,
                        width(), height() - divider_offset_ - kDividerSize);
  }

  SchedulePaint();

  // Invoke super's implementation so that the children are layed out.
  View::Layout();
}

gfx::Size SingleSplitView::GetPreferredSize() {
  int width = 0;
  int height = 0;
  for (int i = 0; i < 2 && i < GetChildViewCount(); ++i) {
    View* view = GetChildViewAt(i);
    gfx::Size pref = view->GetPreferredSize();
    if (is_horizontal_) {
      width += pref.width();
      height = std::max(height, pref.height());
    } else {
      width = std::max(width, pref.width());
      height += pref.height();
    }
  }
  if (is_horizontal_)
    width += kDividerSize;
  else
    height += kDividerSize;
  return gfx::Size(width, height);
}

gfx::NativeCursor SingleSplitView::GetCursorForPoint(Event::EventType event_type,
                                                     int x, int y) {
  if (IsPointInDivider(x, y)) {
#if defined(OS_WIN)
    static HCURSOR resize_cursor = LoadCursor(NULL,
        is_horizontal_ ? IDC_SIZEWE : IDC_SIZENS);
    return resize_cursor;
#elif defined(OS_LINUX)
    return gdk_cursor_new(is_horizontal_ ?
                              GDK_SB_H_DOUBLE_ARROW :
                              GDK_SB_V_DOUBLE_ARROW);
#endif
  }
  return NULL;
}

bool SingleSplitView::OnMousePressed(const MouseEvent& event) {
  if (!IsPointInDivider(event.x(), event.y()))
    return false;
  drag_info_.initial_mouse_offset = GetPrimaryAxisSize(event.x(), event.y());
  drag_info_.initial_divider_offset = divider_offset_;
  return true;
}

bool SingleSplitView::OnMouseDragged(const MouseEvent& event) {
  if (GetChildViewCount() < 2)
    return false;

  int delta_offset = GetPrimaryAxisSize(event.x(), event.y()) -
      drag_info_.initial_mouse_offset;
  if (is_horizontal_ && UILayoutIsRightToLeft())
    delta_offset *= -1;
  // Honor the minimum size when resizing.
  gfx::Size min = GetChildViewAt(0)->GetMinimumSize();
  int new_size = std::max(GetPrimaryAxisSize(min.width(), min.height()),
                          drag_info_.initial_divider_offset + delta_offset);

  // And don't let the view get bigger than our width.
  new_size = std::min(GetPrimaryAxisSize() - kDividerSize, new_size);

  if (new_size != divider_offset_) {
    set_divider_offset(new_size);
    Layout();
  }
  return true;
}

void SingleSplitView::OnMouseReleased(const MouseEvent& event, bool canceled) {
  if (GetChildViewCount() < 2)
    return;

  if (canceled && drag_info_.initial_divider_offset != divider_offset_) {
    set_divider_offset(drag_info_.initial_divider_offset);
    Layout();
  }
}

bool SingleSplitView::IsPointInDivider(int x, int y) {
  if (GetChildViewCount() < 2)
    return false;

  int divider_relative_offset;
  if (is_horizontal_) {
    divider_relative_offset =
        x - GetChildViewAt(UILayoutIsRightToLeft() ? 1 : 0)->width();
  } else {
    divider_relative_offset = y - GetChildViewAt(0)->height();
  }
  return (divider_relative_offset >= 0 &&
      divider_relative_offset < kDividerSize);
}

}  // namespace views
<commit_msg>Add layout support for invisible SingleSplitView children.<commit_after>// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "views/controls/single_split_view.h"

#if defined(OS_LINUX)
#include <gdk/gdk.h>
#endif

#include "app/gfx/canvas.h"
#include "skia/ext/skia_utils_win.h"
#include "views/background.h"

namespace views {

// Size of the divider in pixels.
static const int kDividerSize = 4;

SingleSplitView::SingleSplitView(View* leading,
                                 View* trailing,
                                 Orientation orientation)
    : is_horizontal_(orientation == HORIZONTAL_SPLIT),
      divider_offset_(-1) {
  AddChildView(leading);
  AddChildView(trailing);
  set_background(
      views::Background::CreateSolidBackground(
          skia::COLORREFToSkColor(GetSysColor(COLOR_3DFACE))));
}

void SingleSplitView::Layout() {
  if (GetChildViewCount() != 2)
    return;

  View* leading = GetChildViewAt(0);
  View* trailing = GetChildViewAt(1);

  if (!leading->IsVisible() && !trailing->IsVisible())
    return;

  if (!trailing->IsVisible()) {
    leading->SetBounds(0, 0, width(), height());
  } else if (!leading->IsVisible()) {
    trailing->SetBounds(0, 0, width(), height());
  } else {
    if (divider_offset_ < 0)
      divider_offset_ = (GetPrimaryAxisSize() - kDividerSize) / 2;
    else
      divider_offset_ = std::min(divider_offset_,
                                 GetPrimaryAxisSize() - kDividerSize);

    if (is_horizontal_) {
      leading->SetBounds(0, 0, divider_offset_, height());
      trailing->SetBounds(divider_offset_ + kDividerSize, 0,
                          width() - divider_offset_ - kDividerSize, height());
    } else {
      leading->SetBounds(0, 0, width(), divider_offset_);
      trailing->SetBounds(0, divider_offset_ + kDividerSize,
                          width(), height() - divider_offset_ - kDividerSize);
    }
  }

  SchedulePaint();

  // Invoke super's implementation so that the children are layed out.
  View::Layout();
}

gfx::Size SingleSplitView::GetPreferredSize() {
  int width = 0;
  int height = 0;
  for (int i = 0; i < 2 && i < GetChildViewCount(); ++i) {
    View* view = GetChildViewAt(i);
    gfx::Size pref = view->GetPreferredSize();
    if (is_horizontal_) {
      width += pref.width();
      height = std::max(height, pref.height());
    } else {
      width = std::max(width, pref.width());
      height += pref.height();
    }
  }
  if (is_horizontal_)
    width += kDividerSize;
  else
    height += kDividerSize;
  return gfx::Size(width, height);
}

gfx::NativeCursor SingleSplitView::GetCursorForPoint(Event::EventType event_type,
                                                     int x, int y) {
  if (IsPointInDivider(x, y)) {
#if defined(OS_WIN)
    static HCURSOR resize_cursor = LoadCursor(NULL,
        is_horizontal_ ? IDC_SIZEWE : IDC_SIZENS);
    return resize_cursor;
#elif defined(OS_LINUX)
    return gdk_cursor_new(is_horizontal_ ?
                              GDK_SB_H_DOUBLE_ARROW :
                              GDK_SB_V_DOUBLE_ARROW);
#endif
  }
  return NULL;
}

bool SingleSplitView::OnMousePressed(const MouseEvent& event) {
  if (!IsPointInDivider(event.x(), event.y()))
    return false;
  drag_info_.initial_mouse_offset = GetPrimaryAxisSize(event.x(), event.y());
  drag_info_.initial_divider_offset = divider_offset_;
  return true;
}

bool SingleSplitView::OnMouseDragged(const MouseEvent& event) {
  if (GetChildViewCount() < 2)
    return false;

  int delta_offset = GetPrimaryAxisSize(event.x(), event.y()) -
      drag_info_.initial_mouse_offset;
  if (is_horizontal_ && UILayoutIsRightToLeft())
    delta_offset *= -1;
  // Honor the minimum size when resizing.
  gfx::Size min = GetChildViewAt(0)->GetMinimumSize();
  int new_size = std::max(GetPrimaryAxisSize(min.width(), min.height()),
                          drag_info_.initial_divider_offset + delta_offset);

  // And don't let the view get bigger than our width.
  new_size = std::min(GetPrimaryAxisSize() - kDividerSize, new_size);

  if (new_size != divider_offset_) {
    set_divider_offset(new_size);
    Layout();
  }
  return true;
}

void SingleSplitView::OnMouseReleased(const MouseEvent& event, bool canceled) {
  if (GetChildViewCount() < 2)
    return;

  if (canceled && drag_info_.initial_divider_offset != divider_offset_) {
    set_divider_offset(drag_info_.initial_divider_offset);
    Layout();
  }
}

bool SingleSplitView::IsPointInDivider(int x, int y) {
  if (GetChildViewCount() < 2)
    return false;

  if (!GetChildViewAt(0)->IsVisible() || !GetChildViewAt(1)->IsVisible())
    return false;

  int divider_relative_offset;
  if (is_horizontal_) {
    divider_relative_offset =
        x - GetChildViewAt(UILayoutIsRightToLeft() ? 1 : 0)->width();
  } else {
    divider_relative_offset = y - GetChildViewAt(0)->height();
  }
  return (divider_relative_offset >= 0 &&
      divider_relative_offset < kDividerSize);
}

}  // namespace views
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2005-2017 Centre National d'Etudes Spatiales (CNES)
 *
 * This file is part of Orfeo Toolbox
 *
 *     https://www.orfeo-toolbox.org/
 *
 * 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 "otbWrapperInputImageListParameter.h"
#include "itksys/SystemTools.hxx"

namespace otb
{
namespace Wrapper
{

InputImageListParameter::InputImageListParameter()
  : m_InputImageParameterVector(),
    m_ImageList(FloatVectorImageListType::New())
{
  this->SetName("Input Image List");
  this->SetKey("inList");
}

InputImageListParameter::~InputImageListParameter()
{
}

bool
InputImageListParameter::SetListFromFileName(const std::vector<std::string> & filenames)
{
  // First clear previous file chosen
  this->ClearValue();

  for(unsigned int i=0; i<filenames.size(); i++)
    {
    const std::string filename = filenames[i];

    // File existence checked by the reader
    if (!filename.empty())
      {
      // Try to build a new ParameterInputImage
      InputImageParameter::Pointer tmpInputImageParameter = InputImageParameter::New();
      tmpInputImageParameter->SetFromFileName(filename);

      m_InputImageParameterVector.push_back(tmpInputImageParameter);
      }
    }

  this->Modified();
  SetActive(true);
  return true;
}

void
InputImageListParameter
::AddNullElement()
{
  InsertNullElement();
}

void
InputImageListParameter
::InsertNullElement( int index )
{
  InputImageParameterVectorType::iterator it1( m_InputImageParameterVector.begin() );

  if( index>=0 )
    it1 += index;

  m_InputImageParameterVector.insert( it1, ITK_NULLPTR );

  SetActive( false );

  Modified();
}

bool
InputImageListParameter
::Insert( const std::string & filename, int index )
{
  InputImageParameter::Pointer image(
    InputImageParameter::New()
  );

  if( !filename.empty() )
    image->SetFromFileName( filename );

  m_InputImageParameterVector.insert(
    index<0
    ? m_InputImageParameterVector.end()
    : m_InputImageParameterVector.begin() + index,
    image
  );

  SetActive( true );

  Modified();

  return true;
}

bool
InputImageListParameter::AddFromFileName(const std::string & filename)
{
  // File existence checked by the reader
  if (!filename.empty())
    {
    InputImageParameter::Pointer tmpInputImageParameter = InputImageParameter::New();
    tmpInputImageParameter->SetFromFileName(filename);

    m_InputImageParameterVector.push_back(tmpInputImageParameter);

    this->Modified();
    SetActive(true);
    return true;
    }

  return false;
}

bool
InputImageListParameter
::SetNthFileName( const unsigned int id, const std::string & filename )
{
#if 0
  if( m_InputImageParameterVector.size()<id )
    {
    itkExceptionMacro(<< "No image "<<id<<". Only "<<m_InputImageParameterVector.size()<<" images available.");
    }

  // File existence checked by the reader
  if (!filename.empty())
    {
    InputImageParameter::Pointer tmpInputImageParameter = InputImageParameter::New();
    tmpInputImageParameter->SetFromFileName(filename);

    m_InputImageParameterVector[id] = tmpInputImageParameter;

    this->Modified();
    SetActive(true);
    return true;
    }

  return false;

#else
  assert( id<m_InputImageParameterVector.size() );
  assert( !m_InputImageParameterVector[ id ].IsNull() );

  if( !filename.empty() )
    {
    Modified();

    SetActive( true );
    }

  return m_InputImageParameterVector[ id ]->SetFromFileName( filename );
#endif
}

std::vector<std::string>
InputImageListParameter::GetFileNameList() const
{
  std::vector<std::string> filenames;

  for(InputImageParameterVectorType::const_iterator it = m_InputImageParameterVector.begin();
      it!=m_InputImageParameterVector.end();++it)
    {
    filenames.push_back( (*it)->GetFileName() );
    }

  return filenames;
}

const std::string &
InputImageListParameter::GetNthFileName( unsigned int i ) const
{
    if(m_InputImageParameterVector.size()<i)
      {
      itkExceptionMacro(<< "No image "<<i<<". Only "<<m_InputImageParameterVector.size()<<" images available.");
      }

    return m_InputImageParameterVector[i]->GetFileName();
}

FloatVectorImageListType*
InputImageListParameter::GetImageList() const
{
  m_ImageList->Clear();
  for (unsigned int i=0 ; i < this->Size() ; ++i)
    {
    m_ImageList->PushBack(m_InputImageParameterVector[i]->GetFloatVectorImage());
    }
  return m_ImageList;
}

FloatVectorImageType*
InputImageListParameter::GetNthImage(unsigned int i) const
{
  if(this->Size()<=i)
    {
    itkExceptionMacro(<< "No image "<<i<<". Only "<<this->Size()<<" images available.");
    }
  return m_InputImageParameterVector[i]->GetFloatVectorImage();
}

void
InputImageListParameter::SetImageList(FloatVectorImageListType* imList)
{
  // Check input availability
  for(unsigned int i = 0; i < imList->Size(); i++)
  {
    imList->GetNthElement(i)->UpdateOutputInformation();
  }

  // Clear previous values
  this->ClearValue();

  for(unsigned int i = 0; i<imList->Size(); i++)
    {
    // Try to build a new ParameterInputImage
    InputImageParameter::Pointer tmpInputImageParameter = InputImageParameter::New();

    tmpInputImageParameter->SetImage(imList->GetNthElement(i));

    m_InputImageParameterVector.push_back(tmpInputImageParameter);
    m_ImageList->PushBack(tmpInputImageParameter->GetFloatVectorImage());
    }

  SetActive(true);
  this->Modified();
}

void InputImageListParameter::SetNthImage(unsigned int i, ImageBaseType * img)
{
  if(this->Size()<i)
  {
    itkExceptionMacro(<< "No image "<<i<<". Only "<<this->Size()<<" images available.");
  }

  // Check input availability
  img->UpdateOutputInformation();

  // Try to build a new ParameterInputImage
  InputImageParameter::Pointer tmpInputImageParameter = InputImageParameter::New();

  tmpInputImageParameter->SetImage(img);

  m_InputImageParameterVector[i] = tmpInputImageParameter;
}

void
InputImageListParameter::AddImage(ImageBaseType* image)
{
  // Check input availability
  image->UpdateOutputInformation();

  InputImageParameter::Pointer tmpInputImageParameter = InputImageParameter::New();

  tmpInputImageParameter->SetImage(image);

  m_InputImageParameterVector.push_back(tmpInputImageParameter);

  this->Modified();
}

bool
InputImageListParameter::HasValue() const
{
  if(this->Size() == 0)
    {
    return false;
    }

  bool res(true);
  unsigned int i(0);
  while(i < this->Size() && res == true)
    {
    res = (m_InputImageParameterVector[i] ?
           m_InputImageParameterVector[i]->HasValue() :
           false);
    i++;
    }

  return res;
}

void
InputImageListParameter::Erase( unsigned int id )
{
  if(this->Size()<id)
    {
    itkExceptionMacro(<< "No image "<<id<<". Only "<<this->Size()<<" images available.");
    }

  m_InputImageParameterVector.erase(m_InputImageParameterVector.begin()+id);

  this->Modified();
}

void
InputImageListParameter
::Erase( unsigned int start, unsigned int count )
{
  assert( start<m_InputImageParameterVector.size() );
  assert( start+count<=m_InputImageParameterVector.size() );

  m_InputImageParameterVector.erase(
    m_InputImageParameterVector.begin() + start,
    m_InputImageParameterVector.begin() + start + count
  );

  Modified();
}

unsigned int
InputImageListParameter::Size() const
{
  return m_InputImageParameterVector.size();
}

void
InputImageListParameter::ClearValue()
{
  m_ImageList->Clear();
  m_InputImageParameterVector.clear();

  SetActive(false);
  this->Modified();
}

bool
InputImageListParameter
::IsActive( unsigned int i ) const
{
  assert( i<m_InputImageParameterVector.size() );
  assert( !m_InputImageParameterVector[ i ].IsNull() );

  return m_InputImageParameterVector[ i ]->GetActive();
}

const std::string &
InputImageListParameter
::GetToolTip( unsigned int i ) const
{
  assert( i<m_InputImageParameterVector.size() );
  assert( !m_InputImageParameterVector[ i ].IsNull() );

  return m_InputImageParameterVector[ i ]->GetDescription();
}

}

}
<commit_msg>ENH: Added description on image-parameter in InputImageListParameter.<commit_after>/*
 * Copyright (C) 2005-2017 Centre National d'Etudes Spatiales (CNES)
 *
 * This file is part of Orfeo Toolbox
 *
 *     https://www.orfeo-toolbox.org/
 *
 * 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 "otbWrapperInputImageListParameter.h"
#include "itksys/SystemTools.hxx"

namespace otb
{
namespace Wrapper
{

InputImageListParameter::InputImageListParameter()
  : m_InputImageParameterVector(),
    m_ImageList(FloatVectorImageListType::New())
{
  this->SetName("Input Image List");
  this->SetKey("inList");
}

InputImageListParameter::~InputImageListParameter()
{
}

bool
InputImageListParameter::SetListFromFileName(const std::vector<std::string> & filenames)
{
  // First clear previous file chosen
  this->ClearValue();

  for(unsigned int i=0; i<filenames.size(); i++)
    {
    const std::string filename = filenames[i];

    // File existence checked by the reader
    if (!filename.empty())
      {
      // Try to build a new ParameterInputImage
      InputImageParameter::Pointer tmpInputImageParameter = InputImageParameter::New();

      tmpInputImageParameter->SetFromFileName(filename);
      tmpInputImageParameter->SetDescription( "Image filename" );

      m_InputImageParameterVector.push_back(tmpInputImageParameter);
      }
    }

  this->Modified();
  SetActive(true);
  return true;
}

void
InputImageListParameter
::AddNullElement()
{
  InsertNullElement();
}

void
InputImageListParameter
::InsertNullElement( int index )
{
  InputImageParameterVectorType::iterator it1( m_InputImageParameterVector.begin() );

  if( index>=0 )
    it1 += index;

  m_InputImageParameterVector.insert( it1, ITK_NULLPTR );

  SetActive( false );

  Modified();
}

bool
InputImageListParameter
::Insert( const std::string & filename, int index )
{
  InputImageParameter::Pointer image(
    InputImageParameter::New()
  );

  image->SetDescription( "Image filename" );

  if( !filename.empty() )
    image->SetFromFileName( filename );

  m_InputImageParameterVector.insert(
    index<0
    ? m_InputImageParameterVector.end()
    : m_InputImageParameterVector.begin() + index,
    image
  );

  SetActive( true );

  Modified();

  return true;
}

bool
InputImageListParameter::AddFromFileName(const std::string & filename)
{
  // File existence checked by the reader
  if (!filename.empty())
    {
    InputImageParameter::Pointer tmpInputImageParameter = InputImageParameter::New();

    tmpInputImageParameter->SetFromFileName(filename);
    tmpInputImageParameter->SetDescription( "Image filename" );

    m_InputImageParameterVector.push_back(tmpInputImageParameter);

    this->Modified();
    SetActive(true);
    return true;
    }

  return false;
}

bool
InputImageListParameter
::SetNthFileName( const unsigned int id, const std::string & filename )
{
#if 0
  if( m_InputImageParameterVector.size()<id )
    {
    itkExceptionMacro(<< "No image "<<id<<". Only "<<m_InputImageParameterVector.size()<<" images available.");
    }

  // File existence checked by the reader
  if (!filename.empty())
    {
    InputImageParameter::Pointer tmpInputImageParameter = InputImageParameter::New();

    tmpInputImageParameter->SetFromFileName(filename);
    tmpInputImageParameter->SetDescription( "Image filename" );

    m_InputImageParameterVector[id] = tmpInputImageParameter;

    this->Modified();
    SetActive(true);
    return true;
    }

  return false;

#else
  assert( id<m_InputImageParameterVector.size() );
  assert( !m_InputImageParameterVector[ id ].IsNull() );

  if( !filename.empty() )
    {
    Modified();

    SetActive( true );
    }

  return m_InputImageParameterVector[ id ]->SetFromFileName( filename );
#endif
}

std::vector<std::string>
InputImageListParameter::GetFileNameList() const
{
  std::vector<std::string> filenames;

  for(InputImageParameterVectorType::const_iterator it = m_InputImageParameterVector.begin();
      it!=m_InputImageParameterVector.end();++it)
    {
    filenames.push_back( (*it)->GetFileName() );
    }

  return filenames;
}

const std::string &
InputImageListParameter::GetNthFileName( unsigned int i ) const
{
    if(m_InputImageParameterVector.size()<i)
      {
      itkExceptionMacro(<< "No image "<<i<<". Only "<<m_InputImageParameterVector.size()<<" images available.");
      }

    return m_InputImageParameterVector[i]->GetFileName();
}

FloatVectorImageListType*
InputImageListParameter::GetImageList() const
{
  m_ImageList->Clear();
  for (unsigned int i=0 ; i < this->Size() ; ++i)
    {
    m_ImageList->PushBack(m_InputImageParameterVector[i]->GetFloatVectorImage());
    }
  return m_ImageList;
}

FloatVectorImageType*
InputImageListParameter::GetNthImage(unsigned int i) const
{
  if(this->Size()<=i)
    {
    itkExceptionMacro(<< "No image "<<i<<". Only "<<this->Size()<<" images available.");
    }
  return m_InputImageParameterVector[i]->GetFloatVectorImage();
}

void
InputImageListParameter::SetImageList(FloatVectorImageListType* imList)
{
  // Check input availability
  for(unsigned int i = 0; i < imList->Size(); i++)
  {
    imList->GetNthElement(i)->UpdateOutputInformation();
  }

  // Clear previous values
  this->ClearValue();

  for(unsigned int i = 0; i<imList->Size(); i++)
    {
    // Try to build a new ParameterInputImage
    InputImageParameter::Pointer tmpInputImageParameter = InputImageParameter::New();

    tmpInputImageParameter->SetImage(imList->GetNthElement(i));
    tmpInputImageParameter->SetDescription( "Image filename" );

    m_InputImageParameterVector.push_back(tmpInputImageParameter);
    m_ImageList->PushBack(tmpInputImageParameter->GetFloatVectorImage());
    }

  SetActive(true);
  this->Modified();
}

void InputImageListParameter::SetNthImage(unsigned int i, ImageBaseType * img)
{
  if(this->Size()<i)
  {
    itkExceptionMacro(<< "No image "<<i<<". Only "<<this->Size()<<" images available.");
  }

  // Check input availability
  img->UpdateOutputInformation();

  // Try to build a new ParameterInputImage
  InputImageParameter::Pointer tmpInputImageParameter = InputImageParameter::New();

  tmpInputImageParameter->SetImage(img);
  tmpInputImageParameter->SetDescription( "Image filename" );

  m_InputImageParameterVector[i] = tmpInputImageParameter;
}

void
InputImageListParameter::AddImage(ImageBaseType* image)
{
  // Check input availability
  image->UpdateOutputInformation();

  InputImageParameter::Pointer tmpInputImageParameter = InputImageParameter::New();

  tmpInputImageParameter->SetImage(image);
  tmpInputImageParameter->SetDescription( "Image filename" );

  m_InputImageParameterVector.push_back(tmpInputImageParameter);

  this->Modified();
}

bool
InputImageListParameter::HasValue() const
{
  if(this->Size() == 0)
    {
    return false;
    }

  bool res(true);
  unsigned int i(0);
  while(i < this->Size() && res == true)
    {
    res = (m_InputImageParameterVector[i] ?
           m_InputImageParameterVector[i]->HasValue() :
           false);
    i++;
    }

  return res;
}

void
InputImageListParameter::Erase( unsigned int id )
{
  if(this->Size()<id)
    {
    itkExceptionMacro(<< "No image "<<id<<". Only "<<this->Size()<<" images available.");
    }

  m_InputImageParameterVector.erase(m_InputImageParameterVector.begin()+id);

  this->Modified();
}

void
InputImageListParameter
::Erase( unsigned int start, unsigned int count )
{
  assert( start<m_InputImageParameterVector.size() );
  assert( start+count<=m_InputImageParameterVector.size() );

  m_InputImageParameterVector.erase(
    m_InputImageParameterVector.begin() + start,
    m_InputImageParameterVector.begin() + start + count
  );

  Modified();
}

unsigned int
InputImageListParameter::Size() const
{
  return m_InputImageParameterVector.size();
}

void
InputImageListParameter::ClearValue()
{
  m_ImageList->Clear();
  m_InputImageParameterVector.clear();

  SetActive(false);
  this->Modified();
}

bool
InputImageListParameter
::IsActive( unsigned int i ) const
{
  assert( i<m_InputImageParameterVector.size() );
  assert( !m_InputImageParameterVector[ i ].IsNull() );

  return m_InputImageParameterVector[ i ]->GetActive();
}

const std::string &
InputImageListParameter
::GetToolTip( unsigned int i ) const
{
  assert( i<m_InputImageParameterVector.size() );
  assert( !m_InputImageParameterVector[ i ].IsNull() );

  return m_InputImageParameterVector[ i ]->GetDescription();
}

}

}
<|endoftext|>


<commit_before>#include <openll/layout/algorithm.h>

#include <random>
#include <algorithm>
#include <limits>

#include <openll/GlyphSequence.h>
#include <openll/FontFace.h>
#include <openll/layout/layoutbase.h>
#include <openll/layout/LabelArea.h>
#include <openll/Typesetter.h>

namespace gloperate_text
{

namespace layout
{

namespace
{

struct LabelCollision
{
    size_t index;
    size_t position;
    float overlapArea;
};

struct Index2D
{
    template<typename T>
    void next(const std::vector<std::vector<T>>& container)
    {
        ++inner;
        if (inner == container[outer].size())
        {
            ++outer;
            inner = 0;
        }
    }

    template<typename T>
    bool end(const std::vector<std::vector<T>>& container) const
    {
        return outer == container.size();
    }

    template<typename T>
    T& element(std::vector<std::vector<T>>& container) const
    {
        return container[outer][inner];
    }

    template<typename T>
    const T& element(const std::vector<std::vector<T>>& container) const
    {
        return container[outer][inner];
    }

    size_t outer = 0;
    size_t inner = 0;
};

std::vector<std::vector<std::vector<LabelCollision>>> createCollisionGraph(const std::vector<std::vector<LabelArea>>& labelAreas)
{
    std::vector<std::vector<std::vector<LabelCollision>>> collisionGraph;
    collisionGraph.resize(labelAreas.size());
    for (size_t i = 0; i < labelAreas.size(); ++i)
    {
        collisionGraph[i].resize(labelAreas[i].size());
    }

    for (Index2D index1; !index1.end(labelAreas); index1.next(labelAreas))
    {
        auto & collisionElement = index1.element(collisionGraph);
        for (Index2D index2; !index2.end(labelAreas); index2.next(labelAreas))
        {
            if (index1.outer == index2.outer)
                continue;
            const auto & label1 = index1.element(labelAreas);
            const auto & label2 = index2.element(labelAreas);
            if (label1.overlaps(label2))
            {
                auto area = label1.overlapArea(label2);
                collisionElement.push_back({index2.outer, index2.inner, area});
                assert(index1.element(collisionGraph).size() > 0);
            }
        }
    }
    return collisionGraph;
}

// generate LabelArea objects for all possible label placements
std::vector<std::vector<LabelArea>> computeLabelAreas(const std::vector<Label> & labels, const std::vector<RelativeLabelPosition>& positions)
{
    std::vector<std::vector<LabelArea>> result;
    for (const auto & label : labels)
    {
        result.push_back({});
        const auto extent = Typesetter::extent(label.sequence);
        for (const auto& position : positions)
        {
            const auto origin = labelOrigin(position, label.pointLocation, extent);
            result.back().push_back({origin, extent});
        }
    }
    return result;
}

std::vector<unsigned int> randomStartLabelAreas(const std::vector<std::vector<LabelArea>> & labelAreas)
{
    std::vector<unsigned int> result;
    std::default_random_engine generator;
    for (const auto & singleLabelAreas : labelAreas)
    {
        std::uniform_int_distribution<int> distribution(0, singleLabelAreas.size() - 1);
        result.push_back(distribution(generator));
    }
    return result;
}

template<typename T>
T randomIndexExcept(T except, T size, std::default_random_engine engine)
{
    std::uniform_int_distribution<T> positionDistribution(0, size - 1);
    T randomNumber;
    do
    {
        randomNumber = positionDistribution(engine);
    }
    while (randomNumber == except);
    return randomNumber;
}

}

void constant(std::vector<Label> & labels)
{
    for (auto & label : labels)
    {
        label.placement = {{0.f, 0.f}, Alignment::LeftAligned, LineAnchor::Bottom, true};
    }
}

void random(std::vector<Label> & labels)
{
    std::default_random_engine generator;
    std::bernoulli_distribution bool_distribution;
    for (auto & label : labels)
    {
        const auto extent = Typesetter::extent(label.sequence);
        glm::vec2 offset;
        offset.x = bool_distribution(generator) ? -extent.x : 0.f;
        offset.y = bool_distribution(generator) ? -extent.y : 0.f;
        label.placement = {offset, Alignment::LeftAligned, LineAnchor::Bottom, true};
    }
}

float overlapArea(int, float overlapArea, RelativeLabelPosition, unsigned int)
{
    return overlapArea;
}
float overlapCount(int overlapCount, float, RelativeLabelPosition, unsigned int)
{
    return overlapCount;
}

float standard(int, float overlapArea, RelativeLabelPosition position, unsigned int priority)
{
    unsigned int positionPenalty = 0;
    switch (position)
    {
        case RelativeLabelPosition::UpperRight: positionPenalty = 0; break;
        case RelativeLabelPosition::UpperLeft:  positionPenalty = 1; break;
        case RelativeLabelPosition::LowerLeft:  positionPenalty = 2; break;
        case RelativeLabelPosition::LowerRight: positionPenalty = 3; break;
        case RelativeLabelPosition::Hidden:     return priority * 3.f;
        default: assert(false);
    }
    return 10.f * overlapArea + .3f * positionPenalty;
}

void greedy(std::vector<Label> & labels, PenaltyFunction penaltyFunction)
{
    std::vector<LabelArea> labelAreas;
    const std::vector<RelativeLabelPosition> positions {
        RelativeLabelPosition::UpperRight, RelativeLabelPosition::UpperLeft,
        RelativeLabelPosition::LowerLeft, RelativeLabelPosition::LowerRight
    };
    for (auto & label : labels)
    {
        const auto extent = Typesetter::extent(label.sequence);
        float bestPenalty = std::numeric_limits<float>::max();
        glm::vec2 bestOrigin;
        for (const auto& position : positions)
        {
            const auto origin = labelOrigin(position, label.pointLocation, extent);
            const LabelArea newLabelArea {origin, extent};
            float overlapArea = 0.f;
            int overlapCount = 0;
            for (const auto& other : labelAreas)
            {
                overlapArea += newLabelArea.overlapArea(other);
                overlapCount += newLabelArea.overlaps(other) ? 1 : 0;
            }
            overlapArea /= newLabelArea.area();
            auto penalty = penaltyFunction(overlapCount, overlapArea, position, 1);
            if (penalty < bestPenalty)
            {
                bestPenalty = penalty;
                bestOrigin = origin;
            }
        }
        label.placement = {bestOrigin - label.pointLocation, Alignment::LeftAligned, LineAnchor::Bottom, true};
        labelAreas.push_back({bestOrigin, extent});
    }
}

void discreteGradientDescent(std::vector<Label> & labels, PenaltyFunction penaltyFunction)
{
    const std::vector<RelativeLabelPosition> positions {
        RelativeLabelPosition::UpperRight, RelativeLabelPosition::UpperLeft,
        RelativeLabelPosition::LowerLeft, RelativeLabelPosition::LowerRight
    };

    const std::vector<std::vector<LabelArea>> labelAreas = computeLabelAreas(labels, positions);
    std::vector<unsigned int> chosenLabels = randomStartLabelAreas(labelAreas);
    const auto collisionGraph = createCollisionGraph(labelAreas);
    const auto chosenLabel = [&](unsigned int i) { return labelAreas[i][chosenLabels[i]]; };

    // upper limit to iterations
    for (int iteration = 0; iteration < 1000; ++iteration)
    {
        float bestImprovement = 0.f;
        int bestLabelIndex = -1;
        int bestLabelPositionIndex = -1;
        size_t labelIndex = 0;
        for (auto & singleLabelAreas : labelAreas)
        {
            std::vector<float> penalties;
            int bestIndex = 0;
            for (size_t index = 0; index < singleLabelAreas.size(); ++index)
            {
                const auto & labelArea = singleLabelAreas[index];
                float overlapArea = 0.f;
                int overlapCount = 0;
                for (const auto & collison : collisionGraph[labelIndex][index])
                {
                    if (chosenLabels[collison.index] != collison.position)
                        continue;
                    ++overlapCount;
                    overlapArea += labelArea.overlapArea(chosenLabel(collison.index));
                }
                overlapArea /= labelArea.area();
                const auto penalty = penaltyFunction(overlapCount, overlapArea, positions[index], 1);
                penalties.push_back(penalty);
                if (penalty < penalties[bestIndex])
                {
                    bestIndex = index;
                }
            }
            float improvement = penalties[chosenLabels[labelIndex]] - penalties[bestIndex];
            if (improvement > bestImprovement)
            {
                bestImprovement = improvement;
                bestLabelIndex = labelIndex;
                bestLabelPositionIndex = bestIndex;
            }
            ++labelIndex;
        }
        // local minimum found
        if (bestImprovement == 0) break;
        chosenLabels[bestLabelIndex] = bestLabelPositionIndex;
    }

    for (size_t i = 0; i < labels.size(); ++i)
    {
        labels[i].placement = {chosenLabel(i).origin - labels[i].pointLocation, Alignment::LeftAligned, LineAnchor::Bottom, true};
    }
}

void simulatedAnnealing(std::vector<Label> & labels, PenaltyFunction penaltyFunction, bool allowSelection)
{
    // based on https://www.eecs.harvard.edu/shieber/Biblio/Papers/tog-final.pdf

    const std::vector<RelativeLabelPosition> positions {
        RelativeLabelPosition::UpperRight, RelativeLabelPosition::UpperLeft,
        RelativeLabelPosition::LowerLeft, RelativeLabelPosition::LowerRight
    };

    const std::vector<std::vector<LabelArea>> labelAreas = computeLabelAreas(labels, positions);
    std::vector<unsigned int> chosenLabels = randomStartLabelAreas(labelAreas);
    const auto collisionGraph = createCollisionGraph(labelAreas);
    const auto chosenLabel = [&](unsigned int i) { return labelAreas[i][chosenLabels[i]]; };

    std::default_random_engine generator;
    std::uniform_int_distribution<unsigned int> labelDistribution(0, labels.size() - 1);

    float temperature = 0.91023922662f;
    unsigned int temperatureChanges = 0;
    unsigned int changesAtTemperature = 0;
    unsigned int stepsAtTemperature = 0;

    while (true)
    {
        const auto labelIndex = labelDistribution(generator);
        const auto oldPosition = chosenLabels[labelIndex];
        const auto newPosition = randomIndexExcept<unsigned int>(oldPosition, labelAreas[labelIndex].size() + (allowSelection ? 1 : 0), generator);
        const auto oldHidden = oldPosition == labelAreas[labelIndex].size();
        const auto newHidden = newPosition == labelAreas[labelIndex].size();

        const auto computePenalty = [&](const LabelArea & labelArea, size_t position, bool hidden, unsigned int priority)
        {
            if (hidden)
                return penaltyFunction(0, 0.f, RelativeLabelPosition::Hidden, priority);
            float overlapArea = 0.f;
            int overlapCount = 0;
            for (const auto & collison : collisionGraph[labelIndex][position])
            {
                if (chosenLabels[collison.index] != collison.position)
                    continue;
                overlapArea += labelArea.overlapArea(chosenLabel(collison.index));
                ++overlapCount;
            }
            overlapArea /= labelArea.area();
            return penaltyFunction(overlapCount, overlapArea, positions[position], priority);
        };

        const auto oldPenalty = computePenalty(chosenLabel(labelIndex), oldPosition, oldHidden, labels[labelIndex].priority);
        const auto newPenalty = computePenalty(labelAreas[labelIndex][newPosition], newPosition, newHidden, labels[labelIndex].priority);
        const auto improvement = oldPenalty - newPenalty;

        float chance = std::exp(improvement / temperature);
        std::bernoulli_distribution doAnyway(chance);
        if (improvement > 0 || doAnyway(generator))
        {
            chosenLabels[labelIndex] = newPosition;
            ++changesAtTemperature;
        }

        ++stepsAtTemperature;
        if (changesAtTemperature > 5 * labels.size() || stepsAtTemperature > 20 * labels.size())
        {
            // converged
            if (changesAtTemperature == 0) break;
            if (temperatureChanges == 100) break;

            temperature *= 0.9f;
            changesAtTemperature = 0;
            stepsAtTemperature = 0;
            ++temperatureChanges;
        }
    }

    for (size_t i = 0; i < labels.size(); ++i)
    {
        const auto visible = chosenLabels[i] != labelAreas[i].size();
        const auto position = visible ? (chosenLabel(i).origin - labels[i].pointLocation) : glm::vec2(0.f);
        labels[i].placement = {position, Alignment::LeftAligned, LineAnchor::Bottom, visible};
    }
}

} // namespace layout

} // namespace gloperate_text
<commit_msg>Use precomputed overlap areas<commit_after>#include <openll/layout/algorithm.h>

#include <random>
#include <algorithm>
#include <limits>

#include <openll/GlyphSequence.h>
#include <openll/FontFace.h>
#include <openll/layout/layoutbase.h>
#include <openll/layout/LabelArea.h>
#include <openll/Typesetter.h>

namespace gloperate_text
{

namespace layout
{

namespace
{

struct LabelCollision
{
    size_t index;
    size_t position;
    float overlapArea;
};

struct Index2D
{
    template<typename T>
    void next(const std::vector<std::vector<T>>& container)
    {
        ++inner;
        if (inner == container[outer].size())
        {
            ++outer;
            inner = 0;
        }
    }

    template<typename T>
    bool end(const std::vector<std::vector<T>>& container) const
    {
        return outer == container.size();
    }

    template<typename T>
    T& element(std::vector<std::vector<T>>& container) const
    {
        return container[outer][inner];
    }

    template<typename T>
    const T& element(const std::vector<std::vector<T>>& container) const
    {
        return container[outer][inner];
    }

    size_t outer = 0;
    size_t inner = 0;
};

std::vector<std::vector<std::vector<LabelCollision>>> createCollisionGraph(const std::vector<std::vector<LabelArea>>& labelAreas)
{
    std::vector<std::vector<std::vector<LabelCollision>>> collisionGraph;
    collisionGraph.resize(labelAreas.size());
    for (size_t i = 0; i < labelAreas.size(); ++i)
    {
        collisionGraph[i].resize(labelAreas[i].size());
    }

    for (Index2D index1; !index1.end(labelAreas); index1.next(labelAreas))
    {
        auto & collisionElement = index1.element(collisionGraph);
        for (Index2D index2; !index2.end(labelAreas); index2.next(labelAreas))
        {
            if (index1.outer == index2.outer)
                continue;
            const auto & label1 = index1.element(labelAreas);
            const auto & label2 = index2.element(labelAreas);
            if (label1.overlaps(label2))
            {
                auto area = label1.overlapArea(label2);
                collisionElement.push_back({index2.outer, index2.inner, area});
                assert(index1.element(collisionGraph).size() > 0);
            }
        }
    }
    return collisionGraph;
}

// generate LabelArea objects for all possible label placements
std::vector<std::vector<LabelArea>> computeLabelAreas(const std::vector<Label> & labels, const std::vector<RelativeLabelPosition>& positions)
{
    std::vector<std::vector<LabelArea>> result;
    for (const auto & label : labels)
    {
        result.push_back({});
        const auto extent = Typesetter::extent(label.sequence);
        for (const auto& position : positions)
        {
            const auto origin = labelOrigin(position, label.pointLocation, extent);
            result.back().push_back({origin, extent});
        }
    }
    return result;
}

std::vector<unsigned int> randomStartLabelAreas(const std::vector<std::vector<LabelArea>> & labelAreas)
{
    std::vector<unsigned int> result;
    std::default_random_engine generator;
    for (const auto & singleLabelAreas : labelAreas)
    {
        std::uniform_int_distribution<int> distribution(0, singleLabelAreas.size() - 1);
        result.push_back(distribution(generator));
    }
    return result;
}

template<typename T>
T randomIndexExcept(T except, T size, std::default_random_engine engine)
{
    std::uniform_int_distribution<T> positionDistribution(0, size - 1);
    T randomNumber;
    do
    {
        randomNumber = positionDistribution(engine);
    }
    while (randomNumber == except);
    return randomNumber;
}

}

void constant(std::vector<Label> & labels)
{
    for (auto & label : labels)
    {
        label.placement = {{0.f, 0.f}, Alignment::LeftAligned, LineAnchor::Bottom, true};
    }
}

void random(std::vector<Label> & labels)
{
    std::default_random_engine generator;
    std::bernoulli_distribution bool_distribution;
    for (auto & label : labels)
    {
        const auto extent = Typesetter::extent(label.sequence);
        glm::vec2 offset;
        offset.x = bool_distribution(generator) ? -extent.x : 0.f;
        offset.y = bool_distribution(generator) ? -extent.y : 0.f;
        label.placement = {offset, Alignment::LeftAligned, LineAnchor::Bottom, true};
    }
}

float overlapArea(int, float overlapArea, RelativeLabelPosition, unsigned int)
{
    return overlapArea;
}
float overlapCount(int overlapCount, float, RelativeLabelPosition, unsigned int)
{
    return overlapCount;
}

float standard(int, float overlapArea, RelativeLabelPosition position, unsigned int priority)
{
    unsigned int positionPenalty = 0;
    switch (position)
    {
        case RelativeLabelPosition::UpperRight: positionPenalty = 0; break;
        case RelativeLabelPosition::UpperLeft:  positionPenalty = 1; break;
        case RelativeLabelPosition::LowerLeft:  positionPenalty = 2; break;
        case RelativeLabelPosition::LowerRight: positionPenalty = 3; break;
        case RelativeLabelPosition::Hidden:     return priority * 3.f;
        default: assert(false);
    }
    return 10.f * overlapArea + .3f * positionPenalty;
}

void greedy(std::vector<Label> & labels, PenaltyFunction penaltyFunction)
{
    std::vector<LabelArea> labelAreas;
    const std::vector<RelativeLabelPosition> positions {
        RelativeLabelPosition::UpperRight, RelativeLabelPosition::UpperLeft,
        RelativeLabelPosition::LowerLeft, RelativeLabelPosition::LowerRight
    };
    for (auto & label : labels)
    {
        const auto extent = Typesetter::extent(label.sequence);
        float bestPenalty = std::numeric_limits<float>::max();
        glm::vec2 bestOrigin;
        for (const auto& position : positions)
        {
            const auto origin = labelOrigin(position, label.pointLocation, extent);
            const LabelArea newLabelArea {origin, extent};
            float overlapArea = 0.f;
            int overlapCount = 0;
            for (const auto& other : labelAreas)
            {
                overlapArea += newLabelArea.overlapArea(other);
                overlapCount += newLabelArea.overlaps(other) ? 1 : 0;
            }
            overlapArea /= newLabelArea.area();
            auto penalty = penaltyFunction(overlapCount, overlapArea, position, 1);
            if (penalty < bestPenalty)
            {
                bestPenalty = penalty;
                bestOrigin = origin;
            }
        }
        label.placement = {bestOrigin - label.pointLocation, Alignment::LeftAligned, LineAnchor::Bottom, true};
        labelAreas.push_back({bestOrigin, extent});
    }
}

void discreteGradientDescent(std::vector<Label> & labels, PenaltyFunction penaltyFunction)
{
    const std::vector<RelativeLabelPosition> positions {
        RelativeLabelPosition::UpperRight, RelativeLabelPosition::UpperLeft,
        RelativeLabelPosition::LowerLeft, RelativeLabelPosition::LowerRight
    };

    const std::vector<std::vector<LabelArea>> labelAreas = computeLabelAreas(labels, positions);
    std::vector<unsigned int> chosenLabels = randomStartLabelAreas(labelAreas);
    const auto collisionGraph = createCollisionGraph(labelAreas);
    const auto chosenLabel = [&](unsigned int i) { return labelAreas[i][chosenLabels[i]]; };

    // upper limit to iterations
    for (int iteration = 0; iteration < 1000; ++iteration)
    {
        float bestImprovement = 0.f;
        int bestLabelIndex = -1;
        int bestLabelPositionIndex = -1;
        size_t labelIndex = 0;
        for (auto & singleLabelAreas : labelAreas)
        {
            std::vector<float> penalties;
            int bestIndex = 0;
            for (size_t index = 0; index < singleLabelAreas.size(); ++index)
            {
                const auto & labelArea = singleLabelAreas[index];
                float overlapArea = 0.f;
                int overlapCount = 0;
                for (const auto & collision : collisionGraph[labelIndex][index])
                {
                    if (chosenLabels[collision.index] != collision.position)
                        continue;
                    ++overlapCount;
                    overlapArea += collision.overlapArea;
                }
                overlapArea /= labelArea.area();
                const auto penalty = penaltyFunction(overlapCount, overlapArea, positions[index], 1);
                penalties.push_back(penalty);
                if (penalty < penalties[bestIndex])
                {
                    bestIndex = index;
                }
            }
            float improvement = penalties[chosenLabels[labelIndex]] - penalties[bestIndex];
            if (improvement > bestImprovement)
            {
                bestImprovement = improvement;
                bestLabelIndex = labelIndex;
                bestLabelPositionIndex = bestIndex;
            }
            ++labelIndex;
        }
        // local minimum found
        if (bestImprovement == 0) break;
        chosenLabels[bestLabelIndex] = bestLabelPositionIndex;
    }

    for (size_t i = 0; i < labels.size(); ++i)
    {
        labels[i].placement = {chosenLabel(i).origin - labels[i].pointLocation, Alignment::LeftAligned, LineAnchor::Bottom, true};
    }
}

void simulatedAnnealing(std::vector<Label> & labels, PenaltyFunction penaltyFunction, bool allowSelection)
{
    // based on https://www.eecs.harvard.edu/shieber/Biblio/Papers/tog-final.pdf

    const std::vector<RelativeLabelPosition> positions {
        RelativeLabelPosition::UpperRight, RelativeLabelPosition::UpperLeft,
        RelativeLabelPosition::LowerLeft, RelativeLabelPosition::LowerRight
    };

    const std::vector<std::vector<LabelArea>> labelAreas = computeLabelAreas(labels, positions);
    std::vector<unsigned int> chosenLabels = randomStartLabelAreas(labelAreas);
    const auto collisionGraph = createCollisionGraph(labelAreas);
    const auto chosenLabel = [&](unsigned int i) { return labelAreas[i][chosenLabels[i]]; };

    std::default_random_engine generator;
    std::uniform_int_distribution<unsigned int> labelDistribution(0, labels.size() - 1);

    float temperature = 0.91023922662f;
    unsigned int temperatureChanges = 0;
    unsigned int changesAtTemperature = 0;
    unsigned int stepsAtTemperature = 0;

    while (true)
    {
        const auto labelIndex = labelDistribution(generator);
        const auto oldPosition = chosenLabels[labelIndex];
        const auto newPosition = randomIndexExcept<unsigned int>(oldPosition, labelAreas[labelIndex].size() + (allowSelection ? 1 : 0), generator);
        const auto oldHidden = oldPosition == labelAreas[labelIndex].size();
        const auto newHidden = newPosition == labelAreas[labelIndex].size();

        const auto computePenalty = [&](const LabelArea & labelArea, size_t position, bool hidden, unsigned int priority)
        {
            if (hidden)
                return penaltyFunction(0, 0.f, RelativeLabelPosition::Hidden, priority);
            float overlapArea = 0.f;
            int overlapCount = 0;
            for (const auto & collision : collisionGraph[labelIndex][position])
            {
                if (chosenLabels[collision.index] != collision.position)
                    continue;
                overlapArea += collision.overlapArea;
                ++overlapCount;
            }
            overlapArea /= labelArea.area();
            return penaltyFunction(overlapCount, overlapArea, positions[position], priority);
        };

        const auto oldPenalty = computePenalty(chosenLabel(labelIndex), oldPosition, oldHidden, labels[labelIndex].priority);
        const auto newPenalty = computePenalty(labelAreas[labelIndex][newPosition], newPosition, newHidden, labels[labelIndex].priority);
        const auto improvement = oldPenalty - newPenalty;

        float chance = std::exp(improvement / temperature);
        std::bernoulli_distribution doAnyway(chance);
        if (improvement > 0 || doAnyway(generator))
        {
            chosenLabels[labelIndex] = newPosition;
            ++changesAtTemperature;
        }

        ++stepsAtTemperature;
        if (changesAtTemperature > 5 * labels.size() || stepsAtTemperature > 20 * labels.size())
        {
            // converged
            if (changesAtTemperature == 0) break;
            if (temperatureChanges == 100) break;

            temperature *= 0.9f;
            changesAtTemperature = 0;
            stepsAtTemperature = 0;
            ++temperatureChanges;
        }
    }

    for (size_t i = 0; i < labels.size(); ++i)
    {
        const auto visible = chosenLabels[i] != labelAreas[i].size();
        const auto position = visible ? (chosenLabel(i).origin - labels[i].pointLocation) : glm::vec2(0.f);
        labels[i].placement = {position, Alignment::LeftAligned, LineAnchor::Bottom, visible};
    }
}

} // namespace layout

} // namespace gloperate_text
<|endoftext|>


<commit_before>////////////////////////////////////////////////////////////////////////////
//
// Copyright 2016 Realm Inc.
//
// 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 REALM_OS_SYNC_MANAGER_HPP
#define REALM_OS_SYNC_MANAGER_HPP

#include "shared_realm.hpp"

#include <realm/sync/client.hpp>
#include <realm/util/logger.hpp>
#include <realm/util/optional.hpp>

#include <memory>
#include <mutex>
#include <unordered_map>

namespace realm {

struct SyncConfig;
class SyncSession;
class SyncUser;
class SyncFileManager;
class SyncMetadataManager;

namespace _impl {
struct SyncClient;
}

enum class SyncSessionStopPolicy {
    Immediately,                    // Immediately stop the session as soon as all Realms/Sessions go out of scope.
    LiveIndefinitely,               // Never stop the session.
    AfterChangesUploaded,           // Once all Realms/Sessions go out of scope, wait for uploads to complete and stop.
};

class SyncLoggerFactory {
public:
    virtual std::unique_ptr<util::Logger> make_logger(util::Logger::Level) = 0;
};

class SyncManager {
friend class SyncSession;
public:
    enum class MetadataMode {
        NoEncryption,                   // Enable metadata, but disable encryption.
        Encryption,                     // Enable metadata, and use encryption (automatic if possible).
        NoMetadata,                     // Disable metadata.
    };

    static SyncManager& shared();

    // Configure the metadata and file management subsystems. This MUST be called upon startup.
    void configure_file_system(const std::string& base_file_path,
                               MetadataMode metadata_mode=MetadataMode::Encryption,
                               util::Optional<std::vector<char>> custom_encryption_key=none,
                               bool reset_metadata_on_error=false);

    // Configure the log level for the sync client. This can only be configured before the client is started.
    void set_log_level(util::Logger::Level) noexcept;

    // Configure a custom logger factory. If not provided, a realm::util::StderrLogger will be used.
    void set_logger_factory(SyncLoggerFactory&) noexcept;
    void set_error_handler(std::function<sync::Client::ErrorHandler>);

    /// Control whether the sync client attempts to reconnect immediately. Only set this to `true` for testing purposes.
    void set_client_should_reconnect_immediately(bool reconnect_immediately);
    bool client_should_reconnect_immediately() const noexcept;

    /// Control whether the sync client validates SSL certificates. Should *always* be `true` in production use.
    void set_client_should_validate_ssl(bool validate_ssl);
    bool client_should_validate_ssl() const noexcept;

    util::Logger::Level log_level() const noexcept;

    std::shared_ptr<SyncSession> get_session(const std::string& path, const SyncConfig& config);
    std::shared_ptr<SyncSession> get_existing_active_session(const std::string& path) const;

    // If the metadata manager is configured, perform an update. Returns `true` iff the code was run.
    bool perform_metadata_update(std::function<void(const SyncMetadataManager&)> update_function) const;

    // Get a sync user for a given identity, or create one if none exists yet, and set its token.
    // If a logged-out user exists, it will marked as logged back in.
    std::shared_ptr<SyncUser> get_user(const std::string& identity,
                                       std::string refresh_token,
                                       util::Optional<std::string> auth_server_url=none,
                                       bool is_admin=false);
    // Get an existing user for a given identity, if one exists and is logged in.
    std::shared_ptr<SyncUser> get_existing_logged_in_user(const std::string& identity) const;
    // Get all the users.
    std::vector<std::shared_ptr<SyncUser>> all_users() const;

    // Get the default path for a Realm for the given user and absolute unresolved URL.
    std::string path_for_realm(const std::string& user_identity, const std::string& raw_realm_url) const;

    // Explicitly control when the Sync Client is started. Otherwise it will be started automatically
    // when the first session is created.
    void start_sync_client() const;

    // Reset part of the singleton state for testing purposes. DO NOT CALL OUTSIDE OF TESTING CODE.
    void reset_for_testing();

private:
    void dropped_last_reference_to_session(SyncSession*);

    // Stop tracking the session for the given path if it is inactive.
    // No-op if the session is either still active or in the active sessions list
    // due to someone holding a strong reference to it.
    void unregister_session(const std::string& path);

    SyncManager() = default;
    SyncManager(const SyncManager&) = delete;
    SyncManager& operator=(const SyncManager&) = delete;

    std::shared_ptr<_impl::SyncClient> get_sync_client() const;
    std::shared_ptr<_impl::SyncClient> create_sync_client() const;

    std::shared_ptr<SyncSession> get_existing_active_session_locked(const std::string& path) const;
    std::unique_ptr<SyncSession> get_existing_inactive_session_locked(const std::string& path);

    mutable std::mutex m_mutex;

    // FIXME: Should probably be util::Logger::Level::error
    util::Logger::Level m_log_level = util::Logger::Level::info;
    SyncLoggerFactory* m_logger_factory = nullptr;
    std::function<sync::Client::ErrorHandler> m_error_handler;
    sync::Client::Reconnect m_client_reconnect_mode = sync::Client::Reconnect::normal;
    bool m_client_validate_ssl = true;

    // Protects m_users
    mutable std::mutex m_user_mutex;

    // A map of user identities to (shared pointers to) SyncUser objects.
    std::unordered_map<std::string, std::shared_ptr<SyncUser>> m_users;

    mutable std::shared_ptr<_impl::SyncClient> m_sync_client;

    // Protects m_active_sessions and m_inactive_sessions
    mutable std::mutex m_session_mutex;

    // Protects m_file_manager and m_metadata_manager
    mutable std::mutex m_file_system_mutex;
    std::unique_ptr<SyncFileManager> m_file_manager;
    std::unique_ptr<SyncMetadataManager> m_metadata_manager;

    // Active sessions are sessions which the client code holds a strong
    // reference to. When the last strong reference is released, the session is
    // moved to inactive sessions. Inactive sessions are promoted back to active
    // sessions until the session itself calls unregister_session to remove
    // itself from inactive sessions once it's done with whatever async cleanup
    // it needs to do.
    std::unordered_map<std::string, std::weak_ptr<SyncSession>> m_active_sessions;
    std::unordered_map<std::string, std::unique_ptr<SyncSession>> m_inactive_sessions;
};

} // namespace realm

#endif // REALM_OS_SYNC_MANAGER_HPP
<commit_msg>Update comments<commit_after>////////////////////////////////////////////////////////////////////////////
//
// Copyright 2016 Realm Inc.
//
// 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 REALM_OS_SYNC_MANAGER_HPP
#define REALM_OS_SYNC_MANAGER_HPP

#include "shared_realm.hpp"

#include <realm/sync/client.hpp>
#include <realm/util/logger.hpp>
#include <realm/util/optional.hpp>

#include <memory>
#include <mutex>
#include <unordered_map>

namespace realm {

struct SyncConfig;
class SyncSession;
class SyncUser;
class SyncFileManager;
class SyncMetadataManager;

namespace _impl {
struct SyncClient;
}

enum class SyncSessionStopPolicy {
    Immediately,                    // Immediately stop the session as soon as all Realms/Sessions go out of scope.
    LiveIndefinitely,               // Never stop the session.
    AfterChangesUploaded,           // Once all Realms/Sessions go out of scope, wait for uploads to complete and stop.
};

class SyncLoggerFactory {
public:
    virtual std::unique_ptr<util::Logger> make_logger(util::Logger::Level) = 0;
};

class SyncManager {
friend class SyncSession;
public:
    enum class MetadataMode {
        NoEncryption,                   // Enable metadata, but disable encryption.
        Encryption,                     // Enable metadata, and use encryption (automatic if possible).
        NoMetadata,                     // Disable metadata.
    };

    static SyncManager& shared();

    // Configure the metadata and file management subsystems. This MUST be called upon startup.
    void configure_file_system(const std::string& base_file_path,
                               MetadataMode metadata_mode=MetadataMode::Encryption,
                               util::Optional<std::vector<char>> custom_encryption_key=none,
                               bool reset_metadata_on_error=false);

    // Configure the log level for the sync client. This can only be configured before the client is started.
    void set_log_level(util::Logger::Level) noexcept;

    // Configure a custom logger factory. If not provided, a realm::util::StderrLogger will be used.
    void set_logger_factory(SyncLoggerFactory&) noexcept;
    void set_error_handler(std::function<sync::Client::ErrorHandler>);

    /// Control whether the sync client attempts to reconnect immediately. Only set this to `true` for testing purposes.
    void set_client_should_reconnect_immediately(bool reconnect_immediately);
    bool client_should_reconnect_immediately() const noexcept;

    /// Control whether the sync client validates SSL certificates. Should *always* be `true` in production use.
    void set_client_should_validate_ssl(bool validate_ssl);
    bool client_should_validate_ssl() const noexcept;

    util::Logger::Level log_level() const noexcept;

    std::shared_ptr<SyncSession> get_session(const std::string& path, const SyncConfig& config);
    std::shared_ptr<SyncSession> get_existing_active_session(const std::string& path) const;

    // If the metadata manager is configured, perform an update. Returns `true` iff the code was run.
    bool perform_metadata_update(std::function<void(const SyncMetadataManager&)> update_function) const;

    // Get a sync user for a given identity, or create one if none exists yet, and set its token.
    // If a logged-out user exists, it will be marked as logged back in.
    std::shared_ptr<SyncUser> get_user(const std::string& identity,
                                       std::string refresh_token,
                                       util::Optional<std::string> auth_server_url=none,
                                       bool is_admin=false);
    // Get an existing user for a given identity, if one exists and is logged in.
    // A user is considered logged in until explicitly logged out, even if
    // the users refresh_token has expired.
    std::shared_ptr<SyncUser> get_existing_logged_in_user(const std::string& identity) const;
    // Get all the users.
    std::vector<std::shared_ptr<SyncUser>> all_users() const;

    // Get the default path for a Realm for the given user and absolute unresolved URL.
    std::string path_for_realm(const std::string& user_identity, const std::string& raw_realm_url) const;

    // Explicitly control when the Sync Client is started. Otherwise it will be started automatically
    // when the first session is created.
    void start_sync_client() const;

    // Reset part of the singleton state for testing purposes. DO NOT CALL OUTSIDE OF TESTING CODE.
    void reset_for_testing();

private:
    void dropped_last_reference_to_session(SyncSession*);

    // Stop tracking the session for the given path if it is inactive.
    // No-op if the session is either still active or in the active sessions list
    // due to someone holding a strong reference to it.
    void unregister_session(const std::string& path);

    SyncManager() = default;
    SyncManager(const SyncManager&) = delete;
    SyncManager& operator=(const SyncManager&) = delete;

    std::shared_ptr<_impl::SyncClient> get_sync_client() const;
    std::shared_ptr<_impl::SyncClient> create_sync_client() const;

    std::shared_ptr<SyncSession> get_existing_active_session_locked(const std::string& path) const;
    std::unique_ptr<SyncSession> get_existing_inactive_session_locked(const std::string& path);

    mutable std::mutex m_mutex;

    // FIXME: Should probably be util::Logger::Level::error
    util::Logger::Level m_log_level = util::Logger::Level::info;
    SyncLoggerFactory* m_logger_factory = nullptr;
    std::function<sync::Client::ErrorHandler> m_error_handler;
    sync::Client::Reconnect m_client_reconnect_mode = sync::Client::Reconnect::normal;
    bool m_client_validate_ssl = true;

    // Protects m_users
    mutable std::mutex m_user_mutex;

    // A map of user identities to (shared pointers to) SyncUser objects.
    std::unordered_map<std::string, std::shared_ptr<SyncUser>> m_users;

    mutable std::shared_ptr<_impl::SyncClient> m_sync_client;

    // Protects m_active_sessions and m_inactive_sessions
    mutable std::mutex m_session_mutex;

    // Protects m_file_manager and m_metadata_manager
    mutable std::mutex m_file_system_mutex;
    std::unique_ptr<SyncFileManager> m_file_manager;
    std::unique_ptr<SyncMetadataManager> m_metadata_manager;

    // Active sessions are sessions which the client code holds a strong
    // reference to. When the last strong reference is released, the session is
    // moved to inactive sessions. Inactive sessions are promoted back to active
    // sessions until the session itself calls unregister_session to remove
    // itself from inactive sessions once it's done with whatever async cleanup
    // it needs to do.
    std::unordered_map<std::string, std::weak_ptr<SyncSession>> m_active_sessions;
    std::unordered_map<std::string, std::unique_ptr<SyncSession>> m_inactive_sessions;
};

} // namespace realm

#endif // REALM_OS_SYNC_MANAGER_HPP
<|endoftext|>