<commit_before>/***************************************************************************
                          cryptographyplugin.cpp  -  description
                             -------------------
    begin                : jeu nov 14 2002
    copyright            : (C) 2002 by Olivier Goffart
    email                : ogoffart@tiscalinet.be
 ***************************************************************************/

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

#include <qstylesheet.h>
#include <qtimer.h>
#include <qregexp.h>

#include <kdebug.h>
#include <kaction.h>
#include <kconfig.h>
#include <kgenericfactory.h>

#include "kopetemetacontact.h"
#include "kopetecontactlist.h"
#include "kopetemessagemanagerfactory.h"

#include "cryptographyplugin.h"
#include "cryptographyselectuserkey.h"

#include "kgpginterface.h"

typedef KGenericFactory<CryptographyPlugin> CryptographyPluginFactory;
K_EXPORT_COMPONENT_FACTORY( kopete_cryptography, CryptographyPluginFactory( "kopete_cryptography" ) )

CryptographyPlugin::CryptographyPlugin( QObject *parent, const char *name, const QStringList & /* args */ )
: KopetePlugin( CryptographyPluginFactory::instance(), parent, name ),
		m_cachedPass()
{
	if( !pluginStatic_ )
		pluginStatic_=this;

	connect( KopeteMessageManagerFactory::factory(),
		SIGNAL( aboutToDisplay( KopeteMessage & ) ),
		SLOT( slotIncomingMessage( KopeteMessage & ) ) );
	connect( KopeteMessageManagerFactory::factory(),
		SIGNAL( aboutToSend( KopeteMessage & ) ),
		SLOT( slotOutgoingMessage( KopeteMessage & ) ) );

	m_cachedPass_timer = new QTimer(this, "m_cachedPass_timer" );
	QObject::connect(m_cachedPass_timer, SIGNAL(timeout()), this, SLOT(slotForgetCachedPass() ));

	KAction *action=new KAction( i18n("&Select Cryptography Public Key..."), "kgpg", 0, this, SLOT (slotSelectContactKey()), actionCollection() , "contactSelectKey");
	connect ( KopeteContactList::contactList() , SIGNAL( metaContactSelected(bool)) , action , SLOT(setEnabled(bool)));
	action->setEnabled(KopeteContactList::contactList()->selectedMetaContacts().count()==1 );

	setXMLFile("cryptographyui.rc");
    loadSettings();
	connect(this, SIGNAL(settingsChanged()), this, SLOT( loadSettings() ) );
}

CryptographyPlugin::~CryptographyPlugin()
{
	pluginStatic_ = 0L;
}

void CryptographyPlugin::loadSettings()
{
	KConfig *config = KGlobal::config();
	config->setGroup("Cryptography Plugin");

	mPrivateKeyID = config->readEntry("PGP_private_key");
	mAlsoMyKey = config->readBoolEntry("Also_my_key", false);

	if(config->readBoolEntry("Cache_Till_App_Close", false))
	  mCachePassPhrase = Keep;
	if(config->readBoolEntry("Cache_Till_Time", false))
	  mCachePassPhrase = Time;
	if(config->readBoolEntry("Cache_Never", false))
	  mCachePassPhrase = Never;
	mCacheTime = config->readNumEntry("Cache_Time", 15);
	mAskPassPhrase = config->readBoolEntry("No_Passphrase_Handling", false);
}

CryptographyPlugin* CryptographyPlugin::plugin()
{
	return pluginStatic_ ;
}

CryptographyPlugin* CryptographyPlugin::pluginStatic_ = 0L;

QCString CryptographyPlugin::cachedPass()
{
	return pluginStatic_->m_cachedPass;
}

void CryptographyPlugin::setCachedPass(const QCString& p)
{
	if(pluginStatic_->mCacheMode==Never)
		return;
	if(pluginStatic_->mCacheMode==Time)
		pluginStatic_->m_cachedPass_timer->start(pluginStatic_->mCacheTime * 60000, false);

	pluginStatic_->m_cachedPass=p;
}

bool CryptographyPlugin::passphraseHandling()
{
	return !pluginStatic_->mAskPassPhrase;
}

/*KActionCollection *CryptographyPlugin::customChatActions(KopeteMessageManager *KMM)
{
	delete m_actionCollection;

	m_actionCollection = new KActionCollection(this);
	KAction *actionTranslate = new KAction( i18n ("Translate"), 0,
		this, SLOT( slotTranslateChat() ), m_actionCollection, "actionTranslate" );
	m_actionCollection->insert( actionTranslate );

	m_currentMessageManager=KMM;
	return m_actionCollection;
}*/

