<commit_before>// Copyright (c) 2014-2019 Daniel Kraft
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include <base58.h>
#include <coins.h>
#include <key_io.h>
#include <names/encoding.h>
#include <names/mempool.h>
#include <primitives/transaction.h>
#include <script/names.h>
#include <test/setup_common.h>
#include <txmempool.h>
#include <validation.h>

#include <boost/test/unit_test.hpp>

/* No space between BOOST_FIXTURE_TEST_SUITE and '(', so that extraction of
   the test-suite name works with grep as done in the Makefile.  */
BOOST_FIXTURE_TEST_SUITE(name_mempool_tests, TestingSetup)

namespace
{

/**
 * Utility function that returns a sample address script to use in the tests.
 * @return A script that represents a simple address.
 */
CScript
getTestAddress ()
{
  const CTxDestination dest
    = DecodeDestination ("N5e1vXUUL3KfhPyVjQZSes1qQ7eyarDbUU");
  BOOST_CHECK (IsValidDestination (dest));

  return GetScriptForDestination (dest);
}

} // anonymous namespace

/* ************************************************************************** */

BOOST_AUTO_TEST_CASE (name_mempool)
{
  LOCK(mempool.cs);
  mempool.clear ();

  const valtype nameReg = DecodeName ("name-reg", NameEncoding::ASCII);
  const valtype nameUpd = DecodeName ("name-upd", NameEncoding::ASCII);
  const valtype value = DecodeName ("value", NameEncoding::ASCII);
  const valtype valueA = DecodeName ("value-a", NameEncoding::ASCII);
  const valtype valueB = DecodeName ("value-b", NameEncoding::ASCII);
  const CScript addr = getTestAddress ();
  const CScript addr2 = (CScript (addr) << OP_RETURN);

  const valtype rand1(20, 'a');
  const valtype rand2(20, 'b');

  valtype toHash(rand1);
  toHash.insert (toHash.end (), nameReg.begin (), nameReg.end ());
  uint160 hash = Hash160 (toHash);
  const valtype vchHash1(hash.begin (), hash.end ());
  toHash = rand2;
  toHash.insert (toHash.end (), nameReg.begin (), nameReg.end ());
  hash = Hash160 (toHash);
  const valtype vchHash2(hash.begin (), hash.end ());

  const CScript new1
    = CNameScript::buildNameNew (addr, nameReg, rand1);
  const CScript new1p
    = CNameScript::buildNameNew (addr2, nameReg, rand1);
  const CScript new2
    = CNameScript::buildNameNew (addr, nameReg, rand2);
  const CScript first1
    = CNameScript::buildNameFirstupdate (addr, nameReg, value, rand1);
  const CScript first2
    = CNameScript::buildNameFirstupdate (addr, nameReg, value, rand2);
  const CScript upd1 = CNameScript::buildNameUpdate (addr, nameUpd, valueA);
  const CScript upd2 = CNameScript::buildNameUpdate (addr, nameUpd, valueB);

  /* The constructed tx needs not be valid.  We only test
     the mempool acceptance and not validation.  */

  CMutableTransaction mtxNew1;
  mtxNew1.SetNamecoin ();
  mtxNew1.vout.push_back (CTxOut (COIN, new1));
  const CTransaction txNew1(mtxNew1);
  CMutableTransaction mtxNew1p;
  mtxNew1p.SetNamecoin ();
  mtxNew1p.vout.push_back (CTxOut (COIN, new1p));
  const CTransaction txNew1p(mtxNew1p);
  CMutableTransaction mtxNew2;
  mtxNew2.SetNamecoin ();
  mtxNew2.vout.push_back (CTxOut (COIN, new2));
  const CTransaction txNew2(mtxNew2);

  CMutableTransaction mtxReg1;
  mtxReg1.SetNamecoin ();
  mtxReg1.vout.push_back (CTxOut (COIN, first1));
  const CTransaction txReg1(mtxReg1);
  CMutableTransaction mtxReg2;
  mtxReg2.SetNamecoin ();
  mtxReg2.vout.push_back (CTxOut (COIN, first2));
  const CTransaction txReg2(mtxReg2);

  CMutableTransaction mtxUpd1;
  mtxUpd1.SetNamecoin ();
  mtxUpd1.vout.push_back (CTxOut (COIN, upd1));
  const CTransaction txUpd1(mtxUpd1);
  CMutableTransaction mtxUpd2;
  mtxUpd2.SetNamecoin ();
  mtxUpd2.vout.push_back (CTxOut (COIN, upd2));
  const CTransaction txUpd2(mtxUpd2);

  /* Build an invalid transaction.  It should not crash (assert fail)
     the mempool check.  */

  CMutableTransaction mtxInvalid;
  mtxInvalid.SetNamecoin ();
  mempool.checkNameOps (CTransaction (mtxInvalid));

  mtxInvalid.vout.push_back (CTxOut (COIN, new1));
  mtxInvalid.vout.push_back (CTxOut (COIN, new2));
  mtxInvalid.vout.push_back (CTxOut (COIN, first1));
  mtxInvalid.vout.push_back (CTxOut (COIN, first2));
  mtxInvalid.vout.push_back (CTxOut (COIN, upd1));
  mtxInvalid.vout.push_back (CTxOut (COIN, upd2));
  mempool.checkNameOps (CTransaction (mtxInvalid));

  /* For an empty mempool, all tx should be fine.  */
  BOOST_CHECK (!mempool.registersName (nameReg));
  BOOST_CHECK (!mempool.updatesName (nameUpd));
  BOOST_CHECK (mempool.checkNameOps (txNew1) && mempool.checkNameOps (txNew1p)
                && mempool.checkNameOps (txNew2));
  BOOST_CHECK (mempool.checkNameOps (txReg1) && mempool.checkNameOps (txReg2));
  BOOST_CHECK (mempool.checkNameOps (txUpd1) && mempool.checkNameOps (txUpd2));

  /* Add name_new's with "stealing" check.  */
  const LockPoints lp;
  const CTxMemPoolEntry entryNew1(MakeTransactionRef(txNew1), 0, 0, 100,
                                  false, 1, lp);
  const CTxMemPoolEntry entryNew2(MakeTransactionRef(txNew2), 0, 0, 100,
                                  false, 1, lp);
  BOOST_CHECK (entryNew1.isNameNew () && entryNew2.isNameNew ());
  BOOST_CHECK (entryNew1.getNameNewHash () == vchHash1
                && entryNew2.getNameNewHash () == vchHash2);
  mempool.addUnchecked (entryNew1);
  mempool.addUnchecked (entryNew2);
  BOOST_CHECK (!mempool.checkNameOps (txNew1p));
  BOOST_CHECK (mempool.checkNameOps (txNew1) && mempool.checkNameOps (txNew2));

  /* Add a name registration.  */
  const CTxMemPoolEntry entryReg(MakeTransactionRef(txReg1), 0, 0, 100,
                                 false, 1, lp);
  BOOST_CHECK (entryReg.isNameRegistration () && !entryReg.isNameUpdate ());
  BOOST_CHECK (entryReg.getName () == nameReg);
  mempool.addUnchecked (entryReg);
  BOOST_CHECK (mempool.registersName (nameReg));
  BOOST_CHECK (!mempool.updatesName (nameReg));
  BOOST_CHECK (!mempool.checkNameOps (txReg2) && mempool.checkNameOps (txUpd1));

  /* Add a name update.  */
  const CTxMemPoolEntry entryUpd(MakeTransactionRef(txUpd1), 0, 0, 100,
                                 false, 1, lp);
  BOOST_CHECK (!entryUpd.isNameRegistration () && entryUpd.isNameUpdate ());
  BOOST_CHECK (entryUpd.getName () == nameUpd);
  mempool.addUnchecked (entryUpd);
  BOOST_CHECK (!mempool.registersName (nameUpd));
  BOOST_CHECK (mempool.updatesName (nameUpd));
  BOOST_CHECK (!mempool.checkNameOps (txUpd2));

  /* Check getTxForName.  */
  BOOST_CHECK (mempool.getTxForName (nameReg) == txReg1.GetHash ());
  BOOST_CHECK (mempool.getTxForName (nameUpd) == txUpd1.GetHash ());

  /* Run mempool sanity check.  */
  CCoinsViewCache view(pcoinsTip.get());
  const CNameScript nameOp(upd1);
  CNameData data;
  data.fromScript (100, COutPoint (uint256 (), 0), nameOp);
  view.SetName (nameUpd, data, false);
  mempool.checkNames (&view);

  /* Remove the transactions again.  */

  mempool.removeRecursive (txReg1);
  BOOST_CHECK (!mempool.registersName (nameReg));
  BOOST_CHECK (mempool.checkNameOps (txReg1) && mempool.checkNameOps (txReg2));
  BOOST_CHECK (!mempool.checkNameOps (txUpd2));

  mempool.removeRecursive (txUpd1);
  BOOST_CHECK (!mempool.updatesName (nameUpd));
  BOOST_CHECK (mempool.checkNameOps (txUpd1) && mempool.checkNameOps (txUpd2));
  BOOST_CHECK (mempool.checkNameOps (txReg1));

  mempool.removeRecursive (txNew1);
  mempool.removeRecursive (txNew2);
  BOOST_CHECK (!mempool.checkNameOps (txNew1p));
  BOOST_CHECK (mempool.checkNameOps (txNew1) && mempool.checkNameOps (txNew2));

  /* Check getTxForName with non-existent names.  */
  BOOST_CHECK (mempool.getTxForName (nameReg).IsNull ());
  BOOST_CHECK (mempool.getTxForName (nameUpd).IsNull ());

  /* Check removing of conflicted name registrations.  */

  mempool.addUnchecked (entryReg);
  BOOST_CHECK (mempool.registersName (nameReg));
  BOOST_CHECK (!mempool.checkNameOps (txReg2));

  {
    CNameConflictTracker tracker(mempool);
    mempool.removeConflicts (txReg2);
    BOOST_CHECK (tracker.GetNameConflicts ()->size () == 1);
    BOOST_CHECK (tracker.GetNameConflicts ()->front ()->GetHash ()
                  == txReg1.GetHash ());
  }
  BOOST_CHECK (!mempool.registersName (nameReg));
  BOOST_CHECK (mempool.mapTx.empty ());

  /* Check removing of conflicts after name expiration.  */

  mempool.addUnchecked (entryUpd);
  BOOST_CHECK (mempool.updatesName (nameUpd));
  BOOST_CHECK (!mempool.checkNameOps (txUpd2));

  std::set<valtype> names;
  names.insert (nameUpd);
  {
    CNameConflictTracker tracker(mempool);
    mempool.removeExpireConflicts (names);
    BOOST_CHECK (tracker.GetNameConflicts ()->size () == 1);
    BOOST_CHECK (tracker.GetNameConflicts ()->front ()->GetHash ()
                  == txUpd1.GetHash ());
  }
  BOOST_CHECK (!mempool.updatesName (nameUpd));
  BOOST_CHECK (mempool.mapTx.empty ());

  /* Check removing of conflicts after name unexpiration.  */

  mempool.addUnchecked (entryReg);
  BOOST_CHECK (mempool.registersName (nameReg));
  BOOST_CHECK (!mempool.checkNameOps (txReg2));

  names.clear ();
  names.insert (nameReg);
  {
    CNameConflictTracker tracker(mempool);
    mempool.removeUnexpireConflicts (names);
    BOOST_CHECK (tracker.GetNameConflicts ()->size () == 1);
    BOOST_CHECK (tracker.GetNameConflicts ()->front ()->GetHash ()
                  == txReg1.GetHash ());
  }
  BOOST_CHECK (!mempool.registersName (nameReg));
  BOOST_CHECK (mempool.mapTx.empty ());
}

/* ************************************************************************** */

BOOST_AUTO_TEST_SUITE_END ()
<commit_msg>Split name mempool test into subtests.<commit_after>// Copyright (c) 2014-2019 Daniel Kraft
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include <base58.h>
#include <coins.h>
#include <key_io.h>
#include <names/encoding.h>
#include <names/mempool.h>
#include <primitives/transaction.h>
#include <script/names.h>
#include <sync.h>
#include <test/setup_common.h>
#include <txmempool.h>
#include <validation.h>

#include <boost/test/unit_test.hpp>

/* No space between BOOST_FIXTURE_TEST_SUITE and '(', so that extraction of
   the test-suite name works with grep as done in the Makefile.  */
BOOST_AUTO_TEST_SUITE(name_mempool_tests)

namespace
{

class NameMempoolTestSetup : public TestingSetup
{

public:

  CScript ADDR;
  CScript OTHER_ADDR;

  const LockPoints lp;

  NameMempoolTestSetup ()
  {
    ENTER_CRITICAL_SECTION (mempool.cs);
    mempool.clear ();

    ADDR = CScript () << OP_TRUE;
    OTHER_ADDR = CScript () << OP_TRUE << OP_RETURN;
  }

  ~NameMempoolTestSetup ()
  {
    LEAVE_CRITICAL_SECTION (mempool.cs);
  }

  /**
   * Returns a valtype name based on the given string.
   */
  static valtype
  Name (const std::string& str)
  {
    return DecodeName (str, NameEncoding::ASCII);
  }

