<commit_before>// Copyright (c) 2009 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 <limits>
#include <set>
#include <string>

#include "chrome/browser/history/text_database.h"

#include "app/sql/statement.h"
#include "app/sql/transaction.h"
#include "base/file_util.h"
#include "base/histogram.h"
#include "base/logging.h"
#include "base/string_number_conversions.h"
#include "base/string_util.h"
#include "base/utf_string_conversions.h"
#include "chrome/browser/diagnostics/sqlite_diagnostics.h"

// There are two tables in each database, one full-text search (FTS) table which
// indexes the contents and title of the pages. The other is a regular SQLITE
// table which contains non-indexed information about the page. All columns of
// a FTS table are indexed using the text search algorithm, which isn't what we
// want for things like times. If this were in the FTS table, there would be
// different words in the index for each time number.
//
// "pages" FTS table:
//   url    URL of the page so searches will match the URL.
//   title  Title of the page.
//   body   Body of the page.
//
// "info" regular table:
//   time     Time the corresponding FTS entry was visited.
//
// We do joins across these two tables by using their internal rowids, which we
// keep in sync between the two tables. The internal rowid is the only part of
// an FTS table that is indexed like a normal table, and the index over it is
// free since sqlite always indexes the internal rowid.

namespace history {

namespace {

// Version 1 uses FTS2 for index files.
// Version 2 uses FTS3.
static const int kCurrentVersionNumber = 2;
static const int kCompatibleVersionNumber = 2;

// Snippet computation relies on the index of the columns in the original
// create statement. These are the 0-based indices (as strings) of the
// corresponding columns.
const char kTitleColumnIndex[] = "1";
const char kBodyColumnIndex[] = "2";

// The string prepended to the database identifier to generate the filename.
const FilePath::CharType kFilePrefix[] = FILE_PATH_LITERAL("History Index ");

}  // namespace

TextDatabase::TextDatabase(const FilePath& path,
                           DBIdent id,
                           bool allow_create)
    : path_(path),
      ident_(id),
      allow_create_(allow_create) {
  // Compute the file name.
  file_name_ = path_.Append(IDToFileName(ident_));
}

TextDatabase::~TextDatabase() {
}

// static
const FilePath::CharType* TextDatabase::file_base() {
  return kFilePrefix;
}

// static
FilePath TextDatabase::IDToFileName(DBIdent id) {
  // Identifiers are intended to be a combination of the year and month, for
  // example, 200801 for January 2008. We convert this to
  // "History Index 2008-01". However, we don't make assumptions about this
  // scheme: the caller should assign IDs as it feels fit with the knowledge
  // that they will apppear on disk in this form.
  FilePath::StringType filename(file_base());
  StringAppendF(&filename, FILE_PATH_LITERAL("%d-%02d"),
                id / 100, id % 100);
  return FilePath(filename);
}

// static
TextDatabase::DBIdent TextDatabase::FileNameToID(const FilePath& file_path) {
  FilePath::StringType file_name = file_path.BaseName().value();

  // We don't actually check the prefix here. Since the file system could
  // be case insensitive in ways we can't predict (NTFS), checking could
  // potentially be the wrong thing to do. Instead, we just look for a suffix.
  static const size_t kIDStringLength = 7;  // Room for "xxxx-xx".
  if (file_name.length() < kIDStringLength)
    return 0;
  const FilePath::StringType suffix(
      &file_name[file_name.length() - kIDStringLength]);

  if (suffix.length() != kIDStringLength ||
      suffix[4] != FILE_PATH_LITERAL('-')) {
    return 0;
  }

  int year, month;
  base::StringToInt(suffix.substr(0, 4), &year);
  base::StringToInt(suffix.substr(5, 2), &month);

  return year * 100 + month;
}

bool TextDatabase::Init() {
  // Make sure, if we're not allowed to create the file, that it exists.
  if (!allow_create_) {
    if (!file_util::PathExists(file_name_))
      return false;
  }

  // Set the exceptional sqlite error handler.
  db_.set_error_delegate(GetErrorHandlerForTextDb());

  // Set the database page size to something a little larger to give us
  // better performance (we're typically seek rather than bandwidth limited).
  // This only has an effect before any tables have been created, otherwise
  // this is a NOP. Must be a power of 2 and a max of 8192.
  db_.set_page_size(2096);

  // The default cache size is 2000 which give >8MB of data. Since we will often
  // have 2-3 of these objects, each with their own 8MB, this adds up very fast.
  // We therefore reduce the size so when there are multiple objects, we're not
  // too big.
  db_.set_cache_size(512);

  // Run the database in exclusive mode. Nobody else should be accessing the
  // database while we're running, and this will give somewhat improved perf.
  db_.set_exclusive_locking();

  // Attach the database to our index file.
  if (!db_.Open(file_name_))
    return false;

  // Meta table tracking version information.
  if (!meta_table_.Init(&db_, kCurrentVersionNumber, kCompatibleVersionNumber))
    return false;
  if (meta_table_.GetCompatibleVersionNumber() > kCurrentVersionNumber) {
    // This version is too new. We don't bother notifying the user on this
    // error, and just fail to use the file. Normally if they have version skew,
    // they will get it for the main history file and it won't be necessary
    // here. If that's not the case, since this is only indexed data, it's
    // probably better to just not give FTS results than strange errors when
    // everything else is working OK.
    LOG(WARNING) << "Text database is too new.";
    return false;
  }

  return CreateTables();
}

void TextDatabase::BeginTransaction() {
  db_.BeginTransaction();
}

void TextDatabase::CommitTransaction() {
  db_.CommitTransaction();
}

bool TextDatabase::CreateTables() {
  // FTS table of page contents.
  if (!db_.DoesTableExist("pages")) {
    if (!db_.Execute("CREATE VIRTUAL TABLE pages USING fts3("
                     "TOKENIZE icu,"
                     "url LONGVARCHAR,"
                     "title LONGVARCHAR,"
                     "body LONGVARCHAR)"))
      return false;
  }

  // Non-FTS table containing URLs and times so we can efficiently find them
  // using a regular index (all FTS columns are special and are treated as
  // full-text-search, which is not what we want when retrieving this data).
  if (!db_.DoesTableExist("info")) {
    // Note that there is no point in creating an index over time. Since
    // we must always query the entire FTS table (it can not efficiently do
    // subsets), we will always end up doing that first, and joining the info
    // table off of that.
    if (!db_.Execute("CREATE TABLE info(time INTEGER NOT NULL)"))
      return false;
  }

  // Create the index. This will fail when the index already exists, so we just
  // ignore the error.
  db_.Execute("CREATE INDEX info_time ON info(time)");
  return true;
}

bool TextDatabase::AddPageData(base::Time time,
                               const std::string& url,
                               const std::string& title,
                               const std::string& contents) {
  sql::Transaction committer(&db_);
  if (!committer.Begin())
    return false;

  // Add to the pages table.
  sql::Statement add_to_pages(db_.GetCachedStatement(SQL_FROM_HERE,
      "INSERT INTO pages (url, title, body) VALUES (?,?,?)"));
  if (!add_to_pages) {
    NOTREACHED() << db_.GetErrorMessage();
    return false;
  }
  add_to_pages.BindString(0, url);
  add_to_pages.BindString(1, title);
  add_to_pages.BindString(2, contents);
  if (!add_to_pages.Run()) {
    NOTREACHED() << db_.GetErrorMessage();
    return false;
  }

  int64 rowid = db_.GetLastInsertRowId();

  // Add to the info table with the same rowid.
  sql::Statement add_to_info(db_.GetCachedStatement(SQL_FROM_HERE,
      "INSERT INTO info (rowid, time) VALUES (?,?)"));
  if (!add_to_info) {
    NOTREACHED() << db_.GetErrorMessage();
    return false;
  }
  add_to_info.BindInt64(0, rowid);
  add_to_info.BindInt64(1, time.ToInternalValue());
  if (!add_to_info.Run()) {
    NOTREACHED() << db_.GetErrorMessage();
    return false;
  }

  return committer.Commit();
}

void TextDatabase::DeletePageData(base::Time time, const std::string& url) {
  // First get all rows that match. Selecing on time (which has an index) allows
  // us to avoid brute-force searches on the full-text-index table (there will
  // generally be only one match per time).
  sql::Statement select_ids(db_.GetCachedStatement(SQL_FROM_HERE,
      "SELECT info.rowid "
      "FROM info JOIN pages ON info.rowid = pages.rowid "
      "WHERE info.time=? AND pages.url=?"));
  if (!select_ids)
    return;
  select_ids.BindInt64(0, time.ToInternalValue());
  select_ids.BindString(1, url);
  std::set<int64> rows_to_delete;
  while (select_ids.Step())
    rows_to_delete.insert(select_ids.ColumnInt64(0));

  // Delete from the pages table.
  sql::Statement delete_page(db_.GetCachedStatement(SQL_FROM_HERE,
      "DELETE FROM pages WHERE rowid=?"));
  if (!delete_page)
    return;
  for (std::set<int64>::const_iterator i = rows_to_delete.begin();
       i != rows_to_delete.end(); ++i) {
    delete_page.BindInt64(0, *i);
    if (!delete_page.Run()) {
      NOTREACHED();
      return;
    }
    delete_page.Reset();
  }

  // Delete from the info table.
  sql::Statement delete_info(db_.GetCachedStatement(SQL_FROM_HERE,
      "DELETE FROM info WHERE rowid=?"));
  if (!delete_info)
    return;
  for (std::set<int64>::const_iterator i = rows_to_delete.begin();
       i != rows_to_delete.end(); ++i) {
    delete_info.BindInt64(0, *i);
    if (!delete_info.Run()) {
      NOTREACHED();
      return;
    }
    delete_info.Reset();
  }
}

void TextDatabase::Optimize() {
  sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE,
      "SELECT OPTIMIZE(pages) FROM pages LIMIT 1"));
  if (!statement)
    return;
  statement.Run();
}

void TextDatabase::GetTextMatches(const std::string& query,
                                  const QueryOptions& options,
                                  std::vector<Match>* results,
                                  URLSet* found_urls,
                                  base::Time* first_time_searched) {
  *first_time_searched = options.begin_time;

  sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE,
    "SELECT url, title, time, offsets(pages), body "
        "FROM pages LEFT OUTER JOIN info ON pages.rowid = info.rowid "
        "WHERE pages MATCH ? AND time >= ? AND time < ? "
        "ORDER BY time DESC "
        "LIMIT ?"));
  if (!statement)
    return;

  // When their values indicate "unspecified", saturate the numbers to the max
  // or min to get the correct result.
  int64 effective_begin_time = options.begin_time.is_null() ?
      0 : options.begin_time.ToInternalValue();
  int64 effective_end_time = options.end_time.is_null() ?
      std::numeric_limits<int64>::max() : options.end_time.ToInternalValue();
  int effective_max_count = options.max_count ?
      options.max_count : std::numeric_limits<int>::max();

  statement.BindString(0, query);
  statement.BindInt64(1, effective_begin_time);
  statement.BindInt64(2, effective_end_time);
  statement.BindInt(3, effective_max_count);

  while (statement.Step()) {
    // TODO(brettw) allow canceling the query in the middle.
    // if (canceled_or_something)
    //   break;

    GURL url(statement.ColumnString(0));
    URLSet::const_iterator found_url = found_urls->find(url);
    if (found_url != found_urls->end())
      continue;  // Don't add this duplicate.

    // Fill the results into the vector (avoid copying the URL with Swap()).
    results->resize(results->size() + 1);
    Match& match = results->at(results->size() - 1);
    match.url.Swap(&url);

    match.title = statement.ColumnString16(1);
    match.time = base::Time::FromInternalValue(statement.ColumnInt64(2));

    // Extract any matches in the title.
    std::string offsets_str = statement.ColumnString(3);
    Snippet::ExtractMatchPositions(offsets_str, kTitleColumnIndex,
                                   &match.title_match_positions);
    Snippet::ConvertMatchPositionsToWide(statement.ColumnString(1),
                                         &match.title_match_positions);

    // Extract the matches in the body.
    Snippet::MatchPositions match_positions;
    Snippet::ExtractMatchPositions(offsets_str, kBodyColumnIndex,
                                   &match_positions);

    // Compute the snippet based on those matches.
    std::string body = statement.ColumnString(4);
    match.snippet.ComputeSnippet(match_positions, body);
  }

  // When we have returned all the results possible (or determined that there
  // are none), then we have searched all the time requested, so we can
  // set the first_time_searched to that value.
  if (results->size() == 0 ||
      options.max_count == 0 ||  // Special case for wanting all the results.
      static_cast<int>(results->size()) < options.max_count) {
    *first_time_searched = options.begin_time;
  } else {
    // Since we got the results in order, we know the last item is the last
    // time we considered.
    *first_time_searched = results->back().time;
  }

  statement.Reset();
}

}  // namespace history
<commit_msg>Fix numeric typo. The page size needs to be a power of 2 between 512 and 8192, inclusive. This was 4096 before r26022 but it was broken in that revision despite the comment just above.<commit_after>// Copyright (c) 2009 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 <limits>
#include <set>
#include <string>