void CryptographyPlugin::slotIncomingMessage( KopeteMessage& msg )
{
 	QString body=msg.plainBody();
	if(!body.startsWith("-----BEGIN PGP MESSAGE----"))
		return;

	if(msg.direction() != KopeteMessage::Inbound)
	{
		QString plainBody;
		if ( m_cachedMessages.contains( body ) )
		{
			plainBody = m_cachedMessages[ body ];
			m_cachedMessages.remove( body );
		}
		else
		{
			plainBody = KgpgInterface::KgpgDecryptText( body, mPrivateKeyID );
		}

		if(!plainBody.isEmpty())
		{
			//this is the same algoritm as in KopeteMessage::escapedBody();
			QString escapedBody = QStyleSheet::escape( plainBody );
			escapedBody.replace( QString::fromLatin1( "\n" ), QString::fromLatin1( "
" ) )
				.replace( QString::fromLatin1( "\t" ), QString::fromLatin1( "    " ) )
				.replace( QRegExp( QString::fromLatin1( "\\s\\s" ) ), QString::fromLatin1( "  " ) );

			msg.setBody("

"+i18n("Outgoing Encrypted Message: ")+"

"+escapedBody+"

"
				,KopeteMessage::RichText);
		}

		//if there are too messages in cache, clear the cache
		if(m_cachedMessages.count()>5)
			m_cachedMessages.clear();
		return;
	}

	body=KgpgInterface::KgpgDecryptText(body, mPrivateKeyID);

	if(!body.isEmpty())
	{
		//this is the same algoritm as in KopeteMessage::escapedBody();
		QString escapedBody = QStyleSheet::escape( body );
		escapedBody.replace( QString::fromLatin1( "\n" ), QString::fromLatin1( "
" ) )
			.replace( QString::fromLatin1( "\t" ), QString::fromLatin1( "    " ) )
			.replace( QRegExp( QString::fromLatin1( "\\s\\s" ) ), QString::fromLatin1( "  " ) );

		body="

"+i18n("Incoming Encrypted Message: ")+"

"+ escapedBody  +"

";
		msg.setBody(body,KopeteMessage::RichText);
	}

}

void CryptographyPlugin::slotOutgoingMessage( KopeteMessage& msg )
{
	if(msg.direction() != KopeteMessage::Outbound)
		return;

	QStringList keys;
	QPtrList<KopeteContact> contactlist = msg.to();
	for( KopeteContact *c = contactlist.first(); c; c = contactlist.next() )
	{
		QString tmpKey;
		if( c->metaContact() )
			tmpKey = c->metaContact()->pluginData( this, "gpgKey" );
		if( tmpKey.isEmpty() )
		{
			kdDebug( 14303 ) << "CryptographyPlugin::slotOutgoingMessage: no key selected for one contact" <<endl;
			return;
		}
		keys.append( tmpKey );
	}
	if(mAlsoMyKey) //encrypt also with the self key
		keys.append( mPrivateKeyID );

	QString key = keys.join( " " );

	if(key.isEmpty())
	{
		kdDebug(14303) << "CryptographyPlugin::slotOutgoingMessage: empty key" <<endl;
		return;
	}

	QString original=msg.plainBody();

	/* Code From KGPG */

	//////////////////              encode from editor
	QString encryptOptions="";

	//if (utrust==true)
		encryptOptions+=" --always-trust ";
	//if (arm==true)
		encryptOptions+=" --armor ";

	/* if (pubcryptography==true)
	{
		if (gpgversion<120) encryptOptions+=" --compress-algo 1 --cipher-algo cast5 ";
		else encryptOptions+=" --cryptography6 ";
	}*/

// if (selec==NULL) {KMessageBox::sorry(0,i18n("You have not chosen an encryption key..."));return;}

	QString resultat=KgpgInterface::KgpgEncryptText(original,key,encryptOptions);
	if (!resultat.isEmpty())
	{
		msg.setBody(resultat,KopeteMessage::PlainText);
		m_cachedMessages.insert(resultat,original);
	}
	else
		kdDebug(14303) << "CryptographyPlugin::slotOutgoingMessage: empty result" <<endl;

}

void CryptographyPlugin::slotSelectContactKey()
{
	KopeteMetaContact *m=KopeteContactList::contactList()->selectedMetaContacts().first();
	if(!m)
		return;
	QString key = m->pluginData( this, "gpgKey" );
	CryptographySelectUserKey *opts = new CryptographySelectUserKey( key, m );
	opts->exec();
	if( opts->result() )
	{
		key = opts->publicKey();
		m->setPluginData( this, "gpgKey", key );
	}
	delete opts;
}

void CryptographyPlugin::slotForgetCachedPass()
{
	m_cachedPass=QCString();
	m_cachedPass_timer->stop();
}

#include "cryptographyplugin.moc"

// vim: set noet ts=4 sts=4 sw=4:

<commit_msg>indentation fix CVS_SILENT<commit_after>/***************************************************************************
                          cryptographyplugin.cpp  -  description
                             -------------------
    begin                : jeu nov 14 2002
    copyright            : (C) 2002 by Olivier Goffart
    email                : ogoffart@tiscalinet.be
 ***************************************************************************/

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

#include <qstylesheet.h>
#include <qtimer.h>
#include <qregexp.h>

#include <kdebug.h>
#include <kaction.h>
#include <kconfig.h>
#include <kgenericfactory.h>

#include "kopetemetacontact.h"
#include "kopetecontactlist.h"
#include "kopetemessagemanagerfactory.h"

#include "cryptographyplugin.h"
#include "cryptographyselectuserkey.h"

#include "kgpginterface.h"

typedef KGenericFactory<CryptographyPlugin> CryptographyPluginFactory;
K_EXPORT_COMPONENT_FACTORY( kopete_cryptography, CryptographyPluginFactory( "kopete_cryptography" ) )

CryptographyPlugin::CryptographyPlugin( QObject *parent, const char *name, const QStringList & /* args */ )
: KopetePlugin( CryptographyPluginFactory::instance(), parent, name ),
		m_cachedPass()
{
	if( !pluginStatic_ )
		pluginStatic_=this;

	connect( KopeteMessageManagerFactory::factory(),
		SIGNAL( aboutToDisplay( KopeteMessage & ) ),
		SLOT( slotIncomingMessage( KopeteMessage & ) ) );
	connect( KopeteMessageManagerFactory::factory(),
		SIGNAL( aboutToSend( KopeteMessage & ) ),
		SLOT( slotOutgoingMessage( KopeteMessage & ) ) );

	m_cachedPass_timer = new QTimer(this, "m_cachedPass_timer" );
	QObject::connect(m_cachedPass_timer, SIGNAL(timeout()), this, SLOT(slotForgetCachedPass() ));

	KAction *action=new KAction( i18n("&Select Cryptography Public Key..."), "kgpg", 0, this, SLOT (slotSelectContactKey()), actionCollection() , "contactSelectKey");
	connect ( KopeteContactList::contactList() , SIGNAL( metaContactSelected(bool)) , action , SLOT(setEnabled(bool)));
	action->setEnabled(KopeteContactList::contactList()->selectedMetaContacts().count()==1 );

	setXMLFile("cryptographyui.rc");
	loadSettings();
	connect(this, SIGNAL(settingsChanged()), this, SLOT( loadSettings() ) );
}

CryptographyPlugin::~CryptographyPlugin()
{
	pluginStatic_ = 0L;
}

void CryptographyPlugin::loadSettings()
{
	KConfig *config = KGlobal::config();
	config->setGroup("Cryptography Plugin");

	mPrivateKeyID = config->readEntry("PGP_private_key");
	mAlsoMyKey = config->readBoolEntry("Also_my_key", false);

	if(config->readBoolEntry("Cache_Till_App_Close", false))
	  mCachePassPhrase = Keep;
	if(config->readBoolEntry("Cache_Till_Time", false))
	  mCachePassPhrase = Time;
	if(config->readBoolEntry("Cache_Never", false))
	  mCachePassPhrase = Never;
	mCacheTime = config->readNumEntry("Cache_Time", 15);
	mAskPassPhrase = config->readBoolEntry("No_Passphrase_Handling", false);
}

CryptographyPlugin* CryptographyPlugin::plugin()
{
	return pluginStatic_ ;
}

CryptographyPlugin* CryptographyPlugin::pluginStatic_ = 0L;

QCString CryptographyPlugin::cachedPass()
{
	return pluginStatic_->m_cachedPass;
}

void CryptographyPlugin::setCachedPass(const QCString& p)
{
	if(pluginStatic_->mCacheMode==Never)
		return;
	if(pluginStatic_->mCacheMode==Time)
		pluginStatic_->m_cachedPass_timer->start(pluginStatic_->mCacheTime * 60000, false);

	pluginStatic_->m_cachedPass=p;
}

bool CryptographyPlugin::passphraseHandling()
{
	return !pluginStatic_->mAskPassPhrase;
}

/*KActionCollection *CryptographyPlugin::customChatActions(KopeteMessageManager *KMM)
{
	delete m_actionCollection;

	m_actionCollection = new KActionCollection(this);
	KAction *actionTranslate = new KAction( i18n ("Translate"), 0,
		this, SLOT( slotTranslateChat() ), m_actionCollection, "actionTranslate" );
	m_actionCollection->insert( actionTranslate );

	m_currentMessageManager=KMM;
	return m_actionCollection;
}*/

void CryptographyPlugin::slotIncomingMessage( KopeteMessage& msg )
{
 	QString body=msg.plainBody();
	if(!body.startsWith("-----BEGIN PGP MESSAGE----"))
		return;

	if(msg.direction() != KopeteMessage::Inbound)
	{
		QString plainBody;
		if ( m_cachedMessages.contains( body ) )
		{
			plainBody = m_cachedMessages[ body ];
			m_cachedMessages.remove( body );
		}
		else
		{
			plainBody = KgpgInterface::KgpgDecryptText( body, mPrivateKeyID );
		}

		if(!plainBody.isEmpty())
		{
			//this is the same algoritm as in KopeteMessage::escapedBody();
			QString escapedBody = QStyleSheet::escape( plainBody );
			escapedBody.replace( QString::fromLatin1( "\n" ), QString::fromLatin1( "
" ) )
				.replace( QString::fromLatin1( "\t" ), QString::fromLatin1( "    " ) )
				.replace( QRegExp( QString::fromLatin1( "\\s\\s" ) ), QString::fromLatin1( "  " ) );

			msg.setBody("

"+i18n("Outgoing Encrypted Message: ")+"

"+escapedBody+"

"
				,KopeteMessage::RichText);
		}

		//if there are too messages in cache, clear the cache
		if(m_cachedMessages.count()>5)
			m_cachedMessages.clear();
		return;
	}

	body=KgpgInterface::KgpgDecryptText(body, mPrivateKeyID);

	if(!body.isEmpty())
	{
		//this is the same algoritm as in KopeteMessage::escapedBody();
		QString escapedBody = QStyleSheet::escape( body );
		escapedBody.replace( QString::fromLatin1( "\n" ), QString::fromLatin1( "
" ) )
			.replace( QString::fromLatin1( "\t" ), QString::fromLatin1( "    " ) )
			.replace( QRegExp( QString::fromLatin1( "\\s\\s" ) ), QString::fromLatin1( "  " ) );

		body="

"+i18n("Incoming Encrypted Message: ")+"

"+ escapedBody  +"

";
		msg.setBody(body,KopeteMessage::RichText);
	}

}

void CryptographyPlugin::slotOutgoingMessage( KopeteMessage& msg )
{
	if(msg.direction() != KopeteMessage::Outbound)
		return;

	QStringList keys;
	QPtrList<KopeteContact> contactlist = msg.to();
	for( KopeteContact *c = contactlist.first(); c; c = contactlist.next() )
	{
		QString tmpKey;
		if( c->metaContact() )
			tmpKey = c->metaContact()->pluginData( this, "gpgKey" );
		if( tmpKey.isEmpty() )
		{
			kdDebug( 14303 ) << "CryptographyPlugin::slotOutgoingMessage: no key selected for one contact" <<endl;
			return;
		}
		keys.append( tmpKey );
	}
	if(mAlsoMyKey) //encrypt also with the self key
		keys.append( mPrivateKeyID );

	QString key = keys.join( " " );

	if(key.isEmpty())
	{
		kdDebug(14303) << "CryptographyPlugin::slotOutgoingMessage: empty key" <<endl;
		return;
	}

	QString original=msg.plainBody();

	/* Code From KGPG */

	//////////////////              encode from editor
	QString encryptOptions="";

	//if (utrust==true)
		encryptOptions+=" --always-trust ";
	//if (arm==true)
		encryptOptions+=" --armor ";

	/* if (pubcryptography==true)
	{
		if (gpgversion<120) encryptOptions+=" --compress-algo 1 --cipher-algo cast5 ";
		else encryptOptions+=" --cryptography6 ";
	}*/

// if (selec==NULL) {KMessageBox::sorry(0,i18n("You have not chosen an encryption key..."));return;}

	QString resultat=KgpgInterface::KgpgEncryptText(original,key,encryptOptions);
	if (!resultat.isEmpty())
	{
		msg.setBody(resultat,KopeteMessage::PlainText);
		m_cachedMessages.insert(resultat,original);
	}
	else
		kdDebug(14303) << "CryptographyPlugin::slotOutgoingMessage: empty result" <<endl;

}

void CryptographyPlugin::slotSelectContactKey()
{
	KopeteMetaContact *m=KopeteContactList::contactList()->selectedMetaContacts().first();
	if(!m)
		return;
	QString key = m->pluginData( this, "gpgKey" );
	CryptographySelectUserKey *opts = new CryptographySelectUserKey( key, m );
	opts->exec();
	if( opts->result() )
	{
		key = opts->publicKey();
		m->setPluginData( this, "gpgKey", key );
	}
	delete opts;
}

void CryptographyPlugin::slotForgetCachedPass()
{
	m_cachedPass=QCString();
	m_cachedPass_timer->stop();
}

#include "cryptographyplugin.moc"

// vim: set noet ts=4 sts=4 sw=4:

<|endoftext|>


<commit_before>//  Copyright (c) 2011-present, Facebook, Inc.  All rights reserved.
//  This source code is licensed under the BSD-style license found in the
//  LICENSE file in the root directory of this source tree. An additional grant
//  of patent rights can be found in the PATENTS file in the same directory.

#include "db/compaction_iterator.h"

#include <string>
#include <vector>

#include "util/testharness.h"
#include "util/testutil.h"

namespace rocksdb {

class LoggingForwardVectorIterator : public InternalIterator {
 public:
  struct Action {
    enum class Type {
      SEEK_TO_FIRST,
      SEEK,
      NEXT,
    };

    Type type;
    std::string arg;

    explicit Action(Type _type, std::string _arg = "")
        : type(_type), arg(_arg) {}

    bool operator==(const Action& rhs) const {
      return std::tie(type, arg) == std::tie(rhs.type, rhs.arg);
    }
  };

  LoggingForwardVectorIterator(const std::vector<std::string>& keys,
                               const std::vector<std::string>& values)
      : keys_(keys), values_(values), current_(keys.size()) {
    assert(keys_.size() == values_.size());
  }

  virtual bool Valid() const override { return current_ < keys_.size(); }

  virtual void SeekToFirst() override {
    log.emplace_back(Action::Type::SEEK_TO_FIRST);
    current_ = 0;
  }
  virtual void SeekToLast() override { assert(false); }

  virtual void Seek(const Slice& target) override {
    log.emplace_back(Action::Type::SEEK, target.ToString());
    current_ = std::lower_bound(keys_.begin(), keys_.end(), target.ToString()) -
               keys_.begin();
  }

  virtual void SeekForPrev(const Slice& target) override { assert(false); }

  virtual void Next() override {
    assert(Valid());
    log.emplace_back(Action::Type::NEXT);
    current_++;
  }
  virtual void Prev() override { assert(false); }

  virtual Slice key() const override {
    assert(Valid());
    return Slice(keys_[current_]);
  }
  virtual Slice value() const override {
    assert(Valid());
    return Slice(values_[current_]);
  }

  virtual Status status() const override { return Status::OK(); }

  std::vector<Action> log;

 private:
  std::vector<std::string> keys_;
  std::vector<std::string> values_;
  size_t current_;
};

class FakeCompaction : public CompactionIterator::CompactionProxy {
 public:
  FakeCompaction() = default;

  virtual int level(size_t compaction_input_level) const { return 0; }
  virtual bool KeyNotExistsBeyondOutputLevel(
      const Slice& user_key, std::vector<size_t>* level_ptrs) const {
    return false;
  }
  virtual bool bottommost_level() const { return false; }
  virtual int number_levels() const { return 1; }
  virtual Slice GetLargestUserKey() const {
    return "\xff\xff\xff\xff\xff\xff\xff\xff\xff";
  }
};

class CompactionIteratorTest : public testing::Test {
 public:
  CompactionIteratorTest()
      : cmp_(BytewiseComparator()), icmp_(cmp_), snapshots_({}) {}

  void InitIterators(const std::vector<std::string>& ks,
                     const std::vector<std::string>& vs,
                     const std::vector<std::string>& range_del_ks,
                     const std::vector<std::string>& range_del_vs,
                     SequenceNumber last_sequence,
                     MergeOperator* merge_op = nullptr,
                     CompactionFilter* filter = nullptr) {
    std::unique_ptr<InternalIterator> range_del_iter(
        new test::VectorIterator(range_del_ks, range_del_vs));
    range_del_agg_.reset(new RangeDelAggregator(icmp_, snapshots_));
    ASSERT_OK(range_del_agg_->AddTombstones(std::move(range_del_iter)));

    std::unique_ptr<CompactionIterator::CompactionProxy> compaction;
    if (filter) {
      compaction.reset(new FakeCompaction());
    }

    merge_helper_.reset(new MergeHelper(Env::Default(), cmp_, merge_op, filter,
                                        nullptr, 0U, false, 0));
    iter_.reset(new LoggingForwardVectorIterator(ks, vs));
    iter_->SeekToFirst();
    c_iter_.reset(new CompactionIterator(
        iter_.get(), cmp_, merge_helper_.get(), last_sequence, &snapshots_,
        kMaxSequenceNumber, Env::Default(), false, range_del_agg_.get(),
        std::move(compaction), filter));
  }

  void AddSnapshot(SequenceNumber snapshot) { snapshots_.push_back(snapshot); }

  const Comparator* cmp_;
  const InternalKeyComparator icmp_;
  std::vector<SequenceNumber> snapshots_;
  std::unique_ptr<MergeHelper> merge_helper_;
  std::unique_ptr<LoggingForwardVectorIterator> iter_;
  std::unique_ptr<CompactionIterator> c_iter_;
  std::unique_ptr<RangeDelAggregator> range_del_agg_;
};

// It is possible that the output of the compaction iterator is empty even if
// the input is not.
TEST_F(CompactionIteratorTest, EmptyResult) {
  InitIterators({test::KeyStr("a", 5, kTypeSingleDeletion),
                 test::KeyStr("a", 3, kTypeValue)},
                {"", "val"}, {}, {}, 5);
  c_iter_->SeekToFirst();
  ASSERT_FALSE(c_iter_->Valid());
}

// If there is a corruption after a single deletion, the corrupted key should
// be preserved.
TEST_F(CompactionIteratorTest, CorruptionAfterSingleDeletion) {
  InitIterators({test::KeyStr("a", 5, kTypeSingleDeletion),
                 test::KeyStr("a", 3, kTypeValue, true),
                 test::KeyStr("b", 10, kTypeValue)},
                {"", "val", "val2"}, {}, {}, 10);
  c_iter_->SeekToFirst();
  ASSERT_TRUE(c_iter_->Valid());
  ASSERT_EQ(test::KeyStr("a", 5, kTypeSingleDeletion),
            c_iter_->key().ToString());
  c_iter_->Next();
  ASSERT_TRUE(c_iter_->Valid());
  ASSERT_EQ(test::KeyStr("a", 3, kTypeValue, true), c_iter_->key().ToString());
  c_iter_->Next();
  ASSERT_TRUE(c_iter_->Valid());
  ASSERT_EQ(test::KeyStr("b", 10, kTypeValue), c_iter_->key().ToString());
  c_iter_->Next();
  ASSERT_FALSE(c_iter_->Valid());
}

TEST_F(CompactionIteratorTest, SimpleRangeDeletion) {
  InitIterators({test::KeyStr("morning", 5, kTypeValue),
                 test::KeyStr("morning", 2, kTypeValue),
                 test::KeyStr("night", 3, kTypeValue)},
                {"zao", "zao", "wan"},
                {test::KeyStr("ma", 4, kTypeRangeDeletion)}, {"mz"}, 5);
  c_iter_->SeekToFirst();
  ASSERT_TRUE(c_iter_->Valid());
  ASSERT_EQ(test::KeyStr("morning", 5, kTypeValue), c_iter_->key().ToString());
  c_iter_->Next();
  ASSERT_TRUE(c_iter_->Valid());
  ASSERT_EQ(test::KeyStr("night", 3, kTypeValue), c_iter_->key().ToString());
  c_iter_->Next();
  ASSERT_FALSE(c_iter_->Valid());
}

TEST_F(CompactionIteratorTest, RangeDeletionWithSnapshots) {
  AddSnapshot(10);
  std::vector<std::string> ks1;
  ks1.push_back(test::KeyStr("ma", 28, kTypeRangeDeletion));
  std::vector<std::string> vs1{"mz"};
  std::vector<std::string> ks2{test::KeyStr("morning", 15, kTypeValue),
                               test::KeyStr("morning", 5, kTypeValue),
                               test::KeyStr("night", 40, kTypeValue),
                               test::KeyStr("night", 20, kTypeValue)};
  std::vector<std::string> vs2{"zao 15", "zao 5", "wan 40", "wan 20"};
  InitIterators(ks2, vs2, ks1, vs1, 40);
  c_iter_->SeekToFirst();
  ASSERT_TRUE(c_iter_->Valid());
  ASSERT_EQ(test::KeyStr("morning", 5, kTypeValue), c_iter_->key().ToString());
  c_iter_->Next();
  ASSERT_TRUE(c_iter_->Valid());
  ASSERT_EQ(test::KeyStr("night", 40, kTypeValue), c_iter_->key().ToString());
  c_iter_->Next();
  ASSERT_FALSE(c_iter_->Valid());
}

TEST_F(CompactionIteratorTest, CompactionFilterSkipUntil) {
  // Expect no merging attempts.
  class MergeOp : public MergeOperator {
   public:
    bool FullMergeV2(const MergeOperationInput& merge_in,
                     MergeOperationOutput* merge_out) const override {
      ADD_FAILURE();
      return false;
    }
    bool PartialMergeMulti(const Slice& key,
                           const std::deque<Slice>& operand_list,
                           std::string* new_value,
                           Logger* logger) const override {
      ADD_FAILURE();
      return false;
    }
    const char* Name() const override {
      return "CompactionIteratorTest.CompactionFilterSkipUntil::MergeOp";
    }
  };

  class Filter : public CompactionFilter {
    virtual Decision FilterV2(int level, const Slice& key, ValueType t,
                              const Slice& existing_value,
                              std::string* new_value,
                              std::string* skip_until) const {
      std::string k = key.ToString();
      std::string v = existing_value.ToString();
      // See InitIterators() call below for the sequence of keys and their
      // filtering decisions. Here we closely assert that compaction filter is
      // called with the expected keys and only them, and with the right values.
      if (k == "a") {
        EXPECT_EQ(ValueType::kValue, t);
        EXPECT_EQ("av50", v);
        return Decision::kKeep;
      }
      if (k == "b") {
        EXPECT_EQ(ValueType::kValue, t);
        EXPECT_EQ("bv60", v);
        *skip_until = "d+";
        return Decision::kRemoveAndSkipUntil;
      }
      if (k == "e") {
        EXPECT_EQ(ValueType::kMergeOperand, t);
        EXPECT_EQ("em71", v);
        return Decision::kKeep;
      }
      if (k == "f") {
        if (v == "fm65") {
          EXPECT_EQ(ValueType::kMergeOperand, t);
          *skip_until = "f";
        } else {
          EXPECT_EQ("fm30", v);
          EXPECT_EQ(ValueType::kMergeOperand, t);
          *skip_until = "g+";
        }
        return Decision::kRemoveAndSkipUntil;
      }
      if (k == "h") {
        EXPECT_EQ(ValueType::kValue, t);
        EXPECT_EQ("hv91", v);
        return Decision::kKeep;
      }
      if (k == "i") {
        EXPECT_EQ(ValueType::kValue, t);
        EXPECT_EQ("iv95", v);
        *skip_until = "z";
        return Decision::kRemoveAndSkipUntil;
      }
      ADD_FAILURE();
      return Decision::kKeep;
    }

    const char* Name() const override {
      return "CompactionIteratorTest.CompactionFilterSkipUntil::Filter";
    }
  };

  MergeOp merge_op;
  Filter filter;
  InitIterators(
      {test::KeyStr("a", 50, kTypeValue),  // keep
       test::KeyStr("a", 45, kTypeMerge),
       test::KeyStr("b", 60, kTypeValue),  // skip to "d+"
       test::KeyStr("b", 40, kTypeValue), test::KeyStr("c", 35, kTypeValue),
       test::KeyStr("d", 70, kTypeMerge),
       test::KeyStr("e", 71, kTypeMerge),  // keep
       test::KeyStr("f", 65, kTypeMerge),  // skip to "f", aka keep
       test::KeyStr("f", 30, kTypeMerge),  // skip to "g+"
       test::KeyStr("f", 25, kTypeValue), test::KeyStr("g", 90, kTypeValue),
       test::KeyStr("h", 91, kTypeValue),  // keep
       test::KeyStr("i", 95, kTypeValue),  // skip to "z"
       test::KeyStr("j", 99, kTypeValue)},
      {"av50", "am45", "bv60", "bv40", "cv35", "dm70", "em71", "fm65", "fm30",
       "fv25", "gv90", "hv91", "iv95", "jv99"},
      {}, {}, kMaxSequenceNumber, &merge_op, &filter);

  // Compaction should output just "a", "e" and "h" keys.
  c_iter_->SeekToFirst();
  ASSERT_TRUE(c_iter_->Valid());
  ASSERT_EQ(test::KeyStr("a", 50, kTypeValue), c_iter_->key().ToString());
  ASSERT_EQ("av50", c_iter_->value().ToString());
  c_iter_->Next();
  ASSERT_TRUE(c_iter_->Valid());
  ASSERT_EQ(test::KeyStr("e", 71, kTypeMerge), c_iter_->key().ToString());
  ASSERT_EQ("em71", c_iter_->value().ToString());
  c_iter_->Next();
  ASSERT_TRUE(c_iter_->Valid());
  ASSERT_EQ(test::KeyStr("h", 91, kTypeValue), c_iter_->key().ToString());
  ASSERT_EQ("hv91", c_iter_->value().ToString());
  c_iter_->Next();
  ASSERT_FALSE(c_iter_->Valid());

  // Check that the compaction iterator did the correct sequence of calls on
  // the underlying iterator.
  using A = LoggingForwardVectorIterator::Action;
  using T = A::Type;
  std::vector<A> expected_actions = {
      A(T::SEEK_TO_FIRST),
      A(T::NEXT),
      A(T::NEXT),
      A(T::SEEK, test::KeyStr("d+", kMaxSequenceNumber, kValueTypeForSeek)),
      A(T::NEXT),
      A(T::NEXT),
      A(T::SEEK, test::KeyStr("g+", kMaxSequenceNumber, kValueTypeForSeek)),
      A(T::NEXT),
      A(T::SEEK, test::KeyStr("z", kMaxSequenceNumber, kValueTypeForSeek))};
  ASSERT_EQ(expected_actions, iter_->log);
}

}  // namespace rocksdb

int main(int argc, char** argv) {
  ::testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS();
}
<commit_msg>fix clang build<commit_after>//  Copyright (c) 2011-present, Facebook, Inc.  All rights reserved.
//  This source code is licensed under the BSD-style license found in the
//  LICENSE file in the root directory of this source tree. An additional grant
//  of patent rights can be found in the PATENTS file in the same directory.

#include "db/compaction_iterator.h"

#include <string>
#include <vector>

#include "util/testharness.h"
#include "util/testutil.h"

namespace rocksdb {

class LoggingForwardVectorIterator : public InternalIterator {
 public:
  struct Action {
    enum class Type {
      SEEK_TO_FIRST,
      SEEK,
      NEXT,
    };

    Type type;
    std::string arg;

    explicit Action(Type _type, std::string _arg = "")
        : type(_type), arg(_arg) {}

    bool operator==(const Action& rhs) const {
      return std::tie(type, arg) == std::tie(rhs.type, rhs.arg);
    }
  };

  LoggingForwardVectorIterator(const std::vector<std::string>& keys,
                               const std::vector<std::string>& values)
      : keys_(keys), values_(values), current_(keys.size()) {
    assert(keys_.size() == values_.size());
  }

  virtual bool Valid() const override { return current_ < keys_.size(); }

  virtual void SeekToFirst() override {
    log.emplace_back(Action::Type::SEEK_TO_FIRST);
    current_ = 0;
  }
  virtual void SeekToLast() override { assert(false); }

  virtual void Seek(const Slice& target) override {
    log.emplace_back(Action::Type::SEEK, target.ToString());
    current_ = std::lower_bound(keys_.begin(), keys_.end(), target.ToString()) -
               keys_.begin();
  }

  virtual void SeekForPrev(const Slice& target) override { assert(false); }

  virtual void Next() override {
    assert(Valid());
    log.emplace_back(Action::Type::NEXT);
    current_++;
  }
  virtual void Prev() override { assert(false); }

  virtual Slice key() const override {
    assert(Valid());
    return Slice(keys_[current_]);
  }
  virtual Slice value() const override {
    assert(Valid());
    return Slice(values_[current_]);
  }

  virtual Status status() const override { return Status::OK(); }

  std::vector<Action> log;

 private:
  std::vector<std::string> keys_;
  std::vector<std::string> values_;
  size_t current_;
};

class FakeCompaction : public CompactionIterator::CompactionProxy {
 public:
  FakeCompaction() = default;

  virtual int level(size_t compaction_input_level) const { return 0; }
  virtual bool KeyNotExistsBeyondOutputLevel(
      const Slice& user_key, std::vector<size_t>* level_ptrs) const {
    return false;
  }
  virtual bool bottommost_level() const { return false; }
  virtual int number_levels() const { return 1; }
  virtual Slice GetLargestUserKey() const {
    return "\xff\xff\xff\xff\xff\xff\xff\xff\xff";
  }
};

class CompactionIteratorTest : public testing::Test {
 public:
  CompactionIteratorTest()
      : cmp_(BytewiseComparator()), icmp_(cmp_), snapshots_({}) {}

  void InitIterators(const std::vector<std::string>& ks,
                     const std::vector<std::string>& vs,
                     const std::vector<std::string>& range_del_ks,
                     const std::vector<std::string>& range_del_vs,
                     SequenceNumber last_sequence,
                     MergeOperator* merge_op = nullptr,
                     CompactionFilter* filter = nullptr) {
    std::unique_ptr<InternalIterator> range_del_iter(
        new test::VectorIterator(range_del_ks, range_del_vs));
    range_del_agg_.reset(new RangeDelAggregator(icmp_, snapshots_));
    ASSERT_OK(range_del_agg_->AddTombstones(std::move(range_del_iter)));

    std::unique_ptr<CompactionIterator::CompactionProxy> compaction;
    if (filter) {
      compaction.reset(new FakeCompaction());
    }

    merge_helper_.reset(new MergeHelper(Env::Default(), cmp_, merge_op, filter,
                                        nullptr, 0U, false, 0));
    iter_.reset(new LoggingForwardVectorIterator(ks, vs));
    iter_->SeekToFirst();
    c_iter_.reset(new CompactionIterator(
        iter_.get(), cmp_, merge_helper_.get(), last_sequence, &snapshots_,
        kMaxSequenceNumber, Env::Default(), false, range_del_agg_.get(),
        std::move(compaction), filter));
  }

  void AddSnapshot(SequenceNumber snapshot) { snapshots_.push_back(snapshot); }

  const Comparator* cmp_;
  const InternalKeyComparator icmp_;
  std::vector<SequenceNumber> snapshots_;
  std::unique_ptr<MergeHelper> merge_helper_;
  std::unique_ptr<LoggingForwardVectorIterator> iter_;
  std::unique_ptr<CompactionIterator> c_iter_;
  std::unique_ptr<RangeDelAggregator> range_del_agg_;
};

// It is possible that the output of the compaction iterator is empty even if
// the input is not.
TEST_F(CompactionIteratorTest, EmptyResult) {
  InitIterators({test::KeyStr("a", 5, kTypeSingleDeletion),
                 test::KeyStr("a", 3, kTypeValue)},
                {"", "val"}, {}, {}, 5);
  c_iter_->SeekToFirst();
  ASSERT_FALSE(c_iter_->Valid());
}

// If there is a corruption after a single deletion, the corrupted key should
// be preserved.
TEST_F(CompactionIteratorTest, CorruptionAfterSingleDeletion) {
  InitIterators({test::KeyStr("a", 5, kTypeSingleDeletion),
                 test::KeyStr("a", 3, kTypeValue, true),
                 test::KeyStr("b", 10, kTypeValue)},
                {"", "val", "val2"}, {}, {}, 10);
  c_iter_->SeekToFirst();
  ASSERT_TRUE(c_iter_->Valid());
  ASSERT_EQ(test::KeyStr("a", 5, kTypeSingleDeletion),
            c_iter_->key().ToString());
  c_iter_->Next();
  ASSERT_TRUE(c_iter_->Valid());
  ASSERT_EQ(test::KeyStr("a", 3, kTypeValue, true), c_iter_->key().ToString());
  c_iter_->Next();
  ASSERT_TRUE(c_iter_->Valid());
  ASSERT_EQ(test::KeyStr("b", 10, kTypeValue), c_iter_->key().ToString());
  c_iter_->Next();
  ASSERT_FALSE(c_iter_->Valid());
}

TEST_F(CompactionIteratorTest, SimpleRangeDeletion) {
  InitIterators({test::KeyStr("morning", 5, kTypeValue),
                 test::KeyStr("morning", 2, kTypeValue),
                 test::KeyStr("night", 3, kTypeValue)},
                {"zao", "zao", "wan"},
                {test::KeyStr("ma", 4, kTypeRangeDeletion)}, {"mz"}, 5);
  c_iter_->SeekToFirst();
  ASSERT_TRUE(c_iter_->Valid());
  ASSERT_EQ(test::KeyStr("morning", 5, kTypeValue), c_iter_->key().ToString());
  c_iter_->Next();
  ASSERT_TRUE(c_iter_->Valid());
  ASSERT_EQ(test::KeyStr("night", 3, kTypeValue), c_iter_->key().ToString());
  c_iter_->Next();
  ASSERT_FALSE(c_iter_->Valid());
}

TEST_F(CompactionIteratorTest, RangeDeletionWithSnapshots) {
  AddSnapshot(10);
  std::vector<std::string> ks1;
  ks1.push_back(test::KeyStr("ma", 28, kTypeRangeDeletion));
  std::vector<std::string> vs1{"mz"};
  std::vector<std::string> ks2{test::KeyStr("morning", 15, kTypeValue),
                               test::KeyStr("morning", 5, kTypeValue),
                               test::KeyStr("night", 40, kTypeValue),
                               test::KeyStr("night", 20, kTypeValue)};
  std::vector<std::string> vs2{"zao 15", "zao 5", "wan 40", "wan 20"};
  InitIterators(ks2, vs2, ks1, vs1, 40);
  c_iter_->SeekToFirst();
  ASSERT_TRUE(c_iter_->Valid());
  ASSERT_EQ(test::KeyStr("morning", 5, kTypeValue), c_iter_->key().ToString());
  c_iter_->Next();
  ASSERT_TRUE(c_iter_->Valid());
  ASSERT_EQ(test::KeyStr("night", 40, kTypeValue), c_iter_->key().ToString());
  c_iter_->Next();
  ASSERT_FALSE(c_iter_->Valid());
}

TEST_F(CompactionIteratorTest, CompactionFilterSkipUntil) {
  // Expect no merging attempts.
  class MergeOp : public MergeOperator {
   public:
    bool FullMergeV2(const MergeOperationInput& merge_in,
                     MergeOperationOutput* merge_out) const override {
      ADD_FAILURE();
      return false;
    }
    bool PartialMergeMulti(const Slice& key,
                           const std::deque<Slice>& operand_list,
                           std::string* new_value,
                           Logger* logger) const override {
      ADD_FAILURE();
      return false;
    }
    const char* Name() const override {
      return "CompactionIteratorTest.CompactionFilterSkipUntil::MergeOp";
    }
  };

  class Filter : public CompactionFilter {
    virtual Decision FilterV2(int level, const Slice& key, ValueType t,
                              const Slice& existing_value,
                              std::string* new_value,
                              std::string* skip_until) const override {
      std::string k = key.ToString();
      std::string v = existing_value.ToString();
      // See InitIterators() call below for the sequence of keys and their
      // filtering decisions. Here we closely assert that compaction filter is
      // called with the expected keys and only them, and with the right values.
      if (k == "a") {
        EXPECT_EQ(ValueType::kValue, t);
        EXPECT_EQ("av50", v);
        return Decision::kKeep;
      }
      if (k == "b") {
        EXPECT_EQ(ValueType::kValue, t);
        EXPECT_EQ("bv60", v);
        *skip_until = "d+";
        return Decision::kRemoveAndSkipUntil;
      }
      if (k == "e") {
        EXPECT_EQ(ValueType::kMergeOperand, t);
        EXPECT_EQ("em71", v);
        return Decision::kKeep;
      }
      if (k == "f") {
        if (v == "fm65") {
          EXPECT_EQ(ValueType::kMergeOperand, t);
          *skip_until = "f";
        } else {
          EXPECT_EQ("fm30", v);
          EXPECT_EQ(ValueType::kMergeOperand, t);
          *skip_until = "g+";
        }
        return Decision::kRemoveAndSkipUntil;
      }
      if (k == "h") {
        EXPECT_EQ(ValueType::kValue, t);
        EXPECT_EQ("hv91", v);
        return Decision::kKeep;
      }
      if (k == "i") {
        EXPECT_EQ(ValueType::kValue, t);
        EXPECT_EQ("iv95", v);
        *skip_until = "z";
        return Decision::kRemoveAndSkipUntil;
      }
      ADD_FAILURE();
      return Decision::kKeep;
    }

    const char* Name() const override {
      return "CompactionIteratorTest.CompactionFilterSkipUntil::Filter";
    }
  };

  MergeOp merge_op;
  Filter filter;
  InitIterators(
      {test::KeyStr("a", 50, kTypeValue),  // keep
       test::KeyStr("a", 45, kTypeMerge),
       test::KeyStr("b", 60, kTypeValue),  // skip to "d+"
       test::KeyStr("b", 40, kTypeValue), test::KeyStr("c", 35, kTypeValue),
       test::KeyStr("d", 70, kTypeMerge),
       test::KeyStr("e", 71, kTypeMerge),  // keep
       test::KeyStr("f", 65, kTypeMerge),  // skip to "f", aka keep
       test::KeyStr("f", 30, kTypeMerge),  // skip to "g+"
       test::KeyStr("f", 25, kTypeValue), test::KeyStr("g", 90, kTypeValue),
       test::KeyStr("h", 91, kTypeValue),  // keep
       test::KeyStr("i", 95, kTypeValue),  // skip to "z"
       test::KeyStr("j", 99, kTypeValue)},
      {"av50", "am45", "bv60", "bv40", "cv35", "dm70", "em71", "fm65", "fm30",
       "fv25", "gv90", "hv91", "iv95", "jv99"},
      {}, {}, kMaxSequenceNumber, &merge_op, &filter);

  // Compaction should output just "a", "e" and "h" keys.
  c_iter_->SeekToFirst();
  ASSERT_TRUE(c_iter_->Valid());
  ASSERT_EQ(test::KeyStr("a", 50, kTypeValue), c_iter_->key().ToString());
  ASSERT_EQ("av50", c_iter_->value().ToString());
  c_iter_->Next();
  ASSERT_TRUE(c_iter_->Valid());
  ASSERT_EQ(test::KeyStr("e", 71, kTypeMerge), c_iter_->key().ToString());
  ASSERT_EQ("em71", c_iter_->value().ToString());
  c_iter_->Next();
  ASSERT_TRUE(c_iter_->Valid());
  ASSERT_EQ(test::KeyStr("h", 91, kTypeValue), c_iter_->key().ToString());
  ASSERT_EQ("hv91", c_iter_->value().ToString());
  c_iter_->Next();
  ASSERT_FALSE(c_iter_->Valid());

  // Check that the compaction iterator did the correct sequence of calls on
  // the underlying iterator.
  using A = LoggingForwardVectorIterator::Action;
  using T = A::Type;
  std::vector<A> expected_actions = {
      A(T::SEEK_TO_FIRST),
      A(T::NEXT),
      A(T::NEXT),
      A(T::SEEK, test::KeyStr("d+", kMaxSequenceNumber, kValueTypeForSeek)),
      A(T::NEXT),
      A(T::NEXT),
      A(T::SEEK, test::KeyStr("g+", kMaxSequenceNumber, kValueTypeForSeek)),
      A(T::NEXT),
      A(T::SEEK, test::KeyStr("z", kMaxSequenceNumber, kValueTypeForSeek))};
  ASSERT_EQ(expected_actions, iter_->log);
}

}  // namespace rocksdb

int main(int argc, char** argv) {
  ::testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS();
}
<|endoftext|>


<commit_before>#pragma once

#include <iostream>
#include <string>
#include <string_view>
#include <vector>
#include <cstdint>

#include "acmacs-base/to-string.hh"

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

class Color
{
 public:
    union value_type
    {
        constexpr value_type() : color{0xFF00FF} {}
        constexpr value_type(uint32_t regular) : color{regular} {}
        constexpr value_type(double adj) : adjustment{static_cast<float>(adj)} {}
        constexpr bool operator==(const value_type& rhs) const { return color == rhs.color; }
        uint32_t color;
        float adjustment;
    };
      // constexpr static const uint32_t NoChange = 0xFFFFFFFE;
    enum class type : char { regular, no_change, adjust_saturation, adjust_brightness };

    Color() = default;
    Color(const Color& aSrc) = default;
    template <typename Uint, typename std::enable_if<std::is_integral<Uint>::value>::type* = nullptr> constexpr Color(Uint aColor) : color_(static_cast<uint32_t>(aColor)) {}
    explicit Color(const std::string& aColor) { from_string(aColor); }
    explicit Color(const std::string_view& aColor) { from_string(aColor); }
    constexpr explicit Color(type a_type, double value) : color_(value), type_(a_type) {}
    Color& operator=(const std::string& aColor) { from_string(aColor); return *this; }
    Color& operator=(const std::string_view& aColor) { from_string(aColor); return *this; }
    Color& operator=(const char* aColor) { from_string(aColor); return *this; }
    Color(const char* aColor) { from_string(aColor); }
      // Color(const Color&) = default;
    Color& operator=(const Color& aSrc);
    template <typename Uint, typename std::enable_if<std::is_integral<Uint>::value>::type* = nullptr> Color& operator=(Uint aColor)
        {
            color_ = static_cast<uint32_t>(aColor);
            type_ = type::regular;
            return *this;
        }

    bool operator == (const Color& aColor) const { return color_ == aColor.color_ && type_ == aColor.type_; }
    bool operator != (const Color& aColor) const { return ! operator==(aColor); }
    bool operator < (const Color& aColor) const { return color_.color < aColor.color_.color; }

    constexpr double alpha() const { return double(0xFF - ((color_.color >> 24) & 0xFF)) / 255.0; }
    constexpr double red() const { return double((color_.color >> 16) & 0xFF) / 255.0; }
    constexpr double green() const { return double((color_.color >> 8) & 0xFF) / 255.0; }
    constexpr double blue() const { return double(color_.color & 0xFF) / 255.0; }

    constexpr size_t alphaI() const { return static_cast<size_t>((color_.color >> 24) & 0xFF); }
    constexpr void alphaI(uint32_t v) { color_.color = (color_.color & 0xFFFFFF) | ((v & 0xFF) << 24); }
    constexpr size_t rgbI() const { return static_cast<size_t>(color_.color & 0xFFFFFF); }

    constexpr bool empty() const { return type_ == type::no_change; }

    void light(double value);
    void adjust_saturation(double value);
    void adjust_brightness(double value);

    constexpr void set_transparency(double aTransparency) { color_.color = (color_.color & 0x00FFFFFF) | ((static_cast<unsigned>(aTransparency * 255.0) & 0xFF) << 24); } // for importing from lispmds
    Color without_transparency() const { return {color_.color & 0x00FFFFFF}; }

    void from_string(const std::string_view& aColor);
    explicit operator std::string() const { return to_string(); }
    std::string to_string() const;
    std::string to_hex_string() const;
    void from_string(const char* s, size_t len) { from_string(std::string(s, len)); }

    enum class distinct_t { Ana, GoogleMaps };
    static std::vector<std::string> distinct_s(distinct_t dtype = distinct_t::Ana);
    static std::vector<Color> distinct(distinct_t dtype = distinct_t::Ana);

 private:
      // 4 bytes: most->least significant: transparency-red-green-blue, 0x00FF0000 - opaque red, 0xFF000000 - fully transparent
    value_type color_;
    type type_ = type::regular;

}; // class Color

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

inline std::ostream& operator<<(std::ostream& out, Color c)
{
    return out << c.to_string();
}

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

// the functions below combined with Color imlicit constructor from string lead to incorrect conversion to Color from string-like values (e.g. Titer)
// inline std::string operator+(std::string s, Color c) { return s + c.to_string(); }
// inline std::string operator+(Color c, std::string s) { return c.to_string() + s; }
// inline std::string operator+(const char* s, Color c) { return s + c.to_string(); }
// inline std::string operator+(Color c, const char* s) { return c.to_string() + s; }

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

constexpr const Color ColorNoChange(Color::type::no_change, 0);
constexpr const Color BLACK{0};
constexpr const Color TRANSPARENT{0xFF000000};
constexpr const Color RED{0xFF0000};
constexpr const Color GREEN{0x00FF00};
constexpr const Color BLUE{0x0000FF};
constexpr const Color CYAN{0x00FFFF};
constexpr const Color MAGENTA{0xFF00FF};
constexpr const Color YELLOW{0xFFFF00};
constexpr const Color GREY{0xBEBEBE};
constexpr const Color GREY50{0x7F7F7F};

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

namespace acmacs
{
    inline std::string to_string(Color aColor)
    {
        return aColor.to_string();
    }

} // namespace acmacs

// ----------------------------------------------------------------------
/// Local Variables:
/// eval: (if (fboundp 'eu-rename-buffer) (eu-rename-buffer))
/// End:
<commit_msg>WHITE<commit_after>#pragma once

#include <iostream>
#include <string>
#include <string_view>
#include <vector>
#include <cstdint>

#include "acmacs-base/to-string.hh"

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

class Color
{
 public:
    union value_type
    {
        constexpr value_type() : color{0xFF00FF} {}
        constexpr value_type(uint32_t regular) : color{regular} {}
        constexpr value_type(double adj) : adjustment{static_cast<float>(adj)} {}
        constexpr bool operator==(const value_type& rhs) const { return color == rhs.color; }
        uint32_t color;
        float adjustment;
    };
      // constexpr static const uint32_t NoChange = 0xFFFFFFFE;
    enum class type : char { regular, no_change, adjust_saturation, adjust_brightness };

    Color() = default;
    Color(const Color& aSrc) = default;
    template <typename Uint, typename std::enable_if<std::is_integral<Uint>::value>::type* = nullptr> constexpr Color(Uint aColor) : color_(static_cast<uint32_t>(aColor)) {}
    explicit Color(const std::string& aColor) { from_string(aColor); }
    explicit Color(const std::string_view& aColor) { from_string(aColor); }
    constexpr explicit Color(type a_type, double value) : color_(value), type_(a_type) {}
    Color& operator=(const std::string& aColor) { from_string(aColor); return *this; }
    Color& operator=(const std::string_view& aColor) { from_string(aColor); return *this; }
    Color& operator=(const char* aColor) { from_string(aColor); return *this; }
    Color(const char* aColor) { from_string(aColor); }
      // Color(const Color&) = default;
    Color& operator=(const Color& aSrc);
    template <typename Uint, typename std::enable_if<std::is_integral<Uint>::value>::type* = nullptr> Color& operator=(Uint aColor)
        {
            color_ = static_cast<uint32_t>(aColor);
            type_ = type::regular;
            return *this;
        }

    bool operator == (const Color& aColor) const { return color_ == aColor.color_ && type_ == aColor.type_; }
    bool operator != (const Color& aColor) const { return ! operator==(aColor); }
    bool operator < (const Color& aColor) const { return color_.color < aColor.color_.color; }

    constexpr double alpha() const { return double(0xFF - ((color_.color >> 24) & 0xFF)) / 255.0; }
    constexpr double red() const { return double((color_.color >> 16) & 0xFF) / 255.0; }
    constexpr double green() const { return double((color_.color >> 8) & 0xFF) / 255.0; }
    constexpr double blue() const { return double(color_.color & 0xFF) / 255.0; }

    constexpr size_t alphaI() const { return static_cast<size_t>((color_.color >> 24) & 0xFF); }
    constexpr void alphaI(uint32_t v) { color_.color = (color_.color & 0xFFFFFF) | ((v & 0xFF) << 24); }
    constexpr size_t rgbI() const { return static_cast<size_t>(color_.color & 0xFFFFFF); }

    constexpr bool empty() const { return type_ == type::no_change; }

    void light(double value);
    void adjust_saturation(double value);
    void adjust_brightness(double value);

    constexpr void set_transparency(double aTransparency) { color_.color = (color_.color & 0x00FFFFFF) | ((static_cast<unsigned>(aTransparency * 255.0) & 0xFF) << 24); } // for importing from lispmds
    Color without_transparency() const { return {color_.color & 0x00FFFFFF}; }

    void from_string(const std::string_view& aColor);
    explicit operator std::string() const { return to_string(); }
    std::string to_string() const;
    std::string to_hex_string() const;
    void from_string(const char* s, size_t len) { from_string(std::string(s, len)); }

    enum class distinct_t { Ana, GoogleMaps };
    static std::vector<std::string> distinct_s(distinct_t dtype = distinct_t::Ana);
    static std::vector<Color> distinct(distinct_t dtype = distinct_t::Ana);

 private:
      // 4 bytes: most->least significant: transparency-red-green-blue, 0x00FF0000 - opaque red, 0xFF000000 - fully transparent
    value_type color_;
    type type_ = type::regular;

}; // class Color

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

inline std::ostream& operator<<(std::ostream& out, Color c)
{
    return out << c.to_string();
}

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

// the functions below combined with Color imlicit constructor from string lead to incorrect conversion to Color from string-like values (e.g. Titer)
// inline std::string operator+(std::string s, Color c) { return s + c.to_string(); }
// inline std::string operator+(Color c, std::string s) { return c.to_string() + s; }
// inline std::string operator+(const char* s, Color c) { return s + c.to_string(); }
// inline std::string operator+(Color c, const char* s) { return c.to_string() + s; }

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

constexpr const Color ColorNoChange(Color::type::no_change, 0);
constexpr const Color BLACK{0};
constexpr const Color WHITE{0xFFFFFF};
constexpr const Color TRANSPARENT{0xFF000000};
constexpr const Color RED{0xFF0000};
constexpr const Color GREEN{0x00FF00};
constexpr const Color BLUE{0x0000FF};
constexpr const Color CYAN{0x00FFFF};
constexpr const Color MAGENTA{0xFF00FF};
constexpr const Color YELLOW{0xFFFF00};
constexpr const Color GREY{0xBEBEBE};
constexpr const Color GREY50{0x7F7F7F};

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

namespace acmacs
{
    inline std::string to_string(Color aColor)
    {
        return aColor.to_string();
    }

} // namespace acmacs

// ----------------------------------------------------------------------
/// Local Variables:
/// eval: (if (fboundp 'eu-rename-buffer) (eu-rename-buffer))
/// End:
<|endoftext|>


<commit_before>/**
 * Copyright (c) 2014-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 */
#include <wdt/WdtOptions.h>
#include <wdt/util/WdtFlags.h>
#include <wdt/util/WdtFlagsMacros.h>

#include <glog/logging.h>
#include <gtest/gtest.h>

/*
 * Tests in this file can not be run in the same process. That is because we
 * rely on is_default property of gflags to determine whether a flag has been
 * specified by the user. And if a flag is ever changed, is_default becomes
 * false and can never be reset to true. So, after running one test, other
 * tests would fail. To run tests in this file, use wdt/wdt_option_type_test.sh.
 * That script runs each test in separate process and also tests for both long
 * and short flags.
 */

namespace facebook {
namespace wdt {

const std::string NUM_PORTS_FLAG = WDT_FLAG_STR(num_ports);
const std::string BLOCK_SIZE_FLAG = WDT_FLAG_STR(block_size_mbytes);
const std::string OPTION_TYPE_FLAG = WDT_FLAG_STR(option_type);

void overrideTest1(const std::string &optionType) {
  const auto &options = WdtOptions::get();
  google::SetCommandLineOption(OPTION_TYPE_FLAG.c_str(), optionType.c_str());
  google::SetCommandLineOption(NUM_PORTS_FLAG.c_str(), "4");
  google::SetCommandLineOption(BLOCK_SIZE_FLAG.c_str(), "8");
  WdtFlags::initializeFromFlags();
  EXPECT_EQ(4, options.num_ports);
  EXPECT_EQ(8, options.block_size_mbytes);
}

void overrideTest2(const std::string &optionType) {
  const auto &options = WdtOptions::get();
  google::SetCommandLineOption(OPTION_TYPE_FLAG.c_str(), optionType.c_str());
  google::SetCommandLineOption(NUM_PORTS_FLAG.c_str(), "8");
  google::SetCommandLineOption(BLOCK_SIZE_FLAG.c_str(), "16");
  WdtFlags::initializeFromFlags();
  EXPECT_EQ(8, options.num_ports);
  EXPECT_EQ(16, options.block_size_mbytes);
}

TEST(OptionType, FlashOptionTypeTest1) {
  const auto &options = WdtOptions::get();
  google::SetCommandLineOption(OPTION_TYPE_FLAG.c_str(), "flash");
  WdtFlags::initializeFromFlags();
  EXPECT_EQ(8, options.num_ports);
  EXPECT_EQ(16, options.block_size_mbytes);
}

TEST(OptionType, FlashOptionTypeTest2) {
  overrideTest1("flash");
}

TEST(OptionType, FlashOptionTypeTest3) {
  overrideTest2("flash");
}

TEST(OptionType, DiskOptionTypeTest1) {
  const auto &options = WdtOptions::get();
  google::SetCommandLineOption(OPTION_TYPE_FLAG.c_str(), "disk");
  WdtFlags::initializeFromFlags();
  EXPECT_EQ(1, options.num_ports);
  EXPECT_EQ(-1, options.block_size_mbytes);
}

TEST(OptionType, DiskOptionTypeTest2) {
  overrideTest1("disk");
}

TEST(OptionType, DiskOptionTypeTest3) {
  overrideTest2("disk");
}
}
}

int main(int argc, char *argv[]) {
  FLAGS_logtostderr = true;
  testing::InitGoogleTest(&argc, argv);
  google::ParseCommandLineFlags(&argc, &argv, true);
  google::InitGoogleLogging(argv[0]);
  int ret = RUN_ALL_TESTS();
  return ret;
}
<commit_msg>disk nows runs with 3 threads by default<commit_after>/**
 * Copyright (c) 2014-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 */
#include <wdt/WdtOptions.h>
#include <wdt/util/WdtFlags.h>
#include <wdt/util/WdtFlagsMacros.h>

#include <glog/logging.h>
#include <gtest/gtest.h>

/*
 * Tests in this file can not be run in the same process. That is because we
 * rely on is_default property of gflags to determine whether a flag has been
 * specified by the user. And if a flag is ever changed, is_default becomes
 * false and can never be reset to true. So, after running one test, other
 * tests would fail. To run tests in this file, use wdt/wdt_option_type_test.sh.
 * That script runs each test in separate process and also tests for both long
 * and short flags.
 */

namespace facebook {
namespace wdt {

const std::string NUM_PORTS_FLAG = WDT_FLAG_STR(num_ports);
const std::string BLOCK_SIZE_FLAG = WDT_FLAG_STR(block_size_mbytes);
const std::string OPTION_TYPE_FLAG = WDT_FLAG_STR(option_type);

void overrideTest1(const std::string &optionType) {
  const auto &options = WdtOptions::get();
  google::SetCommandLineOption(OPTION_TYPE_FLAG.c_str(), optionType.c_str());
  google::SetCommandLineOption(NUM_PORTS_FLAG.c_str(), "4");
  google::SetCommandLineOption(BLOCK_SIZE_FLAG.c_str(), "8");
  WdtFlags::initializeFromFlags();
  EXPECT_EQ(4, options.num_ports);
  EXPECT_EQ(8, options.block_size_mbytes);
}

void overrideTest2(const std::string &optionType) {
  const auto &options = WdtOptions::get();
  google::SetCommandLineOption(OPTION_TYPE_FLAG.c_str(), optionType.c_str());
  google::SetCommandLineOption(NUM_PORTS_FLAG.c_str(), "8");
  google::SetCommandLineOption(BLOCK_SIZE_FLAG.c_str(), "16");
  WdtFlags::initializeFromFlags();
  EXPECT_EQ(8, options.num_ports);
  EXPECT_EQ(16, options.block_size_mbytes);
}

TEST(OptionType, FlashOptionTypeTest1) {
  const auto &options = WdtOptions::get();
  google::SetCommandLineOption(OPTION_TYPE_FLAG.c_str(), "flash");
  WdtFlags::initializeFromFlags();
  EXPECT_EQ(8, options.num_ports);
  EXPECT_EQ(16, options.block_size_mbytes);
}

TEST(OptionType, FlashOptionTypeTest2) {
  overrideTest1("flash");
}

TEST(OptionType, FlashOptionTypeTest3) {
  overrideTest2("flash");
}

TEST(OptionType, DiskOptionTypeTest1) {
  const auto &options = WdtOptions::get();
  google::SetCommandLineOption(OPTION_TYPE_FLAG.c_str(), "disk");
  WdtFlags::initializeFromFlags();
  EXPECT_EQ(3, options.num_ports);
  EXPECT_EQ(-1, options.block_size_mbytes);
}

TEST(OptionType, DiskOptionTypeTest2) {
  overrideTest1("disk");
}

TEST(OptionType, DiskOptionTypeTest3) {
  overrideTest2("disk");
}
}
}

int main(int argc, char *argv[]) {
  FLAGS_logtostderr = true;
  testing::InitGoogleTest(&argc, argv);
  google::ParseCommandLineFlags(&argc, &argv, true);
  google::InitGoogleLogging(argv[0]);
  int ret = RUN_ALL_TESTS();
  return ret;
}
<|endoftext|>


<commit_before>/*

   nsjail - capability-related operations
   -----------------------------------------

   Copyright 2014 Google Inc. All Rights Reserved.

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.

*/

#include "caps.h"

#include <linux/capability.h>
#include <string.h>
#include <sys/prctl.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <unistd.h>

#include <string>

#include "logs.h"
#include "macros.h"
#include "util.h"

namespace caps {

static struct {
	const int val;
	const char* const name;
} const capNames[] = {
    NS_VALSTR_STRUCT(CAP_CHOWN),
    NS_VALSTR_STRUCT(CAP_DAC_OVERRIDE),
    NS_VALSTR_STRUCT(CAP_DAC_READ_SEARCH),
    NS_VALSTR_STRUCT(CAP_FOWNER),
    NS_VALSTR_STRUCT(CAP_FSETID),
    NS_VALSTR_STRUCT(CAP_KILL),
    NS_VALSTR_STRUCT(CAP_SETGID),
    NS_VALSTR_STRUCT(CAP_SETUID),
    NS_VALSTR_STRUCT(CAP_SETPCAP),
    NS_VALSTR_STRUCT(CAP_LINUX_IMMUTABLE),
    NS_VALSTR_STRUCT(CAP_NET_BIND_SERVICE),
    NS_VALSTR_STRUCT(CAP_NET_BROADCAST),
    NS_VALSTR_STRUCT(CAP_NET_ADMIN),
    NS_VALSTR_STRUCT(CAP_NET_RAW),
    NS_VALSTR_STRUCT(CAP_IPC_LOCK),
    NS_VALSTR_STRUCT(CAP_IPC_OWNER),
    NS_VALSTR_STRUCT(CAP_SYS_MODULE),
    NS_VALSTR_STRUCT(CAP_SYS_RAWIO),
    NS_VALSTR_STRUCT(CAP_SYS_CHROOT),
    NS_VALSTR_STRUCT(CAP_SYS_PTRACE),
    NS_VALSTR_STRUCT(CAP_SYS_PACCT),
    NS_VALSTR_STRUCT(CAP_SYS_ADMIN),
    NS_VALSTR_STRUCT(CAP_SYS_BOOT),
    NS_VALSTR_STRUCT(CAP_SYS_NICE),
    NS_VALSTR_STRUCT(CAP_SYS_RESOURCE),
    NS_VALSTR_STRUCT(CAP_SYS_TIME),
    NS_VALSTR_STRUCT(CAP_SYS_TTY_CONFIG),
    NS_VALSTR_STRUCT(CAP_MKNOD),
    NS_VALSTR_STRUCT(CAP_LEASE),
    NS_VALSTR_STRUCT(CAP_AUDIT_WRITE),
    NS_VALSTR_STRUCT(CAP_AUDIT_CONTROL),
    NS_VALSTR_STRUCT(CAP_SETFCAP),
    NS_VALSTR_STRUCT(CAP_MAC_OVERRIDE),
    NS_VALSTR_STRUCT(CAP_MAC_ADMIN),
    NS_VALSTR_STRUCT(CAP_SYSLOG),
    NS_VALSTR_STRUCT(CAP_WAKE_ALARM),
    NS_VALSTR_STRUCT(CAP_BLOCK_SUSPEND),
#if defined(CAP_AUDIT_READ)
    NS_VALSTR_STRUCT(CAP_AUDIT_READ),
#endif /* defined(CAP_AUDIT_READ) */
};

int nameToVal(const char* name) {
	for (size_t i = 0; i < ARR_SZ(capNames); i++) {
		if (strcmp(name, capNames[i].name) == 0) {
			return capNames[i].val;
		}
	}
	LOG_W("Uknown capability: '%s'", name);
	return -1;
}

static const std::string capToStr(int val) {
	std::string res;
	for (size_t i = 0; i < ARR_SZ(capNames); i++) {
		if (val == capNames[i].val) {
			return capNames[i].name;
		}
	}

	res.append("CAP_UNKNOWN(");
	res.append(std::to_string(val));
	res.append(")");
	return res;
}

static cap_user_data_t getCaps() {
	static __thread struct __user_cap_data_struct cap_data[_LINUX_CAPABILITY_U32S_3];
	const struct __user_cap_header_struct cap_hdr = {
	    .version = _LINUX_CAPABILITY_VERSION_3,
	    .pid = 0,
	};
	if (syscall(__NR_capget, &cap_hdr, &cap_data) == -1) {
		PLOG_W("capget() failed");
		return NULL;
	}
	return cap_data;
}

static bool setCaps(const cap_user_data_t cap_data) {
	const struct __user_cap_header_struct cap_hdr = {
	    .version = _LINUX_CAPABILITY_VERSION_3,
	    .pid = 0,
	};
	if (syscall(__NR_capset, &cap_hdr, cap_data) == -1) {
		PLOG_W("capset() failed");
		return false;
	}
	return true;
}

static void clearInheritable(cap_user_data_t cap_data) {
	for (size_t i = 0; i < _LINUX_CAPABILITY_U32S_3; i++) {
		cap_data[i].inheritable = 0U;
	}
}

static bool getPermitted(cap_user_data_t cap_data, unsigned int cap) {
	size_t off_byte = cap / (sizeof(cap_data->permitted) * 8);
	size_t off_bit = cap % (sizeof(cap_data->permitted) * 8);
	return cap_data[off_byte].permitted & (1U << off_bit);
}

static bool getEffective(cap_user_data_t cap_data, unsigned int cap) {
	size_t off_byte = cap / (sizeof(cap_data->effective) * 8);
	size_t off_bit = cap % (sizeof(cap_data->effective) * 8);
	return cap_data[off_byte].effective & (1U << off_bit);
}

static bool getInheritable(cap_user_data_t cap_data, unsigned int cap) {
	size_t off_byte = cap / (sizeof(cap_data->inheritable) * 8);
	size_t off_bit = cap % (sizeof(cap_data->inheritable) * 8);
	return cap_data[off_byte].inheritable & (1U << off_bit);
}

static void setInheritable(cap_user_data_t cap_data, unsigned int cap) {
	size_t off_byte = cap / (sizeof(cap_data->inheritable) * 8);
	size_t off_bit = cap % (sizeof(cap_data->inheritable) * 8);
	cap_data[off_byte].inheritable |= (1U << off_bit);
}

#if !defined(PR_CAP_AMBIENT)
#define PR_CAP_AMBIENT 47
#define PR_CAP_AMBIENT_RAISE 2
#define PR_CAP_AMBIENT_CLEAR_ALL 4
#endif /* !defined(PR_CAP_AMBIENT) */
static bool initNsKeepCaps(cap_user_data_t cap_data) {
	char dbgmsg[4096];

	/* Copy all permitted caps to the inheritable set */
	dbgmsg[0] = '\0';
	for (size_t i = 0; i < ARR_SZ(capNames); i++) {
		if (getPermitted(cap_data, capNames[i].val)) {
			util::sSnPrintf(dbgmsg, sizeof(dbgmsg), " %s", capNames[i].name);
			setInheritable(cap_data, capNames[i].val);
		}
	}
	LOG_D("Adding the following capabilities to the inheritable set:%s", dbgmsg);

	if (!setCaps(cap_data)) {
		return false;
	}

	/* Make sure the inheritable set is preserved across execve via the ambient set */
	dbgmsg[0] = '\0';
	for (size_t i = 0; i < ARR_SZ(capNames); i++) {
		if (!getPermitted(cap_data, capNames[i].val)) {
			continue;
		}
		if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, (unsigned long)capNames[i].val, 0UL,
			0UL) == -1) {
			PLOG_W("prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, %s)", capNames[i].name);
		} else {
			util::sSnPrintf(dbgmsg, sizeof(dbgmsg), " %s", capNames[i].name);
		}
	}
	LOG_D("Added the following capabilities to the ambient set:%s", dbgmsg);

	return true;
}

bool initNs(nsjconf_t* nsjconf) {
	struct ints_t* p;

	cap_user_data_t cap_data = getCaps();
	if (cap_data == NULL) {
		return false;
	}

	/* Let's start with an empty inheritable set to avoid any mistakes */
	clearInheritable(cap_data);
	/*
	 * Remove all capabilities from the ambient set first. It works with newer kernel versions
	 * only, so don't panic() if it fails
	 */
	if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_CLEAR_ALL, 0UL, 0UL, 0UL) == -1) {
		PLOG_W("prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_CLEAR_ALL)");
	}

	if (nsjconf->keep_caps) {
		return initNsKeepCaps(cap_data);
	}

	/* Set all requested caps in the inheritable set if these are present in the permitted set
	 */
	std::string dbgmsg;
	for (const auto& cap : nsjconf->caps) {
		if (!getPermitted(cap_data, cap)) {
			LOG_W("Capability %s is not permitted in the namespace",
			    capToStr(cap).c_str());
			return false;
		}
		dbgmsg.append(" ").append(capToStr(cap));
		setInheritable(cap_data, cap);
	}
	LOG_D("Adding the following capabilities to the inheritable set:%s", dbgmsg.c_str());

	if (!setCaps(cap_data)) {
		return false;
	}

	/*
	 * Make sure all other caps (those which were not explicitly requested) are removed from the
	 * bounding set. We need to have CAP_SETPCAP to do that now
	 */
	dbgmsg.clear();
	if (getEffective(cap_data, CAP_SETPCAP)) {
		for (size_t i = 0; i < ARR_SZ(capNames); i++) {
			if (getInheritable(cap_data, capNames[i].val)) {
				continue;
			}
			dbgmsg.append(" ").append(capNames[i].name);
			if (prctl(PR_CAPBSET_DROP, (unsigned long)capNames[i].val, 0UL, 0UL, 0UL) ==
			    -1) {
				PLOG_W("prctl(PR_CAPBSET_DROP, %s)", capNames[i].name);
				return false;
			}
		}
		LOG_D(
		    "Dropped the following capabilities from the bounding set:%s", dbgmsg.c_str());
	}

	/* Make sure inheritable set is preserved across execve via the modified ambient set */
	dbgmsg.clear();
	for (const auto& cap : nsjconf->caps) {
		if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, (unsigned long)cap, 0UL, 0UL) ==
		    -1) {
			PLOG_W("prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, %s)",
			    capToStr(cap).c_str());
		} else {
			dbgmsg.append(" ").append(capToStr(cap));
		}
	}
	LOG_D("Added the following capabilities to the ambient set:%s", dbgmsg.c_str());

	return true;
}

}  // namespace caps
<commit_msg>caps: use standard CAP_TO_INDEX/CAP_TO_MASK<commit_after>/*

   nsjail - capability-related operations
   -----------------------------------------

   Copyright 2014 Google Inc. All Rights Reserved.

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.

*/

#include "caps.h"

#include <linux/capability.h>
#include <string.h>
#include <sys/prctl.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <unistd.h>

#include <string>

#include "logs.h"
#include "macros.h"
#include "util.h"

namespace caps {

static struct {
	const int val;
	const char* const name;
} const capNames[] = {
    NS_VALSTR_STRUCT(CAP_CHOWN),
    NS_VALSTR_STRUCT(CAP_DAC_OVERRIDE),
    NS_VALSTR_STRUCT(CAP_DAC_READ_SEARCH),
    NS_VALSTR_STRUCT(CAP_FOWNER),
    NS_VALSTR_STRUCT(CAP_FSETID),
    NS_VALSTR_STRUCT(CAP_KILL),
    NS_VALSTR_STRUCT(CAP_SETGID),
    NS_VALSTR_STRUCT(CAP_SETUID),
    NS_VALSTR_STRUCT(CAP_SETPCAP),
    NS_VALSTR_STRUCT(CAP_LINUX_IMMUTABLE),
    NS_VALSTR_STRUCT(CAP_NET_BIND_SERVICE),
    NS_VALSTR_STRUCT(CAP_NET_BROADCAST),
    NS_VALSTR_STRUCT(CAP_NET_ADMIN),
    NS_VALSTR_STRUCT(CAP_NET_RAW),
    NS_VALSTR_STRUCT(CAP_IPC_LOCK),
    NS_VALSTR_STRUCT(CAP_IPC_OWNER),
    NS_VALSTR_STRUCT(CAP_SYS_MODULE),
    NS_VALSTR_STRUCT(CAP_SYS_RAWIO),
    NS_VALSTR_STRUCT(CAP_SYS_CHROOT),
    NS_VALSTR_STRUCT(CAP_SYS_PTRACE),
    NS_VALSTR_STRUCT(CAP_SYS_PACCT),
    NS_VALSTR_STRUCT(CAP_SYS_ADMIN),
    NS_VALSTR_STRUCT(CAP_SYS_BOOT),
    NS_VALSTR_STRUCT(CAP_SYS_NICE),
    NS_VALSTR_STRUCT(CAP_SYS_RESOURCE),
    NS_VALSTR_STRUCT(CAP_SYS_TIME),
    NS_VALSTR_STRUCT(CAP_SYS_TTY_CONFIG),
    NS_VALSTR_STRUCT(CAP_MKNOD),
    NS_VALSTR_STRUCT(CAP_LEASE),
    NS_VALSTR_STRUCT(CAP_AUDIT_WRITE),
    NS_VALSTR_STRUCT(CAP_AUDIT_CONTROL),
    NS_VALSTR_STRUCT(CAP_SETFCAP),
    NS_VALSTR_STRUCT(CAP_MAC_OVERRIDE),
    NS_VALSTR_STRUCT(CAP_MAC_ADMIN),
    NS_VALSTR_STRUCT(CAP_SYSLOG),
    NS_VALSTR_STRUCT(CAP_WAKE_ALARM),
    NS_VALSTR_STRUCT(CAP_BLOCK_SUSPEND),
#if defined(CAP_AUDIT_READ)
    NS_VALSTR_STRUCT(CAP_AUDIT_READ),
#endif /* defined(CAP_AUDIT_READ) */
};

int nameToVal(const char* name) {
	for (size_t i = 0; i < ARR_SZ(capNames); i++) {
		if (strcmp(name, capNames[i].name) == 0) {
			return capNames[i].val;
		}
	}
	LOG_W("Uknown capability: '%s'", name);
	return -1;
}

static const std::string capToStr(int val) {
	std::string res;
	for (size_t i = 0; i < ARR_SZ(capNames); i++) {
		if (val == capNames[i].val) {
			return capNames[i].name;
		}
	}

	res.append("CAP_UNKNOWN(");
	res.append(std::to_string(val));
	res.append(")");
	return res;
}

static cap_user_data_t getCaps() {
	static __thread struct __user_cap_data_struct cap_data[_LINUX_CAPABILITY_U32S_3];
	const struct __user_cap_header_struct cap_hdr = {
	    .version = _LINUX_CAPABILITY_VERSION_3,
	    .pid = 0,
	};
	if (syscall(__NR_capget, &cap_hdr, &cap_data) == -1) {
		PLOG_W("capget() failed");
		return NULL;
	}
	return cap_data;
}

static bool setCaps(const cap_user_data_t cap_data) {
	const struct __user_cap_header_struct cap_hdr = {
	    .version = _LINUX_CAPABILITY_VERSION_3,
	    .pid = 0,
	};
	if (syscall(__NR_capset, &cap_hdr, cap_data) == -1) {
		PLOG_W("capset() failed");
		return false;
	}
	return true;
}

static void clearInheritable(cap_user_data_t cap_data) {
	for (size_t i = 0; i < _LINUX_CAPABILITY_U32S_3; i++) {
		cap_data[i].inheritable = 0U;
	}
}

static bool getPermitted(cap_user_data_t cap_data, unsigned int cap) {
	size_t off_byte = CAP_TO_INDEX(cap);
	unsigned mask = CAP_TO_MASK(cap);
	return cap_data[off_byte].permitted & mask;
}

static bool getEffective(cap_user_data_t cap_data, unsigned int cap) {
	size_t off_byte = CAP_TO_INDEX(cap);
	unsigned mask = CAP_TO_MASK(cap);
	return cap_data[off_byte].effective & mask;
}

static bool getInheritable(cap_user_data_t cap_data, unsigned int cap) {
	size_t off_byte = CAP_TO_INDEX(cap);
	unsigned mask = CAP_TO_MASK(cap);
	return cap_data[off_byte].inheritable & mask;
}

static void setInheritable(cap_user_data_t cap_data, unsigned int cap) {
	size_t off_byte = CAP_TO_INDEX(cap);
	unsigned mask = CAP_TO_MASK(cap);
	cap_data[off_byte].inheritable |= mask;
}

#if !defined(PR_CAP_AMBIENT)
#define PR_CAP_AMBIENT 47
#define PR_CAP_AMBIENT_RAISE 2
#define PR_CAP_AMBIENT_CLEAR_ALL 4
#endif /* !defined(PR_CAP_AMBIENT) */
static bool initNsKeepCaps(cap_user_data_t cap_data) {
	char dbgmsg[4096];

	/* Copy all permitted caps to the inheritable set */
	dbgmsg[0] = '\0';
	for (size_t i = 0; i < ARR_SZ(capNames); i++) {
		if (getPermitted(cap_data, capNames[i].val)) {
			util::sSnPrintf(dbgmsg, sizeof(dbgmsg), " %s", capNames[i].name);
			setInheritable(cap_data, capNames[i].val);
		}
	}
	LOG_D("Adding the following capabilities to the inheritable set:%s", dbgmsg);

	if (!setCaps(cap_data)) {
		return false;
	}

	/* Make sure the inheritable set is preserved across execve via the ambient set */
	dbgmsg[0] = '\0';
	for (size_t i = 0; i < ARR_SZ(capNames); i++) {
		if (!getPermitted(cap_data, capNames[i].val)) {
			continue;
		}
		if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, (unsigned long)capNames[i].val, 0UL,
			0UL) == -1) {
			PLOG_W("prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, %s)", capNames[i].name);
		} else {
			util::sSnPrintf(dbgmsg, sizeof(dbgmsg), " %s", capNames[i].name);
		}
	}
	LOG_D("Added the following capabilities to the ambient set:%s", dbgmsg);

	return true;
}

bool initNs(nsjconf_t* nsjconf) {
	struct ints_t* p;

	cap_user_data_t cap_data = getCaps();
	if (cap_data == NULL) {
		return false;
	}

	/* Let's start with an empty inheritable set to avoid any mistakes */
	clearInheritable(cap_data);
	/*
	 * Remove all capabilities from the ambient set first. It works with newer kernel versions
	 * only, so don't panic() if it fails
	 */
	if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_CLEAR_ALL, 0UL, 0UL, 0UL) == -1) {
		PLOG_W("prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_CLEAR_ALL)");
	}

	if (nsjconf->keep_caps) {
		return initNsKeepCaps(cap_data);
	}

	/* Set all requested caps in the inheritable set if these are present in the permitted set
	 */
	std::string dbgmsg;
	for (const auto& cap : nsjconf->caps) {
		if (!getPermitted(cap_data, cap)) {
			LOG_W("Capability %s is not permitted in the namespace",
			    capToStr(cap).c_str());
			return false;
		}
		dbgmsg.append(" ").append(capToStr(cap));
		setInheritable(cap_data, cap);
	}
	LOG_D("Adding the following capabilities to the inheritable set:%s", dbgmsg.c_str());

	if (!setCaps(cap_data)) {
		return false;
	}

	/*
	 * Make sure all other caps (those which were not explicitly requested) are removed from the
	 * bounding set. We need to have CAP_SETPCAP to do that now
	 */
	dbgmsg.clear();
	if (getEffective(cap_data, CAP_SETPCAP)) {
		for (size_t i = 0; i < ARR_SZ(capNames); i++) {
			if (getInheritable(cap_data, capNames[i].val)) {
				continue;
			}
			dbgmsg.append(" ").append(capNames[i].name);
			if (prctl(PR_CAPBSET_DROP, (unsigned long)capNames[i].val, 0UL, 0UL, 0UL) ==
			    -1) {
				PLOG_W("prctl(PR_CAPBSET_DROP, %s)", capNames[i].name);
				return false;
			}
		}
		LOG_D(
		    "Dropped the following capabilities from the bounding set:%s", dbgmsg.c_str());
	}

	/* Make sure inheritable set is preserved across execve via the modified ambient set */
	dbgmsg.clear();
	for (const auto& cap : nsjconf->caps) {
		if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, (unsigned long)cap, 0UL, 0UL) ==
		    -1) {
			PLOG_W("prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, %s)",
			    capToStr(cap).c_str());
		} else {
			dbgmsg.append(" ").append(capToStr(cap));
		}
	}
	LOG_D("Added the following capabilities to the ambient set:%s", dbgmsg.c_str());

	return true;
}

}  // namespace caps
<|endoftext|>


