<commit_before>//------------------------------------------------------------------------------
// CLING - the C++ LLVM-based InterpreterG :)
//
// This file is dual-licensed: you can choose to license it under the University
// of Illinois Open Source License or the GNU Lesser General Public License. See
// LICENSE.TXT for details.
//------------------------------------------------------------------------------

// RUN: cat %s | %cling 2>&1 | FileCheck %s

// This file should be used as regression test for the value printing subsystem
// Reproducers of fixed bugs should be put here

extern "C" int mustPrintFirst() { return 6; }
mustPrintFirst
// CHECK: (int (*)()) Function

// PR #93006
.rawInput 1
extern "C" int printf(const char* fmt, ...);
class A {
public:
  int Var;
  A(int arg) { Var = arg; }
  int someFunc(float) { return 42; }
  ~A() { printf("A d'tor\n"); }
};

const A& foo(const A& arg) { return arg; }
A foo2(const A& arg) { return A(42); }
.rawInput 0

foo(A(12)).Var
// CHECK: (const int) 12
// CHECK: A d'tor
// End PR #93006

// myvector.end() failed to print (roottest/cling/stl/default/VectorSort.C)
foo2(A(42))
// CHECK: (A) @0x{{[0-9a-f]+}}
// CHECK: A d'tor

 // Savannah #96523
int *p = (int*)0x123;
p // CHECK: (int *) 0x123
const int *q = (int*)0x123;
q // CHECK: (const int *) 0x123 <invalid memory address>

0.00001234L // CHECK: (long double) 0.00001234{{[0-9]*}}L

// PR ROOT-5467
&A::someFunc // CHECK: (int (A::*)(float)) Function @0x{{[0-9a-f]+}}

nullptr // CHECK: (nullptr_t) nullptr

true // CHECK: (bool) true
false // CHECK: (bool) false

unordered_multiset<float> {1} // ROOT-7310
// expected-error@2 {{use of undeclared identifier 'unordered_multiset'}}
// expected-error@2 {{expected '(' for function-style cast or type construction}}
// expected-error@2 {{initializer list cannot be used on the right hand side of operator '>'}}

#include <unordered_set>
std::unordered_multiset<float> {1}
// FIXME: BROKEN_ON_LINUX-CHECK: (std::unordered_multiset<float>) { 1.00000f }

// ROOT-7426
#include <string>
#include <vector>
std::vector<std::string> a = { "a", "b", "c" } // CHECK: (std::vector<std::string> &) { "a", "b", "c" }
a // CHECK: (std::vector<std::string> &) { "a", "b", "c" }
a[0] // CHECK: ({{.*}} &) "a"

// ROOT-7918
struct Enumer {
#ifndef _WIN32
  // This variant fails on Windows, and seems conforming behaviour to do so
  enum H {  h = (unsigned long long )-1 };
#else
  enum H : unsigned long long { h = (unsigned long long )-1 };
#endif
};
Enumer::h
// CHECK: (Enumer::H) (Enumer::H::h) : (unsigned long{{( long)?}}) 18446744073709551615

// ROOT-7837
auto bla=[](double *x, double *par, int blub){return x[0]*blub;} // CHECK: ((lambda) &) @0x

#include <functional>
using namespace std::placeholders;
auto fn_moo = std::bind (bla, _1,_2,10) // CHECK: ERROR in cling's callPrintValue(): missing value string.
// expected-error {{use of undeclared identifier 'lambda'}}
// expected-error {{expected expression}}
// expected-error {{type name requires a specifier or qualifier}}
// expected-error {{expected ')'}}
// expected-note  {{to match this '('}}

// Make sure cling survives
12 // CHECK: (int) 12

// ROOT-8077
.rawInput 1
#include <string>
void f(std::string) {}
.rawInput 0
f // CHECK: (void (*)(std::string)) Function @0x{{[0-9a-f]+}}
// CHECK: at input_line_{{[0-9].*}}:1:
// CHECK: void f(std::string) {}
<commit_msg>Update to new output format. Use CHECK-NEXT.<commit_after>//------------------------------------------------------------------------------
// CLING - the C++ LLVM-based InterpreterG :)
//
// This file is dual-licensed: you can choose to license it under the University
// of Illinois Open Source License or the GNU Lesser General Public License. See
// LICENSE.TXT for details.
//------------------------------------------------------------------------------

// RUN: cat %s | %cling 2>&1 | FileCheck %s

// This file should be used as regression test for the value printing subsystem
// Reproducers of fixed bugs should be put here

extern "C" int mustPrintFirst() { return 6; }
mustPrintFirst
// CHECK: (int (*)()) Function

// PR #93006
.rawInput 1
extern "C" int printf(const char* fmt, ...);
class A {
public:
  int Var;
  A(int arg) { Var = arg; }
  int someFunc(float) { return 42; }
  ~A() { printf("A d'tor\n"); }
};

const A& foo(const A& arg) { return arg; }
A foo2(const A& arg) { return A(42); }
.rawInput 0

foo(A(12)).Var
// CHECK: (const int) 12
// CHECK: A d'tor
// End PR #93006

// myvector.end() failed to print (roottest/cling/stl/default/VectorSort.C)
foo2(A(42))
// CHECK: (A) @0x{{[0-9a-f]+}}
// CHECK: A d'tor

 // Savannah #96523
int *p = (int*)0x123;
p // CHECK: (int *) 0x123
const int *q = (int*)0x123;
q // CHECK: (const int *) 0x123 <invalid memory address>

0.00001234L // CHECK-NEXT: (long double) 1.2340000e-05L

// PR ROOT-5467
&A::someFunc // CHECK: (int (A::*)(float)) Function @0x{{[0-9a-f]+}}

nullptr // CHECK: (nullptr_t) nullptr

true // CHECK: (bool) true
false // CHECK: (bool) false

unordered_multiset<float> {1} // ROOT-7310
// expected-error@2 {{use of undeclared identifier 'unordered_multiset'}}
// expected-error@2 {{expected '(' for function-style cast or type construction}}
// expected-error@2 {{initializer list cannot be used on the right hand side of operator '>'}}

#include <unordered_set>
std::unordered_multiset<float> {1}
// FIXME: BROKEN_ON_LINUX-CHECK: (std::unordered_multiset<float>) { 1.00000f }

// ROOT-7426
#include <string>
#include <vector>
std::vector<std::string> a = { "a", "b", "c" } // CHECK: (std::vector<std::string> &) { "a", "b", "c" }
a // CHECK: (std::vector<std::string> &) { "a", "b", "c" }
a[0] // CHECK: ({{.*}} &) "a"

// ROOT-7918
struct Enumer {
#ifndef _WIN32
  // This variant fails on Windows, and seems conforming behaviour to do so
  enum H {  h = (unsigned long long )-1 };
#else
  enum H : unsigned long long { h = (unsigned long long )-1 };
#endif
};
Enumer::h
// CHECK: (Enumer::H) (Enumer::H::h) : (unsigned long{{( long)?}}) 18446744073709551615

// ROOT-7837
auto bla=[](double *x, double *par, int blub){return x[0]*blub;} // CHECK: ((lambda) &) @0x

#include <functional>
using namespace std::placeholders;
auto fn_moo = std::bind (bla, _1,_2,10) // CHECK: ERROR in cling's callPrintValue(): missing value string.
// expected-error {{use of undeclared identifier 'lambda'}}
// expected-error {{expected expression}}
// expected-error {{type name requires a specifier or qualifier}}
// expected-error {{expected ')'}}
// expected-note  {{to match this '('}}

// Make sure cling survives
12 // CHECK: (int) 12

// ROOT-8077
.rawInput 1
#include <string>
void f(std::string) {}
.rawInput 0
f // CHECK: (void (*)(std::string)) Function @0x{{[0-9a-f]+}}
// CHECK: at input_line_{{[0-9].*}}:1:
// CHECK: void f(std::string) {}
<|endoftext|>


<commit_before>// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.

#include <vespa/fastos/fastos.h>
#include "flagattribute.h"
#include <vespa/searchlib/attribute/attributeiterators.h>

#include <vespa/log/log.h>
LOG_SETUP(".searchlib.attribute.flagattribute");

#include <vespa/searchlib/attribute/multinumericattribute.hpp>
#include <vespa/searchlib/queryeval/emptysearch.h>
#include "multivaluemapping.hpp"
#include <vespa/searchlib/common/bitvectoriterator.h>

namespace search {

using queryeval::SearchIterator;
namespace
{

template <class FA, typename T>
class SaveBits
{
    const T  *_map;
    const size_t _mapSize;
    FA &_fa;

public:
    SaveBits(const T *map,
             const size_t mapSize,
             FA &fa)
        : _map(map),
          _mapSize(mapSize),
          _fa(fa)
    {
    }

