<commit_before>// Copyright (c) 2011 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 "content/browser/renderer_host/database_message_filter.h"

#include <string>

#include "base/platform_file.h"
#include "base/string_util.h"
#include "base/threading/thread.h"
#include "base/utf_string_conversions.h"
#include "content/browser/user_metrics.h"
#include "content/common/database_messages.h"
#include "content/common/result_codes.h"
#include "googleurl/src/gurl.h"
#include "third_party/sqlite/sqlite3.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebSecurityOrigin.h"
#include "webkit/database/database_util.h"
#include "webkit/database/vfs_backend.h"
#include "webkit/quota/quota_manager.h"

#if defined(OS_POSIX)
#include "base/file_descriptor_posix.h"
#endif

using quota::QuotaManager;
using quota::QuotaManagerProxy;
using quota::QuotaStatusCode;
using WebKit::WebSecurityOrigin;
using webkit_database::DatabaseTracker;
using webkit_database::DatabaseUtil;
using webkit_database::VfsBackend;

namespace {

class MyGetUsageAndQuotaCallback
    : public QuotaManager::GetUsageAndQuotaCallback  {
 public:
  MyGetUsageAndQuotaCallback(
      DatabaseMessageFilter* sender, IPC::Message* reply_msg)
      : sender_(sender), reply_msg_(reply_msg) {}

  virtual void RunWithParams(
        const Tuple3<QuotaStatusCode, int64, int64>& params) {
    Run(params.a, params.b, params.c);
  }

  void Run(QuotaStatusCode status, int64 usage, int64 quota) {
    int64 available = 0;
    if ((status == quota::kQuotaStatusOk) && (usage < quota))
      available = quota - usage;
    DatabaseHostMsg_GetSpaceAvailable::WriteReplyParams(
        reply_msg_.get(), available);
    sender_->Send(reply_msg_.release());
  }

 private:
  scoped_refptr<DatabaseMessageFilter> sender_;
  scoped_ptr<IPC::Message> reply_msg_;
};

const int kNumDeleteRetries = 2;
const int kDelayDeleteRetryMs = 100;

}  // namespace

DatabaseMessageFilter::DatabaseMessageFilter(
    webkit_database::DatabaseTracker* db_tracker)
    : db_tracker_(db_tracker),
      observer_added_(false) {
  DCHECK(db_tracker_);
}

void DatabaseMessageFilter::OnChannelClosing() {
  BrowserMessageFilter::OnChannelClosing();
  if (observer_added_) {
    observer_added_ = false;
    BrowserThread::PostTask(
        BrowserThread::FILE, FROM_HERE,
        NewRunnableMethod(this, &DatabaseMessageFilter::RemoveObserver));
  }
}

void DatabaseMessageFilter::AddObserver() {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
  db_tracker_->AddObserver(this);
}

void DatabaseMessageFilter::RemoveObserver() {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
  db_tracker_->RemoveObserver(this);

  // If the renderer process died without closing all databases,
  // then we need to manually close those connections
  db_tracker_->CloseDatabases(database_connections_);
  database_connections_.RemoveAllConnections();
}

void DatabaseMessageFilter::OverrideThreadForMessage(
    const IPC::Message& message,
    BrowserThread::ID* thread) {
  if (message.type() == DatabaseHostMsg_GetSpaceAvailable::ID)
    *thread = BrowserThread::IO;
  else if (IPC_MESSAGE_CLASS(message) == DatabaseMsgStart)
    *thread = BrowserThread::FILE;

  if (message.type() == DatabaseHostMsg_Opened::ID && !observer_added_) {
    observer_added_ = true;
    BrowserThread::PostTask(
        BrowserThread::FILE, FROM_HERE,
        NewRunnableMethod(this, &DatabaseMessageFilter::AddObserver));
  }
}

bool DatabaseMessageFilter::OnMessageReceived(
    const IPC::Message& message,
    bool* message_was_ok) {
  bool handled = true;
  IPC_BEGIN_MESSAGE_MAP_EX(DatabaseMessageFilter, message, *message_was_ok)
    IPC_MESSAGE_HANDLER_DELAY_REPLY(DatabaseHostMsg_OpenFile,
                                    OnDatabaseOpenFile)
    IPC_MESSAGE_HANDLER_DELAY_REPLY(DatabaseHostMsg_DeleteFile,
                                    OnDatabaseDeleteFile)
    IPC_MESSAGE_HANDLER_DELAY_REPLY(DatabaseHostMsg_GetFileAttributes,
                                    OnDatabaseGetFileAttributes)
    IPC_MESSAGE_HANDLER_DELAY_REPLY(DatabaseHostMsg_GetFileSize,
                                    OnDatabaseGetFileSize)
    IPC_MESSAGE_HANDLER_DELAY_REPLY(DatabaseHostMsg_GetSpaceAvailable,
                                    OnDatabaseGetSpaceAvailable)
    IPC_MESSAGE_HANDLER(DatabaseHostMsg_Opened, OnDatabaseOpened)
    IPC_MESSAGE_HANDLER(DatabaseHostMsg_Modified, OnDatabaseModified)
    IPC_MESSAGE_HANDLER(DatabaseHostMsg_Closed, OnDatabaseClosed)
    IPC_MESSAGE_UNHANDLED(handled = false)
  IPC_END_MESSAGE_MAP_EX()
  return handled;
}

DatabaseMessageFilter::~DatabaseMessageFilter() {
}

void DatabaseMessageFilter::OnDatabaseOpenFile(const string16& vfs_file_name,
                                               int desired_flags,
                                               IPC::Message* reply_msg) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
  base::PlatformFile file_handle = base::kInvalidPlatformFileValue;
  IPC::PlatformFileForTransit target_handle =
      IPC::InvalidPlatformFileForTransit();
  string16 origin_identifier;
  string16 database_name;

  // When in incognito mode, we want to make sure that all DB files are
  // removed when the incognito profile goes away, so we add the
  // SQLITE_OPEN_DELETEONCLOSE flag when opening all files, and keep
  // open handles to them in the database tracker to make sure they're
  // around for as long as needed.
  if (vfs_file_name.empty()) {
    VfsBackend::OpenTempFileInDirectory(db_tracker_->DatabaseDirectory(),
                                        desired_flags, &file_handle);
  } else if (DatabaseUtil::CrackVfsFileName(vfs_file_name, &origin_identifier,
                                            &database_name, NULL) &&
             !db_tracker_->IsDatabaseScheduledForDeletion(origin_identifier,
                                                          database_name)) {
      FilePath db_file =
          DatabaseUtil::GetFullFilePathForVfsFile(db_tracker_, vfs_file_name);
      if (!db_file.empty()) {
        if (db_tracker_->IsIncognitoProfile()) {
          db_tracker_->GetIncognitoFileHandle(vfs_file_name, &file_handle);
          if (file_handle == base::kInvalidPlatformFileValue) {
            VfsBackend::OpenFile(db_file,
                                 desired_flags | SQLITE_OPEN_DELETEONCLOSE,
                                 &file_handle);
            db_tracker_->SaveIncognitoFileHandle(vfs_file_name, file_handle);
          }
        } else {
          VfsBackend::OpenFile(db_file, desired_flags, &file_handle);
        }
      }
  }

  // Then we duplicate the file handle to make it useable in the renderer
  // process. The original handle is closed, unless we saved it in the
  // database tracker.
  bool auto_close = !db_tracker_->HasSavedIncognitoFileHandle(vfs_file_name);
  DCHECK_NE(base::kInvalidPlatformFileValue, file_handle);
  target_handle =
      IPC::GetFileHandleForProcess(file_handle, peer_handle(), auto_close);

  DatabaseHostMsg_OpenFile::WriteReplyParams(reply_msg, target_handle);
  Send(reply_msg);
}

void DatabaseMessageFilter::OnDatabaseDeleteFile(const string16& vfs_file_name,
                                                 const bool& sync_dir,
                                                 IPC::Message* reply_msg) {
  DatabaseDeleteFile(vfs_file_name, sync_dir, reply_msg, kNumDeleteRetries);
}

void DatabaseMessageFilter::DatabaseDeleteFile(const string16& vfs_file_name,
                                               bool sync_dir,
                                               IPC::Message* reply_msg,
                                               int reschedule_count) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));

  // Return an error if the file name is invalid or if the file could not
  // be deleted after kNumDeleteRetries attempts.
  int error_code = SQLITE_IOERR_DELETE;
  FilePath db_file =
      DatabaseUtil::GetFullFilePathForVfsFile(db_tracker_, vfs_file_name);
  if (!db_file.empty()) {
    // In order to delete a journal file in incognito mode, we only need to
    // close the open handle to it that's stored in the database tracker.
    if (db_tracker_->IsIncognitoProfile()) {
      const string16 wal_suffix(ASCIIToUTF16("-wal"));
      string16 sqlite_suffix;

      // WAL files can be deleted without having previously been opened.
      if (!db_tracker_->HasSavedIncognitoFileHandle(vfs_file_name) &&
          DatabaseUtil::CrackVfsFileName(vfs_file_name,
                                         NULL, NULL, &sqlite_suffix) &&
          sqlite_suffix == wal_suffix) {
        error_code = SQLITE_OK;
      } else if (db_tracker_->CloseIncognitoFileHandle(vfs_file_name)) {
        error_code = SQLITE_OK;
      }
    } else {
      error_code = VfsBackend::DeleteFile(db_file, sync_dir);
    }

    if ((error_code == SQLITE_IOERR_DELETE) && reschedule_count) {
      // If the file could not be deleted, try again.
      BrowserThread::PostDelayedTask(
          BrowserThread::FILE, FROM_HERE,
          NewRunnableMethod(this,
                            &DatabaseMessageFilter::DatabaseDeleteFile,
                            vfs_file_name,
                            sync_dir,
                            reply_msg,
                            reschedule_count - 1),
          kDelayDeleteRetryMs);
      return;
    }
  }

  DatabaseHostMsg_DeleteFile::WriteReplyParams(reply_msg, error_code);
  Send(reply_msg);
}