<commit_before>#ifndef DFG_HPP
#define DFG_HPP

#include <unordered_map>
#include <map>
#include <utility>
#include <forward_list>
#include <vector>
#include <string>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <cmath>
#include <sstream>

#include "loadJsons.hpp"

using namespace std;
using boost::property_tree::ptree;

namespace dfg {
  enum CacheStatus { Primary,Secondary,None };

  template <class T>
  static bool hasKey(const ptree& p,string path) {
    return p.get_optional<T>(path) != boost::none;
  }

  static string toString(const ptree &p) {
    stringstream ss;
    write_json(ss,p);
    return ss.str();
  }

  class DFGType {
    public:
      DFGType(string typeName, forward_list<ptree> typeFragments, vector<string> overrideScheme);
      shared_ptr<ptree> getCfg(const map<string,string>& context);
      const inline string getTypeName() { return _typeName; };
      const inline forward_list<ptree>& getFragments() { return _fragments; };
      const inline vector<string>& getOverrideScheme() { return _overrideScheme; };
      const inline CacheStatus getLastCacheStatus() { return _lastCacheStatus; };
    private:
      void checkType();
      ptree mergeCfgs(const forward_list<shared_ptr<ptree>>& fragments);
      pair<string,forward_list<shared_ptr<ptree>>> match(const map<string,string>& context);
      bool fragMatch(const ptree& fragment,const map<string,string>& context);
      string makeHash(const map<string,string>& context);
      bool fragCmp(const ptree &a, const ptree &b);
    private:
      string _typeName;
      forward_list<ptree> _fragments;
      vector<string> _overrideScheme;
      unordered_map<string,ptree> _primaryCache;
      unordered_map<string,forward_list<shared_ptr<ptree>>> _secondaryCache;
      CacheStatus _lastCacheStatus;
  };