  /**
   * Returns the hash bytes for a name_new.
   */
  static valtype
  NewHash (const std::string& nm, const char rand)
  {
    const valtype nameVal = Name (nm);
    const valtype randVal(20, rand);

    valtype toHash(randVal);
    toHash.insert (toHash.end (), nameVal.begin (), nameVal.end ());
    const uint160 hash = Hash160 (toHash);

    return valtype (hash.begin (), hash.end ());
  }

  /**
   * Builds a name new script for the given test name and rand based
   * on a character (just to allow different rand's).
   */
  static CScript
  NewScript (const CScript& addr, const std::string& nm, const char rand)
  {
    const valtype randVal(20, rand);

    return CNameScript::buildNameNew (addr, Name (nm), randVal);
  }

  /**
   * Builds a name_firstupdate script for the given name and rand value.
   * The value we update to is just a fixed one.
   */
  static CScript
  FirstScript (const CScript& addr, const std::string& nm, const char rand)
  {
    const valtype randVal(20, rand);
    const valtype value = DecodeName ("firstupdate value", NameEncoding::ASCII);

    return CNameScript::buildNameFirstupdate (addr, Name (nm), value, randVal);
  }

  /**
   * Builds a name_update script based on the given name and value.
   */
  static CScript
  UpdateScript (const CScript& addr, const std::string& nm,
                const std::string& val)
  {
    const valtype value = DecodeName (val, NameEncoding::ASCII);

    return CNameScript::buildNameUpdate (addr, Name (nm), value);
  }

  /**
   * Builds a transaction spending to a name-output script.  The transaction
   * is not valid, but it is "valid enough" for testing the name mempool
   * rules with it.
   */
  static CTransaction
  Tx (const CScript& out)
  {
    CMutableTransaction mtx;
    mtx.SetNamecoin ();
    mtx.vout.push_back (CTxOut (COIN, out));

    return mtx;
  }

  /**
   * Builds a mempool entry for the given transaction.
   */
  CTxMemPoolEntry
  Entry (const CTransaction& tx)
  {
    return CTxMemPoolEntry (MakeTransactionRef (tx), 0, 0, 100, false, 1, lp);
  }

};

} // anonymous namespace

/* ************************************************************************** */

BOOST_FIXTURE_TEST_CASE (invalid_tx, NameMempoolTestSetup)
{
  /* Invalid transactions should not crash / assert fail the mempool check.  */

  CMutableTransaction mtx;
  mtx.SetNamecoin ();
  mempool.checkNameOps (CTransaction (mtx));

  mtx.vout.push_back (CTxOut (COIN, NewScript (ADDR, "foo", 'a')));
  mtx.vout.push_back (CTxOut (COIN, NewScript (ADDR, "bar", 'b')));
  mtx.vout.push_back (CTxOut (COIN, FirstScript (ADDR, "foo", 'a')));
  mtx.vout.push_back (CTxOut (COIN, FirstScript (ADDR, "bar", 'b')));
  mtx.vout.push_back (CTxOut (COIN, UpdateScript (ADDR, "foo", "x")));
  mtx.vout.push_back (CTxOut (COIN, UpdateScript (ADDR, "bar", "y")));
  mempool.checkNameOps (CTransaction (mtx));
}

BOOST_FIXTURE_TEST_CASE (empty_mempool, NameMempoolTestSetup)
{
  /* While the mempool is empty (we do not add any transactions in this test),
     all should be fine without respect to conflicts among the transactions.  */

  BOOST_CHECK (!mempool.registersName (Name ("foo")));
  BOOST_CHECK (!mempool.updatesName (Name ("foo")));

  BOOST_CHECK (mempool.checkNameOps (Tx (NewScript (ADDR, "foo", 'a'))));
  BOOST_CHECK (mempool.checkNameOps (Tx (NewScript (ADDR, "foo", 'b'))));
  BOOST_CHECK (mempool.checkNameOps (Tx (NewScript (OTHER_ADDR, "foo", 'a'))));

  BOOST_CHECK (mempool.checkNameOps (Tx (FirstScript (ADDR, "foo", 'a'))));
  BOOST_CHECK (mempool.checkNameOps (Tx (FirstScript (ADDR, "foo", 'b'))));

  BOOST_CHECK (mempool.checkNameOps (Tx (UpdateScript (ADDR, "foo", "x"))));
  BOOST_CHECK (mempool.checkNameOps (Tx (UpdateScript (ADDR, "foo", "y"))));
}

BOOST_FIXTURE_TEST_CASE (name_new, NameMempoolTestSetup)
{
  const auto tx1 = Tx (NewScript (ADDR, "foo", 'a'));
  const auto tx1p = Tx (NewScript (OTHER_ADDR, "foo", 'a'));
  const auto tx2 = Tx (NewScript (ADDR, "foo", 'b'));

  const auto e1 = Entry (tx1);
  const auto e2 = Entry (tx2);
  BOOST_CHECK (e1.isNameNew () && e2.isNameNew ());
  BOOST_CHECK (e1.getNameNewHash () == NewHash ("foo", 'a'));
  BOOST_CHECK (e2.getNameNewHash () == NewHash ("foo", 'b'));

  mempool.addUnchecked (e1);
  mempool.addUnchecked (e2);
  BOOST_CHECK (mempool.checkNameOps (tx1));
  BOOST_CHECK (mempool.checkNameOps (tx2));
  BOOST_CHECK (!mempool.checkNameOps (tx1p));

  mempool.removeRecursive (tx1);
  mempool.removeRecursive (tx2);
  BOOST_CHECK (mempool.checkNameOps (tx1));
  BOOST_CHECK (mempool.checkNameOps (tx2));
  BOOST_CHECK (!mempool.checkNameOps (tx1p));
}

BOOST_FIXTURE_TEST_CASE (name_firstupdate, NameMempoolTestSetup)
{
  const auto tx1 = Tx (FirstScript (ADDR, "foo", 'a'));
  const auto tx2 = Tx (FirstScript (ADDR, "foo", 'b'));

  const auto e = Entry (tx1);
  BOOST_CHECK (e.isNameRegistration () && !e.isNameUpdate ());
  BOOST_CHECK (e.getName () == Name ("foo"));

  mempool.addUnchecked (e);
  BOOST_CHECK (mempool.registersName (Name ("foo")));
  BOOST_CHECK (!mempool.updatesName (Name ("foo")));
  BOOST_CHECK (!mempool.checkNameOps (tx2));

  mempool.removeRecursive (tx1);
  BOOST_CHECK (!mempool.registersName (Name ("foo")));
  BOOST_CHECK (mempool.checkNameOps (tx1));
  BOOST_CHECK (mempool.checkNameOps (tx2));
}

BOOST_FIXTURE_TEST_CASE (name_update, NameMempoolTestSetup)
{
  const auto tx1 = Tx (UpdateScript (ADDR, "foo", "x"));
  const auto tx2 = Tx (UpdateScript (ADDR, "foo", "y"));

  const auto e = Entry (tx1);
  BOOST_CHECK (!e.isNameRegistration () && e.isNameUpdate ());
  BOOST_CHECK (e.getName () == Name ("foo"));

  mempool.addUnchecked (e);
  BOOST_CHECK (!mempool.registersName (Name ("foo")));
  BOOST_CHECK (mempool.updatesName (Name ("foo")));
  BOOST_CHECK (!mempool.checkNameOps (tx2));

  mempool.removeRecursive (tx1);
  BOOST_CHECK (!mempool.updatesName (Name ("foo")));
  BOOST_CHECK (mempool.checkNameOps (tx1));
  BOOST_CHECK (mempool.checkNameOps (tx2));
}

BOOST_FIXTURE_TEST_CASE (getTxForName, NameMempoolTestSetup)
{
  BOOST_CHECK (mempool.getTxForName (Name ("new")).IsNull ());
  BOOST_CHECK (mempool.getTxForName (Name ("reg")).IsNull ());
  BOOST_CHECK (mempool.getTxForName (Name ("upd")).IsNull ());

  const auto txReg = Tx (FirstScript (ADDR, "reg", 'a'));
  const auto txUpd = Tx (UpdateScript (ADDR, "upd", "x"));

  mempool.addUnchecked (Entry (Tx (NewScript (ADDR, "new", 'a'))));
  mempool.addUnchecked (Entry (txReg));
  mempool.addUnchecked (Entry (txUpd));

  BOOST_CHECK (mempool.getTxForName (Name ("new")).IsNull ());
  BOOST_CHECK (mempool.getTxForName (Name ("reg")) == txReg.GetHash ());
  BOOST_CHECK (mempool.getTxForName (Name ("upd")) == txUpd.GetHash ());
}

BOOST_FIXTURE_TEST_CASE (mempool_sanity_check, NameMempoolTestSetup)
{
  mempool.addUnchecked (Entry (Tx (NewScript (ADDR, "new", 'a'))));
  mempool.addUnchecked (Entry (Tx (NewScript (ADDR, "new", 'b'))));

  mempool.addUnchecked (Entry (Tx (FirstScript (ADDR, "reg", 'a'))));
  mempool.addUnchecked (Entry (Tx (UpdateScript (ADDR, "upd", "x"))));

  CCoinsViewCache view(pcoinsTip.get());
  const CNameScript nameOp(UpdateScript (ADDR, "upd", "y"));
  CNameData data;
  data.fromScript (100, COutPoint (uint256 (), 0), nameOp);
  view.SetName (Name ("upd"), data, false);
  mempool.checkNames (&view);
}

BOOST_FIXTURE_TEST_CASE (registration_conflicts, NameMempoolTestSetup)
{
  const auto tx1 = Tx (FirstScript (ADDR, "foo", 'a'));
  const auto tx2 = Tx (FirstScript (ADDR, "foo", 'b'));
  const auto e = Entry (tx1);

  mempool.addUnchecked (e);
  BOOST_CHECK (mempool.registersName (Name ("foo")));
  BOOST_CHECK (!mempool.checkNameOps (tx2));

  CNameConflictTracker tracker(mempool);
  mempool.removeConflicts (tx2);
  BOOST_CHECK (tracker.GetNameConflicts ()->size () == 1);
  BOOST_CHECK (tracker.GetNameConflicts ()->front ()->GetHash ()
                == tx1.GetHash ());

  BOOST_CHECK (!mempool.registersName (Name ("foo")));
  BOOST_CHECK (mempool.checkNameOps (tx1));
  BOOST_CHECK (mempool.checkNameOps (tx2));
  BOOST_CHECK (mempool.mapTx.empty ());
}

BOOST_FIXTURE_TEST_CASE (expire_conflicts, NameMempoolTestSetup)
{
  const auto tx = Tx (UpdateScript (ADDR, "foo", "x"));
  const auto e = Entry (tx);

  mempool.addUnchecked (e);
  BOOST_CHECK (mempool.updatesName (Name ("foo")));

  CNameConflictTracker tracker(mempool);
  mempool.removeExpireConflicts ({Name ("foo")});
  BOOST_CHECK (tracker.GetNameConflicts ()->size () == 1);
  BOOST_CHECK (tracker.GetNameConflicts ()->front ()->GetHash ()
                == tx.GetHash ());

  BOOST_CHECK (!mempool.updatesName (Name ("foo")));
  BOOST_CHECK (mempool.mapTx.empty ());
}

BOOST_FIXTURE_TEST_CASE (unexpire_conflicts, NameMempoolTestSetup)
{
  const auto tx = Tx (FirstScript (ADDR, "foo", 'a'));
  const auto e = Entry (tx);

  mempool.addUnchecked (e);
  BOOST_CHECK (mempool.registersName (Name ("foo")));

  CNameConflictTracker tracker(mempool);
  mempool.removeUnexpireConflicts ({Name ("foo")});
  BOOST_CHECK (tracker.GetNameConflicts ()->size () == 1);
  BOOST_CHECK (tracker.GetNameConflicts ()->front ()->GetHash ()
                == tx.GetHash ());

  BOOST_CHECK (!mempool.registersName (Name ("foo")));
  BOOST_CHECK (mempool.mapTx.empty ());
}

/* ************************************************************************** */

BOOST_AUTO_TEST_SUITE_END ()
<|endoftext|>