#include "chrome/browser/history/text_database.h"

#include "app/sql/statement.h"
#include "app/sql/transaction.h"
#include "base/file_util.h"
#include "base/histogram.h"
#include "base/logging.h"
#include "base/string_number_conversions.h"
#include "base/string_util.h"
#include "base/utf_string_conversions.h"
#include "chrome/browser/diagnostics/sqlite_diagnostics.h"

// There are two tables in each database, one full-text search (FTS) table which
// indexes the contents and title of the pages. The other is a regular SQLITE
// table which contains non-indexed information about the page. All columns of
// a FTS table are indexed using the text search algorithm, which isn't what we
// want for things like times. If this were in the FTS table, there would be
// different words in the index for each time number.
//
// "pages" FTS table:
//   url    URL of the page so searches will match the URL.
//   title  Title of the page.
//   body   Body of the page.
//
// "info" regular table:
//   time     Time the corresponding FTS entry was visited.
//
// We do joins across these two tables by using their internal rowids, which we
// keep in sync between the two tables. The internal rowid is the only part of
// an FTS table that is indexed like a normal table, and the index over it is
// free since sqlite always indexes the internal rowid.

namespace history {

namespace {

// Version 1 uses FTS2 for index files.
// Version 2 uses FTS3.
static const int kCurrentVersionNumber = 2;
static const int kCompatibleVersionNumber = 2;

// Snippet computation relies on the index of the columns in the original
// create statement. These are the 0-based indices (as strings) of the
// corresponding columns.
const char kTitleColumnIndex[] = "1";
const char kBodyColumnIndex[] = "2";

// The string prepended to the database identifier to generate the filename.
const FilePath::CharType kFilePrefix[] = FILE_PATH_LITERAL("History Index ");

}  // namespace

TextDatabase::TextDatabase(const FilePath& path,
                           DBIdent id,
                           bool allow_create)
    : path_(path),
      ident_(id),
      allow_create_(allow_create) {
  // Compute the file name.
  file_name_ = path_.Append(IDToFileName(ident_));
}

TextDatabase::~TextDatabase() {
}

// static
const FilePath::CharType* TextDatabase::file_base() {
  return kFilePrefix;
}

// static
FilePath TextDatabase::IDToFileName(DBIdent id) {
  // Identifiers are intended to be a combination of the year and month, for
  // example, 200801 for January 2008. We convert this to
  // "History Index 2008-01". However, we don't make assumptions about this
  // scheme: the caller should assign IDs as it feels fit with the knowledge
  // that they will apppear on disk in this form.
  FilePath::StringType filename(file_base());
  StringAppendF(&filename, FILE_PATH_LITERAL("%d-%02d"),
                id / 100, id % 100);
  return FilePath(filename);
}

// static
TextDatabase::DBIdent TextDatabase::FileNameToID(const FilePath& file_path) {
  FilePath::StringType file_name = file_path.BaseName().value();

  // We don't actually check the prefix here. Since the file system could
  // be case insensitive in ways we can't predict (NTFS), checking could
  // potentially be the wrong thing to do. Instead, we just look for a suffix.
  static const size_t kIDStringLength = 7;  // Room for "xxxx-xx".
  if (file_name.length() < kIDStringLength)
    return 0;
  const FilePath::StringType suffix(
      &file_name[file_name.length() - kIDStringLength]);

  if (suffix.length() != kIDStringLength ||
      suffix[4] != FILE_PATH_LITERAL('-')) {
    return 0;
  }

  int year, month;
  base::StringToInt(suffix.substr(0, 4), &year);
  base::StringToInt(suffix.substr(5, 2), &month);

  return year * 100 + month;
}

bool TextDatabase::Init() {
  // Make sure, if we're not allowed to create the file, that it exists.
  if (!allow_create_) {
    if (!file_util::PathExists(file_name_))
      return false;
  }

  // Set the exceptional sqlite error handler.
  db_.set_error_delegate(GetErrorHandlerForTextDb());

  // Set the database page size to something a little larger to give us
  // better performance (we're typically seek rather than bandwidth limited).
  // This only has an effect before any tables have been created, otherwise
  // this is a NOP. Must be a power of 2 and a max of 8192.
  db_.set_page_size(4096);

  // The default cache size is 2000 which give >8MB of data. Since we will often
  // have 2-3 of these objects, each with their own 8MB, this adds up very fast.
  // We therefore reduce the size so when there are multiple objects, we're not
  // too big.
  db_.set_cache_size(512);

  // Run the database in exclusive mode. Nobody else should be accessing the
  // database while we're running, and this will give somewhat improved perf.
  db_.set_exclusive_locking();

  // Attach the database to our index file.
  if (!db_.Open(file_name_))
    return false;

  // Meta table tracking version information.
  if (!meta_table_.Init(&db_, kCurrentVersionNumber, kCompatibleVersionNumber))
    return false;
  if (meta_table_.GetCompatibleVersionNumber() > kCurrentVersionNumber) {
    // This version is too new. We don't bother notifying the user on this
    // error, and just fail to use the file. Normally if they have version skew,
    // they will get it for the main history file and it won't be necessary
    // here. If that's not the case, since this is only indexed data, it's
    // probably better to just not give FTS results than strange errors when
    // everything else is working OK.
    LOG(WARNING) << "Text database is too new.";
    return false;
  }

  return CreateTables();
}

void TextDatabase::BeginTransaction() {
  db_.BeginTransaction();
}

void TextDatabase::CommitTransaction() {
  db_.CommitTransaction();
}

bool TextDatabase::CreateTables() {
  // FTS table of page contents.
  if (!db_.DoesTableExist("pages")) {
    if (!db_.Execute("CREATE VIRTUAL TABLE pages USING fts3("
                     "TOKENIZE icu,"
                     "url LONGVARCHAR,"
                     "title LONGVARCHAR,"
                     "body LONGVARCHAR)"))
      return false;
  }

  // Non-FTS table containing URLs and times so we can efficiently find them
  // using a regular index (all FTS columns are special and are treated as
  // full-text-search, which is not what we want when retrieving this data).
  if (!db_.DoesTableExist("info")) {
    // Note that there is no point in creating an index over time. Since
    // we must always query the entire FTS table (it can not efficiently do
    // subsets), we will always end up doing that first, and joining the info
    // table off of that.
    if (!db_.Execute("CREATE TABLE info(time INTEGER NOT NULL)"))
      return false;
  }

  // Create the index. This will fail when the index already exists, so we just
  // ignore the error.
  db_.Execute("CREATE INDEX info_time ON info(time)");
  return true;
}

bool TextDatabase::AddPageData(base::Time time,
                               const std::string& url,
                               const std::string& title,
                               const std::string& contents) {
  sql::Transaction committer(&db_);
  if (!committer.Begin())
    return false;

  // Add to the pages table.
  sql::Statement add_to_pages(db_.GetCachedStatement(SQL_FROM_HERE,
      "INSERT INTO pages (url, title, body) VALUES (?,?,?)"));
  if (!add_to_pages) {
    NOTREACHED() << db_.GetErrorMessage();
    return false;
  }
  add_to_pages.BindString(0, url);
  add_to_pages.BindString(1, title);
  add_to_pages.BindString(2, contents);
  if (!add_to_pages.Run()) {
    NOTREACHED() << db_.GetErrorMessage();
    return false;
  }

  int64 rowid = db_.GetLastInsertRowId();

  // Add to the info table with the same rowid.
  sql::Statement add_to_info(db_.GetCachedStatement(SQL_FROM_HERE,
      "INSERT INTO info (rowid, time) VALUES (?,?)"));
  if (!add_to_info) {
    NOTREACHED() << db_.GetErrorMessage();
    return false;
  }
  add_to_info.BindInt64(0, rowid);
  add_to_info.BindInt64(1, time.ToInternalValue());
  if (!add_to_info.Run()) {
    NOTREACHED() << db_.GetErrorMessage();
    return false;
  }

  return committer.Commit();
}

void TextDatabase::DeletePageData(base::Time time, const std::string& url) {
  // First get all rows that match. Selecing on time (which has an index) allows
  // us to avoid brute-force searches on the full-text-index table (there will
  // generally be only one match per time).
  sql::Statement select_ids(db_.GetCachedStatement(SQL_FROM_HERE,
      "SELECT info.rowid "
      "FROM info JOIN pages ON info.rowid = pages.rowid "
      "WHERE info.time=? AND pages.url=?"));
  if (!select_ids)
    return;
  select_ids.BindInt64(0, time.ToInternalValue());
  select_ids.BindString(1, url);
  std::set<int64> rows_to_delete;
  while (select_ids.Step())
    rows_to_delete.insert(select_ids.ColumnInt64(0));

  // Delete from the pages table.
  sql::Statement delete_page(db_.GetCachedStatement(SQL_FROM_HERE,
      "DELETE FROM pages WHERE rowid=?"));
  if (!delete_page)
    return;
  for (std::set<int64>::const_iterator i = rows_to_delete.begin();
       i != rows_to_delete.end(); ++i) {
    delete_page.BindInt64(0, *i);
    if (!delete_page.Run()) {
      NOTREACHED();
      return;
    }
    delete_page.Reset();
  }

  // Delete from the info table.
  sql::Statement delete_info(db_.GetCachedStatement(SQL_FROM_HERE,
      "DELETE FROM info WHERE rowid=?"));
  if (!delete_info)
    return;
  for (std::set<int64>::const_iterator i = rows_to_delete.begin();
       i != rows_to_delete.end(); ++i) {
    delete_info.BindInt64(0, *i);
    if (!delete_info.Run()) {
      NOTREACHED();
      return;
    }
    delete_info.Reset();
  }
}

void TextDatabase::Optimize() {
  sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE,
      "SELECT OPTIMIZE(pages) FROM pages LIMIT 1"));
  if (!statement)
    return;
  statement.Run();
}