  class DFGTypeFactory {
    public:
      DFGTypeFactory(string path);
      const shared_ptr<DFGType> createType(string typeName, vector<string> overrideScheme);
      const inline shared_ptr<DFGType> getType(string typeName) { return _types[typeName]; };
    private:
      forward_list<ptree> _fragments;
      unordered_map<string,shared_ptr<DFGType>> _types;
  };

  //Type
  /////////////////////////
  DFGType::DFGType(string typeName, forward_list<ptree> typeFragments, vector<string> overrideScheme) {
    _typeName = typeName;
    _fragments = typeFragments;
    _overrideScheme = overrideScheme;
    _lastCacheStatus = CacheStatus::None;

    _fragments.sort([this](ptree &a,ptree &b) { return fragCmp(a,b);});

    checkType();
  }

  bool DFGType::fragCmp(const ptree &a, const ptree &b) {
    int aSum = 0, bSum = 0;

    for(auto i = 0; i < _overrideScheme.size(); i++) {
      int inc = pow(2,i);
      auto k = _overrideScheme[i];
      if(hasKey<string>(a,"@override." + k)) {
        aSum |= inc;
      }
      if(hasKey<string>(b,"@override." + k)) {
        bSum |= inc;
      }
    }
    return aSum < bSum;
  }