void DatabaseMessageFilter::OnDatabaseGetFileAttributes(
    const string16& vfs_file_name,
    IPC::Message* reply_msg) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
  int32 attributes = -1;
  FilePath db_file =
      DatabaseUtil::GetFullFilePathForVfsFile(db_tracker_, vfs_file_name);
  if (!db_file.empty())
    attributes = VfsBackend::GetFileAttributes(db_file);

  DatabaseHostMsg_GetFileAttributes::WriteReplyParams(
      reply_msg, attributes);
  Send(reply_msg);
}

void DatabaseMessageFilter::OnDatabaseGetFileSize(
    const string16& vfs_file_name, IPC::Message* reply_msg) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
  int64 size = 0;
  FilePath db_file =
      DatabaseUtil::GetFullFilePathForVfsFile(db_tracker_, vfs_file_name);
  if (!db_file.empty())
    size = VfsBackend::GetFileSize(db_file);

  DatabaseHostMsg_GetFileSize::WriteReplyParams(reply_msg, size);
  Send(reply_msg);
}

void DatabaseMessageFilter::OnDatabaseGetSpaceAvailable(
    const string16& origin_identifier, IPC::Message* reply_msg) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
  DCHECK(db_tracker_->quota_manager_proxy());

  QuotaManager* quota_manager =
      db_tracker_->quota_manager_proxy()->quota_manager();
  if (!quota_manager) {
    NOTREACHED();  // The system is shutting down, messages are unexpected.
    DatabaseHostMsg_GetSpaceAvailable::WriteReplyParams(
        reply_msg, static_cast<int64>(0));
    Send(reply_msg);
    return;
  }

  quota_manager->GetUsageAndQuota(
      DatabaseUtil::GetOriginFromIdentifier(origin_identifier),
      quota::kStorageTypeTemporary,
      new MyGetUsageAndQuotaCallback(this, reply_msg));
}

void DatabaseMessageFilter::OnDatabaseOpened(const string16& origin_identifier,
                                             const string16& database_name,
                                             const string16& description,
                                             int64 estimated_size) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
  int64 database_size = 0;
  db_tracker_->DatabaseOpened(origin_identifier, database_name, description,
                              estimated_size, &database_size);
  database_connections_.AddConnection(origin_identifier, database_name);
  Send(new DatabaseMsg_UpdateSize(origin_identifier, database_name,
                                  database_size));
}

void DatabaseMessageFilter::OnDatabaseModified(
    const string16& origin_identifier,
    const string16& database_name) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
  if (!database_connections_.IsDatabaseOpened(
          origin_identifier, database_name)) {
    UserMetrics::RecordAction(UserMetricsAction("BadMessageTerminate_DBMF"));
    BadMessageReceived();
    return;
  }

  db_tracker_->DatabaseModified(origin_identifier, database_name);
}

void DatabaseMessageFilter::OnDatabaseClosed(const string16& origin_identifier,
                                             const string16& database_name) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
  if (!database_connections_.IsDatabaseOpened(
          origin_identifier, database_name)) {
    UserMetrics::RecordAction(UserMetricsAction("BadMessageTerminate_DBMF"));
    BadMessageReceived();
    return;
  }

  database_connections_.RemoveConnection(origin_identifier, database_name);
  db_tracker_->DatabaseClosed(origin_identifier, database_name);
}

void DatabaseMessageFilter::OnDatabaseSizeChanged(
    const string16& origin_identifier,
    const string16& database_name,
    int64 database_size) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
  if (database_connections_.IsOriginUsed(origin_identifier)) {
    Send(new DatabaseMsg_UpdateSize(origin_identifier, database_name,
                                    database_size));
  }
}

void DatabaseMessageFilter::OnDatabaseScheduledForDeletion(
    const string16& origin_identifier,
    const string16& database_name) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
  Send(new DatabaseMsg_CloseImmediately(origin_identifier, database_name));
}
<commit_msg>Fix a bug where the SQLITE_OPEN_DELETEONCLOSE flag wasn't being respected. Review URL: http://codereview.chromium.org/7518041<commit_after>// Copyright (c) 2011 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 "content/browser/renderer_host/database_message_filter.h"

#include <string>

#include "base/platform_file.h"
#include "base/string_util.h"
#include "base/threading/thread.h"
#include "base/utf_string_conversions.h"
#include "content/browser/user_metrics.h"
#include "content/common/database_messages.h"
#include "content/common/result_codes.h"
#include "googleurl/src/gurl.h"
#include "third_party/sqlite/sqlite3.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebSecurityOrigin.h"
#include "webkit/database/database_util.h"
#include "webkit/database/vfs_backend.h"
#include "webkit/quota/quota_manager.h"

#if defined(OS_POSIX)
#include "base/file_descriptor_posix.h"
#endif

using quota::QuotaManager;
using quota::QuotaManagerProxy;
using quota::QuotaStatusCode;
using WebKit::WebSecurityOrigin;
using webkit_database::DatabaseTracker;
using webkit_database::DatabaseUtil;
using webkit_database::VfsBackend;

namespace {

class MyGetUsageAndQuotaCallback
    : public QuotaManager::GetUsageAndQuotaCallback  {
 public:
  MyGetUsageAndQuotaCallback(
      DatabaseMessageFilter* sender, IPC::Message* reply_msg)
      : sender_(sender), reply_msg_(reply_msg) {}

  virtual void RunWithParams(
        const Tuple3<QuotaStatusCode, int64, int64>& params) {
    Run(params.a, params.b, params.c);
  }

  void Run(QuotaStatusCode status, int64 usage, int64 quota) {
    int64 available = 0;
    if ((status == quota::kQuotaStatusOk) && (usage < quota))
      available = quota - usage;
    DatabaseHostMsg_GetSpaceAvailable::WriteReplyParams(
        reply_msg_.get(), available);
    sender_->Send(reply_msg_.release());
  }

 private:
  scoped_refptr<DatabaseMessageFilter> sender_;
  scoped_ptr<IPC::Message> reply_msg_;
};

const int kNumDeleteRetries = 2;
const int kDelayDeleteRetryMs = 100;

}  // namespace

DatabaseMessageFilter::DatabaseMessageFilter(
    webkit_database::DatabaseTracker* db_tracker)
    : db_tracker_(db_tracker),
      observer_added_(false) {
  DCHECK(db_tracker_);
}

void DatabaseMessageFilter::OnChannelClosing() {
  BrowserMessageFilter::OnChannelClosing();
  if (observer_added_) {
    observer_added_ = false;
    BrowserThread::PostTask(
        BrowserThread::FILE, FROM_HERE,
        NewRunnableMethod(this, &DatabaseMessageFilter::RemoveObserver));
  }
}

void DatabaseMessageFilter::AddObserver() {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
  db_tracker_->AddObserver(this);
}

void DatabaseMessageFilter::RemoveObserver() {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
  db_tracker_->RemoveObserver(this);

  // If the renderer process died without closing all databases,
  // then we need to manually close those connections
  db_tracker_->CloseDatabases(database_connections_);
  database_connections_.RemoveAllConnections();
}

void DatabaseMessageFilter::OverrideThreadForMessage(
    const IPC::Message& message,
    BrowserThread::ID* thread) {
  if (message.type() == DatabaseHostMsg_GetSpaceAvailable::ID)
    *thread = BrowserThread::IO;
  else if (IPC_MESSAGE_CLASS(message) == DatabaseMsgStart)
    *thread = BrowserThread::FILE;

  if (message.type() == DatabaseHostMsg_Opened::ID && !observer_added_) {
    observer_added_ = true;
    BrowserThread::PostTask(
        BrowserThread::FILE, FROM_HERE,
        NewRunnableMethod(this, &DatabaseMessageFilter::AddObserver));
  }
}

bool DatabaseMessageFilter::OnMessageReceived(
    const IPC::Message& message,
    bool* message_was_ok) {
  bool handled = true;
  IPC_BEGIN_MESSAGE_MAP_EX(DatabaseMessageFilter, message, *message_was_ok)
    IPC_MESSAGE_HANDLER_DELAY_REPLY(DatabaseHostMsg_OpenFile,
                                    OnDatabaseOpenFile)
    IPC_MESSAGE_HANDLER_DELAY_REPLY(DatabaseHostMsg_DeleteFile,
                                    OnDatabaseDeleteFile)
    IPC_MESSAGE_HANDLER_DELAY_REPLY(DatabaseHostMsg_GetFileAttributes,
                                    OnDatabaseGetFileAttributes)
    IPC_MESSAGE_HANDLER_DELAY_REPLY(DatabaseHostMsg_GetFileSize,
                                    OnDatabaseGetFileSize)
    IPC_MESSAGE_HANDLER_DELAY_REPLY(DatabaseHostMsg_GetSpaceAvailable,
                                    OnDatabaseGetSpaceAvailable)
    IPC_MESSAGE_HANDLER(DatabaseHostMsg_Opened, OnDatabaseOpened)
    IPC_MESSAGE_HANDLER(DatabaseHostMsg_Modified, OnDatabaseModified)
    IPC_MESSAGE_HANDLER(DatabaseHostMsg_Closed, OnDatabaseClosed)
    IPC_MESSAGE_UNHANDLED(handled = false)
  IPC_END_MESSAGE_MAP_EX()
  return handled;
}