void TextDatabase::GetTextMatches(const std::string& query,
                                  const QueryOptions& options,
                                  std::vector<Match>* results,
                                  URLSet* found_urls,
                                  base::Time* first_time_searched) {
  *first_time_searched = options.begin_time;

  sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE,
    "SELECT url, title, time, offsets(pages), body "
        "FROM pages LEFT OUTER JOIN info ON pages.rowid = info.rowid "
        "WHERE pages MATCH ? AND time >= ? AND time < ? "
        "ORDER BY time DESC "
        "LIMIT ?"));
  if (!statement)
    return;

  // When their values indicate "unspecified", saturate the numbers to the max
  // or min to get the correct result.
  int64 effective_begin_time = options.begin_time.is_null() ?
      0 : options.begin_time.ToInternalValue();
  int64 effective_end_time = options.end_time.is_null() ?
      std::numeric_limits<int64>::max() : options.end_time.ToInternalValue();
  int effective_max_count = options.max_count ?
      options.max_count : std::numeric_limits<int>::max();

  statement.BindString(0, query);
  statement.BindInt64(1, effective_begin_time);
  statement.BindInt64(2, effective_end_time);
  statement.BindInt(3, effective_max_count);

  while (statement.Step()) {
    // TODO(brettw) allow canceling the query in the middle.
    // if (canceled_or_something)
    //   break;

    GURL url(statement.ColumnString(0));
    URLSet::const_iterator found_url = found_urls->find(url);
    if (found_url != found_urls->end())
      continue;  // Don't add this duplicate.

    // Fill the results into the vector (avoid copying the URL with Swap()).
    results->resize(results->size() + 1);
    Match& match = results->at(results->size() - 1);
    match.url.Swap(&url);

    match.title = statement.ColumnString16(1);
    match.time = base::Time::FromInternalValue(statement.ColumnInt64(2));

    // Extract any matches in the title.
    std::string offsets_str = statement.ColumnString(3);
    Snippet::ExtractMatchPositions(offsets_str, kTitleColumnIndex,
                                   &match.title_match_positions);
    Snippet::ConvertMatchPositionsToWide(statement.ColumnString(1),
                                         &match.title_match_positions);

    // Extract the matches in the body.
    Snippet::MatchPositions match_positions;
    Snippet::ExtractMatchPositions(offsets_str, kBodyColumnIndex,
                                   &match_positions);

    // Compute the snippet based on those matches.
    std::string body = statement.ColumnString(4);
    match.snippet.ComputeSnippet(match_positions, body);
  }

  // When we have returned all the results possible (or determined that there
  // are none), then we have searched all the time requested, so we can
  // set the first_time_searched to that value.
  if (results->size() == 0 ||
      options.max_count == 0 ||  // Special case for wanting all the results.
      static_cast<int>(results->size()) < options.max_count) {
    *first_time_searched = options.begin_time;
  } else {
    // Since we got the results in order, we know the last item is the last
    // time we considered.
    *first_time_searched = results->back().time;
  }

  statement.Reset();
}

}  // namespace history
<|endoftext|>


<commit_before>// Copyright (c) 2009 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 "chrome/browser/process_singleton.h"

#include "base/base_paths.h"
#include "base/command_line.h"
#include "base/process_util.h"
#include "base/win_util.h"
#include "chrome/browser/browser_init.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/profile.h"
#include "chrome/browser/profile_manager.h"
#include "chrome/common/chrome_constants.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/l10n_util.h"
#include "chrome/common/result_codes.h"
#include "chrome/common/win_util.h"
#include "grit/chromium_strings.h"
#include "grit/generated_resources.h"

namespace {

// Checks the visiblilty of the enumerated window and signals once a visible
// window has been found.
BOOL CALLBACK BrowserWindowEnumeration(HWND window, LPARAM param) {
  bool* result = reinterpret_cast<bool*>(param);
  *result = IsWindowVisible(window) != 0;
  // Stops enumeration if a visible window has been found.
  return !*result;
}

}  // namespace

ProcessSingleton::ProcessSingleton(const FilePath& user_data_dir)
    : window_(NULL),
      locked_(false) {
  // Look for a Chrome instance that uses the same profile directory:
  remote_window_ = FindWindowEx(HWND_MESSAGE,
                                NULL,
                                chrome::kMessageWindowClass,
                                user_data_dir.ToWStringHack().c_str());
}

ProcessSingleton::~ProcessSingleton() {
  if (window_)
    DestroyWindow(window_);
}

bool ProcessSingleton::NotifyOtherProcess() {
  if (!remote_window_)
    return false;

  // Found another window, send our command line to it
  // format is "START\0<<<current directory>>>\0<<<commandline>>>".
  std::wstring to_send(L"START\0", 6);  // want the NULL in the string.
  std::wstring cur_dir;
  if (!PathService::Get(base::DIR_CURRENT, &cur_dir))
    return false;
  to_send.append(cur_dir);
  to_send.append(L"\0", 1);  // Null separator.
  to_send.append(GetCommandLineW());
  to_send.append(L"\0", 1);  // Null separator.

  // Allow the current running browser window making itself the foreground
  // window (otherwise it will just flash in the taskbar).
  DWORD process_id = 0;
  DWORD thread_id = GetWindowThreadProcessId(remote_window_, &process_id);
  // It is possible that the process owning this window may have died by now.
  if (!thread_id || !process_id) {
    remote_window_ = NULL;
    return false;
  }

  AllowSetForegroundWindow(process_id);

  // Gives 20 seconds timeout for the current browser process to respond.
  const int kTimeout = 20000;
  COPYDATASTRUCT cds;
  cds.dwData = 0;
  cds.cbData = static_cast<DWORD>((to_send.length() + 1) * sizeof(wchar_t));
  cds.lpData = const_cast<wchar_t*>(to_send.c_str());
  DWORD_PTR result = 0;
  if (SendMessageTimeout(remote_window_,
                         WM_COPYDATA,
                         NULL,
                         reinterpret_cast<LPARAM>(&cds),
                         SMTO_ABORTIFHUNG,
                         kTimeout,
                         &result)) {
    // It is possible that the process owning this window may have died by now.
    if (!result) {
      remote_window_ = NULL;
      return false;
    }
    return true;
  }

  // It is possible that the process owning this window may have died by now.
  if (!IsWindow(remote_window_)) {
    remote_window_ = NULL;
    return false;
  }

  // The window is hung. Scan for every window to find a visible one.
  bool visible_window = false;
  EnumThreadWindows(thread_id,
                    &BrowserWindowEnumeration,
                    reinterpret_cast<LPARAM>(&visible_window));

  // If there is a visible browser window, ask the user before killing it.
  if (visible_window) {
    std::wstring text = l10n_util::GetString(IDS_BROWSER_HUNGBROWSER_MESSAGE);
    std::wstring caption = l10n_util::GetString(IDS_PRODUCT_NAME);
    if (IDYES != win_util::MessageBox(NULL, text, caption,
                                      MB_YESNO | MB_ICONSTOP | MB_TOPMOST)) {
      // The user denied. Quit silently.
      return true;
    }
  }

  // Time to take action. Kill the browser process.
  base::KillProcessById(process_id, ResultCodes::HUNG, true);
  remote_window_ = NULL;
  return false;
}

void ProcessSingleton::Create() {
  DCHECK(!window_);
  DCHECK(!remote_window_);
  HINSTANCE hinst = GetModuleHandle(NULL);

  WNDCLASSEX wc = {0};
  wc.cbSize = sizeof(wc);
  wc.lpfnWndProc = ProcessSingleton::WndProcStatic;
  wc.hInstance = hinst;
  wc.lpszClassName = chrome::kMessageWindowClass;
  RegisterClassEx(&wc);

  std::wstring user_data_dir;
  PathService::Get(chrome::DIR_USER_DATA, &user_data_dir);

  // Set the window's title to the path of our user data directory so other
  // Chrome instances can decide if they should forward to us or not.
  window_ = CreateWindow(chrome::kMessageWindowClass, user_data_dir.c_str(),
                         0, 0, 0, 0, 0, HWND_MESSAGE, 0, hinst, 0);
  DCHECK(window_);

  win_util::SetWindowUserData(window_, this);
}

LRESULT ProcessSingleton::OnCopyData(HWND hwnd, const COPYDATASTRUCT* cds) {
  // Ignore the request if the browser process is already in shutdown path.
  if (!g_browser_process || g_browser_process->IsShuttingDown()) {
    LOG(WARNING) << "Not handling WM_COPYDATA as browser is shutting down";
    return FALSE;
  }

  // If locked, it means we are not ready to process this message because
  // we are probably in a first run critical phase.
  if (locked_)
    return TRUE;

  // We should have enough room for the shortest command (min_message_size)
  // and also be a multiple of wchar_t bytes.
  static const int min_message_size = 7;
  if (cds->cbData < min_message_size || cds->cbData % sizeof(wchar_t) != 0) {
    LOG(WARNING) << "Invalid WM_COPYDATA, length = " << cds->cbData;
    return TRUE;
  }

  // We split the string into 4 parts on NULLs.
  const std::wstring msg(static_cast<wchar_t*>(cds->lpData),
                         cds->cbData / sizeof(wchar_t));
  const std::wstring::size_type first_null = msg.find_first_of(L'\0');
  if (first_null == 0 || first_null == std::wstring::npos) {
    // no NULL byte, don't know what to do
    LOG(WARNING) << "Invalid WM_COPYDATA, length = " << msg.length() <<
      ", first null = " << first_null;
    return TRUE;
  }

  // Decode the command, which is everything until the first NULL.
  if (msg.substr(0, first_null) == L"START") {
    // Another instance is starting parse the command line & do what it would
    // have done.
    LOG(INFO) << "Handling STARTUP request from another process";
    const std::wstring::size_type second_null =
      msg.find_first_of(L'\0', first_null + 1);
    if (second_null == std::wstring::npos ||
        first_null == msg.length() - 1 || second_null == msg.length()) {
      LOG(WARNING) << "Invalid format for start command, we need a string in 4 "
        "parts separated by NULLs";
      return TRUE;
    }

    // Get current directory.
    const std::wstring cur_dir =
      msg.substr(first_null + 1, second_null - first_null);

    const std::wstring::size_type third_null =
        msg.find_first_of(L'\0', second_null + 1);
    if (third_null == std::wstring::npos ||
        third_null == msg.length()) {
      LOG(WARNING) << "Invalid format for start command, we need a string in 4 "
        "parts separated by NULLs";
    }

    // Get command line.
    const std::wstring cmd_line =
      msg.substr(second_null + 1, third_null - second_null);

    CommandLine parsed_command_line(L"");
    parsed_command_line.ParseFromString(cmd_line);
    PrefService* prefs = g_browser_process->local_state();
    DCHECK(prefs);

    FilePath user_data_dir;
    PathService::Get(chrome::DIR_USER_DATA, &user_data_dir);
    ProfileManager* profile_manager = g_browser_process->profile_manager();
    Profile* profile = profile_manager->GetDefaultProfile(user_data_dir);
    if (!profile) {
      // We should only be able to get here if the profile already exists and
      // has been created.
      NOTREACHED();
      return TRUE;
    }

    // Run the browser startup sequence again, with the command line of the
    // signalling process.
    BrowserInit::ProcessCommandLine(parsed_command_line, cur_dir, false,
                                    profile, NULL);
    return TRUE;
  }
  return TRUE;
}

LRESULT CALLBACK ProcessSingleton::WndProc(HWND hwnd, UINT message,
                                        WPARAM wparam, LPARAM lparam) {
  switch (message) {
    case WM_COPYDATA:
      return OnCopyData(reinterpret_cast<HWND>(wparam),
                        reinterpret_cast<COPYDATASTRUCT*>(lparam));
    default:
      break;
  }

  return ::DefWindowProc(hwnd, message, wparam, lparam);
}
<commit_msg>Greatly reduce the race window in ProcessSingleton - fix only for windows - Anantha has a good test to be commited RSN<commit_after>// Copyright (c) 2009 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 "chrome/browser/process_singleton.h"

#include "base/base_paths.h"
#include "base/command_line.h"
#include "base/process_util.h"
#include "base/win_util.h"
#include "chrome/browser/browser_init.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/profile.h"
#include "chrome/browser/profile_manager.h"
#include "chrome/common/chrome_constants.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/l10n_util.h"
#include "chrome/common/result_codes.h"
#include "chrome/common/win_util.h"
#include "grit/chromium_strings.h"
#include "grit/generated_resources.h"

namespace {

// Checks the visibility of the enumerated window and signals once a visible
// window has been found.
BOOL CALLBACK BrowserWindowEnumeration(HWND window, LPARAM param) {
  bool* result = reinterpret_cast<bool*>(param);
  *result = IsWindowVisible(window) != 0;
  // Stops enumeration if a visible window has been found.
  return !*result;
}

}  // namespace

// Look for a Chrome instance that uses the same profile directory.
ProcessSingleton::ProcessSingleton(const FilePath& user_data_dir)
    : window_(NULL), locked_(false) {
  // FindWindoEx and Create() should be one atomic operation in order to not
  // have a race condition.
  remote_window_ = FindWindowEx(HWND_MESSAGE, NULL, chrome::kMessageWindowClass,
                                user_data_dir.ToWStringHack().c_str());
  if (!remote_window_)
    Create();
}

ProcessSingleton::~ProcessSingleton() {
  if (window_)
    DestroyWindow(window_);
}