<commit_before>/*
Copyright (c) 2019, Dimitri Diakopoulos 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.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#include "Decoders.h"
#include "wavpack.h"
#include <string.h>
#include <cstring>

using namespace nqr;

class WavPackInternal
{

public:

    WavPackInternal(AudioData * d, const std::string & path) : d(d)
    {
        char errorStr[128];
        context = WavpackOpenFileInput(path.c_str(), errorStr, OPEN_WVC | OPEN_NORMALIZE, 0);

        if (!context) throw std::runtime_error("Not a WavPack file");

        auto bitdepth = WavpackGetBitsPerSample(context);

        d->sampleRate = WavpackGetSampleRate(context);
        d->channelCount = WavpackGetNumChannels(context);
        d->lengthSeconds = double(getLengthInSeconds());
        d->frameSize = d->channelCount * bitdepth;

        //@todo support channel masks
        // WavpackGetChannelMask

        auto totalSamples = size_t(getTotalSamples());

        int mode = WavpackGetMode(context);
        bool isFloatingPoint = (MODE_FLOAT & mode);

        d->sourceFormat = MakeFormatForBits(bitdepth, isFloatingPoint, false);

        /// From the  WavPack docs:
        /// "... required memory at "buffer" is 4 * samples * num_channels bytes. The
        /// audio data is returned right-justified in 32-bit longs in the endian
        /// mode native to the executing processor."
        d->samples.resize(totalSamples * d->channelCount);

        if (!isFloatingPoint)
            internalBuffer.resize(totalSamples * d->channelCount);

        if (!readInternal(totalSamples))
            throw std::runtime_error("could not read any data");

        // Next, process internal buffer into the user-visible samples array
        if (!isFloatingPoint)
            ConvertToFloat32(d->samples.data(), internalBuffer.data(), totalSamples * d->channelCount, d->sourceFormat);

    }

    WavPackInternal(AudioData * d, const std::vector<uint8_t> & memory) : d(d), data(std::move(memory)), dataPos(0)
    {
        WavpackStreamReader64 reader =
        {
            read_bytes,
            write_bytes,
            get_pos,
            set_pos_abs,
            set_pos_rel,
            push_back_byte,
            get_length,
            can_seek,
            truncate_here,
            close,
        };

        char errorStr[128];
        context = WavpackOpenFileInputEx64(&reader, this, nullptr, errorStr, OPEN_WVC | OPEN_NORMALIZE, 0);

        if (!context)
        {
            throw std::runtime_error("Not a WavPack file");
        }

        auto bitdepth = WavpackGetBitsPerSample(context);

        d->sampleRate = WavpackGetSampleRate(context);
        d->channelCount = WavpackGetNumChannels(context);
        d->lengthSeconds = double(getLengthInSeconds());
        d->frameSize = d->channelCount * bitdepth;

        //@todo support channel masks
        // WavpackGetChannelMask

        auto totalSamples = size_t(getTotalSamples());

        int mode = WavpackGetMode(context);
        bool isFloatingPoint = (MODE_FLOAT & mode);

        d->sourceFormat = MakeFormatForBits(bitdepth, isFloatingPoint, false);

        d->samples.resize(totalSamples * d->channelCount);

        if (!isFloatingPoint)
            internalBuffer.resize(totalSamples * d->channelCount);

        if (!readInternal(totalSamples))
            throw std::runtime_error("could not read any data");

        // Next, process internal buffer into the user-visible samples array
        if (!isFloatingPoint)
            ConvertToFloat32(d->samples.data(), internalBuffer.data(), totalSamples * d->channelCount, d->sourceFormat);

    }

    ~WavPackInternal()
    {
        WavpackCloseFile(context);
    }

    size_t readInternal(size_t requestedFrameCount, size_t frameOffset = 0)
    {
        size_t framesRemaining = requestedFrameCount;
        size_t totalFramesRead = 0;

        // The samples returned are handled differently based on the file's mode
        int mode = WavpackGetMode(context);

        while (0 < framesRemaining)
        {
            uint32_t framesRead = -1;

            if (MODE_FLOAT & mode)
            {
                // Since it's float, we can decode directly into our buffer as a huge blob
                framesRead = WavpackUnpackSamples(context, reinterpret_cast<int32_t*>(&d->samples.data()[0]), uint32_t(d->samples.size() / d->channelCount));
            }

            else if(MODE_LOSSLESS & mode)
            {
                // Lossless files will be handed off as integers
                framesRead = WavpackUnpackSamples(context, internalBuffer.data(), uint32_t(internalBuffer.size() / d->channelCount));
            }

            // EOF
            //if (framesRead == 0) break;

            totalFramesRead += framesRead;
            framesRemaining -= framesRead;
        }

        return totalFramesRead;
    }

    static int32_t read_bytes(void * id, void * data, int32_t byte_count)
    {
        if (id != nullptr)
        {
            WavPackInternal *decoder = (WavPackInternal *)id;
            auto readLength = std::min<size_t>(byte_count, decoder->data.size() - decoder->dataPos);
            if (readLength > 0)
            {
                std::memcpy(data, decoder->data.data(), readLength);
                decoder->dataPos += readLength;
                return readLength;
            }
            else return 0;
        }
        return 0;
    }
    static int32_t write_bytes(void * id, void * data, int32_t byte_count)
    {
        if (id != nullptr)
        {
            WavPackInternal *decoder = (WavPackInternal *)id;
            auto writeLength = std::min<size_t>(byte_count, decoder->data.size() - decoder->dataPos);
            if (writeLength > 0)
            {
                std::memcpy(decoder->data.data(), data, writeLength);
                decoder->dataPos += writeLength;
                return writeLength;
            }
            else return 0;
        }
        return 0;
    }
    static int64_t get_pos(void *id)
    {
        if (id != nullptr)
        {
            WavPackInternal *decoder = (WavPackInternal *)id;
            return decoder->dataPos;
        }
        return 0;
    }
    static int set_pos_abs(void *id, int64_t pos)
    {
        if (id != nullptr)
        {
            WavPackInternal *decoder = (WavPackInternal *)id;
            size_t newPos = std::min<size_t>(pos, decoder->data.size());
            decoder->dataPos = newPos;
            return newPos;
        }
        return 0;
    }
    static int set_pos_rel(void *id, int64_t delta, int mode)
    {
        if (id != nullptr)
        {
            WavPackInternal *decoder = (WavPackInternal *)id;
            size_t newPos = 0;
            if (mode == SEEK_SET) newPos = delta;
            else if (mode == SEEK_CUR) newPos = decoder->dataPos + delta;
            else if (mode == SEEK_END) newPos = decoder->data.size() + delta;
            newPos = std::min<size_t>(newPos, decoder->data.size());
            decoder->dataPos = newPos;
            return newPos;
        }
        return 0;
    }
    static int push_back_byte(void *id, int c)
    {
        if (id != nullptr)
        {
            WavPackInternal *decoder = (WavPackInternal *)id;
            decoder->dataPos--;
            decoder->data[decoder->dataPos] = c;
            return 1;
        }
        return 0;
    }
    static int64_t get_length(void *id)
    {
        if (id != nullptr)
        {
            WavPackInternal *decoder = (WavPackInternal *)id;
            return decoder->data.size();
        }
        return 0;
    }
    static int can_seek(void *id)
    {
        if (id != nullptr) return 1;
        return 0;
    }

    static int truncate_here(void *id)
    {
        if (id != nullptr)
        {
            WavPackInternal *decoder = (WavPackInternal *)id;
            decoder->data.resize(decoder->dataPos);
            return 1;
        }
        return 0;
    }
    static int close(void *id)
    {
        if (id != nullptr) return 1;
        return 0;
    }

private:

    NO_MOVE(WavPackInternal);

    //WavpackStreamReader streamReader; //@todo: streaming support

    WavpackContext * context; //@todo unique_ptr

    AudioData * d;
    std::vector<uint8_t> data;
    size_t dataPos;

    std::vector<int32_t> internalBuffer;

    inline int64_t getTotalSamples() const { return WavpackGetNumSamples(context); }
    inline int64_t getLengthInSeconds() const { return getTotalSamples() / WavpackGetSampleRate(context); }

};

//////////////////////
// Public Interface //
//////////////////////

void WavPackDecoder::LoadFromPath(AudioData * data, const std::string & path)
{
    WavPackInternal decoder(data, path);
}

void WavPackDecoder::LoadFromBuffer(AudioData * data, const std::vector<uint8_t> & memory)
{
    WavPackInternal decoder(data, memory);
}

std::vector<std::string> WavPackDecoder::GetSupportedFileExtensions()
{
    return {"wv"};
}
<commit_msg>Load in-memory WavPack files using WavpackOpenRawDecoder function<commit_after>/*
Copyright (c) 2019, Dimitri Diakopoulos 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.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#include "Decoders.h"
#include "wavpack.h"
#include <string.h>
#include <cstring>

using namespace nqr;

class WavPackInternal
{

public:

    WavPackInternal(AudioData * d, const std::string & path) : d(d)
    {
        char errorStr[128];
        context = WavpackOpenFileInput(path.c_str(), errorStr, OPEN_WVC | OPEN_NORMALIZE, 0);

        if (!context) throw std::runtime_error("Not a WavPack file");

        auto totalSamples = size_t(WavpackGetNumSamples(context));

        decode(totalSamples);
    }

    WavPackInternal(AudioData * d, const std::vector<uint8_t> & memory) : d(d)
    {
        char errorStr[128];
        context = WavpackOpenRawDecoder((void *) memory.data(), memory.size(), nullptr, 0, 0, errorStr, OPEN_WVC | OPEN_NORMALIZE, 0);

        // Since we are using OpenRawDecoder, WavpackGetNumSamples won't work.
        // Instead, find the first block and get totalSamples from its header.
        WavpackHeader wph;
        auto headerOffset = readNextHeader(memory, &wph, 0);

        if (!context || headerOffset == -1)
        {
            throw std::runtime_error("Not a WavPack file");
        }

        auto totalSamples = wph.total_samples;

        decode(totalSamples);
    }

    ~WavPackInternal()
    {
        WavpackCloseFile(context);
    }

    size_t readInternal(size_t requestedFrameCount, size_t frameOffset = 0)
    {
        size_t framesRemaining = requestedFrameCount;
        size_t totalFramesRead = 0;

        // The samples returned are handled differently based on the file's mode
        int mode = WavpackGetMode(context);

        while (0 < framesRemaining)
        {
            uint32_t framesRead = -1;

            if (MODE_FLOAT & mode)
            {
                // Since it's float, we can decode directly into our buffer as a huge blob
                framesRead = WavpackUnpackSamples(context, reinterpret_cast<int32_t*>(&d->samples.data()[0]), uint32_t(d->samples.size() / d->channelCount));
            }

            else if(MODE_LOSSLESS & mode)
            {
                // Lossless files will be handed off as integers
                framesRead = WavpackUnpackSamples(context, internalBuffer.data(), uint32_t(internalBuffer.size() / d->channelCount));
            }

            // EOF
            //if (framesRead == 0) break;

            totalFramesRead += framesRead;
            framesRemaining -= framesRead;
        }

        return totalFramesRead;
    }

private:
    void decode(size_t totalSamples) {
        auto bitdepth = WavpackGetBitsPerSample(context);

        d->sampleRate = WavpackGetSampleRate(context);
        d->channelCount = WavpackGetNumChannels(context);
        d->lengthSeconds = double(totalSamples / WavpackGetSampleRate(context));
        d->frameSize = d->channelCount * bitdepth;

        //@todo support channel masks
        // WavpackGetChannelMask

        int mode = WavpackGetMode(context);
        bool isFloatingPoint = (MODE_FLOAT & mode);

        d->sourceFormat = MakeFormatForBits(bitdepth, isFloatingPoint, false);

        d->samples.resize(totalSamples * d->channelCount);

        if (!isFloatingPoint)
            internalBuffer.resize(totalSamples * d->channelCount);

        if (!readInternal(totalSamples))
            throw std::runtime_error("could not read any data");

        // Next, process internal buffer into the user-visible samples array
        if (!isFloatingPoint)
            ConvertToFloat32(d->samples.data(), internalBuffer.data(), totalSamples * d->channelCount, d->sourceFormat);
    }

    int64_t readNextHeader(const std::vector<uint8_t> & memory, WavpackHeader *wphdr, size_t startOffset) {
        /// Based on read_next_header function in wavpack's openutils.c.
        /// This will find the position of the next WavPack header in the given vector, at or after startOffset.
        /// If a header is found, it will write the header to *wphdr and return the position of its first byte in the vector.
        /// Otherwise, it will return -1.
        unsigned char* sp;

        for (size_t i = startOffset; i < memory.size(); i++) {
            sp = const_cast<unsigned char *>(memory.data() + i);

            auto headerStartPoint = sp;

            if (*sp++ == 'w' && *sp == 'v' && *++sp == 'p' && *++sp == 'k' &&
                !(*++sp & 1) && sp [2] < 16 && !sp [3] && (sp [2] || sp [1] || *sp >= 24) && sp [5] == 4 &&
                sp [4] >= (MIN_STREAM_VERS & 0xff) && sp [4] <= (MAX_STREAM_VERS & 0xff) && sp [18] < 3 && !sp [19]) {

                memcpy (wphdr, headerStartPoint, sizeof (*wphdr));
                WavpackLittleEndianToNative (wphdr, (char*)WavpackHeaderFormat);
                return i;
            }
        }

        return -1;
    }

    NO_MOVE(WavPackInternal);

    //WavpackStreamReader streamReader; //@todo: streaming support

    WavpackContext * context; //@todo unique_ptr

    AudioData * d;

    std::vector<int32_t> internalBuffer;

    inline int64_t getTotalSamples() const { return WavpackGetNumSamples(context); }
    inline int64_t getLengthInSeconds() const { return getTotalSamples() / WavpackGetSampleRate(context); }

};

//////////////////////
// Public Interface //
//////////////////////

void WavPackDecoder::LoadFromPath(AudioData * data, const std::string & path)
{
    WavPackInternal decoder(data, path);
}

void WavPackDecoder::LoadFromBuffer(AudioData * data, const std::vector<uint8_t> & memory)
{
    WavPackInternal decoder(data, memory);
}

std::vector<std::string> WavPackDecoder::GetSupportedFileExtensions()
{
    return {"wv"};
}
<|endoftext|>


<commit_before>/*
 * The Apache Software License, Version 1.1
 *
 *
 * Copyright (c) 2000 The Apache Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. 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.
 *
 * 3. The end-user documentation included with the redistribution,
 *    if any, must include the following acknowledgment:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowledgment may appear in the software itself,
 *    if and wherever such third-party acknowledgments normally appear.
 *
 * 4. The names "Xalan" and "Apache Software Foundation" must
 *    not be used to endorse or promote products derived from this
 *    software without prior written permission. For written
 *    permission, please contact apache@apache.org.
 *
 * 5. Products derived from this software may not be called "Apache",
 *    nor may "Apache" appear in their name, without prior written
 *    permission of the Apache Software Foundation.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation and was
 * originally based on software copyright (c) 1999, International
 * Business Machines, Inc., http://www.ibm.com.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 */