DatabaseMessageFilter::~DatabaseMessageFilter() {
}

void DatabaseMessageFilter::OnDatabaseOpenFile(const string16& vfs_file_name,
                                               int desired_flags,
                                               IPC::Message* reply_msg) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
  base::PlatformFile file_handle = base::kInvalidPlatformFileValue;
  IPC::PlatformFileForTransit target_handle =
      IPC::InvalidPlatformFileForTransit();
  string16 origin_identifier;
  string16 database_name;

  // When in incognito mode, we want to make sure that all DB files are
  // removed when the incognito profile goes away, so we add the
  // SQLITE_OPEN_DELETEONCLOSE flag when opening all files, and keep
  // open handles to them in the database tracker to make sure they're
  // around for as long as needed.
  if (vfs_file_name.empty()) {
    VfsBackend::OpenTempFileInDirectory(db_tracker_->DatabaseDirectory(),
                                        desired_flags, &file_handle);
  } else if (DatabaseUtil::CrackVfsFileName(vfs_file_name, &origin_identifier,
                                            &database_name, NULL) &&
             !db_tracker_->IsDatabaseScheduledForDeletion(origin_identifier,
                                                          database_name)) {
      FilePath db_file =
          DatabaseUtil::GetFullFilePathForVfsFile(db_tracker_, vfs_file_name);
      if (!db_file.empty()) {
        if (db_tracker_->IsIncognitoProfile()) {
          db_tracker_->GetIncognitoFileHandle(vfs_file_name, &file_handle);
          if (file_handle == base::kInvalidPlatformFileValue) {
            VfsBackend::OpenFile(db_file,
                                 desired_flags | SQLITE_OPEN_DELETEONCLOSE,
                                 &file_handle);
            if (!(desired_flags & SQLITE_OPEN_DELETEONCLOSE))
              db_tracker_->SaveIncognitoFileHandle(vfs_file_name, file_handle);
          }
        } else {
          VfsBackend::OpenFile(db_file, desired_flags, &file_handle);
        }
      }
  }

  // Then we duplicate the file handle to make it useable in the renderer
  // process. The original handle is closed, unless we saved it in the
  // database tracker.
  bool auto_close = !db_tracker_->HasSavedIncognitoFileHandle(vfs_file_name);
  DCHECK_NE(base::kInvalidPlatformFileValue, file_handle);
  target_handle =
      IPC::GetFileHandleForProcess(file_handle, peer_handle(), auto_close);

  DatabaseHostMsg_OpenFile::WriteReplyParams(reply_msg, target_handle);
  Send(reply_msg);
}

void DatabaseMessageFilter::OnDatabaseDeleteFile(const string16& vfs_file_name,
                                                 const bool& sync_dir,
                                                 IPC::Message* reply_msg) {
  DatabaseDeleteFile(vfs_file_name, sync_dir, reply_msg, kNumDeleteRetries);
}

void DatabaseMessageFilter::DatabaseDeleteFile(const string16& vfs_file_name,
                                               bool sync_dir,
                                               IPC::Message* reply_msg,
                                               int reschedule_count) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));

  // Return an error if the file name is invalid or if the file could not
  // be deleted after kNumDeleteRetries attempts.
  int error_code = SQLITE_IOERR_DELETE;
  FilePath db_file =
      DatabaseUtil::GetFullFilePathForVfsFile(db_tracker_, vfs_file_name);
  if (!db_file.empty()) {
    // In order to delete a journal file in incognito mode, we only need to
    // close the open handle to it that's stored in the database tracker.
    if (db_tracker_->IsIncognitoProfile()) {
      const string16 wal_suffix(ASCIIToUTF16("-wal"));
      string16 sqlite_suffix;

      // WAL files can be deleted without having previously been opened.
      if (!db_tracker_->HasSavedIncognitoFileHandle(vfs_file_name) &&
          DatabaseUtil::CrackVfsFileName(vfs_file_name,
                                         NULL, NULL, &sqlite_suffix) &&
          sqlite_suffix == wal_suffix) {
        error_code = SQLITE_OK;
      } else if (db_tracker_->CloseIncognitoFileHandle(vfs_file_name)) {
        error_code = SQLITE_OK;
      }
    } else {
      error_code = VfsBackend::DeleteFile(db_file, sync_dir);
    }

    if ((error_code == SQLITE_IOERR_DELETE) && reschedule_count) {
      // If the file could not be deleted, try again.
      BrowserThread::PostDelayedTask(
          BrowserThread::FILE, FROM_HERE,
          NewRunnableMethod(this,
                            &DatabaseMessageFilter::DatabaseDeleteFile,
                            vfs_file_name,
                            sync_dir,
                            reply_msg,
                            reschedule_count - 1),
          kDelayDeleteRetryMs);
      return;
    }
  }

  DatabaseHostMsg_DeleteFile::WriteReplyParams(reply_msg, error_code);
  Send(reply_msg);
}

void DatabaseMessageFilter::OnDatabaseGetFileAttributes(
    const string16& vfs_file_name,
    IPC::Message* reply_msg) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
  int32 attributes = -1;
  FilePath db_file =
      DatabaseUtil::GetFullFilePathForVfsFile(db_tracker_, vfs_file_name);
  if (!db_file.empty())
    attributes = VfsBackend::GetFileAttributes(db_file);

  DatabaseHostMsg_GetFileAttributes::WriteReplyParams(
      reply_msg, attributes);
  Send(reply_msg);
}

void DatabaseMessageFilter::OnDatabaseGetFileSize(
    const string16& vfs_file_name, IPC::Message* reply_msg) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
  int64 size = 0;
  FilePath db_file =
      DatabaseUtil::GetFullFilePathForVfsFile(db_tracker_, vfs_file_name);
  if (!db_file.empty())
    size = VfsBackend::GetFileSize(db_file);

  DatabaseHostMsg_GetFileSize::WriteReplyParams(reply_msg, size);
  Send(reply_msg);
}

void DatabaseMessageFilter::OnDatabaseGetSpaceAvailable(
    const string16& origin_identifier, IPC::Message* reply_msg) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
  DCHECK(db_tracker_->quota_manager_proxy());

  QuotaManager* quota_manager =
      db_tracker_->quota_manager_proxy()->quota_manager();
  if (!quota_manager) {
    NOTREACHED();  // The system is shutting down, messages are unexpected.
    DatabaseHostMsg_GetSpaceAvailable::WriteReplyParams(
        reply_msg, static_cast<int64>(0));
    Send(reply_msg);
    return;
  }

  quota_manager->GetUsageAndQuota(
      DatabaseUtil::GetOriginFromIdentifier(origin_identifier),
      quota::kStorageTypeTemporary,
      new MyGetUsageAndQuotaCallback(this, reply_msg));
}

void DatabaseMessageFilter::OnDatabaseOpened(const string16& origin_identifier,
                                             const string16& database_name,
                                             const string16& description,
                                             int64 estimated_size) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
  int64 database_size = 0;
  db_tracker_->DatabaseOpened(origin_identifier, database_name, description,
                              estimated_size, &database_size);
  database_connections_.AddConnection(origin_identifier, database_name);
  Send(new DatabaseMsg_UpdateSize(origin_identifier, database_name,
                                  database_size));
}

void DatabaseMessageFilter::OnDatabaseModified(
    const string16& origin_identifier,
    const string16& database_name) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
  if (!database_connections_.IsDatabaseOpened(
          origin_identifier, database_name)) {
    UserMetrics::RecordAction(UserMetricsAction("BadMessageTerminate_DBMF"));
    BadMessageReceived();
    return;
  }

  db_tracker_->DatabaseModified(origin_identifier, database_name);
}

void DatabaseMessageFilter::OnDatabaseClosed(const string16& origin_identifier,
                                             const string16& database_name) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
  if (!database_connections_.IsDatabaseOpened(
          origin_identifier, database_name)) {
    UserMetrics::RecordAction(UserMetricsAction("BadMessageTerminate_DBMF"));
    BadMessageReceived();
    return;
  }

  database_connections_.RemoveConnection(origin_identifier, database_name);
  db_tracker_->DatabaseClosed(origin_identifier, database_name);
}

void DatabaseMessageFilter::OnDatabaseSizeChanged(
    const string16& origin_identifier,
    const string16& database_name,
    int64 database_size) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
  if (database_connections_.IsOriginUsed(origin_identifier)) {
    Send(new DatabaseMsg_UpdateSize(origin_identifier, database_name,
                                    database_size));
  }
}

void DatabaseMessageFilter::OnDatabaseScheduledForDeletion(
    const string16& origin_identifier,
    const string16& database_name) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
  Send(new DatabaseMsg_CloseImmediately(origin_identifier, database_name));
}
<|endoftext|>


<commit_before>#include <fcntl.h>
#include <dirent.h>

#include "SamsonConnectorConnection.h"
#include "SamsonConnector.h"

#include "DiskConnection.h" // Own interface

namespace samson {

    void* run_DiskConnection(void*p)
    {
        DiskConnection* d = (DiskConnection*)p;
        d->run();
        d->thread_running = false; // notify that thread is not running any more
        return NULL;
    }