bool ProcessSingleton::NotifyOtherProcess() {
  if (!remote_window_)
    return false;

  // Found another window, send our command line to it
  // format is "START\0<<<current directory>>>\0<<<commandline>>>".
  std::wstring to_send(L"START\0", 6);  // want the NULL in the string.
  std::wstring cur_dir;
  if (!PathService::Get(base::DIR_CURRENT, &cur_dir))
    return false;
  to_send.append(cur_dir);
  to_send.append(L"\0", 1);  // Null separator.
  to_send.append(GetCommandLineW());
  to_send.append(L"\0", 1);  // Null separator.

  // Allow the current running browser window making itself the foreground
  // window (otherwise it will just flash in the taskbar).
  DWORD process_id = 0;
  DWORD thread_id = GetWindowThreadProcessId(remote_window_, &process_id);
  // It is possible that the process owning this window may have died by now.
  if (!thread_id || !process_id) {
    remote_window_ = NULL;
    return false;
  }

  AllowSetForegroundWindow(process_id);

  // Gives 20 seconds timeout for the current browser process to respond.
  const int kTimeout = 20000;
  COPYDATASTRUCT cds;
  cds.dwData = 0;
  cds.cbData = static_cast<DWORD>((to_send.length() + 1) * sizeof(wchar_t));
  cds.lpData = const_cast<wchar_t*>(to_send.c_str());
  DWORD_PTR result = 0;
  if (SendMessageTimeout(remote_window_,
                         WM_COPYDATA,
                         NULL,
                         reinterpret_cast<LPARAM>(&cds),
                         SMTO_ABORTIFHUNG,
                         kTimeout,
                         &result)) {
    // It is possible that the process owning this window may have died by now.
    if (!result) {
      remote_window_ = NULL;
      return false;
    }
    return true;
  }

  // It is possible that the process owning this window may have died by now.
  if (!IsWindow(remote_window_)) {
    remote_window_ = NULL;
    return false;
  }

  // The window is hung. Scan for every window to find a visible one.
  bool visible_window = false;
  EnumThreadWindows(thread_id,
                    &BrowserWindowEnumeration,
                    reinterpret_cast<LPARAM>(&visible_window));

  // If there is a visible browser window, ask the user before killing it.
  if (visible_window) {
    std::wstring text = l10n_util::GetString(IDS_BROWSER_HUNGBROWSER_MESSAGE);
    std::wstring caption = l10n_util::GetString(IDS_PRODUCT_NAME);
    if (IDYES != win_util::MessageBox(NULL, text, caption,
                                      MB_YESNO | MB_ICONSTOP | MB_TOPMOST)) {
      // The user denied. Quit silently.
      return true;
    }
  }

  // Time to take action. Kill the browser process.
  base::KillProcessById(process_id, ResultCodes::HUNG, true);
  remote_window_ = NULL;
  return false;
}

// For windows, there is no need to call Create() since the call is made in
// the constructor but to avoid having more platform specific code in
// browser_main.cc we tolerate a second call which will do nothing.
void ProcessSingleton::Create() {
  DCHECK(!remote_window_);
  if (window_)
    return;

  HINSTANCE hinst = GetModuleHandle(NULL);

  WNDCLASSEX wc = {0};
  wc.cbSize = sizeof(wc);
  wc.lpfnWndProc = ProcessSingleton::WndProcStatic;
  wc.hInstance = hinst;
  wc.lpszClassName = chrome::kMessageWindowClass;
  RegisterClassEx(&wc);

  std::wstring user_data_dir;
  PathService::Get(chrome::DIR_USER_DATA, &user_data_dir);

  // Set the window's title to the path of our user data directory so other
  // Chrome instances can decide if they should forward to us or not.
  window_ = CreateWindow(chrome::kMessageWindowClass, user_data_dir.c_str(),
                         0, 0, 0, 0, 0, HWND_MESSAGE, 0, hinst, 0);
  DCHECK(window_);

  win_util::SetWindowUserData(window_, this);
}

LRESULT ProcessSingleton::OnCopyData(HWND hwnd, const COPYDATASTRUCT* cds) {
  // Ignore the request if the browser process is already in shutdown path.
  if (!g_browser_process || g_browser_process->IsShuttingDown()) {
    LOG(WARNING) << "Not handling WM_COPYDATA as browser is shutting down";
    return FALSE;
  }

  // If locked, it means we are not ready to process this message because
  // we are probably in a first run critical phase.
  if (locked_)
    return TRUE;

  // We should have enough room for the shortest command (min_message_size)
  // and also be a multiple of wchar_t bytes.
  static const int min_message_size = 7;
  if (cds->cbData < min_message_size || cds->cbData % sizeof(wchar_t) != 0) {
    LOG(WARNING) << "Invalid WM_COPYDATA, length = " << cds->cbData;
    return TRUE;
  }

  // We split the string into 4 parts on NULLs.
  const std::wstring msg(static_cast<wchar_t*>(cds->lpData),
                         cds->cbData / sizeof(wchar_t));
  const std::wstring::size_type first_null = msg.find_first_of(L'\0');
  if (first_null == 0 || first_null == std::wstring::npos) {
    // no NULL byte, don't know what to do
    LOG(WARNING) << "Invalid WM_COPYDATA, length = " << msg.length() <<
      ", first null = " << first_null;
    return TRUE;
  }

  // Decode the command, which is everything until the first NULL.
  if (msg.substr(0, first_null) == L"START") {
    // Another instance is starting parse the command line & do what it would
    // have done.
    LOG(INFO) << "Handling STARTUP request from another process";
    const std::wstring::size_type second_null =
      msg.find_first_of(L'\0', first_null + 1);
    if (second_null == std::wstring::npos ||
        first_null == msg.length() - 1 || second_null == msg.length()) {
      LOG(WARNING) << "Invalid format for start command, we need a string in 4 "
        "parts separated by NULLs";
      return TRUE;
    }

    // Get current directory.
    const std::wstring cur_dir =
      msg.substr(first_null + 1, second_null - first_null);

    const std::wstring::size_type third_null =
        msg.find_first_of(L'\0', second_null + 1);
    if (third_null == std::wstring::npos ||
        third_null == msg.length()) {
      LOG(WARNING) << "Invalid format for start command, we need a string in 4 "
        "parts separated by NULLs";
    }

    // Get command line.
    const std::wstring cmd_line =
      msg.substr(second_null + 1, third_null - second_null);

    CommandLine parsed_command_line(L"");
    parsed_command_line.ParseFromString(cmd_line);
    PrefService* prefs = g_browser_process->local_state();
    DCHECK(prefs);

    FilePath user_data_dir;
    PathService::Get(chrome::DIR_USER_DATA, &user_data_dir);
    ProfileManager* profile_manager = g_browser_process->profile_manager();
    Profile* profile = profile_manager->GetDefaultProfile(user_data_dir);
    if (!profile) {
      // We should only be able to get here if the profile already exists and
      // has been created.
      NOTREACHED();
      return TRUE;
    }

    // Run the browser startup sequence again, with the command line of the
    // signalling process.
    BrowserInit::ProcessCommandLine(parsed_command_line, cur_dir, false,
                                    profile, NULL);
    return TRUE;
  }
  return TRUE;
}

LRESULT CALLBACK ProcessSingleton::WndProc(HWND hwnd, UINT message,
                                        WPARAM wparam, LPARAM lparam) {
  switch (message) {
    case WM_COPYDATA:
      return OnCopyData(reinterpret_cast<HWND>(wparam),
                        reinterpret_cast<COPYDATASTRUCT*>(lparam));
    default:
      break;
  }

  return ::DefWindowProc(hwnd, message, wparam, lparam);
}
<|endoftext|>


<commit_before>
#include "stdafx.h"
#include <cstdlib>
#include <stdio.h>
#include <iostream>

#include <zorba/util/time.h>

#include "zorbautils/hashmap.h"
#include "util/hashmap32.h"
#include "util/hashmap.h"
#include "util/unordered_map.h"

namespace zorba {

namespace UnitTests {

class IntCompFunc
{
public:
  static bool equal(uint64_t k1, uint64_t k2)
  {
    return k1 == k2;
  }

  static uint32_t hash(uint64_t key)
  {
#if 1
    return static_cast<uint32_t>(key);
#else
    char buf[9];
    buf[0] = (unsigned char)(key>>56);
    buf[1] = (unsigned char)(key>>48);
    buf[2] = (unsigned char)(key>>40);
    buf[3] = (unsigned char)(key>>32);
    buf[4] = (unsigned char)(key>>24);
    buf[5] = (unsigned char)(key>>16);
    buf[6] = (unsigned char)(key>>8 );
    buf[7] = (unsigned char)(key    );
    buf[8] = 0;
    return (uint32_t)hashfun::h64((void*)buf,8,FNV_64_INIT);
#endif
  }
};

class StrCompFunc
{
public:
  static bool equal(const zstring& k1, const zstring& k2)
  {
    return k1 == k2;
  }

  static uint32_t hash(const zstring& key)
  {
    return hashfun::h32(key.c_str(), FNV_32_INIT);
  }
};

int test_hashmaps(int argc, char* argv[])
{
  if (argc < 4)
    return 1;

  int test_id = atoi(argv[1]);

  double load_factor = atof(argv[2]);

  int num = atoi(argv[3]);

  uint64_t* int_buf = new uint64_t[num];

  for (int i = 0; i < num; ++i)
  {
    int_buf[i] = rand() % num;
  }

  std::string* str_buf = new std::string[num];

  for (int i = 0; i < num; ++i)
  {
    char tmp[20];
    sprintf(tmp, "%d", rand() % num);
    str_buf[i] = tmp;

    //std::cout << str_buf[i] << std::endl;
  }

  HashMap<uint64_t, int, IntCompFunc> map1(1024, false);
  HashMap<std::string, int, StrCompFunc> map2(1024, false);

  std::unordered_map<uint64_t, int> map3(1024);
  std::unordered_map<std::string, int> map4(1024);

  hash64map<int> map5(1024, load_factor);
  hashmap<std::string, int> map6(1024, load_factor);

  map1.set_load_factor(load_factor);
  map2.set_load_factor(load_factor);

  if (test_id == 1)
  {
    zorba::time::walltime startTime;
    zorba::time::get_current_walltime(startTime);

    for (int i = 0; i < num; ++i)
    {
      uint64_t key = int_buf[i];
      int value = 1;
      (void)map1.insert(key, value);
    }

    zorba::time::walltime stopTime;
    time::get_current_walltime(stopTime);

    double time = time::get_walltime_elapsed(startTime, stopTime);

    std::cout << "Load factor = " << load_factor << std::endl
              << "Num numeric insertions = " << num << std::endl
              << "HashMap entries = " << map1.size() << std::endl
              << "HashMap capacity = " << map1.capacity() << std::endl
              << "HashMap colisions = " << map1.collisions() << std::endl
              << "Time = " << time << std::endl;
  }
  else if (test_id == 2)
  {
    zorba::time::walltime startTime;
    zorba::time::get_current_walltime(startTime);

    for (int i = 0; i < num; ++i)
    {
      int value = 1;
      (void)map2.insert(str_buf[i], value);
    }

    zorba::time::walltime stopTime;
    time::get_current_walltime(stopTime);

    double time = time::get_walltime_elapsed(startTime, stopTime);

    std::cout << "Load factor = " << load_factor << std::endl
              << "Num string insertions = " << num << std::endl
              << "HashMap entries = " << map2.size() << std::endl
              << "HashMap buckets = " << map2.bucket_count() << std::endl
              << "HashMap capacity = " << map2.capacity() << std::endl
              << "Time = " << time << std::endl;
  }
  else if (test_id == 3)
  {
    zorba::time::walltime startTime;
    zorba::time::get_current_walltime(startTime);

    for (int i = 0; i < num; ++i)
    {
      uint64_t key = int_buf[i];
      int value = 1;
      (void)map3.insert(std::pair<uint64_t, int>(key, value));
    }

    zorba::time::walltime stopTime;
    time::get_current_walltime(stopTime);

    double time = time::get_walltime_elapsed(startTime, stopTime);

    std::cout << "Load factor = " << load_factor << std::endl
              << "Num numeric insertions = " << num << std::endl
              << "UnorderedMap entries = " << map3.size() << std::endl
              << "UnorderedMap buckets = " << map3.bucket_count() << std::endl
              << "Time = " << time << std::endl;
  }
  else if (test_id == 4)
  {
    zorba::time::walltime startTime;
    zorba::time::get_current_walltime(startTime);

    for (int i = 0; i < num; ++i)
    {
      int value = 1;
      (void)map4.insert(std::pair<std::string, int>(str_buf[i], value));
    }

    zorba::time::walltime stopTime;
    time::get_current_walltime(stopTime);

    double time = time::get_walltime_elapsed(startTime, stopTime);

    std::cout << "Load factor = " << load_factor << std::endl
              << "Num string insertions = " << num << std::endl
              << "UnorderedMap entries = " << map4.size() << std::endl
              << "UnorderedMap buckets = " << map4.bucket_count() << std::endl
              << "Time = " << time << std::endl;
  }
  else if (test_id == 5)
  {
    zorba::time::walltime startTime;
    zorba::time::get_current_walltime(startTime);

    for (int i = 0; i < num; ++i)
    {
      uint64_t key = int_buf[i];
      int value = 1;
      (void)map5.put_unsync(key, value);
    }

    zorba::time::walltime stopTime;
    time::get_current_walltime(stopTime);

    double time = time::get_walltime_elapsed(startTime, stopTime);

    std::cout << "Load factor = " << load_factor << std::endl
              << "Num numeric insertions = " << num << std::endl
              << "hashmap entries = " << map5.size() << std::endl
              << "Time = " << time << std::endl;
  }
  else if (test_id == 6)
  {
    zorba::time::walltime startTime;
    zorba::time::get_current_walltime(startTime);

    for (int i = 0; i < num; ++i)
    {
      int value = 1;
      (void)map6.put(str_buf[i], value, false);
    }

    zorba::time::walltime stopTime;
    time::get_current_walltime(stopTime);

    double time = time::get_walltime_elapsed(startTime, stopTime);

    std::cout << "Load factor = " << load_factor << std::endl
              << "Num string insertions = " << num << std::endl
              << "hashmap entries = " << map6.size() << std::endl
              << "Time = " << time << std::endl;
  }
  else
  {
    std::cout << "Invalid test id" << std::endl;
    return 2;
  }

  delete [] int_buf;
  delete [] str_buf;

  return 0;
}

}
}
<commit_msg>s/zstring/std::string/<commit_after>#include "stdafx.h"