// Class header file...
#include "Function.hpp"

#include "XObjectFactory.hpp"

Function::Function()
{
}

Function::~Function()
{
}

XObjectPtr
Function::execute(
			XPathExecutionContext&			executionContext,
			XalanNode*						context,
			int								/* opPos */,
			const XObjectArgVectorType&		/* args */)
{
	executionContext.error(getError(), context);

	return XObjectPtr(0);
}

XObjectPtr
Function::execute(
			XPathExecutionContext&	executionContext,
			XalanNode*				context)
{
	executionContext.error(getError(), context);

	return XObjectPtr(0);
}

XObjectPtr
Function::execute(
			XPathExecutionContext&	executionContext,
			XalanNode*				context,
			const XObjectPtr		/* arg1 */)
{
	executionContext.error(getError(), context);

	return XObjectPtr(0);
}

XObjectPtr
Function::execute(
			XPathExecutionContext&	executionContext,
			XalanNode*				context,
			const XObjectPtr		/* arg1 */,
			const XObjectPtr		/* arg2 */)
{
	executionContext.error(getError(), context);

	return XObjectPtr(0);
}

XObjectPtr
Function::execute(
			XPathExecutionContext&	executionContext,
			XalanNode*				context,
			const XObjectPtr		/* arg1 */,
			const XObjectPtr		/* arg2 */,
			const XObjectPtr		/* arg3 */)
{
	executionContext.error(getError(), context);

	return XObjectPtr(0);
}

const XalanDOMString
Function::getError() const
{
	return XALAN_STATIC_UCODE_STRING("Unknown function called.");
}
<commit_msg>Changed error message.<commit_after>/*
 * The Apache Software License, Version 1.1
 *
 *
 * Copyright (c) 2000 The Apache Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. 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.
 *
 * 3. The end-user documentation included with the redistribution,
 *    if any, must include the following acknowledgment:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowledgment may appear in the software itself,
 *    if and wherever such third-party acknowledgments normally appear.
 *
 * 4. The names "Xalan" and "Apache Software Foundation" must
 *    not be used to endorse or promote products derived from this
 *    software without prior written permission. For written
 *    permission, please contact apache@apache.org.
 *
 * 5. Products derived from this software may not be called "Apache",
 *    nor may "Apache" appear in their name, without prior written
 *    permission of the Apache Software Foundation.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation and was
 * originally based on software copyright (c) 1999, International
 * Business Machines, Inc., http://www.ibm.com.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 */

// Class header file...
#include "Function.hpp"

#include "XObjectFactory.hpp"

Function::Function()
{
}

Function::~Function()
{
}

XObjectPtr
Function::execute(
			XPathExecutionContext&			executionContext,
			XalanNode*						context,
			int								/* opPos */,
			const XObjectArgVectorType&		/* args */)
{
	executionContext.error(getError(), context);

	return XObjectPtr(0);
}

XObjectPtr
Function::execute(
			XPathExecutionContext&	executionContext,
			XalanNode*				context)
{
	executionContext.error(getError(), context);

	return XObjectPtr(0);
}

XObjectPtr
Function::execute(
			XPathExecutionContext&	executionContext,
			XalanNode*				context,
			const XObjectPtr		/* arg1 */)
{
	executionContext.error(getError(), context);

	return XObjectPtr(0);
}

XObjectPtr
Function::execute(
			XPathExecutionContext&	executionContext,
			XalanNode*				context,
			const XObjectPtr		/* arg1 */,
			const XObjectPtr		/* arg2 */)
{
	executionContext.error(getError(), context);

	return XObjectPtr(0);
}

XObjectPtr
Function::execute(
			XPathExecutionContext&	executionContext,
			XalanNode*				context,
			const XObjectPtr		/* arg1 */,
			const XObjectPtr		/* arg2 */,
			const XObjectPtr		/* arg3 */)
{
	executionContext.error(getError(), context);

	return XObjectPtr(0);
}

const XalanDOMString
Function::getError() const
{
	return XALAN_STATIC_UCODE_STRING("Invalid number of arguments for function!");
}
<|endoftext|>


<commit_before>/*****************************************************************************
 *
 * This file is part of Mapnik (c++ mapping toolkit)
 *
 * Copyright (C) 2011 Artem Pavlenko
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 *****************************************************************************/
#ifndef MAPNIK_PYTHON_BINDING_VALUE_CONVERTER_INCLUDED
#define MAPNIK_PYTHON_BINDING_VALUE_CONVERTER_INCLUDED

// boost
#include <boost/python.hpp>
#include <boost/implicit_cast.hpp>

namespace boost { namespace python {

    struct value_converter : public boost::static_visitor<PyObject*>
    {
        PyObject * operator() (int val) const
        {
#if PY_VERSION_HEX >= 0x03000000
            return ::PyLong_FromLong(val);
#else
            return ::PyInt_FromLong(val);
#endif
        }

        PyObject * operator() (double val) const
        {
            return ::PyFloat_FromDouble(val);
        }

        PyObject * operator() (bool val) const
        {
            return ::PyBool_FromLong(val);
        }

        PyObject * operator() (std::string const& s) const
        {
            PyObject *obj = Py_None;
            obj = ::PyUnicode_DecodeUTF8(s.c_str(),implicit_cast<ssize_t>(s.length()),0);
            return obj;
        }

        PyObject * operator() (UnicodeString const& s) const
        {
            std::string buffer;
            mapnik::to_utf8(s,buffer);
            PyObject *obj = Py_None;
            obj = ::PyUnicode_DecodeUTF8(buffer.c_str(),implicit_cast<ssize_t>(buffer.length()),0);
            return obj;
        }

        PyObject * operator() (mapnik::value_null const& /*s*/) const
        {
            Py_INCREF(Py_None);
            return Py_None;
        }
    };

    struct mapnik_value_to_python
    {
        static PyObject* convert(mapnik::value const& v)
        {
            return boost::apply_visitor(value_converter(),v.base());
        }

    };

    struct mapnik_param_to_python
    {
        static PyObject* convert(mapnik::value_holder const& v)
        {
            return boost::apply_visitor(value_converter(),v);
        }
    };

    }
}

#endif // MAPNIK_PYTHON_BINDING_VALUE_CONVERTER_INCLUDED
<commit_msg>+ use Py_RETURN_NONE + cleanup/simplify<commit_after>/*****************************************************************************
 *
 * This file is part of Mapnik (c++ mapping toolkit)
 *
 * Copyright (C) 2011 Artem Pavlenko
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 *****************************************************************************/
#ifndef MAPNIK_PYTHON_BINDING_VALUE_CONVERTER_INCLUDED
#define MAPNIK_PYTHON_BINDING_VALUE_CONVERTER_INCLUDED

// boost
#include <boost/python.hpp>
#include <boost/implicit_cast.hpp>

namespace boost { namespace python {

    struct value_converter : public boost::static_visitor<PyObject*>
    {
        PyObject * operator() (int val) const
        {
#if PY_VERSION_HEX >= 0x03000000
            return ::PyLong_FromLong(val);
#else
            return ::PyInt_FromLong(val);
#endif
        }

        PyObject * operator() (double val) const
        {
            return ::PyFloat_FromDouble(val);
        }

        PyObject * operator() (bool val) const
        {
            return ::PyBool_FromLong(val);
        }

        PyObject * operator() (std::string const& s) const
        {
            return ::PyUnicode_DecodeUTF8(s.c_str(),implicit_cast<ssize_t>(s.length()),0);
        }

        PyObject * operator() (UnicodeString const& s) const
        {
            std::string buffer;
            mapnik::to_utf8(s,buffer);
            return ::PyUnicode_DecodeUTF8(buffer.c_str(),implicit_cast<ssize_t>(buffer.length()),0);
        }

        PyObject * operator() (mapnik::value_null const& /*s*/) const
        {
            Py_RETURN_NONE;
        }
    };

    struct mapnik_value_to_python
    {
        static PyObject* convert(mapnik::value const& v)
        {
            return boost::apply_visitor(value_converter(),v.base());
        }

    };

    struct mapnik_param_to_python
    {
        static PyObject* convert(mapnik::value_holder const& v)
        {
            return boost::apply_visitor(value_converter(),v);
        }
    };

    }
}

#endif // MAPNIK_PYTHON_BINDING_VALUE_CONVERTER_INCLUDED
<|endoftext|>


<commit_before>// Copyright (c) 2017 The PIVX developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "accumulatormap.h"
#include "accumulators.h"
#include "main.h"
#include "txdb.h"
#include "libzerocoin/Denominations.h"

using namespace libzerocoin;
using namespace std;

AccumulatorMap::AccumulatorMap()
{
    //construct accumulators for all denominations
    for (auto& denom : zerocoinDenomList) {
        unique_ptr<Accumulator> uptr(new Accumulator(Params().Zerocoin_Params(), denom));
        mapAccumulators.insert(make_pair(denom, std::move(uptr)));
    }
}

void AccumulatorMap::Reset()
{
    mapAccumulators.clear();
    for (auto& denom : zerocoinDenomList) {
        unique_ptr<Accumulator> uptr(new Accumulator(Params().Zerocoin_Params(), denom));
        mapAccumulators.insert(make_pair(denom, std::move(uptr)));
    }
}

bool AccumulatorMap::Load(uint256 nCheckpoint)
{
    for (auto& denom : zerocoinDenomList) {
        uint32_t nChecksum = ParseChecksum(nCheckpoint, denom);

        CBigNum bnValue;
        if (!zerocoinDB->ReadAccumulatorValue(nChecksum, bnValue)) {
            LogPrintf("%s : cannot find checksum %d", __func__, nChecksum);
            return false;
        }

        mapAccumulators.at(denom)->setValue(bnValue);
    }
    return true;
}

bool AccumulatorMap::Accumulate(PublicCoin pubCoin, bool fSkipValidation)
{
    CoinDenomination denom = pubCoin.getDenomination();
    if (denom == CoinDenomination::ZQ_ERROR)
        return false;

    if (fSkipValidation)
        mapAccumulators.at(denom)->increment(pubCoin.getValue());
    else
        mapAccumulators.at(denom)->accumulate(pubCoin);
    return true;
}

CBigNum AccumulatorMap::GetValue(CoinDenomination denom)
{
    if (denom == CoinDenomination::ZQ_ERROR)
        return CBigNum(0);
    return mapAccumulators.at(denom)->getValue();
}

uint256 AccumulatorMap::GetCheckpoint()
{
    uint256 nCheckpoint;
    for (auto& denom : zerocoinDenomList) {
        CBigNum bnValue = mapAccumulators.at(denom)->getValue();
        uint32_t nCheckSum = GetChecksum(bnValue);
        nCheckpoint = nCheckpoint << 32 | nCheckSum;
    }

    return nCheckpoint;
}

<commit_msg>Assert that zerocoinDenomList is size 8.<commit_after>// Copyright (c) 2017 The PIVX developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "accumulatormap.h"
#include "accumulators.h"
#include "main.h"
#include "txdb.h"
#include "libzerocoin/Denominations.h"

using namespace libzerocoin;
using namespace std;

AccumulatorMap::AccumulatorMap()
{
    //construct accumulators for all denominations
    for (auto& denom : zerocoinDenomList) {
        unique_ptr<Accumulator> uptr(new Accumulator(Params().Zerocoin_Params(), denom));
        mapAccumulators.insert(make_pair(denom, std::move(uptr)));
    }
}

void AccumulatorMap::Reset()
{
    mapAccumulators.clear();
    for (auto& denom : zerocoinDenomList) {
        unique_ptr<Accumulator> uptr(new Accumulator(Params().Zerocoin_Params(), denom));
        mapAccumulators.insert(make_pair(denom, std::move(uptr)));
    }
}

bool AccumulatorMap::Load(uint256 nCheckpoint)
{
    for (auto& denom : zerocoinDenomList) {
        uint32_t nChecksum = ParseChecksum(nCheckpoint, denom);

        CBigNum bnValue;
        if (!zerocoinDB->ReadAccumulatorValue(nChecksum, bnValue)) {
            LogPrintf("%s : cannot find checksum %d", __func__, nChecksum);
            return false;
        }

        mapAccumulators.at(denom)->setValue(bnValue);
    }
    return true;
}

bool AccumulatorMap::Accumulate(PublicCoin pubCoin, bool fSkipValidation)
{
    CoinDenomination denom = pubCoin.getDenomination();
    if (denom == CoinDenomination::ZQ_ERROR)
        return false;

    if (fSkipValidation)
        mapAccumulators.at(denom)->increment(pubCoin.getValue());
    else
        mapAccumulators.at(denom)->accumulate(pubCoin);
    return true;
}