    DiskConnection::DiskConnection( SamsonConnector* samson_connector , ConnectionType type , std::string _file_name )
    : SamsonConnectorItem( samson_connector , type ) , token("DiskConnection")
    {
        file_name = _file_name;

        // Open files if input
        if( type == connection_input )
        {
            // Analyse files to be read...
            struct stat s;
            stat( file_name.c_str() , &s);

            if( S_ISDIR( s.st_mode ) )
            {
                // Add all plain files in this directory
                DIR *pdir = opendir (file_name.c_str()); // "." will refer to the current directory
                struct dirent *pent = NULL;
                if (pdir != NULL) // if pdir wasn't initialised correctly
                {
                    while ((pent = readdir (pdir))) // while there is still something in the directory to list
                        if (pent != NULL)
                        {
                            if( strcmp( "." , pent->d_name )==0 )
                                continue;
                            if( strcmp( ".." , pent->d_name )==0 )
                                continue;

                            std::ostringstream localFileName;
                            localFileName << file_name << "/" << pent->d_name;

                            // Add to the list of files
                            files.push_back( localFileName.str() );

                        }
                    // finally, let's close the directory
                    closedir (pdir);
                }
            }
            else if( S_ISREG( s.st_mode ) )
                files.push_back( file_name );
        }
        else
        {
            // Create directory...
            int s = mkdir( file_name.c_str() , 0755 );
            if( ( s != 0 ) && ( errno != EEXIST) )
            {
                error.set(  au::str("Problems creating directory %s (%s)" , file_name.c_str() , strerror(errno) ) );
            }

        }

        // No file descriptor by default
        file_descriptor = NULL;

        // Create the background thread
        thread_running = true;
        pthread_t t;
        pthread_create(&t, NULL, run_DiskConnection, this );
    }

    DiskConnection::~DiskConnection()
    {
    }

    bool DiskConnection::isConnected()
    {
        return thread_running;
    }

    void DiskConnection::run_as_output()
    {
        std::string current_file_name;
        int file_counter = 0;
        size_t accumulated_size = 0;
        size_t max_size = 64000000; // Should be a parameter

        while( true )
        {
            Block* current_block = getNextOutputBlock();

            if( !current_block )
            {
                samson_connector->review();
                usleep(100000);
                continue;
            }

            // Size of the block
            size_t current_size = current_block->buffer->getSize();

            if( current_size> max_size )
            {
                error.set( au::str("Received a block with size %s. Max file size %s."
                                   , au::str( current_size,"B" ).c_str()
                                   , au::str( max_size ,"B" ).c_str() ) );

                samson_connector->show_warning( au::str("%s: %s" , getName().c_str() , error.getMessage().c_str() ) );

                return;
            }

            if( ( accumulated_size + current_size ) > max_size )
            {
                file_descriptor->close();
                delete file_descriptor;
                file_descriptor = NULL;
            }

            if( !file_descriptor )
            {
                accumulated_size = 0;

                while( true )
                {
                    current_file_name = au::str("%s/file_%d" , file_name.c_str() , file_counter );

                    struct stat buf;
                    int s = stat( current_file_name.c_str() , &buf );
                    if( s == 0 )
                    {
                        file_counter++;
                        continue; // found
                    }

                    // Open a file to write
                    int _fd = open( current_file_name.c_str() , O_WRONLY | O_CREAT );

                    if( _fd < 0 )
                    {
                        error.set( au::str("Not possible to open file %s (%s)" , current_file_name.c_str() , strerror(errno) ) );
                        samson_connector->show_warning( au::str("%s: %s" , getName().c_str() , error.getMessage().c_str() ) );
                        return;
                    }

                    file_descriptor = new FileDescriptor( current_file_name , _fd  );
                    break;
                }

            }

            // Write to disk
            Status s = file_descriptor->partWrite(current_block->buffer->getData(), current_block->buffer->getSize(), "samsonConnectorConnection");

            if( s != OK )
            {
                error.set( au::str("Not possible to write buffer with %s to file %s"
                                   , au::str( current_block->buffer->getSize() ).c_str()
                                   ,  current_file_name.c_str() ) );
                samson_connector->show_warning( au::str("%s: %s" , getName().c_str() , error.getMessage().c_str() ) );
                return;
            }

            // Pop output bock
            popOutputBlock();
        }

    }

    void DiskConnection::run_as_input()
    {

        while( true )
        {

            if( file_descriptor )
            {
                // Still reading from a file...
                engine::Buffer * buffer = engine::MemoryManager::shared()->newBuffer("stdin", "connector", buffer_size );

                // Read the entire buffer
                size_t read_size = 0;
                Status s = file_descriptor->partRead(buffer->getData()
                                                     , buffer_size
                                                     , "read connector connections"
                                                     , 300
                                                     , &read_size );
                // If we have read something...
                if( read_size > 0 )
                {
                    // Set the buffer size
                    buffer->setSize(read_size);
                    pushInputBuffer(buffer);
                    buffer = NULL; // Avoid releasing latter
                }

                // If last read is not ok...
                if( s != OK )
                {
                    // Deallocated the buffer
                    if( buffer )
                        engine::MemoryManager::shared()->destroyBuffer(buffer);

                    // Close current file descriptor
                    delete file_descriptor;
                    file_descriptor=NULL;
                }
            }
            else
            {
                if( files.size() == 0)
                {
                    // Flush whatever we have..
                    flush();
                    return;
                }

                // Open the next file...
                std::string current_file_name = files.front();
                files.pop_front();

                int _fd = open( current_file_name.c_str() , O_RDONLY );

                if( _fd > 0 )
                    file_descriptor = new FileDescriptor( file_name , _fd );
                else
                {
                    // Skipping file, show a message?
                }

            }

        }

    }

    void DiskConnection::run()
    {
        if( error.isActivated() )
            return;

        if( type == connection_input )
            run_as_input();
        else
            run_as_output();
    }

    std::string DiskConnection::getStatus()
    {
        au::TokenTaker tt(&token);

        if( error.isActivated() )
            return au::str("Error: %s" , error.getMessage().c_str() );

        if ( thread_running )
            return "OK";

        return "Stopped";
    }

    std::string DiskConnection::getName()
    {
        return au::str("Disk %s" , file_name.c_str() );
    }

    bool DiskConnection::canBeRemoved()
    {
        return !thread_running;
    }

}<commit_msg>Fix compilation error in ubuntu<commit_after>#include <fcntl.h>
#include <dirent.h>

#include "SamsonConnectorConnection.h"
#include "SamsonConnector.h"

#include "DiskConnection.h" // Own interface

namespace samson {

    void* run_DiskConnection(void*p)
    {
        DiskConnection* d = (DiskConnection*)p;
        d->run();
        d->thread_running = false; // notify that thread is not running any more
        return NULL;
    }

    DiskConnection::DiskConnection( SamsonConnector* samson_connector , ConnectionType type , std::string _file_name )
    : SamsonConnectorItem( samson_connector , type ) , token("DiskConnection")
    {
        file_name = _file_name;

        // Open files if input
        if( type == connection_input )
        {
            // Analyse files to be read...
            struct stat s;
            stat( file_name.c_str() , &s);

            if( S_ISDIR( s.st_mode ) )
            {
                // Add all plain files in this directory
                DIR *pdir = opendir (file_name.c_str()); // "." will refer to the current directory
                struct dirent *pent = NULL;
                if (pdir != NULL) // if pdir wasn't initialised correctly
                {
                    while ((pent = readdir (pdir))) // while there is still something in the directory to list
                        if (pent != NULL)
                        {
                            if( strcmp( "." , pent->d_name )==0 )
                                continue;
                            if( strcmp( ".." , pent->d_name )==0 )
                                continue;

                            std::ostringstream localFileName;
                            localFileName << file_name << "/" << pent->d_name;

                            // Add to the list of files
                            files.push_back( localFileName.str() );

                        }
                    // finally, let's close the directory
                    closedir (pdir);
                }
            }
            else if( S_ISREG( s.st_mode ) )
                files.push_back( file_name );
        }
        else
        {
            // Create directory...
            int s = mkdir( file_name.c_str() , 0755 );
            if( ( s != 0 ) && ( errno != EEXIST) )
            {
                error.set(  au::str("Problems creating directory %s (%s)" , file_name.c_str() , strerror(errno) ) );
            }

        }

        // No file descriptor by default
        file_descriptor = NULL;

        // Create the background thread
        thread_running = true;
        pthread_t t;
        pthread_create(&t, NULL, run_DiskConnection, this );
    }

    DiskConnection::~DiskConnection()
    {
    }

    bool DiskConnection::isConnected()
    {
        return thread_running;
    }

    void DiskConnection::run_as_output()
    {
        std::string current_file_name;
        int file_counter = 0;
        size_t accumulated_size = 0;
        size_t max_size = 64000000; // Should be a parameter

        while( true )
        {
            Block* current_block = getNextOutputBlock();

            if( !current_block )
            {
                samson_connector->review();
                usleep(100000);
                continue;
            }

            // Size of the block
            size_t current_size = current_block->buffer->getSize();

            if( current_size> max_size )
            {
                error.set( au::str("Received a block with size %s. Max file size %s."
                                   , au::str( current_size,"B" ).c_str()
                                   , au::str( max_size ,"B" ).c_str() ) );

                samson_connector->show_warning( au::str("%s: %s" , getName().c_str() , error.getMessage().c_str() ) );

                return;
            }

            if( ( accumulated_size + current_size ) > max_size )
            {
                file_descriptor->close();
                delete file_descriptor;
                file_descriptor = NULL;
            }

            if( !file_descriptor )
            {
                accumulated_size = 0;

                while( true )
                {
                    current_file_name = au::str("%s/file_%d" , file_name.c_str() , file_counter );

                    struct stat buf;
                    int s = stat( current_file_name.c_str() , &buf );
                    if( s == 0 )
                    {
                        file_counter++;
                        continue; // found
                    }

                    // Open a file to write
                    int _fd = open( current_file_name.c_str() , O_CREAT | O_WRONLY , 0644 );

                    if( _fd < 0 )
                    {
                        error.set( au::str("Not possible to open file %s (%s)" , current_file_name.c_str() , strerror(errno) ) );
                        samson_connector->show_warning( au::str("%s: %s" , getName().c_str() , error.getMessage().c_str() ) );
                        return;
                    }

                    file_descriptor = new FileDescriptor( current_file_name , _fd  );
                    break;
                }

            }

            // Write to disk
            Status s = file_descriptor->partWrite(current_block->buffer->getData(), current_block->buffer->getSize(), "samsonConnectorConnection");

            if( s != OK )
            {
                error.set( au::str("Not possible to write buffer with %s to file %s"
                                   , au::str( current_block->buffer->getSize() ).c_str()
                                   ,  current_file_name.c_str() ) );
                samson_connector->show_warning( au::str("%s: %s" , getName().c_str() , error.getMessage().c_str() ) );
                return;
            }

            // Pop output bock
            popOutputBlock();
        }

    }

