<commit_before>// Copyright (c) 2012 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 "webkit/dom_storage/dom_storage_database.h"

#include "base/file_path.h"
#include "base/file_util.h"
#include "base/path_service.h"
#include "base/scoped_temp_dir.h"
#include "base/utf_string_conversions.h"
#include "sql/statement.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace dom_storage {

DomStorageDatabase::DomStorageDatabase()
    : db_(NULL),
      failed_to_open_(false),
      tried_to_recreate_(false) {
}

void CreateV1Table(sql::Connection* db) {
  ASSERT_TRUE(db->is_open());
  ASSERT_TRUE(db->Execute("DROP TABLE IF EXISTS ItemTable"));
  ASSERT_TRUE(db->Execute(
      "CREATE TABLE ItemTable ("
      "key TEXT UNIQUE ON CONFLICT REPLACE, "
      "value TEXT NOT NULL ON CONFLICT FAIL)"));
}

void CreateV2Table(sql::Connection* db) {
  ASSERT_TRUE(db->is_open());
  ASSERT_TRUE(db->Execute("DROP TABLE IF EXISTS ItemTable"));
  ASSERT_TRUE(db->Execute(
      "CREATE TABLE ItemTable ("
      "key TEXT UNIQUE ON CONFLICT REPLACE, "
      "value BLOB NOT NULL ON CONFLICT FAIL)"));
}

void CreateInvalidKeyColumnTable(sql::Connection* db) {
  // Create a table with the key type as FLOAT - this is "invalid"
  // as far as the DOM Storage db is concerned.
  ASSERT_TRUE(db->is_open());
  ASSERT_TRUE(db->Execute("DROP TABLE IF EXISTS ItemTable"));
  ASSERT_TRUE(db->Execute(
      "CREATE TABLE IF NOT EXISTS ItemTable ("
      "key FLOAT UNIQUE ON CONFLICT REPLACE, "
      "value BLOB NOT NULL ON CONFLICT FAIL)"));
}
void CreateInvalidValueColumnTable(sql::Connection* db) {
  // Create a table with the value type as FLOAT - this is "invalid"
  // as far as the DOM Storage db is concerned.
  ASSERT_TRUE(db->is_open());
  ASSERT_TRUE(db->Execute("DROP TABLE IF EXISTS ItemTable"));
  ASSERT_TRUE(db->Execute(
      "CREATE TABLE IF NOT EXISTS ItemTable ("
      "key TEXT UNIQUE ON CONFLICT REPLACE, "
      "value FLOAT NOT NULL ON CONFLICT FAIL)"));
}

void InsertDataV1(sql::Connection* db,
                  const string16& key,
                  const string16& value) {
  sql::Statement statement(db->GetCachedStatement(SQL_FROM_HERE,
      "INSERT INTO ItemTable VALUES (?,?)"));
  statement.BindString16(0, key);
  statement.BindString16(1, value);
  ASSERT_TRUE(statement.is_valid());
  statement.Run();
}

void CheckValuesMatch(DomStorageDatabase* db,
                      const ValuesMap& expected) {
  ValuesMap values_read;
  db->ReadAllValues(&values_read);
  EXPECT_EQ(expected.size(), values_read.size());

  ValuesMap::const_iterator it = values_read.begin();
  for (; it != values_read.end(); ++it) {
    string16 key = it->first;
    NullableString16 value = it->second;
    NullableString16 expected_value = expected.find(key)->second;
    EXPECT_EQ(expected_value.string(), value.string());
    EXPECT_EQ(expected_value.is_null(), value.is_null());
  }
}

void CreateMapWithValues(ValuesMap* values) {
  string16 kCannedKeys[] = {
      ASCIIToUTF16("test"),
      ASCIIToUTF16("company"),
      ASCIIToUTF16("date"),
      ASCIIToUTF16("empty")
  };
  NullableString16 kCannedValues[] = {
      NullableString16(ASCIIToUTF16("123"), false),
      NullableString16(ASCIIToUTF16("Google"), false),
      NullableString16(ASCIIToUTF16("18-01-2012"), false),
      NullableString16(ASCIIToUTF16(""), false)
  };
  for (unsigned i = 0; i < sizeof(kCannedKeys) / sizeof(kCannedKeys[0]); i++)
    (*values)[kCannedKeys[i]] = kCannedValues[i];
}

TEST(DomStorageDatabaseTest, SimpleOpenAndClose) {
  DomStorageDatabase db;
  EXPECT_FALSE(db.IsOpen());
  ASSERT_TRUE(db.LazyOpen(true));
  EXPECT_TRUE(db.IsOpen());
  EXPECT_EQ(DomStorageDatabase::V2, db.DetectSchemaVersion());
  db.Close();
  EXPECT_FALSE(db.IsOpen());
}

TEST(DomStorageDatabaseTest, TestLazyOpenIsLazy) {
  // This test needs to operate with a file on disk to ensure that we will
  // open a file that already exists when only invoking ReadAllValues.
  ScopedTempDir temp_dir;
  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
  FilePath file_name = temp_dir.path().AppendASCII("TestDomStorageDatabase.db");

  DomStorageDatabase db(file_name);
  EXPECT_FALSE(db.IsOpen());
  ValuesMap values;
  db.ReadAllValues(&values);
  // Reading an empty db should not open the database.
  EXPECT_FALSE(db.IsOpen());

  values[ASCIIToUTF16("key")] = NullableString16(ASCIIToUTF16("value"), false);
  db.CommitChanges(false, values);
  // Writing content should open the database.
  EXPECT_TRUE(db.IsOpen());

  db.Close();
  ASSERT_FALSE(db.IsOpen());

  // Reading from an existing database should open the database.
  CheckValuesMatch(&db, values);
  EXPECT_TRUE(db.IsOpen());
}

TEST(DomStorageDatabaseTest, TestDetectSchemaVersion) {
  DomStorageDatabase db;
  db.db_.reset(new sql::Connection());
  ASSERT_TRUE(db.db_->OpenInMemory());

  CreateInvalidValueColumnTable(db.db_.get());
  EXPECT_EQ(DomStorageDatabase::INVALID, db.DetectSchemaVersion());

  CreateInvalidKeyColumnTable(db.db_.get());
  EXPECT_EQ(DomStorageDatabase::INVALID, db.DetectSchemaVersion());

  CreateV1Table(db.db_.get());
  EXPECT_EQ(DomStorageDatabase::V1, db.DetectSchemaVersion());

  CreateV2Table(db.db_.get());
  EXPECT_EQ(DomStorageDatabase::V2, db.DetectSchemaVersion());
}

TEST(DomStorageDatabaseTest, TestLazyOpenUpgradesDatabase) {
  // This test needs to operate with a file on disk so that we
  // can create a table at version 1 and then close it again
  // so that LazyOpen sees there is work to do (LazyOpen will return
  // early if the database is already open).
  ScopedTempDir temp_dir;
  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
  FilePath file_name = temp_dir.path().AppendASCII("TestDomStorageDatabase.db");

  DomStorageDatabase db(file_name);
  db.db_.reset(new sql::Connection());
  ASSERT_TRUE(db.db_->Open(file_name));
  CreateV1Table(db.db_.get());
  db.Close();

  EXPECT_TRUE(db.LazyOpen(true));
  EXPECT_EQ(DomStorageDatabase::V2, db.DetectSchemaVersion());
}

TEST(DomStorageDatabaseTest, SimpleWriteAndReadBack) {
  DomStorageDatabase db;

  ValuesMap storage;
  CreateMapWithValues(&storage);

  EXPECT_TRUE(db.CommitChanges(false, storage));
  CheckValuesMatch(&db, storage);
}

TEST(DomStorageDatabaseTest, WriteWithClear) {
  DomStorageDatabase db;

  ValuesMap storage;
  CreateMapWithValues(&storage);

  ASSERT_TRUE(db.CommitChanges(false, storage));
  CheckValuesMatch(&db, storage);

  // Insert some values, clearing the database first.
  storage.clear();
  storage[ASCIIToUTF16("another_key")] =
      NullableString16(ASCIIToUTF16("test"), false);
  ASSERT_TRUE(db.CommitChanges(true, storage));
  CheckValuesMatch(&db, storage);

  // Now clear the values without inserting any new ones.
  storage.clear();
  ASSERT_TRUE(db.CommitChanges(true, storage));
  CheckValuesMatch(&db, storage);
}

TEST(DomStorageDatabaseTest, UpgradeFromV1ToV2WithData) {
  const string16 kCannedKey = ASCIIToUTF16("foo");
  const NullableString16 kCannedValue(ASCIIToUTF16("bar"), false);
  ValuesMap expected;
  expected[kCannedKey] = kCannedValue;

  DomStorageDatabase db;
  db.db_.reset(new sql::Connection());
  ASSERT_TRUE(db.db_->OpenInMemory());
  CreateV1Table(db.db_.get());
  InsertDataV1(db.db_.get(), kCannedKey, kCannedValue.string());

  ASSERT_TRUE(db.UpgradeVersion1To2());

  EXPECT_EQ(DomStorageDatabase::V2, db.DetectSchemaVersion());

  CheckValuesMatch(&db, expected);
}

TEST(DomStorageDatabaseTest, TestSimpleRemoveOneValue) {
  DomStorageDatabase db;

  ASSERT_TRUE(db.LazyOpen(true));
  const string16 kCannedKey = ASCIIToUTF16("test");
  const NullableString16 kCannedValue(ASCIIToUTF16("data"), false);
  ValuesMap expected;
  expected[kCannedKey] = kCannedValue;

  // First write some data into the database.
  ASSERT_TRUE(db.CommitChanges(false, expected));
  CheckValuesMatch(&db, expected);

  ValuesMap values;
  // A null string in the map should mean that that key gets
  // removed.
  values[kCannedKey] = NullableString16(true);
  EXPECT_TRUE(db.CommitChanges(false, values));

  expected.clear();
  CheckValuesMatch(&db, expected);
}

// TODO(benm): Enable this test in follow up patch once the test data has
// landed. (try-bots don't like adding binary files like test databases :) )
TEST(DomStorageDatabaseTest, DISABLED_TestCanOpenAndReadWebCoreDatabase) {
  FilePath webcore_database;
  PathService::Get(base::DIR_SOURCE_ROOT, &webcore_database);
  webcore_database = webcore_database.AppendASCII("webkit");
  webcore_database = webcore_database.AppendASCII("data");
  webcore_database = webcore_database.AppendASCII("dom_storage");
  webcore_database =
      webcore_database.AppendASCII("webcore_test_database.localstorage");

  ASSERT_TRUE(file_util::PathExists(webcore_database));

  DomStorageDatabase db(webcore_database);
  ValuesMap values;
  db.ReadAllValues(&values);
  EXPECT_TRUE(db.IsOpen());
  EXPECT_EQ(2u, values.size());

  ValuesMap::const_iterator it =
      values.find(ASCIIToUTF16("value"));
  EXPECT_TRUE(it != values.end());
  EXPECT_EQ(ASCIIToUTF16("I am in local storage!"), it->second.string());

  it = values.find(ASCIIToUTF16("timestamp"));
  EXPECT_TRUE(it != values.end());
  EXPECT_EQ(ASCIIToUTF16("1326738338841"), it->second.string());

  it = values.find(ASCIIToUTF16("not_there"));
  EXPECT_TRUE(it == values.end());
}

TEST(DomStorageDatabaseTest, TestCanOpenFileThatIsNotADatabase) {
  // Write into the temporary file first.
  ScopedTempDir temp_dir;
  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
  FilePath file_name = temp_dir.path().AppendASCII("TestDomStorageDatabase.db");

  const char kData[] = "I am not a database.";
  file_util::WriteFile(file_name, kData, strlen(kData));

  {
    // Try and open the file. As it's not a database, we should end up deleting
    // it and creating a new, valid file, so everything should actually
    // succeed.
    DomStorageDatabase db(file_name);
    ValuesMap values;
    CreateMapWithValues(&values);
    EXPECT_TRUE(db.CommitChanges(true, values));
    EXPECT_TRUE(db.CommitChanges(false, values));
    EXPECT_TRUE(db.IsOpen());

    CheckValuesMatch(&db, values);
  }

  {
    // Try to open a directory, we should fail gracefully and not attempt
    // to delete it.
    DomStorageDatabase db(temp_dir.path());
    ValuesMap values;
    CreateMapWithValues(&values);
    EXPECT_FALSE(db.CommitChanges(true, values));
    EXPECT_FALSE(db.CommitChanges(false, values));
    EXPECT_FALSE(db.IsOpen());

    values.clear();

    db.ReadAllValues(&values);
    EXPECT_EQ(0u, values.size());
    EXPECT_FALSE(db.IsOpen());

    EXPECT_TRUE(file_util::PathExists(temp_dir.path()));
  }
}

}  // namespace dom_storage
<commit_msg>Enable DomStorageDatabaseTest.TestCanOpenAndReadWebCoreDatabase.<commit_after>// Copyright (c) 2012 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 "webkit/dom_storage/dom_storage_database.h"

#include "base/file_path.h"
#include "base/file_util.h"
#include "base/path_service.h"
#include "base/scoped_temp_dir.h"
#include "base/utf_string_conversions.h"
#include "sql/statement.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace dom_storage {