CBigNum AccumulatorMap::GetValue(CoinDenomination denom)
{
    if (denom == CoinDenomination::ZQ_ERROR)
        return CBigNum(0);
    return mapAccumulators.at(denom)->getValue();
}

uint256 AccumulatorMap::GetCheckpoint()
{
    uint256 nCheckpoint;

    //Prevent possible overflows from future changes to the list and forgetting to update this code
    assert(zerocoinDenomList.size() == 8);
    for (auto& denom : zerocoinDenomList) {
        CBigNum bnValue = mapAccumulators.at(denom)->getValue();
        uint32_t nCheckSum = GetChecksum(bnValue);
        nCheckpoint = nCheckpoint << 32 | nCheckSum;
    }

    return nCheckpoint;
}

<|endoftext|>


<commit_before>#include "ExampleGame/Components/GameScripts/Units/Assassin.h"
#include "ExampleGame/Components/Grid/GameGrid.h"
#include "ExampleGame/Components/Grid/GridCell.h"
#include "ExampleGame/Components/Grid/GridManager.h"
#include "ExampleGame/Components/Grid/GridNavigator.h"
#include "ExampleGame/GameConstants/GameConstants.h"
#include "ExampleGame/GameSingletons/GameSingletons.h"
#include "ExampleGame/Messages/Declarations.h"

#include "Vajra/Engine/Components/DerivedComponents/Renderer/SpriteRenderer.h"
#include "Vajra/Engine/Components/DerivedComponents/Transform/Transform.h"
#include "Vajra/Engine/Core/Engine.h"
#include "Vajra/Engine/Input/Input.h"
#include "Vajra/Engine/MessageHub/MessageHub.h"
#include "Vajra/Engine/SceneGraph/SceneGraph3D.h"
#include "Vajra/Framework/DeviceUtils/FileSystemUtils/FileSystemUtils.h"

Assassin::Assassin() : PlayerUnit() {
	this->init();
}

Assassin::Assassin(Object* object_) : PlayerUnit(object_) {
	this->init();
}

Assassin::~Assassin() {
	this->destroy();
}

void Assassin::init() {
	this->unitType = UnitType::UNIT_TYPE_ASSASSIN;

	{
		// create the arrow tail
		this->arrowTail = new GameObject(ENGINE->GetSceneGraph3D());
		SpriteRenderer* spriteRenderer = this->arrowTail->AddComponent<SpriteRenderer>();
		std::vector<std::string> pathsToTextures;
		pathsToTextures.push_back(FRAMEWORK->GetFileSystemUtils()->GetDevicePictureResourcesFolderName() + "SD_UIEffect_Assassin_ArrowStem_magenta_02.png");
		spriteRenderer->initPlane(1.0f, 1.0f, "sptshdr", pathsToTextures, PlaneOrigin::Center);
		this->arrowTail->GetTransform()->SetScale( glm::vec3(GetFloatGameConstant(GAME_CONSTANT_target_indicator_scale)));
		this->arrowTail->GetTransform()->Rotate(90.0f inRadians, XAXIS);
		this->arrowTail->SetVisible(false);
	}
	{
		// create the arrow head
		this->arrowHead = new GameObject(ENGINE->GetSceneGraph3D());
		SpriteRenderer* spriteRenderer = this->arrowHead->AddComponent<SpriteRenderer>();
		std::vector<std::string> pathsToTextures;
		pathsToTextures.push_back(FRAMEWORK->GetFileSystemUtils()->GetDevicePictureResourcesFolderName() + "SD_UIEffect_Assassin_Arrow_magenta_03.png");
		spriteRenderer->initPlane(1.0f, 1.0f, "sptshdr", pathsToTextures, PlaneOrigin::Center);
		this->arrowHead->GetTransform()->Rotate(90.0f inRadians, XAXIS);
		this->arrowHead->SetVisible(false);
	}

	this->addSubscriptionToMessageType(MESSAGE_TYPE_GRID_CELL_CHANGED, this->GetTypeId(), false);
}

void Assassin::destroy() {
}

void Assassin::HandleMessage(MessageChunk messageChunk) {
	PlayerUnit::HandleMessage(messageChunk);
	switch (messageChunk->GetMessageType()) {
		case MESSAGE_TYPE_GRID_CELL_CHANGED:
			this->onGridCellChanged(messageChunk->GetSenderId(), messageChunk->messageData.iv1.x, messageChunk->messageData.iv1.z);
			break;

		default:
			break;
	}
}

bool Assassin::isSpecialTouch(int touchId) {
	if(this->getTouchNearUnit()) {
		Touch touch = ENGINE->GetInput()->GetTouch(touchId);
		if(touch.timeDown <= GetFloatGameConstant(GAME_CONSTANT_swipe_duration_in_seconds) && glm::distance(touch.pos, this->touchStartPos) > GetFloatGameConstant(GAME_CONSTANT_assassin_swipe_dist_in_pixels)) {
			this->swipeDirectionScreen = this->touchStartPos - touch.pos;
			this->targetedCell = nullptr;
			this->gridNavRef->HaltMovement();
			this->aimSpecial(touchId);
			return true;
		}
	}
	return false;
}

void Assassin::onSpecialTouch(int touchId) {
	Touch touch = ENGINE->GetInput()->GetTouch(touchId);
	if(touch.phase == TouchPhase::Ended) {
		this->trySpecial(touchId);
	} else if(touch.phase == TouchPhase::Cancelled) {
		this->cancelSpecial();
	} else if(touch.phase == TouchPhase::Moved) {
		this->aimSpecial(touchId);
	}
}

void Assassin::trySpecial(int /*touchId*/) {
	if(this->targetedCell != nullptr && this->targetedCell != this->gridNavRef->GetCurrentCell()) {
		this->startSpecial();
	} else {
		this->cancelSpecial();
	}
}

void Assassin::startSpecial() {
	PlayerUnit::startSpecial();

	this->gridNavRef->SetMovementSpeed(GetFloatGameConstant(GAME_CONSTANT_assassin_attack_speed));
	this->gridNavRef->SetDestination(this->targetedCell, true);

	this->startTouchIndicatorPulse();
	this->arrowHead->SetVisible(false);
	this->arrowTail->SetVisible(false);
}

void Assassin::onSpecialEnd() {
	PlayerUnit::onSpecialEnd();
	this->arrowHead->SetVisible(false);
	this->arrowTail->SetVisible(false);
	this->gridNavRef->SetMovementSpeed(MOVE_SPEED);

}

void Assassin::cancelSpecial() {
	PlayerUnit::cancelSpecial();
	this->arrowHead->SetVisible(false);
	this->arrowTail->SetVisible(false);
	this->gridNavRef->SetMovementSpeed(MOVE_SPEED);
}

void Assassin::onGridCellChanged(ObjectIdType id, int gridX, int gridZ) {
	// If this object is the one that sent the message, broadcast an attack message as well
	if (id == this->GetObject()->GetId()) {
		GridCell* cell = SINGLETONS->GetGridManager()->GetGrid()->GetCell(gridX, gridZ);
		if (cell != nullptr) {
			if (this->GetUnitActionState() == UnitActionState::UNIT_ACTION_STATE_DOING_SPECIAL) {
				// Send an attack message
				MessageChunk attackMessage = ENGINE->GetMessageHub()->GetOneFreeMessage();
				attackMessage->SetMessageType(MESSAGE_TYPE_UNIT_SPECIAL_HIT);
				attackMessage->messageData.iv1.x = cell->x;
				attackMessage->messageData.iv1.y = cell->y;
				attackMessage->messageData.iv1.z = cell->z;
				attackMessage->messageData.fv1 = this->specialStartPos;
				ENGINE->GetMessageHub()->SendMulticastMessage(attackMessage, this->GetObject()->GetId());
			}
		}
	}
}

void Assassin::aimSpecial(int touchId){
	glm::vec3 targetLoc = SINGLETONS->GetGridManager()->TouchPositionToGridPositionAtElevation(ENGINE->GetInput()->GetTouch(touchId).pos, this->gridNavRef->GetCurrentCell()->y);
	glm::vec3 sinPos = this->gameObjectRef->GetTransform()->GetPosition();

	std::list<GridCell*> touchedCells;
	GridCell* touchedCell = SINGLETONS->GetGridManager()->GetGrid()->GetCell(targetLoc);
	ASSERT(touchedCell != nullptr, "the target cell is not null");

	SINGLETONS->GetGridManager()->GetGrid()->TouchedCells(sinPos, targetLoc, touchedCells);
	int elevation = SINGLETONS->GetGridManager()->GetGrid()->GetElevationFromWorldY(this->gridNavRef->GetCurrentCell()->center.y);
	int cellIndex = 0;
	for( GridCell* c : touchedCells) {
		if(cellIndex <= GetFloatGameConstant(GAME_CONSTANT_dash_distance_in_units) && SINGLETONS->GetGridManager()->GetGrid()->IsCellPassableAtElevation(c->x, c->z, elevation)) {
			this->targetedCell = c;
		} else {
			break;
		}
		cellIndex++;
	}
	if(this->targetedCell != nullptr && this->targetedCell != this->gridNavRef->GetCurrentCell()) {
		if(touchedCell != this->targetedCell) {
			targetLoc = this->targetedCell->center;
		}
		this->SetTouchIndicatorVisible(true);
		this->gridNavRef->SetLookTarget(targetLoc);

		glm::vec3 sinPos = this->gameObjectRef->GetTransform()->GetPosition();
		// Arrow Tail
		this->arrowTail->SetVisible(true);
		this->SetTouchIndicatorLocation(targetLoc);
		this->TouchIndicatorLookAt(targetedCell);

		glm::vec3 attackDir = targetLoc - sinPos;
		if(glm::length(attackDir) > GetFloatGameConstant(GAME_CONSTANT_dash_distance_in_units)) {
			targetLoc = sinPos + glm::normalize(attackDir) * GetFloatGameConstant(GAME_CONSTANT_dash_distance_in_units);
		}
		float dist = glm::distance(sinPos, targetLoc) - 1.5f;
		if(dist < 0.5f) {
			this->arrowTail->SetVisible(false);
		}

		this->arrowTail->GetTransform()->SetScale(1.0f, dist, 1.0f);

		Transform* trans = this->arrowTail->GetComponent<Transform>();
		this->GridPlaneLookAt(this->arrowTail, targetLoc);

		trans->SetPosition(sinPos + glm::vec3(0.0f, .1f, 0.0f));
		trans->Translate(dist * .5f, trans->GetUp());

		// Arrow Head
		this->arrowHead->SetVisible(true);
		this->GridPlaneLookAt(this->arrowHead, targetLoc);
		this->arrowHead->GetTransform()->SetPosition(sinPos + glm::vec3(0.0f, .1f, 0.0f));
		this->arrowHead->GetTransform()->Translate(dist + .5f, this->arrowHead->GetTransform()->GetUp());

	} else {
		this->SetTouchIndicatorVisible(false);
		this->arrowHead->SetVisible(false);
		this->arrowTail->SetVisible(false);
	}
}
<commit_msg>assassin's target indicator is now correct when aiming through buildings<commit_after>#include "ExampleGame/Components/GameScripts/Units/Assassin.h"
#include "ExampleGame/Components/Grid/GameGrid.h"
#include "ExampleGame/Components/Grid/GridCell.h"
#include "ExampleGame/Components/Grid/GridManager.h"
#include "ExampleGame/Components/Grid/GridNavigator.h"
#include "ExampleGame/GameConstants/GameConstants.h"
#include "ExampleGame/GameSingletons/GameSingletons.h"
#include "ExampleGame/Messages/Declarations.h"

#include "Vajra/Engine/Components/DerivedComponents/Renderer/SpriteRenderer.h"
#include "Vajra/Engine/Components/DerivedComponents/Transform/Transform.h"
#include "Vajra/Engine/Core/Engine.h"
#include "Vajra/Engine/Input/Input.h"
#include "Vajra/Engine/MessageHub/MessageHub.h"
#include "Vajra/Engine/SceneGraph/SceneGraph3D.h"
#include "Vajra/Framework/DeviceUtils/FileSystemUtils/FileSystemUtils.h"

Assassin::Assassin() : PlayerUnit() {
	this->init();
}

Assassin::Assassin(Object* object_) : PlayerUnit(object_) {
	this->init();
}

Assassin::~Assassin() {
	this->destroy();
}

void Assassin::init() {
	this->unitType = UnitType::UNIT_TYPE_ASSASSIN;

	{
		// create the arrow tail
		this->arrowTail = new GameObject(ENGINE->GetSceneGraph3D());
		SpriteRenderer* spriteRenderer = this->arrowTail->AddComponent<SpriteRenderer>();
		std::vector<std::string> pathsToTextures;
		pathsToTextures.push_back(FRAMEWORK->GetFileSystemUtils()->GetDevicePictureResourcesFolderName() + "SD_UIEffect_Assassin_ArrowStem_magenta_02.png");
		spriteRenderer->initPlane(1.0f, 1.0f, "sptshdr", pathsToTextures, PlaneOrigin::Center);
		this->arrowTail->GetTransform()->SetScale( glm::vec3(GetFloatGameConstant(GAME_CONSTANT_target_indicator_scale)));
		this->arrowTail->GetTransform()->Rotate(90.0f inRadians, XAXIS);
		this->arrowTail->SetVisible(false);
	}
	{
		// create the arrow head
		this->arrowHead = new GameObject(ENGINE->GetSceneGraph3D());
		SpriteRenderer* spriteRenderer = this->arrowHead->AddComponent<SpriteRenderer>();
		std::vector<std::string> pathsToTextures;
		pathsToTextures.push_back(FRAMEWORK->GetFileSystemUtils()->GetDevicePictureResourcesFolderName() + "SD_UIEffect_Assassin_Arrow_magenta_03.png");
		spriteRenderer->initPlane(1.0f, 1.0f, "sptshdr", pathsToTextures, PlaneOrigin::Center);
		this->arrowHead->GetTransform()->Rotate(90.0f inRadians, XAXIS);
		this->arrowHead->SetVisible(false);
	}

	this->addSubscriptionToMessageType(MESSAGE_TYPE_GRID_CELL_CHANGED, this->GetTypeId(), false);
}