    void DiskConnection::run_as_input()
    {

        while( true )
        {

            if( file_descriptor )
            {
                // Still reading from a file...
                engine::Buffer * buffer = engine::MemoryManager::shared()->newBuffer("stdin", "connector", buffer_size );

                // Read the entire buffer
                size_t read_size = 0;
                Status s = file_descriptor->partRead(buffer->getData()
                                                     , buffer_size
                                                     , "read connector connections"
                                                     , 300
                                                     , &read_size );
                // If we have read something...
                if( read_size > 0 )
                {
                    // Set the buffer size
                    buffer->setSize(read_size);
                    pushInputBuffer(buffer);
                    buffer = NULL; // Avoid releasing latter
                }

                // If last read is not ok...
                if( s != OK )
                {
                    // Deallocated the buffer
                    if( buffer )
                        engine::MemoryManager::shared()->destroyBuffer(buffer);

                    // Close current file descriptor
                    delete file_descriptor;
                    file_descriptor=NULL;
                }
            }
            else
            {
                if( files.size() == 0)
                {
                    // Flush whatever we have..
                    flush();
                    return;
                }

                // Open the next file...
                std::string current_file_name = files.front();
                files.pop_front();

                int _fd = open( current_file_name.c_str() , O_RDONLY );

                if( _fd > 0 )
                    file_descriptor = new FileDescriptor( file_name , _fd );
                else
                {
                    // Skipping file, show a message?
                }

            }

        }

    }

    void DiskConnection::run()
    {
        if( error.isActivated() )
            return;

        if( type == connection_input )
            run_as_input();
        else
            run_as_output();
    }

    std::string DiskConnection::getStatus()
    {
        au::TokenTaker tt(&token);

        if( error.isActivated() )
            return au::str("Error: %s" , error.getMessage().c_str() );

        if ( thread_running )
            return "OK";

        return "Stopped";
    }

    std::string DiskConnection::getName()
    {
        return au::str("Disk %s" , file_name.c_str() );
    }

    bool DiskConnection::canBeRemoved()
    {
        return !thread_running;
    }

}
<|endoftext|>


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

#include "IECore/CompoundData.h"
#include "IECore/CompoundParameter.h"
#include "IECore/MessageHandler.h"

#include "Convert.h"
#include "ToHoudiniAttribConverter.h"
#include "ToHoudiniGeometryConverter.h"
#include "ToHoudiniStringAttribConverter.h"

using namespace IECore;
using namespace IECoreHoudini;

IE_CORE_DEFINERUNTIMETYPED( ToHoudiniGeometryConverter );

ToHoudiniGeometryConverter::ToHoudiniGeometryConverter( const VisibleRenderable *renderable, const std::string &description )
	:	ToHoudiniConverter( description, VisibleRenderableTypeId )
{
	srcParameter()->setValue( (VisibleRenderable *)renderable );
}

ToHoudiniGeometryConverter::~ToHoudiniGeometryConverter()
{
}

bool ToHoudiniGeometryConverter::convert( GU_DetailHandle handle ) const
{
	ConstCompoundObjectPtr operands = parameters()->getTypedValidatedValue<CompoundObject>();
	GU_DetailHandleAutoWriteLock writeHandle( handle );

	GU_Detail *geo = writeHandle.getGdp();
	if ( !geo )
	{
		return false;
	}

	const VisibleRenderable *renderable = IECore::runTimeCast<const VisibleRenderable>( srcParameter()->getValidatedValue() );
	if ( !renderable )
	{
		return false;
	}

	return doConversion( renderable, geo );
}

GA_Range ToHoudiniGeometryConverter::appendPoints( GA_Detail *geo, const IECore::V3fVectorData *positions ) const
{
	if ( !positions )
	{
		return GA_Range();
	}

	const std::vector<Imath::V3f> &pos = positions->readable();
	GA_OffsetList offsets;
	offsets.reserve( pos.size() );

	for ( size_t i=0; i < pos.size(); i++ )
	{
		GA_Offset offset = geo->appendPoint();
		geo->setPos3( offset, IECore::convert<UT_Vector3>( pos[i] ) );
		offsets.append( offset );
	}

	return GA_Range( geo->getPointMap(), offsets );
}

void ToHoudiniGeometryConverter::transferAttribs(
	const Primitive *primitive, GU_Detail *geo,
	const GA_Range &newPoints, const GA_Range &newPrims,
	PrimitiveVariable::Interpolation vertexInterpolation,
	PrimitiveVariable::Interpolation primitiveInterpolation,
	PrimitiveVariable::Interpolation pointInterpolation,
	PrimitiveVariable::Interpolation detailInterpolation
) const
{
	GA_OffsetList offsets;
	if ( newPrims.isValid() )
	{
		const GA_PrimitiveList &primitives = geo->getPrimitiveList();
		for ( GA_Iterator it=newPrims.begin(); !it.atEnd(); ++it )
		{
			const GA_Primitive *prim = primitives.get( it.getOffset() );
			size_t numPrimVerts = prim->getVertexCount();
			for ( size_t v=0; v < numPrimVerts; v++ )
			{
				if ( prim->getTypeId() == GEO_PRIMPOLY )
				{
					offsets.append( prim->getVertexOffset( numPrimVerts - 1 - v ) );
				}
				else
				{
					offsets.append( prim->getVertexOffset( v ) );
				}
			}
		}
	}

	GA_Range vertRange( geo->getVertexMap(), offsets );

	// P should already have been added as points
	std::vector<std::string> variablesToIgnore;
	variablesToIgnore.push_back( "P" );

	// match all the string variables to each associated indices variable
	/// \todo: replace all this logic with IECore::IndexedData once it exists...
	PrimitiveVariableMap stringsToIndices;
	for ( PrimitiveVariableMap::const_iterator it=primitive->variables.begin() ; it != primitive->variables.end(); it++ )
	{
		if ( !primitive->isPrimitiveVariableValid( it->second ) )
		{
			IECore::msg( IECore::MessageHandler::Warning, "ToHoudiniGeometryConverter", "PrimitiveVariable " + it->first + " is invalid. Ignoring." );
			variablesToIgnore.push_back( it->first );
			continue;
		}

		ToHoudiniAttribConverterPtr converter = ToHoudiniAttribConverter::create( it->second.data );
		if ( !converter )
		{
			continue;
		}

		if ( it->second.data->isInstanceOf( StringVectorDataTypeId ) )
		{
			std::string indicesVariableName = it->first + "Indices";
			PrimitiveVariableMap::const_iterator indices = primitive->variables.find( indicesVariableName );
			if ( indices != primitive->variables.end() && indices->second.data->isInstanceOf( IntVectorDataTypeId ) && primitive->isPrimitiveVariableValid( indices->second ) )
			{
				stringsToIndices[it->first] = indices->second;
				variablesToIgnore.push_back( indicesVariableName );
			}
		}
	}

	/// \todo: should we convert s and t to uv automatically?

 	// add the primitive variables to the various GEO_AttribDicts based on interpolation type
	for ( PrimitiveVariableMap::const_iterator it=primitive->variables.begin() ; it != primitive->variables.end(); it++ )
	{
		if ( find( variablesToIgnore.begin(), variablesToIgnore.end(), it->first ) != variablesToIgnore.end() )
		{
			continue;
		}

		ToHoudiniAttribConverterPtr converter = ToHoudiniAttribConverter::create( it->second.data );
		if ( !converter )
		{
			continue;
		}

		PrimitiveVariable::Interpolation interpolation = it->second.interpolation;

		if ( converter->isInstanceOf( (IECore::TypeId)ToHoudiniStringVectorAttribConverterTypeId ) )
		{
			PrimitiveVariableMap::const_iterator indices = stringsToIndices.find( it->first );
			if ( indices != stringsToIndices.end() )
			{
				ToHoudiniStringVectorAttribConverter *stringVectorConverter = IECore::runTimeCast<ToHoudiniStringVectorAttribConverter>( converter );
				stringVectorConverter->indicesParameter()->setValidatedValue( indices->second.data );
				interpolation = indices->second.interpolation;
			}
		}

		if ( interpolation == detailInterpolation )
 		{
			// add detail attribs
			converter->convert( it->first, geo );
	 	}
		else if ( interpolation == pointInterpolation )
		{
			// add point attribs
 			converter->convert( it->first, geo, newPoints );
		}
		else if ( interpolation == primitiveInterpolation )
		{
			// add primitive attribs
			converter->convert( it->first, geo, newPrims );
		}
		else if ( interpolation == vertexInterpolation )
		{
			// add vertex attribs
			converter->convert( it->first, geo, vertRange );
		}
	}

	// add the groups based on blindData
	const StringData *nameData = primitive->blindData()->member<StringData>( "name" );
	if ( nameData )
	{
		const char *name = nameData->readable().c_str();

		if ( newPoints.isValid() )
		{
			GA_ElementGroup *group = geo->findPointGroup( name );
			if ( !group || group->classType() != GA_GROUP_POINT )
			{
				group = geo->createElementGroup( GA_ATTRIB_POINT, name );
			}

			group->addRange( newPoints );
		}

		if ( newPrims.isValid() )
		{
			GA_ElementGroup *group = geo->findPrimitiveGroup( name );
			if ( !group || group->classType() != GA_GROUP_PRIMITIVE )
			{
				group = geo->createElementGroup( GA_ATTRIB_PRIMITIVE, name );
			}

			group->addRange( newPrims );
		}
	}
}