DomStorageDatabase::DomStorageDatabase()
    : db_(NULL),
      failed_to_open_(false),
      tried_to_recreate_(false) {
}

void CreateV1Table(sql::Connection* db) {
  ASSERT_TRUE(db->is_open());
  ASSERT_TRUE(db->Execute("DROP TABLE IF EXISTS ItemTable"));
  ASSERT_TRUE(db->Execute(
      "CREATE TABLE ItemTable ("
      "key TEXT UNIQUE ON CONFLICT REPLACE, "
      "value TEXT NOT NULL ON CONFLICT FAIL)"));
}

void CreateV2Table(sql::Connection* db) {
  ASSERT_TRUE(db->is_open());
  ASSERT_TRUE(db->Execute("DROP TABLE IF EXISTS ItemTable"));
  ASSERT_TRUE(db->Execute(
      "CREATE TABLE ItemTable ("
      "key TEXT UNIQUE ON CONFLICT REPLACE, "
      "value BLOB NOT NULL ON CONFLICT FAIL)"));
}

void CreateInvalidKeyColumnTable(sql::Connection* db) {
  // Create a table with the key type as FLOAT - this is "invalid"
  // as far as the DOM Storage db is concerned.
  ASSERT_TRUE(db->is_open());
  ASSERT_TRUE(db->Execute("DROP TABLE IF EXISTS ItemTable"));
  ASSERT_TRUE(db->Execute(
      "CREATE TABLE IF NOT EXISTS ItemTable ("
      "key FLOAT UNIQUE ON CONFLICT REPLACE, "
      "value BLOB NOT NULL ON CONFLICT FAIL)"));
}
void CreateInvalidValueColumnTable(sql::Connection* db) {
  // Create a table with the value type as FLOAT - this is "invalid"
  // as far as the DOM Storage db is concerned.
  ASSERT_TRUE(db->is_open());
  ASSERT_TRUE(db->Execute("DROP TABLE IF EXISTS ItemTable"));
  ASSERT_TRUE(db->Execute(
      "CREATE TABLE IF NOT EXISTS ItemTable ("
      "key TEXT UNIQUE ON CONFLICT REPLACE, "
      "value FLOAT NOT NULL ON CONFLICT FAIL)"));
}

void InsertDataV1(sql::Connection* db,
                  const string16& key,
                  const string16& value) {
  sql::Statement statement(db->GetCachedStatement(SQL_FROM_HERE,
      "INSERT INTO ItemTable VALUES (?,?)"));
  statement.BindString16(0, key);
  statement.BindString16(1, value);
  ASSERT_TRUE(statement.is_valid());
  statement.Run();
}

void CheckValuesMatch(DomStorageDatabase* db,
                      const ValuesMap& expected) {
  ValuesMap values_read;
  db->ReadAllValues(&values_read);
  EXPECT_EQ(expected.size(), values_read.size());

  ValuesMap::const_iterator it = values_read.begin();
  for (; it != values_read.end(); ++it) {
    string16 key = it->first;
    NullableString16 value = it->second;
    NullableString16 expected_value = expected.find(key)->second;
    EXPECT_EQ(expected_value.string(), value.string());
    EXPECT_EQ(expected_value.is_null(), value.is_null());
  }
}

void CreateMapWithValues(ValuesMap* values) {
  string16 kCannedKeys[] = {
      ASCIIToUTF16("test"),
      ASCIIToUTF16("company"),
      ASCIIToUTF16("date"),
      ASCIIToUTF16("empty")
  };
  NullableString16 kCannedValues[] = {
      NullableString16(ASCIIToUTF16("123"), false),
      NullableString16(ASCIIToUTF16("Google"), false),
      NullableString16(ASCIIToUTF16("18-01-2012"), false),
      NullableString16(ASCIIToUTF16(""), false)
  };
  for (unsigned i = 0; i < sizeof(kCannedKeys) / sizeof(kCannedKeys[0]); i++)
    (*values)[kCannedKeys[i]] = kCannedValues[i];
}

TEST(DomStorageDatabaseTest, SimpleOpenAndClose) {
  DomStorageDatabase db;
  EXPECT_FALSE(db.IsOpen());
  ASSERT_TRUE(db.LazyOpen(true));
  EXPECT_TRUE(db.IsOpen());
  EXPECT_EQ(DomStorageDatabase::V2, db.DetectSchemaVersion());
  db.Close();
  EXPECT_FALSE(db.IsOpen());
}

TEST(DomStorageDatabaseTest, TestLazyOpenIsLazy) {
  // This test needs to operate with a file on disk to ensure that we will
  // open a file that already exists when only invoking ReadAllValues.
  ScopedTempDir temp_dir;
  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
  FilePath file_name = temp_dir.path().AppendASCII("TestDomStorageDatabase.db");

  DomStorageDatabase db(file_name);
  EXPECT_FALSE(db.IsOpen());
  ValuesMap values;
  db.ReadAllValues(&values);
  // Reading an empty db should not open the database.
  EXPECT_FALSE(db.IsOpen());

  values[ASCIIToUTF16("key")] = NullableString16(ASCIIToUTF16("value"), false);
  db.CommitChanges(false, values);
  // Writing content should open the database.
  EXPECT_TRUE(db.IsOpen());

  db.Close();
  ASSERT_FALSE(db.IsOpen());

  // Reading from an existing database should open the database.
  CheckValuesMatch(&db, values);
  EXPECT_TRUE(db.IsOpen());
}

TEST(DomStorageDatabaseTest, TestDetectSchemaVersion) {
  DomStorageDatabase db;
  db.db_.reset(new sql::Connection());
  ASSERT_TRUE(db.db_->OpenInMemory());

  CreateInvalidValueColumnTable(db.db_.get());
  EXPECT_EQ(DomStorageDatabase::INVALID, db.DetectSchemaVersion());

  CreateInvalidKeyColumnTable(db.db_.get());
  EXPECT_EQ(DomStorageDatabase::INVALID, db.DetectSchemaVersion());

  CreateV1Table(db.db_.get());
  EXPECT_EQ(DomStorageDatabase::V1, db.DetectSchemaVersion());

  CreateV2Table(db.db_.get());
  EXPECT_EQ(DomStorageDatabase::V2, db.DetectSchemaVersion());
}

TEST(DomStorageDatabaseTest, TestLazyOpenUpgradesDatabase) {
  // This test needs to operate with a file on disk so that we
  // can create a table at version 1 and then close it again
  // so that LazyOpen sees there is work to do (LazyOpen will return
  // early if the database is already open).
  ScopedTempDir temp_dir;
  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
  FilePath file_name = temp_dir.path().AppendASCII("TestDomStorageDatabase.db");

  DomStorageDatabase db(file_name);
  db.db_.reset(new sql::Connection());
  ASSERT_TRUE(db.db_->Open(file_name));
  CreateV1Table(db.db_.get());
  db.Close();

  EXPECT_TRUE(db.LazyOpen(true));
  EXPECT_EQ(DomStorageDatabase::V2, db.DetectSchemaVersion());
}

TEST(DomStorageDatabaseTest, SimpleWriteAndReadBack) {
  DomStorageDatabase db;

  ValuesMap storage;
  CreateMapWithValues(&storage);

  EXPECT_TRUE(db.CommitChanges(false, storage));
  CheckValuesMatch(&db, storage);
}

TEST(DomStorageDatabaseTest, WriteWithClear) {
  DomStorageDatabase db;

  ValuesMap storage;
  CreateMapWithValues(&storage);

  ASSERT_TRUE(db.CommitChanges(false, storage));
  CheckValuesMatch(&db, storage);

  // Insert some values, clearing the database first.
  storage.clear();
  storage[ASCIIToUTF16("another_key")] =
      NullableString16(ASCIIToUTF16("test"), false);
  ASSERT_TRUE(db.CommitChanges(true, storage));
  CheckValuesMatch(&db, storage);

  // Now clear the values without inserting any new ones.
  storage.clear();
  ASSERT_TRUE(db.CommitChanges(true, storage));
  CheckValuesMatch(&db, storage);
}

TEST(DomStorageDatabaseTest, UpgradeFromV1ToV2WithData) {
  const string16 kCannedKey = ASCIIToUTF16("foo");
  const NullableString16 kCannedValue(ASCIIToUTF16("bar"), false);
  ValuesMap expected;
  expected[kCannedKey] = kCannedValue;

  DomStorageDatabase db;
  db.db_.reset(new sql::Connection());
  ASSERT_TRUE(db.db_->OpenInMemory());
  CreateV1Table(db.db_.get());
  InsertDataV1(db.db_.get(), kCannedKey, kCannedValue.string());

  ASSERT_TRUE(db.UpgradeVersion1To2());

  EXPECT_EQ(DomStorageDatabase::V2, db.DetectSchemaVersion());

  CheckValuesMatch(&db, expected);
}

TEST(DomStorageDatabaseTest, TestSimpleRemoveOneValue) {
  DomStorageDatabase db;

  ASSERT_TRUE(db.LazyOpen(true));
  const string16 kCannedKey = ASCIIToUTF16("test");
  const NullableString16 kCannedValue(ASCIIToUTF16("data"), false);
  ValuesMap expected;
  expected[kCannedKey] = kCannedValue;

  // First write some data into the database.
  ASSERT_TRUE(db.CommitChanges(false, expected));
  CheckValuesMatch(&db, expected);

  ValuesMap values;
  // A null string in the map should mean that that key gets
  // removed.
  values[kCannedKey] = NullableString16(true);
  EXPECT_TRUE(db.CommitChanges(false, values));

  expected.clear();
  CheckValuesMatch(&db, expected);
}

TEST(DomStorageDatabaseTest, TestCanOpenAndReadWebCoreDatabase) {
  FilePath webcore_database;
  PathService::Get(base::DIR_SOURCE_ROOT, &webcore_database);
  webcore_database = webcore_database.AppendASCII("webkit");
  webcore_database = webcore_database.AppendASCII("data");
  webcore_database = webcore_database.AppendASCII("dom_storage");
  webcore_database =
      webcore_database.AppendASCII("webcore_test_database.localstorage");

  ASSERT_TRUE(file_util::PathExists(webcore_database));

  DomStorageDatabase db(webcore_database);
  ValuesMap values;
  db.ReadAllValues(&values);
  EXPECT_TRUE(db.IsOpen());
  EXPECT_EQ(2u, values.size());

  ValuesMap::const_iterator it =
      values.find(ASCIIToUTF16("value"));
  EXPECT_TRUE(it != values.end());
  EXPECT_EQ(ASCIIToUTF16("I am in local storage!"), it->second.string());

  it = values.find(ASCIIToUTF16("timestamp"));
  EXPECT_TRUE(it != values.end());
  EXPECT_EQ(ASCIIToUTF16("1326738338841"), it->second.string());

  it = values.find(ASCIIToUTF16("not_there"));
  EXPECT_TRUE(it == values.end());
}

TEST(DomStorageDatabaseTest, TestCanOpenFileThatIsNotADatabase) {
  // Write into the temporary file first.
  ScopedTempDir temp_dir;
  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
  FilePath file_name = temp_dir.path().AppendASCII("TestDomStorageDatabase.db");

  const char kData[] = "I am not a database.";
  file_util::WriteFile(file_name, kData, strlen(kData));

  {
    // Try and open the file. As it's not a database, we should end up deleting
    // it and creating a new, valid file, so everything should actually
    // succeed.
    DomStorageDatabase db(file_name);
    ValuesMap values;
    CreateMapWithValues(&values);
    EXPECT_TRUE(db.CommitChanges(true, values));
    EXPECT_TRUE(db.CommitChanges(false, values));
    EXPECT_TRUE(db.IsOpen());

    CheckValuesMatch(&db, values);
  }

  {
    // Try to open a directory, we should fail gracefully and not attempt
    // to delete it.
    DomStorageDatabase db(temp_dir.path());
    ValuesMap values;
    CreateMapWithValues(&values);
    EXPECT_FALSE(db.CommitChanges(true, values));
    EXPECT_FALSE(db.CommitChanges(false, values));
    EXPECT_FALSE(db.IsOpen());

    values.clear();

    db.ReadAllValues(&values);
    EXPECT_EQ(0u, values.size());
    EXPECT_FALSE(db.IsOpen());

    EXPECT_TRUE(file_util::PathExists(temp_dir.path()));
  }
}

}  // namespace dom_storage
<|endoftext|>


<commit_before>/* Copyright 2012 Dietrich Epp <depp@zdome.net> */
#include "defs.hpp"
#include "gamescreen.hpp"
#include "sprite.hpp"
#include "text.hpp"
#include "level.hpp"
#include "lball.hpp"
#include "linvade.hpp"
#include "lchase.hpp"
#include "ltitle.hpp"

#include "client/ui/event.hpp"
#include "client/viewport.hpp"
#include "client/opengl.hpp"
#include "client/bitmapfont.hpp"
#include "client/keycode.hpp"

#include "base/audio_source.h"

#include <cstdlib>