  class CollisionException : public exception {
    public:
      CollisionException(string message) { _message = message; };
      string str() { return _message; };
    private:
      string _message;
  };

  void DFGType::checkType() {
    auto prev = _fragments.begin();

    for(auto cur = _fragments.begin(); cur != _fragments.end(); cur++) {
      if(cur == prev) {
        if(hasKey<string>(*prev,"@override")) {
          throw CollisionException("No defaults for type");
        }
        continue;
      }

      if(!hasKey<string>(*cur,"@override")) {
        throw CollisionException("Default collision for type");
      }

      if(prev == _fragments.begin()) { //note increment
        prev++;
        continue;
      }

      int numCurOverrides = 0;
      bool different = false;
      for(auto &kv : cur->get_child("@override")) {
        numCurOverrides++;
        if(prev->get_optional<string>("@override." + kv.first) != kv.second.get_value<string>()) {
          different = true;
          break;
        }
      }

      if(!different && numCurOverrides == prev->get_child("@override").size()) {
        throw CollisionException("Duplicate keys:\n" + toString(cur->get_child("@override")) + toString(prev->get_child("@override")));
      }
      prev++;
    }
  }

  //Type Factory
  ////////////////////////
  DFGTypeFactory::DFGTypeFactory(string path) {
    _fragments = loadJsons(path);
  }