    void
    save(uint32_t e, uint32_t docId, uint32_t vci, int32_t weight)
    {
        (void) vci;
        (void) weight;
        assert(e < _mapSize);
        _fa.setNewBVValue(docId, _map[e]);
    }
};

}

template <typename B>
FlagAttributeT<B>::FlagAttributeT(const vespalib::string & baseFileName, const AttributeVector::Config & cfg) :
    B(baseFileName, cfg),
    _bitVectorHolder(),
    _bitVectorStore(256),
    _bitVectors(256),
    _bitVectorSize(cfg.getGrowStrategy().getDocsInitialCapacity())
{
}

template <typename B>
AttributeVector::SearchContext::UP
FlagAttributeT<B>::getSearch(QueryTermSimple::UP qTerm,
                             const AttributeVector::SearchContext::Params & params) const
{
    (void) params;
    return AttributeVector::SearchContext::UP (new SearchContext(std::move(qTerm), *this));
}

template <typename B>
void FlagAttributeT<B>::clearOldValues(DocId doc)
{
    const typename B::WType * values(NULL);
    for (uint32_t i(0), m(this->get(doc, values)); i < m; i++) {
        BitVector * bv = _bitVectors[getOffset(values[i].value())];
        if (bv != NULL) {
            bv->clearBit(doc);
        }
    }
}

template <typename B>
bool
FlagAttributeT<B>::onLoadEnumerated(typename B::ReaderBase &attrReader)
{
    typedef typename B::WType::ValueType TT;

    uint32_t numDocs = attrReader.getNumIdx() - 1;
    uint64_t numValues = attrReader.getNumValues();
    uint64_t enumCount = attrReader.getEnumCount();
    assert(numValues == enumCount);
    (void) enumCount;

    this->setNumDocs(numDocs);
    this->setCommittedDocIdLimit(numDocs);

    if (numValues > 0)
        _bitVectorSize = numDocs;

    FileUtil::LoadedBuffer::UP udatBuffer(this->loadUDAT());
    const TT *map = reinterpret_cast<const TT *>(udatBuffer->buffer());
    assert((udatBuffer->size() % sizeof(TT)) == 0);
    size_t mapSize = udatBuffer->size() / sizeof(TT);
    SaveBits<FlagAttributeT<B>, TT> saver(map, mapSize, *this);
    uint32_t maxvc = this->_mvMapping.fillMapped(attrReader,
                                                 numValues,
                                                 map,
                                                 mapSize,
                                                 saver,
                                                 this->getNumDocs(),
                                                 this->hasWeightedSetType());
    this->checkSetMaxValueCount(maxvc);

    return true;
}

template <typename B>
bool FlagAttributeT<B>::onLoad()
{
    for (size_t i(0), m(_bitVectors.size()); i < m; i++) {
        _bitVectorStore[i].reset();
        _bitVectors[i] = NULL;
    }
    _bitVectorSize = 0;
    return B::onLoad();
}

template <typename B>
void FlagAttributeT<B>::setNewValues(DocId doc, const std::vector<typename B::WType> & values)
{
    B::setNewValues(doc, values);
    if (_bitVectorSize == 0) { // attribute being loaded
        _bitVectorSize = this->getNumDocs();
    }
    for (uint32_t i(0), m(values.size()); i < m; i++) {
        typename B::WType value = values[i];
        uint32_t offset = getOffset(value);
        BitVector * bv = _bitVectors[offset];
        if (bv == NULL) {
            assert(_bitVectorSize >= this->getNumDocs());
            _bitVectorStore[offset] = BitVector::create(_bitVectorSize);
            _bitVectors[offset] = _bitVectorStore[offset].get();
            bv = _bitVectors[offset];
            bv->invalidateCachedCount();
            ensureGuardBit(*bv);
        }
        bv->setBit(doc);
    }
}

template <typename B>
void
FlagAttributeT<B>::setNewBVValue(DocId doc, typename B::WType::ValueType value)
{
    uint32_t offset = getOffset(value);
    BitVector * bv = _bitVectors[offset];
    if (bv == NULL) {
        assert(_bitVectorSize >= this->getNumDocs());
            _bitVectorStore[offset] = BitVector::create(_bitVectorSize);
        _bitVectors[offset] = _bitVectorStore[offset].get();
        bv = _bitVectors[offset];
        bv->invalidateCachedCount();
        ensureGuardBit(*bv);
    }
    bv->setBit(doc);
}

template <typename B>
bool
FlagAttributeT<B>::onAddDoc(DocId doc)
{
    bool retval = false;
    if (doc >= _bitVectorSize) {
        resizeBitVectors(this->getNumDocs());
        retval = true;
    } else {
        ensureGuardBit();
    }
    std::atomic_thread_fence(std::memory_order_release);
    clearGuardBit(doc);
    return retval;
}

template <typename B>
void
FlagAttributeT<B>::ensureGuardBit(BitVector & bv)
{
    if (this->getNumDocs() < bv.size()) {
        bv.setBit(this->getNumDocs()); // add guard bit to avoid scanning to the end during search
    }
}

template <typename B>
void
FlagAttributeT<B>::ensureGuardBit()
{
    for (uint32_t i = 0; i < _bitVectors.size(); ++i) {
        BitVector * bv = _bitVectors[i];
        if (bv != NULL) {
            ensureGuardBit(*bv);
        }
    }
}

template <typename B>
void
FlagAttributeT<B>::clearGuardBit(DocId doc)
{
    for (uint32_t i = 0; i < _bitVectors.size(); ++i) {
        BitVector * bv = _bitVectors[i];
        if (bv != NULL) {
            bv->clearBit(doc); // clear guard bit and start using this doc id
        }
    }
}

template <typename B>
void
FlagAttributeT<B>::resizeBitVectors(uint32_t neededSize)
{
    const GrowStrategy & gs = this->getConfig().getGrowStrategy();
    uint32_t newSize = neededSize + (neededSize * gs.getDocsGrowPercent() / 100) + gs.getDocsGrowDelta();
    for (uint32_t i = 0; i < _bitVectors.size(); ++i) {
        BitVector * bv = _bitVectors[i];
        if (bv != NULL) {
            vespalib::GenerationHeldBase::UP hold(bv->grow(newSize));
            ensureGuardBit(*bv);
            _bitVectorHolder.hold(std::move(hold));
        }
    }
    _bitVectorSize = newSize;
    _bitVectorHolder.transferHoldLists(this->getCurrentGeneration());
}

template <typename B>
void
FlagAttributeT<B>::removeOldGenerations(vespalib::GenerationHandler::generation_t firstUsed)
{
    B::removeOldGenerations(firstUsed);
    _bitVectorHolder.trimHoldLists(firstUsed);
}

template <typename B>
FlagAttributeT<B>::SearchContext::SearchContext(QueryTermSimple::UP qTerm, const FlagAttributeT<B> & toBeSearched) :
    BaseSC(std::move(qTerm), toBeSearched),
    _zeroHits(false)
{
}

template <typename B>
SearchIterator::UP
FlagAttributeT<B>::SearchContext::createIterator(fef::TermFieldMatchData *
                                                 matchData,
                                                 bool strict)
{
    if (valid()) {
        if (_low == _high) {
            const Attribute & attr(static_cast<const Attribute &>(attribute()));
            const BitVector * bv(attr.getBitVector(_low));
            if (bv != NULL) {
                return BitVectorIterator::create(bv, attr.getCommittedDocIdLimit(), *matchData, strict);
            } else {
                return SearchIterator::UP(new queryeval::EmptySearch());
            }
        } else {
            SearchIterator::UP flagIterator(
              strict
                 ? new FlagAttributeIteratorStrict<typename FlagAttributeT<B>::SearchContext>(*this, matchData)
                 : new FlagAttributeIteratorT<typename FlagAttributeT<B>::SearchContext>(*this, matchData));
            return flagIterator;
        }
    } else {
        return SearchIterator::UP(new queryeval::EmptySearch());
    }
}

template class FlagAttributeT<FlagBaseImpl>;
template class FlagAttributeT<HugeFlagBaseImpl>;

}
<commit_msg>Use ConstArrayRef when referencing externally owned const array.<commit_after>// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.

#include <vespa/fastos/fastos.h>
#include "flagattribute.h"
#include <vespa/searchlib/attribute/attributeiterators.h>

#include <vespa/log/log.h>
LOG_SETUP(".searchlib.attribute.flagattribute");

#include <vespa/searchlib/attribute/multinumericattribute.hpp>
#include <vespa/searchlib/queryeval/emptysearch.h>
#include "multivaluemapping.hpp"
#include <vespa/searchlib/common/bitvectoriterator.h>

namespace search {

using queryeval::SearchIterator;
namespace
{

template <class FA, typename T>
class SaveBits
{
    vespalib::ConstArrayRef<T> _map;
    FA &_fa;

public:
    SaveBits(vespalib::ConstArrayRef<T> map,
             FA &fa)
        : _map(map),
          _fa(fa)
    {
    }

    void
    save(uint32_t e, uint32_t docId, uint32_t vci, int32_t weight)
    {
        (void) vci;
        (void) weight;
        assert(e < _map.size());
        _fa.setNewBVValue(docId, _map[e]);
    }
};

}

template <typename B>
FlagAttributeT<B>::FlagAttributeT(const vespalib::string & baseFileName, const AttributeVector::Config & cfg) :
    B(baseFileName, cfg),
    _bitVectorHolder(),
    _bitVectorStore(256),
    _bitVectors(256),
    _bitVectorSize(cfg.getGrowStrategy().getDocsInitialCapacity())
{
}

template <typename B>
AttributeVector::SearchContext::UP
FlagAttributeT<B>::getSearch(QueryTermSimple::UP qTerm,
                             const AttributeVector::SearchContext::Params & params) const
{
    (void) params;
    return AttributeVector::SearchContext::UP (new SearchContext(std::move(qTerm), *this));
}

template <typename B>
void FlagAttributeT<B>::clearOldValues(DocId doc)
{
    const typename B::WType * values(NULL);
    for (uint32_t i(0), m(this->get(doc, values)); i < m; i++) {
        BitVector * bv = _bitVectors[getOffset(values[i].value())];
        if (bv != NULL) {
            bv->clearBit(doc);
        }
    }
}

template <typename B>
bool
FlagAttributeT<B>::onLoadEnumerated(typename B::ReaderBase &attrReader)
{
    typedef typename B::WType::ValueType TT;

    uint32_t numDocs = attrReader.getNumIdx() - 1;
    uint64_t numValues = attrReader.getNumValues();
    uint64_t enumCount = attrReader.getEnumCount();
    assert(numValues == enumCount);
    (void) enumCount;

    this->setNumDocs(numDocs);
    this->setCommittedDocIdLimit(numDocs);

    if (numValues > 0)
        _bitVectorSize = numDocs;

    FileUtil::LoadedBuffer::UP udatBuffer(this->loadUDAT());
    assert((udatBuffer->size() % sizeof(TT)) == 0);
    vespalib::ConstArrayRef<TT> map(reinterpret_cast<const TT *>
                                    (udatBuffer->buffer()),
                                    udatBuffer->size() / sizeof(TT));
    SaveBits<FlagAttributeT<B>, TT> saver(map, *this);
    uint32_t maxvc = this->_mvMapping.fillMapped(attrReader,
                                                 numValues,
                                                 &map[0],
                                                 map.size(),
                                                 saver,
                                                 this->getNumDocs(),
                                                 this->hasWeightedSetType());
    this->checkSetMaxValueCount(maxvc);

    return true;
}

template <typename B>
bool FlagAttributeT<B>::onLoad()
{
    for (size_t i(0), m(_bitVectors.size()); i < m; i++) {
        _bitVectorStore[i].reset();
        _bitVectors[i] = NULL;
    }
    _bitVectorSize = 0;
    return B::onLoad();
}

template <typename B>
void FlagAttributeT<B>::setNewValues(DocId doc, const std::vector<typename B::WType> & values)
{
    B::setNewValues(doc, values);
    if (_bitVectorSize == 0) { // attribute being loaded
        _bitVectorSize = this->getNumDocs();
    }
    for (uint32_t i(0), m(values.size()); i < m; i++) {
        typename B::WType value = values[i];
        uint32_t offset = getOffset(value);
        BitVector * bv = _bitVectors[offset];
        if (bv == NULL) {
            assert(_bitVectorSize >= this->getNumDocs());
            _bitVectorStore[offset] = BitVector::create(_bitVectorSize);
            _bitVectors[offset] = _bitVectorStore[offset].get();
            bv = _bitVectors[offset];
            bv->invalidateCachedCount();
            ensureGuardBit(*bv);
        }
        bv->setBit(doc);
    }
}

template <typename B>
void
FlagAttributeT<B>::setNewBVValue(DocId doc, typename B::WType::ValueType value)
{
    uint32_t offset = getOffset(value);
    BitVector * bv = _bitVectors[offset];
    if (bv == NULL) {
        assert(_bitVectorSize >= this->getNumDocs());
            _bitVectorStore[offset] = BitVector::create(_bitVectorSize);
        _bitVectors[offset] = _bitVectorStore[offset].get();
        bv = _bitVectors[offset];
        bv->invalidateCachedCount();
        ensureGuardBit(*bv);
    }
    bv->setBit(doc);
}

template <typename B>
bool
FlagAttributeT<B>::onAddDoc(DocId doc)
{
    bool retval = false;
    if (doc >= _bitVectorSize) {
        resizeBitVectors(this->getNumDocs());
        retval = true;
    } else {
        ensureGuardBit();
    }
    std::atomic_thread_fence(std::memory_order_release);
    clearGuardBit(doc);
    return retval;
}

template <typename B>
void
FlagAttributeT<B>::ensureGuardBit(BitVector & bv)
{
    if (this->getNumDocs() < bv.size()) {
        bv.setBit(this->getNumDocs()); // add guard bit to avoid scanning to the end during search
    }
}

template <typename B>
void
FlagAttributeT<B>::ensureGuardBit()
{
    for (uint32_t i = 0; i < _bitVectors.size(); ++i) {
        BitVector * bv = _bitVectors[i];
        if (bv != NULL) {
            ensureGuardBit(*bv);
        }
    }
}

template <typename B>
void
FlagAttributeT<B>::clearGuardBit(DocId doc)
{
    for (uint32_t i = 0; i < _bitVectors.size(); ++i) {
        BitVector * bv = _bitVectors[i];
        if (bv != NULL) {
            bv->clearBit(doc); // clear guard bit and start using this doc id
        }
    }
}

template <typename B>
void
FlagAttributeT<B>::resizeBitVectors(uint32_t neededSize)
{
    const GrowStrategy & gs = this->getConfig().getGrowStrategy();
    uint32_t newSize = neededSize + (neededSize * gs.getDocsGrowPercent() / 100) + gs.getDocsGrowDelta();
    for (uint32_t i = 0; i < _bitVectors.size(); ++i) {
        BitVector * bv = _bitVectors[i];
        if (bv != NULL) {
            vespalib::GenerationHeldBase::UP hold(bv->grow(newSize));
            ensureGuardBit(*bv);
            _bitVectorHolder.hold(std::move(hold));
        }
    }
    _bitVectorSize = newSize;
    _bitVectorHolder.transferHoldLists(this->getCurrentGeneration());
}

template <typename B>
void
FlagAttributeT<B>::removeOldGenerations(vespalib::GenerationHandler::generation_t firstUsed)
{
    B::removeOldGenerations(firstUsed);
    _bitVectorHolder.trimHoldLists(firstUsed);
}

template <typename B>
FlagAttributeT<B>::SearchContext::SearchContext(QueryTermSimple::UP qTerm, const FlagAttributeT<B> & toBeSearched) :
    BaseSC(std::move(qTerm), toBeSearched),
    _zeroHits(false)
{
}

template <typename B>
SearchIterator::UP
FlagAttributeT<B>::SearchContext::createIterator(fef::TermFieldMatchData *
                                                 matchData,
                                                 bool strict)
{
    if (valid()) {
        if (_low == _high) {
            const Attribute & attr(static_cast<const Attribute &>(attribute()));
            const BitVector * bv(attr.getBitVector(_low));
            if (bv != NULL) {
                return BitVectorIterator::create(bv, attr.getCommittedDocIdLimit(), *matchData, strict);
            } else {
                return SearchIterator::UP(new queryeval::EmptySearch());
            }
        } else {
            SearchIterator::UP flagIterator(
              strict
                 ? new FlagAttributeIteratorStrict<typename FlagAttributeT<B>::SearchContext>(*this, matchData)
                 : new FlagAttributeIteratorT<typename FlagAttributeT<B>::SearchContext>(*this, matchData));
            return flagIterator;
        }
    } else {
        return SearchIterator::UP(new queryeval::EmptySearch());
    }
}

template class FlagAttributeT<FlagBaseImpl>;
template class FlagAttributeT<HugeFlagBaseImpl>;

}
<|endoftext|>