static const char *const TMSG[] = {
    "Level 1\n"
    "\n"
    "I grew up in a four-bit\n"
    "microcontroller on the\n"
    "wrong side of town...\n",

    "Level 2\n"
    "\n"
    "Sometimes you can change\n"
    "with the times...",

    "Level 3\n"
    "\n"
    "I could hardly keep up\n"
    "with these new things!",

    "But,\n"
    "I managed in the end.\n"
    "\n"
    "      For Ludum Dare #24\n"
    "         by Dietrich Epp"
};

static const int TIP_TIME = 1000 * 5;

static const unsigned LAG_THRESHOLD = 250;

using namespace LD24;

static const unsigned char KEY_MAP[] = {
    KEY_W,      CTL_UP,
    KP_8,       CTL_UP,
    KEY_Up,     CTL_UP,

    KEY_A,      CTL_LEFT,
    KP_4,       CTL_LEFT,
    KEY_Left,   CTL_LEFT,

    KEY_S,      CTL_DOWN,
    KP_5,       CTL_DOWN,
    KEY_Down,   CTL_DOWN,

    KEY_D,      CTL_RIGHT,
    KP_6,       CTL_RIGHT,
    KEY_Right,  CTL_RIGHT,

    KEY_Space,  CTL_ACTION,
    KP_0,       CTL_ACTION,

    255
};

GameScreen::GameScreen()
    : m_level(NULL),
      m_init(false),
      m_key(KEY_MAP),
      m_lvchange(-1),
      m_lvno(0),
      m_tip(-1)
{
    m_letterbox.setISize(1280, 720);
}

GameScreen::~GameScreen()
{
    if (m_level)
        delete m_level;
}

unsigned GameScreen::getControls()
{
    unsigned x = 0;
    for (int i = 0; i < 5; ++i)
        if (m_key.inputState(i))
            x |= 1u << i;
    return x;
}

void GameScreen::advance(unsigned ticks, int controls)
{
    if (m_lvchange > 7)
        m_lvchange = -1;
    if (m_lvchange >= 0) {
        if (m_level) {
            delete m_level;
            m_level = NULL;
        }
        switch (m_lvchange) {
        case 1: m_level = new LTitle(*this, TMSG[0], false); break;
        case 2: m_level = new LBall(*this); break;
        case 3: m_level = new LTitle(*this, TMSG[1], false); break;
        case 4: m_level = new LChase(*this); break;
        case 5: m_level = new LTitle(*this, TMSG[2], false); break;
        case 6: m_level = new LInvade(*this); break;
        case 7: m_level = new LTitle(*this, TMSG[3], true); break;
        default: assert(0);
        }
        m_lvno = m_lvchange;
        m_lvchange = -1;
        m_tip = 0;
        m_tiptick = ticks;
        if (m_level->levelTips.empty())
            m_tip = -1;
    }
    assert(m_level != NULL);
    m_level->advance(ticks, controls);
    if (m_tip < 0 && !m_level->levelTips.empty())
        m_tip = 0;
    if (m_tip >= 0) {
        if (std::abs((int) (ticks - m_tiptick)) > TIP_TIME)
            m_tip++;
        if ((size_t) m_tip >= m_level->levelTips.size())
            m_tip = 0;
    }
}

void GameScreen::update(unsigned ticks)
{
    if (!m_init) {
        m_lvchange = m_lvno + 1;
        m_tickref = ticks;
        m_init = true;
        m_delta = 0;
    } else {
        unsigned delta = ticks - m_tickref;
        if (delta > LAG_THRESHOLD) {
            m_tickref = ticks;
            m_delta = 0;
            advance(ticks, getControls());
        } else {
            if (delta >= (unsigned) FRAME_TIME) {
                unsigned ctl = getControls();
                unsigned frames = delta / FRAME_TIME;
                unsigned ref = m_tickref;
                for (unsigned i = 0; i < frames; ++i)
                    advance(ref + (i + 1) * FRAME_TIME, ctl);
                m_tickref = ref + FRAME_TIME * frames;
                delta -= FRAME_TIME * frames;
            }
            m_delta = delta;
        }
    }
    sg_audio_source_commit(ticks, ticks);
}

void GameScreen::draw(Viewport &v, unsigned msec)
{
    (void) msec;
    (void) &v;
    m_letterbox.setOSize(v.width(), v.height());
    m_letterbox.enable();

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(0, 1280, 0, 720, -1, 1);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

    if (m_level) {
        m_level->draw(m_delta);

        if (m_tip >= 0) {
            glEnable(GL_BLEND);
            glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_COLOR);
            getFont().print(8, 8, m_level->levelTips.at(m_tip));
            glDisable(GL_BLEND);
        }
    }

    m_letterbox.disable();
}

void GameScreen::handleEvent(const UI::Event &evt)
{
    switch (evt.type) {
    case UI::KeyDown:
        switch (evt.keyEvent().key) {
        case KEY_F5:
            nextLevel();
            break;
        }
    case UI::KeyUp:
        m_key.handleKeyEvent(evt.keyEvent());
        break;

    default:
        break;
    }
}
<commit_msg>Fix help tip timing issues<commit_after>/* Copyright 2012 Dietrich Epp <depp@zdome.net> */
#include "defs.hpp"
#include "gamescreen.hpp"
#include "sprite.hpp"
#include "text.hpp"
#include "level.hpp"
#include "lball.hpp"
#include "linvade.hpp"
#include "lchase.hpp"
#include "ltitle.hpp"

#include "client/ui/event.hpp"
#include "client/viewport.hpp"
#include "client/opengl.hpp"
#include "client/bitmapfont.hpp"
#include "client/keycode.hpp"

#include "base/audio_source.h"

#include <cstdlib>

static const char *const TMSG[] = {
    "Level 1\n"
    "\n"
    "I grew up in a four-bit\n"
    "microcontroller on the\n"
    "wrong side of town...\n",

    "Level 2\n"
    "\n"
    "Sometimes you can change\n"
    "with the times...",

    "Level 3\n"
    "\n"
    "I could hardly keep up\n"
    "with these new things!",

    "But,\n"
    "I managed in the end.\n"
    "\n"
    "      For Ludum Dare #24\n"
    "         by Dietrich Epp"
};

static const int TIP_TIME = 1000 * 5;

static const unsigned LAG_THRESHOLD = 250;

using namespace LD24;

static const unsigned char KEY_MAP[] = {
    KEY_W,      CTL_UP,
    KP_8,       CTL_UP,
    KEY_Up,     CTL_UP,

    KEY_A,      CTL_LEFT,
    KP_4,       CTL_LEFT,
    KEY_Left,   CTL_LEFT,

    KEY_S,      CTL_DOWN,
    KP_5,       CTL_DOWN,
    KEY_Down,   CTL_DOWN,

    KEY_D,      CTL_RIGHT,
    KP_6,       CTL_RIGHT,
    KEY_Right,  CTL_RIGHT,

    KEY_Space,  CTL_ACTION,
    KP_0,       CTL_ACTION,

    255
};

GameScreen::GameScreen()
    : m_level(NULL),
      m_init(false),
      m_key(KEY_MAP),
      m_lvchange(-1),
      m_lvno(0),
      m_tip(-1)
{
    m_letterbox.setISize(1280, 720);
}

GameScreen::~GameScreen()
{
    if (m_level)
        delete m_level;
}

unsigned GameScreen::getControls()
{
    unsigned x = 0;
    for (int i = 0; i < 5; ++i)
        if (m_key.inputState(i))
            x |= 1u << i;
    return x;
}

void GameScreen::advance(unsigned ticks, int controls)
{
    if (m_lvchange > 7)
        m_lvchange = -1;
    if (m_lvchange >= 0) {
        if (m_level) {
            delete m_level;
            m_level = NULL;
        }
        switch (m_lvchange) {
        case 1: m_level = new LTitle(*this, TMSG[0], false); break;
        case 2: m_level = new LBall(*this); break;
        case 3: m_level = new LTitle(*this, TMSG[1], false); break;
        case 4: m_level = new LChase(*this); break;
        case 5: m_level = new LTitle(*this, TMSG[2], false); break;
        case 6: m_level = new LInvade(*this); break;
        case 7: m_level = new LTitle(*this, TMSG[3], true); break;
        default: assert(0);
        }
        m_lvno = m_lvchange;
        m_lvchange = -1;
        m_tip = 0;
        m_tiptick = ticks;
        if (m_level->levelTips.empty())
            m_tip = -1;
    }
    assert(m_level != NULL);
    m_level->advance(ticks, controls);
    if (m_tip < 0 && !m_level->levelTips.empty()) {
        m_tiptick = ticks;
        m_tip = 0;
    }
    if (m_tip >= 0) {
        if (std::abs((int) (ticks - m_tiptick)) > TIP_TIME) {
            m_tiptick = ticks;
            m_tip++;
        }
        if ((size_t) m_tip >= m_level->levelTips.size()) {
            m_tip = 0;
            if (m_level->levelTips.empty())
                m_tip = -1;
        }
    }
}

void GameScreen::update(unsigned ticks)
{
    if (!m_init) {
        m_lvchange = m_lvno + 1;
        m_tickref = ticks;
        m_init = true;
        m_delta = 0;
    } else {
        unsigned delta = ticks - m_tickref;
        if (delta > LAG_THRESHOLD) {
            m_tickref = ticks;
            m_delta = 0;
            advance(ticks, getControls());
        } else {
            if (delta >= (unsigned) FRAME_TIME) {
                unsigned ctl = getControls();
                unsigned frames = delta / FRAME_TIME;
                unsigned ref = m_tickref;
                for (unsigned i = 0; i < frames; ++i)
                    advance(ref + (i + 1) * FRAME_TIME, ctl);
                m_tickref = ref + FRAME_TIME * frames;
                delta -= FRAME_TIME * frames;
            }
            m_delta = delta;
        }
    }
    sg_audio_source_commit(ticks, ticks);
}

void GameScreen::draw(Viewport &v, unsigned msec)
{
    (void) msec;
    (void) &v;
    m_letterbox.setOSize(v.width(), v.height());
    m_letterbox.enable();

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(0, 1280, 0, 720, -1, 1);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

    if (m_level) {
        m_level->draw(m_delta);

        if (m_tip >= 0) {
            glEnable(GL_BLEND);
            glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_COLOR);
            getFont().print(8, 8, m_level->levelTips.at(m_tip));
            glDisable(GL_BLEND);
        }
    }

    m_letterbox.disable();
}

void GameScreen::handleEvent(const UI::Event &evt)
{
    switch (evt.type) {
    case UI::KeyDown:
        switch (evt.keyEvent().key) {
        case KEY_F5:
            nextLevel();
            break;
        }
    case UI::KeyUp:
        m_key.handleKeyEvent(evt.keyEvent());
        break;

    default:
        break;
    }
}
<|endoftext|>


<commit_before>// stars.hxx -- data structures and routines for managing and rendering stars.
//
// Written by Curtis Olson, started August 1997.
//
// Copyright (C) 1997  Curtis L. Olson  - curt@me.umn.edu
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program 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
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//
// $Id$
// (Log is kept at end of this file)

#ifndef _STARS_HXX
#define _STARS_HXX

#ifndef __cplusplus
# error This library requires C++
#endif

#include <Time/fg_time.hxx>

#define FG_STAR_LEVELS 8         // how many star transitions

// Initialize the Star Management Subsystem
int fgStarsInit( void );

// Draw the Stars
void fgStarsRender( void );

extern struct OrbElements pltOrbElements[9];
extern fgTIME cur_time_params;

#endif // _STARS_HXX

// $Log$
// Revision 1.7  1998/09/24 15:36:20  curt
// Converted to c++ style comments.
//
// Revision 1.6  1998/09/24 15:25:26  curt
// Miscellaneous tweaks.
//
//
// Revision 1.5  1998/09/17 18:25:13  curt
// Fixed output message.
//
// Revision 1.4  1998/09/15 04:26:23  curt
// New textured moon and rewritten/restructured Astro code contributed by Durk
// Talsma.
//
// Revision 1.3  1998/08/06 12:45:20  curt
// Modified to bring in stars in 8 increments based on magnitude, not number
// of stars.
//
// Revision 1.2  1998/04/28 01:19:03  curt
// Type-ified fgTIME and fgVIEW
//
// Revision 1.1  1998/04/22 13:21:35  curt
// C++ - ifing the code a bit.
//
// Revision 1.5  1998/04/21 17:02:33  curt
// Prepairing for C++ integration.
//
// Revision 1.4  1998/02/12 21:59:39  curt
// Incorporated code changes contributed by Charlie Hotchkiss
// <chotchkiss@namg.us.anritsu.com>
//
// Revision 1.3  1998/01/22 02:59:28  curt
// Changed #ifdef FILE_H to #ifdef _FILE_H
//
// Revision 1.2  1998/01/19 18:40:18  curt
// Tons of little changes to clean up the code and to remove fatal errors
// when building with the c++ compiler.
//
// Revision 1.1  1998/01/07 03:16:20  curt
// Moved from .../Src/Scenery/ to .../Src/Astro/
//
// Revision 1.6  1997/10/25 03:18:29  curt
// Incorporated sun, moon, and planet position and rendering code contributed
// by Durk Talsma.
//
// Revision 1.5  1997/09/18 16:20:09  curt
// At dusk/dawn add/remove stars in stages.
//
// Revision 1.4  1997/09/05 01:36:00  curt
// Working on getting stars right.
//
// Revision 1.3  1997/08/29 17:55:28  curt
// Worked on properly aligning the stars.
//
// Revision 1.2  1997/08/27 21:32:30  curt
// Restructured view calculation code.  Added stars.
//
// Revision 1.1  1997/08/27 03:34:50  curt
// Initial revision.
//