void Assassin::destroy() {
}

void Assassin::HandleMessage(MessageChunk messageChunk) {
	PlayerUnit::HandleMessage(messageChunk);
	switch (messageChunk->GetMessageType()) {
		case MESSAGE_TYPE_GRID_CELL_CHANGED:
			this->onGridCellChanged(messageChunk->GetSenderId(), messageChunk->messageData.iv1.x, messageChunk->messageData.iv1.z);
			break;

		default:
			break;
	}
}

bool Assassin::isSpecialTouch(int touchId) {
	if(this->getTouchNearUnit()) {
		Touch touch = ENGINE->GetInput()->GetTouch(touchId);
		if(touch.timeDown <= GetFloatGameConstant(GAME_CONSTANT_swipe_duration_in_seconds) && glm::distance(touch.pos, this->touchStartPos) > GetFloatGameConstant(GAME_CONSTANT_assassin_swipe_dist_in_pixels)) {
			this->swipeDirectionScreen = this->touchStartPos - touch.pos;
			this->targetedCell = nullptr;
			this->gridNavRef->HaltMovement();
			this->aimSpecial(touchId);
			return true;
		}
	}
	return false;
}

void Assassin::onSpecialTouch(int touchId) {
	Touch touch = ENGINE->GetInput()->GetTouch(touchId);
	if(touch.phase == TouchPhase::Ended) {
		this->trySpecial(touchId);
	} else if(touch.phase == TouchPhase::Cancelled) {
		this->cancelSpecial();
	} else if(touch.phase == TouchPhase::Moved) {
		this->aimSpecial(touchId);
	}
}

void Assassin::trySpecial(int /*touchId*/) {
	if(this->targetedCell != nullptr && this->targetedCell != this->gridNavRef->GetCurrentCell()) {
		this->startSpecial();
	} else {
		this->cancelSpecial();
	}
}

void Assassin::startSpecial() {
	PlayerUnit::startSpecial();

	this->gridNavRef->SetMovementSpeed(GetFloatGameConstant(GAME_CONSTANT_assassin_attack_speed));
	this->gridNavRef->SetDestination(this->targetedCell, true);

	this->startTouchIndicatorPulse();
	this->arrowHead->SetVisible(false);
	this->arrowTail->SetVisible(false);
}

void Assassin::onSpecialEnd() {
	PlayerUnit::onSpecialEnd();
	this->arrowHead->SetVisible(false);
	this->arrowTail->SetVisible(false);
	this->gridNavRef->SetMovementSpeed(MOVE_SPEED);

}

void Assassin::cancelSpecial() {
	PlayerUnit::cancelSpecial();
	this->arrowHead->SetVisible(false);
	this->arrowTail->SetVisible(false);
	this->gridNavRef->SetMovementSpeed(MOVE_SPEED);
}

void Assassin::onGridCellChanged(ObjectIdType id, int gridX, int gridZ) {
	// If this object is the one that sent the message, broadcast an attack message as well
	if (id == this->GetObject()->GetId()) {
		GridCell* cell = SINGLETONS->GetGridManager()->GetGrid()->GetCell(gridX, gridZ);
		if (cell != nullptr) {
			if (this->GetUnitActionState() == UnitActionState::UNIT_ACTION_STATE_DOING_SPECIAL) {
				// Send an attack message
				MessageChunk attackMessage = ENGINE->GetMessageHub()->GetOneFreeMessage();
				attackMessage->SetMessageType(MESSAGE_TYPE_UNIT_SPECIAL_HIT);
				attackMessage->messageData.iv1.x = cell->x;
				attackMessage->messageData.iv1.y = cell->y;
				attackMessage->messageData.iv1.z = cell->z;
				attackMessage->messageData.fv1 = this->specialStartPos;
				ENGINE->GetMessageHub()->SendMulticastMessage(attackMessage, this->GetObject()->GetId());
			}
		}
	}
}

void Assassin::aimSpecial(int touchId){
	glm::vec3 targetLoc = SINGLETONS->GetGridManager()->TouchPositionToGridPositionAtElevation(ENGINE->GetInput()->GetTouch(touchId).pos, this->gridNavRef->GetCurrentCell()->y);
	glm::vec3 sinPos = this->gameObjectRef->GetTransform()->GetPosition();

	std::list<GridCell*> touchedCells;
	GridCell* touchedCell = SINGLETONS->GetGridManager()->GetGrid()->GetCell(targetLoc);
	ASSERT(touchedCell != nullptr, "the target cell is not null");

	SINGLETONS->GetGridManager()->GetGrid()->TouchedCells(sinPos, targetLoc, touchedCells);
	int elevation = SINGLETONS->GetGridManager()->GetGrid()->GetElevationFromWorldY(this->gridNavRef->GetCurrentCell()->center.y);
	int cellIndex = 0;
	for( GridCell* c : touchedCells) {
		if(cellIndex <= GetFloatGameConstant(GAME_CONSTANT_dash_distance_in_units) && SINGLETONS->GetGridManager()->GetGrid()->IsCellPassableAtElevation(c->x, c->z, elevation)) {
			this->targetedCell = c;
		} else {
			break;
		}
		cellIndex++;
	}
	if(this->targetedCell != nullptr && touchedCell != nullptr && this->targetedCell != this->gridNavRef->GetCurrentCell()) {
		glm::vec3 attackDir = glm::normalize(targetLoc - sinPos);
		if(touchedCell != this->targetedCell) {
			glm::vec3 temp = targetLoc;
			GridCell* tempCell = touchedCell;
			while(tempCell != this->targetedCell) {
				temp -= attackDir * .01f;
				tempCell = SINGLETONS->GetGridManager()->GetGrid()->GetCell(temp);
			}
			targetLoc = temp;
			touchedCell = tempCell;

		}
		this->SetTouchIndicatorVisible(true);
		this->gridNavRef->SetLookTarget(targetLoc);

		// Arrow Tail
		this->arrowTail->SetVisible(true);
		this->SetTouchIndicatorLocation(targetLoc);
		this->TouchIndicatorLookAt(targetedCell);

		if(glm::length(attackDir) > GetFloatGameConstant(GAME_CONSTANT_dash_distance_in_units)) {
			targetLoc = sinPos + glm::normalize(attackDir) * GetFloatGameConstant(GAME_CONSTANT_dash_distance_in_units);
		}
		float dist = glm::distance(sinPos, targetLoc) - 1.5f;
		if(dist < 0.5f) {
			this->arrowTail->SetVisible(false);
		}

		this->arrowTail->GetTransform()->SetScale(1.0f, dist, 1.0f);

		Transform* trans = this->arrowTail->GetComponent<Transform>();
		this->GridPlaneLookAt(this->arrowTail, targetLoc);

		trans->SetPosition(sinPos + glm::vec3(0.0f, .1f, 0.0f));
		trans->Translate(dist * .5f, trans->GetUp());

		// Arrow Head
		this->arrowHead->SetVisible(true);
		this->GridPlaneLookAt(this->arrowHead, targetLoc);
		this->arrowHead->GetTransform()->SetPosition(sinPos + glm::vec3(0.0f, .1f, 0.0f));
		this->arrowHead->GetTransform()->Translate(dist + .5f, this->arrowHead->GetTransform()->GetUp());

	} else {
		this->SetTouchIndicatorVisible(false);
		this->arrowHead->SetVisible(false);
		this->arrowTail->SetVisible(false);
	}
}
<|endoftext|>


<commit_before>#include "videoframe.h"
#include <algorithm>

namespace gg
{

VideoFrame::VideoFrame(bool manage_data)
{
    _manage_data = manage_data;
}

std::unique_ptr<MaskFrame> VideoFrame::compute_image_mask(int x, int y,
                                                               int width, int height)
{
    cv::Rect rect(x, y, width, height);
    cv::Mat temp(_rows, _cols, CV_8UC4, _data);
    cv::Mat image, result;
    temp.copyTo(image);
    cv::cvtColor(image, image, CV_BGRA2BGR);
    cv::Mat bg, fg;

    for (int i = 0; i < 3; ++i) {
        if (i == 0) cv::grabCut(image, result, rect, bg, fg, 1, cv::GC_INIT_WITH_RECT);
        else cv::grabCut(image, result, rect, bg, fg, 1, cv::GC_INIT_WITH_MASK);
    }

    cv::Mat result_a, result_b;
    cv::compare(result,cv::GC_FGD,result_a,cv::CMP_EQ);
    // Get the pixels marked as likely background
    cv::compare(result,cv::GC_PR_FGD,result_b,cv::CMP_EQ);
    // Final results
    result=result_a + result_b;

    cv::vector<cv::vector<cv::Point> > contours;
    cv::findContours(result, contours, cv::RETR_LIST, cv::CHAIN_APPROX_SIMPLE);

    if (!(contours.size() >= 1)) {
        throw std::runtime_error("Could not generate the image mask");
    }

    int index = 0;
    for (int i = 1; i < contours.size(); ++i) {
        if (contours[i].size() > contours[index].size()) index = i;
    }

    if (contours[index].size() < 5) {
        throw std::runtime_error("Could not generate the image mask");
    }

    cv::RotatedRect e = cv::fitEllipse(contours[index]);
    // Small fudge factor
    e.size.width -= 30;
    e.size.height -= 30;

    cv::Rect bound_rect = e.boundingRect();
    int rx_1, ry_1, rx_2, ry_2;
    rx_1 = bound_rect.x; ry_1 = bound_rect.y;
    rx_2 = rx_1 + bound_rect.width; ry_2 = ry_1 + bound_rect.height;

    rx_1 = std::max(0, rx_1); rx_1 = std::min((int)_cols -1, rx_1);
    ry_1 = std::max(0, ry_1); ry_1 = std::min((int)_rows -1, ry_1);
    rx_2 = std::max(0, rx_2); rx_2 = std::min((int)_cols -1, rx_2);
    ry_2 = std::max(0, ry_2); ry_2 = std::min((int)_rows -1, ry_2);

    cv::Mat filled_mask = cv::Mat::zeros(_rows, _cols, CV_8UC1);
    cv::ellipse(filled_mask, e, cv::Scalar(255), CV_FILLED);

    int mw = rx_2 - rx_1;
    int mh = ry_2 - ry_1;

    mw -= (int)mw % 16;
    mh -= (int)mh % 16;

    cv::Rect sub_rect(rx_1, ry_1, mw, mh);
    cv::Mat cropped_mask;
    filled_mask(sub_rect).copyTo(cropped_mask);
    // Now create the mask with the correct dimensions.
    auto mask = std::unique_ptr<MaskFrame>(new MaskFrame(mw, mh));
    unsigned char * mask_data = const_cast<unsigned char *>(mask->data());
    memcpy(mask_data, cropped_mask.data, mw * mh);
    mask->set_ellipse(e.center.x, e.center.y, e.size.width, e.size.height, e.angle);
    mask->set_bound_rect(rx_1, ry_1, mw, mh);

    unsigned char * mask_weights = const_cast<unsigned char *>(mask->weights());
    cv::Mat wts = cv::Mat(cropped_mask.rows, cropped_mask.cols, CV_8UC1, mask_weights);

    float w;
    double cx = mw/2.0;
    double cy = mh/2.0;

    for (double r = 0; r < cropped_mask.rows; ++r) {
        uchar * p = cropped_mask.ptr((int)r);
        uchar * q = wts.ptr((int)r);
        for (double c = 0; c < cropped_mask.cols; ++c) {
            if (p[(int)c] > 0) {
                w = std::sqrt(std::pow((cx - c)/cx, 2.f) + std::pow((cy - r)/cy, 2.f));
                if (w > 1) w = 1.f;
                q[(int)c] = (unsigned char)((1.f - w)*255.9999);
            }
        }
    }
    return mask;
}

VideoFrame::~VideoFrame()
{
    clear();
}

void VideoFrame::clear()
{
    if (_manage_data) {
        delete [] _data;
        _data = 0;
    }
}

}

VideoFrame_BGRA::VideoFrame_BGRA(bool manage_data)
    : VideoFrame(manage_data)
{

}

VideoFrame_BGRA::VideoFrame_BGRA(const size_t rows, const size_t cols)
    : gg::VideoFrame(true)
{
    init_from_opencv_mat(cv::Mat::zeros(rows, cols, CV_8UC4));
}

VideoFrame_BGRA::VideoFrame_BGRA(const cv::Mat & mat, bool manage_data)
{
    _manage_data = manage_data;
    init_from_opencv_mat(mat);
}

