<commit_before>#include <gtest/gtest.h>
#include <string>
#include <map>
#include <cassert>
#include <iostream> // TODO: remove me!
#include "../../cvmfs/prng.h"

#include "../../cvmfs/catalog_traversal.h"
#include "../../cvmfs/manifest.h"
#include "../../cvmfs/hash.h"

using namespace swissknife;

static const std::string rhs       = "f9d87ae2cc46be52b324335ff05fae4c1a7c4dd4";
static const shash::Any  root_hash = shash::Any(shash::kSha1, shash::HexPtr(rhs));

/**
 * This is a mock of an ObjectFetcher that does essentially nothing.
 */
class MockObjectFetcher {
 public:
  MockObjectFetcher(const CatalogTraversalParams &params) {}
 public:
  manifest::Manifest* FetchManifest() {
    return new manifest::Manifest(root_hash, 0, "");
  }
  inline bool Fetch(const shash::Any  &catalog_hash,
                    std::string       *catalog_file) {
    return true;
  }
  inline bool Exists(const std::string &file) {
    return false;
  }
};

/**
 * This is a minimal mock of a Catalog class.
 */
class MockCatalog {
 public:
  typedef std::map<shash::Any, MockCatalog*> AvailableCatalogs;
  static AvailableCatalogs available_catalogs;

  static void RegisterCatalog(MockCatalog *catalog) {
    ASSERT_EQ (MockCatalog::available_catalogs.end(),
               MockCatalog::available_catalogs.find(catalog->catalog_hash()));
    MockCatalog::available_catalogs[catalog->catalog_hash()] = catalog;
  }

  static void UnregisterCatalogs() {
    MockCatalog::AvailableCatalogs::const_iterator i, iend;
    for (i    = MockCatalog::available_catalogs.begin(),
         iend = MockCatalog::available_catalogs.end();
         i != iend; ++i)
    {
      delete i->second;
    }
    MockCatalog::available_catalogs.clear();
  }

  static MockCatalog* GetCatalog(const shash::Any &catalog_hash) {
    AvailableCatalogs::const_iterator clg_itr =
      MockCatalog::available_catalogs.find(catalog_hash);
    return (MockCatalog::available_catalogs.end() != clg_itr)
      ? clg_itr->second
      : NULL;
  }

 public:
  struct NestedCatalog {
    PathString   path;
    shash::Any   hash;
    MockCatalog *child;
    uint64_t     size;
  };
  typedef std::vector<NestedCatalog> NestedCatalogList;

 public:
  MockCatalog(const std::string &root_path,
              const shash::Any  &catalog_hash,
              const uint64_t     catalog_size,
              const unsigned int revision,
              MockCatalog *parent   = NULL,
              MockCatalog *previous = NULL) :
    parent_(parent), previous_(previous), root_path_(root_path),
    catalog_hash_(catalog_hash), catalog_size_(catalog_size),
    revision_(revision)
  {
    if (parent != NULL) {
      parent->RegisterChild(this);
    }
  }

 public: /* API in this 'public block' is used by CatalogTraversal
          * (see catalog.h - catalog::Catalog for details)
          */
  static MockCatalog* AttachFreely(const std::string  &root_path,
                                   const std::string  &file,
                                   const shash::Any   &catalog_hash,
                                         MockCatalog  *parent = NULL) {
    const MockCatalog *catalog = MockCatalog::GetCatalog(catalog_hash);
    if (catalog == NULL) {
      return NULL;
    } else {
      return catalog->Clone();
    }
  }

  bool IsRoot() const {
    return (parent_ == NULL);
  }

  NestedCatalogList *ListNestedCatalogs() const {
    return const_cast<NestedCatalogList*>(&children_);
  }

  shash::Any GetPreviousRevision() const {
    return (previous_ != NULL) ? previous_->catalog_hash() : shash::Any();
  }

 public:
  const std::string& root_path()    const { return root_path_;    }
  const shash::Any&  catalog_hash() const { return catalog_hash_; }
  uint64_t           catalog_size() const { return catalog_size_; }
  unsigned int       revision()     const { return revision_;     }

  MockCatalog*       parent()       const { return parent_;       }
  MockCatalog*       previous()     const { return previous_;     }

 public:
  void RegisterChild(MockCatalog *child) {
    NestedCatalog nested;
    nested.path  = PathString(child->root_path());
    nested.hash  = child->catalog_hash();
    nested.child = child;
    nested.size  = child->catalog_size();
    children_.push_back(nested);
  }

 protected:
  MockCatalog* Clone() const {
    MockCatalog *new_catalog = new MockCatalog(root_path_, catalog_hash_,
                                               catalog_size_, revision_,
                                               parent_, previous_);
    new_catalog->children_ = children_;
    return new_catalog;
  }

 private:
  MockCatalog        *parent_;
  MockCatalog        *previous_;
  const std::string   root_path_;
  const shash::Any    catalog_hash_;
  const uint64_t      catalog_size_;
  const unsigned int  revision_;

  NestedCatalogList   children_;
};

MockCatalog::AvailableCatalogs MockCatalog::available_catalogs;

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

typedef CatalogTraversal<MockCatalog, MockObjectFetcher> MockedCatalogTraversal;

class T_CatalogTraversal : public ::testing::Test {
 public:
  MockCatalog *dummy_catalog_hierarchy;

 protected:
  void SetUp() {
    SetupDummyCatalogs();
  }

  void TearDown() {
    MockCatalog::UnregisterCatalogs();
  }

  void SetupDummyCatalogs() {
    /**
     * Dummy catalog hierarchy:
     *
     *  0-0 HEAD
     *   |
     *   +-------------------+---------------+---------------+
     *   |                   |               |               |
     *  1-0                 1-1             1-2             1-3
     *   |                   |               |               |
     *   +-------------+     +----+----+     +----+----+     +----+
     *   |             |     |    |    |     |    |    |     |    |
     *  2-0           2-1   2-2  2-3  2-4   2-5  2-6  2-7   2-8  2-9
     *   |                   |                    |
     *   +----+----+         +-----+              +-----+-----+-----+
     *   |    |    |         |     |              |     |     |     |
     *  3-0  3-1  3-2       3-3   3-4            3-5   3-6   3-7   3-8
     *   |                         |
     *   |                         +-----+-----+
     *   |                         |     |     |
     *  4-0                       4-1   4-2   4-3
     *
     * Parts of the hierarchy are created multiple times in order to get some
     * historic catalogs. To simplify the creation the history looks like so:
     *    Revision 1:   - only the root catalog (0-0)
     *    Revision 2:   - adds branch 1-0
     *    Revision 3:   - adds branch 1-1
     *    Revision 4:   - adds branch 1-2 and branch 1-1 is recreated
     *    Revision 5:   - adds branch 1-3
     *
     */
    MockCatalog *head = CreateAndRegisterCatalog("", root_hash, 42);

    Make_10_Branch(head);
    Make_11_Branch(head);
    Make_12_Branch(head);
    Make_13_Branch(head);
  }

  void Make_10_Branch(MockCatalog *parent) {
    MockCatalog *_10 = CreateAndRegisterCatalog("/00/10",          GetRandomHash(), 13124, parent);
    MockCatalog *_20 = CreateAndRegisterCatalog("/00/10/20",       GetRandomHash(), 23524, _10);
    MockCatalog *_21 = CreateAndRegisterCatalog("/00/10/21",       GetRandomHash(), 74546, _10);
    MockCatalog *_30 = CreateAndRegisterCatalog("/00/10/20/30",    GetRandomHash(), 66234, _20);
    MockCatalog *_31 = CreateAndRegisterCatalog("/00/10/20/31",    GetRandomHash(), 87365, _20);
    MockCatalog *_32 = CreateAndRegisterCatalog("/00/10/20/32",    GetRandomHash(), 93405, _20);
    MockCatalog *_40 = CreateAndRegisterCatalog("/00/10/20/30/40", GetRandomHash(), 85617, _30);
  }

  void Make_11_Branch(MockCatalog *parent) {
    MockCatalog *_11 = CreateAndRegisterCatalog("/00/11",          GetRandomHash(), 87648, parent);
    MockCatalog *_22 = CreateAndRegisterCatalog("/00/11/22",       GetRandomHash(), 86546, _11);
    MockCatalog *_23 = CreateAndRegisterCatalog("/00/11/23",       GetRandomHash(), 98565, _11);
    MockCatalog *_24 = CreateAndRegisterCatalog("/00/11/24",       GetRandomHash(), 45271, _11);
    MockCatalog *_33 = CreateAndRegisterCatalog("/00/11/22/33",    GetRandomHash(), 17412, _22);
    MockCatalog *_34 = CreateAndRegisterCatalog("/00/11/22/34",    GetRandomHash(), 89127, _22);
    MockCatalog *_41 = CreateAndRegisterCatalog("/00/11/22/34/41", GetRandomHash(), 10987, _34);
    MockCatalog *_42 = CreateAndRegisterCatalog("/00/11/22/34/42", GetRandomHash(), 40987, _34);
    MockCatalog *_43 = CreateAndRegisterCatalog("/00/11/22/34/43", GetRandomHash(), 12234, _34);
  }

  void Make_12_Branch(MockCatalog *parent) {
    MockCatalog *_12 = CreateAndRegisterCatalog("/00/12",       GetRandomHash(), 39272, parent);
    MockCatalog *_25 = CreateAndRegisterCatalog("/00/12/25",    GetRandomHash(), 91999, _12);
    MockCatalog *_26 = CreateAndRegisterCatalog("/00/12/26",    GetRandomHash(), 11111, _12);
    MockCatalog *_27 = CreateAndRegisterCatalog("/00/12/27",    GetRandomHash(), 12344, _12);
    MockCatalog *_35 = CreateAndRegisterCatalog("/00/12/26/35", GetRandomHash(), 99992, _26);
    MockCatalog *_36 = CreateAndRegisterCatalog("/00/12/26/36", GetRandomHash(), 12333, _26);
    MockCatalog *_37 = CreateAndRegisterCatalog("/00/12/26/37", GetRandomHash(), 23442, _26);
    MockCatalog *_38 = CreateAndRegisterCatalog("/00/12/26/38", GetRandomHash(), 14112, _26);
  }

  void Make_13_Branch(MockCatalog *parent) {
    MockCatalog *_13 = CreateAndRegisterCatalog("/00/13",    GetRandomHash(), 14120, parent);
    MockCatalog *_28 = CreateAndRegisterCatalog("/00/13/28", GetRandomHash(), 92370, _13);
    MockCatalog *_29 = CreateAndRegisterCatalog("/00/13/29", GetRandomHash(), 14122, _13);
  }

 private:
  MockCatalog* CreateAndRegisterCatalog(const std::string &root_path,
                                        const shash::Any  &catalog_hash,
                                        const uint64_t     catalog_size,
                                        MockCatalog       *parent   = NULL,
                                        MockCatalog       *previous = NULL) {
    MockCatalog *catalog = new MockCatalog(root_path, catalog_hash, catalog_size,
                                           parent, previous);
    MockCatalog::RegisterCatalog(catalog);
    return catalog;
  }

  shash::Any GetRandomHash() const {
    shash::Any hash(shash::kSha1);
    hash.Randomize();
    return hash;
  }
};

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

TEST_F(T_CatalogTraversal, Initialize) {
  CatalogTraversalParams params;
  MockedCatalogTraversal traverse(params);
}

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

  CatalogTraversal<MockCatalog, MockObjectFetcher> traverse(params);
  traverse.Traverse();
}
<commit_msg>build a full test catalog hierarchy<commit_after>#include <gtest/gtest.h>
#include <string>
#include <map>
#include <cassert>
#include <iostream> // TODO: remove me!
#include "../../cvmfs/prng.h"

#include "../../cvmfs/catalog_traversal.h"
#include "../../cvmfs/manifest.h"
#include "../../cvmfs/hash.h"

using namespace swissknife;

static const std::string rhs       = "f9d87ae2cc46be52b324335ff05fae4c1a7c4dd4";
static const shash::Any  root_hash = shash::Any(shash::kSha1, shash::HexPtr(rhs));

/**
 * This is a mock of an ObjectFetcher that does essentially nothing.
 */