<commit_msg>MacOS portability changes contributed by "Robert Puyol" <puyol@abvent.fr><commit_after>// stars.hxx -- data structures and routines for managing and rendering stars.
//
// Written by Curtis Olson, started August 1997.
//
// Copyright (C) 1997  Curtis L. Olson  - curt@me.umn.edu
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program 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
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//
// $Id$
// (Log is kept at end of this file)

#ifndef _STARS_HXX
#define _STARS_HXX

#ifndef __cplusplus
# error This library requires C++
#endif

#include <Time/fg_time.hxx>

#define FG_STAR_LEVELS 8         // how many star transitions

// Initialize the Star Management Subsystem
int fgStarsInit( void );

// Draw the Stars
void fgStarsRender( void );

// [no longer used?] extern struct OrbElements pltOrbElements[9];
extern fgTIME cur_time_params;

#endif // _STARS_HXX

// $Log$
// Revision 1.8  1999/01/19 20:57:00  curt
// MacOS portability changes contributed by "Robert Puyol" <puyol@abvent.fr>
//
// Revision 1.7  1998/09/24 15:36:20  curt
// Converted to c++ style comments.
//
// Revision 1.6  1998/09/24 15:25:26  curt
// Miscellaneous tweaks.
//
//
// Revision 1.5  1998/09/17 18:25:13  curt
// Fixed output message.
//
// Revision 1.4  1998/09/15 04:26:23  curt
// New textured moon and rewritten/restructured Astro code contributed by Durk
// Talsma.
//
// Revision 1.3  1998/08/06 12:45:20  curt
// Modified to bring in stars in 8 increments based on magnitude, not number
// of stars.
//
// Revision 1.2  1998/04/28 01:19:03  curt
// Type-ified fgTIME and fgVIEW
//
// Revision 1.1  1998/04/22 13:21:35  curt
// C++ - ifing the code a bit.
//
// Revision 1.5  1998/04/21 17:02:33  curt
// Prepairing for C++ integration.
//
// Revision 1.4  1998/02/12 21:59:39  curt
// Incorporated code changes contributed by Charlie Hotchkiss
// <chotchkiss@namg.us.anritsu.com>
//
// Revision 1.3  1998/01/22 02:59:28  curt
// Changed #ifdef FILE_H to #ifdef _FILE_H
//
// Revision 1.2  1998/01/19 18:40:18  curt
// Tons of little changes to clean up the code and to remove fatal errors
// when building with the c++ compiler.
//
// Revision 1.1  1998/01/07 03:16:20  curt
// Moved from .../Src/Scenery/ to .../Src/Astro/
//
// Revision 1.6  1997/10/25 03:18:29  curt
// Incorporated sun, moon, and planet position and rendering code contributed
// by Durk Talsma.
//
// Revision 1.5  1997/09/18 16:20:09  curt
// At dusk/dawn add/remove stars in stages.
//
// Revision 1.4  1997/09/05 01:36:00  curt
// Working on getting stars right.
//
// Revision 1.3  1997/08/29 17:55:28  curt
// Worked on properly aligning the stars.
//
// Revision 1.2  1997/08/27 21:32:30  curt
// Restructured view calculation code.  Added stars.
//
// Revision 1.1  1997/08/27 03:34:50  curt
// Initial revision.
//

<|endoftext|>


<commit_before>#include "phAdapt.h"
#include "chef.h"
#include "ph.h"
#include <ma.h>
#include <PCU.h>
#include <sam.h>

#include <cstdio>
#include <cassert>

namespace ph {

void setupPreBalance(Input& in, ma::Input* ma_in) {
  if ( in.preAdaptBalanceMethod == "parma" ) {
    ma_in->shouldRunPreParma = true;
  } else if( in.preAdaptBalanceMethod == "graph" ) {
    ma_in->shouldRunPreZoltan = true;
  } else if( in.preAdaptBalanceMethod == "rib" ) {
    ma_in->shouldRunPreZoltanRib = true;
  } else if ( in.preAdaptBalanceMethod == "none" ) {
    ma_in->shouldRunPreZoltan = false;
    ma_in->shouldRunPreZoltanRib = false;
    ma_in->shouldRunPreParma = false;
  } else {
    if (!PCU_Comm_Self())
      fprintf(stderr,
          "warning: ignoring unknown value of preAdaptBalanceMethod %s\n",
          in.preAdaptBalanceMethod.c_str());
  }
}

void setupMatching(ma::Input& in) {
  if (!PCU_Comm_Self())
    printf("Matched mesh: disabling"
           " snapping, and shape correction,\n");
  in.shouldSnap = false;
  in.shouldFixShape = false;
}

static void runFromErrorThreshold(Input& in, apf::Mesh2* m)
{
  const char* fieldname = in.adaptErrorFieldName.c_str();
  const unsigned idx = in.adaptErrorFieldIndex;
  const double errLimit = in.adaptErrorThreshold;
  const double factor = 0.5;
  apf::Field* szFld = sam::errorThreshold(m,fieldname,idx,errLimit,factor);
  assert(szFld);
  chef::adapt(m, szFld);
  apf::destroyField(szFld);
}

static void runFromGivenSize(Input&, apf::Mesh2* m)
{
  const unsigned idx = 5;
  apf::Field* szFld = sam::specifiedIso(m,"errors",idx);
  assert(szFld);
  chef::adapt(m, szFld);
  apf::destroyField(szFld);
}

void tetrahedronize(Input& in, apf::Mesh2* m)
{
  ma::Input* ma_in = ma::configureIdentity(m);
  setupPreBalance(in, ma_in);
  ma_in->shouldTurnLayerToTets = true;
  ma::adapt(ma_in);
  m->verify();
}

void adapt(Input& in, apf::Mesh2* m)
{
  typedef void (*Strategy)(Input&, apf::Mesh2*);
  static Strategy const table[PH_STRATEGIES] =
  {0//0
  ,runFromGivenSize//1
  ,runFromErrorThreshold//2
  ,0//3
  ,0//4
  ,0//5
  ,0//6
  ,chef::uniformRefinement //7
  ,0//8
  };
  table[in.adaptStrategy](in, m);
  m->verify();
}

}

namespace chef {
  void adapt(apf::Mesh2* m, apf::Field* szFld) {
    ma::Input* ma_in = ma::configure(m, szFld);
    ma_in->shouldRunPreZoltan = true;
    if (m->hasMatching())
      ph::setupMatching(*ma_in);
    ma::adapt(ma_in);
  }

  void uniformRefinement(ph::Input& in, apf::Mesh2* m)
  {
    ma::Input* ma_in = ma::configureMatching(m, in.recursiveUR);
    setupPreBalance(in, ma_in);
    ma_in->shouldRefineLayer = true;
    ma_in->splitAllLayerEdges = in.splitAllLayerEdges;
    if (in.snap) {
      if (!ma_in->shouldSnap)
        ph::fail("adapt.inp requests snapping but model doesn't support it\n");
    } else
      ma_in->shouldSnap = false;
    ma::adapt(ma_in);
  }
}
<commit_msg>zrib for Zoltan RIB to be consistent<commit_after>#include "phAdapt.h"
#include "chef.h"
#include "ph.h"
#include <ma.h>
#include <PCU.h>
#include <sam.h>

#include <cstdio>
#include <cassert>

namespace ph {

void setupPreBalance(Input& in, ma::Input* ma_in) {
  if ( in.preAdaptBalanceMethod == "parma" ) {
    ma_in->shouldRunPreParma = true;
  } else if( in.preAdaptBalanceMethod == "graph" ) {
    ma_in->shouldRunPreZoltan = true;
  } else if( in.preAdaptBalanceMethod == "zrib" ) {
    ma_in->shouldRunPreZoltanRib = true;
  } else if ( in.preAdaptBalanceMethod == "none" ) {
    ma_in->shouldRunPreZoltan = false;
    ma_in->shouldRunPreZoltanRib = false;
    ma_in->shouldRunPreParma = false;
  } else {
    if (!PCU_Comm_Self())
      fprintf(stderr,
          "warning: ignoring unknown value of preAdaptBalanceMethod %s\n",
          in.preAdaptBalanceMethod.c_str());
  }
}

void setupMatching(ma::Input& in) {
  if (!PCU_Comm_Self())
    printf("Matched mesh: disabling"
           " snapping, and shape correction,\n");
  in.shouldSnap = false;
  in.shouldFixShape = false;
}

static void runFromErrorThreshold(Input& in, apf::Mesh2* m)
{
  const char* fieldname = in.adaptErrorFieldName.c_str();
  const unsigned idx = in.adaptErrorFieldIndex;
  const double errLimit = in.adaptErrorThreshold;
  const double factor = 0.5;
  apf::Field* szFld = sam::errorThreshold(m,fieldname,idx,errLimit,factor);
  assert(szFld);
  chef::adapt(m, szFld);
  apf::destroyField(szFld);
}

static void runFromGivenSize(Input&, apf::Mesh2* m)
{
  const unsigned idx = 5;
  apf::Field* szFld = sam::specifiedIso(m,"errors",idx);
  assert(szFld);
  chef::adapt(m, szFld);
  apf::destroyField(szFld);
}

void tetrahedronize(Input& in, apf::Mesh2* m)
{
  ma::Input* ma_in = ma::configureIdentity(m);
  setupPreBalance(in, ma_in);
  ma_in->shouldTurnLayerToTets = true;
  ma::adapt(ma_in);
  m->verify();
}

void adapt(Input& in, apf::Mesh2* m)
{
  typedef void (*Strategy)(Input&, apf::Mesh2*);
  static Strategy const table[PH_STRATEGIES] =
  {0//0
  ,runFromGivenSize//1
  ,runFromErrorThreshold//2
  ,0//3
  ,0//4
  ,0//5
  ,0//6
  ,chef::uniformRefinement //7
  ,0//8
  };
  table[in.adaptStrategy](in, m);
  m->verify();
}

}

namespace chef {
  void adapt(apf::Mesh2* m, apf::Field* szFld) {
    ma::Input* ma_in = ma::configure(m, szFld);
    ma_in->shouldRunPreZoltan = true;
    if (m->hasMatching())
      ph::setupMatching(*ma_in);
    ma::adapt(ma_in);
  }

  void uniformRefinement(ph::Input& in, apf::Mesh2* m)
  {
    ma::Input* ma_in = ma::configureMatching(m, in.recursiveUR);
    setupPreBalance(in, ma_in);
    ma_in->shouldRefineLayer = true;
    ma_in->splitAllLayerEdges = in.splitAllLayerEdges;
    if (in.snap) {
      if (!ma_in->shouldSnap)
        ph::fail("adapt.inp requests snapping but model doesn't support it\n");
    } else
      ma_in->shouldSnap = false;
    ma::adapt(ma_in);
  }
}
<|endoftext|>


<commit_before>/*
 * AttentionSCM.cc
 *
 * Guile Scheme bindings for the attentionbank
 * Copyright (C) 2014 Cosmo Harrigan
 * Copyright (c) 2008, 2014, 2015, 2018 Linas Vepstas <linasvepstas@gmail.com>
 */

#ifdef HAVE_GUILE

#include <opencog/atoms/base/Link.h>
#include <opencog/attentionbank/AttentionBank.h>
#include <opencog/guile/SchemePrimitive.h>

namespace opencog {

class AttentionSCM
{
	protected:
		static void* init_in_guile(void*);
		static void init_in_module(void*);
		void init(void);
	public:
		AttentionSCM(void);
		~AttentionSCM();

		AttentionValuePtr get_av(const Handle&);
		Handle set_av(const Handle&, const AttentionValuePtr&);
		Handle inc_vlti(const Handle&);
		Handle dec_vlti(const Handle&);

		Handle update_af(int);
		int af_size(void);
		int set_af_size(int);
		Handle stimulate (const Handle&, double);

		Handle af_bindlink(const Handle&);
};
}

#include <opencog/atomspace/AtomSpace.h>
#include <opencog/guile/SchemePrimitive.h>
#include <opencog/guile/SchemeSmob.h>
#include <opencog/atoms/value/LinkValue.h>
#include <opencog/attentionbank/AFImplicator.h>

using namespace opencog;

// ========================================================

AttentionSCM::AttentionSCM(void)
{
	static bool is_init = false;
	if (is_init) return;
	is_init = true;
	scm_with_guile(init_in_guile, this);
}

void* AttentionSCM::init_in_guile(void* self)
{
	scm_c_define_module("opencog attention-bank", init_in_module, self);
	scm_c_use_module("opencog attention-bank");
	return NULL;
}

void AttentionSCM::init_in_module(void* data)
{
	AttentionSCM* self = (AttentionSCM*) data;
	self->init();
}