VideoFrame_BGRA::VideoFrame_BGRA(unsigned char * data, size_t rows, size_t cols, bool manage_data)
  : VideoFrame(manage_data)
{
   init_from_pointer(data, rows, cols);
}

VideoFrame_BGRA::VideoFrame_BGRA(const VideoFrame_BGRA & rhs)
{
    clone(rhs);
}

void VideoFrame_BGRA::operator =(const VideoFrame_BGRA & rhs)
{
    clone(rhs);
}

void VideoFrame_BGRA::init_from_opencv_mat(const cv::Mat &mat)
{
    assert(mat.channels() == 4);
    _rows = mat.rows;
    _cols = mat.cols;
    _data_length = _rows * _cols * 4;

    // OpenCV stores images top to bottom while OpenGL is bottom to top.
    // The caller is supposed to setup the client visualization to have
    // origin at upper left corner
    //cv::flip(mat, mat, 0);

    if (_manage_data) {
        _data = new unsigned char[_data_length];
        memcpy(_data, mat.data, _data_length * sizeof(unsigned char));
    }
    // Just copy the pointer address
    else {
        _data = mat.data;
    }
}

void VideoFrame_BGRA::init_from_pointer(unsigned char * data, size_t rows, size_t cols)
{
    _rows = rows;
    _cols = cols;

    _data_length = _rows * _cols * 4;
    if( _manage_data ){
        _data = new unsigned char[_data_length];
        memcpy(_data, data, _data_length * sizeof(unsigned char));
    }
    else{
        _data = data;
    }
}

void VideoFrame_BGRA::clone(const VideoFrame_BGRA & rhs)
{
    clear();
    _manage_data = true;
    _rows = rhs.rows();
    _cols = rhs.cols();
    _data_length = _rows * _cols * 4;
    _data = new unsigned char[_data_length];
    memcpy(_data, rhs._data, _data_length * sizeof(unsigned char));
}
<commit_msg>Issue #48: VideoFrame constructor complete initialiser list, and clear function resetting data length parameters<commit_after>#include "videoframe.h"
#include <algorithm>

namespace gg
{

VideoFrame::VideoFrame(bool manage_data)
  : _manage_data(manage_data),
    _data(nullptr),
    _data_length(0),
    _rows(0),
    _cols(0)
{

}

std::unique_ptr<MaskFrame> VideoFrame::compute_image_mask(int x, int y,
                                                               int width, int height)
{
    cv::Rect rect(x, y, width, height);
    cv::Mat temp(_rows, _cols, CV_8UC4, _data);
    cv::Mat image, result;
    temp.copyTo(image);
    cv::cvtColor(image, image, CV_BGRA2BGR);
    cv::Mat bg, fg;

    for (int i = 0; i < 3; ++i) {
        if (i == 0) cv::grabCut(image, result, rect, bg, fg, 1, cv::GC_INIT_WITH_RECT);
        else cv::grabCut(image, result, rect, bg, fg, 1, cv::GC_INIT_WITH_MASK);
    }

    cv::Mat result_a, result_b;
    cv::compare(result,cv::GC_FGD,result_a,cv::CMP_EQ);
    // Get the pixels marked as likely background
    cv::compare(result,cv::GC_PR_FGD,result_b,cv::CMP_EQ);
    // Final results
    result=result_a + result_b;

    cv::vector<cv::vector<cv::Point> > contours;
    cv::findContours(result, contours, cv::RETR_LIST, cv::CHAIN_APPROX_SIMPLE);

    if (!(contours.size() >= 1)) {
        throw std::runtime_error("Could not generate the image mask");
    }

    int index = 0;
    for (int i = 1; i < contours.size(); ++i) {
        if (contours[i].size() > contours[index].size()) index = i;
    }

    if (contours[index].size() < 5) {
        throw std::runtime_error("Could not generate the image mask");
    }

    cv::RotatedRect e = cv::fitEllipse(contours[index]);
    // Small fudge factor
    e.size.width -= 30;
    e.size.height -= 30;

    cv::Rect bound_rect = e.boundingRect();
    int rx_1, ry_1, rx_2, ry_2;
    rx_1 = bound_rect.x; ry_1 = bound_rect.y;
    rx_2 = rx_1 + bound_rect.width; ry_2 = ry_1 + bound_rect.height;

    rx_1 = std::max(0, rx_1); rx_1 = std::min((int)_cols -1, rx_1);
    ry_1 = std::max(0, ry_1); ry_1 = std::min((int)_rows -1, ry_1);
    rx_2 = std::max(0, rx_2); rx_2 = std::min((int)_cols -1, rx_2);
    ry_2 = std::max(0, ry_2); ry_2 = std::min((int)_rows -1, ry_2);

    cv::Mat filled_mask = cv::Mat::zeros(_rows, _cols, CV_8UC1);
    cv::ellipse(filled_mask, e, cv::Scalar(255), CV_FILLED);

    int mw = rx_2 - rx_1;
    int mh = ry_2 - ry_1;

    mw -= (int)mw % 16;
    mh -= (int)mh % 16;

    cv::Rect sub_rect(rx_1, ry_1, mw, mh);
    cv::Mat cropped_mask;
    filled_mask(sub_rect).copyTo(cropped_mask);
    // Now create the mask with the correct dimensions.
    auto mask = std::unique_ptr<MaskFrame>(new MaskFrame(mw, mh));
    unsigned char * mask_data = const_cast<unsigned char *>(mask->data());
    memcpy(mask_data, cropped_mask.data, mw * mh);
    mask->set_ellipse(e.center.x, e.center.y, e.size.width, e.size.height, e.angle);
    mask->set_bound_rect(rx_1, ry_1, mw, mh);

    unsigned char * mask_weights = const_cast<unsigned char *>(mask->weights());
    cv::Mat wts = cv::Mat(cropped_mask.rows, cropped_mask.cols, CV_8UC1, mask_weights);

    float w;
    double cx = mw/2.0;
    double cy = mh/2.0;

    for (double r = 0; r < cropped_mask.rows; ++r) {
        uchar * p = cropped_mask.ptr((int)r);
        uchar * q = wts.ptr((int)r);
        for (double c = 0; c < cropped_mask.cols; ++c) {
            if (p[(int)c] > 0) {
                w = std::sqrt(std::pow((cx - c)/cx, 2.f) + std::pow((cy - r)/cy, 2.f));
                if (w > 1) w = 1.f;
                q[(int)c] = (unsigned char)((1.f - w)*255.9999);
            }
        }
    }
    return mask;
}

VideoFrame::~VideoFrame()
{
    clear();
}

void VideoFrame::clear()
{
    if (_manage_data) {
        delete [] _data;
        _data = 0;
        _data_length = 0;
        _rows = 0;
        _cols = 0;
    }
}

}

VideoFrame_BGRA::VideoFrame_BGRA(bool manage_data)
    : VideoFrame(manage_data)
{

}

VideoFrame_BGRA::VideoFrame_BGRA(const size_t rows, const size_t cols)
    : gg::VideoFrame(true)
{
    init_from_opencv_mat(cv::Mat::zeros(rows, cols, CV_8UC4));
}

VideoFrame_BGRA::VideoFrame_BGRA(const cv::Mat & mat, bool manage_data)
{
    _manage_data = manage_data;
    init_from_opencv_mat(mat);
}

VideoFrame_BGRA::VideoFrame_BGRA(unsigned char * data, size_t rows, size_t cols, bool manage_data)
  : VideoFrame(manage_data)
{
    init_from_pointer(data, rows, cols);
}

VideoFrame_BGRA::VideoFrame_BGRA(const VideoFrame_BGRA & rhs)
{
    clone(rhs);
}

void VideoFrame_BGRA::operator =(const VideoFrame_BGRA & rhs)
{
    clone(rhs);
}

void VideoFrame_BGRA::init_from_opencv_mat(const cv::Mat &mat)
{
    assert(mat.channels() == 4);
    _rows = mat.rows;
    _cols = mat.cols;
    _data_length = _rows * _cols * 4;

    // OpenCV stores images top to bottom while OpenGL is bottom to top.
    // The caller is supposed to setup the client visualization to have
    // origin at upper left corner
    //cv::flip(mat, mat, 0);

    if (_manage_data) {
        _data = new unsigned char[_data_length];
        memcpy(_data, mat.data, _data_length * sizeof(unsigned char));
    }
    // Just copy the pointer address
    else {
        _data = mat.data;
    }
}

void VideoFrame_BGRA::init_from_pointer(unsigned char * data, size_t rows, size_t cols)
{
    _rows = rows;
    _cols = cols;

    _data_length = _rows * _cols * 4;
    if( _manage_data ){
        _data = new unsigned char[_data_length];
        memcpy(_data, data, _data_length * sizeof(unsigned char));
    }
    else{
        _data = data;
    }
}

void VideoFrame_BGRA::clone(const VideoFrame_BGRA & rhs)
{
    clear();
    _manage_data = true;
    _rows = rhs.rows();
    _cols = rhs.cols();
    _data_length = _rows * _cols * 4;
    _data = new unsigned char[_data_length];
    memcpy(_data, rhs._data, _data_length * sizeof(unsigned char));
}
<|endoftext|>


<commit_before>#include "videoframe.h"
#include <cstring>