class MockObjectFetcher {
 public:
  MockObjectFetcher(const CatalogTraversalParams &params) {}
 public:
  manifest::Manifest* FetchManifest() {
    return new manifest::Manifest(root_hash, 0, "");
  }
  inline bool Fetch(const shash::Any  &catalog_hash,
                    std::string       *catalog_file) {
    return true;
  }
  inline bool Exists(const std::string &file) {
    return false;
  }
};

/**
 * This is a minimal mock of a Catalog class.
 */
class MockCatalog {
 public:
  typedef std::map<shash::Any, MockCatalog*> AvailableCatalogs;
  static AvailableCatalogs available_catalogs;

  static void RegisterCatalog(MockCatalog *catalog) {
    ASSERT_EQ (MockCatalog::available_catalogs.end(),
               MockCatalog::available_catalogs.find(catalog->catalog_hash()));
    MockCatalog::available_catalogs[catalog->catalog_hash()] = catalog;
  }

  static void UnregisterCatalogs() {
    MockCatalog::AvailableCatalogs::const_iterator i, iend;
    for (i    = MockCatalog::available_catalogs.begin(),
         iend = MockCatalog::available_catalogs.end();
         i != iend; ++i)
    {
      delete i->second;
    }
    MockCatalog::available_catalogs.clear();
  }

  static MockCatalog* GetCatalog(const shash::Any &catalog_hash) {
    AvailableCatalogs::const_iterator clg_itr =
      MockCatalog::available_catalogs.find(catalog_hash);
    return (MockCatalog::available_catalogs.end() != clg_itr)
      ? clg_itr->second
      : NULL;
  }

 public:
  struct NestedCatalog {
    PathString   path;
    shash::Any   hash;
    MockCatalog *child;
    uint64_t     size;
  };
  typedef std::vector<NestedCatalog> NestedCatalogList;

 public:
  MockCatalog(const std::string &root_path,
              const shash::Any  &catalog_hash,
              const uint64_t     catalog_size,
              const unsigned int revision,
              MockCatalog *parent   = NULL,
              MockCatalog *previous = NULL) :
    parent_(parent), previous_(previous), root_path_(root_path),
    catalog_hash_(catalog_hash), catalog_size_(catalog_size),
    revision_(revision)
  {
    if (parent != NULL) {
      parent->RegisterChild(this);
    }
  }

 public: /* API in this 'public block' is used by CatalogTraversal
          * (see catalog.h - catalog::Catalog for details)
          */
  static MockCatalog* AttachFreely(const std::string  &root_path,
                                   const std::string  &file,
                                   const shash::Any   &catalog_hash,
                                         MockCatalog  *parent = NULL) {
    const MockCatalog *catalog = MockCatalog::GetCatalog(catalog_hash);
    if (catalog == NULL) {
      return NULL;
    } else {
      return catalog->Clone();
    }
  }

  bool IsRoot() const {
    return (parent_ == NULL);
  }

  NestedCatalogList *ListNestedCatalogs() const {
    return const_cast<NestedCatalogList*>(&children_);
  }

  shash::Any GetPreviousRevision() const {
    return (previous_ != NULL) ? previous_->catalog_hash() : shash::Any();
  }

 public:
  const std::string& root_path()    const { return root_path_;    }
  const shash::Any&  catalog_hash() const { return catalog_hash_; }
  uint64_t           catalog_size() const { return catalog_size_; }
  unsigned int       revision()     const { return revision_;     }

  MockCatalog*       parent()       const { return parent_;       }
  MockCatalog*       previous()     const { return previous_;     }

 public:
  void RegisterChild(MockCatalog *child) {
    NestedCatalog nested;
    nested.path  = PathString(child->root_path());
    nested.hash  = child->catalog_hash();
    nested.child = child;
    nested.size  = child->catalog_size();
    children_.push_back(nested);
  }

 protected:
  MockCatalog* Clone() const {
    MockCatalog *new_catalog = new MockCatalog(root_path_, catalog_hash_,
                                               catalog_size_, revision_,
                                               parent_, previous_);
    new_catalog->children_ = children_;
    return new_catalog;
  }

 private:
  MockCatalog        *parent_;
  MockCatalog        *previous_;
  const std::string   root_path_;
  const shash::Any    catalog_hash_;
  const uint64_t      catalog_size_;
  const unsigned int  revision_;

  NestedCatalogList   children_;
};

MockCatalog::AvailableCatalogs MockCatalog::available_catalogs;

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

typedef CatalogTraversal<MockCatalog, MockObjectFetcher> MockedCatalogTraversal;

class T_CatalogTraversal : public ::testing::Test {
 public:
  MockCatalog *dummy_catalog_hierarchy;

 private:
  typedef std::map<std::string, MockCatalog*>    CatalogPathMap;
  typedef std::map<unsigned int, CatalogPathMap> RevisionMap;

  const unsigned int max_revision;

 public:
  T_CatalogTraversal() : max_revision(6) {}

 protected:
  void SetUp() {
    SetupDummyCatalogs();
  }

  void TearDown() {
    MockCatalog::UnregisterCatalogs();
  }

 private:
  CatalogPathMap& GetCatalogTree(const unsigned int  revision,
                                 RevisionMap        &revisions) const {
    RevisionMap::iterator rev_itr = revisions.find(revision);
    assert (rev_itr != revisions.end());
    return rev_itr->second;
  }

  MockCatalog* GetRevisionHead(const unsigned int revision,
                               RevisionMap &revisions) const {
    CatalogPathMap &catalogs = GetCatalogTree(revision, revisions);
    CatalogPathMap::iterator catalogs_itr = catalogs.find("");
    assert (catalogs_itr != catalogs.end());
    assert (catalogs_itr->second->revision() == revision);
    assert (catalogs_itr->second->IsRoot());
    return catalogs_itr->second;
  }

  MockCatalog* GetBranchHead(const std::string   &root_path,
                             const unsigned int   revision,
                             RevisionMap         &revisions) const {
    CatalogPathMap &catalogs = GetCatalogTree(revision, revisions);
    CatalogPathMap::iterator catalogs_itr = catalogs.find(root_path);
    assert (catalogs_itr != catalogs.end());
    assert (catalogs_itr->second->revision()  == revision);
    assert (catalogs_itr->second->root_path() == root_path);
    return catalogs_itr->second;
  }

  void SetupDummyCatalogs() {
    /**
     * Dummy catalog hierarchy:
     *
     *  0-0 HEAD
     *   |
     *   +-------------------+---------------+---------------+
     *   |                   |               |               |
     *  1-0                 1-1             1-2             1-3
     *   |                   |               |               |
     *   +-------------+     +----+----+     +----+----+     +----+
     *   |             |     |    |    |     |    |    |     |    |
     *  2-0           2-1   2-2  2-3  2-4   2-5  2-6  2-7   2-8  2-9
     *   |                   |                    |
     *   +----+----+         +-----+              +-----+-----+-----+
     *   |    |    |         |     |              |     |     |     |
     *  3-0  3-1  3-2       3-3   3-4            3-5   3-6   3-7   3-8
     *   |                         |
     *   |                         +-----+-----+
     *   |                         |     |     |
     *  4-0                       4-1   4-2   4-3
     *
     * Parts of the hierarchy are created multiple times in order to get some
     * historic catalogs. To simplify the creation the history looks like so:
     *    Revision 1:   - only the root catalog (0-0)
     *    Revision 2:   - adds branch 1-0
     *    Revision 3:   - adds branch 1-1
     *    Revision 4:   - adds branch 1-2 and branch 1-1 is recreated
     *    Revision 5:   - adds branch 1-3
     *    Revision 6:   - removes branch 1-0
     *
     */

    RevisionMap revisions;
    for (unsigned int r = 1; r <= max_revision; ++r) {
      MakeRevision(r, revisions);
    }
  }

  void MakeRevision(const unsigned int revision, RevisionMap &revisions) {
    // sanity checks
    RevisionMap::const_iterator rev_itr = revisions.find(revision);
    ASSERT_EQ (revisions.end(), rev_itr);
    ASSERT_LE (1u, revision);
    ASSERT_GE (max_revision, revision);

    // create map for new catalog tree
    revisions[revision] = CatalogPathMap();

    // create the root catalog
    MockCatalog *root_catalog = (revision < max_revision)
      ? CreateAndRegisterCatalog("", revision, revisions)
      : CreateAndRegisterCatalog("", revision, revisions, NULL, root_hash);

    // create the catalog hierarchy depending on the revision
    switch (revision) {
      case 1:
        // NOOP
        break;
      case 2:
        MakeBranch("/00/10", revision, revisions);
        break;
      case 3:
        MakeBranch("/00/11", revision, revisions);
        root_catalog->RegisterChild(GetBranchHead("/00/10", 2, revisions));
        break;
      case 4:
        MakeBranch("/00/12", revision, revisions);
        MakeBranch("/00/11", revision, revisions);
        root_catalog->RegisterChild(GetBranchHead("/00/10", 2, revisions));
        break;
      case 5:
        MakeBranch("/00/13", revision, revisions);
        root_catalog->RegisterChild(GetBranchHead("/00/10", 2, revisions));
        root_catalog->RegisterChild(GetBranchHead("/00/11", 4, revisions));
        root_catalog->RegisterChild(GetBranchHead("/00/12", 4, revisions));
        break;
      case 6:
        root_catalog->RegisterChild(GetBranchHead("/00/11", 4, revisions));
        root_catalog->RegisterChild(GetBranchHead("/00/12", 4, revisions));
        root_catalog->RegisterChild(GetBranchHead("/00/13", 5, revisions));
        dummy_catalog_hierarchy = root_catalog; // sets current repo HEAD
        break;
      default:
        FAIL() << "hit revision: " << revision;
    }
  }

  void MakeBranch(const std::string &branch, const unsigned int revision, RevisionMap &revisions) {
    MockCatalog *revision_root = GetRevisionHead(revision, revisions);

    if (branch == "/00/10") {
      MockCatalog *_10 = CreateAndRegisterCatalog("/00/10",          revision, revisions, revision_root);
      MockCatalog *_20 = CreateAndRegisterCatalog("/00/10/20",       revision, revisions,           _10);
                         CreateAndRegisterCatalog("/00/10/21",       revision, revisions,           _10);
      MockCatalog *_30 = CreateAndRegisterCatalog("/00/10/20/30",    revision, revisions,           _20);
                         CreateAndRegisterCatalog("/00/10/20/31",    revision, revisions,           _20);
                         CreateAndRegisterCatalog("/00/10/20/32",    revision, revisions,           _20);
                         CreateAndRegisterCatalog("/00/10/20/30/40", revision, revisions,           _30);

    } else if (branch == "/00/11") {
      MockCatalog *_11 = CreateAndRegisterCatalog("/00/11",          revision, revisions, revision_root);
      MockCatalog *_22 = CreateAndRegisterCatalog("/00/11/22",       revision, revisions,           _11);
                         CreateAndRegisterCatalog("/00/11/23",       revision, revisions,           _11);
                         CreateAndRegisterCatalog("/00/11/24",       revision, revisions,           _11);
                         CreateAndRegisterCatalog("/00/11/22/33",    revision, revisions,           _22);
      MockCatalog *_34 = CreateAndRegisterCatalog("/00/11/22/34",    revision, revisions,           _22);
                         CreateAndRegisterCatalog("/00/11/22/34/41", revision, revisions,           _34);
                         CreateAndRegisterCatalog("/00/11/22/34/42", revision, revisions,           _34);
                         CreateAndRegisterCatalog("/00/11/22/34/43", revision, revisions,           _34);

    } else if (branch == "/00/12") {
      MockCatalog *_12 = CreateAndRegisterCatalog("/00/12",          revision, revisions, revision_root);
                         CreateAndRegisterCatalog("/00/12/25",       revision, revisions,           _12);
      MockCatalog *_26 = CreateAndRegisterCatalog("/00/12/26",       revision, revisions,           _12);
                         CreateAndRegisterCatalog("/00/12/27",       revision, revisions,           _12);
                         CreateAndRegisterCatalog("/00/12/26/35",    revision, revisions,           _26);
                         CreateAndRegisterCatalog("/00/12/26/36",    revision, revisions,           _26);
                         CreateAndRegisterCatalog("/00/12/26/37",    revision, revisions,           _26);
                         CreateAndRegisterCatalog("/00/12/26/38",    revision, revisions,           _26);

    } else if (branch == "/00/13") {
      MockCatalog *_13 = CreateAndRegisterCatalog("/00/13",          revision, revisions, revision_root);
                         CreateAndRegisterCatalog("/00/13/28",       revision, revisions,          _13);
                         CreateAndRegisterCatalog("/00/13/29",       revision, revisions,          _13);

    } else {
      FAIL();
    }
  }