/// This is called while (opencog attention-bank) is the current module.
/// Thus, all the definitions below happen in that module.
void AttentionSCM::init(void)
{
	define_scheme_primitive("cog-av", &AttentionSCM::get_av, this, "attention-bank");
	define_scheme_primitive("cog-set-av!", &AttentionSCM::set_av, this, "attention-bank");
	define_scheme_primitive("cog-inc-vlti!", &AttentionSCM::inc_vlti, this, "attention-bank");
	define_scheme_primitive("cog-dec-vlti!", &AttentionSCM::dec_vlti, this, "attention-bank");

	// Attentional-focus-related stuff
	define_scheme_primitive("cog-update-af", &AttentionSCM::update_af, this, "attention-bank");
	define_scheme_primitive("cog-af-size", &AttentionSCM::af_size, this, "attention-bank");
	define_scheme_primitive("cog-set-af-size!", &AttentionSCM::set_af_size, this, "attention-bank");
	define_scheme_primitive("cog-stimulate", &AttentionSCM::stimulate, this, "attention-bank");

	// Pattern matching with filtering.
	define_scheme_primitive("cog-bind-af", &AttentionSCM::af_bindlink, this, "attention-bank");
}

AttentionSCM::~AttentionSCM()
{
}

AttentionValuePtr AttentionSCM::get_av(const Handle& h)
{
	return opencog::get_av(h);
}

Handle AttentionSCM::set_av(const Handle& h, const AttentionValuePtr& av)
{
	AtomSpace* atomspace = SchemeSmob::ss_get_env_as("cog-set-av!");
	attentionbank(atomspace).change_av(h, av);
	return h;
}

Handle AttentionSCM::inc_vlti(const Handle& h)
{
	AtomSpace* atomspace = SchemeSmob::ss_get_env_as("cog-inc-vlti!");
	attentionbank(atomspace).inc_vlti(h);
	return h;
}

Handle AttentionSCM::dec_vlti(const Handle& h)
{
	AtomSpace* atomspace = SchemeSmob::ss_get_env_as("cog-dec-vlti!");
	attentionbank(atomspace).dec_vlti(h);
	return h;
}

/**
 *   Return AttentionalFocus Size
 **/
int AttentionSCM::af_size(void)
{
	AtomSpace* atomspace = SchemeSmob::ss_get_env_as("cog-af-size");
	return attentionbank(atomspace).get_af_size();
}

/**
 * Set AttentionalFocus Size
 */
int AttentionSCM::set_af_size (int ssize)
{
	AtomSpace* atomspace = SchemeSmob::ss_get_env_as("cog-set-af-size!");

	attentionbank(atomspace).set_af_size(ssize);
	return attentionbank(atomspace).get_af_size();
}

/**
 * Return the list of top n atoms in the AttentionalFocus or
 * return all atoms in the AF if n is unspecified or is larger
 * than the AF size.
 */
Handle AttentionSCM::update_af(int n)
{
	HandleSeq attentionalFocus;

	AtomSpace* atomspace = SchemeSmob::ss_get_env_as("cog-af");

	Handle af_anchor = atomspace->add_node(ANCHOR_NODE, "*-attentional-focus-boundary-*");
	Handle af_key = atomspace->add_node(PREDICATE_NODE, "AttentionalFocus");

	attentionbank(atomspace).get_handle_set_in_attentional_focus(back_inserter(attentionalFocus));
	size_t isz = attentionalFocus.size();

	size_t N = isz;
	if (0 < n) N = n;
	if( N > isz)  N = isz;
	std::reverse(attentionalFocus.begin(), attentionalFocus.end());
	attentionalFocus.resize(N);

	std::vector<ValuePtr> af;
	for (Handle& h : attentionalFocus) {
		af.push_back(h);
	}

	af_anchor->setValue(af_key, createLinkValue(af));

	return af_anchor;
}

/**
 *  Stimulate an atom with given stimulus amount.
 */
Handle AttentionSCM::stimulate (const Handle& h, double stimulus)
{
	AtomSpace* atomspace = SchemeSmob::ss_get_env_as("cog-stimulate");
	attentionbank(atomspace).stimulate(h, stimulus);
	return h;
}

Handle AttentionSCM::af_bindlink(const Handle& h)
{
	AtomSpace* atomspace = SchemeSmob::ss_get_env_as("cog-bind-af");
	return opencog::af_bindlink(atomspace, h);
}

extern "C" {
void opencog_attention_init(void);
};

void opencog_attention_init(void)
{
	static AttentionSCM atty;
}
#endif // HAVE_GUILE
<commit_msg>Misc cleanup<commit_after>/*
 * AttentionSCM.cc
 *
 * Guile Scheme bindings for the attentionbank
 * Copyright (C) 2014 Cosmo Harrigan
 * Copyright (c) 2008, 2014, 2015, 2018 Linas Vepstas <linasvepstas@gmail.com>
 */

#ifdef HAVE_GUILE

#include <opencog/atoms/base/Link.h>
#include <opencog/attentionbank/AttentionBank.h>
#include <opencog/guile/SchemePrimitive.h>

namespace opencog {

class AttentionSCM
{
	protected:
		static void* init_in_guile(void*);
		static void init_in_module(void*);
		void init(void);
	public:
		AttentionSCM(void);
		~AttentionSCM();

		AttentionValuePtr get_av(const Handle&);
		Handle set_av(const Handle&, const ValuePtr&);
		Handle inc_vlti(const Handle&);
		Handle dec_vlti(const Handle&);

		Handle update_af(int);
		int af_size(void);
		int set_af_size(int);
		Handle stimulate(const Handle&, double);

		Handle af_bindlink(const Handle&);
};
}

#include <opencog/atomspace/AtomSpace.h>
#include <opencog/guile/SchemePrimitive.h>
#include <opencog/guile/SchemeSmob.h>
#include <opencog/atoms/value/LinkValue.h>
#include <opencog/attentionbank/AFImplicator.h>

using namespace opencog;

// ========================================================

AttentionSCM::AttentionSCM(void)
{
	static bool is_init = false;
	if (is_init) return;
	is_init = true;
	scm_with_guile(init_in_guile, this);
}

void* AttentionSCM::init_in_guile(void* self)
{
	scm_c_define_module("opencog attention-bank", init_in_module, self);
	scm_c_use_module("opencog attention-bank");
	return NULL;
}

void AttentionSCM::init_in_module(void* data)
{
	AttentionSCM* self = (AttentionSCM*) data;
	self->init();
}

/// This is called while (opencog attention-bank) is the current module.
/// Thus, all the definitions below happen in that module.
void AttentionSCM::init(void)
{
	define_scheme_primitive("cog-av", &AttentionSCM::get_av, this, "attention-bank");
	define_scheme_primitive("cog-set-av!", &AttentionSCM::set_av, this, "attention-bank");
	define_scheme_primitive("cog-inc-vlti!", &AttentionSCM::inc_vlti, this, "attention-bank");
	define_scheme_primitive("cog-dec-vlti!", &AttentionSCM::dec_vlti, this, "attention-bank");

	// Attentional-focus-related stuff
	define_scheme_primitive("cog-update-af", &AttentionSCM::update_af, this, "attention-bank");
	define_scheme_primitive("cog-af-size", &AttentionSCM::af_size, this, "attention-bank");
	define_scheme_primitive("cog-set-af-size!", &AttentionSCM::set_af_size, this, "attention-bank");
	define_scheme_primitive("cog-stimulate", &AttentionSCM::stimulate, this, "attention-bank");

	// Pattern matching with filtering.
	define_scheme_primitive("cog-bind-af", &AttentionSCM::af_bindlink, this, "attention-bank");
}

AttentionSCM::~AttentionSCM()
{
}

AttentionValuePtr AttentionSCM::get_av(const Handle& h)
{
	return opencog::get_av(h);
}

Handle AttentionSCM::set_av(const Handle& h, const ValuePtr& av)
{
	AtomSpace* atomspace = SchemeSmob::ss_get_env_as("cog-set-av!");
	attentionbank(atomspace).change_av(h, AttentionValueCast(av));
	return h;
}

Handle AttentionSCM::inc_vlti(const Handle& h)
{
	AtomSpace* atomspace = SchemeSmob::ss_get_env_as("cog-inc-vlti!");
	attentionbank(atomspace).inc_vlti(h);
	return h;
}

Handle AttentionSCM::dec_vlti(const Handle& h)
{
	AtomSpace* atomspace = SchemeSmob::ss_get_env_as("cog-dec-vlti!");
	attentionbank(atomspace).dec_vlti(h);
	return h;
}

/**
 *   Return AttentionalFocus Size
 **/
int AttentionSCM::af_size(void)
{
	AtomSpace* atomspace = SchemeSmob::ss_get_env_as("cog-af-size");
	return attentionbank(atomspace).get_af_size();
}

/**
 * Set AttentionalFocus Size
 */
int AttentionSCM::set_af_size (int ssize)
{
	AtomSpace* atomspace = SchemeSmob::ss_get_env_as("cog-set-af-size!");

	attentionbank(atomspace).set_af_size(ssize);
	return attentionbank(atomspace).get_af_size();
}

/**
 * Return the list of top n atoms in the AttentionalFocus or
 * return all atoms in the AF if n is unspecified or is larger
 * than the AF size.
 */
Handle AttentionSCM::update_af(int n)
{
	HandleSeq attentionalFocus;

	AtomSpace* atomspace = SchemeSmob::ss_get_env_as("cog-af");

	Handle af_anchor = atomspace->add_node(ANCHOR_NODE, "*-attentional-focus-boundary-*");
	Handle af_key = atomspace->add_node(PREDICATE_NODE, "AttentionalFocus");

	attentionbank(atomspace).get_handle_set_in_attentional_focus(back_inserter(attentionalFocus));
	size_t isz = attentionalFocus.size();

	size_t N = isz;
	if (0 < n) N = n;
	if( N > isz)  N = isz;
	std::reverse(attentionalFocus.begin(), attentionalFocus.end());
	attentionalFocus.resize(N);

	std::vector<ValuePtr> af;
	for (Handle& h : attentionalFocus) {
		af.push_back(h);
	}

	af_anchor->setValue(af_key, createLinkValue(af));

	return af_anchor;
}

/**
 *  Stimulate an atom with given stimulus amount.
 */
Handle AttentionSCM::stimulate (const Handle& h, double stimulus)
{
	AtomSpace* atomspace = SchemeSmob::ss_get_env_as("cog-stimulate");
	attentionbank(atomspace).stimulate(h, stimulus);
	return h;
}

Handle AttentionSCM::af_bindlink(const Handle& h)
{
	AtomSpace* atomspace = SchemeSmob::ss_get_env_as("cog-bind-af");
	return opencog::af_bindlink(atomspace, h);
}

extern "C" {
void opencog_attention_init(void);
};

void opencog_attention_init(void)
{
	static AttentionSCM atty;
}
#endif // HAVE_GUILE
<|endoftext|>


<commit_before>// Test to make sure basic initialization order errors are caught.

// RUN: %clangxx_asan -O0 %s %p/Helpers/initialization-bug-extra2.cc -o %t-INIT-ORDER-EXE
// RUN: env ASAN_OPTIONS=check_initialization_order=true not %run %t-INIT-ORDER-EXE 2>&1 | FileCheck %s

// Do not test with optimization -- the error may be optimized away.

// FIXME: https://code.google.com/p/address-sanitizer/issues/detail?id=186
// XFAIL: darwin

#include <cstdio>

// The structure of the test is:
// "x", "y", "z" are dynamically initialized globals.
// Value of "x" depends on "y", value of "y" depends on "z".
// "x" and "z" are defined in this TU, "y" is defined in another one.
// Thus we shoud stably report initialization order fiasco independently of
// the translation unit order.

int initZ() {
  return 5;
}
int z = initZ();

// 'y' is a dynamically initialized global residing in a different TU.  This
// dynamic initializer will read the value of 'y' before main starts.  The
// result is undefined behavior, which should be caught by initialization order
// checking.
extern int y;
int __attribute__((noinline)) initX() {
  return y + 1;
  // CHECK: {{AddressSanitizer: initialization-order-fiasco}}
  // CHECK: {{READ of size .* at 0x.* thread T0}}
  // CHECK: {{0x.* is located 0 bytes inside of global variable .*(y|z).*}}
  // CHECK: registered at:
  // CHECK: 0x{{.*}} in asan.module_ctor {{.*}}INIT-ORDER-EXE
}

// This initializer begins our initialization order problems.
static int x = initX();

int main() {
  // ASan should have caused an exit before main runs.
  printf("PASS\n");
  // CHECK-NOT: PASS
  return 0;
}
<commit_msg>[ASan] Relax test modified in r235540 to pacify ARM buildbots.<commit_after>// Test to make sure basic initialization order errors are caught.

// RUN: %clangxx_asan -O0 %s %p/Helpers/initialization-bug-extra2.cc -o %t-INIT-ORDER-EXE
// RUN: env ASAN_OPTIONS=check_initialization_order=true not %run %t-INIT-ORDER-EXE 2>&1 | FileCheck %s

// Do not test with optimization -- the error may be optimized away.

// FIXME: https://code.google.com/p/address-sanitizer/issues/detail?id=186
// XFAIL: darwin

#include <cstdio>

// The structure of the test is:
// "x", "y", "z" are dynamically initialized globals.
// Value of "x" depends on "y", value of "y" depends on "z".
// "x" and "z" are defined in this TU, "y" is defined in another one.
// Thus we shoud stably report initialization order fiasco independently of
// the translation unit order.