namespace gg
{

VideoFrame::VideoFrame()
{
    // nop: never to be called externally
}

VideoFrame::VideoFrame(enum ColourSpace colour, bool manage_data)
    : _colour(colour)
    , _manage_data(manage_data)
    , _data(nullptr)
    , _data_length(0)
    , _stereo_count(1)
{
    set_dimensions(0, 0);
}

VideoFrame::VideoFrame(ColourSpace colour, size_t cols, size_t rows,
                       size_t stereo_count)
    : _colour(colour)
    , _manage_data(true)
    , _data(nullptr)
    , _data_length(0)
    , _stereo_count(stereo_count)
{
    set_dimensions(cols, rows);
    size_t data_length = required_data_length(_colour, _cols, _rows);
    data_length *= _stereo_count;
    allocate_memory(data_length);
    set_pixels_black();
}

VideoFrame::VideoFrame(const VideoFrame & rhs)
    : _colour(rhs.colour())
    , _manage_data(true)
    , _data(nullptr)
    , _data_length(0)
    , _stereo_count(1)
{
    clone(rhs);
}

#ifdef USE_OPENCV
std::unique_ptr<MaskFrame> VideoFrame::compute_image_mask(int x, int y,
                                                               int width, int height)
{
    cv::Rect rect(x, y, width, height);
    cv::Mat temp(_rows, _cols, CV_8UC4, _data);
    cv::Mat image, result;
    temp.copyTo(image);
    cv::cvtColor(image, image, CV_BGRA2BGR);
    cv::Mat bg, fg;

    for (int i = 0; i < 3; ++i) {
        if (i == 0) cv::grabCut(image, result, rect, bg, fg, 1, cv::GC_INIT_WITH_RECT);
        else cv::grabCut(image, result, rect, bg, fg, 1, cv::GC_INIT_WITH_MASK);
    }

    cv::Mat result_a, result_b;
    cv::compare(result,cv::GC_FGD,result_a,cv::CMP_EQ);
    // Get the pixels marked as likely background
    cv::compare(result,cv::GC_PR_FGD,result_b,cv::CMP_EQ);
    // Final results
    result=result_a + result_b;

    cv::vector<cv::vector<cv::Point> > contours;
    cv::findContours(result, contours, cv::RETR_LIST, cv::CHAIN_APPROX_SIMPLE);

    if (!(contours.size() >= 1)) {
        throw std::runtime_error("Could not generate the image mask");
    }

    int index = 0;
    for (int i = 1; i < contours.size(); ++i) {
        if (contours[i].size() > contours[index].size()) index = i;
    }

    if (contours[index].size() < 5) {
        throw std::runtime_error("Could not generate the image mask");
    }

    cv::RotatedRect e = cv::fitEllipse(contours[index]);
    // Small fudge factor
    e.size.width -= 30;
    e.size.height -= 30;

    cv::Rect bound_rect = e.boundingRect();
    int rx_1, ry_1, rx_2, ry_2;
    rx_1 = bound_rect.x; ry_1 = bound_rect.y;
    rx_2 = rx_1 + bound_rect.width; ry_2 = ry_1 + bound_rect.height;

    rx_1 = std::max(0, rx_1); rx_1 = std::min((int)_cols -1, rx_1);
    ry_1 = std::max(0, ry_1); ry_1 = std::min((int)_rows -1, ry_1);
    rx_2 = std::max(0, rx_2); rx_2 = std::min((int)_cols -1, rx_2);
    ry_2 = std::max(0, ry_2); ry_2 = std::min((int)_rows -1, ry_2);

    cv::Mat filled_mask = cv::Mat::zeros(_rows, _cols, CV_8UC1);
    cv::ellipse(filled_mask, e, cv::Scalar(255), CV_FILLED);

    int mw = rx_2 - rx_1;
    int mh = ry_2 - ry_1;

    mw -= (int)mw % 16;
    mh -= (int)mh % 16;

    cv::Rect sub_rect(rx_1, ry_1, mw, mh);
    cv::Mat cropped_mask;
    filled_mask(sub_rect).copyTo(cropped_mask);
    // Now create the mask with the correct dimensions.
    auto mask = std::unique_ptr<MaskFrame>(new MaskFrame(mw, mh));
    unsigned char * mask_data = const_cast<unsigned char *>(mask->data());
    memcpy(mask_data, cropped_mask.data, mw * mh);
    mask->set_ellipse(e.center.x, e.center.y, e.size.width, e.size.height, e.angle);
    mask->set_bound_rect(rx_1, ry_1, mw, mh);

    unsigned char * mask_weights = const_cast<unsigned char *>(mask->weights());
    cv::Mat wts = cv::Mat(cropped_mask.rows, cropped_mask.cols, CV_8UC1, mask_weights);

    float w;
    double cx = mw/2.0;
    double cy = mh/2.0;

    for (double r = 0; r < cropped_mask.rows; ++r) {
        uchar * p = cropped_mask.ptr((int)r);
        uchar * q = wts.ptr((int)r);
        for (double c = 0; c < cropped_mask.cols; ++c) {
            if (p[(int)c] > 0) {
                w = std::sqrt(std::pow((cx - c)/cx, 2.f) + std::pow((cy - r)/cy, 2.f));
                if (w > 1) w = 1.f;
                q[(int)c] = (unsigned char)((1.f - w)*255.9999);
            }
        }
    }
    return mask;
}
#endif // USE_OPENCV

VideoFrame::~VideoFrame()
{
    free_memory();
}

void VideoFrame::operator=(const VideoFrame & rhs)
{
    clone(rhs);
}

void VideoFrame::init_from_specs(unsigned char * data, size_t data_length,
                                 size_t cols, size_t rows, size_t stereo_count)
{
    if (_manage_data)
    {
        allocate_memory(data_length);
        memcpy(_data, data, _data_length * sizeof(unsigned char));
    }
    else
    {
        _data_length = data_length;
        _data = data;
    }

    set_dimensions(cols, rows);
}

const size_t VideoFrame::data_length(size_t stereo_index) const
{
    validate_stereo_index(stereo_index);
    return _data_length / _stereo_count;
}

unsigned char * const VideoFrame::data(size_t stereo_index) const
{
    validate_stereo_index(stereo_index);
    return &_data[stereo_index * _data_length / _stereo_count];
}

void VideoFrame::clone(const VideoFrame & rhs)
{
    if (not _manage_data)
    {
        _data = nullptr;
        _data_length = 0;
        set_dimensions(0, 0);
    }
    _manage_data = true;
    _colour = rhs._colour;
    init_from_specs(rhs._data, rhs._data_length,
                    rhs._cols, rhs._rows, rhs._stereo_count);
}

void VideoFrame::set_dimensions(size_t cols, size_t rows)
{
    _cols = cols;
    _rows = rows;
    switch (_colour)
    {
    case I420:
        if (_cols % 2 == 1) ++_cols;
        if (_rows % 2 == 1) ++_rows;
        break;
    case BGRA:
    case UYVY:
    default:
        // nop
        // Also not caring if colour space not set properly,
        // as this function is for internal use only
        break;
    }
}

void VideoFrame::allocate_memory(size_t data_length)
{
    if (_manage_data and data_length > _data_length)
    {
        _data_length = data_length;
        _data = reinterpret_cast<unsigned char*>(
                    realloc(_data, _data_length * sizeof(unsigned char)));
    }
}

void VideoFrame::free_memory()
{
    if (_manage_data)
    {
        free(_data);
        _data = nullptr;
        _data_length = 0;
        set_dimensions(0, 0);
    }
}

size_t VideoFrame::required_data_length(enum ColourSpace colour,
                                        size_t cols, size_t rows)
{
    if (cols <= 0 or rows <= 0)
        throw BasicException("Frame dimensions must be positive");

    switch (colour)
    {
    case BGRA:
        return cols * rows * 4;

    case I420:
        // 1 for Y plane, 0.5 for UV plane
        return cols * rows * 1.5; // both dimensions always even due to set_dimensions()

    case UYVY:
        return cols * rows * 2; // 2 bytes per pixel, see http://www.fourcc.org/pixel-format/yuv-uyvy/

    default:
        throw BasicException("Colour space indicator not set properly, cannot compute memory requirement");
    }
}

size_t VideoFrame::required_pixel_length(enum ColourSpace colour)
{
    size_t unit_size = sizeof(unsigned char);
    switch(colour)
    {
    case BGRA:
        return 4 * unit_size;
    case I420:
        return 1.5 * unit_size;
    case UYVY:
        return 2 * unit_size;
    default:
        throw BasicException("Colour space not supported");
    }
}

void VideoFrame::set_pixels_black()
{
    memset(_data, 0, _data_length * sizeof(unsigned char));
}

}
<commit_msg>Issue #18: init_from_specs now respects stereo count parameter<commit_after>#include "videoframe.h"
#include <cstring>

namespace gg
{

VideoFrame::VideoFrame()
{
    // nop: never to be called externally
}

VideoFrame::VideoFrame(enum ColourSpace colour, bool manage_data)
    : _colour(colour)
    , _manage_data(manage_data)
    , _data(nullptr)
    , _data_length(0)
    , _stereo_count(1)
{
    set_dimensions(0, 0);
}

VideoFrame::VideoFrame(ColourSpace colour, size_t cols, size_t rows,
                       size_t stereo_count)
    : _colour(colour)
    , _manage_data(true)
    , _data(nullptr)
    , _data_length(0)
    , _stereo_count(stereo_count)
{
    set_dimensions(cols, rows);
    size_t data_length = required_data_length(_colour, _cols, _rows);
    data_length *= _stereo_count;
    allocate_memory(data_length);
    set_pixels_black();
}

VideoFrame::VideoFrame(const VideoFrame & rhs)
    : _colour(rhs.colour())
    , _manage_data(true)
    , _data(nullptr)
    , _data_length(0)
    , _stereo_count(1)
{
    clone(rhs);
}

#ifdef USE_OPENCV
std::unique_ptr<MaskFrame> VideoFrame::compute_image_mask(int x, int y,
                                                               int width, int height)
{
    cv::Rect rect(x, y, width, height);
    cv::Mat temp(_rows, _cols, CV_8UC4, _data);
    cv::Mat image, result;
    temp.copyTo(image);
    cv::cvtColor(image, image, CV_BGRA2BGR);
    cv::Mat bg, fg;

    for (int i = 0; i < 3; ++i) {
        if (i == 0) cv::grabCut(image, result, rect, bg, fg, 1, cv::GC_INIT_WITH_RECT);
        else cv::grabCut(image, result, rect, bg, fg, 1, cv::GC_INIT_WITH_MASK);
    }

    cv::Mat result_a, result_b;
    cv::compare(result,cv::GC_FGD,result_a,cv::CMP_EQ);
    // Get the pixels marked as likely background
    cv::compare(result,cv::GC_PR_FGD,result_b,cv::CMP_EQ);
    // Final results
    result=result_a + result_b;

    cv::vector<cv::vector<cv::Point> > contours;
    cv::findContours(result, contours, cv::RETR_LIST, cv::CHAIN_APPROX_SIMPLE);

    if (!(contours.size() >= 1)) {
        throw std::runtime_error("Could not generate the image mask");
    }

    int index = 0;
    for (int i = 1; i < contours.size(); ++i) {
        if (contours[i].size() > contours[index].size()) index = i;
    }

    if (contours[index].size() < 5) {
        throw std::runtime_error("Could not generate the image mask");
    }

    cv::RotatedRect e = cv::fitEllipse(contours[index]);
    // Small fudge factor
    e.size.width -= 30;
    e.size.height -= 30;

    cv::Rect bound_rect = e.boundingRect();
    int rx_1, ry_1, rx_2, ry_2;
    rx_1 = bound_rect.x; ry_1 = bound_rect.y;
    rx_2 = rx_1 + bound_rect.width; ry_2 = ry_1 + bound_rect.height;

    rx_1 = std::max(0, rx_1); rx_1 = std::min((int)_cols -1, rx_1);
    ry_1 = std::max(0, ry_1); ry_1 = std::min((int)_rows -1, ry_1);
    rx_2 = std::max(0, rx_2); rx_2 = std::min((int)_cols -1, rx_2);
    ry_2 = std::max(0, ry_2); ry_2 = std::min((int)_rows -1, ry_2);

    cv::Mat filled_mask = cv::Mat::zeros(_rows, _cols, CV_8UC1);
    cv::ellipse(filled_mask, e, cv::Scalar(255), CV_FILLED);

    int mw = rx_2 - rx_1;
    int mh = ry_2 - ry_1;

    mw -= (int)mw % 16;
    mh -= (int)mh % 16;

    cv::Rect sub_rect(rx_1, ry_1, mw, mh);
    cv::Mat cropped_mask;
    filled_mask(sub_rect).copyTo(cropped_mask);
    // Now create the mask with the correct dimensions.
    auto mask = std::unique_ptr<MaskFrame>(new MaskFrame(mw, mh));
    unsigned char * mask_data = const_cast<unsigned char *>(mask->data());
    memcpy(mask_data, cropped_mask.data, mw * mh);
    mask->set_ellipse(e.center.x, e.center.y, e.size.width, e.size.height, e.angle);
    mask->set_bound_rect(rx_1, ry_1, mw, mh);

    unsigned char * mask_weights = const_cast<unsigned char *>(mask->weights());
    cv::Mat wts = cv::Mat(cropped_mask.rows, cropped_mask.cols, CV_8UC1, mask_weights);

    float w;
    double cx = mw/2.0;
    double cy = mh/2.0;

    for (double r = 0; r < cropped_mask.rows; ++r) {
        uchar * p = cropped_mask.ptr((int)r);
        uchar * q = wts.ptr((int)r);
        for (double c = 0; c < cropped_mask.cols; ++c) {
            if (p[(int)c] > 0) {
                w = std::sqrt(std::pow((cx - c)/cx, 2.f) + std::pow((cy - r)/cy, 2.f));
                if (w > 1) w = 1.f;
                q[(int)c] = (unsigned char)((1.f - w)*255.9999);
            }
        }
    }
    return mask;
}
#endif // USE_OPENCV

VideoFrame::~VideoFrame()
{
    free_memory();
}

void VideoFrame::operator=(const VideoFrame & rhs)
{
    clone(rhs);
}

void VideoFrame::init_from_specs(unsigned char * data, size_t data_length,
                                 size_t cols, size_t rows, size_t stereo_count)
{
    _stereo_count = stereo_count;

    if (_manage_data)
    {
        allocate_memory(data_length);
        memcpy(_data, data, _data_length * sizeof(unsigned char));
    }
    else
    {
        _data_length = data_length;
        _data = data;
    }

    set_dimensions(cols, rows);
}

const size_t VideoFrame::data_length(size_t stereo_index) const
{
    validate_stereo_index(stereo_index);
    return _data_length / _stereo_count;
}

unsigned char * const VideoFrame::data(size_t stereo_index) const
{
    validate_stereo_index(stereo_index);
    return &_data[stereo_index * _data_length / _stereo_count];
}

void VideoFrame::clone(const VideoFrame & rhs)
{
    if (not _manage_data)
    {
        _data = nullptr;
        _data_length = 0;
        set_dimensions(0, 0);
    }
    _manage_data = true;
    _colour = rhs._colour;
    init_from_specs(rhs._data, rhs._data_length,
                    rhs._cols, rhs._rows, rhs._stereo_count);
}

void VideoFrame::set_dimensions(size_t cols, size_t rows)
{
    _cols = cols;
    _rows = rows;
    switch (_colour)
    {
    case I420:
        if (_cols % 2 == 1) ++_cols;
        if (_rows % 2 == 1) ++_rows;
        break;
    case BGRA:
    case UYVY:
    default:
        // nop
        // Also not caring if colour space not set properly,
        // as this function is for internal use only
        break;
    }
}

void VideoFrame::allocate_memory(size_t data_length)
{
    if (_manage_data and data_length > _data_length)
    {
        _data_length = data_length;
        _data = reinterpret_cast<unsigned char*>(
                    realloc(_data, _data_length * sizeof(unsigned char)));
    }
}

void VideoFrame::free_memory()
{
    if (_manage_data)
    {
        free(_data);
        _data = nullptr;
        _data_length = 0;
        set_dimensions(0, 0);
    }
}

size_t VideoFrame::required_data_length(enum ColourSpace colour,
                                        size_t cols, size_t rows)
{
    if (cols <= 0 or rows <= 0)
        throw BasicException("Frame dimensions must be positive");

    switch (colour)
    {
    case BGRA:
        return cols * rows * 4;

    case I420:
        // 1 for Y plane, 0.5 for UV plane
        return cols * rows * 1.5; // both dimensions always even due to set_dimensions()

    case UYVY:
        return cols * rows * 2; // 2 bytes per pixel, see http://www.fourcc.org/pixel-format/yuv-uyvy/

    default:
        throw BasicException("Colour space indicator not set properly, cannot compute memory requirement");
    }
}

size_t VideoFrame::required_pixel_length(enum ColourSpace colour)
{
    size_t unit_size = sizeof(unsigned char);
    switch(colour)
    {
    case BGRA:
        return 4 * unit_size;
    case I420:
        return 1.5 * unit_size;
    case UYVY:
        return 2 * unit_size;
    default:
        throw BasicException("Colour space not supported");
    }
}

void VideoFrame::set_pixels_black()
{
    memset(_data, 0, _data_length * sizeof(unsigned char));
}

}
<|endoftext|>