<commit_before>#include "context.h"

#include <QAbstractEventDispatcher>
#include <QDebug>

#warning todo this needs to be a singleton as multiple contexts dont make sense and it makes it eaier to use from qml

static bool isGoodState(int eol)
{
    if (eol < 0) {
        // Error
        return false;
    }

    if (eol > 0) {
        // End of callback chain
        return false;
    }

    return true;
}

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

static void sink_cb(pa_context *context, const pa_sink_info *info, int eol, void *data)
{
    if (!isGoodState(eol))
        return;
    Q_ASSERT(context);
    Q_ASSERT(data);
    ((Context *)data)->sinkCallback(info);
}

static void sink_input_callback(pa_context *context, const pa_sink_input_info *info, int eol, void *data)
{
    if (!isGoodState(eol))
        return;
    Q_ASSERT(context);
    Q_ASSERT(data);
    ((Context *)data)->sinkInputCallback(info);
}

static void client_cb(pa_context *context, const pa_client_info *info, int eol, void *data)
{
    if (!isGoodState(eol))
        return;
    Q_ASSERT(context);
    Q_ASSERT(data);
    ((Context *)data)->clientCallback(info);
}

static void context_state_callback(pa_context *context, void *data)
{
    Q_ASSERT(data);
    ((Context *)data)->contextStateCallback(context);
}

static void subscribe_cb(pa_context *context, pa_subscription_event_type_t type, uint32_t index, void *data)
{
    Q_ASSERT(data);
    ((Context *)data)->subscribeCallback(context, type, index);
}

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

Context::Context(QObject *parent)
    : QObject(parent)
    , m_context(nullptr)
    , m_mainloop(nullptr)
{
    // We require a glib event loop
    if (!QByteArray(QAbstractEventDispatcher::instance()->metaObject()->className()).contains("EventDispatcherGlib")) {
        qDebug() << "Disabling PulseAudio integration for lack of GLib event loop";
        return;
    }

    connectToDaemon();
}

Context::~Context()
{
    pa_context_unref(m_context);
    pa_glib_mainloop_free(m_mainloop);
}