  MockCatalog* CreateAndRegisterCatalog(
                  const std::string  &root_path,
                  const unsigned int  revision,
                  RevisionMap        &revisions,
                  MockCatalog        *parent       = NULL,
                  const shash::Any   &catalog_hash = shash::Any(shash::kSha1)) {
    // produce a random hash if no catalog has was given
    shash::Any effective_clg_hash = catalog_hash;
    if (effective_clg_hash.IsNull()) {
      effective_clg_hash.Randomize();
    }

    // get catalog tree for current revision
    CatalogPathMap &catalogs = GetCatalogTree(revision, revisions);

    // find previous catalog from the RevisionsMaps (if there is one)
    MockCatalog *previous_catalog = NULL;
    if (revision > 1) {
      RevisionMap::iterator prev_rev_itr = revisions.find(revision - 1);
      assert (prev_rev_itr != revisions.end());
      CatalogPathMap::iterator prev_clg_itr =
        prev_rev_itr->second.find(root_path);
      if (prev_clg_itr != prev_rev_itr->second.end()) {
        previous_catalog = prev_clg_itr->second;
      }
    }

    // produce the new catalog with references to it's predecessor and parent
    MockCatalog *catalog = new MockCatalog(root_path,
                                           effective_clg_hash,
                                           dice_.Next(10000),
                                           revision,
                                           parent,
                                           previous_catalog);

    // register the new catalog in the data structures
    MockCatalog::RegisterCatalog(catalog);
    catalogs[root_path] = catalog;
    return catalog;
  }

 private:
  Prng dice_;
};

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

TEST_F(T_CatalogTraversal, Initialize) {
  CatalogTraversalParams params;
  MockedCatalogTraversal traverse(params);
}

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

  CatalogTraversal<MockCatalog, MockObjectFetcher> traverse(params);
  traverse.Traverse();
}
<|endoftext|>


<commit_before>//
// Urho3D Engine
// Copyright (c) 2008-2012 Lasse Oorni
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//

#include "Precompiled.h"
#include "Tangent.h"
#include "Vector4.h"

namespace Urho3D
{

inline unsigned GetIndex(void*& indexPointer, unsigned indexSize)
{
    if (indexSize == sizeof(unsigned short))
    {
        unsigned short* p = (unsigned short*)indexPointer;
        return *p++;
    }
    else
    {
        unsigned* p = (unsigned*)indexPointer;
        return *p++;
    }
}

void GenerateTangents(void* vertexData, unsigned vertexSize, const void* indexData, unsigned indexSize, unsigned indexStart,
    unsigned indexCount, unsigned normalOffset, unsigned texCoordOffset, unsigned tangentOffset)
{
    // Tangent generation from
    // http://www.terathon.com/code/tangent.html
    unsigned minVertex = M_MAX_UNSIGNED;
    unsigned maxVertex = 0;
    unsigned char* vertices = (unsigned char*)vertexData;

    void* indexPointer = const_cast<void*>(indexData);
    for (unsigned i = indexStart; i < indexStart + indexCount; ++i)
    {
        unsigned v = GetIndex(indexPointer, indexSize);
        if (v < minVertex)
            minVertex = v;
        if (v > maxVertex)
            maxVertex = v;
    }

    unsigned vertexCount = maxVertex + 1;
    Vector3 *tan1 = new Vector3[vertexCount * 2];
    Vector3 *tan2 = tan1 + vertexCount;
    memset(tan1, 0, sizeof(Vector3) * vertexCount * 2);

    indexPointer = const_cast<void*>(indexData);
    for (unsigned i = indexStart; i < indexStart + indexCount; i += 3)
    {
        unsigned i1 = GetIndex(indexPointer, indexSize);
        unsigned i2 = GetIndex(indexPointer, indexSize);
        unsigned i3 = GetIndex(indexPointer, indexSize);

        const Vector3& v1 = *((Vector3*)(vertices + i1 * vertexSize));
        const Vector3& v2 = *((Vector3*)(vertices + i2 * vertexSize));
        const Vector3& v3 = *((Vector3*)(vertices + i3 * vertexSize));

        const Vector2& w1 = *((Vector2*)(vertices + i1 * vertexSize + texCoordOffset));
        const Vector2& w2 = *((Vector2*)(vertices + i2 * vertexSize + texCoordOffset));
        const Vector2& w3 = *((Vector2*)(vertices + i3 * vertexSize + texCoordOffset));

        float x1 = v2.x_ - v1.x_;
        float x2 = v3.x_ - v1.x_;
        float y1 = v2.y_ - v1.y_;
        float y2 = v3.y_ - v1.y_;
        float z1 = v2.z_ - v1.z_;
        float z2 = v3.z_ - v1.z_;

        float s1 = w2.x_ - w1.x_;
        float s2 = w3.x_ - w1.x_;
        float t1 = w2.y_ - w1.y_;
        float t2 = w3.y_ - w1.y_;

        float r = 1.0f / (s1 * t2 - s2 * t1);
        Vector3 sdir((t2 * x1 - t1 * x2) * r, (t2 * y1 - t1 * y2) * r,
                (t2 * z1 - t1 * z2) * r);
        Vector3 tdir((s1 * x2 - s2 * x1) * r, (s1 * y2 - s2 * y1) * r,
                (s1 * z2 - s2 * z1) * r);

        tan1[i1] += sdir;
        tan1[i2] += sdir;
        tan1[i3] += sdir;

        tan2[i1] += tdir;
        tan2[i2] += tdir;
        tan2[i3] += tdir;
    }

    for (unsigned i = minVertex; i <= maxVertex; i++)
    {
        const Vector3& n = *((Vector3*)(vertices + i * vertexSize + normalOffset));
        const Vector3& t = tan1[i];
        Vector3 xyz;
        float w;

        // Gram-Schmidt orthogonalize
        xyz = (t - n * n.DotProduct(t)).Normalized();

        // Calculate handedness
        w = n.CrossProduct(t).DotProduct(tan2[i]) < 0.0f ? -1.0f : 1.0f;

        Vector4& tangent = *((Vector4*)(vertices + i * vertexSize + tangentOffset));
        tangent = Vector4(xyz, w);
    }

    delete[] tan1;
}

}
<commit_msg>Applied tangent generation patch from Magic.Lixin.<commit_after>//
// Urho3D Engine
// Copyright (c) 2008-2012 Lasse Oorni
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//

#include "Precompiled.h"
#include "Tangent.h"
#include "Vector4.h"

namespace Urho3D
{

inline unsigned GetIndex(void*& indexPointer, unsigned indexSize)
{
    if (indexSize == sizeof(unsigned short))
    {
        unsigned short*& p = (unsigned short*&)indexPointer;
        return *p++;
    }
    else
    {
        unsigned*& p = (unsigned*&)indexPointer;
        return *p++;
    }
}

void GenerateTangents(void* vertexData, unsigned vertexSize, const void* indexData, unsigned indexSize, unsigned indexStart,
    unsigned indexCount, unsigned normalOffset, unsigned texCoordOffset, unsigned tangentOffset)
{
    // Tangent generation from
    // http://www.terathon.com/code/tangent.html
    unsigned minVertex = M_MAX_UNSIGNED;
    unsigned maxVertex = 0;
    unsigned char* vertices = (unsigned char*)vertexData;

    void* indexPointer = const_cast<void*>(indexData);
    for (unsigned i = indexStart; i < indexStart + indexCount; ++i)
    {
        unsigned v = GetIndex(indexPointer, indexSize);
        if (v < minVertex)
            minVertex = v;
        if (v > maxVertex)
            maxVertex = v;
    }

    unsigned vertexCount = maxVertex + 1;
    Vector3 *tan1 = new Vector3[vertexCount * 2];
    Vector3 *tan2 = tan1 + vertexCount;
    memset(tan1, 0, sizeof(Vector3) * vertexCount * 2);

    indexPointer = const_cast<void*>(indexData);
    for (unsigned i = indexStart; i < indexStart + indexCount; i += 3)
    {
        unsigned i1 = GetIndex(indexPointer, indexSize);
        unsigned i2 = GetIndex(indexPointer, indexSize);
        unsigned i3 = GetIndex(indexPointer, indexSize);

        const Vector3& v1 = *((Vector3*)(vertices + i1 * vertexSize));
        const Vector3& v2 = *((Vector3*)(vertices + i2 * vertexSize));
        const Vector3& v3 = *((Vector3*)(vertices + i3 * vertexSize));

        const Vector2& w1 = *((Vector2*)(vertices + i1 * vertexSize + texCoordOffset));
        const Vector2& w2 = *((Vector2*)(vertices + i2 * vertexSize + texCoordOffset));
        const Vector2& w3 = *((Vector2*)(vertices + i3 * vertexSize + texCoordOffset));

        float x1 = v2.x_ - v1.x_;
        float x2 = v3.x_ - v1.x_;
        float y1 = v2.y_ - v1.y_;
        float y2 = v3.y_ - v1.y_;
        float z1 = v2.z_ - v1.z_;
        float z2 = v3.z_ - v1.z_;

        float s1 = w2.x_ - w1.x_;
        float s2 = w3.x_ - w1.x_;
        float t1 = w2.y_ - w1.y_;
        float t2 = w3.y_ - w1.y_;

        float r = 1.0f / (s1 * t2 - s2 * t1);
        Vector3 sdir((t2 * x1 - t1 * x2) * r, (t2 * y1 - t1 * y2) * r,
                (t2 * z1 - t1 * z2) * r);
        Vector3 tdir((s1 * x2 - s2 * x1) * r, (s1 * y2 - s2 * y1) * r,
                (s1 * z2 - s2 * z1) * r);

        tan1[i1] += sdir;
        tan1[i2] += sdir;
        tan1[i3] += sdir;

        tan2[i1] += tdir;
        tan2[i2] += tdir;
        tan2[i3] += tdir;
    }

    for (unsigned i = minVertex; i <= maxVertex; i++)
    {
        const Vector3& n = *((Vector3*)(vertices + i * vertexSize + normalOffset));
        const Vector3& t = tan1[i];
        Vector3 xyz;
        float w;

        // Gram-Schmidt orthogonalize
        xyz = (t - n * n.DotProduct(t)).Normalized();

        // Calculate handedness
        w = n.CrossProduct(t).DotProduct(tan2[i]) < 0.0f ? -1.0f : 1.0f;

        Vector4& tangent = *((Vector4*)(vertices + i * vertexSize + tangentOffset));
        tangent = Vector4(xyz, w);
    }

    delete[] tan1;
}

}
<|endoftext|>


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

// Allocator must be global. Otherwise, it does not exist when memory leak detector
// reports memory leaks.
static FailableMemoryAllocator failableMallocAllocator("failable malloc", "malloc", "free");
static FailableMemoryAllocator failableNewAllocator("failable new", "new", "delete");

TEST_GROUP(FailableMemoryAllocator)
{
    TestTestingFixture *fixture;

    void setup()
    {
        fixture = new TestTestingFixture;
        failableMallocAllocator.clearFailedAllocations();
        failableNewAllocator.clearFailedAllocations();
        setCurrentMallocAllocator(&failableMallocAllocator);
        setCurrentNewAllocator(&failableNewAllocator);
    }
    void teardown()
    {
        setCurrentMallocAllocatorToDefault();
        setCurrentNewAllocatorToDefault();
        delete fixture;
    }
};

TEST(FailableMemoryAllocator, MallocWorksNormallyIfNotAskedToFail)
{
    int *memory = (int*)malloc(sizeof(int));
    *memory = 1;
    CHECK(memory != NULL);
    free(memory);
}

TEST(FailableMemoryAllocator, FailFirstMalloc)
{
    failableMallocAllocator.failAllocNumber(1);
    LONGS_EQUAL(NULL, (int*)malloc(sizeof(int)));
}