/////////////////////////////////////////////////////////////////////////////////
// Factory
/////////////////////////////////////////////////////////////////////////////////

ToHoudiniGeometryConverterPtr ToHoudiniGeometryConverter::create( const VisibleRenderable *renderable )
{
	const TypesToFnsMap *m = typesToFns();
	TypesToFnsMap::const_iterator it = m->find( Types( renderable->typeId() ) );
	if( it!=m->end() )
	{
		return it->second( renderable );
	}

	return 0;
}

void ToHoudiniGeometryConverter::registerConverter( IECore::TypeId fromType, CreatorFn creator )
{
	TypesToFnsMap *m = typesToFns();
	m->insert( TypesToFnsMap::value_type( Types( fromType ), creator ) );
}

ToHoudiniGeometryConverter::TypesToFnsMap *ToHoudiniGeometryConverter::typesToFns()
{
	static TypesToFnsMap *m = new TypesToFnsMap;
	return m;
}

/////////////////////////////////////////////////////////////////////////////////
// Implementation of nested Types class
/////////////////////////////////////////////////////////////////////////////////

ToHoudiniGeometryConverter::Types::Types( IECore::TypeId from ) : fromType( from )
{
}

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

#include "IECore/CompoundData.h"
#include "IECore/CompoundParameter.h"
#include "IECore/MessageHandler.h"

#include "Convert.h"
#include "ToHoudiniAttribConverter.h"
#include "ToHoudiniGeometryConverter.h"
#include "ToHoudiniStringAttribConverter.h"

using namespace IECore;
using namespace IECoreHoudini;

IE_CORE_DEFINERUNTIMETYPED( ToHoudiniGeometryConverter );

ToHoudiniGeometryConverter::ToHoudiniGeometryConverter( const VisibleRenderable *renderable, const std::string &description )
	:	ToHoudiniConverter( description, VisibleRenderableTypeId )
{
	srcParameter()->setValue( (VisibleRenderable *)renderable );
}

ToHoudiniGeometryConverter::~ToHoudiniGeometryConverter()
{
}

bool ToHoudiniGeometryConverter::convert( GU_DetailHandle handle ) const
{
	ConstCompoundObjectPtr operands = parameters()->getTypedValidatedValue<CompoundObject>();
	GU_DetailHandleAutoWriteLock writeHandle( handle );

	GU_Detail *geo = writeHandle.getGdp();
	if ( !geo )
	{
		return false;
	}

	const VisibleRenderable *renderable = IECore::runTimeCast<const VisibleRenderable>( srcParameter()->getValidatedValue() );
	if ( !renderable )
	{
		return false;
	}

	return doConversion( renderable, geo );
}

GA_Range ToHoudiniGeometryConverter::appendPoints( GA_Detail *geo, const IECore::V3fVectorData *positions ) const
{
	if ( !positions )
	{
		return GA_Range();
	}

	const std::vector<Imath::V3f> &pos = positions->readable();
	GA_OffsetList offsets;
	offsets.reserve( pos.size() );

	for ( size_t i=0; i < pos.size(); i++ )
	{
		GA_Offset offset = geo->appendPoint();
		geo->setPos3( offset, IECore::convert<UT_Vector3>( pos[i] ) );
		offsets.append( offset );
	}

	return GA_Range( geo->getPointMap(), offsets );
}

void ToHoudiniGeometryConverter::transferAttribs(
	const Primitive *primitive, GU_Detail *geo,
	const GA_Range &newPoints, const GA_Range &newPrims,
	PrimitiveVariable::Interpolation vertexInterpolation,
	PrimitiveVariable::Interpolation primitiveInterpolation,
	PrimitiveVariable::Interpolation pointInterpolation,
	PrimitiveVariable::Interpolation detailInterpolation
) const
{
	GA_OffsetList offsets;
	if ( newPrims.isValid() )
	{
		const GA_PrimitiveList &primitives = geo->getPrimitiveList();
		for ( GA_Iterator it=newPrims.begin(); !it.atEnd(); ++it )
		{
			const GA_Primitive *prim = primitives.get( it.getOffset() );
			size_t numPrimVerts = prim->getVertexCount();
			for ( size_t v=0; v < numPrimVerts; v++ )
			{
				if ( prim->getTypeId() == GEO_PRIMPOLY )
				{
					offsets.append( prim->getVertexOffset( numPrimVerts - 1 - v ) );
				}
				else
				{
					offsets.append( prim->getVertexOffset( v ) );
				}
			}
		}
	}

	GA_Range vertRange( geo->getVertexMap(), offsets );

	// P should already have been added as points
	std::vector<std::string> variablesToIgnore;
	variablesToIgnore.push_back( "P" );

	// match all the string variables to each associated indices variable
	/// \todo: replace all this logic with IECore::IndexedData once it exists...
	PrimitiveVariableMap stringsToIndices;
	for ( PrimitiveVariableMap::const_iterator it=primitive->variables.begin() ; it != primitive->variables.end(); it++ )
	{
		if ( !primitive->isPrimitiveVariableValid( it->second ) )
		{
			IECore::msg( IECore::MessageHandler::Warning, "ToHoudiniGeometryConverter", "PrimitiveVariable " + it->first + " is invalid. Ignoring." );
			variablesToIgnore.push_back( it->first );
			continue;
		}

		ToHoudiniAttribConverterPtr converter = ToHoudiniAttribConverter::create( it->second.data );
		if ( !converter )
		{
			continue;
		}

		if ( it->second.data->isInstanceOf( StringVectorDataTypeId ) )
		{
			std::string indicesVariableName = it->first + "Indices";
			PrimitiveVariableMap::const_iterator indices = primitive->variables.find( indicesVariableName );
			if ( indices != primitive->variables.end() && indices->second.data->isInstanceOf( IntVectorDataTypeId ) && primitive->isPrimitiveVariableValid( indices->second ) )
			{
				stringsToIndices[it->first] = indices->second;
				variablesToIgnore.push_back( indicesVariableName );
			}
		}
	}

	/// \todo: should we convert s and t to uv automatically?

 	// add the primitive variables to the various GEO_AttribDicts based on interpolation type
	for ( PrimitiveVariableMap::const_iterator it=primitive->variables.begin() ; it != primitive->variables.end(); it++ )
	{
		if ( find( variablesToIgnore.begin(), variablesToIgnore.end(), it->first ) != variablesToIgnore.end() )
		{
			continue;
		}

		ToHoudiniAttribConverterPtr converter = ToHoudiniAttribConverter::create( it->second.data );
		if ( !converter )
		{
			continue;
		}

		PrimitiveVariable::Interpolation interpolation = it->second.interpolation;

		if ( converter->isInstanceOf( (IECore::TypeId)ToHoudiniStringVectorAttribConverterTypeId ) )
		{
			PrimitiveVariableMap::const_iterator indices = stringsToIndices.find( it->first );
			if ( indices != stringsToIndices.end() )
			{
				ToHoudiniStringVectorAttribConverter *stringVectorConverter = IECore::runTimeCast<ToHoudiniStringVectorAttribConverter>( converter );
				stringVectorConverter->indicesParameter()->setValidatedValue( indices->second.data );
				interpolation = indices->second.interpolation;
			}
		}

		if ( interpolation == detailInterpolation )
 		{
			// add detail attribs
			converter->convert( it->first, geo );
	 	}
		else if ( interpolation == pointInterpolation )
		{
			// add point attribs
 			converter->convert( it->first, geo, newPoints );
		}
		else if ( interpolation == primitiveInterpolation )
		{
			// add primitive attribs
			converter->convert( it->first, geo, newPrims );
		}
		else if ( interpolation == vertexInterpolation )
		{
			// add vertex attribs
			converter->convert( it->first, geo, vertRange );
		}
	}

	// add the groups based on blindData
	const StringData *nameData = primitive->blindData()->member<StringData>( "name" );
	if ( nameData )
	{
		const char *name = nameData->readable().c_str();

		if ( newPoints.isValid() )
		{
			GA_ElementGroup *group = geo->findPointGroup( name );
			if ( !group || group->classType() != GA_GROUP_POINT )
			{
				group = geo->createElementGroup( GA_ATTRIB_POINT, name );
			}

			if ( group )
			{
				group->addRange( newPoints );
			}
			else
			{
				IECore::msg( IECore::MessageHandler::Warning, "ToHoudiniGeometryConverter", "Group " + nameData->readable() + " is invalid." );
			}
		}

		if ( newPrims.isValid() )
		{
			GA_ElementGroup *group = geo->findPrimitiveGroup( name );
			if ( !group || group->classType() != GA_GROUP_PRIMITIVE )
			{
				group = geo->createElementGroup( GA_ATTRIB_PRIMITIVE, name );
			}

			if ( group )
			{
				group->addRange( newPrims );
			}
			else
			{
				IECore::msg( IECore::MessageHandler::Warning, "ToHoudiniGeometryConverter", "Group " + nameData->readable() + " is invalid." );
			}
		}
	}
}