int initZ() {
  return 5;
}
int z = initZ();

// 'y' is a dynamically initialized global residing in a different TU.  This
// dynamic initializer will read the value of 'y' before main starts.  The
// result is undefined behavior, which should be caught by initialization order
// checking.
extern int y;
int __attribute__((noinline)) initX() {
  return y + 1;
  // CHECK: {{AddressSanitizer: initialization-order-fiasco}}
  // CHECK: {{READ of size .* at 0x.* thread T0}}
  // CHECK: {{0x.* is located 0 bytes inside of global variable .*(y|z).*}}
  // CHECK: registered at:
  // CHECK: 0x{{.*}} in __asan_register_globals
}

// This initializer begins our initialization order problems.
static int x = initX();

int main() {
  // ASan should have caused an exit before main runs.
  printf("PASS\n");
  // CHECK-NOT: PASS
  return 0;
}
<|endoftext|>


<commit_before>//===- Writer.cpp ---------------------------------------------------------===//
//
//                             The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "Config.h"
#include "Writer.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/Endian.h"
#include "llvm/Support/FileOutputBuffer.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm>
#include <cstdio>
#include <functional>
#include <map>
#include <utility>

using namespace llvm;
using namespace llvm::COFF;
using namespace llvm::object;
using namespace llvm::support;
using namespace llvm::support::endian;

static const int PageSize = 4096;
static const int FileAlignment = 512;
static const int SectionAlignment = 4096;
static const int DOSStubSize = 64;
static const int NumberfOfDataDirectory = 16;

namespace lld {
namespace coff {

void OutputSection::setRVA(uint64_t RVA) {
  Header.VirtualAddress = RVA;
  for (Chunk *C : Chunks)
    C->setRVA(C->getRVA() + RVA);
}

void OutputSection::setFileOffset(uint64_t Off) {
  // If a section has no actual data (i.e. BSS section), we want to
  // set 0 to its PointerToRawData. Otherwise the output is rejected
  // by the loader.
  if (Header.SizeOfRawData == 0)
    return;
  Header.PointerToRawData = Off;
  for (Chunk *C : Chunks)
    C->setFileOff(C->getFileOff() + Off);
}

void OutputSection::addChunk(Chunk *C) {
  Chunks.push_back(C);
  uint64_t Off = Header.VirtualSize;
  Off = RoundUpToAlignment(Off, C->getAlign());
  C->setRVA(Off);
  C->setFileOff(Off);
  Off += C->getSize();
  Header.VirtualSize = Off;
  if (C->hasData())
    Header.SizeOfRawData = RoundUpToAlignment(Off, FileAlignment);
}

void OutputSection::addPermissions(uint32_t C) {
  Header.Characteristics = Header.Characteristics | (C & PermMask);
}

// Write the section header to a given buffer.
void OutputSection::writeHeaderTo(uint8_t *Buf) {
  auto *Hdr = reinterpret_cast<coff_section *>(Buf);
  *Hdr = Header;
  if (StringTableOff) {
    // If name is too long, write offset into the string table as a name.
    sprintf(Hdr->Name, "/%d", StringTableOff);
  } else {
    assert(Name.size() <= COFF::NameSize);
    strncpy(Hdr->Name, Name.data(), Name.size());
  }
}

void Writer::markLive() {
  for (StringRef Name : Config->GCRoots)
    cast<Defined>(Symtab->find(Name))->markLive();
  for (Chunk *C : Symtab->getChunks())
    if (C->isRoot())
      C->markLive();
}

void Writer::createSections() {
  std::map<StringRef, std::vector<Chunk *>> Map;
  for (Chunk *C : Symtab->getChunks()) {
    if (!C->isLive()) {
      if (Config->Verbose)
        C->printDiscardedMessage();
      continue;
    }
    // '$' and all following characters in input section names are
    // discarded when determining output section. So, .text$foo
    // contributes to .text, for example. See PE/COFF spec 3.2.
    Map[C->getSectionName().split('$').first].push_back(C);
  }

  // Input sections are ordered by their names including '$' parts,
  // which gives you some control over the output layout.
  auto Comp = [](Chunk *A, Chunk *B) {
    return A->getSectionName() < B->getSectionName();
  };
  for (auto &P : Map) {
    StringRef SectionName = P.first;
    std::vector<Chunk *> &Chunks = P.second;
    std::stable_sort(Chunks.begin(), Chunks.end(), Comp);
    size_t SectIdx = OutputSections.size();
    auto Sec = new (CAlloc.Allocate()) OutputSection(SectionName, SectIdx);
    for (Chunk *C : Chunks) {
      C->setOutputSection(Sec);
      Sec->addChunk(C);
      Sec->addPermissions(C->getPermissions());
    }
    OutputSections.push_back(Sec);
  }
}

// Create .idata section for the DLL-imported symbol table.
// The format of this section is inherently Windows-specific.
// IdataContents class abstracted away the details for us,
// so we just let it create chunks and add them to the section.
void Writer::createImportTables() {
  if (Symtab->ImportFiles.empty())
    return;
  OutputSection *Text = createSection(".text");
  Idata.reset(new IdataContents());
  for (std::unique_ptr<ImportFile> &File : Symtab->ImportFiles) {
    for (SymbolBody *Body : File->getSymbols()) {
      if (auto *Import = dyn_cast<DefinedImportData>(Body)) {
        Idata->add(Import);
        continue;
      }
      // Linker-created function thunks for DLL symbols are added to
      // .text section.
      Text->addChunk(cast<DefinedImportThunk>(Body)->getChunk());
    }
  }
  OutputSection *Sec = createSection(".idata");
  for (Chunk *C : Idata->getChunks())
    Sec->addChunk(C);
}

// The Windows loader doesn't seem to like empty sections,
// so we remove them if any.
void Writer::removeEmptySections() {
  auto IsEmpty = [](OutputSection *S) { return S->getVirtualSize() == 0; };
  OutputSections.erase(
      std::remove_if(OutputSections.begin(), OutputSections.end(), IsEmpty),
      OutputSections.end());
}

// Visits all sections to assign incremental, non-overlapping RVAs and
// file offsets.
void Writer::assignAddresses() {
  SizeOfHeaders = RoundUpToAlignment(
      DOSStubSize + sizeof(PEMagic) + sizeof(coff_file_header) +
      sizeof(pe32plus_header) +
      sizeof(data_directory) * NumberfOfDataDirectory +
      sizeof(coff_section) * OutputSections.size(), PageSize);
  uint64_t RVA = 0x1000; // The first page is kept unmapped.
  uint64_t FileOff = SizeOfHeaders;
  for (OutputSection *Sec : OutputSections) {
    Sec->setRVA(RVA);
    Sec->setFileOffset(FileOff);
    RVA += RoundUpToAlignment(Sec->getVirtualSize(), PageSize);
    FileOff += RoundUpToAlignment(Sec->getRawSize(), FileAlignment);
  }
  SizeOfImage = SizeOfHeaders + RoundUpToAlignment(RVA - 0x1000, PageSize);
  FileSize = SizeOfHeaders +
             RoundUpToAlignment(FileOff - SizeOfHeaders, FileAlignment);
}

static MachineTypes
inferMachineType(std::vector<std::unique_ptr<ObjectFile>> &ObjectFiles) {
  for (std::unique_ptr<ObjectFile> &File : ObjectFiles) {
    // Try to infer machine type from the magic byte of the object file.
    auto MT = static_cast<MachineTypes>(File->getCOFFObj()->getMachine());
    if (MT != IMAGE_FILE_MACHINE_UNKNOWN)
      return MT;
  }
  return IMAGE_FILE_MACHINE_UNKNOWN;
}

void Writer::writeHeader() {
  // Write DOS stub
  uint8_t *Buf = Buffer->getBufferStart();
  auto *DOS = reinterpret_cast<dos_header *>(Buf);
  Buf += DOSStubSize;
  DOS->Magic[0] = 'M';
  DOS->Magic[1] = 'Z';
  DOS->AddressOfRelocationTable = sizeof(dos_header);
  DOS->AddressOfNewExeHeader = DOSStubSize;

  // Write PE magic
  memcpy(Buf, PEMagic, sizeof(PEMagic));
  Buf += sizeof(PEMagic);

  // Determine machine type, infer if needed. TODO: diagnose conflicts.
  MachineTypes MachineType = Config->MachineType;
  if (MachineType == IMAGE_FILE_MACHINE_UNKNOWN)
    MachineType = inferMachineType(Symtab->ObjectFiles);

  // Write COFF header
  auto *COFF = reinterpret_cast<coff_file_header *>(Buf);
  Buf += sizeof(*COFF);
  COFF->Machine = MachineType;
  COFF->NumberOfSections = OutputSections.size();
  COFF->Characteristics =
      (IMAGE_FILE_EXECUTABLE_IMAGE | IMAGE_FILE_RELOCS_STRIPPED |
       IMAGE_FILE_LARGE_ADDRESS_AWARE);
  COFF->SizeOfOptionalHeader =
      sizeof(pe32plus_header) + sizeof(data_directory) * NumberfOfDataDirectory;

  // Write PE header
  auto *PE = reinterpret_cast<pe32plus_header *>(Buf);
  Buf += sizeof(*PE);
  PE->Magic = PE32Header::PE32_PLUS;
  PE->ImageBase = Config->ImageBase;
  PE->SectionAlignment = SectionAlignment;
  PE->FileAlignment = FileAlignment;
  PE->MajorImageVersion = Config->MajorImageVersion;
  PE->MinorImageVersion = Config->MinorImageVersion;
  PE->MajorOperatingSystemVersion = Config->MajorOSVersion;
  PE->MinorOperatingSystemVersion = Config->MinorOSVersion;
  PE->MajorSubsystemVersion = Config->MajorOSVersion;
  PE->MinorSubsystemVersion = Config->MinorOSVersion;
  PE->Subsystem = Config->Subsystem;
  PE->SizeOfImage = SizeOfImage;
  PE->SizeOfHeaders = SizeOfHeaders;
  Defined *Entry = cast<Defined>(Symtab->find(Config->EntryName));
  PE->AddressOfEntryPoint = Entry->getRVA();
  PE->SizeOfStackReserve = Config->StackReserve;
  PE->SizeOfStackCommit = Config->StackCommit;
  PE->SizeOfHeapReserve = Config->HeapReserve;
  PE->SizeOfHeapCommit = Config->HeapCommit;
  PE->NumberOfRvaAndSize = NumberfOfDataDirectory;
  if (OutputSection *Text = findSection(".text")) {
    PE->BaseOfCode = Text->getRVA();
    PE->SizeOfCode = Text->getRawSize();
  }
  PE->SizeOfInitializedData = getSizeOfInitializedData();

  // Write data directory
  auto *DataDirectory = reinterpret_cast<data_directory *>(Buf);
  Buf += sizeof(*DataDirectory) * NumberfOfDataDirectory;
  if (Idata) {
    DataDirectory[IMPORT_TABLE].RelativeVirtualAddress = Idata->getDirRVA();
    DataDirectory[IMPORT_TABLE].Size = Idata->getDirSize();
    DataDirectory[IAT].RelativeVirtualAddress = Idata->getIATRVA();
    DataDirectory[IAT].Size = Idata->getIATSize();
  }

  // Section table
  // Name field in the section table is 8 byte long. Longer names need
  // to be written to the string table. First, construct string table.
  std::vector<char> Strtab;
  for (OutputSection *Sec : OutputSections) {
    StringRef Name = Sec->getName();
    if (Name.size() <= COFF::NameSize)
      continue;
    Sec->setStringTableOff(Strtab.size() + 4); // +4 for the size field
    Strtab.insert(Strtab.end(), Name.begin(), Name.end());
    Strtab.push_back('\0');
  }

  // Write section table
  for (OutputSection *Sec : OutputSections) {
    Sec->writeHeaderTo(Buf);
    Buf += sizeof(coff_section);
  }

  // Write string table if we need to. The string table immediately
  // follows the symbol table, so we create a dummy symbol table
  // first. The symbol table contains one dummy symbol.
  if (Strtab.empty())
    return;
  COFF->PointerToSymbolTable = Buf - Buffer->getBufferStart();
  COFF->NumberOfSymbols = 1;
  auto *SymbolTable = reinterpret_cast<coff_symbol16 *>(Buf);
  Buf += sizeof(*SymbolTable);
  // (Set 4 to make the dummy symbol point to the first string table
  // entry, so that tools to print out symbols don't read NUL bytes.)
  SymbolTable->Name.Offset.Offset = 4;
  // Then create the symbol table. The first 4 bytes is length
  // including itself.
  write32le(Buf, Strtab.size() + 4);
  memcpy(Buf + 4, Strtab.data(), Strtab.size());
}

std::error_code Writer::openFile(StringRef Path) {
  if (auto EC = FileOutputBuffer::create(Path, FileSize, Buffer,
                                         FileOutputBuffer::F_executable)) {
    llvm::errs() << "failed to open " << Path << ": " << EC.message() << "\n";
    return EC;
  }
  return std::error_code();
}

// Write section contents to a mmap'ed file.
void Writer::writeSections() {
  uint8_t *Buf = Buffer->getBufferStart();
  for (OutputSection *Sec : OutputSections) {
    // Fill gaps between functions in .text with INT3 instructions
    // instead of leaving as NUL bytes (which can be interpreted as
    // ADD instructions).
    if (Sec->getPermissions() & IMAGE_SCN_CNT_CODE)
      memset(Buf + Sec->getFileOff(), 0xCC, Sec->getRawSize());
    for (Chunk *C : Sec->getChunks())
      C->writeTo(Buf);
  }
}

OutputSection *Writer::findSection(StringRef Name) {
  for (OutputSection *Sec : OutputSections)
    if (Sec->getName() == Name)
      return Sec;
  return nullptr;
}

uint32_t Writer::getSizeOfInitializedData() {
  uint32_t Res = 0;
  for (OutputSection *S : OutputSections)
    if (S->getPermissions() & IMAGE_SCN_CNT_INITIALIZED_DATA)
      Res += S->getRawSize();
  return Res;
}

// Returns an existing section or create a new one if not found.
OutputSection *Writer::createSection(StringRef Name) {
  if (auto *Sec = findSection(Name))
    return Sec;
  const auto DATA = IMAGE_SCN_CNT_INITIALIZED_DATA;
  const auto BSS = IMAGE_SCN_CNT_UNINITIALIZED_DATA;
  const auto CODE = IMAGE_SCN_CNT_CODE;
  const auto R = IMAGE_SCN_MEM_READ;
  const auto W = IMAGE_SCN_MEM_WRITE;
  const auto E = IMAGE_SCN_MEM_EXECUTE;
  uint32_t Perms = StringSwitch<uint32_t>(Name)
                       .Case(".bss", BSS | R | W)
                       .Case(".data", DATA | R | W)
                       .Case(".didat", DATA | R)
                       .Case(".idata", DATA | R)
                       .Case(".rdata", DATA | R)
                       .Case(".text", CODE | R | E)
                       .Default(0);
  if (!Perms)
    llvm_unreachable("unknown section name");
  size_t SectIdx = OutputSections.size();
  auto Sec = new (CAlloc.Allocate()) OutputSection(Name, SectIdx);
  Sec->addPermissions(Perms);
  OutputSections.push_back(Sec);
  return Sec;
}

std::error_code Writer::write(StringRef OutputPath) {
  markLive();
  createSections();
  createImportTables();
  assignAddresses();
  removeEmptySections();
  if (auto EC = openFile(OutputPath))
    return EC;
  writeHeader();
  writeSections();
  return Buffer->commit();
}

} // namespace coff
} // namespace lld
<commit_msg>COFF: Add comments and move main function to the top. NFC.<commit_after>//===- Writer.cpp ---------------------------------------------------------===//
//
//                             The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "Config.h"
#include "Writer.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/Endian.h"
#include "llvm/Support/FileOutputBuffer.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm>
#include <cstdio>
#include <functional>
#include <map>
#include <utility>