TEST(FailableMemoryAllocator, FailSecondAndFourthMalloc)
{
    failableMallocAllocator.failAllocNumber(2);
    failableMallocAllocator.failAllocNumber(4);
    int *memory1 = (int*)malloc(sizeof(int));
    int *memory2 = (int*)malloc(sizeof(int));
    int *memory3 = (int*)malloc(sizeof(int));
    int *memory4 = (int*)malloc(sizeof(int));

    CHECK(NULL != memory1);
    LONGS_EQUAL(NULL, memory2);
    CHECK(NULL != memory3);
    LONGS_EQUAL(NULL, memory4);

    free(memory1);
    free(memory3);
}

static void _setUpTooManyFailedMallocs()
{
    FailableMemoryAllocator allocator;
    for (int i = 0; i <= allocator.MAX_NUMBER_OF_FAILED_ALLOCS; i++)
        allocator.failAllocNumber(i + 1);
}

TEST(FailableMemoryAllocator, SettingUpTooManyFailedAllocsWillFail)
{
    fixture->setTestFunction(_setUpTooManyFailedMallocs);
    fixture->runAllTests();
    LONGS_EQUAL(1, fixture->getFailureCount());
    fixture->assertPrintContains("Maximum number of failed memory allocations exceeded");
}

TEST(FailableMemoryAllocator, NewWorksNormallyIfNotAskedToFail)
{
    int *memory = new int;
    *memory = 1;
    CHECK(memory != NULL);
    delete memory;
}

#if CPPUTEST_USE_STD_CPP_LIB

TEST(FailableMemoryAllocator, FailSecondRaisesException)
{
    failableNewAllocator.failAllocNumber(2);
    int *memory1 = new int;

    CHECK_THROWS(std::bad_alloc, new int);

    delete memory1;
}

#endif

TEST(FailableMemoryAllocator, FailSecondAndFourthNewNoThrow)
{
#undef new
    failableNewAllocator.failAllocNumber(2);
    failableNewAllocator.failAllocNumber(4);
    int *memory1 = new (std::nothrow) int;
    int *memory2 = new (std::nothrow) int;
    int *memory3 = new (std::nothrow) int;
    int *memory4 = new (std::nothrow) int;

    CHECK(NULL != memory1);
    LONGS_EQUAL(NULL, memory2);
    CHECK(NULL != memory3);
    LONGS_EQUAL(NULL, memory4);

    delete memory1;
    delete memory3;
}
<commit_msg>New array allocator<commit_after>/*
 * Copyright (c) 2007, Michael Feathers, James Grenning and Bas Vodde
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the <organization> nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE EARLIER MENTIONED AUTHORS ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL <copyright holder> BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
#include "CppUTest/TestHarness.h"
#include "CppUTest/FailableMemoryAllocator.h"
#include "CppUTest/TestTestingFixture.h"

// Allocator must be global. Otherwise, it does not exist when memory leak detector
// reports memory leaks.
static FailableMemoryAllocator failableMallocAllocator("Failable Malloc Allocator", "malloc", "free");
static FailableMemoryAllocator failableNewAllocator("Failable New Allocator", "new", "delete");
static FailableMemoryAllocator failableNewArrayAllocator("Failable New [] Allocator", "new []", "delete []");

TEST_GROUP(FailableMemoryAllocator)
{
    TestTestingFixture *fixture;

    void setup()
    {
        fixture = new TestTestingFixture;
        failableMallocAllocator.clearFailedAllocations();
        failableNewAllocator.clearFailedAllocations();
        failableNewArrayAllocator.clearFailedAllocations();

        setCurrentMallocAllocator(&failableMallocAllocator);
        setCurrentNewAllocator(&failableNewAllocator);
        setCurrentNewArrayAllocator(&failableNewArrayAllocator);
    }
    void teardown()
    {
        setCurrentMallocAllocatorToDefault();
        setCurrentNewAllocatorToDefault();
        setCurrentNewArrayAllocatorToDefault();
        delete fixture;
    }
};

TEST(FailableMemoryAllocator, MallocWorksNormallyIfNotAskedToFail)
{
    int *memory = (int*)malloc(sizeof(int));
    *memory = 1;
    CHECK(memory != NULL);
    free(memory);
}

TEST(FailableMemoryAllocator, FailFirstMalloc)
{
    failableMallocAllocator.failAllocNumber(1);
    LONGS_EQUAL(NULL, (int*)malloc(sizeof(int)));
}

TEST(FailableMemoryAllocator, FailSecondAndFourthMalloc)
{
    failableMallocAllocator.failAllocNumber(2);
    failableMallocAllocator.failAllocNumber(4);
    int *memory1 = (int*)malloc(sizeof(int));
    int *memory2 = (int*)malloc(sizeof(int));
    int *memory3 = (int*)malloc(sizeof(int));
    int *memory4 = (int*)malloc(sizeof(int));

    CHECK(NULL != memory1);
    LONGS_EQUAL(NULL, memory2);
    CHECK(NULL != memory3);
    LONGS_EQUAL(NULL, memory4);

    free(memory1);
    free(memory3);
}

static void _setUpTooManyFailedMallocs()
{
    FailableMemoryAllocator allocator;
    for (int i = 0; i <= allocator.MAX_NUMBER_OF_FAILED_ALLOCS; i++)
        allocator.failAllocNumber(i + 1);
}

TEST(FailableMemoryAllocator, SettingUpTooManyFailedAllocsWillFail)
{
    fixture->setTestFunction(_setUpTooManyFailedMallocs);
    fixture->runAllTests();
    LONGS_EQUAL(1, fixture->getFailureCount());
    fixture->assertPrintContains("Maximum number of failed memory allocations exceeded");
}

TEST(FailableMemoryAllocator, NewWorksNormallyIfNotAskedToFail)
{
    int *memory = new int;
    *memory = 1;
    CHECK(memory != NULL);
    delete memory;
}

TEST(FailableMemoryAllocator, NewArrayWorksNormallyIfNotAskedToFail)
{
    int *memory = new int[10];
    memory[0] = 1;
    CHECK(memory != NULL);
    delete [] memory;
}

#if CPPUTEST_USE_STD_CPP_LIB

TEST(FailableMemoryAllocator, FailSecondNewRaisesException)
{
    failableNewAllocator.failAllocNumber(2);
    int *memory1 = new int;

    CHECK_THROWS(std::bad_alloc, new int);

    delete memory1;
}

TEST(FailableMemoryAllocator, FailSecondNewArrayRaisesException)
{
    failableNewArrayAllocator.failAllocNumber(2);
    int *memory1 = new int[10];

    CHECK_THROWS(std::bad_alloc, new int[10]);

    delete [] memory1;
}

#endif

TEST(FailableMemoryAllocator, FailSecondAndFourthNewNoThrow)
{
#undef new
    failableNewAllocator.failAllocNumber(2);
    failableNewAllocator.failAllocNumber(4);
    int *memory1 = new (std::nothrow) int;
    int *memory2 = new (std::nothrow) int;
    int *memory3 = new (std::nothrow) int;
    int *memory4 = new (std::nothrow) int;

    CHECK(NULL != memory1);
    LONGS_EQUAL(NULL, memory2);
    CHECK(NULL != memory3);
    LONGS_EQUAL(NULL, memory4);

    delete memory1;
    delete memory3;
}

TEST(FailableMemoryAllocator, FailSecondAndFourthNewArrayNoThrow)
{
    failableNewArrayAllocator.failAllocNumber(2);
    failableNewArrayAllocator.failAllocNumber(4);
    int *memory1 = new (std::nothrow) int[10];
    int *memory2 = new (std::nothrow) int[10];
    int *memory3 = new (std::nothrow) int[10];
    int *memory4 = new (std::nothrow) int[10];

    CHECK(NULL != memory1);
    LONGS_EQUAL(NULL, memory2);
    CHECK(NULL != memory3);
    LONGS_EQUAL(NULL, memory4);

    delete [] memory1;
    delete [] memory3;
}
<|endoftext|>


<commit_before>//===- PassManagerBuilder.cpp - Build Standard Pass -----------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file defines the PassManagerBuilder class, which is used to set up a
// "standard" optimization sequence suitable for languages like C and C++.
//
//===----------------------------------------------------------------------===//

#include "llvm/Transforms/IPO/PassManagerBuilder.h"

#include "llvm-c/Transforms/PassManagerBuilder.h"

#include "llvm/PassManager.h"
#include "llvm/DefaultPasses.h"
#include "llvm/PassManager.h"
#include "llvm/Analysis/Passes.h"
#include "llvm/Analysis/Verifier.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Target/TargetLibraryInfo.h"
#include "llvm/Transforms/Scalar.h"
#include "llvm/Transforms/Vectorize.h"
#include "llvm/Transforms/IPO.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/ManagedStatic.h"

using namespace llvm;

static cl::opt<bool>
RunVectorization("vectorize", cl::desc("Run vectorization passes"));

static cl::opt<bool>
UseGVNAfterVectorization("use-gvn-after-vectorization",
  cl::init(false), cl::Hidden,
  cl::desc("Run GVN instead of Early CSE after vectorization passes"));

static cl::opt<bool> UseNewSROA("use-new-sroa",
  cl::init(true), cl::Hidden,
  cl::desc("Enable the new, experimental SROA pass"));

PassManagerBuilder::PassManagerBuilder() {
    OptLevel = 2;
    SizeLevel = 0;
    LibraryInfo = 0;
    Inliner = 0;
    DisableSimplifyLibCalls = false;
    DisableUnitAtATime = false;
    DisableUnrollLoops = false;
    Vectorize = RunVectorization;
}

PassManagerBuilder::~PassManagerBuilder() {
  delete LibraryInfo;
  delete Inliner;
}

/// Set of global extensions, automatically added as part of the standard set.
static ManagedStatic<SmallVector<std::pair<PassManagerBuilder::ExtensionPointTy,
   PassManagerBuilder::ExtensionFn>, 8> > GlobalExtensions;

void PassManagerBuilder::addGlobalExtension(
    PassManagerBuilder::ExtensionPointTy Ty,
    PassManagerBuilder::ExtensionFn Fn) {
  GlobalExtensions->push_back(std::make_pair(Ty, Fn));
}

void PassManagerBuilder::addExtension(ExtensionPointTy Ty, ExtensionFn Fn) {
  Extensions.push_back(std::make_pair(Ty, Fn));
}

void PassManagerBuilder::addExtensionsToPM(ExtensionPointTy ETy,
                                           PassManagerBase &PM) const {
  for (unsigned i = 0, e = GlobalExtensions->size(); i != e; ++i)
    if ((*GlobalExtensions)[i].first == ETy)
      (*GlobalExtensions)[i].second(*this, PM);
  for (unsigned i = 0, e = Extensions.size(); i != e; ++i)
    if (Extensions[i].first == ETy)
      Extensions[i].second(*this, PM);
}

void
PassManagerBuilder::addInitialAliasAnalysisPasses(PassManagerBase &PM) const {
  // Add TypeBasedAliasAnalysis before BasicAliasAnalysis so that
  // BasicAliasAnalysis wins if they disagree. This is intended to help
  // support "obvious" type-punning idioms.
  PM.add(createTypeBasedAliasAnalysisPass());
  PM.add(createBasicAliasAnalysisPass());
}

void PassManagerBuilder::populateFunctionPassManager(FunctionPassManager &FPM) {
  addExtensionsToPM(EP_EarlyAsPossible, FPM);

  // Add LibraryInfo if we have some.
  if (LibraryInfo) FPM.add(new TargetLibraryInfo(*LibraryInfo));

  if (OptLevel == 0) return;

  addInitialAliasAnalysisPasses(FPM);

  FPM.add(createCFGSimplificationPass());
  if (UseNewSROA)
    FPM.add(createSROAPass());
  else
    FPM.add(createScalarReplAggregatesPass());
  FPM.add(createEarlyCSEPass());
  FPM.add(createLowerExpectIntrinsicPass());
}

void PassManagerBuilder::populateModulePassManager(PassManagerBase &MPM) {
  // If all optimizations are disabled, just run the always-inline pass.
  if (OptLevel == 0) {
    if (Inliner) {
      MPM.add(Inliner);
      Inliner = 0;
    }
    addExtensionsToPM(EP_EnabledOnOptLevel0, MPM);
    return;
  }

  // Add LibraryInfo if we have some.
  if (LibraryInfo) MPM.add(new TargetLibraryInfo(*LibraryInfo));

  addInitialAliasAnalysisPasses(MPM);

  if (!DisableUnitAtATime) {
    addExtensionsToPM(EP_ModuleOptimizerEarly, MPM);

    MPM.add(createGlobalOptimizerPass());     // Optimize out global vars

    MPM.add(createIPSCCPPass());              // IP SCCP
    MPM.add(createDeadArgEliminationPass());  // Dead argument elimination

    MPM.add(createInstructionCombiningPass());// Clean up after IPCP & DAE
    MPM.add(createCFGSimplificationPass());   // Clean up after IPCP & DAE
  }

  // Start of CallGraph SCC passes.
  if (!DisableUnitAtATime)
    MPM.add(createPruneEHPass());             // Remove dead EH info
  if (Inliner) {
    MPM.add(Inliner);
    Inliner = 0;
  }
  if (!DisableUnitAtATime)
    MPM.add(createFunctionAttrsPass());       // Set readonly/readnone attrs
  if (OptLevel > 2)
    MPM.add(createArgumentPromotionPass());   // Scalarize uninlined fn args

  // Start of function pass.
  // Break up aggregate allocas, using SSAUpdater.
  if (UseNewSROA)
    MPM.add(createSROAPass(/*RequiresDomTree*/ false));
  else
    MPM.add(createScalarReplAggregatesPass(-1, false));
  MPM.add(createEarlyCSEPass());              // Catch trivial redundancies
  if (!DisableSimplifyLibCalls)
    MPM.add(createSimplifyLibCallsPass());    // Library Call Optimizations
  MPM.add(createJumpThreadingPass());         // Thread jumps.
  MPM.add(createCorrelatedValuePropagationPass()); // Propagate conditionals
  MPM.add(createCFGSimplificationPass());     // Merge & remove BBs
  MPM.add(createInstructionCombiningPass());  // Combine silly seq's

  MPM.add(createTailCallEliminationPass());   // Eliminate tail calls
  MPM.add(createCFGSimplificationPass());     // Merge & remove BBs
  MPM.add(createReassociatePass());           // Reassociate expressions
  MPM.add(createLoopRotatePass());            // Rotate Loop
  MPM.add(createLICMPass());                  // Hoist loop invariants
  MPM.add(createLoopUnswitchPass(SizeLevel || OptLevel < 3));
  MPM.add(createInstructionCombiningPass());
  MPM.add(createIndVarSimplifyPass());        // Canonicalize indvars
  MPM.add(createLoopIdiomPass());             // Recognize idioms like memset.
  MPM.add(createLoopDeletionPass());          // Delete dead loops
  if (!DisableUnrollLoops)
    MPM.add(createLoopUnrollPass());          // Unroll small loops
  addExtensionsToPM(EP_LoopOptimizerEnd, MPM);

  if (OptLevel > 1)
    MPM.add(createGVNPass());                 // Remove redundancies
  MPM.add(createMemCpyOptPass());             // Remove memcpy / form memset
  MPM.add(createSCCPPass());                  // Constant prop with SCCP

  // Run instcombine after redundancy elimination to exploit opportunities
  // opened up by them.
  MPM.add(createInstructionCombiningPass());
  MPM.add(createJumpThreadingPass());         // Thread jumps
  MPM.add(createCorrelatedValuePropagationPass());
  MPM.add(createDeadStoreEliminationPass());  // Delete dead stores

  addExtensionsToPM(EP_ScalarOptimizerLate, MPM);

  if (Vectorize) {
    MPM.add(createBBVectorizePass());
    MPM.add(createInstructionCombiningPass());
    if (OptLevel > 1 && UseGVNAfterVectorization)
      MPM.add(createGVNPass());                   // Remove redundancies
    else
      MPM.add(createEarlyCSEPass());              // Catch trivial redundancies
  }

  MPM.add(createAggressiveDCEPass());         // Delete dead instructions
  MPM.add(createCFGSimplificationPass());     // Merge & remove BBs
  MPM.add(createInstructionCombiningPass());  // Clean up after everything.

  if (!DisableUnitAtATime) {
    // FIXME: We shouldn't bother with this anymore.
    MPM.add(createStripDeadPrototypesPass()); // Get rid of dead prototypes

    // GlobalOpt already deletes dead functions and globals, at -O3 try a
    // late pass of GlobalDCE.  It is capable of deleting dead cycles.
    if (OptLevel > 2)
      MPM.add(createGlobalDCEPass());         // Remove dead fns and globals.

    if (OptLevel > 1)
      MPM.add(createConstantMergePass());     // Merge dup global constants
  }
  addExtensionsToPM(EP_OptimizerLast, MPM);
}