/////////////////////////////////////////////////////////////////////////////////
// Factory
/////////////////////////////////////////////////////////////////////////////////

ToHoudiniGeometryConverterPtr ToHoudiniGeometryConverter::create( const VisibleRenderable *renderable )
{
	const TypesToFnsMap *m = typesToFns();
	TypesToFnsMap::const_iterator it = m->find( Types( renderable->typeId() ) );
	if( it!=m->end() )
	{
		return it->second( renderable );
	}

	return 0;
}

void ToHoudiniGeometryConverter::registerConverter( IECore::TypeId fromType, CreatorFn creator )
{
	TypesToFnsMap *m = typesToFns();
	m->insert( TypesToFnsMap::value_type( Types( fromType ), creator ) );
}

ToHoudiniGeometryConverter::TypesToFnsMap *ToHoudiniGeometryConverter::typesToFns()
{
	static TypesToFnsMap *m = new TypesToFnsMap;
	return m;
}

/////////////////////////////////////////////////////////////////////////////////
// Implementation of nested Types class
/////////////////////////////////////////////////////////////////////////////////

ToHoudiniGeometryConverter::Types::Types( IECore::TypeId from ) : fromType( from )
{
}

bool ToHoudiniGeometryConverter::Types::operator < ( const Types &other ) const
{
	return fromType < other.fromType;
}
<|endoftext|>


<commit_before>#ifndef TIMER_HH
#define TIMER_HH

#include <avr/interrupt.h>
#include <stddef.h>

#include "utilities.hh"
#include "Board_traits.hh"

//TODO do we need to put everything here in a namespace?