void Context::subscribeCallback(pa_context *context, pa_subscription_event_type_t type, uint32_t index)
{
#warning fixme
//    Q_ASSERT(c == s_context);

    switch (type & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) {
#warning fixme
        case PA_SUBSCRIPTION_EVENT_SINK:
            if ((type & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
                qDebug() << "DEL Sink" << index;
                if (!m_sinks.contains(index)) {
                    m_recentlyDeletedSinks.insert(index);
                } else {
                    const int modelIndex = m_sinks.keys().indexOf(index);
                    m_sinks.take(index)->deleteLater();
                    emit sinkRemoved(modelIndex);
                }
            } else {
                pa_operation *o;
                if (!(o = pa_context_get_sink_info_by_index(context, index, sink_cb, this))) {
                    qWarning() << "pa_context_get_sink_info_by_index() failed";
                    return;
                }
                pa_operation_unref(o);
            }
            break;

//        case PA_SUBSCRIPTION_EVENT_SOURCE:
//            if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
//                if (s_mixers.contains(KMIXPA_CAPTURE))
//                    s_mixers[KMIXPA_CAPTURE]->removeWidget(index);
//            } else {
//                pa_operation *o;
//                if (!(o = pa_context_get_source_info_by_index(c, index, source_cb, NULL))) {
//                    qWarning() << "pa_context_get_source_info_by_index() failed";
//                    return;
//                }
//                pa_operation_unref(o);
//            }
//            break;

        case PA_SUBSCRIPTION_EVENT_SINK_INPUT:
            if ((type & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
                qDebug() << Q_FUNC_INFO << "::: dropping :::" << index;
                if (!m_sinkInputs.contains(index)) {
                    m_recentlyDeletedSinkInputs.insert(index);
                } else {
                    const int modelIndex = m_sinkInputs.keys().indexOf(index);
                    m_sinkInputs.take(index)->deleteLater();
                    emit sinkInputRemoved(modelIndex);
                }
            } else {
                pa_operation *o;
                if (!(o = pa_context_get_sink_input_info(context, index, sink_input_callback, this))) {
                    qWarning() << "pa_context_get_sink_input_info() failed";
                    return;
                }
                pa_operation_unref(o);
            }
            break;

//        case PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT:
//            if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
//                if (s_mixers.contains(KMIXPA_APP_CAPTURE))
//                    s_mixers[KMIXPA_APP_CAPTURE]->removeWidget(index);
//            } else {
//                pa_operation *o;
//                if (!(o = pa_context_get_source_output_info(c, index, source_output_cb, NULL))) {
//                    qWarning() << "pa_context_get_sink_input_info() failed";
//                    return;
//                }
//                pa_operation_unref(o);
//            }
//            break;

        case PA_SUBSCRIPTION_EVENT_CLIENT:
            if ((type & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
                qDebug() << "dropping client" << index;
                if (!m_clients.contains(index)) {
                    m_recentDeletedClients.insert(index);
                } else {
                    const int modelIndex = m_clients.keys().indexOf(index);
                    m_clients.take(index)->deleteLater();
                    emit clientRemoved(modelIndex);
                }
            } else {
                pa_operation *o;
                if (!(o = pa_context_get_client_info(context, index, client_cb, this))) {
                    qWarning() << "pa_context_get_client_info() failed";
                    return;
                }
                pa_operation_unref(o);
            }
            break;

    }
}

void Context::contextStateCallback(pa_context *c)
{
    qDebug() << "state callback";
    pa_context_state_t state = pa_context_get_state(c);
    if (state == PA_CONTEXT_READY) {
        qDebug() << "ready";
        // Attempt to load things up
        pa_operation *o = nullptr;

        // 1. Register for the stream changes (except during probe)
        if (m_context == c) {
#warning fixme
            pa_context_set_subscribe_callback(c, subscribe_cb, this);

            if (!(o = pa_context_subscribe(c, (pa_subscription_mask_t)
                                           (PA_SUBSCRIPTION_MASK_SINK|
                                            PA_SUBSCRIPTION_MASK_SOURCE|
                                            PA_SUBSCRIPTION_MASK_CLIENT|
                                            PA_SUBSCRIPTION_MASK_SINK_INPUT|
                                            PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT), NULL, NULL))) {
                qWarning() << "pa_context_subscribe() failed";
                return;
            }
            pa_operation_unref(o);
        }

#warning fixme
        if (!(o = pa_context_get_sink_info_list(c, sink_cb, this))) {
            qWarning() << "pa_context_get_sink_info_list() failed";
            return;
        }
        pa_operation_unref(o);

//        if (!(o = pa_context_get_source_info_list(c, source_cb, NULL))) {
//            qWarning() << "pa_context_get_source_info_list() failed";
//            return;
//        }
//        pa_operation_unref(o);

        if (!(o = pa_context_get_client_info_list(c, client_cb, this))) {
            qWarning() << "pa_context_client_info_list() failed";
            return;
        }
        pa_operation_unref(o);

        if (!(o = pa_context_get_sink_input_info_list(c, sink_input_callback, this))) {
            qWarning() << "pa_context_get_sink_input_info_list() failed";
            return;
        }
        pa_operation_unref(o);

//        if (!(o = pa_context_get_source_output_info_list(c, source_output_cb, NULL))) {
//            qWarning() << "pa_context_get_source_output_info_list() failed";
//            return;
//        }
//        pa_operation_unref(o);

        /* These calls are not always supported */
//        if ((o = pa_ext_stream_restore_read(c, ext_stream_restore_read_cb, NULL))) {
//            pa_operation_unref(o);

//            pa_ext_stream_restore_set_subscribe_cb(c, ext_stream_restore_subscribe_cb, NULL);

//            if ((o = pa_ext_stream_restore_subscribe(c, 1, NULL, NULL)))
//                pa_operation_unref(o);
//        } else {
//            qWarning() << "Failed to initialize stream_restore extension: " << pa_strerror(pa_context_errno(m_context));
//        }
    } else if (!PA_CONTEXT_IS_GOOD(state)) {
        qDebug() << "context kaput";
        // If we're not probing, it means we've been disconnected from our
        // glib context
        pa_context_unref(m_context);
        m_context = nullptr;
#warning fixme need to clear out all our data and possibly reconnect
    }
}

template <typename C, typename PAInfo, typename AddedSignal, typename UpdatedSignal>
void Context::updateMap(QMap<quint32, C *> &map,
                        QSet<quint32> &exclusionSet,
                        const PAInfo *info,
                        AddedSignal addedSignal,
                        UpdatedSignal updatedSignal)
{
    Q_ASSERT(info);

    if (exclusionSet.remove(info->index)) {
        // Was already removed again.
        return;
    }

    const bool isNew = !map.contains(info->index);

    auto *obj = map.value(info->index, nullptr);
    if (!obj) {
        obj = new C;
    }
    obj->setInfo(info);
    map.insert(info->index, obj);

    const int modelIndex = map.keys().indexOf(info->index);
    Q_ASSERT(modelIndex >= 0);

    if (isNew) {
        emit (this->*addedSignal)(modelIndex);
    } else {
        emit (this->*updatedSignal)(modelIndex);
    }
}

#warning fixme recentlydeleted has the same type for everything making it easy to use the wrong one possibly
void Context::sinkCallback(const pa_sink_info *info)
{
    updateMap<Sink, pa_sink_info>(m_sinks, m_recentlyDeletedSinks,
                                  info,
                                  &Context::sinkAdded,
                                  &Context::sinkUpdated);
}

void Context::clientCallback(const pa_client_info *info)
{
    updateMap<Client, pa_client_info>(m_clients, m_recentDeletedClients,
                                      info,
                                      &Context::clientAdded,
                                      &Context::clientUpdated);
}

void Context::sinkInputCallback(const pa_sink_input_info *info)
{
    updateMap<SinkInput, pa_sink_input_info>(m_sinkInputs, m_recentlyDeletedSinkInputs,
                                             info,
                                             &Context::sinkInputAdded,
                                             &Context::sinkInputUpdated);
}

void Context::setSinkVolume(quint32 index, quint32 volume)
{
    qDebug() << Q_FUNC_INFO << index << volume;
#warning fixme volume limit enforcement needs review for sensibility
    if (volume > 65536)
        return;
    Sink *sink = m_sinks.value(index, nullptr);
    if (!sink)
        return;
    pa_cvolume newVolume = sink->volume();
    for (int i = 0; i < newVolume.channels; ++i) {
        newVolume.values[i] = volume;
    }
    pa_operation *o;
    if (!(o = pa_context_set_sink_volume_by_index(m_context, index, &newVolume, NULL, NULL))) {
        qWarning() <<  "pa_context_set_sink_volume_by_index() failed";
        return;
    }
    pa_operation_unref(o);
}

void Context::setSinkPort(quint32 portIndex)
{
    pa_operation *o;
    if (!(o = pa_context_set_sink_port_by_index(m_context, portIndex, nullptr, nullptr, nullptr))) {
        qWarning() << "pa_context_set_sink_port_by_index failed";
        return;
    }
}

void Context::setSinkInputVolume(quint32 index, quint32 volume)
{
    qDebug() << Q_FUNC_INFO << index << volume;
#warning fixme volume limit enforcement needs review for sensibility also this prevents overdrive
    if (volume > 65536)
        volume = 0;
    SinkInput *obj = m_sinkInputs.value(index, nullptr);
    if (!obj)
        return;
    pa_cvolume newVolume = obj->volume();
    for (int i = 0; i < newVolume.channels; ++i) {
        newVolume.values[i] = volume;
    }
    pa_operation *o;
    if (!(o = pa_context_set_sink_input_volume(m_context, index, &newVolume, NULL, NULL))) {
        qWarning() <<  "pa_context_set_sink_volume_by_index() failed";
        return;
    }
    pa_operation_unref(o);
}

void Context::connectToDaemon()
{
    Q_ASSERT(m_context == nullptr);
    Q_ASSERT(m_mainloop == nullptr);

    qDebug() <<  "Attempting connection to PulseAudio sound daemon";
    m_mainloop = pa_glib_mainloop_new(nullptr);
    pa_mainloop_api *api = pa_glib_mainloop_get_api(m_mainloop);
    Q_ASSERT(api);

    m_context = pa_context_new(api, "QPulse");
    Q_ASSERT(m_context);

    if (pa_context_connect(m_context, NULL, PA_CONTEXT_NOFAIL, 0) < 0) {
        pa_context_unref(m_context);
        m_context = nullptr;
        return;
    }
    pa_context_set_state_callback(m_context, &context_state_callback, this);
}
<commit_msg>s/0/nullptr<commit_after>#include "context.h"

#include <QAbstractEventDispatcher>
#include <QDebug>

#warning todo this needs to be a singleton as multiple contexts dont make sense and it makes it eaier to use from qml

static bool isGoodState(int eol)
{
    if (eol < 0) {
        // Error
        return false;
    }

    if (eol > 0) {
        // End of callback chain
        return false;
    }

    return true;
}

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

static void sink_cb(pa_context *context, const pa_sink_info *info, int eol, void *data)
{
    if (!isGoodState(eol))
        return;
    Q_ASSERT(context);
    Q_ASSERT(data);
    ((Context *)data)->sinkCallback(info);
}

static void sink_input_callback(pa_context *context, const pa_sink_input_info *info, int eol, void *data)
{
    if (!isGoodState(eol))
        return;
    Q_ASSERT(context);
    Q_ASSERT(data);
    ((Context *)data)->sinkInputCallback(info);
}

static void client_cb(pa_context *context, const pa_client_info *info, int eol, void *data)
{
    if (!isGoodState(eol))
        return;
    Q_ASSERT(context);
    Q_ASSERT(data);
    ((Context *)data)->clientCallback(info);
}

static void context_state_callback(pa_context *context, void *data)
{
    Q_ASSERT(data);
    ((Context *)data)->contextStateCallback(context);
}

static void subscribe_cb(pa_context *context, pa_subscription_event_type_t type, uint32_t index, void *data)
{
    Q_ASSERT(data);
    ((Context *)data)->subscribeCallback(context, type, index);
}

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

Context::Context(QObject *parent)
    : QObject(parent)
    , m_context(nullptr)
    , m_mainloop(nullptr)
{
    // We require a glib event loop
    if (!QByteArray(QAbstractEventDispatcher::instance()->metaObject()->className()).contains("EventDispatcherGlib")) {
        qDebug() << "Disabling PulseAudio integration for lack of GLib event loop";
        return;
    }

    connectToDaemon();
}

Context::~Context()
{
    pa_context_unref(m_context);
    pa_glib_mainloop_free(m_mainloop);
}

void Context::subscribeCallback(pa_context *context, pa_subscription_event_type_t type, uint32_t index)
{
#warning fixme
//    Q_ASSERT(c == s_context);

    switch (type & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) {
#warning fixme
        case PA_SUBSCRIPTION_EVENT_SINK:
            if ((type & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
                qDebug() << "DEL Sink" << index;
                if (!m_sinks.contains(index)) {
                    m_recentlyDeletedSinks.insert(index);
                } else {
                    const int modelIndex = m_sinks.keys().indexOf(index);
                    m_sinks.take(index)->deleteLater();
                    emit sinkRemoved(modelIndex);
                }
            } else {
                pa_operation *o;
                if (!(o = pa_context_get_sink_info_by_index(context, index, sink_cb, this))) {
                    qWarning() << "pa_context_get_sink_info_by_index() failed";
                    return;
                }
                pa_operation_unref(o);
            }
            break;

//        case PA_SUBSCRIPTION_EVENT_SOURCE:
//            if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
//                if (s_mixers.contains(KMIXPA_CAPTURE))
//                    s_mixers[KMIXPA_CAPTURE]->removeWidget(index);
//            } else {
//                pa_operation *o;
//                if (!(o = pa_context_get_source_info_by_index(c, index, source_cb, NULL))) {
//                    qWarning() << "pa_context_get_source_info_by_index() failed";
//                    return;
//                }
//                pa_operation_unref(o);
//            }
//            break;

        case PA_SUBSCRIPTION_EVENT_SINK_INPUT:
            if ((type & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
                qDebug() << Q_FUNC_INFO << "::: dropping :::" << index;
                if (!m_sinkInputs.contains(index)) {
                    m_recentlyDeletedSinkInputs.insert(index);
                } else {
                    const int modelIndex = m_sinkInputs.keys().indexOf(index);
                    m_sinkInputs.take(index)->deleteLater();
                    emit sinkInputRemoved(modelIndex);
                }
            } else {
                pa_operation *o;
                if (!(o = pa_context_get_sink_input_info(context, index, sink_input_callback, this))) {
                    qWarning() << "pa_context_get_sink_input_info() failed";
                    return;
                }
                pa_operation_unref(o);
            }
            break;

//        case PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT:
//            if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
//                if (s_mixers.contains(KMIXPA_APP_CAPTURE))
//                    s_mixers[KMIXPA_APP_CAPTURE]->removeWidget(index);
//            } else {
//                pa_operation *o;
//                if (!(o = pa_context_get_source_output_info(c, index, source_output_cb, NULL))) {
//                    qWarning() << "pa_context_get_sink_input_info() failed";
//                    return;
//                }
//                pa_operation_unref(o);
//            }
//            break;

        case PA_SUBSCRIPTION_EVENT_CLIENT:
            if ((type & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
                qDebug() << "dropping client" << index;
                if (!m_clients.contains(index)) {
                    m_recentDeletedClients.insert(index);
                } else {
                    const int modelIndex = m_clients.keys().indexOf(index);
                    m_clients.take(index)->deleteLater();
                    emit clientRemoved(modelIndex);
                }
            } else {
                pa_operation *o;
                if (!(o = pa_context_get_client_info(context, index, client_cb, this))) {
                    qWarning() << "pa_context_get_client_info() failed";
                    return;
                }
                pa_operation_unref(o);
            }
            break;

    }
}

void Context::contextStateCallback(pa_context *c)
{
    qDebug() << "state callback";
    pa_context_state_t state = pa_context_get_state(c);
    if (state == PA_CONTEXT_READY) {
        qDebug() << "ready";
        // Attempt to load things up
        pa_operation *o = nullptr;

        // 1. Register for the stream changes (except during probe)
        if (m_context == c) {
#warning fixme
            pa_context_set_subscribe_callback(c, subscribe_cb, this);

            if (!(o = pa_context_subscribe(c, (pa_subscription_mask_t)
                                           (PA_SUBSCRIPTION_MASK_SINK|
                                            PA_SUBSCRIPTION_MASK_SOURCE|
                                            PA_SUBSCRIPTION_MASK_CLIENT|
                                            PA_SUBSCRIPTION_MASK_SINK_INPUT|
                                            PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT), NULL, NULL))) {
                qWarning() << "pa_context_subscribe() failed";
                return;
            }
            pa_operation_unref(o);
        }

#warning fixme
        if (!(o = pa_context_get_sink_info_list(c, sink_cb, this))) {
            qWarning() << "pa_context_get_sink_info_list() failed";
            return;
        }
        pa_operation_unref(o);

//        if (!(o = pa_context_get_source_info_list(c, source_cb, NULL))) {
//            qWarning() << "pa_context_get_source_info_list() failed";
//            return;
//        }
//        pa_operation_unref(o);

        if (!(o = pa_context_get_client_info_list(c, client_cb, this))) {
            qWarning() << "pa_context_client_info_list() failed";
            return;
        }
        pa_operation_unref(o);

        if (!(o = pa_context_get_sink_input_info_list(c, sink_input_callback, this))) {
            qWarning() << "pa_context_get_sink_input_info_list() failed";
            return;
        }
        pa_operation_unref(o);

//        if (!(o = pa_context_get_source_output_info_list(c, source_output_cb, NULL))) {
//            qWarning() << "pa_context_get_source_output_info_list() failed";
//            return;
//        }
//        pa_operation_unref(o);

        /* These calls are not always supported */
//        if ((o = pa_ext_stream_restore_read(c, ext_stream_restore_read_cb, NULL))) {
//            pa_operation_unref(o);

//            pa_ext_stream_restore_set_subscribe_cb(c, ext_stream_restore_subscribe_cb, NULL);

//            if ((o = pa_ext_stream_restore_subscribe(c, 1, NULL, NULL)))
//                pa_operation_unref(o);
//        } else {
//            qWarning() << "Failed to initialize stream_restore extension: " << pa_strerror(pa_context_errno(m_context));
//        }
    } else if (!PA_CONTEXT_IS_GOOD(state)) {
        qDebug() << "context kaput";
        // If we're not probing, it means we've been disconnected from our
        // glib context
        pa_context_unref(m_context);
        m_context = nullptr;
#warning fixme need to clear out all our data and possibly reconnect
    }
}

template <typename C, typename PAInfo, typename AddedSignal, typename UpdatedSignal>
void Context::updateMap(QMap<quint32, C *> &map,
                        QSet<quint32> &exclusionSet,
                        const PAInfo *info,
                        AddedSignal addedSignal,
                        UpdatedSignal updatedSignal)
{
    Q_ASSERT(info);

    if (exclusionSet.remove(info->index)) {
        // Was already removed again.
        return;
    }

    const bool isNew = !map.contains(info->index);

    auto *obj = map.value(info->index, nullptr);
    if (!obj) {
        obj = new C;
    }
    obj->setInfo(info);
    map.insert(info->index, obj);

    const int modelIndex = map.keys().indexOf(info->index);
    Q_ASSERT(modelIndex >= 0);

    if (isNew) {
        emit (this->*addedSignal)(modelIndex);
    } else {
        emit (this->*updatedSignal)(modelIndex);
    }
}

#warning fixme recentlydeleted has the same type for everything making it easy to use the wrong one possibly
void Context::sinkCallback(const pa_sink_info *info)
{
    updateMap<Sink, pa_sink_info>(m_sinks, m_recentlyDeletedSinks,
                                  info,
                                  &Context::sinkAdded,
                                  &Context::sinkUpdated);
}

void Context::clientCallback(const pa_client_info *info)
{
    updateMap<Client, pa_client_info>(m_clients, m_recentDeletedClients,
                                      info,
                                      &Context::clientAdded,
                                      &Context::clientUpdated);
}

void Context::sinkInputCallback(const pa_sink_input_info *info)
{
    updateMap<SinkInput, pa_sink_input_info>(m_sinkInputs, m_recentlyDeletedSinkInputs,
                                             info,
                                             &Context::sinkInputAdded,
                                             &Context::sinkInputUpdated);
}

void Context::setSinkVolume(quint32 index, quint32 volume)
{
    qDebug() << Q_FUNC_INFO << index << volume;
#warning fixme volume limit enforcement needs review for sensibility
    if (volume > 65536)
        return;
    Sink *sink = m_sinks.value(index, nullptr);
    if (!sink)
        return;
    pa_cvolume newVolume = sink->volume();
    for (int i = 0; i < newVolume.channels; ++i) {
        newVolume.values[i] = volume;
    }
    pa_operation *o;
    if (!(o = pa_context_set_sink_volume_by_index(m_context, index, &newVolume, NULL, NULL))) {
        qWarning() <<  "pa_context_set_sink_volume_by_index() failed";
        return;
    }
    pa_operation_unref(o);
}

void Context::setSinkPort(quint32 portIndex)
{
    pa_operation *o;
    if (!(o = pa_context_set_sink_port_by_index(m_context, portIndex, nullptr, nullptr, nullptr))) {
        qWarning() << "pa_context_set_sink_port_by_index failed";
        return;
    }
}

void Context::setSinkInputVolume(quint32 index, quint32 volume)
{
    qDebug() << Q_FUNC_INFO << index << volume;
#warning fixme volume limit enforcement needs review for sensibility also this prevents overdrive
    if (volume > 65536)
        volume = 0;
    SinkInput *obj = m_sinkInputs.value(index, nullptr);
    if (!obj)
        return;
    pa_cvolume newVolume = obj->volume();
    for (int i = 0; i < newVolume.channels; ++i) {
        newVolume.values[i] = volume;
    }
    pa_operation *o;
    if (!(o = pa_context_set_sink_input_volume(m_context, index, &newVolume, NULL, NULL))) {
        qWarning() <<  "pa_context_set_sink_volume_by_index() failed";
        return;
    }
    pa_operation_unref(o);
}

void Context::connectToDaemon()
{
    Q_ASSERT(m_context == nullptr);
    Q_ASSERT(m_mainloop == nullptr);

    qDebug() <<  "Attempting connection to PulseAudio sound daemon";
    m_mainloop = pa_glib_mainloop_new(nullptr);
    pa_mainloop_api *api = pa_glib_mainloop_get_api(m_mainloop);
    Q_ASSERT(api);

    m_context = pa_context_new(api, "QPulse");
    Q_ASSERT(m_context);

    if (pa_context_connect(m_context, NULL, PA_CONTEXT_NOFAIL, nullptr) < 0) {
        pa_context_unref(m_context);
        m_context = nullptr;
        return;
    }
    pa_context_set_state_callback(m_context, &context_state_callback, this);
}
<|endoftext|>


<commit_before>#include <iostream>
#include <seqan/bam_io.h>

int main()
{
    seqan::BamStream bamInStream("example.sam");

    for (unsigned i = 0; i < length(bamInStream.header.sequenceInfos); ++i)
        std::cout << bamInStream.header.sequenceInfos[i].i1 << '\t'
                  << bamInStream.header.sequenceInfos[i].i2 << '\n';

    return 0;
}
<commit_msg>[DOC] Adjustment to Basic SAM/BAM I/O `example2.cpp`.<commit_after>#include <iostream>
#include <seqan/bam_io.h>

int main()
{
    seqan::BamStream bamInStream("example.bam");

    for (unsigned i = 0; i < length(bamInStream.header.sequenceInfos); ++i)
        std::cout << bamInStream.header.sequenceInfos[i].i1 << '\t'
                  << bamInStream.header.sequenceInfos[i].i2 << '\n';

    return 0;
}
<|endoftext|>


<commit_before>// RUN: %clangxx_asan -mllvm -asan-coverage=1 -DSHARED %s -shared -o %t.so -fPIC
// RUN: %clangxx_asan -mllvm -asan-coverage=1 %s   -o %t -Wl,-R. %t.so
// RUN: export ASAN_OPTIONS=coverage=1:verbosity=1
// RUN: %t 2>&1         | FileCheck %s --check-prefix=CHECK-main
// RUN: %t foo 2>&1     | FileCheck %s --check-prefix=CHECK-foo
// RUN: %t bar 2>&1     | FileCheck %s --check-prefix=CHECK-bar
// RUN: %t foo bar 2>&1 | FileCheck %s --check-prefix=CHECK-foo-bar
// RUN: not %t foo bar 1 2  2>&1 | FileCheck %s --check-prefix=CHECK-report
//
// https://code.google.com/p/address-sanitizer/issues/detail?id=263
// XFAIL: android

#include <stdio.h>
#include <string.h>
#include <unistd.h>

#ifdef SHARED
void bar() { printf("bar\n"); }
#else
__attribute__((noinline))
void foo() { printf("foo\n"); }
extern void bar();

int G[4];

int main(int argc, char **argv) {
  fprintf(stderr, "PID: %d\n", getpid());
  for (int i = 1; i < argc; i++) {
    if (!strcmp(argv[i], "foo"))
      foo();
    if (!strcmp(argv[i], "bar"))
      bar();
  }
  return G[argc];  // Buffer overflow if argc >= 4.
}
#endif

// CHECK-main: PID: [[PID:[0-9]+]]
// CHECK-main: [[PID]].sancov: 1 PCs written
// CHECK-main-NOT: .so.[[PID]]
//
// CHECK-foo: PID: [[PID:[0-9]+]]
// CHECK-foo: [[PID]].sancov: 2 PCs written
// CHECK-foo-NOT: .so.[[PID]]
//
// CHECK-bar: PID: [[PID:[0-9]+]]
// CHECK-bar: [[PID]].sancov: 1 PCs written
// CHECK-bar: .so.[[PID]].sancov: 1 PCs written
//
// CHECK-foo-bar: PID: [[PID:[0-9]+]]
// CHECK-foo-bar: [[PID]].sancov: 2 PCs written
// CHECK-foo-bar: so.[[PID]].sancov: 1 PCs written
//
// CHECK-report: AddressSanitizer: global-buffer-overflow
// CHECK-report: PCs written
<commit_msg>[asan] Avoid hardcoding library path in test binary.<commit_after>// RUN: %clangxx_asan -mllvm -asan-coverage=1 -DSHARED %s -shared -o %T/libcoverage_test.so -fPIC -Wl,--soname,libcoverage_test.so
// RUN: %clangxx_asan -mllvm -asan-coverage=1 %s   -o %t -Wl,-R,\$ORIGIN -L%T -lcoverage_test
// RUN: export ASAN_OPTIONS=coverage=1:verbosity=1
// RUN: %t 2>&1         | FileCheck %s --check-prefix=CHECK-main
// RUN: %t foo 2>&1     | FileCheck %s --check-prefix=CHECK-foo
// RUN: %t bar 2>&1     | FileCheck %s --check-prefix=CHECK-bar
// RUN: %t foo bar 2>&1 | FileCheck %s --check-prefix=CHECK-foo-bar
// RUN: not %t foo bar 1 2  2>&1 | FileCheck %s --check-prefix=CHECK-report
//
// https://code.google.com/p/address-sanitizer/issues/detail?id=263
// XFAIL: android

#include <stdio.h>
#include <string.h>
#include <unistd.h>

#ifdef SHARED
void bar() { printf("bar\n"); }
#else
__attribute__((noinline))
void foo() { printf("foo\n"); }
extern void bar();

int G[4];

int main(int argc, char **argv) {
  fprintf(stderr, "PID: %d\n", getpid());
  for (int i = 1; i < argc; i++) {
    if (!strcmp(argv[i], "foo"))
      foo();
    if (!strcmp(argv[i], "bar"))
      bar();
  }
  return G[argc];  // Buffer overflow if argc >= 4.
}
#endif

// CHECK-main: PID: [[PID:[0-9]+]]
// CHECK-main: [[PID]].sancov: 1 PCs written
// CHECK-main-NOT: .so.[[PID]]
//
// CHECK-foo: PID: [[PID:[0-9]+]]
// CHECK-foo: [[PID]].sancov: 2 PCs written
// CHECK-foo-NOT: .so.[[PID]]
//
// CHECK-bar: PID: [[PID:[0-9]+]]
// CHECK-bar: [[PID]].sancov: 1 PCs written
// CHECK-bar: .so.[[PID]].sancov: 1 PCs written
//
// CHECK-foo-bar: PID: [[PID:[0-9]+]]
// CHECK-foo-bar: [[PID]].sancov: 2 PCs written
// CHECK-foo-bar: so.[[PID]].sancov: 1 PCs written
//
// CHECK-report: AddressSanitizer: global-buffer-overflow
// CHECK-report: PCs written
<|endoftext|>


<commit_before>/*
** Copyright 2009-2012 Merethis
**
** This file is part of Centreon Broker.
**
** Centreon Broker is free software: you can redistribute it and/or
** modify it under the terms of the GNU General Public License version 2
** as published by the Free Software Foundation.
**
** Centreon Broker is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
** General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with Centreon Broker. If not, see
** <http://www.gnu.org/licenses/>.
*/

#include <csignal>
#include <cstring>
#include <QCoreApplication>
#include <QTimer>
#include "com/centreon/broker/config/applier/endpoint.hh"
#include "com/centreon/broker/config/applier/init.hh"
#include "com/centreon/broker/config/applier/logger.hh"
#include "com/centreon/broker/config/applier/modules.hh"
#include "com/centreon/broker/config/parser.hh"
#include "com/centreon/broker/config/state.hh"
#include "com/centreon/broker/exceptions/msg.hh"
#include "com/centreon/broker/logging/file.hh"
#include "com/centreon/broker/logging/logging.hh"
#include "com/centreon/broker/logging/manager.hh"
#include "com/centreon/broker/neb/callbacks.hh"
#include "com/centreon/broker/neb/internal.hh"
#include "com/centreon/broker/neb/monitoring_logger.hh"
#include "com/centreon/engine/common.hh"
#include "com/centreon/engine/globals.hh"
#include "com/centreon/engine/nebcallbacks.hh"

using namespace com::centreon;
using namespace com::centreon::broker;

// Specify the event broker API version.
NEB_API_VERSION(CURRENT_NEB_API_VERSION)

// Centreon Engine/Nagios function.
extern "C" {
  extern timed_event* event_list_high;
  extern timed_event* event_list_high_tail;
}

/**************************************
*                                     *
*           Static Objects            *
*                                     *
**************************************/

// Qt initialization flag.
static bool        gl_initialized_qt(false);
static int         gl_qt_argc;
static char const* gl_qt_argv[2] = {
  "CentreonBrokerModule",
  NULL
};

/**************************************
*                                     *
*          Static Functions           *
*                                     *
**************************************/

/**
 *  Process Qt events.
 *
 *  @param[in] arg Unused.
 */
static void process_qcore(void* arg) {
  (void)arg;
  QCoreApplication* app(QCoreApplication::instance());
  QTimer::singleShot(0, app, SLOT(quit()));
  app->exec();
  return ;
}

/**************************************
*                                     *
*         Exported Functions          *
*                                     *
**************************************/

extern "C" {
  /**
   *  @brief Module exit point.
   *
   *  This function is called when the module gets unloaded by Nagios.
   *  It will deregister all previously registered callbacks and perform
   *  some shutdown stuff.
   *
   *  @param[in] flags  Informational flags.
   *  @param[in] reason Unload reason.
   *
   *  @return 0 on success, any other value on failure.
   */
  int nebmodule_deinit(int flags, int reason) {
    (void)flags;
    (void)reason;

    try {
      // Unregister callbacks.
      neb::unregister_callbacks();

      // Unload singletons.
      broker::config::applier::deinit();

      // Deregister Qt application object.
      if (gl_initialized_qt) {
        timed_event* te(NULL);
        for (timed_event* current = event_list_high;
             current != event_list_high_tail;
             current = current->next)
          if (current->event_data == (void*)process_qcore) {
            te = current;
            break ;
          }
        if (te)
          remove_event(te, &event_list_high, &event_list_high_tail);
        delete QCoreApplication::instance();
      }
    }
    // Avoid exception propagation in C code.
    catch (...) {}

    return (0);
  }

  /**
   *  @brief Module entry point.
   *
   *  This function is called when the module gets loaded by Nagios. It
   *  will register callbacks to catch events and perform some
   *  initialization stuff like config file parsing, thread creation,
   *  ...
   *
   *  @param[in] flags  Informational flags.
   *  @param[in] args   The argument string of the module (shall contain the
   *                    configuration file name).
   *  @param[in] handle The module handle.
   *
   *  @return 0 on success, any other value on failure.
   */
  int nebmodule_init(int flags, char const* args, void* handle) {
    // Initialization.
    broker::config::applier::init();

    // Save module handle and flags for future use.
    neb::gl_mod_flags = flags;
    neb::gl_mod_handle = handle;

    // Set module informations.
    neb_set_module_info(
      neb::gl_mod_handle,
      NEBMODULE_MODINFO_TITLE,
      "Centreon Broker's cbmod");
    neb_set_module_info(
      neb::gl_mod_handle,
      NEBMODULE_MODINFO_AUTHOR,
      "Merethis");
    neb_set_module_info(
      neb::gl_mod_handle,
      NEBMODULE_MODINFO_COPYRIGHT,
      "Copyright 2009-2012 Merethis");
    neb_set_module_info(
      neb::gl_mod_handle,
      NEBMODULE_MODINFO_VERSION,
      CENTREON_BROKER_VERSION);
    neb_set_module_info(
      neb::gl_mod_handle,
      NEBMODULE_MODINFO_LICENSE,
      "GPL version 2");
    neb_set_module_info(
      neb::gl_mod_handle,
      NEBMODULE_MODINFO_DESC,
      "cbmod is part of CentreonBroker and is designed to " \
      "convert internal Nagios events to a proper data "    \
      "stream that can then be parsed by CentreonBroker's " \
      "cbd.");

    // Initialize Qt if not already done by parent process.
    if (!QCoreApplication::instance()) {
      gl_initialized_qt = true;
      new QCoreApplication(gl_qt_argc, (char**)gl_qt_argv);
      signal(SIGCHLD, SIG_DFL);
    }

    // Disable timestamp printing in logs (cause starvation when forking).
    logging::file::with_timestamp(false);

    // Default logging object.
    neb::monitoring_logger monlog;

    try {
      // Default logging object.
      {
        // Debug ?
        bool debug;
        char const* dbg_flag("-d ");
        if (args && !strncmp(args, dbg_flag, strlen(dbg_flag))) {
          debug = true;
          args += strlen(dbg_flag);
        }
        else
          debug = false;

        // Add log.
        logging::manager::instance().log_on(
                                       monlog,
                                       (debug
                                        ? logging::config_type
                                        | logging::debug_type
                                        | logging::error_type
                                        | logging::info_type
                                        : logging::config_type
                                        | logging::error_type
                                        | logging::info_type),
                                       (debug ?
                                        logging::low
                                        : logging::high));
      }

      // Set configuration file.
      if (args) {
        char const* config_file("config_file=");
        size_t config_file_size(strlen(config_file));
        if (!strncmp(args, config_file, config_file_size))
          args += config_file_size;
        neb::gl_configuration_file = args;
      }
      else
        throw (exceptions::msg()
                 << "main: no configuration file provided");

      // Try configuration parsing.
      broker::config::parser p;
      broker::config::state s;
      p.parse(neb::gl_configuration_file, s);

      // Apply loggers.
      broker::config::applier::logger::instance().apply(s.loggers());

      // Remove monitoring log.
      logging::manager::instance().log_on(monlog, 0);
    }
    catch (std::exception const& e) {
      logging::error(logging::high) << e.what();
      logging::manager::instance().log_on(monlog, 0);
      return (-1);
    }
    catch (...) {
      logging::error(logging::high)
        << "main: configuration file parsing failed";
      logging::manager::instance().log_on(monlog, 0);
      return (-1);
    }

    // Remove old monitoring object.
    logging::manager::instance().log_on(monlog, 0);

    // Register process callback.
    if (neb_register_callback(
          NEBCALLBACK_PROCESS_DATA,
          neb::gl_mod_handle,
          0,
          &neb::callback_process) != OK)
      return (-1);

    // Process Qt events if necessary.
    if (gl_initialized_qt)
      schedule_new_event(
        99,
        1,
        time(NULL) + 1,
        1,
        1,
        NULL,
        1,
        (void*)process_qcore,
        NULL,
        0);

    return (0);
  }
}
<commit_msg>Set locale when Broker gets loaded as cbmod.<commit_after>/*
** Copyright 2009-2012 Merethis
**
** This file is part of Centreon Broker.
**
** Centreon Broker is free software: you can redistribute it and/or
** modify it under the terms of the GNU General Public License version 2
** as published by the Free Software Foundation.
**
** Centreon Broker is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
** General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with Centreon Broker. If not, see
** <http://www.gnu.org/licenses/>.
*/

#include <csignal>
#include <cstring>
#include <locale.h>
#include <QCoreApplication>
#include <QTimer>
#include "com/centreon/broker/config/applier/endpoint.hh"
#include "com/centreon/broker/config/applier/init.hh"
#include "com/centreon/broker/config/applier/logger.hh"
#include "com/centreon/broker/config/applier/modules.hh"
#include "com/centreon/broker/config/parser.hh"
#include "com/centreon/broker/config/state.hh"
#include "com/centreon/broker/exceptions/msg.hh"
#include "com/centreon/broker/logging/file.hh"
#include "com/centreon/broker/logging/logging.hh"
#include "com/centreon/broker/logging/manager.hh"
#include "com/centreon/broker/neb/callbacks.hh"
#include "com/centreon/broker/neb/internal.hh"
#include "com/centreon/broker/neb/monitoring_logger.hh"
#include "com/centreon/engine/common.hh"
#include "com/centreon/engine/globals.hh"
#include "com/centreon/engine/nebcallbacks.hh"

using namespace com::centreon;
using namespace com::centreon::broker;

// Specify the event broker API version.
NEB_API_VERSION(CURRENT_NEB_API_VERSION)

// Centreon Engine/Nagios function.
extern "C" {
  extern timed_event* event_list_high;
  extern timed_event* event_list_high_tail;
}

/**************************************
*                                     *
*           Static Objects            *
*                                     *
**************************************/

// Qt initialization flag.
static bool        gl_initialized_qt(false);
static int         gl_qt_argc;
static char const* gl_qt_argv[2] = {
  "CentreonBrokerModule",
  NULL
};

/**************************************
*                                     *
*          Static Functions           *
*                                     *
**************************************/

/**
 *  Process Qt events.
 *
 *  @param[in] arg Unused.
 */
static void process_qcore(void* arg) {
  (void)arg;
  QCoreApplication* app(QCoreApplication::instance());
  QTimer::singleShot(0, app, SLOT(quit()));
  app->exec();
  return ;
}

/**************************************
*                                     *
*         Exported Functions          *
*                                     *
**************************************/

extern "C" {
  /**
   *  @brief Module exit point.
   *
   *  This function is called when the module gets unloaded by Nagios.
   *  It will deregister all previously registered callbacks and perform
   *  some shutdown stuff.
   *
   *  @param[in] flags  Informational flags.
   *  @param[in] reason Unload reason.
   *
   *  @return 0 on success, any other value on failure.
   */
  int nebmodule_deinit(int flags, int reason) {
    (void)flags;
    (void)reason;

    try {
      // Unregister callbacks.
      neb::unregister_callbacks();

      // Unload singletons.
      broker::config::applier::deinit();

      // Deregister Qt application object.
      if (gl_initialized_qt) {
        timed_event* te(NULL);
        for (timed_event* current = event_list_high;
             current != event_list_high_tail;
             current = current->next)
          if (current->event_data == (void*)process_qcore) {
            te = current;
            break ;
          }
        if (te)
          remove_event(te, &event_list_high, &event_list_high_tail);
        delete QCoreApplication::instance();
      }
    }
    // Avoid exception propagation in C code.
    catch (...) {}

    return (0);
  }

  /**
   *  @brief Module entry point.
   *
   *  This function is called when the module gets loaded by Nagios. It
   *  will register callbacks to catch events and perform some
   *  initialization stuff like config file parsing, thread creation,
   *  ...
   *
   *  @param[in] flags  Informational flags.
   *  @param[in] args   The argument string of the module (shall contain the
   *                    configuration file name).
   *  @param[in] handle The module handle.
   *
   *  @return 0 on success, any other value on failure.
   */
  int nebmodule_init(int flags, char const* args, void* handle) {
    // Initialization.
    broker::config::applier::init();

    // Save module handle and flags for future use.
    neb::gl_mod_flags = flags;
    neb::gl_mod_handle = handle;

    // Set module informations.
    neb_set_module_info(
      neb::gl_mod_handle,
      NEBMODULE_MODINFO_TITLE,
      "Centreon Broker's cbmod");
    neb_set_module_info(
      neb::gl_mod_handle,
      NEBMODULE_MODINFO_AUTHOR,
      "Merethis");
    neb_set_module_info(
      neb::gl_mod_handle,
      NEBMODULE_MODINFO_COPYRIGHT,
      "Copyright 2009-2012 Merethis");
    neb_set_module_info(
      neb::gl_mod_handle,
      NEBMODULE_MODINFO_VERSION,
      CENTREON_BROKER_VERSION);
    neb_set_module_info(
      neb::gl_mod_handle,
      NEBMODULE_MODINFO_LICENSE,
      "GPL version 2");
    neb_set_module_info(
      neb::gl_mod_handle,
      NEBMODULE_MODINFO_DESC,
      "cbmod is part of CentreonBroker and is designed to " \
      "convert internal Nagios events to a proper data "    \
      "stream that can then be parsed by CentreonBroker's " \
      "cbd.");

    // Initialize Qt if not already done by parent process.
    if (!QCoreApplication::instance()) {
      gl_initialized_qt = true;
      new QCoreApplication(gl_qt_argc, (char**)gl_qt_argv);
      signal(SIGCHLD, SIG_DFL);
    }

    // Reset locale.
    setlocale(LC_NUMERIC, "C");

    // Disable timestamp printing in logs (cause starvation when forking).
    logging::file::with_timestamp(false);

    // Default logging object.
    neb::monitoring_logger monlog;

    try {
      // Default logging object.
      {
        // Debug ?
        bool debug;
        char const* dbg_flag("-d ");
        if (args && !strncmp(args, dbg_flag, strlen(dbg_flag))) {
          debug = true;
          args += strlen(dbg_flag);
        }
        else
          debug = false;

        // Add log.
        logging::manager::instance().log_on(
                                       monlog,
                                       (debug
                                        ? logging::config_type
                                        | logging::debug_type
                                        | logging::error_type
                                        | logging::info_type
                                        : logging::config_type
                                        | logging::error_type
                                        | logging::info_type),
                                       (debug ?
                                        logging::low
                                        : logging::high));
      }

      // Set configuration file.
      if (args) {
        char const* config_file("config_file=");
        size_t config_file_size(strlen(config_file));
        if (!strncmp(args, config_file, config_file_size))
          args += config_file_size;
        neb::gl_configuration_file = args;
      }
      else
        throw (exceptions::msg()
                 << "main: no configuration file provided");

      // Try configuration parsing.
      broker::config::parser p;
      broker::config::state s;
      p.parse(neb::gl_configuration_file, s);

      // Apply loggers.
      broker::config::applier::logger::instance().apply(s.loggers());

      // Remove monitoring log.
      logging::manager::instance().log_on(monlog, 0);
    }
    catch (std::exception const& e) {
      logging::error(logging::high) << e.what();
      logging::manager::instance().log_on(monlog, 0);
      return (-1);
    }
    catch (...) {
      logging::error(logging::high)
        << "main: configuration file parsing failed";
      logging::manager::instance().log_on(monlog, 0);
      return (-1);
    }

    // Remove old monitoring object.
    logging::manager::instance().log_on(monlog, 0);

    // Register process callback.
    if (neb_register_callback(
          NEBCALLBACK_PROCESS_DATA,
          neb::gl_mod_handle,
          0,
          &neb::callback_process) != OK)
      return (-1);

    // Process Qt events if necessary.
    if (gl_initialized_qt)
      schedule_new_event(
        99,
        1,
        time(NULL) + 1,
        1,
        1,
        NULL,
        1,
        (void*)process_qcore,
        NULL,
        0);

    return (0);
  }
}
<|endoftext|>


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

#include <iostream>
#include <type_traits>

// Google Test
#include <gtest/gtest.h>
// rocPRIM API
#include <rocprim/rocprim.hpp>

#include "test_utils.hpp"

TEST(RocprimZipIteratorTests, Traits)
{
    ASSERT_TRUE((
        std::is_same<
            rocprim::zip_iterator<
                rocprim::tuple<int*, double*, char*>
            >::reference,
            rocprim::tuple<int&, double&, char&>
        >::value
    ));
    ASSERT_TRUE((
        std::is_same<
            rocprim::zip_iterator<
                rocprim::tuple<const int*, double*, const char*>
            >::reference,
            rocprim::tuple<const int&, double&, const char&>
        >::value
    ));
    auto to_double = [](const int& x) -> double { return double(x); };
    ASSERT_TRUE((
        std::is_same<
            rocprim::zip_iterator<
                rocprim::tuple<
                    rocprim::counting_iterator<int>,
                    rocprim::transform_iterator<int*, decltype(to_double)>
                >
            >::reference,
            rocprim::tuple<
                rocprim::counting_iterator<int>::reference,
                rocprim::transform_iterator<int*, decltype(to_double)>::reference
            >
        >::value
    ));
}

TEST(RocprimZipIteratorTests, Basics)
{
    int a[] = { 1, 2, 3, 4, 5};
    int b[] = { 6, 7, 8, 9, 10};
    double c[] = { 1., 2., 3., 4., 5.};
    auto iterator_tuple = rocprim::make_tuple(a, b, c);

    // Constructor
    rocprim::zip_iterator<decltype(iterator_tuple)> zit(iterator_tuple);

    // dereferencing
    ASSERT_EQ(*zit, rocprim::make_tuple(1, 6, 1.0));
    *zit = rocprim::make_tuple(1, 8, 15);
    ASSERT_EQ(*zit, rocprim::make_tuple(1, 8, 15.0));
    ASSERT_EQ(a[0], 1);
    ASSERT_EQ(b[0], 8);
    ASSERT_EQ(c[0], 15.0);
    auto ref = *zit;
    ref = rocprim::make_tuple(1, 6, 1.0);
    ASSERT_EQ(*zit, rocprim::make_tuple(1, 6, 1.0));
    ASSERT_EQ(a[0], 1);
    ASSERT_EQ(b[0], 6);
    ASSERT_EQ(c[0], 1.0);

    // inc, dec, advance
    ++zit;
    ASSERT_EQ(*zit, rocprim::make_tuple(2, 7, 2.0));
    zit++;
    ASSERT_EQ(*zit, rocprim::make_tuple(3, 8, 3.0));
    --zit;
    ASSERT_EQ(*zit, rocprim::make_tuple(2, 7, 2.0));
    zit--;
    ASSERT_EQ(*zit, rocprim::make_tuple(1, 6, 1.0));
    zit += 3;
    ASSERT_EQ(*zit, rocprim::make_tuple(4, 9, 4.0));
    zit -= 2;
    ASSERT_EQ(*zit, rocprim::make_tuple(2, 7, 2.0));

    // <, >, <=, >=, ==, !=
    auto zit2 = rocprim::zip_iterator<decltype(iterator_tuple)>(iterator_tuple);
    ASSERT_LT(zit2, zit);
    ASSERT_GT(zit, zit2);

    ASSERT_LE(zit2, zit);
    ASSERT_LE(zit, zit);
    ASSERT_LE(zit2, zit2);
    ASSERT_GE(zit, zit2);
    ASSERT_GE(zit, zit);
    ASSERT_GE(zit2, zit2);

    ASSERT_NE(zit2, zit);
    ASSERT_NE(zit, zit2);
    ASSERT_NE(zit2, ++rocprim::zip_iterator<decltype(iterator_tuple)>(iterator_tuple));

    ASSERT_EQ(zit2, zit2);
    ASSERT_EQ(zit, zit);
    ASSERT_EQ(zit2, rocprim::zip_iterator<decltype(iterator_tuple)>(iterator_tuple));

    // distance
    ASSERT_EQ(zit - zit2, 1);
    ASSERT_EQ(zit2 - zit, -1);
    ASSERT_EQ(zit - zit, 0);

    // []
    ASSERT_EQ((zit2[0]), rocprim::make_tuple(1, 6, 1.0));
    ASSERT_EQ((zit2[2]), rocprim::make_tuple(3, 8, 3.0));
    // +
    ASSERT_EQ(*(zit2+3), rocprim::make_tuple(4, 9, 4.0));
}

TEST(RocprimZipIteratorTests, Transform)
{
    using T1 = int;
    using T2 = double;
    using T3 = unsigned char;
    using U = T1;
    const bool debug_synchronous = false;
    const size_t size = 1024 * 16;

    hc::accelerator acc;
    hc::accelerator_view acc_view = acc.create_view();

    // Generate data
    std::vector<T1> input1 = test_utils::get_random_data<T1>(size, 1, 100);
    std::vector<T2> input2 = test_utils::get_random_data<T2>(size, 1, 100);
    std::vector<T3> input3 = test_utils::get_random_data<T3>(size, 1, 100);

    hc::array<T1> d_input1(hc::extent<1>(size), input1.begin(), acc_view);
    hc::array<T2> d_input2(hc::extent<1>(size), input2.begin(), acc_view);
    hc::array<T3> d_input3(hc::extent<1>(size), input3.begin(), acc_view);
    hc::array<U> d_output(size, acc_view);
    acc_view.wait();

    auto transform_op =
        [](const rocprim::tuple<T1, T2, T3>& t) [[hc]] [[cpu]] -> int
        {
            return rocprim::get<0>(t) + rocprim::get<1>(t) + rocprim::get<2>(t);
        };

    // Calculate expected results on host
    std::vector<U> expected(input1.size());
    std::transform(
        rocprim::make_zip_iterator(
            rocprim::make_tuple(input1.begin(), input2.begin(), input3.begin())
        ),
        rocprim::make_zip_iterator(
            rocprim::make_tuple(input1.end(), input2.end(), input3.end())
        ),
        expected.begin(),
        transform_op
    );

    // Run
    rocprim::transform(
        rocprim::make_zip_iterator(
            rocprim::make_tuple(
                d_input1.accelerator_pointer(),
                d_input2.accelerator_pointer(),
                d_input3.accelerator_pointer()
            )
        ),
        d_output.accelerator_pointer(),
        input1.size(),
        transform_op,
        acc_view,
        debug_synchronous
    );
    acc_view.wait();

    // Check if output values are as expected
    std::vector<U> output = d_output;
    for(size_t i = 0; i < output.size(); i++)
    {
        SCOPED_TRACE(testing::Message() << "where index = " << i);
        auto diff = std::max<U>(std::abs(0.01f * expected[i]), U(0.01f));
        if(std::is_integral<U>::value) diff = 0;
        ASSERT_NEAR(output[i], expected[i], diff);
    }
}

TEST(RocprimZipIteratorTests, TransformReduce)
{
    using T1 = int;
    using T2 = unsigned int;
    using T3 = unsigned char;
    using U1 = T1;
    using U2 = T2;
    const bool debug_synchronous = false;
    const size_t size = 1024 * 16;

    hc::accelerator acc;
    hc::accelerator_view acc_view = acc.create_view();

    // Generate data
    std::vector<T1> input1 = test_utils::get_random_data<T1>(size, 1, 100);
    std::vector<T2> input2 = test_utils::get_random_data<T2>(size, 1, 50);
    std::vector<T3> input3 = test_utils::get_random_data<T3>(size, 1, 10);

    hc::array<T1> d_input1(hc::extent<1>(size), input1.begin(), acc_view);
    hc::array<T2> d_input2(hc::extent<1>(size), input2.begin(), acc_view);
    hc::array<T3> d_input3(hc::extent<1>(size), input3.begin(), acc_view);
    hc::array<U1> d_output1(1, acc_view);
    hc::array<U2> d_output2(1, acc_view);
    acc_view.wait();

    // transform function
    auto transform_op =
        [](const rocprim::tuple<T1, T2, T3>& t) [[hc]] [[cpu]]
            -> rocprim::tuple<T1, T2>
        {
            return rocprim::make_tuple(
                rocprim::get<0>(t), T2(rocprim::get<1>(t) + rocprim::get<2>(t))
            );
        };

    // reduce function
    auto reduce_op =
        [](const rocprim::tuple<T1, T2>& t1,
           const rocprim::tuple<T1, T2>& t2) [[hc]] [[cpu]]
            -> rocprim::tuple<T1, T2>
        {
            return rocprim::make_tuple(
                rocprim::get<0>(t1) + rocprim::get<0>(t2),
                rocprim::get<1>(t1) + rocprim::get<1>(t2)
            );
        };

    // Calculate expected results on host
    U1 expected1 = std::accumulate(input1.begin(), input1.end(), T1(0));
    U2 expected2 = std::accumulate(input2.begin(), input2.end(), T2(0))
        + std::accumulate(input3.begin(), input3.end(), T2(0));

    // temp storage
    size_t temp_storage_size_bytes;
    // Get size of d_temp_storage
    rocprim::reduce(
        nullptr,
        temp_storage_size_bytes,
        rocprim::make_transform_iterator(
            rocprim::make_zip_iterator(
                rocprim::make_tuple(
                    d_input1.accelerator_pointer(),
                    d_input2.accelerator_pointer(),
                    d_input3.accelerator_pointer()
                )
            ),
            transform_op
        ),
        rocprim::make_zip_iterator(
            rocprim::make_tuple(
                d_output1.accelerator_pointer(),
                d_output2.accelerator_pointer()
            )
        ),
        input1.size(),
        reduce_op,
        acc_view,
        debug_synchronous
    );
    acc_view.wait();

    // temp_storage_size_bytes must be >0
    ASSERT_GT(temp_storage_size_bytes, 0);

    // allocate temporary storage
    hc::array<char> d_temp_storage(temp_storage_size_bytes, acc_view);
    acc_view.wait();

    // Run
    rocprim::reduce(
        d_temp_storage.accelerator_pointer(),
        temp_storage_size_bytes,
        rocprim::make_transform_iterator(
            rocprim::make_zip_iterator(
                rocprim::make_tuple(
                    d_input1.accelerator_pointer(),
                    d_input2.accelerator_pointer(),
                    d_input3.accelerator_pointer()
                )
            ),
            transform_op
        ),
        rocprim::make_zip_iterator(
            rocprim::make_tuple(
                d_output1.accelerator_pointer(),
                d_output2.accelerator_pointer()
            )
        ),
        input1.size(),
        reduce_op,
        acc_view,
        debug_synchronous
    );
    acc_view.wait();

    // Check if output values are as expected
    std::vector<U1> output1 = d_output1;
    auto diff1 = std::max<U1>(std::abs(0.01f * expected1), U1(0.01f));
    if(std::is_integral<U1>::value) diff1 = 0;
    ASSERT_NEAR(output1[0], expected1, diff1);

    std::vector<U2> output2 = d_output2;
    auto diff2 = std::max<U2>(std::abs(0.01f * expected2), U2(0.01f));
    if(std::is_integral<U2>::value) diff2 = 0;
    ASSERT_NEAR(output2[0], expected2, diff2);
}
<commit_msg>Test zip_iterator with device-lvl scan function<commit_after>// MIT License
//
// Copyright (c) 2017 Advanced Micro Devices, Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

#include <iostream>
#include <type_traits>

// Google Test
#include <gtest/gtest.h>
// rocPRIM API
#include <rocprim/rocprim.hpp>

#include "test_utils.hpp"

TEST(RocprimZipIteratorTests, Traits)
{
    ASSERT_TRUE((
        std::is_same<
            rocprim::zip_iterator<
                rocprim::tuple<int*, double*, char*>
            >::reference,
            rocprim::tuple<int&, double&, char&>
        >::value
    ));
    ASSERT_TRUE((
        std::is_same<
            rocprim::zip_iterator<
                rocprim::tuple<const int*, double*, const char*>
            >::reference,
            rocprim::tuple<const int&, double&, const char&>
        >::value
    ));
    auto to_double = [](const int& x) -> double { return double(x); };
    ASSERT_TRUE((
        std::is_same<
            rocprim::zip_iterator<
                rocprim::tuple<
                    rocprim::counting_iterator<int>,
                    rocprim::transform_iterator<int*, decltype(to_double)>
                >
            >::reference,
            rocprim::tuple<
                rocprim::counting_iterator<int>::reference,
                rocprim::transform_iterator<int*, decltype(to_double)>::reference
            >
        >::value
    ));
}

TEST(RocprimZipIteratorTests, Basics)
{
    int a[] = { 1, 2, 3, 4, 5};
    int b[] = { 6, 7, 8, 9, 10};
    double c[] = { 1., 2., 3., 4., 5.};
    auto iterator_tuple = rocprim::make_tuple(a, b, c);

    // Constructor
    rocprim::zip_iterator<decltype(iterator_tuple)> zit(iterator_tuple);

    // dereferencing
    ASSERT_EQ(*zit, rocprim::make_tuple(1, 6, 1.0));
    *zit = rocprim::make_tuple(1, 8, 15);
    ASSERT_EQ(*zit, rocprim::make_tuple(1, 8, 15.0));
    ASSERT_EQ(a[0], 1);
    ASSERT_EQ(b[0], 8);
    ASSERT_EQ(c[0], 15.0);
    auto ref = *zit;
    ref = rocprim::make_tuple(1, 6, 1.0);
    ASSERT_EQ(*zit, rocprim::make_tuple(1, 6, 1.0));
    ASSERT_EQ(a[0], 1);
    ASSERT_EQ(b[0], 6);
    ASSERT_EQ(c[0], 1.0);

    // inc, dec, advance
    ++zit;
    ASSERT_EQ(*zit, rocprim::make_tuple(2, 7, 2.0));
    zit++;
    ASSERT_EQ(*zit, rocprim::make_tuple(3, 8, 3.0));
    --zit;
    ASSERT_EQ(*zit, rocprim::make_tuple(2, 7, 2.0));
    zit--;
    ASSERT_EQ(*zit, rocprim::make_tuple(1, 6, 1.0));
    zit += 3;
    ASSERT_EQ(*zit, rocprim::make_tuple(4, 9, 4.0));
    zit -= 2;
    ASSERT_EQ(*zit, rocprim::make_tuple(2, 7, 2.0));

    // <, >, <=, >=, ==, !=
    auto zit2 = rocprim::zip_iterator<decltype(iterator_tuple)>(iterator_tuple);
    ASSERT_LT(zit2, zit);
    ASSERT_GT(zit, zit2);

    ASSERT_LE(zit2, zit);
    ASSERT_LE(zit, zit);
    ASSERT_LE(zit2, zit2);
    ASSERT_GE(zit, zit2);
    ASSERT_GE(zit, zit);
    ASSERT_GE(zit2, zit2);

    ASSERT_NE(zit2, zit);
    ASSERT_NE(zit, zit2);
    ASSERT_NE(zit2, ++rocprim::zip_iterator<decltype(iterator_tuple)>(iterator_tuple));

    ASSERT_EQ(zit2, zit2);
    ASSERT_EQ(zit, zit);
    ASSERT_EQ(zit2, rocprim::zip_iterator<decltype(iterator_tuple)>(iterator_tuple));

    // distance
    ASSERT_EQ(zit - zit2, 1);
    ASSERT_EQ(zit2 - zit, -1);
    ASSERT_EQ(zit - zit, 0);

    // []
    ASSERT_EQ((zit2[0]), rocprim::make_tuple(1, 6, 1.0));
    ASSERT_EQ((zit2[2]), rocprim::make_tuple(3, 8, 3.0));
    // +
    ASSERT_EQ(*(zit2+3), rocprim::make_tuple(4, 9, 4.0));
}

TEST(RocprimZipIteratorTests, Transform)
{
    using T1 = int;
    using T2 = double;
    using T3 = unsigned char;
    using U = T1;
    const bool debug_synchronous = false;
    const size_t size = 1024 * 16;

    hc::accelerator acc;
    hc::accelerator_view acc_view = acc.create_view();

    // Generate data
    std::vector<T1> input1 = test_utils::get_random_data<T1>(size, 1, 100);
    std::vector<T2> input2 = test_utils::get_random_data<T2>(size, 1, 100);
    std::vector<T3> input3 = test_utils::get_random_data<T3>(size, 1, 100);

    hc::array<T1> d_input1(hc::extent<1>(size), input1.begin(), acc_view);
    hc::array<T2> d_input2(hc::extent<1>(size), input2.begin(), acc_view);
    hc::array<T3> d_input3(hc::extent<1>(size), input3.begin(), acc_view);
    hc::array<U> d_output(size, acc_view);
    acc_view.wait();

    auto transform_op =
        [](const rocprim::tuple<T1, T2, T3>& t) [[hc]] [[cpu]] -> int
        {
            return rocprim::get<0>(t) + rocprim::get<1>(t) + rocprim::get<2>(t);
        };

    // Calculate expected results on host
    std::vector<U> expected(input1.size());
    std::transform(
        rocprim::make_zip_iterator(
            rocprim::make_tuple(input1.begin(), input2.begin(), input3.begin())
        ),
        rocprim::make_zip_iterator(
            rocprim::make_tuple(input1.end(), input2.end(), input3.end())
        ),
        expected.begin(),
        transform_op
    );

    // Run
    rocprim::transform(
        rocprim::make_zip_iterator(
            rocprim::make_tuple(
                d_input1.accelerator_pointer(),
                d_input2.accelerator_pointer(),
                d_input3.accelerator_pointer()
            )
        ),
        d_output.accelerator_pointer(),
        input1.size(),
        transform_op,
        acc_view,
        debug_synchronous
    );
    acc_view.wait();

    // Check if output values are as expected
    std::vector<U> output = d_output;
    for(size_t i = 0; i < output.size(); i++)
    {
        SCOPED_TRACE(testing::Message() << "where index = " << i);
        auto diff = std::max<U>(std::abs(0.01f * expected[i]), U(0.01f));
        if(std::is_integral<U>::value) diff = 0;
        ASSERT_NEAR(output[i], expected[i], diff);
    }
}

TEST(RocprimZipIteratorTests, TransformReduce)
{
    using T1 = int;
    using T2 = unsigned int;
    using T3 = unsigned char;
    using U1 = T1;
    using U2 = T2;
    const bool debug_synchronous = false;
    const size_t size = 1024 * 16;

    hc::accelerator acc;
    hc::accelerator_view acc_view = acc.create_view();

    // Generate data
    std::vector<T1> input1 = test_utils::get_random_data<T1>(size, 1, 100);
    std::vector<T2> input2 = test_utils::get_random_data<T2>(size, 1, 50);
    std::vector<T3> input3 = test_utils::get_random_data<T3>(size, 1, 10);

    hc::array<T1> d_input1(hc::extent<1>(size), input1.begin(), acc_view);
    hc::array<T2> d_input2(hc::extent<1>(size), input2.begin(), acc_view);
    hc::array<T3> d_input3(hc::extent<1>(size), input3.begin(), acc_view);
    hc::array<U1> d_output1(1, acc_view);
    hc::array<U2> d_output2(1, acc_view);
    acc_view.wait();

    // transform function
    auto transform_op =
        [](const rocprim::tuple<T1, T2, T3>& t) [[hc]] [[cpu]]
            -> rocprim::tuple<T1, T2>
        {
            return rocprim::make_tuple(
                rocprim::get<0>(t), T2(rocprim::get<1>(t) + rocprim::get<2>(t))
            );
        };

    // reduce function
    auto reduce_op =
        [](const rocprim::tuple<T1, T2>& t1,
           const rocprim::tuple<T1, T2>& t2) [[hc]] [[cpu]]
            -> rocprim::tuple<T1, T2>
        {
            return rocprim::make_tuple(
                rocprim::get<0>(t1) + rocprim::get<0>(t2),
                rocprim::get<1>(t1) + rocprim::get<1>(t2)
            );
        };

    // Calculate expected results on host
    U1 expected1 = std::accumulate(input1.begin(), input1.end(), T1(0));
    U2 expected2 = std::accumulate(input2.begin(), input2.end(), T2(0))
        + std::accumulate(input3.begin(), input3.end(), T2(0));

    // temp storage
    size_t temp_storage_size_bytes;
    // Get size of d_temp_storage
    rocprim::reduce(
        nullptr,
        temp_storage_size_bytes,
        rocprim::make_transform_iterator(
            rocprim::make_zip_iterator(
                rocprim::make_tuple(
                    d_input1.accelerator_pointer(),
                    d_input2.accelerator_pointer(),
                    d_input3.accelerator_pointer()
                )
            ),
            transform_op
        ),
        rocprim::make_zip_iterator(
            rocprim::make_tuple(
                d_output1.accelerator_pointer(),
                d_output2.accelerator_pointer()
            )
        ),
        input1.size(),
        reduce_op,
        acc_view,
        debug_synchronous
    );
    acc_view.wait();

    // temp_storage_size_bytes must be >0
    ASSERT_GT(temp_storage_size_bytes, 0);

    // allocate temporary storage
    hc::array<char> d_temp_storage(temp_storage_size_bytes, acc_view);
    acc_view.wait();

    // Run
    rocprim::reduce(
        d_temp_storage.accelerator_pointer(),
        temp_storage_size_bytes,
        rocprim::make_transform_iterator(
            rocprim::make_zip_iterator(
                rocprim::make_tuple(
                    d_input1.accelerator_pointer(),
                    d_input2.accelerator_pointer(),
                    d_input3.accelerator_pointer()
                )
            ),
            transform_op
        ),
        rocprim::make_zip_iterator(
            rocprim::make_tuple(
                d_output1.accelerator_pointer(),
                d_output2.accelerator_pointer()
            )
        ),
        input1.size(),
        reduce_op,
        acc_view,
        debug_synchronous
    );
    acc_view.wait();

    // Check if output values are as expected
    std::vector<U1> output1 = d_output1;
    auto diff1 = std::max<U1>(std::abs(0.01f * expected1), U1(0.01f));
    if(std::is_integral<U1>::value) diff1 = 0;
    ASSERT_NEAR(output1[0], expected1, diff1);

    std::vector<U2> output2 = d_output2;
    auto diff2 = std::max<U2>(std::abs(0.01f * expected2), U2(0.01f));
    if(std::is_integral<U2>::value) diff2 = 0;
    ASSERT_NEAR(output2[0], expected2, diff2);
}

TEST(RocprimZipIteratorTests, ScanByKey)
{
    using T1 = int;
    using T2 = unsigned int;
    using U1 = T1;
    using U2 = T2;
    const bool debug_synchronous = false;
    const size_t size = 1024 * 16;

    hc::accelerator acc;
    hc::accelerator_view acc_view = acc.create_view();

    // Generate data
    std::vector<T1> input = test_utils::get_random_data<T1>(size, 1, 100);
    std::vector<T2> keys = test_utils::get_random_data<T2>(size, 1, 16);
    std::sort(keys.begin(), keys.end());

    hc::array<T1> d_input(hc::extent<1>(size), input.begin(), acc_view);
    hc::array<T2> d_keys(hc::extent<1>(size), keys.begin(), acc_view);
    hc::array<U1> d_output(hc::extent<1>(size), acc_view);
    hc::array<U2> d_output_keys(hc::extent<1>(size), acc_view);
    acc_view.wait();

    // scan function
    auto scan_op =
        [](const rocprim::tuple<T1, T2>& t1,
           const rocprim::tuple<T1, T2>& t2) [[hc]] [[cpu]]
            -> rocprim::tuple<T1, T2>
        {
            if(rocprim::get<1>(t1) == rocprim::get<1>(t2))
            {
                return rocprim::make_tuple(
                    rocprim::get<0>(t1) + rocprim::get<0>(t2),
                    rocprim::get<1>(t2)
                );
            }
            return t2;
        };

    // Calculate expected results on host
    std::vector<U1> expected(input.size());
    std::vector<U2> expected_keys(keys.size());
    std::partial_sum(
        rocprim::make_zip_iterator(
            rocprim::make_tuple(input.begin(), keys.begin())
        ),
        rocprim::make_zip_iterator(
            rocprim::make_tuple(input.end(), keys.end())
        ),
        rocprim::make_zip_iterator(
            rocprim::make_tuple(expected.begin(), expected_keys.begin())
        ),
        scan_op
    );

    // temp storage
    size_t temp_storage_size_bytes;
    // Get size of d_temp_storage
    rocprim::inclusive_scan(
        nullptr,
        temp_storage_size_bytes,
        rocprim::make_zip_iterator(
            rocprim::make_tuple(
                d_input.accelerator_pointer(),
                d_keys.accelerator_pointer()
            )
        ),
        rocprim::make_zip_iterator(
            rocprim::make_tuple(
                d_output.accelerator_pointer(),
                d_output_keys.accelerator_pointer()
            )
        ),
        input.size(),
        scan_op,
        acc_view,
        debug_synchronous
    );
    acc_view.wait();

    // temp_storage_size_bytes must be >0
    ASSERT_GT(temp_storage_size_bytes, 0);

    // allocate temporary storage
    hc::array<char> d_temp_storage(temp_storage_size_bytes, acc_view);
    acc_view.wait();

    // Run
    rocprim::inclusive_scan(
        d_temp_storage.accelerator_pointer(),
        temp_storage_size_bytes,
        rocprim::make_zip_iterator(
            rocprim::make_tuple(
                d_input.accelerator_pointer(),
                d_keys.accelerator_pointer()
            )
        ),
        rocprim::make_zip_iterator(
            rocprim::make_tuple(
                d_output.accelerator_pointer(),
                d_output_keys.accelerator_pointer()
            )
        ),
        input.size(),
        scan_op,
        acc_view,
        debug_synchronous
    );
    acc_view.wait();

    // Check if output values are as expected
    std::vector<U1> output = d_output;
    std::vector<U2> output_keys = d_output_keys;
    for(size_t i = 0; i < output.size(); i++)
    {
        SCOPED_TRACE(testing::Message() << "where index = " << i);

        auto diff1 = std::max<U1>(std::abs(0.01f * expected[i]), U1(0.01f));
        if(std::is_integral<U1>::value) diff1 = 0;
        ASSERT_NEAR(output[i], expected[i], diff1);
        ASSERT_EQ(output_keys[i], expected_keys[i]);
    }
}
<|endoftext|>