void PassManagerBuilder::populateLTOPassManager(PassManagerBase &PM,
                                                bool Internalize,
                                                bool RunInliner,
                                                bool DisableGVNLoadPRE) {
  // Provide AliasAnalysis services for optimizations.
  addInitialAliasAnalysisPasses(PM);

  // Now that composite has been compiled, scan through the module, looking
  // for a main function.  If main is defined, mark all other functions
  // internal.
  if (Internalize)
    PM.add(createInternalizePass(true));

  // Propagate constants at call sites into the functions they call.  This
  // opens opportunities for globalopt (and inlining) by substituting function
  // pointers passed as arguments to direct uses of functions.
  PM.add(createIPSCCPPass());

  // Now that we internalized some globals, see if we can hack on them!
  PM.add(createGlobalOptimizerPass());

  // Linking modules together can lead to duplicated global constants, only
  // keep one copy of each constant.
  PM.add(createConstantMergePass());

  // Remove unused arguments from functions.
  PM.add(createDeadArgEliminationPass());

  // Reduce the code after globalopt and ipsccp.  Both can open up significant
  // simplification opportunities, and both can propagate functions through
  // function pointers.  When this happens, we often have to resolve varargs
  // calls, etc, so let instcombine do this.
  PM.add(createInstructionCombiningPass());

  // Inline small functions
  if (RunInliner)
    PM.add(createFunctionInliningPass());

  PM.add(createPruneEHPass());   // Remove dead EH info.

  // Optimize globals again if we ran the inliner.
  if (RunInliner)
    PM.add(createGlobalOptimizerPass());
  PM.add(createGlobalDCEPass()); // Remove dead functions.

  // If we didn't decide to inline a function, check to see if we can
  // transform it to pass arguments by value instead of by reference.
  PM.add(createArgumentPromotionPass());

  // The IPO passes may leave cruft around.  Clean up after them.
  PM.add(createInstructionCombiningPass());
  PM.add(createJumpThreadingPass());
  // Break up allocas
  if (UseNewSROA)
    PM.add(createSROAPass());
  else
    PM.add(createScalarReplAggregatesPass());

  // Run a few AA driven optimizations here and now, to cleanup the code.
  PM.add(createFunctionAttrsPass()); // Add nocapture.
  PM.add(createGlobalsModRefPass()); // IP alias analysis.

  PM.add(createLICMPass());                 // Hoist loop invariants.
  PM.add(createGVNPass(DisableGVNLoadPRE)); // Remove redundancies.
  PM.add(createMemCpyOptPass());            // Remove dead memcpys.
  // Nuke dead stores.
  PM.add(createDeadStoreEliminationPass());

  // Cleanup and simplify the code after the scalar optimizations.
  PM.add(createInstructionCombiningPass());

  PM.add(createJumpThreadingPass());

  // Delete basic blocks, which optimization passes may have killed.
  PM.add(createCFGSimplificationPass());

  // Now that we have optimized the program, discard unreachable functions.
  PM.add(createGlobalDCEPass());
}

LLVMPassManagerBuilderRef LLVMPassManagerBuilderCreate(void) {
  PassManagerBuilder *PMB = new PassManagerBuilder();
  return wrap(PMB);
}

void LLVMPassManagerBuilderDispose(LLVMPassManagerBuilderRef PMB) {
  PassManagerBuilder *Builder = unwrap(PMB);
  delete Builder;
}

void
LLVMPassManagerBuilderSetOptLevel(LLVMPassManagerBuilderRef PMB,
                                  unsigned OptLevel) {
  PassManagerBuilder *Builder = unwrap(PMB);
  Builder->OptLevel = OptLevel;
}

void
LLVMPassManagerBuilderSetSizeLevel(LLVMPassManagerBuilderRef PMB,
                                   unsigned SizeLevel) {
  PassManagerBuilder *Builder = unwrap(PMB);
  Builder->SizeLevel = SizeLevel;
}

void
LLVMPassManagerBuilderSetDisableUnitAtATime(LLVMPassManagerBuilderRef PMB,
                                            LLVMBool Value) {
  PassManagerBuilder *Builder = unwrap(PMB);
  Builder->DisableUnitAtATime = Value;
}

void
LLVMPassManagerBuilderSetDisableUnrollLoops(LLVMPassManagerBuilderRef PMB,
                                            LLVMBool Value) {
  PassManagerBuilder *Builder = unwrap(PMB);
  Builder->DisableUnrollLoops = Value;
}

void
LLVMPassManagerBuilderSetDisableSimplifyLibCalls(LLVMPassManagerBuilderRef PMB,
                                                 LLVMBool Value) {
  PassManagerBuilder *Builder = unwrap(PMB);
  Builder->DisableSimplifyLibCalls = Value;
}

void
LLVMPassManagerBuilderUseInlinerWithThreshold(LLVMPassManagerBuilderRef PMB,
                                              unsigned Threshold) {
  PassManagerBuilder *Builder = unwrap(PMB);
  Builder->Inliner = createFunctionInliningPass(Threshold);
}

void
LLVMPassManagerBuilderPopulateFunctionPassManager(LLVMPassManagerBuilderRef PMB,
                                                  LLVMPassManagerRef PM) {
  PassManagerBuilder *Builder = unwrap(PMB);
  FunctionPassManager *FPM = unwrap<FunctionPassManager>(PM);
  Builder->populateFunctionPassManager(*FPM);
}

void
LLVMPassManagerBuilderPopulateModulePassManager(LLVMPassManagerBuilderRef PMB,
                                                LLVMPassManagerRef PM) {
  PassManagerBuilder *Builder = unwrap(PMB);
  PassManagerBase *MPM = unwrap(PM);
  Builder->populateModulePassManager(*MPM);
}

void LLVMPassManagerBuilderPopulateLTOPassManager(LLVMPassManagerBuilderRef PMB,
                                                  LLVMPassManagerRef PM,
                                                  bool Internalize,
                                                  bool RunInliner) {
  PassManagerBuilder *Builder = unwrap(PMB);
  PassManagerBase *LPM = unwrap(PM);
  Builder->populateLTOPassManager(*LPM, Internalize, RunInliner);
}
<commit_msg>LNT builders have picked up new SROA, disable it to get the remaining builders green again.<commit_after>//===- PassManagerBuilder.cpp - Build Standard Pass -----------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file defines the PassManagerBuilder class, which is used to set up a
// "standard" optimization sequence suitable for languages like C and C++.
//
//===----------------------------------------------------------------------===//

#include "llvm/Transforms/IPO/PassManagerBuilder.h"

#include "llvm-c/Transforms/PassManagerBuilder.h"

#include "llvm/PassManager.h"
#include "llvm/DefaultPasses.h"
#include "llvm/PassManager.h"
#include "llvm/Analysis/Passes.h"
#include "llvm/Analysis/Verifier.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Target/TargetLibraryInfo.h"
#include "llvm/Transforms/Scalar.h"
#include "llvm/Transforms/Vectorize.h"
#include "llvm/Transforms/IPO.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/ManagedStatic.h"

using namespace llvm;

static cl::opt<bool>
RunVectorization("vectorize", cl::desc("Run vectorization passes"));

static cl::opt<bool>
UseGVNAfterVectorization("use-gvn-after-vectorization",
  cl::init(false), cl::Hidden,
  cl::desc("Run GVN instead of Early CSE after vectorization passes"));

static cl::opt<bool> UseNewSROA("use-new-sroa",
  cl::init(false), cl::Hidden,
  cl::desc("Enable the new, experimental SROA pass"));