  const shared_ptr<DFGType> DFGTypeFactory::createType(string typeName, vector<string> overrideScheme) {
    forward_list<ptree> typeFragments;

    auto prevIt = _fragments.before_begin();
    for(auto it = _fragments.begin(); it != _fragments.end(); ) {
      auto fragment = *it;
      if(fragment.get<string>("@type") == typeName) {
        typeFragments.push_front(std::move(fragment));
        it = _fragments.erase_after(prevIt);
      }
      else {
        prevIt = it;
        ++it;
      }
    }

    auto type = make_shared<DFGType>(typeName,move(typeFragments),overrideScheme);
    _types.insert(make_pair(typeName,type));
    return type;
  }
}

#endif
<commit_msg>small improvements<commit_after>#ifndef DFG_HPP
#define DFG_HPP

#include <unordered_map>
#include <map>
#include <utility>
#include <forward_list>
#include <vector>
#include <string>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <cmath>
#include <sstream>

#include "loadJsons.hpp"

using namespace std;
using boost::property_tree::ptree;

namespace dfg {
  enum CacheStatus { Primary,Secondary,None };

  template <class T>
  static bool hasKey(const ptree& p,string path) {
    return p.get_optional<T>(path) != boost::none;
  }

  static string toString(const ptree &p) {
    stringstream ss;
    write_json(ss,p);
    return ss.str();
  }