#include <cstdlib>
#include <stdio.h>
#include <iostream>

#include <zorba/util/time.h>

#include "zorbautils/hashmap.h"
#include "util/hashmap32.h"
#include "util/hashmap.h"
#include "util/unordered_map.h"

namespace zorba {

namespace UnitTests {

class IntCompFunc
{
public:
  static bool equal(uint64_t k1, uint64_t k2)
  {
    return k1 == k2;
  }

  static uint32_t hash(uint64_t key)
  {
#if 1
    return static_cast<uint32_t>(key);
#else
    char buf[9];
    buf[0] = (unsigned char)(key>>56);
    buf[1] = (unsigned char)(key>>48);
    buf[2] = (unsigned char)(key>>40);
    buf[3] = (unsigned char)(key>>32);
    buf[4] = (unsigned char)(key>>24);
    buf[5] = (unsigned char)(key>>16);
    buf[6] = (unsigned char)(key>>8 );
    buf[7] = (unsigned char)(key    );
    buf[8] = 0;
    return (uint32_t)hashfun::h64((void*)buf,8,FNV_64_INIT);
#endif
  }
};

class StrCompFunc
{
public:
  static bool equal(const std::string& k1, const std::string& k2)
  {
    return k1 == k2;
  }

  static uint32_t hash(const std::string& key)
  {
    return hashfun::h32(key.c_str(), FNV_32_INIT);
  }
};

int test_hashmaps(int argc, char* argv[])
{
  if (argc < 4)
    return 1;

  int test_id = atoi(argv[1]);

  double load_factor = atof(argv[2]);

  int num = atoi(argv[3]);

  uint64_t* int_buf = new uint64_t[num];

  for (int i = 0; i < num; ++i)
  {
    int_buf[i] = rand() % num;
  }

  std::string* str_buf = new std::string[num];

  for (int i = 0; i < num; ++i)
  {
    char tmp[20];
    sprintf(tmp, "%d", rand() % num);
    str_buf[i] = tmp;

    //std::cout << str_buf[i] << std::endl;
  }

  HashMap<uint64_t, int, IntCompFunc> map1(1024, false);
  HashMap<std::string, int, StrCompFunc> map2(1024, false);

  std::unordered_map<uint64_t, int> map3(1024);
  std::unordered_map<std::string, int> map4(1024);

  hash64map<int> map5(1024, load_factor);
  hashmap<std::string, int> map6(1024, load_factor);

  map1.set_load_factor(load_factor);
  map2.set_load_factor(load_factor);

  if (test_id == 1)
  {
    zorba::time::walltime startTime;
    zorba::time::get_current_walltime(startTime);

    for (int i = 0; i < num; ++i)
    {
      uint64_t key = int_buf[i];
      int value = 1;
      (void)map1.insert(key, value);
    }

    zorba::time::walltime stopTime;
    time::get_current_walltime(stopTime);

    double time = time::get_walltime_elapsed(startTime, stopTime);

    std::cout << "Load factor = " << load_factor << std::endl
              << "Num numeric insertions = " << num << std::endl
              << "HashMap entries = " << map1.size() << std::endl
              << "HashMap capacity = " << map1.capacity() << std::endl
              << "HashMap colisions = " << map1.collisions() << std::endl
              << "Time = " << time << std::endl;
  }
  else if (test_id == 2)
  {
    zorba::time::walltime startTime;
    zorba::time::get_current_walltime(startTime);

    for (int i = 0; i < num; ++i)
    {
      int value = 1;
      (void)map2.insert(str_buf[i], value);
    }

    zorba::time::walltime stopTime;
    time::get_current_walltime(stopTime);

    double time = time::get_walltime_elapsed(startTime, stopTime);

    std::cout << "Load factor = " << load_factor << std::endl
              << "Num string insertions = " << num << std::endl
              << "HashMap entries = " << map2.size() << std::endl
              << "HashMap buckets = " << map2.bucket_count() << std::endl
              << "HashMap capacity = " << map2.capacity() << std::endl
              << "Time = " << time << std::endl;
  }
  else if (test_id == 3)
  {
    zorba::time::walltime startTime;
    zorba::time::get_current_walltime(startTime);

    for (int i = 0; i < num; ++i)
    {
      uint64_t key = int_buf[i];
      int value = 1;
      (void)map3.insert(std::pair<uint64_t, int>(key, value));
    }

    zorba::time::walltime stopTime;
    time::get_current_walltime(stopTime);

    double time = time::get_walltime_elapsed(startTime, stopTime);

    std::cout << "Load factor = " << load_factor << std::endl
              << "Num numeric insertions = " << num << std::endl
              << "UnorderedMap entries = " << map3.size() << std::endl
              << "UnorderedMap buckets = " << map3.bucket_count() << std::endl
              << "Time = " << time << std::endl;
  }
  else if (test_id == 4)
  {
    zorba::time::walltime startTime;
    zorba::time::get_current_walltime(startTime);

    for (int i = 0; i < num; ++i)
    {
      int value = 1;
      (void)map4.insert(std::pair<std::string, int>(str_buf[i], value));
    }

    zorba::time::walltime stopTime;
    time::get_current_walltime(stopTime);

    double time = time::get_walltime_elapsed(startTime, stopTime);

    std::cout << "Load factor = " << load_factor << std::endl
              << "Num string insertions = " << num << std::endl
              << "UnorderedMap entries = " << map4.size() << std::endl
              << "UnorderedMap buckets = " << map4.bucket_count() << std::endl
              << "Time = " << time << std::endl;
  }
  else if (test_id == 5)
  {
    zorba::time::walltime startTime;
    zorba::time::get_current_walltime(startTime);

    for (int i = 0; i < num; ++i)
    {
      uint64_t key = int_buf[i];
      int value = 1;
      (void)map5.put_unsync(key, value);
    }

    zorba::time::walltime stopTime;
    time::get_current_walltime(stopTime);

    double time = time::get_walltime_elapsed(startTime, stopTime);

    std::cout << "Load factor = " << load_factor << std::endl
              << "Num numeric insertions = " << num << std::endl
              << "hashmap entries = " << map5.size() << std::endl
              << "Time = " << time << std::endl;
  }
  else if (test_id == 6)
  {
    zorba::time::walltime startTime;
    zorba::time::get_current_walltime(startTime);

    for (int i = 0; i < num; ++i)
    {
      int value = 1;
      (void)map6.put(str_buf[i], value, false);
    }

    zorba::time::walltime stopTime;
    time::get_current_walltime(stopTime);

    double time = time::get_walltime_elapsed(startTime, stopTime);

    std::cout << "Load factor = " << load_factor << std::endl
              << "Num string insertions = " << num << std::endl
              << "hashmap entries = " << map6.size() << std::endl
              << "Time = " << time << std::endl;
  }
  else
  {
    std::cout << "Invalid test id" << std::endl;
    return 2;
  }

  delete [] int_buf;
  delete [] str_buf;

  return 0;
}

}
}
<|endoftext|>


<commit_before>/* IBM_PROLOG_BEGIN_TAG                                                   */
/* This is an automatically generated prolog.                             */
/*                                                                        */
/* $Source: src/usr/mctp/runtime/mctprp_rt.C $                            */
/*                                                                        */
/* OpenPOWER HostBoot Project                                             */
/*                                                                        */
/* Contributors Listed Below - COPYRIGHT 2019,2021                        */
/* [+] International Business Machines Corp.                              */
/*                                                                        */
/*                                                                        */
/* 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.                         */
/*                                                                        */
/* IBM_PROLOG_END_TAG                                                     */

/**
 *  @file  mctprp_rt.C
 *  @brief Source code for hbrt's MCTP resource provider. Its primary job is to
 *         provide interaces for MCTP users to send messages across the bus and
 *         to attempt to look for new messages. The resource provider will initalize
 *         the mctp binding to the virtual bus provided by the hypervisor. Also it
 *         will provide an interface to the hbrtvirt mctp binding to route completed
 *         mctp messages to the appropriate message handler (see MctpRP::rx_message)
 */

// Headers from local directory
#include "mctprp_rt.H"
#include "../mctp_trace.H"
#include "libmctp-hbrtvirt.h"
// System Headers
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
// Userspace Headers
#include <trace/interface.H>
#include <runtime/interface.h>
#include <errl/errlmanager.H>
#include <mctp/mctp_errl.H>
#include <mctp/mctp_reasoncodes.H>
#include <pldm/pldmif.H>

using namespace ERRORLOG;
using namespace MCTP;

trace_desc_t* g_trac_mctp = nullptr;
TRAC_INIT(&g_trac_mctp, MCTP_COMP_NAME, 4*KILOBYTE, TRACE::BUFFER_SLOW);

namespace MCTP
{
    int get_next_packet(void)
    {
        return Singleton<MctpRP>::instance().get_next_packet();
    }

    int send_message(byte_vector_t& i_mctp_payload)
    {
        return Singleton<MctpRP>::instance().send_message(i_mctp_payload);
    }
}

int MctpRP::get_next_packet(void)
{
    TRACDCOMP(g_trac_mctp,
             "requesting next mctp packet");
    return mctp_hbrtvirt_rx_start(iv_hbrtvirt);
}

int MctpRP::send_message(byte_vector_t& i_mctp_payload)
{
    TRACDBIN(g_trac_mctp,
             "sending mctp payload: ",
             i_mctp_payload.data(),
             i_mctp_payload.size());

    int rc = mctp_message_tx(iv_mctp,
                             BMC_EID,
                             i_mctp_payload.data(),
                             i_mctp_payload.size());

    return rc;
}