PassManagerBuilder::PassManagerBuilder() {
    OptLevel = 2;
    SizeLevel = 0;
    LibraryInfo = 0;
    Inliner = 0;
    DisableSimplifyLibCalls = false;
    DisableUnitAtATime = false;
    DisableUnrollLoops = false;
    Vectorize = RunVectorization;
}

PassManagerBuilder::~PassManagerBuilder() {
  delete LibraryInfo;
  delete Inliner;
}

/// Set of global extensions, automatically added as part of the standard set.
static ManagedStatic<SmallVector<std::pair<PassManagerBuilder::ExtensionPointTy,
   PassManagerBuilder::ExtensionFn>, 8> > GlobalExtensions;

void PassManagerBuilder::addGlobalExtension(
    PassManagerBuilder::ExtensionPointTy Ty,
    PassManagerBuilder::ExtensionFn Fn) {
  GlobalExtensions->push_back(std::make_pair(Ty, Fn));
}

void PassManagerBuilder::addExtension(ExtensionPointTy Ty, ExtensionFn Fn) {
  Extensions.push_back(std::make_pair(Ty, Fn));
}

void PassManagerBuilder::addExtensionsToPM(ExtensionPointTy ETy,
                                           PassManagerBase &PM) const {
  for (unsigned i = 0, e = GlobalExtensions->size(); i != e; ++i)
    if ((*GlobalExtensions)[i].first == ETy)
      (*GlobalExtensions)[i].second(*this, PM);
  for (unsigned i = 0, e = Extensions.size(); i != e; ++i)
    if (Extensions[i].first == ETy)
      Extensions[i].second(*this, PM);
}

void
PassManagerBuilder::addInitialAliasAnalysisPasses(PassManagerBase &PM) const {
  // Add TypeBasedAliasAnalysis before BasicAliasAnalysis so that
  // BasicAliasAnalysis wins if they disagree. This is intended to help
  // support "obvious" type-punning idioms.
  PM.add(createTypeBasedAliasAnalysisPass());
  PM.add(createBasicAliasAnalysisPass());
}

void PassManagerBuilder::populateFunctionPassManager(FunctionPassManager &FPM) {
  addExtensionsToPM(EP_EarlyAsPossible, FPM);

  // Add LibraryInfo if we have some.
  if (LibraryInfo) FPM.add(new TargetLibraryInfo(*LibraryInfo));

  if (OptLevel == 0) return;

  addInitialAliasAnalysisPasses(FPM);

  FPM.add(createCFGSimplificationPass());
  if (UseNewSROA)
    FPM.add(createSROAPass());
  else
    FPM.add(createScalarReplAggregatesPass());
  FPM.add(createEarlyCSEPass());
  FPM.add(createLowerExpectIntrinsicPass());
}

void PassManagerBuilder::populateModulePassManager(PassManagerBase &MPM) {
  // If all optimizations are disabled, just run the always-inline pass.
  if (OptLevel == 0) {
    if (Inliner) {
      MPM.add(Inliner);
      Inliner = 0;
    }
    addExtensionsToPM(EP_EnabledOnOptLevel0, MPM);
    return;
  }

  // Add LibraryInfo if we have some.
  if (LibraryInfo) MPM.add(new TargetLibraryInfo(*LibraryInfo));

  addInitialAliasAnalysisPasses(MPM);

  if (!DisableUnitAtATime) {
    addExtensionsToPM(EP_ModuleOptimizerEarly, MPM);

    MPM.add(createGlobalOptimizerPass());     // Optimize out global vars

    MPM.add(createIPSCCPPass());              // IP SCCP
    MPM.add(createDeadArgEliminationPass());  // Dead argument elimination

    MPM.add(createInstructionCombiningPass());// Clean up after IPCP & DAE
    MPM.add(createCFGSimplificationPass());   // Clean up after IPCP & DAE
  }

  // Start of CallGraph SCC passes.
  if (!DisableUnitAtATime)
    MPM.add(createPruneEHPass());             // Remove dead EH info
  if (Inliner) {
    MPM.add(Inliner);
    Inliner = 0;
  }
  if (!DisableUnitAtATime)
    MPM.add(createFunctionAttrsPass());       // Set readonly/readnone attrs
  if (OptLevel > 2)
    MPM.add(createArgumentPromotionPass());   // Scalarize uninlined fn args

  // Start of function pass.
  // Break up aggregate allocas, using SSAUpdater.
  if (UseNewSROA)
    MPM.add(createSROAPass(/*RequiresDomTree*/ false));
  else
    MPM.add(createScalarReplAggregatesPass(-1, false));
  MPM.add(createEarlyCSEPass());              // Catch trivial redundancies
  if (!DisableSimplifyLibCalls)
    MPM.add(createSimplifyLibCallsPass());    // Library Call Optimizations
  MPM.add(createJumpThreadingPass());         // Thread jumps.
  MPM.add(createCorrelatedValuePropagationPass()); // Propagate conditionals
  MPM.add(createCFGSimplificationPass());     // Merge & remove BBs
  MPM.add(createInstructionCombiningPass());  // Combine silly seq's

  MPM.add(createTailCallEliminationPass());   // Eliminate tail calls
  MPM.add(createCFGSimplificationPass());     // Merge & remove BBs
  MPM.add(createReassociatePass());           // Reassociate expressions
  MPM.add(createLoopRotatePass());            // Rotate Loop
  MPM.add(createLICMPass());                  // Hoist loop invariants
  MPM.add(createLoopUnswitchPass(SizeLevel || OptLevel < 3));
  MPM.add(createInstructionCombiningPass());
  MPM.add(createIndVarSimplifyPass());        // Canonicalize indvars
  MPM.add(createLoopIdiomPass());             // Recognize idioms like memset.
  MPM.add(createLoopDeletionPass());          // Delete dead loops
  if (!DisableUnrollLoops)
    MPM.add(createLoopUnrollPass());          // Unroll small loops
  addExtensionsToPM(EP_LoopOptimizerEnd, MPM);

  if (OptLevel > 1)
    MPM.add(createGVNPass());                 // Remove redundancies
  MPM.add(createMemCpyOptPass());             // Remove memcpy / form memset
  MPM.add(createSCCPPass());                  // Constant prop with SCCP

  // Run instcombine after redundancy elimination to exploit opportunities
  // opened up by them.
  MPM.add(createInstructionCombiningPass());
  MPM.add(createJumpThreadingPass());         // Thread jumps
  MPM.add(createCorrelatedValuePropagationPass());
  MPM.add(createDeadStoreEliminationPass());  // Delete dead stores

  addExtensionsToPM(EP_ScalarOptimizerLate, MPM);

  if (Vectorize) {
    MPM.add(createBBVectorizePass());
    MPM.add(createInstructionCombiningPass());
    if (OptLevel > 1 && UseGVNAfterVectorization)
      MPM.add(createGVNPass());                   // Remove redundancies
    else
      MPM.add(createEarlyCSEPass());              // Catch trivial redundancies
  }

  MPM.add(createAggressiveDCEPass());         // Delete dead instructions
  MPM.add(createCFGSimplificationPass());     // Merge & remove BBs
  MPM.add(createInstructionCombiningPass());  // Clean up after everything.

  if (!DisableUnitAtATime) {
    // FIXME: We shouldn't bother with this anymore.
    MPM.add(createStripDeadPrototypesPass()); // Get rid of dead prototypes

    // GlobalOpt already deletes dead functions and globals, at -O3 try a
    // late pass of GlobalDCE.  It is capable of deleting dead cycles.
    if (OptLevel > 2)
      MPM.add(createGlobalDCEPass());         // Remove dead fns and globals.

    if (OptLevel > 1)
      MPM.add(createConstantMergePass());     // Merge dup global constants
  }
  addExtensionsToPM(EP_OptimizerLast, MPM);
}

void PassManagerBuilder::populateLTOPassManager(PassManagerBase &PM,
                                                bool Internalize,
                                                bool RunInliner,
                                                bool DisableGVNLoadPRE) {
  // Provide AliasAnalysis services for optimizations.
  addInitialAliasAnalysisPasses(PM);

  // Now that composite has been compiled, scan through the module, looking
  // for a main function.  If main is defined, mark all other functions
  // internal.
  if (Internalize)
    PM.add(createInternalizePass(true));

  // Propagate constants at call sites into the functions they call.  This
  // opens opportunities for globalopt (and inlining) by substituting function
  // pointers passed as arguments to direct uses of functions.
  PM.add(createIPSCCPPass());

  // Now that we internalized some globals, see if we can hack on them!
  PM.add(createGlobalOptimizerPass());

  // Linking modules together can lead to duplicated global constants, only
  // keep one copy of each constant.
  PM.add(createConstantMergePass());

  // Remove unused arguments from functions.
  PM.add(createDeadArgEliminationPass());

  // Reduce the code after globalopt and ipsccp.  Both can open up significant
  // simplification opportunities, and both can propagate functions through
  // function pointers.  When this happens, we often have to resolve varargs
  // calls, etc, so let instcombine do this.
  PM.add(createInstructionCombiningPass());

  // Inline small functions
  if (RunInliner)
    PM.add(createFunctionInliningPass());

  PM.add(createPruneEHPass());   // Remove dead EH info.

  // Optimize globals again if we ran the inliner.
  if (RunInliner)
    PM.add(createGlobalOptimizerPass());
  PM.add(createGlobalDCEPass()); // Remove dead functions.

  // If we didn't decide to inline a function, check to see if we can
  // transform it to pass arguments by value instead of by reference.
  PM.add(createArgumentPromotionPass());

  // The IPO passes may leave cruft around.  Clean up after them.
  PM.add(createInstructionCombiningPass());
  PM.add(createJumpThreadingPass());
  // Break up allocas
  if (UseNewSROA)
    PM.add(createSROAPass());
  else
    PM.add(createScalarReplAggregatesPass());

  // Run a few AA driven optimizations here and now, to cleanup the code.
  PM.add(createFunctionAttrsPass()); // Add nocapture.
  PM.add(createGlobalsModRefPass()); // IP alias analysis.

  PM.add(createLICMPass());                 // Hoist loop invariants.
  PM.add(createGVNPass(DisableGVNLoadPRE)); // Remove redundancies.
  PM.add(createMemCpyOptPass());            // Remove dead memcpys.
  // Nuke dead stores.
  PM.add(createDeadStoreEliminationPass());

  // Cleanup and simplify the code after the scalar optimizations.
  PM.add(createInstructionCombiningPass());

  PM.add(createJumpThreadingPass());

  // Delete basic blocks, which optimization passes may have killed.
  PM.add(createCFGSimplificationPass());

  // Now that we have optimized the program, discard unreachable functions.
  PM.add(createGlobalDCEPass());
}

LLVMPassManagerBuilderRef LLVMPassManagerBuilderCreate(void) {
  PassManagerBuilder *PMB = new PassManagerBuilder();
  return wrap(PMB);
}

void LLVMPassManagerBuilderDispose(LLVMPassManagerBuilderRef PMB) {
  PassManagerBuilder *Builder = unwrap(PMB);
  delete Builder;
}

void
LLVMPassManagerBuilderSetOptLevel(LLVMPassManagerBuilderRef PMB,
                                  unsigned OptLevel) {
  PassManagerBuilder *Builder = unwrap(PMB);
  Builder->OptLevel = OptLevel;
}

void
LLVMPassManagerBuilderSetSizeLevel(LLVMPassManagerBuilderRef PMB,
                                   unsigned SizeLevel) {
  PassManagerBuilder *Builder = unwrap(PMB);
  Builder->SizeLevel = SizeLevel;
}

void
LLVMPassManagerBuilderSetDisableUnitAtATime(LLVMPassManagerBuilderRef PMB,
                                            LLVMBool Value) {
  PassManagerBuilder *Builder = unwrap(PMB);
  Builder->DisableUnitAtATime = Value;
}

void
LLVMPassManagerBuilderSetDisableUnrollLoops(LLVMPassManagerBuilderRef PMB,
                                            LLVMBool Value) {
  PassManagerBuilder *Builder = unwrap(PMB);
  Builder->DisableUnrollLoops = Value;
}