// These macros are internally used in further macro and should not be used in your programs
#define ISR_TIMER_(T0, ...)										\
ISR(TIMER ## T0 ## _COMPA_vect, ISR_NAKED)						\
{																\
	asm volatile(												\
		"push r16\n\t"											\
		"ldi r16, %[TIMER]\n\t"									\
		"out %[GPIOR], r16\n\t"									\
		::[GPIOR] "I" (_SFR_IO_ADDR(GPIOR0)), [TIMER] "I" (T0)	\
	);															\
	ISRCallback::callback();									\
	asm volatile(												\
		"pop r16\n\t"											\
		"reti\n\t"												\
	);															\
}																\

#define PREPEND_TIMER_(TN, ...) Board::Timer::TIMER ## TN

#define USE_TIMERS(T0, ...)																				\
using ISRCallback =																						\
	timer_impl::ISRHandler<FOR_EACH_SEP(PREPEND_TIMER_, , EMPTY, COMMA, EMPTY, T0, ##__VA_ARGS__)>;		\
																										\
FOR_EACH(ISR_TIMER_, _0, T0, ##__VA_ARGS__)

// Forward declaration necessary to be declared as friend
// Complete declaration can be found at the end of this file
namespace timer_impl
{
	template<Board::Timer T0, Board::Timer... TS> struct CallbackHandler;
}

class TimerCallback
{
public:
	virtual void on_timer() = 0;
};

template<Board::Timer TIMER>
class Timer
{
protected:
	using TRAIT = Board::Timer_trait<TIMER>;
	using PRESCALERS_TRAIT = typename TRAIT::PRESCALERS_TRAIT;

public:
	using TIMER_TYPE = typename TRAIT::TYPE;
	using TIMER_PRESCALER = typename PRESCALERS_TRAIT::TYPE;

	Timer(TimerCallback& callback) INLINE
	{
		_callback = &callback;
	}

	static constexpr bool is_adequate(TIMER_PRESCALER p, uint32_t us)
	{
		return prescaler_is_adequate(prescaler_quotient(p, us));
	}
	static constexpr TIMER_PRESCALER prescaler(uint32_t us)
	{
		return best_prescaler(PRESCALERS_TRAIT::ALL_PRESCALERS, us);
	}
	static constexpr TIMER_TYPE counter(TIMER_PRESCALER prescaler, uint32_t us)
	{
		return (TIMER_TYPE) prescaler_quotient(prescaler, us) - 1;
	}
	static constexpr TIMER_TYPE counter(uint32_t us)
	{
		return (TIMER_TYPE) prescaler_quotient(prescaler(us), us) - 1;
	}

	inline void begin(TIMER_PRESCALER prescaler, TIMER_TYPE max)
	{
		synchronized _begin(prescaler, max);
	}

	// We'll need additional methods in Timer_trait<>
	inline void _begin(TIMER_PRESCALER prescaler, TIMER_TYPE max)
	{
		// OCnA & OCnB disconnected, CTC (Clear Timer on Compare match)
		(volatile uint8_t&) TRAIT::TCCRA = TRAIT::CTC_TCCRA;
		// Don't force output compare (FOCA & FOCB), Clock Select according to prescaler
		(volatile uint8_t&) TRAIT::TCCRB = TRAIT::CTC_TCCRB | TRAIT::TCCRB_prescaler(prescaler);
		// Set timer counter compare match (when value reached, 1ms has elapsed)
		(volatile TIMER_TYPE&) TRAIT::OCRA = max;
		// Reset timer counter
		(volatile TIMER_TYPE&) TRAIT::TCNT = 0;
		// Set timer interrupt mode (set interrupt on OCRnA compare match)
		(volatile uint8_t&) TRAIT::TIMSK = _BV(OCIE0A);
	}
	inline void suspend()
	{
		synchronized _suspend();
	}
	inline void _suspend()
	{
		// Clear timer interrupt mode
		(volatile uint8_t&) TRAIT::TIMSK = 0;
	}
	inline void resume()
	{
		synchronized _resume();
	}
	inline void _resume()
	{
		// Reset timer counter
		(volatile TIMER_TYPE&) TRAIT::TCNT = 0;
		// Set timer interrupt mode (set interrupt on OCRnA compare match)
		(volatile uint8_t&) TRAIT::TIMSK = _BV(OCIE0A);
	}
	inline bool is_suspended()
	{
		return (volatile uint8_t&) TRAIT::TIMSK == 0;
	}
	inline void end()
	{
		synchronized _end();
	}
	inline void _end()
	{
		// Stop timer
		(volatile uint8_t&) TRAIT::TCCRB = 0;
		// Clear timer interrupt mode (set interrupt on OCRnA compare match)
		(volatile uint8_t&) TRAIT::TIMSK = 0;
	}

private:
	static constexpr uint32_t prescaler_quotient(TIMER_PRESCALER p, uint32_t us)
	{
		return (F_CPU / 1000000UL * us) / _BV(uint8_t(p));
	}

	static constexpr uint32_t prescaler_remainder(TIMER_PRESCALER p, uint32_t us)
	{
		return (F_CPU / 1000000UL * us) % _BV(uint8_t(p));
	}

	static constexpr bool prescaler_is_adequate(uint32_t quotient)
	{
		return quotient > 1 and quotient < TRAIT::MAX_COUNTER;
	}

	static constexpr TIMER_PRESCALER best_prescaler_in_2(TIMER_PRESCALER p1, TIMER_PRESCALER p2, uint32_t us)
	{
		return (!prescaler_is_adequate(prescaler_quotient(p1, us)) ? p2 :
				!prescaler_is_adequate(prescaler_quotient(p2, us)) ? p1 :
				prescaler_remainder(p1, us) < prescaler_remainder(p2, us) ? p1 :
				prescaler_remainder(p1, us) > prescaler_remainder(p2, us) ? p2 :
				prescaler_quotient(p1, us) > prescaler_quotient(p2, us) ? p1 : p2);
	}

	static constexpr TIMER_PRESCALER best_prescaler(const TIMER_PRESCALER* begin, const TIMER_PRESCALER* end, uint32_t us)
	{
		return (begin + 1 == end ? *begin : best_prescaler_in_2(*begin, best_prescaler(begin + 1 , end, us), us));
	}

	template<size_t N>
	static constexpr TIMER_PRESCALER best_prescaler(const TIMER_PRESCALER(&prescalers)[N], uint32_t us)
	{
		return best_prescaler(prescalers, prescalers + N, us);
	}

	static void call_back_if_needed()
	{
		if (GPIOR0 == uint8_t(TIMER))
			_callback->on_timer();
	}

	static TimerCallback* _callback;

	template<Board::Timer, Board::Timer...> friend struct timer_impl::CallbackHandler;
};

template<Board::Timer TIMER>
TimerCallback* Timer<TIMER>::_callback = 0;

namespace timer_impl
{
	template<Board::Timer... TS>
	struct ISRHandler
	{
		static void callback() __attribute__((naked))
		{
			asm volatile(
				"push r1\n\t"
				"push r0\n\t"
				"in r0, __SREG__\n\t"
				"push r0\n\t"
				"eor r1, r1\n\t"
				"push r18\n\t"
				"push r19\n\t"
				"push r20\n\t"
				"push r21\n\t"
				"push r22\n\t"
				"push r23\n\t"
				"push r24\n\t"
				"push r25\n\t"
				"push r26\n\t"
				"push r27\n\t"
				"push r30\n\t"
				"push r31\n\t"
			);
			CallbackHandler<TS...>::callback();
			asm volatile(
				"pop r31\n\t"
				"pop r30\n\t"
				"pop r27\n\t"
				"pop r26\n\t"
				"pop r25\n\t"
				"pop r24\n\t"
				"pop r23\n\t"
				"pop r22\n\t"
				"pop r21\n\t"
				"pop r20\n\t"
				"pop r19\n\t"
				"pop r18\n\t"
				"pop r0\n\t"
				"out __SREG__, r0\n\t"
				"pop r0\n\t"
				"pop r1\n\t"
				"ret\n\t"
			);
		}
	};

	template<Board::Timer T0, Board::Timer... TS>
	struct CallbackHandler
	{
		static void callback()
		{
			CallbackHandler<T0>::callback();
			CallbackHandler<TS...>::callback();
		}
	};

	template<Board::Timer T>
	struct CallbackHandler<T>
	{
		static void callback()
		{
			Timer<T>::call_back_if_needed();
		}
	};
}

#endif /* TIMER_HH */
<commit_msg>Rework and fix Timer ISR size optimization.<commit_after>#ifndef TIMER_HH
#define TIMER_HH

#include <avr/interrupt.h>
#include <stddef.h>

#include "utilities.hh"
#include "Board_traits.hh"

//TODO do we need to put everything here in a namespace?

// These macros are internally used in further macro and should not be used in your programs
#define ISR_TIMER_(T0, ...)										\
ISR(TIMER ## T0 ## _COMPA_vect, ISR_NAKED)						\
{																\
	asm volatile(												\
		"push r16\n\t"											\
		"ldi r16, %[TIMER]\n\t"									\
		"out %[GPIOR], r16\n\t"									\
		::[GPIOR] "I" (_SFR_IO_ADDR(GPIOR0)), [TIMER] "I" (T0)	\
	);															\
	ISRCallback::callback();									\
	asm volatile(												\
		"pop r16\n\t"											\
		"reti\n\t"												\
	);															\
}																\

#define PREPEND_TIMER_(TN, ...) Board::Timer::TIMER ## TN

#define USE_TIMERS(T0, ...)																				\
using ISRCallback =																						\
	timer_impl::ISRHandler<FOR_EACH_SEP(PREPEND_TIMER_, , EMPTY, COMMA, EMPTY, T0, ##__VA_ARGS__)>;		\
																										\
FOR_EACH(ISR_TIMER_, , T0, ##__VA_ARGS__)

// Forward declaration necessary to be declared as friend
// Complete declaration can be found at the end of this file
namespace timer_impl
{
	template<Board::Timer T0, Board::Timer... TS> struct CallbackHandler;
}

class TimerCallback
{
public:
	virtual void on_timer() = 0;
};

template<Board::Timer TIMER>
class Timer
{
protected:
	using TRAIT = Board::Timer_trait<TIMER>;
	using PRESCALERS_TRAIT = typename TRAIT::PRESCALERS_TRAIT;

public:
	using TIMER_TYPE = typename TRAIT::TYPE;
	using TIMER_PRESCALER = typename PRESCALERS_TRAIT::TYPE;

	Timer(TimerCallback& callback) INLINE
	{
		_callback = &callback;
	}

	static constexpr bool is_adequate(TIMER_PRESCALER p, uint32_t us)
	{
		return prescaler_is_adequate(prescaler_quotient(p, us));
	}
	static constexpr TIMER_PRESCALER prescaler(uint32_t us)
	{
		return best_prescaler(PRESCALERS_TRAIT::ALL_PRESCALERS, us);
	}
	static constexpr TIMER_TYPE counter(TIMER_PRESCALER prescaler, uint32_t us)
	{
		return (TIMER_TYPE) prescaler_quotient(prescaler, us) - 1;
	}
	static constexpr TIMER_TYPE counter(uint32_t us)
	{
		return (TIMER_TYPE) prescaler_quotient(prescaler(us), us) - 1;
	}

	inline void begin(TIMER_PRESCALER prescaler, TIMER_TYPE max)
	{
		synchronized _begin(prescaler, max);
	}

	// We'll need additional methods in Timer_trait<>
	inline void _begin(TIMER_PRESCALER prescaler, TIMER_TYPE max)
	{
		// OCnA & OCnB disconnected, CTC (Clear Timer on Compare match)
		(volatile uint8_t&) TRAIT::TCCRA = TRAIT::CTC_TCCRA;
		// Don't force output compare (FOCA & FOCB), Clock Select according to prescaler
		(volatile uint8_t&) TRAIT::TCCRB = TRAIT::CTC_TCCRB | TRAIT::TCCRB_prescaler(prescaler);
		// Set timer counter compare match (when value reached, 1ms has elapsed)
		(volatile TIMER_TYPE&) TRAIT::OCRA = max;
		// Reset timer counter
		(volatile TIMER_TYPE&) TRAIT::TCNT = 0;
		// Set timer interrupt mode (set interrupt on OCRnA compare match)
		(volatile uint8_t&) TRAIT::TIMSK = _BV(OCIE0A);
	}
	inline void suspend()
	{
		synchronized _suspend();
	}
	inline void _suspend()
	{
		// Clear timer interrupt mode
		(volatile uint8_t&) TRAIT::TIMSK = 0;
	}
	inline void resume()
	{
		synchronized _resume();
	}
	inline void _resume()
	{
		// Reset timer counter
		(volatile TIMER_TYPE&) TRAIT::TCNT = 0;
		// Set timer interrupt mode (set interrupt on OCRnA compare match)
		(volatile uint8_t&) TRAIT::TIMSK = _BV(OCIE0A);
	}
	inline bool is_suspended()
	{
		return (volatile uint8_t&) TRAIT::TIMSK == 0;
	}
	inline void end()
	{
		synchronized _end();
	}
	inline void _end()
	{
		// Stop timer
		(volatile uint8_t&) TRAIT::TCCRB = 0;
		// Clear timer interrupt mode (set interrupt on OCRnA compare match)
		(volatile uint8_t&) TRAIT::TIMSK = 0;
	}

private:
	static constexpr uint32_t prescaler_quotient(TIMER_PRESCALER p, uint32_t us)
	{
		return (F_CPU / 1000000UL * us) / _BV(uint8_t(p));
	}

	static constexpr uint32_t prescaler_remainder(TIMER_PRESCALER p, uint32_t us)
	{
		return (F_CPU / 1000000UL * us) % _BV(uint8_t(p));
	}

	static constexpr bool prescaler_is_adequate(uint32_t quotient)
	{
		return quotient > 1 and quotient < TRAIT::MAX_COUNTER;
	}

	static constexpr TIMER_PRESCALER best_prescaler_in_2(TIMER_PRESCALER p1, TIMER_PRESCALER p2, uint32_t us)
	{
		return (!prescaler_is_adequate(prescaler_quotient(p1, us)) ? p2 :
				!prescaler_is_adequate(prescaler_quotient(p2, us)) ? p1 :
				prescaler_remainder(p1, us) < prescaler_remainder(p2, us) ? p1 :
				prescaler_remainder(p1, us) > prescaler_remainder(p2, us) ? p2 :
				prescaler_quotient(p1, us) > prescaler_quotient(p2, us) ? p1 : p2);
	}

	static constexpr TIMER_PRESCALER best_prescaler(const TIMER_PRESCALER* begin, const TIMER_PRESCALER* end, uint32_t us)
	{
		return (begin + 1 == end ? *begin : best_prescaler_in_2(*begin, best_prescaler(begin + 1 , end, us), us));
	}

	template<size_t N>
	static constexpr TIMER_PRESCALER best_prescaler(const TIMER_PRESCALER(&prescalers)[N], uint32_t us)
	{
		return best_prescaler(prescalers, prescalers + N, us);
	}

	static void call_back_if_needed()
	{
		if (GPIOR0 == uint8_t(TIMER))
			_callback->on_timer();
	}

	static TimerCallback* _callback;

	template<Board::Timer, Board::Timer...> friend struct timer_impl::CallbackHandler;
};

template<Board::Timer TIMER>
TimerCallback* Timer<TIMER>::_callback = 0;

namespace timer_impl
{
	template<Board::Timer... TS>
	struct ISRHandler
	{
		static void callback() __attribute__((naked))
		{
			asm volatile(
				"push r1\n\t"
				"push r0\n\t"
				"in r0, __SREG__\n\t"
				"push r0\n\t"
				"eor r1, r1\n\t"
				"push r18\n\t"
				"push r19\n\t"
				"push r20\n\t"
				"push r21\n\t"
				"push r22\n\t"
				"push r23\n\t"
				"push r24\n\t"
				"push r25\n\t"
				"push r26\n\t"
				"push r27\n\t"
				"push r30\n\t"
				"push r31\n\t"
			);
			CallbackHandler<TS...>::callback();
			asm volatile(
				"pop r31\n\t"
				"pop r30\n\t"
				"pop r27\n\t"
				"pop r26\n\t"
				"pop r25\n\t"
				"pop r24\n\t"
				"pop r23\n\t"
				"pop r22\n\t"
				"pop r21\n\t"
				"pop r20\n\t"
				"pop r19\n\t"
				"pop r18\n\t"
				"pop r0\n\t"
				"out __SREG__, r0\n\t"
				"pop r0\n\t"
				"pop r1\n\t"
				"ret\n\t"
			);
		}
	};

	template<Board::Timer T0, Board::Timer... TS>
	struct CallbackHandler
	{
		static void callback()
		{
			CallbackHandler<T0>::callback();
			CallbackHandler<TS...>::callback();
		}
	};

	template<Board::Timer T>
	struct CallbackHandler<T>
	{
		static void callback()
		{
			Timer<T>::call_back_if_needed();
		}
	};
}

#endif /* TIMER_HH */
<|endoftext|>