static void rx_message(uint8_t i_eid, void * i_data,
                       void * i_msg, size_t i_len)
{
   TRACDBIN(g_trac_mctp, "mctp rx_message:", i_msg, i_len);

   auto msg_bytes = static_cast<const uint8_t *const>(i_msg);

   // First byte of the msg should be the MCTP payload type.
   // For now we only support PLDM over MCTP
   switch(*msg_bytes)
   {
      case MCTP_MSG_TYPE_PLDM :
      {
          // Skip first byte of the MCTP payload which has the payload type.
          // The PLDM layer is unaware of this byte.
          PLDM::pldmrp_rt_rc rc =
              PLDM::cache_next_pldm_msg(msg_bytes + sizeof(MCTP::message_type),
                                        i_len - sizeof(MCTP::message_type));
          if(rc)
          {
              uint64_t request_hdr_data = 0;
              if(rc != PLDM::RC_INVALID_MESSAGE_LEN)
              {
                   request_hdr_data =
                      PLDM::pldmHdrToUint64(
                        *reinterpret_cast<const pldm_msg*>(msg_bytes + sizeof(MCTP::message_type)));
              }
              TRACFBIN(g_trac_mctp, "Failed to cache mctp payload:", i_msg, i_len);
              /*@
              * @errortype  ERRL_SEV_PREDICTIVE
              * @moduleid   MOD_RX_CALLBACK
              * @reasoncode RC_ERROR_CACHING_MSG
              * @userdata1[0:31]  HBRT PLDM RP Return Code
              * @userdata1[32:63] Source MCTP Endpoint ID
              * @userdata2        Header of PLDM message
              * @devdesc    Unable to process PLDM message from the BMC
              * @custdesc   A software error occurred during host/bmc
              *             communication
              */
              errlHndl_t errl = new ErrlEntry(ERRL_SEV_PREDICTIVE,
                                              MOD_RX_CALLBACK,
                                              RC_ERROR_CACHING_MSG,
                                              TWO_UINT32_TO_UINT64(
                                                TO_UINT32(rc),
                                                TO_UINT32(i_eid)),
                                              request_hdr_data,
                                              ErrlEntry::NO_SW_CALLOUT);
              addBmcAndHypErrorCallouts(errl);
              errlCommit(errl, MCTP_COMP_ID);
          }
          break;
      }
      default :
      {
          TRACFCOMP(g_trac_mctp,
                    "Warning! Received a MCTP message with a payload type %u from eid %u we do not know how to handle, it will be ignored",
                    *msg_bytes,
                    i_eid);
          TRACFBIN(g_trac_mctp, "failing mctp rx_message:", i_msg, i_len);
         /*@
          * @errortype  ERRL_SEV_PREDICTIVE
          * @moduleid   MOD_RX_CALLBACK
          * @reasoncode RC_INVALID_MSG_TYPE
          * @userdata1  MCTP Message Type (first byte of MCTP payload)
          * @userdata2  Source MCTP Endpoint ID
          * @devdesc    MCTP message of unknown type received
          * @custdesc   A software error occurred during host/bmc
          *             communication
          */
          errlHndl_t errl = new ErrlEntry(ERRL_SEV_PREDICTIVE,
                                          MOD_RX_CALLBACK,
                                          RC_INVALID_MSG_TYPE,
                                          *msg_bytes,
                                          i_eid,
                                          ErrlEntry::NO_SW_CALLOUT);
          addBmcAndHypErrorCallouts(errl);
          errlCommit(errl, MCTP_COMP_ID);
          break;
      }
   }
}

/**
* @brief Initialize mctp binding for hbrt and provide clear
*        interfaces for sending and receiving mctp messages.
*
* @return void
*/
void init_mctp_resource_provider(void)
{
    Singleton<MctpRP>::instance();
}

MctpRP::MctpRP(void):
    iv_mctp(mctp_init()),
    iv_hbrtvirt(mctp_hbrtvirt_init_hbrt())
{
    TRACFCOMP(g_trac_mctp, "MctpRP constructor entry");
    // Set the receive function to be rx_message which
    // will handle the message in the RX space accordingly
    mctp_set_rx_all(iv_mctp, rx_message, NULL);

    mctp_register_bus(iv_mctp, &iv_hbrtvirt->binding, HBRT_EID);

    mctp_binding_set_tx_enabled(&iv_hbrtvirt->binding, true);

    mctp_set_log_custom(mctp_log_fn);

    TRACFCOMP(g_trac_mctp, "MctpRP constructor exit");
}

MctpRP::~MctpRP(void)
{
    TRACFCOMP(g_trac_mctp, "MctpRP destructor entry");
    mctp_hbrtvirt_destroy(iv_hbrtvirt);
    mctp_destroy(iv_mctp);
    TRACFCOMP(g_trac_mctp, "MctpRP destructor exit");

}

struct registerinitMctp
{
    registerinitMctp()
    {
        // Register interface for Host to call
        postInitCalls_t * rt_post = getPostInitCalls();
        rt_post->callInitMctp = &init_mctp_resource_provider;
    }
};

registerinitMctp g_registerinitMctp;
<commit_msg>Setup memory allocation function for mctprp_rt<commit_after>/* IBM_PROLOG_BEGIN_TAG                                                   */
/* This is an automatically generated prolog.                             */
/*                                                                        */
/* $Source: src/usr/mctp/runtime/mctprp_rt.C $                            */
/*                                                                        */
/* OpenPOWER HostBoot Project                                             */
/*                                                                        */
/* Contributors Listed Below - COPYRIGHT 2019,2021                        */
/* [+] International Business Machines Corp.                              */
/*                                                                        */
/*                                                                        */
/* 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.                         */
/*                                                                        */
/* IBM_PROLOG_END_TAG                                                     */

/**
 *  @file  mctprp_rt.C
 *  @brief Source code for hbrt's MCTP resource provider. Its primary job is to
 *         provide interaces for MCTP users to send messages across the bus and
 *         to attempt to look for new messages. The resource provider will initalize
 *         the mctp binding to the virtual bus provided by the hypervisor. Also it
 *         will provide an interface to the hbrtvirt mctp binding to route completed
 *         mctp messages to the appropriate message handler (see MctpRP::rx_message)
 */

// Headers from local directory
#include "mctprp_rt.H"
#include "../mctp_trace.H"
#include "libmctp-hbrtvirt.h"
// System Headers
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
// Userspace Headers
#include <trace/interface.H>
#include <runtime/interface.h>
#include <errl/errlmanager.H>
#include <mctp/mctp_errl.H>
#include <mctp/mctp_reasoncodes.H>
#include <pldm/pldmif.H>

using namespace ERRORLOG;
using namespace MCTP;

trace_desc_t* g_trac_mctp = nullptr;
TRAC_INIT(&g_trac_mctp, MCTP_COMP_NAME, 4*KILOBYTE, TRACE::BUFFER_SLOW);

namespace MCTP
{
    int get_next_packet(void)
    {
        return Singleton<MctpRP>::instance().get_next_packet();
    }

    int send_message(byte_vector_t& i_mctp_payload)
    {
        return Singleton<MctpRP>::instance().send_message(i_mctp_payload);
    }
}

int MctpRP::get_next_packet(void)
{
    TRACDCOMP(g_trac_mctp,
             "requesting next mctp packet");
    return mctp_hbrtvirt_rx_start(iv_hbrtvirt);
}

int MctpRP::send_message(byte_vector_t& i_mctp_payload)
{
    TRACDBIN(g_trac_mctp,
             "sending mctp payload: ",
             i_mctp_payload.data(),
             i_mctp_payload.size());

    int rc = mctp_message_tx(iv_mctp,
                             BMC_EID,
                             i_mctp_payload.data(),
                             i_mctp_payload.size());

    return rc;
}

static void rx_message(uint8_t i_eid, void * i_data,
                       void * i_msg, size_t i_len)
{
   TRACDBIN(g_trac_mctp, "mctp rx_message:", i_msg, i_len);

   auto msg_bytes = static_cast<const uint8_t *const>(i_msg);

   // First byte of the msg should be the MCTP payload type.
   // For now we only support PLDM over MCTP
   switch(*msg_bytes)
   {
      case MCTP_MSG_TYPE_PLDM :
      {
          // Skip first byte of the MCTP payload which has the payload type.
          // The PLDM layer is unaware of this byte.
          PLDM::pldmrp_rt_rc rc =
              PLDM::cache_next_pldm_msg(msg_bytes + sizeof(MCTP::message_type),
                                        i_len - sizeof(MCTP::message_type));
          if(rc)
          {
              uint64_t request_hdr_data = 0;
              if(rc != PLDM::RC_INVALID_MESSAGE_LEN)
              {
                   request_hdr_data =
                      PLDM::pldmHdrToUint64(
                        *reinterpret_cast<const pldm_msg*>(msg_bytes + sizeof(MCTP::message_type)));
              }
              TRACFBIN(g_trac_mctp, "Failed to cache mctp payload:", i_msg, i_len);
              /*@
              * @errortype  ERRL_SEV_PREDICTIVE
              * @moduleid   MOD_RX_CALLBACK
              * @reasoncode RC_ERROR_CACHING_MSG
              * @userdata1[0:31]  HBRT PLDM RP Return Code
              * @userdata1[32:63] Source MCTP Endpoint ID
              * @userdata2        Header of PLDM message
              * @devdesc    Unable to process PLDM message from the BMC
              * @custdesc   A software error occurred during host/bmc
              *             communication
              */
              errlHndl_t errl = new ErrlEntry(ERRL_SEV_PREDICTIVE,
                                              MOD_RX_CALLBACK,
                                              RC_ERROR_CACHING_MSG,
                                              TWO_UINT32_TO_UINT64(
                                                TO_UINT32(rc),
                                                TO_UINT32(i_eid)),
                                              request_hdr_data,
                                              ErrlEntry::NO_SW_CALLOUT);
              addBmcAndHypErrorCallouts(errl);
              errlCommit(errl, MCTP_COMP_ID);
          }
          break;
      }
      default :
      {
          TRACFCOMP(g_trac_mctp,
                    "Warning! Received a MCTP message with a payload type %u from eid %u we do not know how to handle, it will be ignored",
                    *msg_bytes,
                    i_eid);
          TRACFBIN(g_trac_mctp, "failing mctp rx_message:", i_msg, i_len);
         /*@
          * @errortype  ERRL_SEV_PREDICTIVE
          * @moduleid   MOD_RX_CALLBACK
          * @reasoncode RC_INVALID_MSG_TYPE
          * @userdata1  MCTP Message Type (first byte of MCTP payload)
          * @userdata2  Source MCTP Endpoint ID
          * @devdesc    MCTP message of unknown type received
          * @custdesc   A software error occurred during host/bmc
          *             communication
          */
          errlHndl_t errl = new ErrlEntry(ERRL_SEV_PREDICTIVE,
                                          MOD_RX_CALLBACK,
                                          RC_INVALID_MSG_TYPE,
                                          *msg_bytes,
                                          i_eid,
                                          ErrlEntry::NO_SW_CALLOUT);
          addBmcAndHypErrorCallouts(errl);
          errlCommit(errl, MCTP_COMP_ID);
          break;
      }
   }
}

/**
* @brief Initialize mctp binding for hbrt and provide clear
*        interfaces for sending and receiving mctp messages.
*
* @return void
*/
void init_mctp_resource_provider(void)
{
    mctp_set_alloc_ops(malloc, free, realloc);
    Singleton<MctpRP>::instance();
}

MctpRP::MctpRP(void):
    iv_mctp(mctp_init()),
    iv_hbrtvirt(mctp_hbrtvirt_init_hbrt())
{
    TRACFCOMP(g_trac_mctp, "MctpRP constructor entry");
    // Set the receive function to be rx_message which
    // will handle the message in the RX space accordingly
    mctp_set_rx_all(iv_mctp, rx_message, NULL);

    mctp_register_bus(iv_mctp, &iv_hbrtvirt->binding, HBRT_EID);

    mctp_binding_set_tx_enabled(&iv_hbrtvirt->binding, true);

    mctp_set_log_custom(mctp_log_fn);

    TRACFCOMP(g_trac_mctp, "MctpRP constructor exit");
}

MctpRP::~MctpRP(void)
{
    TRACFCOMP(g_trac_mctp, "MctpRP destructor entry");
    mctp_hbrtvirt_destroy(iv_hbrtvirt);
    mctp_destroy(iv_mctp);
    TRACFCOMP(g_trac_mctp, "MctpRP destructor exit");

}