void
LLVMPassManagerBuilderSetDisableSimplifyLibCalls(LLVMPassManagerBuilderRef PMB,
                                                 LLVMBool Value) {
  PassManagerBuilder *Builder = unwrap(PMB);
  Builder->DisableSimplifyLibCalls = Value;
}

void
LLVMPassManagerBuilderUseInlinerWithThreshold(LLVMPassManagerBuilderRef PMB,
                                              unsigned Threshold) {
  PassManagerBuilder *Builder = unwrap(PMB);
  Builder->Inliner = createFunctionInliningPass(Threshold);
}

void
LLVMPassManagerBuilderPopulateFunctionPassManager(LLVMPassManagerBuilderRef PMB,
                                                  LLVMPassManagerRef PM) {
  PassManagerBuilder *Builder = unwrap(PMB);
  FunctionPassManager *FPM = unwrap<FunctionPassManager>(PM);
  Builder->populateFunctionPassManager(*FPM);
}

void
LLVMPassManagerBuilderPopulateModulePassManager(LLVMPassManagerBuilderRef PMB,
                                                LLVMPassManagerRef PM) {
  PassManagerBuilder *Builder = unwrap(PMB);
  PassManagerBase *MPM = unwrap(PM);
  Builder->populateModulePassManager(*MPM);
}

void LLVMPassManagerBuilderPopulateLTOPassManager(LLVMPassManagerBuilderRef PMB,
                                                  LLVMPassManagerRef PM,
                                                  bool Internalize,
                                                  bool RunInliner) {
  PassManagerBuilder *Builder = unwrap(PMB);
  PassManagerBase *LPM = unwrap(PM);
  Builder->populateLTOPassManager(*LPM, Internalize, RunInliner);
}
<|endoftext|>


<commit_before>/**
 * This file is part of the "libfnord" project
 *   Copyright (c) 2015 Paul Asmuth
 *
 * FnordMetric is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License v3.0. You should have received a
 * copy of the GNU General Public License along with this program. If not, see
 * <http://www.gnu.org/licenses/>.
 */
#include <stx/util/Base64.h>
#include <stx/fnv.h>
#include <stx/protobuf/msg.h>
#include <stx/io/fileutil.h>
#include <sstable/sstablereader.h>
#include <tsdb/PartitionMap.h>
#include <tsdb/PartitionState.pb.h>

using namespace stx;

namespace tsdb {

static mdb::MDBOptions tsdb_mdb_opts() {
  mdb::MDBOptions opts;
  opts.data_filename = "index.db";
  opts.lock_filename = "index.db.lck";
  return opts;
};

PartitionMap::PartitionMap(const String& db_path) :
    db_path_(db_path),
    db_(mdb::MDB::open(db_path, tsdb_mdb_opts())) {}

Option<RefPtr<Table>> PartitionMap::findTable(
    const String& stream_ns,
    const String& stream_key) const {
  std::unique_lock<std::mutex> lk(mutex_);
  return findTableWithLock(stream_ns, stream_key);
}

Option<RefPtr<Table>> PartitionMap::findTableWithLock(
    const String& stream_ns,
    const String& stream_key) const {
  auto stream_ns_key = stream_ns + "~" + stream_key;

  const auto& iter = tables_.find(stream_ns_key);
  if (iter == tables_.end()) {
    return None<RefPtr<Table>>();
  } else {
    return Some(iter->second);
  }
}

void PartitionMap::configureTable(const TableConfig& table) {
  std::unique_lock<std::mutex> lk(mutex_);
  auto stream_ns_key = table.tsdb_namespace() + "~" + table.table_name();

  tables_.emplace(
      stream_ns_key,
      new Table(
          table,
          msg::MessageSchema::decode(table.schema())));
}

void PartitionMap::open() {
  auto txn = db_->startTransaction(false);
  auto cursor = txn->getCursor();

  for (int i = 0; ; ++i) {
    Buffer key;
    Buffer value;
    if (i == 0) {
      if (!cursor->getFirst(&key, &value)) {
        break;
      }
    } else {
      if (!cursor->getNext(&key, &value)) {
        break;
      }
    }

    auto db_key = key.toString();
    if (db_key.size() == 0) {
      continue;
    }

    if (db_key[0] == 0x1b) {
      continue;
    }

    auto tsdb_namespace_off = StringUtil::find(db_key, '~');
    if (tsdb_namespace_off == String::npos) {
      RAISEF(kRuntimeError, "invalid partition key: $0", db_key);
    }

    auto tsdb_namespace = db_key.substr(0, tsdb_namespace_off);
    SHA1Hash partition_key(
        db_key.data() + tsdb_namespace_off + 1,
        db_key.size() - tsdb_namespace_off - 1);

    auto table_key = value.toString();
    auto table = findTableWithLock(tsdb_namespace, table_key);

    if (table.isEmpty()) {
      logWarning(
          "tsdb",
          "Orphaned partition: $0/$1",
          table_key,
          partition_key.toString());
      continue;
    }

    auto partition = Partition::reopen(
        tsdb_namespace,
        table.get(),
        partition_key,
        db_path_);

    partitions_.emplace(db_key, partition);
  }

  cursor->close();
  txn->abort();
}

RefPtr<Partition> PartitionMap::findOrCreatePartition(
    const String& tsdb_namespace,
    const String& stream_key,
    const SHA1Hash& partition_key) {
  auto db_key = tsdb_namespace + "~";
  db_key.append((char*) partition_key.data(), partition_key.size());

  std::unique_lock<std::mutex> lk(mutex_);
  auto iter = partitions_.find(db_key);
  if (iter != partitions_.end()) {
    return iter->second;
  }

  auto table = findTableWithLock(tsdb_namespace, stream_key);
  if (table.isEmpty()) {
    RAISEF(kNotFoundError, "table not found: $0", stream_key);
  }

  auto partition = Partition::create(
      tsdb_namespace,
      table.get(),
      partition_key,
      db_path_);

  partitions_.emplace(db_key, partition);

  auto txn = db_->startTransaction(false);
  txn->update(
      db_key.data(),
      db_key.size(),
      stream_key.data(),
      stream_key.size());

  txn->commit();

  return partition;
}

Option<RefPtr<Partition>> PartitionMap::findPartition(
    const String& tsdb_namespace,
    const String& stream_key,
    const SHA1Hash& partition_key) {
  auto db_key = tsdb_namespace + "~";
  db_key.append((char*) partition_key.data(), partition_key.size());

  std::unique_lock<std::mutex> lk(mutex_);
  auto iter = partitions_.find(db_key);
  if (iter == partitions_.end()) {
    return None<RefPtr<Partition>>();
  } else {
    return Some(iter->second);
  }
}

void PartitionMap::listTables(
    const String& tsdb_namespace,
    Function<void (const TSDBTableInfo& table)> fn) const {
  for (const auto& tbl : tables_) {
    if (tbl.second->tsdbNamespace() != tsdb_namespace) {
      continue;
    }

    TSDBTableInfo ti;
    ti.table_name = tbl.second->name();
    ti.config = tbl.second->config();
    ti.schema = tbl.second->schema();
    fn(ti);
  }
}

Option<TSDBTableInfo> PartitionMap::tableInfo(
      const String& tsdb_namespace,
      const String& table_key) const {
  auto table = findTable(tsdb_namespace, table_key);
  if (table.isEmpty()) {
    return None<TSDBTableInfo>();
  }

  TSDBTableInfo ti;
  ti.table_name = table.get()->name();
  ti.config = table.get()->config();
  ti.schema = table.get()->schema();
  return Some(ti);
}

void PartitionMap::subscribeToPartitionChanges(PartitionChangeCallbackFn fn) {
  callbacks_.emplace_back(fn);
}

void PartitionMap::publishPartitionChange(
    RefPtr<PartitionChangeNotification> change) {
  for (const auto& cb : callbacks_) {
    cb(change);
  }
}

} // namespace tdsb

<commit_msg>publish change notification on partition reopen<commit_after>/**
 * This file is part of the "libfnord" project
 *   Copyright (c) 2015 Paul Asmuth
 *
 * FnordMetric is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License v3.0. You should have received a
 * copy of the GNU General Public License along with this program. If not, see
 * <http://www.gnu.org/licenses/>.
 */
#include <stx/util/Base64.h>
#include <stx/fnv.h>
#include <stx/protobuf/msg.h>
#include <stx/io/fileutil.h>
#include <sstable/sstablereader.h>
#include <tsdb/PartitionMap.h>
#include <tsdb/PartitionState.pb.h>

using namespace stx;

namespace tsdb {

static mdb::MDBOptions tsdb_mdb_opts() {
  mdb::MDBOptions opts;
  opts.data_filename = "index.db";
  opts.lock_filename = "index.db.lck";
  return opts;
};

PartitionMap::PartitionMap(const String& db_path) :
    db_path_(db_path),
    db_(mdb::MDB::open(db_path, tsdb_mdb_opts())) {}

Option<RefPtr<Table>> PartitionMap::findTable(
    const String& stream_ns,
    const String& stream_key) const {
  std::unique_lock<std::mutex> lk(mutex_);
  return findTableWithLock(stream_ns, stream_key);
}

Option<RefPtr<Table>> PartitionMap::findTableWithLock(
    const String& stream_ns,
    const String& stream_key) const {
  auto stream_ns_key = stream_ns + "~" + stream_key;

  const auto& iter = tables_.find(stream_ns_key);
  if (iter == tables_.end()) {
    return None<RefPtr<Table>>();
  } else {
    return Some(iter->second);
  }
}

void PartitionMap::configureTable(const TableConfig& table) {
  std::unique_lock<std::mutex> lk(mutex_);
  auto stream_ns_key = table.tsdb_namespace() + "~" + table.table_name();

  tables_.emplace(
      stream_ns_key,
      new Table(
          table,
          msg::MessageSchema::decode(table.schema())));
}

void PartitionMap::open() {
  auto txn = db_->startTransaction(false);
  auto cursor = txn->getCursor();

  for (int i = 0; ; ++i) {
    Buffer key;
    Buffer value;
    if (i == 0) {
      if (!cursor->getFirst(&key, &value)) {
        break;
      }
    } else {
      if (!cursor->getNext(&key, &value)) {
        break;
      }
    }

    auto db_key = key.toString();
    if (db_key.size() == 0) {
      continue;
    }

    if (db_key[0] == 0x1b) {
      continue;
    }

    auto tsdb_namespace_off = StringUtil::find(db_key, '~');
    if (tsdb_namespace_off == String::npos) {
      RAISEF(kRuntimeError, "invalid partition key: $0", db_key);
    }

    auto tsdb_namespace = db_key.substr(0, tsdb_namespace_off);
    SHA1Hash partition_key(
        db_key.data() + tsdb_namespace_off + 1,
        db_key.size() - tsdb_namespace_off - 1);

    auto table_key = value.toString();
    auto table = findTableWithLock(tsdb_namespace, table_key);

    if (table.isEmpty()) {
      logWarning(
          "tsdb",
          "Orphaned partition: $0/$1",
          table_key,
          partition_key.toString());
      continue;
    }

    auto partition = Partition::reopen(
        tsdb_namespace,
        table.get(),
        partition_key,
        db_path_);

    partitions_.emplace(db_key, partition);

    auto change = mkRef(new PartitionChangeNotification());
    change->partition = partition;
    publishPartitionChange(change);
  }

  cursor->close();
  txn->abort();
}

RefPtr<Partition> PartitionMap::findOrCreatePartition(
    const String& tsdb_namespace,
    const String& stream_key,
    const SHA1Hash& partition_key) {
  auto db_key = tsdb_namespace + "~";
  db_key.append((char*) partition_key.data(), partition_key.size());

  std::unique_lock<std::mutex> lk(mutex_);
  auto iter = partitions_.find(db_key);
  if (iter != partitions_.end()) {
    return iter->second;
  }

  auto table = findTableWithLock(tsdb_namespace, stream_key);
  if (table.isEmpty()) {
    RAISEF(kNotFoundError, "table not found: $0", stream_key);
  }

  auto partition = Partition::create(
      tsdb_namespace,
      table.get(),
      partition_key,
      db_path_);

  partitions_.emplace(db_key, partition);

  auto txn = db_->startTransaction(false);
  txn->update(
      db_key.data(),
      db_key.size(),
      stream_key.data(),
      stream_key.size());

  txn->commit();

  return partition;
}

Option<RefPtr<Partition>> PartitionMap::findPartition(
    const String& tsdb_namespace,
    const String& stream_key,
    const SHA1Hash& partition_key) {
  auto db_key = tsdb_namespace + "~";
  db_key.append((char*) partition_key.data(), partition_key.size());

  std::unique_lock<std::mutex> lk(mutex_);
  auto iter = partitions_.find(db_key);
  if (iter == partitions_.end()) {
    return None<RefPtr<Partition>>();
  } else {
    return Some(iter->second);
  }
}

void PartitionMap::listTables(
    const String& tsdb_namespace,
    Function<void (const TSDBTableInfo& table)> fn) const {
  for (const auto& tbl : tables_) {
    if (tbl.second->tsdbNamespace() != tsdb_namespace) {
      continue;
    }

    TSDBTableInfo ti;
    ti.table_name = tbl.second->name();
    ti.config = tbl.second->config();
    ti.schema = tbl.second->schema();
    fn(ti);
  }
}

Option<TSDBTableInfo> PartitionMap::tableInfo(
      const String& tsdb_namespace,
      const String& table_key) const {
  auto table = findTable(tsdb_namespace, table_key);
  if (table.isEmpty()) {
    return None<TSDBTableInfo>();
  }

  TSDBTableInfo ti;
  ti.table_name = table.get()->name();
  ti.config = table.get()->config();
  ti.schema = table.get()->schema();
  return Some(ti);
}

void PartitionMap::subscribeToPartitionChanges(PartitionChangeCallbackFn fn) {
  callbacks_.emplace_back(fn);
}

void PartitionMap::publishPartitionChange(
    RefPtr<PartitionChangeNotification> change) {
  for (const auto& cb : callbacks_) {
    cb(change);
  }
}

} // namespace tdsb