using namespace llvm;
using namespace llvm::COFF;
using namespace llvm::object;
using namespace llvm::support;
using namespace llvm::support::endian;

static const int PageSize = 4096;
static const int FileAlignment = 512;
static const int SectionAlignment = 4096;
static const int DOSStubSize = 64;
static const int NumberfOfDataDirectory = 16;

namespace lld {
namespace coff {

// The main function of the writer.
std::error_code Writer::write(StringRef OutputPath) {
  markLive();
  createSections();
  createImportTables();
  assignAddresses();
  removeEmptySections();
  if (auto EC = openFile(OutputPath))
    return EC;
  writeHeader();
  writeSections();
  return Buffer->commit();
}

void OutputSection::setRVA(uint64_t RVA) {
  Header.VirtualAddress = RVA;
  for (Chunk *C : Chunks)
    C->setRVA(C->getRVA() + RVA);
}

void OutputSection::setFileOffset(uint64_t Off) {
  // If a section has no actual data (i.e. BSS section), we want to
  // set 0 to its PointerToRawData. Otherwise the output is rejected
  // by the loader.
  if (Header.SizeOfRawData == 0)
    return;
  Header.PointerToRawData = Off;
  for (Chunk *C : Chunks)
    C->setFileOff(C->getFileOff() + Off);
}

void OutputSection::addChunk(Chunk *C) {
  Chunks.push_back(C);
  uint64_t Off = Header.VirtualSize;
  Off = RoundUpToAlignment(Off, C->getAlign());
  C->setRVA(Off);
  C->setFileOff(Off);
  Off += C->getSize();
  Header.VirtualSize = Off;
  if (C->hasData())
    Header.SizeOfRawData = RoundUpToAlignment(Off, FileAlignment);
}

void OutputSection::addPermissions(uint32_t C) {
  Header.Characteristics = Header.Characteristics | (C & PermMask);
}

// Write the section header to a given buffer.
void OutputSection::writeHeaderTo(uint8_t *Buf) {
  auto *Hdr = reinterpret_cast<coff_section *>(Buf);
  *Hdr = Header;
  if (StringTableOff) {
    // If name is too long, write offset into the string table as a name.
    sprintf(Hdr->Name, "/%d", StringTableOff);
  } else {
    assert(Name.size() <= COFF::NameSize);
    strncpy(Hdr->Name, Name.data(), Name.size());
  }
}

// Set live bit on for each reachable chunk. Unmarked (unreachable)
// COMDAT chunks will be ignored in the next step, so that they don't
// come to the final output file.
void Writer::markLive() {
  for (StringRef Name : Config->GCRoots)
    cast<Defined>(Symtab->find(Name))->markLive();
  for (Chunk *C : Symtab->getChunks())
    if (C->isRoot())
      C->markLive();
}

// Create output section objects and add them to OutputSections.
void Writer::createSections() {
  // First, bin chunks by name.
  std::map<StringRef, std::vector<Chunk *>> Map;
  for (Chunk *C : Symtab->getChunks()) {
    if (!C->isLive()) {
      if (Config->Verbose)
        C->printDiscardedMessage();
      continue;
    }
    // '$' and all following characters in input section names are
    // discarded when determining output section. So, .text$foo
    // contributes to .text, for example. See PE/COFF spec 3.2.
    Map[C->getSectionName().split('$').first].push_back(C);
  }

  // Then create an OutputSection for each section.
  for (auto &P : Map) {
    StringRef SectionName = P.first;
    std::vector<Chunk *> &Chunks = P.second;
    // Input sections are ordered by their names including '$' parts,
    // which gives you some control over the output layout.
    std::stable_sort(Chunks.begin(), Chunks.end(),
                     [](Chunk *A, Chunk *B) {
                       return A->getSectionName() < B->getSectionName();
                     });
    size_t SectIdx = OutputSections.size();
    auto Sec = new (CAlloc.Allocate()) OutputSection(SectionName, SectIdx);
    for (Chunk *C : Chunks) {
      C->setOutputSection(Sec);
      Sec->addChunk(C);
      Sec->addPermissions(C->getPermissions());
    }
    OutputSections.push_back(Sec);
  }
}

// Create .idata section for the DLL-imported symbol table.
// The format of this section is inherently Windows-specific.
// IdataContents class abstracted away the details for us,
// so we just let it create chunks and add them to the section.
void Writer::createImportTables() {
  if (Symtab->ImportFiles.empty())
    return;
  OutputSection *Text = createSection(".text");
  Idata.reset(new IdataContents());
  for (std::unique_ptr<ImportFile> &File : Symtab->ImportFiles) {
    for (SymbolBody *Body : File->getSymbols()) {
      if (auto *Import = dyn_cast<DefinedImportData>(Body)) {
        Idata->add(Import);
        continue;
      }
      // Linker-created function thunks for DLL symbols are added to
      // .text section.
      Text->addChunk(cast<DefinedImportThunk>(Body)->getChunk());
    }
  }
  OutputSection *Sec = createSection(".idata");
  for (Chunk *C : Idata->getChunks())
    Sec->addChunk(C);
}

// The Windows loader doesn't seem to like empty sections,
// so we remove them if any.
void Writer::removeEmptySections() {
  auto IsEmpty = [](OutputSection *S) { return S->getVirtualSize() == 0; };
  OutputSections.erase(
      std::remove_if(OutputSections.begin(), OutputSections.end(), IsEmpty),
      OutputSections.end());
}

// Visits all sections to assign incremental, non-overlapping RVAs and
// file offsets.
void Writer::assignAddresses() {
  SizeOfHeaders = RoundUpToAlignment(
      DOSStubSize + sizeof(PEMagic) + sizeof(coff_file_header) +
      sizeof(pe32plus_header) +
      sizeof(data_directory) * NumberfOfDataDirectory +
      sizeof(coff_section) * OutputSections.size(), PageSize);
  uint64_t RVA = 0x1000; // The first page is kept unmapped.
  uint64_t FileOff = SizeOfHeaders;
  for (OutputSection *Sec : OutputSections) {
    Sec->setRVA(RVA);
    Sec->setFileOffset(FileOff);
    RVA += RoundUpToAlignment(Sec->getVirtualSize(), PageSize);
    FileOff += RoundUpToAlignment(Sec->getRawSize(), FileAlignment);
  }
  SizeOfImage = SizeOfHeaders + RoundUpToAlignment(RVA - 0x1000, PageSize);
  FileSize = SizeOfHeaders +
             RoundUpToAlignment(FileOff - SizeOfHeaders, FileAlignment);
}

static MachineTypes
inferMachineType(std::vector<std::unique_ptr<ObjectFile>> &ObjectFiles) {
  for (std::unique_ptr<ObjectFile> &File : ObjectFiles) {
    // Try to infer machine type from the magic byte of the object file.
    auto MT = static_cast<MachineTypes>(File->getCOFFObj()->getMachine());
    if (MT != IMAGE_FILE_MACHINE_UNKNOWN)
      return MT;
  }
  return IMAGE_FILE_MACHINE_UNKNOWN;
}

void Writer::writeHeader() {
  // Write DOS stub
  uint8_t *Buf = Buffer->getBufferStart();
  auto *DOS = reinterpret_cast<dos_header *>(Buf);
  Buf += DOSStubSize;
  DOS->Magic[0] = 'M';
  DOS->Magic[1] = 'Z';
  DOS->AddressOfRelocationTable = sizeof(dos_header);
  DOS->AddressOfNewExeHeader = DOSStubSize;

  // Write PE magic
  memcpy(Buf, PEMagic, sizeof(PEMagic));
  Buf += sizeof(PEMagic);

  // Determine machine type, infer if needed. TODO: diagnose conflicts.
  MachineTypes MachineType = Config->MachineType;
  if (MachineType == IMAGE_FILE_MACHINE_UNKNOWN)
    MachineType = inferMachineType(Symtab->ObjectFiles);

  // Write COFF header
  auto *COFF = reinterpret_cast<coff_file_header *>(Buf);
  Buf += sizeof(*COFF);
  COFF->Machine = MachineType;
  COFF->NumberOfSections = OutputSections.size();
  COFF->Characteristics =
      (IMAGE_FILE_EXECUTABLE_IMAGE | IMAGE_FILE_RELOCS_STRIPPED |
       IMAGE_FILE_LARGE_ADDRESS_AWARE);
  COFF->SizeOfOptionalHeader =
      sizeof(pe32plus_header) + sizeof(data_directory) * NumberfOfDataDirectory;

  // Write PE header
  auto *PE = reinterpret_cast<pe32plus_header *>(Buf);
  Buf += sizeof(*PE);
  PE->Magic = PE32Header::PE32_PLUS;
  PE->ImageBase = Config->ImageBase;
  PE->SectionAlignment = SectionAlignment;
  PE->FileAlignment = FileAlignment;
  PE->MajorImageVersion = Config->MajorImageVersion;
  PE->MinorImageVersion = Config->MinorImageVersion;
  PE->MajorOperatingSystemVersion = Config->MajorOSVersion;
  PE->MinorOperatingSystemVersion = Config->MinorOSVersion;
  PE->MajorSubsystemVersion = Config->MajorOSVersion;
  PE->MinorSubsystemVersion = Config->MinorOSVersion;
  PE->Subsystem = Config->Subsystem;
  PE->SizeOfImage = SizeOfImage;
  PE->SizeOfHeaders = SizeOfHeaders;
  Defined *Entry = cast<Defined>(Symtab->find(Config->EntryName));
  PE->AddressOfEntryPoint = Entry->getRVA();
  PE->SizeOfStackReserve = Config->StackReserve;
  PE->SizeOfStackCommit = Config->StackCommit;
  PE->SizeOfHeapReserve = Config->HeapReserve;
  PE->SizeOfHeapCommit = Config->HeapCommit;
  PE->NumberOfRvaAndSize = NumberfOfDataDirectory;
  if (OutputSection *Text = findSection(".text")) {
    PE->BaseOfCode = Text->getRVA();
    PE->SizeOfCode = Text->getRawSize();
  }
  PE->SizeOfInitializedData = getSizeOfInitializedData();

  // Write data directory
  auto *DataDirectory = reinterpret_cast<data_directory *>(Buf);
  Buf += sizeof(*DataDirectory) * NumberfOfDataDirectory;
  if (Idata) {
    DataDirectory[IMPORT_TABLE].RelativeVirtualAddress = Idata->getDirRVA();
    DataDirectory[IMPORT_TABLE].Size = Idata->getDirSize();
    DataDirectory[IAT].RelativeVirtualAddress = Idata->getIATRVA();
    DataDirectory[IAT].Size = Idata->getIATSize();
  }

  // Section table
  // Name field in the section table is 8 byte long. Longer names need
  // to be written to the string table. First, construct string table.
  std::vector<char> Strtab;
  for (OutputSection *Sec : OutputSections) {
    StringRef Name = Sec->getName();
    if (Name.size() <= COFF::NameSize)
      continue;
    Sec->setStringTableOff(Strtab.size() + 4); // +4 for the size field
    Strtab.insert(Strtab.end(), Name.begin(), Name.end());
    Strtab.push_back('\0');
  }

  // Write section table
  for (OutputSection *Sec : OutputSections) {
    Sec->writeHeaderTo(Buf);
    Buf += sizeof(coff_section);
  }

  // Write string table if we need to. The string table immediately
  // follows the symbol table, so we create a dummy symbol table
  // first. The symbol table contains one dummy symbol.
  if (Strtab.empty())
    return;
  COFF->PointerToSymbolTable = Buf - Buffer->getBufferStart();
  COFF->NumberOfSymbols = 1;
  auto *SymbolTable = reinterpret_cast<coff_symbol16 *>(Buf);
  Buf += sizeof(*SymbolTable);
  // (Set 4 to make the dummy symbol point to the first string table
  // entry, so that tools to print out symbols don't read NUL bytes.)
  SymbolTable->Name.Offset.Offset = 4;
  // Then create the symbol table. The first 4 bytes is length
  // including itself.
  write32le(Buf, Strtab.size() + 4);
  memcpy(Buf + 4, Strtab.data(), Strtab.size());
}

std::error_code Writer::openFile(StringRef Path) {
  if (auto EC = FileOutputBuffer::create(Path, FileSize, Buffer,
                                         FileOutputBuffer::F_executable)) {
    llvm::errs() << "failed to open " << Path << ": " << EC.message() << "\n";
    return EC;
  }
  return std::error_code();
}

// Write section contents to a mmap'ed file.
void Writer::writeSections() {
  uint8_t *Buf = Buffer->getBufferStart();
  for (OutputSection *Sec : OutputSections) {
    // Fill gaps between functions in .text with INT3 instructions
    // instead of leaving as NUL bytes (which can be interpreted as
    // ADD instructions).
    if (Sec->getPermissions() & IMAGE_SCN_CNT_CODE)
      memset(Buf + Sec->getFileOff(), 0xCC, Sec->getRawSize());
    for (Chunk *C : Sec->getChunks())
      C->writeTo(Buf);
  }
}

OutputSection *Writer::findSection(StringRef Name) {
  for (OutputSection *Sec : OutputSections)
    if (Sec->getName() == Name)
      return Sec;
  return nullptr;
}

uint32_t Writer::getSizeOfInitializedData() {
  uint32_t Res = 0;
  for (OutputSection *S : OutputSections)
    if (S->getPermissions() & IMAGE_SCN_CNT_INITIALIZED_DATA)
      Res += S->getRawSize();
  return Res;
}

// Returns an existing section or create a new one if not found.
OutputSection *Writer::createSection(StringRef Name) {
  if (auto *Sec = findSection(Name))
    return Sec;
  const auto DATA = IMAGE_SCN_CNT_INITIALIZED_DATA;
  const auto BSS = IMAGE_SCN_CNT_UNINITIALIZED_DATA;
  const auto CODE = IMAGE_SCN_CNT_CODE;
  const auto R = IMAGE_SCN_MEM_READ;
  const auto W = IMAGE_SCN_MEM_WRITE;
  const auto E = IMAGE_SCN_MEM_EXECUTE;
  uint32_t Perms = StringSwitch<uint32_t>(Name)
                       .Case(".bss", BSS | R | W)
                       .Case(".data", DATA | R | W)
                       .Case(".didat", DATA | R)
                       .Case(".idata", DATA | R)
                       .Case(".rdata", DATA | R)
                       .Case(".text", CODE | R | E)
                       .Default(0);
  if (!Perms)
    llvm_unreachable("unknown section name");
  size_t SectIdx = OutputSections.size();
  auto Sec = new (CAlloc.Allocate()) OutputSection(Name, SectIdx);
  Sec->addPermissions(Perms);
  OutputSections.push_back(Sec);
  return Sec;
}

} // namespace coff
} // namespace lld
<|endoftext|>