  class DFGType {
    public:
      DFGType(string typeName, forward_list<ptree> typeFragments, vector<string> overrideScheme);
      shared_ptr<ptree> getCfg(const map<string,string>& context);
      const inline string getTypeName() { return _typeName; };
      const inline forward_list<ptree>& getFragments() { return _fragments; };
      const inline vector<string>& getOverrideScheme() { return _overrideScheme; };
      const inline CacheStatus getLastCacheStatus() { return _lastCacheStatus; };
    private:
      void checkType();
      ptree mergeCfgs(const forward_list<shared_ptr<ptree>>& fragments);
      pair<string,forward_list<shared_ptr<ptree>>> match(const map<string,string>& context);
      bool fragMatch(const ptree& fragment,const map<string,string>& context);
      string makeHash(const map<string,string>& context);
      bool fragCmp(const ptree &a, const ptree &b);
    private:
      string _typeName;
      forward_list<ptree> _fragments;
      vector<string> _overrideScheme;
      unordered_map<string,ptree> _primaryCache;
      unordered_map<string,forward_list<shared_ptr<ptree>>> _secondaryCache;
      CacheStatus _lastCacheStatus;
  };

  class DFGTypeFactory {
    public:
      DFGTypeFactory(string path);
      const shared_ptr<DFGType> createType(const string typeName, const vector<string>& overrideScheme);
      const inline shared_ptr<DFGType> getType(const string typeName) { return _types[typeName]; };
    private:
      forward_list<ptree> _fragments;
      unordered_map<string,shared_ptr<DFGType>> _types;
  };

  //Type
  /////////////////////////
  DFGType::DFGType(string typeName, forward_list<ptree> typeFragments, vector<string> overrideScheme) {
    //FIXME:use initilization
    _typeName = typeName;
    _fragments = typeFragments;
    _overrideScheme = overrideScheme;
    _lastCacheStatus = CacheStatus::None;

    _fragments.sort([this](ptree &a,ptree &b) { return fragCmp(a,b);});

    checkType();
  }

  bool DFGType::fragCmp(const ptree &a, const ptree &b) {
    int aSum = 0, bSum = 0;

    for(auto i = 0; i < _overrideScheme.size(); i++) {
      int inc = pow(2,i);
      auto k = _overrideScheme[i];
      if(hasKey<string>(a,"@override." + k)) {
        aSum |= inc;
      }
      if(hasKey<string>(b,"@override." + k)) {
        bSum |= inc;
      }
    }
    return aSum < bSum;
  }

  class CollisionException : public exception {
    public:
      CollisionException(string message) { _message = message; };
      string str() { return _message; };
    private:
      string _message;
  };

  void DFGType::checkType() {
    auto prev = _fragments.begin();

    for(auto cur = _fragments.begin(); cur != _fragments.end(); cur++) {
      if(cur == prev) {
        if(hasKey<string>(*prev,"@override")) {
          throw CollisionException("No defaults for type");
        }
        continue;
      }

      if(!hasKey<string>(*cur,"@override")) {
        throw CollisionException("Default collision for type");
      }

      if(prev == _fragments.begin()) { //note increment
        prev++;
        continue;
      }

      int numCurOverrides = 0;
      bool different = false;
      for(auto &kv : cur->get_child("@override")) {
        numCurOverrides++;
        if(prev->get_optional<string>("@override." + kv.first) != kv.second.get_value<string>()) {
          different = true;
          break;
        }
      }

      if(!different && numCurOverrides == prev->get_child("@override").size()) {
        throw CollisionException("Duplicate keys:\n" + toString(cur->get_child("@override")) + toString(prev->get_child("@override")));
      }
      prev++;
    }
  }