<|endoftext|>


<commit_before>#include "common.h"
#include "Mmdeviceapi.h"
#include "endpointvolume.h"

#include "callbacks.h"
#include "handlers.h"
#include "ControlChangeHandler.h"
#include "EndpointNotificationHandler.h"

using namespace EarTrumpet::Interop;

HRESULT EndpointNotificationHandler::OnDefaultDeviceChanged(EDataFlow dataFlow, ERole role, PCWSTR rawDeviceId)
{
    if (dataFlow == EDataFlow::eRender && role != ERole::eCommunications)
    {
        std::wstring deviceId(rawDeviceId);
        _lastSeenDeviceId = deviceId;
        if (_controlChangeHandlers.find(deviceId) == _controlChangeHandlers.end())
        {
            CComObject<ControlChangeHandler>* controlChangeHandler;
            FAST_FAIL(GetCachedControlChangeHandlerByDeviceId(deviceId, &controlChangeHandler));
            controlChangeHandler->RegisterVolumeChangedCallback(deviceId.c_str(), this);

            IAudioEndpointVolume* audioEndpointVolume;
            FAST_FAIL(GetCachedAudioEndpointVolumeByDeviceId(deviceId, &audioEndpointVolume));
            FAST_FAIL(audioEndpointVolume->RegisterControlChangeNotify(controlChangeHandler));
        }

        float volume;
        FAST_FAIL(GetDefaultDeviceVolume(&volume));
        FAST_FAIL(OnVolumeChanged(_lastSeenDeviceId.c_str(), volume));
    }

    return S_OK;
}

HRESULT EndpointNotificationHandler::RegisterVolumeChangeHandler(IMMDeviceEnumerator* deviceEnumerator, IEndpointNotificationCallback* callback)
{
    _deviceEnumerator = deviceEnumerator;
    _endpointCallback = callback;

    CComPtr<IMMDevice> defaultDevice;
    FAST_FAIL(deviceEnumerator->GetDefaultAudioEndpoint(EDataFlow::eRender, ERole::eMultimedia, &defaultDevice));

    CComHeapPtr<wchar_t> defaultDeviceId;
    FAST_FAIL(defaultDevice->GetId(&defaultDeviceId));

    return OnDefaultDeviceChanged(EDataFlow::eRender, ERole::eMultimedia, defaultDeviceId);
}

HRESULT EndpointNotificationHandler::OnVolumeChanged(PCWSTR deviceId, float volume)
{
    if (_lastSeenDeviceId == deviceId)
    {
        FAST_FAIL(_endpointCallback->OnVolumeChanged(volume));
    }

    return S_OK;
}

HRESULT EndpointNotificationHandler::GetDefaultDeviceVolume(float* volume)
{
    IAudioEndpointVolume* endpointVolume;
    FAST_FAIL(GetCachedAudioEndpointVolumeByDeviceId(_lastSeenDeviceId, &endpointVolume));
    return endpointVolume->GetMasterVolumeLevelScalar(volume);
}

HRESULT EndpointNotificationHandler::GetCachedDeviceByDeviceId(std::wstring const& deviceId, IMMDevice** device)
{
    if (_cachedDevices.find(deviceId) == _cachedDevices.end())
    {
        FAST_FAIL(_deviceEnumerator->GetDevice(deviceId.c_str(), &_cachedDevices[deviceId]));
    }

    *device = _cachedDevices[deviceId].p;
    return S_OK;
}

HRESULT EndpointNotificationHandler::GetCachedAudioEndpointVolumeByDeviceId(std::wstring const& deviceId, IAudioEndpointVolume** audioEndpointVolume)
{
    IMMDevice* device;
    FAST_FAIL(GetCachedDeviceByDeviceId(deviceId, &device));

    if (_cachedEndpoints.find(deviceId) == _cachedEndpoints.end())
    {
        FAST_FAIL(device->Activate(__uuidof(IAudioEndpointVolume), CLSCTX_INPROC_SERVER,
            nullptr, reinterpret_cast<void**>(&_cachedEndpoints[deviceId])));
    }

    *audioEndpointVolume = _cachedEndpoints[deviceId].p;
    return S_OK;
}

HRESULT EndpointNotificationHandler::GetCachedControlChangeHandlerByDeviceId(std::wstring const& deviceId, CComObject<ControlChangeHandler>** controlChangeHandler)
{
    if (_controlChangeHandlers.find(deviceId) == _controlChangeHandlers.end())
    {
        FAST_FAIL(CComObject<ControlChangeHandler>::CreateInstance(&_controlChangeHandlers[deviceId]));
        _controlChangeHandlers[deviceId]->AddRef();
    }

    *controlChangeHandler = _controlChangeHandlers[deviceId];
    return S_OK;
}

HRESULT EndpointNotificationHandler::OnDeviceStateChanged(PCWSTR, DWORD)
{
    return S_OK;
}

HRESULT EndpointNotificationHandler::OnDeviceAdded(PCWSTR)
{
    return S_OK;
}

HRESULT EndpointNotificationHandler::OnDeviceRemoved(PCWSTR)
{
    return S_OK;
}

HRESULT EndpointNotificationHandler::OnPropertyValueChanged(PCWSTR, const PROPERTYKEY)
{
    return S_OK;
}<commit_msg>Safely ignore events with null deviceid<commit_after>#include "common.h"
#include "Mmdeviceapi.h"
#include "endpointvolume.h"

#include "callbacks.h"
#include "handlers.h"
#include "ControlChangeHandler.h"
#include "EndpointNotificationHandler.h"

using namespace EarTrumpet::Interop;

HRESULT EndpointNotificationHandler::OnDefaultDeviceChanged(EDataFlow dataFlow, ERole role, PCWSTR rawDeviceId)
{
    if (dataFlow == EDataFlow::eRender && role != ERole::eCommunications && rawDeviceId != nullptr)
    {
        std::wstring deviceId(rawDeviceId);
        _lastSeenDeviceId = deviceId;
        if (_controlChangeHandlers.find(deviceId) == _controlChangeHandlers.end())
        {
            CComObject<ControlChangeHandler>* controlChangeHandler;
            FAST_FAIL(GetCachedControlChangeHandlerByDeviceId(deviceId, &controlChangeHandler));
            controlChangeHandler->RegisterVolumeChangedCallback(deviceId.c_str(), this);

            IAudioEndpointVolume* audioEndpointVolume;
            FAST_FAIL(GetCachedAudioEndpointVolumeByDeviceId(deviceId, &audioEndpointVolume));
            FAST_FAIL(audioEndpointVolume->RegisterControlChangeNotify(controlChangeHandler));
        }

        float volume;
        FAST_FAIL(GetDefaultDeviceVolume(&volume));
        FAST_FAIL(OnVolumeChanged(_lastSeenDeviceId.c_str(), volume));
    }

    return S_OK;
}

HRESULT EndpointNotificationHandler::RegisterVolumeChangeHandler(IMMDeviceEnumerator* deviceEnumerator, IEndpointNotificationCallback* callback)
{
    _deviceEnumerator = deviceEnumerator;
    _endpointCallback = callback;

    CComPtr<IMMDevice> defaultDevice;
    FAST_FAIL(deviceEnumerator->GetDefaultAudioEndpoint(EDataFlow::eRender, ERole::eMultimedia, &defaultDevice));

    CComHeapPtr<wchar_t> defaultDeviceId;
    FAST_FAIL(defaultDevice->GetId(&defaultDeviceId));

    return OnDefaultDeviceChanged(EDataFlow::eRender, ERole::eMultimedia, defaultDeviceId);
}

HRESULT EndpointNotificationHandler::OnVolumeChanged(PCWSTR deviceId, float volume)
{
    if (_lastSeenDeviceId == deviceId)
    {
        FAST_FAIL(_endpointCallback->OnVolumeChanged(volume));
    }

    return S_OK;
}

HRESULT EndpointNotificationHandler::GetDefaultDeviceVolume(float* volume)
{
    IAudioEndpointVolume* endpointVolume;
    FAST_FAIL(GetCachedAudioEndpointVolumeByDeviceId(_lastSeenDeviceId, &endpointVolume));
    return endpointVolume->GetMasterVolumeLevelScalar(volume);
}

HRESULT EndpointNotificationHandler::GetCachedDeviceByDeviceId(std::wstring const& deviceId, IMMDevice** device)
{
    if (_cachedDevices.find(deviceId) == _cachedDevices.end())
    {
        FAST_FAIL(_deviceEnumerator->GetDevice(deviceId.c_str(), &_cachedDevices[deviceId]));
    }

    *device = _cachedDevices[deviceId].p;
    return S_OK;
}

HRESULT EndpointNotificationHandler::GetCachedAudioEndpointVolumeByDeviceId(std::wstring const& deviceId, IAudioEndpointVolume** audioEndpointVolume)
{
    IMMDevice* device;
    FAST_FAIL(GetCachedDeviceByDeviceId(deviceId, &device));

    if (_cachedEndpoints.find(deviceId) == _cachedEndpoints.end())
    {
        FAST_FAIL(device->Activate(__uuidof(IAudioEndpointVolume), CLSCTX_INPROC_SERVER,
            nullptr, reinterpret_cast<void**>(&_cachedEndpoints[deviceId])));
    }

    *audioEndpointVolume = _cachedEndpoints[deviceId].p;
    return S_OK;
}

HRESULT EndpointNotificationHandler::GetCachedControlChangeHandlerByDeviceId(std::wstring const& deviceId, CComObject<ControlChangeHandler>** controlChangeHandler)
{
    if (_controlChangeHandlers.find(deviceId) == _controlChangeHandlers.end())
    {
        FAST_FAIL(CComObject<ControlChangeHandler>::CreateInstance(&_controlChangeHandlers[deviceId]));
        _controlChangeHandlers[deviceId]->AddRef();
    }

    *controlChangeHandler = _controlChangeHandlers[deviceId];
    return S_OK;
}

HRESULT EndpointNotificationHandler::OnDeviceStateChanged(PCWSTR, DWORD)
{
    return S_OK;
}

HRESULT EndpointNotificationHandler::OnDeviceAdded(PCWSTR)
{
    return S_OK;
}

HRESULT EndpointNotificationHandler::OnDeviceRemoved(PCWSTR)
{
    return S_OK;
}

HRESULT EndpointNotificationHandler::OnPropertyValueChanged(PCWSTR, const PROPERTYKEY)
{
    return S_OK;
}<|endoftext|>