<commit_before>#include "TcpClient.hpp"
#include <iostream>
#include <iomanip>
#include <QtEndian>
#include "VarInt.hpp"

//----------------------------------------------------------------------------//

namespace NetworkClient {

//----------------------------------------------------------------------------//

TcpClient::TcpClient(QObject *parent) :
    QObject(parent),
    m_socket(),
    m_host(),
    m_port(0),
    m_compressionThreshold(-1)
{
    connect(&m_socket, SIGNAL(connected()), this, SIGNAL(connected()));
    connect(&m_socket, SIGNAL(disconnected()), this, SIGNAL(disconnected()));
    connect(&m_socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SIGNAL(socketError(QAbstractSocket::SocketError)));

    connect(&m_socket, SIGNAL(readyRead()), this, SLOT(onSocketReadyRead()));
}

TcpClient::~TcpClient()
{
}

void TcpClient::connectToHost(const QString & host, const quint16 port)
{
    m_host = host;
    m_port = port;
    m_socket.connectToHost(host, port);
}

void TcpClient::disconnectFromHost()
{
    m_host = QString();
    m_port = 0;
    m_socket.disconnectFromHost();
}

//----------------------------------------------------------------------------//

void TcpClient::writeMessage(QByteArray data)
{
    MessageBuffer sizeBuffer;

    const bool isCompressionEnabled = (m_compressionThreshold > -1);

    if(isCompressionEnabled)
    {
        VarInt uncompressedSize;

        if(data.size() >= m_compressionThreshold)
        {
            uncompressedSize.setValue(data.size());
            data = qCompress(data);
        }
        else
        {
            uncompressedSize.setValue(0);
        }

        // Full packet size; uncompressed data size
        sizeBuffer << VarInt(data.size() + uncompressedSize.getSizeAsArray()) << uncompressedSize;
    }
    else
    {
        sizeBuffer << VarInt(data.size());
    }

    m_socket.write(sizeBuffer.getAllBytes());
    m_socket.write(data);
    m_socket.flush();
}

void TcpClient::setCompressionThreshold(int threshold)
{
    m_compressionThreshold = threshold;
}

//----------------------------------------------------------------------------//

void TcpClient::onSocketReadyRead()
{
    QByteArray data = m_socket.readAll();
    m_incomingMessagesBuffer.writeBytesToBuffer(data);
    handleMessages();
}

//----------------------------------------------------------------------------//

void TcpClient::handleMessages()
{
    while(m_incomingMessagesBuffer.getSize() > 0 && handleNextMessage())
    {
//        std::cout << "Handled message!" << std::endl;
    }

    m_incomingMessagesBuffer.clearToOffset();
}

bool TcpClient::handleNextMessage()
{
    VarInt messageSize;

    // TODO: check for possibility to read VarInt without try/catch.
    try
    {
        m_incomingMessagesBuffer >> messageSize;
    }
    catch(...)
    {
        std::cout << "Caught exception!" << std::endl;
        return false;
    }

    if(m_incomingMessagesBuffer.getSize() < messageSize.getValue())
    {
//        std::cout << "Not enough data!" << std::endl;
        // Not enough data, return offset and wait for more data.
        // TODO: bytes rollback function.
        m_incomingMessagesBuffer.moveOffset(-messageSize.getSizeAsArray());
        return false;
    }

    QByteArray messageBytes;

    const bool isCompressionEnabled = (m_compressionThreshold > -1);
    if(isCompressionEnabled)
    {
        VarInt decompressedSize;
        m_incomingMessagesBuffer >> decompressedSize;

        const bool isCompressed = (decompressedSize.getValue() != 0);
        const int nBytesToRead = messageSize.getValue() - decompressedSize.getSizeAsArray();

        messageBytes = m_incomingMessagesBuffer.readBytesFromBuffer(nBytesToRead);

        if(isCompressed)
        {
            uchar compressedSize[4];
            qToBigEndian(messageBytes.size(), compressedSize);

            messageBytes.prepend(reinterpret_cast<char *>(compressedSize), 4);
            messageBytes = qUncompress(messageBytes);
        }
    }
    else
    {
        messageBytes = m_incomingMessagesBuffer.readBytesFromBuffer(messageSize.getValue());
    }

    emit messageRead(messageBytes);

    return true;
}

//----------------------------------------------------------------------------//

} // namespace NetworkClient

//----------------------------------------------------------------------------//
<commit_msg>Libs/NetworkClient: TcpClient: disconnectFromHost() does not work when socket is in connecting state; worked this around using abort() on socket.<commit_after>#include "TcpClient.hpp"
#include <iostream>
#include <iomanip>
#include <QtEndian>
#include "VarInt.hpp"

//----------------------------------------------------------------------------//

namespace NetworkClient {

//----------------------------------------------------------------------------//

TcpClient::TcpClient(QObject *parent) :
    QObject(parent),
    m_socket(),
    m_host(),
    m_port(0),
    m_compressionThreshold(-1)
{
    connect(&m_socket, SIGNAL(connected()), this, SIGNAL(connected()));
    connect(&m_socket, SIGNAL(disconnected()), this, SIGNAL(disconnected()));
    connect(&m_socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SIGNAL(socketError(QAbstractSocket::SocketError)));

    connect(&m_socket, SIGNAL(readyRead()), this, SLOT(onSocketReadyRead()));
}

TcpClient::~TcpClient()
{
}

void TcpClient::connectToHost(const QString & host, const quint16 port)
{
    m_host = host;
    m_port = port;
    m_socket.connectToHost(host, port);
}

void TcpClient::disconnectFromHost()
{
    m_host = QString();
    m_port = 0;
//    m_socket.disconnectFromHost();

    // TODO: disconnectFromHost() does not disconnect while connecting. Figure out why.
    m_socket.abort();
    emit disconnected();
}

//----------------------------------------------------------------------------//

void TcpClient::writeMessage(QByteArray data)
{
    MessageBuffer sizeBuffer;

    const bool isCompressionEnabled = (m_compressionThreshold > -1);

    if(isCompressionEnabled)
    {
        VarInt uncompressedSize;

        if(data.size() >= m_compressionThreshold)
        {
            uncompressedSize.setValue(data.size());
            data = qCompress(data);
        }
        else
        {
            uncompressedSize.setValue(0);
        }

        // Full packet size; uncompressed data size
        sizeBuffer << VarInt(data.size() + uncompressedSize.getSizeAsArray()) << uncompressedSize;
    }
    else
    {
        sizeBuffer << VarInt(data.size());
    }

    m_socket.write(sizeBuffer.getAllBytes());
    m_socket.write(data);
    m_socket.flush();
}

void TcpClient::setCompressionThreshold(int threshold)
{
    m_compressionThreshold = threshold;
}

//----------------------------------------------------------------------------//

void TcpClient::onSocketReadyRead()
{
    QByteArray data = m_socket.readAll();
    m_incomingMessagesBuffer.writeBytesToBuffer(data);
    handleMessages();
}

//----------------------------------------------------------------------------//

void TcpClient::handleMessages()
{
    while(m_incomingMessagesBuffer.getSize() > 0 && handleNextMessage())
    {
//        std::cout << "Handled message!" << std::endl;
    }

    m_incomingMessagesBuffer.clearToOffset();
}

bool TcpClient::handleNextMessage()
{
    VarInt messageSize;

    // TODO: check for possibility to read VarInt without try/catch.
    try
    {
        m_incomingMessagesBuffer >> messageSize;
    }
    catch(...)
    {
        std::cout << "Caught exception!" << std::endl;
        return false;
    }

    if(m_incomingMessagesBuffer.getSize() < messageSize.getValue())
    {
//        std::cout << "Not enough data!" << std::endl;
        // Not enough data, return offset and wait for more data.
        // TODO: bytes rollback function.
        m_incomingMessagesBuffer.moveOffset(-messageSize.getSizeAsArray());
        return false;
    }

    QByteArray messageBytes;

    const bool isCompressionEnabled = (m_compressionThreshold > -1);
    if(isCompressionEnabled)
    {
        VarInt decompressedSize;
        m_incomingMessagesBuffer >> decompressedSize;

        const bool isCompressed = (decompressedSize.getValue() != 0);
        const int nBytesToRead = messageSize.getValue() - decompressedSize.getSizeAsArray();

        messageBytes = m_incomingMessagesBuffer.readBytesFromBuffer(nBytesToRead);

        if(isCompressed)
        {
            uchar compressedSize[4];
            qToBigEndian(messageBytes.size(), compressedSize);

            messageBytes.prepend(reinterpret_cast<char *>(compressedSize), 4);
            messageBytes = qUncompress(messageBytes);
        }
    }
    else
    {
        messageBytes = m_incomingMessagesBuffer.readBytesFromBuffer(messageSize.getValue());
    }

    emit messageRead(messageBytes);

    return true;
}

//----------------------------------------------------------------------------//

} // namespace NetworkClient

//----------------------------------------------------------------------------//
<|endoftext|>