struct registerinitMctp
{
    registerinitMctp()
    {
        // Register interface for Host to call
        postInitCalls_t * rt_post = getPostInitCalls();
        rt_post->callInitMctp = &init_mctp_resource_provider;
    }
};

registerinitMctp g_registerinitMctp;
<|endoftext|>


<commit_before>/*
    Copyright (C) 2005 by Andrew Mann

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library General Public
    License as published by the Free Software Foundation; either
    version 2 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Library General Public License for more details.

    You should have received a copy of the GNU Library General Public
    License along with this library; if not, write to the Free
    Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#include "cssysdef.h"

#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <sys/soundcard.h>

#include "csutil/sysfunc.h"
#include "csutil/event.h"

#include "iutil/plugin.h"
#include "iutil/cfgfile.h"
#include "iutil/event.h"
#include "iutil/eventh.h"
#include "iutil/eventq.h"
#include "iutil/objreg.h"
#include "iutil/eventh.h"
#include "iutil/comp.h"
#include "iutil/virtclk.h"
#include "iutil/cmdline.h"
#include "ivaria/reporter.h"

#include "../../renderer.h"
#include "isndsys/ss_driver.h"
#include "isndsys/ss_renderer.h"
#include "driver_oss.h"

CS_IMPLEMENT_PLUGIN

SCF_IMPLEMENT_FACTORY (SndSysDriverOSS)

SCF_IMPLEMENT_IBASE(SndSysDriverOSS)
SCF_IMPLEMENTS_INTERFACE(iSndSysSoftwareDriver)
SCF_IMPLEMENTS_EMBEDDED_INTERFACE(iComponent)
SCF_IMPLEMENT_IBASE_END;

SCF_IMPLEMENT_EMBEDDED_IBASE (SndSysDriverOSS::eiComponent)
SCF_IMPLEMENTS_INTERFACE (iComponent)
SCF_IMPLEMENT_EMBEDDED_IBASE_END

// The system driver.
iObjectRegistry *SndSysDriverOSS::object_reg=0;

SndSysDriverOSS::SndSysDriverOSS(iBase* piBase) :
 oss_buffer(0), output_fd(-1), running(false)
{
  SCF_CONSTRUCT_IBASE(piBase);
  SCF_CONSTRUCT_EMBEDDED_IBASE(scfiComponent);

//  scfiEventHandler = 0;
  object_reg = 0;
}

SndSysDriverOSS::~SndSysDriverOSS()
{
  if (output_fd>=0)
    close(output_fd);
  output_fd=-1;

  delete[] oss_buffer;

  SCF_DESTRUCT_EMBEDDED_IBASE(scfiComponent);
  SCF_DESTRUCT_IBASE();
}

void SndSysDriverOSS::Report(int severity, const char* msg, ...)
{
  va_list arg;
  va_start (arg, msg);

  csRef<iReporter> reporter = CS_QUERY_REGISTRY(object_reg, iReporter);

  if (reporter)
    reporter->ReportV (severity, "crystalspace.SndSys.driver.software.oss", msg, arg);
  else
  {
    csPrintfV (msg, arg);
    csPrintf ("\n");
  }
  va_end (arg);
}

bool SndSysDriverOSS::Initialize (iObjectRegistry *obj_reg)
{
  // copy the system pointer
  object_reg=obj_reg;

  Report (CS_REPORTER_SEVERITY_DEBUG, "Sound System: OSS driver for software sound renderer initialized.");

  // read the config file
//  Config.AddConfig(object_reg, "/config/sound.cfg");

  strcpy(output_device, "/dev/dsp");

  return true;
}

//////////////////////////////////////////////////////////////////////////
//
//
//
//
//
//
//////////////////////////////////////////////////////////////////////////
bool SndSysDriverOSS::Open (csSndSysRendererSoftware*renderer,
			     csSndSysSoundFormat *requested_format)
{
  int result, param;

  Report (CS_REPORTER_SEVERITY_DEBUG, "Sound System: OSS Driver: Open()");
//  CS_ASSERT (Config != 0);

  attached_renderer=renderer;

  output_fd=open(output_device, O_WRONLY, 0);
  if (output_fd==-1)
  {
    Report (CS_REPORTER_SEVERITY_ERROR,
      "Sound System: OSS Driver: Failed to open output device [%s].",
      output_device);
    return false;
  }

  // Set bits per sample and sample format
  switch (requested_format->Bits)
  {
    case 8:
      param=AFMT_U8;
    break;
    case 16:
      param=AFMT_S16_LE;
    break;
    default:
      Report (CS_REPORTER_SEVERITY_ERROR, "Sound System: OSS Driver: Unhandled output bits %d. Forcing to 16 bit.", requested_format->Bits);
      requested_format->Bits=16;
      param=AFMT_S16_LE;
    break;
  }
  result=ioctl(output_fd, SNDCTL_DSP_SETFMT, &param);
  if (result==-1)
  {
    Report (CS_REPORTER_SEVERITY_ERROR, "Sound System: OSS Driver: Failed to set output format to %d bit.", requested_format->Bits);
    close(output_fd);
    output_fd=-1;
    return false;
  }

  // Set channels
  param=requested_format->Channels;
  result=ioctl(output_fd, SNDCTL_DSP_CHANNELS, &param);
  if (result==-1)
  {
    Report (CS_REPORTER_SEVERITY_ERROR, "Sound System: OSS Driver: Failed to set output format to %d channels.", requested_format->Channels);
    close(output_fd);
    output_fd=-1;
    return false;
  }

  // Set sample frequency
  param=requested_format->Freq;
  result=ioctl(output_fd, SNDCTL_DSP_SPEED, &param);
  if (result==-1)
  {
    Report (CS_REPORTER_SEVERITY_ERROR, "Sound System: OSS Driver: Failed to set output bitrate to %d bits per channel per second.", requested_format->Freq);
    close(output_fd);
    output_fd=-1;
    return false;
  }

  // Copy the final format into local storage
  memcpy(&playback_format, requested_format, sizeof(csSndSysSoundFormat));

  // Setup a playback buffer

  // 1/10th of a second
  oss_buffer_bytes=playback_format.Channels * (playback_format.Bits/8) * playback_format.Freq / 10;
  oss_buffer=new uint8[oss_buffer_bytes];

  return true;
}

void SndSysDriverOSS::Close ()
{
  if (output_fd)
  {
    close(output_fd);
    output_fd=-1;
  }
}

bool SndSysDriverOSS::StartThread()
{
  if (running) return false;

  running=true;
  SndSysDriverRunnable* runnable = new SndSysDriverRunnable (this);
  bgthread = csThread::Create(runnable);
  runnable->DecRef ();

  bgthread->Start();

  return true;
}

void SndSysDriverOSS::StopThread()
{
  running=false;
  csSleep(100);
}

void SndSysDriverRunnable::Run ()
{
  parent->Run ();
}

void SndSysDriverOSS::Run()
{
  csTicks last_write, current_ticks, tick_difference;

  // Write at about 20 times per second
  tick_difference=1000/20;

  last_write=csGetTicks();
  // Clear the buffer and write one section ahead
  ClearBuffer();
  WriteBuffer(oss_buffer_bytes);

  while (running)
  {
    current_ticks=csGetTicks();
    if (last_write + tick_difference <= current_ticks)
    {
      uint32 bytes_used=attached_renderer->FillDriverBuffer(oss_buffer, oss_buffer_bytes, 0, 0);
      if (bytes_used > 0)
      {
        WriteBuffer(bytes_used);
      }
      last_write=current_ticks;
    }
    csSleep(0);
  }
}

void SndSysDriverOSS::ClearBuffer()
{
  if (oss_buffer)
    memset(oss_buffer, 0, sizeof(uint8) * oss_buffer_bytes);
}

void SndSysDriverOSS::WriteBuffer(size_t bytes)
{
  write(output_fd, oss_buffer, bytes);
}

<commit_msg>UNTESTED: Changes to the mac oss software sound driver to hopefully get things working. The oss driver will now honor the byte order indicator in the sound format structure provided by the renderer (though it does not attempt negotiation)<commit_after>/*
    Copyright (C) 2005 by Andrew Mann

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library General Public
    License as published by the Free Software Foundation; either
    version 2 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Library General Public License for more details.

    You should have received a copy of the GNU Library General Public
    License along with this library; if not, write to the Free
    Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#include "cssysdef.h"

#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <sys/soundcard.h>

#include "csutil/sysfunc.h"
#include "csutil/event.h"

#include "iutil/plugin.h"
#include "iutil/cfgfile.h"
#include "iutil/event.h"
#include "iutil/eventh.h"
#include "iutil/eventq.h"
#include "iutil/objreg.h"
#include "iutil/eventh.h"
#include "iutil/comp.h"
#include "iutil/virtclk.h"
#include "iutil/cmdline.h"
#include "ivaria/reporter.h"

#include "../../renderer.h"
#include "isndsys/ss_driver.h"
#include "isndsys/ss_renderer.h"
#include "driver_oss.h"

CS_IMPLEMENT_PLUGIN

SCF_IMPLEMENT_FACTORY (SndSysDriverOSS)

SCF_IMPLEMENT_IBASE(SndSysDriverOSS)
SCF_IMPLEMENTS_INTERFACE(iSndSysSoftwareDriver)
SCF_IMPLEMENTS_EMBEDDED_INTERFACE(iComponent)
SCF_IMPLEMENT_IBASE_END;

SCF_IMPLEMENT_EMBEDDED_IBASE (SndSysDriverOSS::eiComponent)
SCF_IMPLEMENTS_INTERFACE (iComponent)
SCF_IMPLEMENT_EMBEDDED_IBASE_END

// The system driver.
iObjectRegistry *SndSysDriverOSS::object_reg=0;

SndSysDriverOSS::SndSysDriverOSS(iBase* piBase) :
 oss_buffer(0), output_fd(-1), running(false)
{
  SCF_CONSTRUCT_IBASE(piBase);
  SCF_CONSTRUCT_EMBEDDED_IBASE(scfiComponent);

//  scfiEventHandler = 0;
  object_reg = 0;
}

SndSysDriverOSS::~SndSysDriverOSS()
{
  if (output_fd>=0)
    close(output_fd);
  output_fd=-1;

  delete[] oss_buffer;

  SCF_DESTRUCT_EMBEDDED_IBASE(scfiComponent);
  SCF_DESTRUCT_IBASE();
}

void SndSysDriverOSS::Report(int severity, const char* msg, ...)
{
  va_list arg;
  va_start (arg, msg);

  csRef<iReporter> reporter = CS_QUERY_REGISTRY(object_reg, iReporter);

  if (reporter)
    reporter->ReportV (severity, "crystalspace.SndSys.driver.software.oss", msg, arg);
  else
  {
    csPrintfV (msg, arg);
    csPrintf ("\n");
  }
  va_end (arg);
}

bool SndSysDriverOSS::Initialize (iObjectRegistry *obj_reg)
{
  // copy the system pointer
  object_reg=obj_reg;

  Report (CS_REPORTER_SEVERITY_DEBUG, "Sound System: OSS driver for software sound renderer initialized.");

  // read the config file
//  Config.AddConfig(object_reg, "/config/sound.cfg");

  strcpy(output_device, "/dev/dsp");

  return true;
}

//////////////////////////////////////////////////////////////////////////
//
//
//
//
//
//
//////////////////////////////////////////////////////////////////////////
bool SndSysDriverOSS::Open (csSndSysRendererSoftware*renderer,
			     csSndSysSoundFormat *requested_format)
{
  int result, param;

  Report (CS_REPORTER_SEVERITY_DEBUG, "Sound System: OSS Driver: Open()");
//  CS_ASSERT (Config != 0);

  attached_renderer=renderer;

  output_fd=open(output_device, O_WRONLY, 0);
  if (output_fd==-1)
  {
    Report (CS_REPORTER_SEVERITY_ERROR,
      "Sound System: OSS Driver: Failed to open output device [%s].",
      output_device);
    return false;
  }

  // Set bits per sample and sample format
  switch (requested_format->Bits)
  {
    case 8:
      param=AFMT_U8;
    break;
    case 16:
      if ((requested_format->Flags & CSSNDSYS_SAMPLE_ENDIAN_MASK) == CSSNDSYS_SAMPLE_BIG_ENDIAN)
        param=AFMT_S16_BE;
      else
        param=AFMT_S16_LE;
    break;
    default:
      Report (CS_REPORTER_SEVERITY_ERROR, "Sound System: OSS Driver: Unhandled output bits %d. Forcing to 16 bit.", requested_format->Bits);
      requested_format->Bits=16;
      if ((requested_format->Flags & CSSNDSYS_SAMPLE_ENDIAN_MASK) == CSSNDSYS_SAMPLE_BIG_ENDIAN)
        param=AFMT_S16_BE;
      else
        param=AFMT_S16_LE;
    break;
  }
  result=ioctl(output_fd, SNDCTL_DSP_SETFMT, &param);
  if (result==-1)
  {
    const char *endian_string;

    if ((requested_format->Flags & CSSNDSYS_SAMPLE_ENDIAN_MASK) == CSSNDSYS_SAMPLE_BIG_ENDIAN)
      endian_string="Big Endian";
    else
      endian_string="Little Endian";

    Report (CS_REPORTER_SEVERITY_ERROR, "Sound System: OSS Driver: Failed to set output format to %d bit (%s).", requested_format->Bits, endian_string);
    close(output_fd);
    output_fd=-1;
    return false;
  }

  // Set channels
  param=requested_format->Channels;
  result=ioctl(output_fd, SNDCTL_DSP_CHANNELS, &param);
  if (result==-1)
  {
    Report (CS_REPORTER_SEVERITY_ERROR, "Sound System: OSS Driver: Failed to set output format to %d channels.", requested_format->Channels);
    close(output_fd);
    output_fd=-1;
    return false;
  }

  // Set sample frequency
  param=requested_format->Freq;
  result=ioctl(output_fd, SNDCTL_DSP_SPEED, &param);
  if (result==-1)
  {
    Report (CS_REPORTER_SEVERITY_ERROR, "Sound System: OSS Driver: Failed to set output bitrate to %d bits per channel per second.", requested_format->Freq);
    close(output_fd);
    output_fd=-1;
    return false;
  }

  // Copy the final format into local storage
  memcpy(&playback_format, requested_format, sizeof(csSndSysSoundFormat));

  // Setup a playback buffer

  // 1/10th of a second
  oss_buffer_bytes=playback_format.Channels * (playback_format.Bits/8) * playback_format.Freq / 10;
  oss_buffer=new uint8[oss_buffer_bytes];

  return true;
}

void SndSysDriverOSS::Close ()
{
  if (output_fd)
  {
    close(output_fd);
    output_fd=-1;
  }
}

bool SndSysDriverOSS::StartThread()
{
  if (running) return false;

  running=true;
  SndSysDriverRunnable* runnable = new SndSysDriverRunnable (this);
  bgthread = csThread::Create(runnable);
  runnable->DecRef ();

  bgthread->Start();

  return true;
}

void SndSysDriverOSS::StopThread()
{
  running=false;
  csSleep(100);
}

void SndSysDriverRunnable::Run ()
{
  parent->Run ();
}

void SndSysDriverOSS::Run()
{
  csTicks last_write, current_ticks, tick_difference;

  // Write at about 20 times per second
  tick_difference=1000/20;

  last_write=csGetTicks();
  // Clear the buffer and write one section ahead
  ClearBuffer();
  WriteBuffer(oss_buffer_bytes);

  while (running)
  {
    current_ticks=csGetTicks();
    if (last_write + tick_difference <= current_ticks)
    {
      uint32 bytes_used=attached_renderer->FillDriverBuffer(oss_buffer, oss_buffer_bytes, 0, 0);
      if (bytes_used > 0)
      {
        WriteBuffer(bytes_used);
      }
      last_write=current_ticks;
    }
    csSleep(0);
  }
}

void SndSysDriverOSS::ClearBuffer()
{
  if (oss_buffer)
    memset(oss_buffer, 0, sizeof(uint8) * oss_buffer_bytes);
}

void SndSysDriverOSS::WriteBuffer(size_t bytes)
{
  write(output_fd, oss_buffer, bytes);
}

<|endoftext|>


<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 "chrome/browser/ui/webui/theme_source.h"

#include "base/message_loop.h"
#include "base/ref_counted_memory.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/resources_util.h"
#include "chrome/browser/themes/browser_theme_provider.h"
#include "chrome/browser/ui/webui/ntp_resource_cache.h"
#include "chrome/common/url_constants.h"
#include "content/browser/browser_thread.h"
#include "googleurl/src/gurl.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/base/theme_provider.h"

// use a resource map rather than hard-coded strings.
static const char* kNewTabCSSPath = "css/newtab.css";
static const char* kNewIncognitoTabCSSPath = "css/newincognitotab.css";

static std::string StripQueryParams(const std::string& path) {
  GURL path_url = GURL(std::string(chrome::kChromeUIScheme) + "://" +
                       std::string(chrome::kChromeUIThemePath) + "/" + path);
  return path_url.path().substr(1);  // path() always includes a leading '/'.
}

////////////////////////////////////////////////////////////////////////////////
// ThemeSource, public:

ThemeSource::ThemeSource(Profile* profile)
    : DataSource(chrome::kChromeUIThemePath, MessageLoop::current()),
      profile_(profile->GetOriginalProfile()) {
  css_bytes_ = profile_->GetNTPResourceCache()->GetNewTabCSS(
      profile->IsOffTheRecord());
}

ThemeSource::~ThemeSource() {
}

void ThemeSource::StartDataRequest(const std::string& path,
                                   bool is_off_the_record,
                                   int request_id) {
  // Our path may include cachebuster arguments, so trim them off.
  std::string uncached_path = StripQueryParams(path);

  if (uncached_path == kNewTabCSSPath ||
      uncached_path == kNewIncognitoTabCSSPath) {
    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    DCHECK((uncached_path == kNewTabCSSPath && !is_off_the_record) ||
           (uncached_path == kNewIncognitoTabCSSPath && is_off_the_record));

    SendResponse(request_id, css_bytes_);
    return;
  } else {
    int resource_id = ResourcesUtil::GetThemeResourceId(uncached_path);
    if (resource_id != -1) {
      SendThemeBitmap(request_id, resource_id);
      return;
    }
  }
  // We don't have any data to send back.
  SendResponse(request_id, NULL);
}

std::string ThemeSource::GetMimeType(const std::string& path) const {
  std::string uncached_path = StripQueryParams(path);

  if (uncached_path == kNewTabCSSPath ||
      uncached_path == kNewIncognitoTabCSSPath) {
    return "text/css";
  }

  return "image/png";
}

MessageLoop* ThemeSource::MessageLoopForRequestPath(
    const std::string& path) const {
  std::string uncached_path = StripQueryParams(path);

  if (uncached_path == kNewTabCSSPath ||
      uncached_path == kNewIncognitoTabCSSPath) {
    // We generated and cached this when we initialized the object.  We don't
    // have to go back to the UI thread to send the data.
    return NULL;
  }

  // If it's not a themeable image, we don't need to go to the UI thread.
  int resource_id = ResourcesUtil::GetThemeResourceId(uncached_path);
  if (!BrowserThemeProvider::IsThemeableImage(resource_id))
    return NULL;

  return DataSource::MessageLoopForRequestPath(path);
}

bool ThemeSource::ShouldReplaceExistingSource() const {
  // We currently get the css_bytes_ in the ThemeSource constructor, so we need
  // to recreate the source itself when a theme changes.
  return true;
}

////////////////////////////////////////////////////////////////////////////////
// ThemeSource, private:

void ThemeSource::SendThemeBitmap(int request_id, int resource_id) {
  if (BrowserThemeProvider::IsThemeableImage(resource_id)) {
    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    ui::ThemeProvider* tp = profile_->GetThemeProvider();
    DCHECK(tp);

    scoped_refptr<RefCountedMemory> image_data(tp->GetRawData(resource_id));
    SendResponse(request_id, image_data);
  } else {
    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    const ResourceBundle& rb = ResourceBundle::GetSharedInstance();
    SendResponse(request_id, rb.LoadDataResourceBytes(resource_id));
  }
}
<commit_msg>Revert 77517 - Force NTP reload when a new theme is installed.<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 "chrome/browser/ui/webui/theme_source.h"

#include "base/message_loop.h"
#include "base/ref_counted_memory.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/resources_util.h"
#include "chrome/browser/themes/browser_theme_provider.h"
#include "chrome/browser/ui/webui/ntp_resource_cache.h"
#include "chrome/common/url_constants.h"
#include "content/browser/browser_thread.h"
#include "googleurl/src/gurl.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/base/theme_provider.h"

// use a resource map rather than hard-coded strings.
static const char* kNewTabCSSPath = "css/newtab.css";
static const char* kNewIncognitoTabCSSPath = "css/newincognitotab.css";

static std::string StripQueryParams(const std::string& path) {
  GURL path_url = GURL(std::string(chrome::kChromeUIScheme) + "://" +
                       std::string(chrome::kChromeUIThemePath) + "/" + path);
  return path_url.path().substr(1);  // path() always includes a leading '/'.
}

////////////////////////////////////////////////////////////////////////////////
// ThemeSource, public:

ThemeSource::ThemeSource(Profile* profile)
    : DataSource(chrome::kChromeUIThemePath, MessageLoop::current()),
      profile_(profile->GetOriginalProfile()) {
  css_bytes_ = profile_->GetNTPResourceCache()->GetNewTabCSS(
      profile->IsOffTheRecord());
}

ThemeSource::~ThemeSource() {
}

void ThemeSource::StartDataRequest(const std::string& path,
                                   bool is_off_the_record,
                                   int request_id) {
  // Our path may include cachebuster arguments, so trim them off.
  std::string uncached_path = StripQueryParams(path);

  if (uncached_path == kNewTabCSSPath ||
      uncached_path == kNewIncognitoTabCSSPath) {
    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    DCHECK((uncached_path == kNewTabCSSPath && !is_off_the_record) ||
           (uncached_path == kNewIncognitoTabCSSPath && is_off_the_record));

    SendResponse(request_id, css_bytes_);
    return;
  } else {
    int resource_id = ResourcesUtil::GetThemeResourceId(uncached_path);
    if (resource_id != -1) {
      SendThemeBitmap(request_id, resource_id);
      return;
    }
  }
  // We don't have any data to send back.
  SendResponse(request_id, NULL);
}

std::string ThemeSource::GetMimeType(const std::string& path) const {
  std::string uncached_path = StripQueryParams(path);

  if (uncached_path == kNewTabCSSPath ||
      uncached_path == kNewIncognitoTabCSSPath) {
    return "text/css";
  }

  return "image/png";
}

MessageLoop* ThemeSource::MessageLoopForRequestPath(
    const std::string& path) const {
  std::string uncached_path = StripQueryParams(path);

  if (uncached_path == kNewTabCSSPath ||
      uncached_path == kNewIncognitoTabCSSPath) {
    // We generated and cached this when we initialized the object.  We don't
    // have to go back to the UI thread to send the data.
    return NULL;
  }

  // If it's not a themeable image, we don't need to go to the UI thread.
  int resource_id = ResourcesUtil::GetThemeResourceId(uncached_path);
  if (!BrowserThemeProvider::IsThemeableImage(resource_id))
    return NULL;

  return DataSource::MessageLoopForRequestPath(path);
}

bool ThemeSource::ShouldReplaceExistingSource() const {
  return false;
}

////////////////////////////////////////////////////////////////////////////////
// ThemeSource, private:

void ThemeSource::SendThemeBitmap(int request_id, int resource_id) {
  if (BrowserThemeProvider::IsThemeableImage(resource_id)) {
    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    ui::ThemeProvider* tp = profile_->GetThemeProvider();
    DCHECK(tp);

    scoped_refptr<RefCountedMemory> image_data(tp->GetRawData(resource_id));
    SendResponse(request_id, image_data);
  } else {
    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    const ResourceBundle& rb = ResourceBundle::GetSharedInstance();
    SendResponse(request_id, rb.LoadDataResourceBytes(resource_id));
  }
}
<|endoftext|>