  //Type Factory
  ////////////////////////
  DFGTypeFactory::DFGTypeFactory(string path) {
    _fragments = loadJsons(path);
  }

  const shared_ptr<DFGType> DFGTypeFactory::createType(const string typeName, const vector<string>& overrideScheme) {
    forward_list<ptree> typeFragments;

    auto prevIt = _fragments.before_begin();
    for(auto it = _fragments.begin(); it != _fragments.end(); ) {
      auto fragment = *it;
      if(fragment.get<string>("@type") == typeName) {
        typeFragments.push_front(std::move(fragment));
        it = _fragments.erase_after(prevIt);
      }
      else {
        prevIt = it;
        ++it;
      }
    }

    auto type = make_shared<DFGType>(typeName,move(typeFragments),overrideScheme);
    _types.insert(make_pair(typeName,type));
    return type;
  }
}

#endif
<|endoftext|>


<commit_before>/*
 * benchmark.cpp
 */
#include "benchmark.hpp"
#include "isa-support.hpp"

#include <cassert>

using namespace std;

arg_provider_t constant(void *value) {
    return [=]{ return value; };
}

const arg_provider_t null_provider = constant(nullptr);

void printBenchName(Context& c, const std::string& name) {
    c.out() << setprecision(c.getPrecision()) << fixed << setw(DESC_WIDTH) << name;
}

void printBenchName(Context& c, const Benchmark& b) {
    printBenchName(c, b->getDescription());
}

void printResultLine(Context& c, const Benchmark& b, const TimingResult& result) {
    std::ostream& os = c.out();
    printBenchName(c, b);
    printAlignedMetrics(c, result.getResults());
    os << endl;
}

void printNameHeader(Context& c) {
    c.out() << setw(DESC_WIDTH) << "Benchmark";
}

void printResultHeader(Context& c) {
    // "Benchmark", "Cycles", "Nanos"
    printNameHeader(c);
    printAlignedMetrics(c, c.getTimerInfo().getMetricNames());
    c.out() << endl;
}

BenchmarkBase::BenchmarkBase(BenchArgs args) : args{std::move(args)} {}

void BenchmarkBase::runAndPrint(Context& c) {
    if (!supports(args.features)) {
        // can't run this test on this hardware
        printBenchName(c, this);
        printOneMetric(c, std::string("Skipped because hardware doesn't support required features: ") +
                container_to_string(args.features));
    } else {
        runAndPrintInner(c);
    }
}

std::string BenchmarkBase::getPath() const {
    return getGroup().getId() + "/" + getId();
}

<commit_msg>another bugfix<commit_after>/*
 * benchmark.cpp
 */
#include "benchmark.hpp"
#include "isa-support.hpp"

#include <cassert>

using namespace std;

arg_provider_t constant(void *value) {
    return [=]{ return value; };
}

const arg_provider_t null_provider = constant(nullptr);

void printBenchName(Context& c, const std::string& name) {
    c.out() << setprecision(c.getPrecision()) << fixed << setw(DESC_WIDTH) << name;
}

void printBenchName(Context& c, const Benchmark& b) {
    printBenchName(c, b->getDescription());
}

void printResultLine(Context& c, const Benchmark& b, const TimingResult& result) {
    std::ostream& os = c.out();
    printBenchName(c, b);
    printAlignedMetrics(c, result.getResults());
    os << endl;
}

void printNameHeader(Context& c) {
    c.out() << setw(DESC_WIDTH) << "Benchmark";
}

void printResultHeader(Context& c) {
    // "Benchmark", "Cycles", "Nanos"
    printNameHeader(c);
    printAlignedMetrics(c, c.getTimerInfo().getMetricNames());
    c.out() << endl;
}

BenchmarkBase::BenchmarkBase(BenchArgs args) : args{std::move(args)} {}

void BenchmarkBase::runAndPrint(Context& c) {
    if (!supports(args.features)) {
        // can't run this test on this hardware
        printBenchName(c, this);
        printOneMetric(c, std::string("Skipped because hardware doesn't support required features: ") +
                container_to_string(args.features));
        c.out() << endl;
    } else {
        runAndPrintInner(c);
    }
}

std::string BenchmarkBase::getPath() const {
    return getGroup().getId() + "/" + getId();
}

<|endoftext|>


<commit_before>// This is a work in progress.
// C.M.Cantalupo

#include <algorithm>
#ifdef _OPENMP
#include <omp.h>
#include <iostream>
#endif
#include "sorter_threaded.hpp"
#include "partition.hpp"
#include "splinter.hpp"
#include "sorter_threaded_exception.hpp"
#ifndef STL_SORT_THREAD_SAFE
#include "quick_sort.hpp"
#endif

void SorterThreaded::sort(std::vector<double>::iterator begin,
                          std::vector<double>::iterator end) {
  // To achieve parallelism here we will choose a set of pivots from
  // the list to be sorted.  Here we will just choose the first elements
  // in the vector.  The number of pivots will determine the number of
  // tasks to be done: one more task than the number of pivots.
  //
  // For simplicity here we will choose the number of tasks to be
  // a factor of the number of threads.  This will be determined
  // by the class attribute taskFactor_.
  //
  // We need to break down the main scaling dimension of the problem,
  // the length of the input vector.  The first thing that we want to
  // do with the input is to partition it into intervals bounded by
  // the pivots.  This can be done with the std::set.upper_bound() to
  // partition the input vector.  In the end we want taskFactor_ times
  // the number of threads jobs and we need one fewer pivot than that
  // to do so.  Bundle up the pivots with a stack and put them in a
  // std::set along with a dummy pivot for the end.  We will
  // call this class "PartitionWall." The set provides a upper_bound
  // bound function so we need to overload less than.
  //

#ifndef _OPENMP
  std::sort(begin, end);
#else
  int numThreads = omp_get_max_threads();

  if (maxThreads_ != -1 && maxThreads_ < numThreads) {
    numThreads = maxThreads_;
  }

  if (numThreads == 1) {
    std::sort(begin,end);
    return;
  }

  int numTasks = numThreads * taskFactor_;

  std::set<double> pivots;
  // Create a set of pivots by going through the vector
  for (std::vector<double>::iterator it = begin;
       it != end && pivots.size() < numThreads*taskFactor_ - 1;
       ++it) {
      pivots.insert(*it);
  }
  // Check that there were as many unique values as we need
  if (pivots.size() < numThreads * taskFactor_ - 1) {
    std::sort(begin, end);
    return;
  }

  SorterThreadedHelper::Splinter splinter(begin, end, numTasks);
  std::vector<std::vector<double>::iterator> chunks;

  splinter.even(numThreads, chunks);

  std::vector<std::vector<double>::iterator> taskOffsets(numTasks);
#pragma omp parallel default (shared)
{
  int threadID = omp_get_thread_num();
  SorterThreadedHelper::Partition partition(pivots);
  partition.fill(chunks[threadID], chunks[threadID+1]);
  std::vector<size_t> mySizes;
  partition.taskSizes(mySizes);

  for (int i = 0; i < numThreads; ++i) {
    if (i == threadID) {
      splinter.addSizes(mySizes);
    }
#pragma omp barrier
  }

  std::vector<std::vector<double>::iterator> offsets;
  for (int i = 0; i < numThreads; ++i) {
    if (i == threadID) {
      splinter.getOffsets(mySizes, offsets);
    }
#pragma omp barrier
  }

  for (int i = 0; i < numTasks; ++i) {
    partition.popTask(offsets[i]);
  }

  if (threadID == numThreads - 1) {
    std::copy(offsets.begin(), offsets.end(), taskOffsets.begin());
  }
}

#pragma omp parallel for schedule (dynamic)
  for (int i = 0; i < numTasks; ++i) {
    if (i != numTasks - 1) {
#ifdef STL_SORT_THREAD_SAFE
      std::sort(taskOffsets[i], taskOffsets[i+1]);
#else
      SorterThreadedHelper::quick_sort(taskOffsets[i], taskOffsets[i+1]);
#endif
    }
    else {
#ifdef STL_SORT_THREAD_SAFE
      std::sort(taskOffsets[numTasks - 1], end);
#else
      SorterThreadedHelper::quick_sort(taskOffsets[numTasks - 1], end);
#endif
    }
  }
#endif //end of #ifdef _OPENMP
}

SorterThreaded::SorterThreaded(int taskFactor, int maxThreads) :
  taskFactor_(taskFactor),
  maxThreads_(maxThreads) {}

void SorterThreaded::setMaxThreads(int maxThreads) {
  maxThreads_ = maxThreads;
}

void SorterThreaded::setTaskFactor(int taskFactor) {
  taskFactor_ = taskFactor;
}

<commit_msg>need to specify the number of threads if it isn't max<commit_after>// This is a work in progress.
// C.M.Cantalupo

#include <algorithm>
#ifdef _OPENMP
#include <omp.h>
#include <iostream>
#endif
#include "sorter_threaded.hpp"
#include "partition.hpp"
#include "splinter.hpp"
#include "sorter_threaded_exception.hpp"
#ifndef STL_SORT_THREAD_SAFE
#include "quick_sort.hpp"
#endif

void SorterThreaded::sort(std::vector<double>::iterator begin,
                          std::vector<double>::iterator end) {
  // To achieve parallelism here we will choose a set of pivots from
  // the list to be sorted.  Here we will just choose the first elements
  // in the vector.  The number of pivots will determine the number of
  // tasks to be done: one more task than the number of pivots.
  //
  // For simplicity here we will choose the number of tasks to be
  // a factor of the number of threads.  This will be determined
  // by the class attribute taskFactor_.
  //
  // We need to break down the main scaling dimension of the problem,
  // the length of the input vector.  The first thing that we want to
  // do with the input is to partition it into intervals bounded by
  // the pivots.  This can be done with the std::set.upper_bound() to
  // partition the input vector.  In the end we want taskFactor_ times
  // the number of threads jobs and we need one fewer pivot than that
  // to do so.  Bundle up the pivots with a stack and put them in a
  // std::set along with a dummy pivot for the end.  We will
  // call this class "PartitionWall." The set provides a upper_bound
  // bound function so we need to overload less than.
  //

#ifndef _OPENMP
  std::sort(begin, end);
#else
  int numThreads = omp_get_max_threads();

  if (maxThreads_ != -1 && maxThreads_ < numThreads) {
    numThreads = maxThreads_;
  }

  if (numThreads == 1) {
    std::sort(begin,end);
    return;
  }

  int numTasks = numThreads * taskFactor_;

  std::set<double> pivots;
  // Create a set of pivots by going through the vector
  for (std::vector<double>::iterator it = begin;
       it != end && pivots.size() < numThreads*taskFactor_ - 1;
       ++it) {
      pivots.insert(*it);
  }
  // Check that there were as many unique values as we need
  if (pivots.size() < numThreads * taskFactor_ - 1) {
    std::sort(begin, end);
    return;
  }

  SorterThreadedHelper::Splinter splinter(begin, end, numTasks);
  std::vector<std::vector<double>::iterator> chunks;

  splinter.even(numThreads, chunks);

  std::vector<std::vector<double>::iterator> taskOffsets(numTasks);
#pragma omp parallel default (shared) num_threads (numThreads)
{
  int threadID = omp_get_thread_num();
  SorterThreadedHelper::Partition partition(pivots);
  partition.fill(chunks[threadID], chunks[threadID+1]);
  std::vector<size_t> mySizes;
  partition.taskSizes(mySizes);

  for (int i = 0; i < numThreads; ++i) {
    if (i == threadID) {
      splinter.addSizes(mySizes);
    }
#pragma omp barrier
  }

  std::vector<std::vector<double>::iterator> offsets;
  for (int i = 0; i < numThreads; ++i) {
    if (i == threadID) {
      splinter.getOffsets(mySizes, offsets);
    }
#pragma omp barrier
  }

  for (int i = 0; i < numTasks; ++i) {
    partition.popTask(offsets[i]);
  }

  if (threadID == numThreads - 1) {
    std::copy(offsets.begin(), offsets.end(), taskOffsets.begin());
  }
}

#pragma omp parallel for schedule (dynamic) num_threads(numThreads)
  for (int i = 0; i < numTasks; ++i) {
    if (i != numTasks - 1) {
#ifdef STL_SORT_THREAD_SAFE
      std::sort(taskOffsets[i], taskOffsets[i+1]);
#else
      SorterThreadedHelper::quick_sort(taskOffsets[i], taskOffsets[i+1]);
#endif
    }
    else {
#ifdef STL_SORT_THREAD_SAFE
      std::sort(taskOffsets[numTasks - 1], end);
#else
      SorterThreadedHelper::quick_sort(taskOffsets[numTasks - 1], end);
#endif
    }
  }
#endif //end of #ifdef _OPENMP
}

SorterThreaded::SorterThreaded(int taskFactor, int maxThreads) :
  taskFactor_(taskFactor),
  maxThreads_(maxThreads) {}

void SorterThreaded::setMaxThreads(int maxThreads) {
  maxThreads_ = maxThreads;
}

void SorterThreaded::setTaskFactor(int taskFactor) {
  taskFactor_ = taskFactor;
}

<|endoftext|>