<commit_before>// Copyright Hugh Perkins 2014,2015 hughperkins at gmail
//
// This Source Code Form is subject to the terms of the Mozilla Public License,
// v. 2.0. If a copy of the MPL was not distributed with this file, You can
// obtain one at http://mozilla.org/MPL/2.0/.

#include <stdexcept>
#include <random>

#include "Timer.h"
#include "ConvolutionalLayer.h"
#include "LayerMaker.h"
#include "ActivationFunction.h"
#include "StatefulTimer.h"
#include "AccuracyHelper.h"
#include "NeuralNetMould.h"
#include "Layer.h"
#include "InputLayer.h"
#include "FullyConnectedLayer.h"
#include "EpochMaker.h"
#include "LossLayer.h"
#include "IAcceptsLabels.h"
#include "ExceptionMacros.h"
#include "InputLayerMaker.h"

#include "NeuralNet.h"

using namespace std;

//static std::mt19937 random;

#undef VIRTUAL
#define VIRTUAL
#undef STATIC
#define STATIC

NeuralNet::NeuralNet() {
//    cout << "NeuralNet()" << endl;
    cl = OpenCLHelper::createForFirstGpuOtherwiseCpu();
//    InputLayerMaker<T> *maker = new InputLayerMaker<T>( this, numPlanes, boardSize );
//    maker->insert();
}
NeuralNet::NeuralNet( int numPlanes, int boardSize ) {
//    cout << "NeuralNet( " << numPlanes << ", " << boardSize << " )" << endl;
    cl = OpenCLHelper::createForFirstGpuOtherwiseCpu();
//    InputLayerMaker<float> *maker = ( new InputLayerMaker<float>( this ) )
//        ->numPlanes( numPlanes )->boardSize( boardSize );
//    maker->insert();
//    InputLayerMaker<float> *maker = In;
//    maker;
    addLayer( InputLayerMaker<float>::instance()->numPlanes( numPlanes )->boardSize( boardSize ) );
//    print();
}
NeuralNet::~NeuralNet() {
    for( int i = 0; i < layers.size(); i++ ) {
        delete layers[i];
    }
    delete cl;
}
NeuralNet *NeuralNet::clone() {
    NeuralNet *copy = new NeuralNet();
//    Layer *previousLayer = 0;
    for( vector<Layer *>::iterator it = layers.begin(); it != layers.end(); it++ ) {
        LayerMaker2 *maker = (*it)->maker;

        LayerMaker2 *makerCopy = maker->clone();
//        Layer *layerCopy  = makerCopy->createLayer( previousLayer );
        copy->addLayer( makerCopy );
//        previousLayer = layerCopy;

//        LayerMaker const*maker1 = dynamic_cast< LayerMaker const*>( maker );
//        LayerMaker2 const*maker2 = dynamic_cast< LayerMaker2 const*>( maker );
//        if( maker1 != 0 ) {
//            LayerMaker const*makerCopy = maker1->clone( previousLayer );
//            Layer *layerCopy = makerCopy->instance();
//            copy->layers.push_back( layerCopy );
//            previousLayer = layerCopy;
//        } else {
//            throw runtime_error("not implemetned yet, layermaker2 clone");
//        }
    }
    copy->print();
    cout << "outputboardsize: " << copy->getOutputBoardSize() << endl;
    return copy;
}
OpenCLHelper *NeuralNet::getCl() {
    return cl;
}
STATIC NeuralNetMould *NeuralNet::maker() {
    return new NeuralNetMould();
}
//template< typename T > void NeuralNet::insert( InputLayerMaker<T> inputLayerMaker ) {
////    cout << "neuralnet::insert numplanes " << inputLayerMaker._numPlanes << " boardSize " << inputLayerMaker._boardSize << endl;
//    InputLayer<T> *inputLayer = new InputLayer<T>( inputLayerMaker );
//    layers.push_back( inputLayer );
//}
void NeuralNet::addLayer( LayerMaker2 *maker ) {
//    cout << "neuralnet::insert numplanes " << inputLayerMaker._numPlanes << " boardSize " << inputLayerMaker._boardSize << endl;
    maker->setCl( cl );
    Layer *layer = maker->createLayer( getLastLayer() );
    layers.push_back( layer );
}
//template< typename T >InputLayerMaker<T> *NeuralNet::inputMaker() {
//    if( layers.size() != 0 ) {
//        throw runtime_error("Already added an InputLayer to this net");
//    }
//    return new InputLayerMaker<T>( this );
//}
//FullyConnectedMaker *NeuralNet::fullyConnectedMaker() {
//    return new FullyConnectedMaker( this, getLastLayer() );
//}
//ConvolutionalMaker *NeuralNet::convolutionalMaker() {
//    return new ConvolutionalMaker( this, getLastLayer() );
//}
//PoolingMaker *NeuralNet::poolingMaker() {
//    return new PoolingMaker( this, getLastLayer() );
//}
//NormalizationLayerMaker *NeuralNet::normalizationMaker() {
//    return new NormalizationLayerMaker( this, getLastLayer() );
//}
//RandomPatchesMaker *NeuralNet::randomPatchesMaker() {
//    return new RandomPatchesMaker( this, getLastLayer() );
//}
//RandomTranslationsMaker *NeuralNet::randomTranslationsMaker() {
//    return new RandomTranslationsMaker( this, getLastLayer() );
//}
//SquareLossMaker *NeuralNet::squareLossMaker() {
//    return new SquareLossMaker( this, getLastLayer() );
//}
//CrossEntropyLossMaker *NeuralNet::crossEntropyLossMaker() {
//    return new CrossEntropyLossMaker( this, getLastLayer() );
//}
//SoftMaxMaker *NeuralNet::softMaxLossMaker() {
//    return new SoftMaxMaker( this, getLastLayer() );
//}
void NeuralNet::initWeights( int layerIndex, float *weights, float *biasWeights ) {
    initWeights( layerIndex, weights );
    initBiasWeights( layerIndex, biasWeights );
}
void NeuralNet::initWeights( int layerIndex, float *weights ) {
    layers[layerIndex]->initWeights( weights );
}
void NeuralNet::initBiasWeights( int layerIndex, float *weights ) {
    layers[layerIndex]->initBiasWeights( weights );
}
void NeuralNet::printWeightsAsCode() {
    for( int layer = 1; layer < layers.size(); layer++ ) {
        layers[layer]->printWeightsAsCode();
    }
}
void NeuralNet::printBiasWeightsAsCode() {
    for( int layer = 1; layer < layers.size(); layer++ ) {
        layers[layer]->printBiasWeightsAsCode();
    }
}
float NeuralNet::calcLoss(float const *expectedValues ) {
    return dynamic_cast<LossLayer*>(layers[layers.size()-1])->calcLoss( expectedValues );
}
float NeuralNet::calcLossFromLabels(int const *labels ) {
    return dynamic_cast<IAcceptsLabels*>(layers[layers.size()-1])->calcLossFromLabels( labels );
}
EpochMaker *NeuralNet::epochMaker() {
     return new EpochMaker(this);
}
VIRTUAL LossLayerMaker *NeuralNet::cloneLossLayerMaker() const {
    LossLayer const *lossLayer = dynamic_cast< LossLayer const*>( getLastLayer() );
    if( lossLayer == 0 ) {
        throw runtime_error("error: last layer must be a losslayer");
    }
    return dynamic_cast< LossLayerMaker *>( lossLayer->maker->clone() );
//    throw runtime_error("need to implement neuralnet::clonelosslayermaker :-)" );
//    LossLayer const*lossLayer = dynamic_cast< LossLayer const*>( getLastLayer() );
//    return dynamic_cast< LossLayerMaker *>( lossLayer->maker->clone( clonePreviousLayer ) ) ;
}
template< typename T > InputLayer<T> *NeuralNet::getFirstLayer() {
    return dynamic_cast<InputLayer<T> *>( layers[0] );
}
Layer *NeuralNet::getLastLayer() {
    if( layers.size() == 0 ) {
        return 0;
    }
    return layers[layers.size() - 1];
}
Layer const*NeuralNet::getLastLayer() const {
    if( layers.size() == 0 ) {
        return 0;
    }
    return layers[layers.size() - 1];
}
VIRTUAL int NeuralNet::getOutputPlanes() const {
    return getLastLayer()->getOutputPlanes();
}
VIRTUAL int NeuralNet::getOutputBoardSize() const {
    return getLastLayer()->getOutputBoardSize();
}
//Layer *NeuralNet::addLayer( LayerMaker *maker ) {
////    Layer *previousLayer = 0;
////    if( layers.size() > 0 ) {
////        previousLayer = layers[ layers.size() - 1 ];
////    }
////    maker->setPreviousLayer( previousLayer );
//    Layer *layer = maker->instance();
//    layers.push_back( layer );
//    return layer;
//}
void NeuralNet::setBatchSize( int batchSize ) {
    for( std::vector<Layer*>::iterator it = layers.begin(); it != layers.end(); it++ ) {
        (*it)->setBatchSize( batchSize );
    }
}
void NeuralNet::setTraining( bool training ) {
    for( std::vector<Layer*>::iterator it = layers.begin(); it != layers.end(); it++ ) {
        (*it)->setTraining( training );
    }
}
int NeuralNet::calcNumRight( int const *labels ) {
    IAcceptsLabels *acceptsLabels = dynamic_cast<IAcceptsLabels*>(getLastLayer());
    if( acceptsLabels == 0 ) {
        THROW("You need to add a IAcceptsLabels as the last layer, in order to use calcNumRight");
    }
    return acceptsLabels->calcNumRight( labels );
}
void NeuralNet::propagate( float const*images) {
    // forward...
    dynamic_cast<InputLayer<float> *>(layers[0])->in( images );
    for( int layerId = 0; layerId < layers.size(); layerId++ ) {
        StatefulTimer::setPrefix("layer" + toString(layerId) + " " );
        layers[layerId]->propagate();
        StatefulTimer::setPrefix("" );
    }
}
void NeuralNet::propagate( unsigned char const*images) {
    // forward...
    dynamic_cast<InputLayer<unsigned char> *>(layers[0])->in( images );
    for( int layerId = 0; layerId < layers.size(); layerId++ ) {
        StatefulTimer::setPrefix("layer" + toString(layerId) + " " );
        layers[layerId]->propagate();
        StatefulTimer::setPrefix("" );
    }
}
void NeuralNet::backPropFromLabels( float learningRate, int const *labels) {
    IAcceptsLabels *acceptsLabels = dynamic_cast<IAcceptsLabels*>(getLastLayer());
    if( acceptsLabels == 0 ) {
        throw std::runtime_error("Must add a child of IAcceptsLabels as last layer, to use backPropFromLabels");
    }
    acceptsLabels->calcErrorsFromLabels( labels );
    for( int layerIdx = (int)layers.size() - 2; layerIdx >= 1; layerIdx-- ) { // no point in propagating to input layer :-P
        StatefulTimer::setPrefix("layer" + toString(layerIdx) + " " );
        Layer *layer = layers[layerIdx];
        if( layer->needsBackProp() ) {
            layer->backProp( learningRate );
        }
        StatefulTimer::setPrefix("" );
    }
}
void NeuralNet::backProp( float learningRate, float const *expectedResults) {
    LossLayer *lossLayer = dynamic_cast<LossLayer*>(getLastLayer());
    if( lossLayer == 0 ) {
        throw std::runtime_error("Must add a LossLayer as last layer of net");
    }
    lossLayer->calcErrors( expectedResults );
    for( int layerIdx = (int)layers.size() - 2; layerIdx >= 1; layerIdx-- ) { // no point in propagating to input layer :-P
        StatefulTimer::setPrefix("layer" + toString(layerIdx) + " " );
        layers[layerIdx]->backProp( learningRate );
        StatefulTimer::setPrefix("" );
    }
}
int NeuralNet::getNumLayers() {
    return (int)layers.size();
}
float const *NeuralNet::getResults( int layer ) const {
    return layers[layer]->getResults();
}
int NeuralNet::getInputCubeSize() const {
    return layers[ 0 ]->getOutputCubeSize();
}
int NeuralNet::getOutputCubeSize() const {
    return layers[ layers.size() - 1 ]->getOutputCubeSize();
}
float const *NeuralNet::getResults() const {
    return getResults( (int)layers.size() - 1 );
}
VIRTUAL int NeuralNet::getResultsSize() const {
    return getLastLayer()->getResultsSize();
}
void NeuralNet::print() {
    cout << this->asString();
//    int i = 0;
//    for( std::vector< Layer* >::iterator it = layers.begin(); it != layers.end(); it++ ) {
//        std::cout << "layer " << i << ":" << (*it)->asString() << endl;
//        i++;
//    }
}
void NeuralNet::printWeights() {
    int i = 0;
    for( std::vector< Layer* >::iterator it = layers.begin(); it != layers.end(); it++ ) {
        std::cout << "layer " << i << ":" << std::endl;
        (*it)->printWeights();
        i++;
    }
}
void NeuralNet::printOutput() {
    int i = 0;
    for( std::vector< Layer* >::iterator it = layers.begin(); it != layers.end(); it++ ) {
        std::cout << "layer " << i << ":" << std::endl;
        (*it)->printOutput();
        i++;
    }
}
std::string NeuralNet::asString() {
    std::string result = "";
    int i = 0;
    for( std::vector< Layer* >::iterator it = layers.begin(); it != layers.end(); it++ ) {
        result += "layer " + toString( i ) + ":" + (*it)->asString() + "\n";
        i++;
    }
    return result;
}

//template ClConvolve_EXPORT InputLayerMaker<unsigned char> *NeuralNet::inputMaker<unsigned char>();
//template ClConvolve_EXPORT InputLayerMaker<float> *NeuralNet::inputMaker<float>();

//template ClConvolve_EXPORT void NeuralNet::append( InputLayerMaker<unsigned char> inputLayerMaker );
//template ClConvolve_EXPORT void NeuralNet::append( InputLayerMaker<float> inputLayerMaker );

<commit_msg>maybe remove some windows warnings<commit_after>// Copyright Hugh Perkins 2014,2015 hughperkins at gmail
//
// This Source Code Form is subject to the terms of the Mozilla Public License,
// v. 2.0. If a copy of the MPL was not distributed with this file, You can
// obtain one at http://mozilla.org/MPL/2.0/.

#include <stdexcept>
#include <random>

#include "Timer.h"
#include "ConvolutionalLayer.h"
#include "LayerMaker.h"
#include "ActivationFunction.h"
#include "StatefulTimer.h"
#include "AccuracyHelper.h"
#include "NeuralNetMould.h"
#include "Layer.h"
#include "InputLayer.h"
#include "FullyConnectedLayer.h"
#include "EpochMaker.h"
#include "LossLayer.h"
#include "IAcceptsLabels.h"
#include "ExceptionMacros.h"
#include "InputLayerMaker.h"

#include "NeuralNet.h"

using namespace std;

//static std::mt19937 random;

#undef VIRTUAL
#define VIRTUAL
#undef STATIC
#define STATIC

NeuralNet::NeuralNet() {
//    cout << "NeuralNet()" << endl;
    cl = OpenCLHelper::createForFirstGpuOtherwiseCpu();
//    InputLayerMaker<T> *maker = new InputLayerMaker<T>( this, numPlanes, boardSize );
//    maker->insert();
}
NeuralNet::NeuralNet( int numPlanes, int boardSize ) {
//    cout << "NeuralNet( " << numPlanes << ", " << boardSize << " )" << endl;
    cl = OpenCLHelper::createForFirstGpuOtherwiseCpu();
//    InputLayerMaker<float> *maker = ( new InputLayerMaker<float>( this ) )
//        ->numPlanes( numPlanes )->boardSize( boardSize );
//    maker->insert();
//    InputLayerMaker<float> *maker = In;
//    maker;
    addLayer( InputLayerMaker<float>::instance()->numPlanes( numPlanes )->boardSize( boardSize ) );
//    print();
}
NeuralNet::~NeuralNet() {
    for( int i = 0; i < (int)layers.size(); i++ ) {
        delete layers[i];
    }
    delete cl;
}
NeuralNet *NeuralNet::clone() {
    NeuralNet *copy = new NeuralNet();
//    Layer *previousLayer = 0;
    for( vector<Layer *>::iterator it = layers.begin(); it != layers.end(); it++ ) {
        LayerMaker2 *maker = (*it)->maker;

        LayerMaker2 *makerCopy = maker->clone();
//        Layer *layerCopy  = makerCopy->createLayer( previousLayer );
        copy->addLayer( makerCopy );
//        previousLayer = layerCopy;

//        LayerMaker const*maker1 = dynamic_cast< LayerMaker const*>( maker );
//        LayerMaker2 const*maker2 = dynamic_cast< LayerMaker2 const*>( maker );
//        if( maker1 != 0 ) {
//            LayerMaker const*makerCopy = maker1->clone( previousLayer );
//            Layer *layerCopy = makerCopy->instance();
//            copy->layers.push_back( layerCopy );
//            previousLayer = layerCopy;
//        } else {
//            throw runtime_error("not implemetned yet, layermaker2 clone");
//        }
    }
    copy->print();
    cout << "outputboardsize: " << copy->getOutputBoardSize() << endl;
    return copy;
}
OpenCLHelper *NeuralNet::getCl() {
    return cl;
}
STATIC NeuralNetMould *NeuralNet::maker() {
    return new NeuralNetMould();
}
//template< typename T > void NeuralNet::insert( InputLayerMaker<T> inputLayerMaker ) {
////    cout << "neuralnet::insert numplanes " << inputLayerMaker._numPlanes << " boardSize " << inputLayerMaker._boardSize << endl;
//    InputLayer<T> *inputLayer = new InputLayer<T>( inputLayerMaker );
//    layers.push_back( inputLayer );
//}
void NeuralNet::addLayer( LayerMaker2 *maker ) {
//    cout << "neuralnet::insert numplanes " << inputLayerMaker._numPlanes << " boardSize " << inputLayerMaker._boardSize << endl;
    maker->setCl( cl );
    Layer *layer = maker->createLayer( getLastLayer() );
    layers.push_back( layer );
}
//template< typename T >InputLayerMaker<T> *NeuralNet::inputMaker() {
//    if( layers.size() != 0 ) {
//        throw runtime_error("Already added an InputLayer to this net");
//    }
//    return new InputLayerMaker<T>( this );
//}
//FullyConnectedMaker *NeuralNet::fullyConnectedMaker() {
//    return new FullyConnectedMaker( this, getLastLayer() );
//}
//ConvolutionalMaker *NeuralNet::convolutionalMaker() {
//    return new ConvolutionalMaker( this, getLastLayer() );
//}
//PoolingMaker *NeuralNet::poolingMaker() {
//    return new PoolingMaker( this, getLastLayer() );
//}
//NormalizationLayerMaker *NeuralNet::normalizationMaker() {
//    return new NormalizationLayerMaker( this, getLastLayer() );
//}
//RandomPatchesMaker *NeuralNet::randomPatchesMaker() {
//    return new RandomPatchesMaker( this, getLastLayer() );
//}
//RandomTranslationsMaker *NeuralNet::randomTranslationsMaker() {
//    return new RandomTranslationsMaker( this, getLastLayer() );
//}
//SquareLossMaker *NeuralNet::squareLossMaker() {
//    return new SquareLossMaker( this, getLastLayer() );
//}
//CrossEntropyLossMaker *NeuralNet::crossEntropyLossMaker() {
//    return new CrossEntropyLossMaker( this, getLastLayer() );
//}
//SoftMaxMaker *NeuralNet::softMaxLossMaker() {
//    return new SoftMaxMaker( this, getLastLayer() );
//}
void NeuralNet::initWeights( int layerIndex, float *weights, float *biasWeights ) {
    initWeights( layerIndex, weights );
    initBiasWeights( layerIndex, biasWeights );
}
void NeuralNet::initWeights( int layerIndex, float *weights ) {
    layers[layerIndex]->initWeights( weights );
}
void NeuralNet::initBiasWeights( int layerIndex, float *weights ) {
    layers[layerIndex]->initBiasWeights( weights );
}
void NeuralNet::printWeightsAsCode() {
    for( int layer = 1; layer < (int)layers.size(); layer++ ) {
        layers[layer]->printWeightsAsCode();
    }
}
void NeuralNet::printBiasWeightsAsCode() {
    for( int layer = 1; layer < (int)layers.size(); layer++ ) {
        layers[layer]->printBiasWeightsAsCode();
    }
}
float NeuralNet::calcLoss(float const *expectedValues ) {
    return dynamic_cast<LossLayer*>(layers[layers.size()-1])->calcLoss( expectedValues );
}
float NeuralNet::calcLossFromLabels(int const *labels ) {
    return dynamic_cast<IAcceptsLabels*>(layers[layers.size()-1])->calcLossFromLabels( labels );
}
EpochMaker *NeuralNet::epochMaker() {
     return new EpochMaker(this);
}
VIRTUAL LossLayerMaker *NeuralNet::cloneLossLayerMaker() const {
    LossLayer const *lossLayer = dynamic_cast< LossLayer const*>( getLastLayer() );
    if( lossLayer == 0 ) {
        throw runtime_error("error: last layer must be a losslayer");
    }
    return dynamic_cast< LossLayerMaker *>( lossLayer->maker->clone() );
//    throw runtime_error("need to implement neuralnet::clonelosslayermaker :-)" );
//    LossLayer const*lossLayer = dynamic_cast< LossLayer const*>( getLastLayer() );
//    return dynamic_cast< LossLayerMaker *>( lossLayer->maker->clone( clonePreviousLayer ) ) ;
}
template< typename T > InputLayer<T> *NeuralNet::getFirstLayer() {
    return dynamic_cast<InputLayer<T> *>( layers[0] );
}
Layer *NeuralNet::getLastLayer() {
    if( layers.size() == 0 ) {
        return 0;
    }
    return layers[layers.size() - 1];
}
Layer const*NeuralNet::getLastLayer() const {
    if( layers.size() == 0 ) {
        return 0;
    }
    return layers[layers.size() - 1];
}
VIRTUAL int NeuralNet::getOutputPlanes() const {
    return getLastLayer()->getOutputPlanes();
}
VIRTUAL int NeuralNet::getOutputBoardSize() const {
    return getLastLayer()->getOutputBoardSize();
}
//Layer *NeuralNet::addLayer( LayerMaker *maker ) {
////    Layer *previousLayer = 0;
////    if( layers.size() > 0 ) {
////        previousLayer = layers[ layers.size() - 1 ];
////    }
////    maker->setPreviousLayer( previousLayer );
//    Layer *layer = maker->instance();
//    layers.push_back( layer );
//    return layer;
//}
void NeuralNet::setBatchSize( int batchSize ) {
    for( std::vector<Layer*>::iterator it = layers.begin(); it != layers.end(); it++ ) {
        (*it)->setBatchSize( batchSize );
    }
}
void NeuralNet::setTraining( bool training ) {
    for( std::vector<Layer*>::iterator it = layers.begin(); it != layers.end(); it++ ) {
        (*it)->setTraining( training );
    }
}
int NeuralNet::calcNumRight( int const *labels ) {
    IAcceptsLabels *acceptsLabels = dynamic_cast<IAcceptsLabels*>(getLastLayer());
    if( acceptsLabels == 0 ) {
        THROW("You need to add a IAcceptsLabels as the last layer, in order to use calcNumRight");
    }
    return acceptsLabels->calcNumRight( labels );
}
void NeuralNet::propagate( float const*images) {
    // forward...
    dynamic_cast<InputLayer<float> *>(layers[0])->in( images );
    for( int layerId = 0; layerId < (int)layers.size(); layerId++ ) {
        StatefulTimer::setPrefix("layer" + toString(layerId) + " " );
        layers[layerId]->propagate();
        StatefulTimer::setPrefix("" );
    }
}
void NeuralNet::propagate( unsigned char const*images) {
    // forward...
    dynamic_cast<InputLayer<unsigned char> *>(layers[0])->in( images );
    for( int layerId = 0; layerId < (int)layers.size(); layerId++ ) {
        StatefulTimer::setPrefix("layer" + toString(layerId) + " " );
        layers[layerId]->propagate();
        StatefulTimer::setPrefix("" );
    }
}
void NeuralNet::backPropFromLabels( float learningRate, int const *labels) {
    IAcceptsLabels *acceptsLabels = dynamic_cast<IAcceptsLabels*>(getLastLayer());
    if( acceptsLabels == 0 ) {
        throw std::runtime_error("Must add a child of IAcceptsLabels as last layer, to use backPropFromLabels");
    }
    acceptsLabels->calcErrorsFromLabels( labels );
    for( int layerIdx = (int)layers.size() - 2; layerIdx >= 1; layerIdx-- ) { // no point in propagating to input layer :-P
        StatefulTimer::setPrefix("layer" + toString(layerIdx) + " " );
        Layer *layer = layers[layerIdx];
        if( layer->needsBackProp() ) {
            layer->backProp( learningRate );
        }
        StatefulTimer::setPrefix("" );
    }
}
void NeuralNet::backProp( float learningRate, float const *expectedResults) {
    LossLayer *lossLayer = dynamic_cast<LossLayer*>(getLastLayer());
    if( lossLayer == 0 ) {
        throw std::runtime_error("Must add a LossLayer as last layer of net");
    }
    lossLayer->calcErrors( expectedResults );
    for( int layerIdx = (int)layers.size() - 2; layerIdx >= 1; layerIdx-- ) { // no point in propagating to input layer :-P
        StatefulTimer::setPrefix("layer" + toString(layerIdx) + " " );
        layers[layerIdx]->backProp( learningRate );
        StatefulTimer::setPrefix("" );
    }
}
int NeuralNet::getNumLayers() {
    return (int)layers.size();
}
float const *NeuralNet::getResults( int layer ) const {
    return layers[layer]->getResults();
}
int NeuralNet::getInputCubeSize() const {
    return layers[ 0 ]->getOutputCubeSize();
}
int NeuralNet::getOutputCubeSize() const {
    return layers[ layers.size() - 1 ]->getOutputCubeSize();
}
float const *NeuralNet::getResults() const {
    return getResults( (int)layers.size() - 1 );
}
VIRTUAL int NeuralNet::getResultsSize() const {
    return getLastLayer()->getResultsSize();
}
void NeuralNet::print() {
    cout << this->asString();
//    int i = 0;
//    for( std::vector< Layer* >::iterator it = layers.begin(); it != layers.end(); it++ ) {
//        std::cout << "layer " << i << ":" << (*it)->asString() << endl;
//        i++;
//    }
}
void NeuralNet::printWeights() {
    int i = 0;
    for( std::vector< Layer* >::iterator it = layers.begin(); it != layers.end(); it++ ) {
        std::cout << "layer " << i << ":" << std::endl;
        (*it)->printWeights();
        i++;
    }
}
void NeuralNet::printOutput() {
    int i = 0;
    for( std::vector< Layer* >::iterator it = layers.begin(); it != layers.end(); it++ ) {
        std::cout << "layer " << i << ":" << std::endl;
        (*it)->printOutput();
        i++;
    }
}
std::string NeuralNet::asString() {
    std::string result = "";
    int i = 0;
    for( std::vector< Layer* >::iterator it = layers.begin(); it != layers.end(); it++ ) {
        result += "layer " + toString( i ) + ":" + (*it)->asString() + "\n";
        i++;
    }
    return result;
}

//template ClConvolve_EXPORT InputLayerMaker<unsigned char> *NeuralNet::inputMaker<unsigned char>();
//template ClConvolve_EXPORT InputLayerMaker<float> *NeuralNet::inputMaker<float>();

//template ClConvolve_EXPORT void NeuralNet::append( InputLayerMaker<unsigned char> inputLayerMaker );
//template ClConvolve_EXPORT void NeuralNet::append( InputLayerMaker<float> inputLayerMaker );

<|endoftext|>


<commit_before>#include "gl/ShaderHelper.h"

#include "Log.h"

using namespace std;

namespace chr
{
  namespace gl
  {
    GLuint ShaderHelper::createShader(GLenum type, const char *source)
    {
     GLuint shader = 0;
     shader = glCreateShader(type);

     if (shader != 0)
     {
       glShaderSource(shader, 1, reinterpret_cast<const GLchar**>(&source), NULL);
       glCompileShader(shader);

       GLint success;
       glGetShaderiv(shader, GL_COMPILE_STATUS, &success);

       if (success != GL_TRUE)
       {
         GLint maxLength = 0;
         glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &maxLength);
         string buf(maxLength, 0);
         glGetShaderInfoLog(shader, maxLength, &maxLength, &buf[0]);

         LOGE << "ERROR: FAILED TO COMPILE " << ((type == GL_FRAGMENT_SHADER) ? "FRAGMENT" : "VERTEX") << " SHADER" << endl;
         LOGE << buf << endl;

         glDeleteShader(shader);
         shader = 0;
       }
     }

     return shader;
    }

    tuple<GLuint, GLuint, GLuint> ShaderHelper::loadProgram(experimental::string_view vertexShaderSource, experimental::string_view fragmentShaderSource)
    {
      GLuint programId = 0;
      GLuint vertexShaderId = 0;
      GLuint fragmentShaderId = 0;

      vertexShaderId = createShader(GL_VERTEX_SHADER, vertexShaderSource.data());

      if (vertexShaderId != 0)
      {
        fragmentShaderId = createShader(GL_FRAGMENT_SHADER, fragmentShaderSource.data());

        if (fragmentShaderId != 0)
        {
          programId = glCreateProgram();

          if (programId != 0)
          {
            glAttachShader(programId, vertexShaderId);
            glAttachShader(programId, fragmentShaderId);
            glLinkProgram(programId);

            GLint success;
            glGetProgramiv(programId, GL_LINK_STATUS, &success);

            if (success != GL_TRUE)
            {
              GLint maxLength = 0;
              glGetShaderiv(programId, GL_INFO_LOG_LENGTH, &maxLength);
              string buf(maxLength, 0);
              glGetShaderInfoLog(programId, maxLength, &maxLength, &buf[0]);

              LOGE << "ERROR: FAILED TO LINK SHADER PROGRAM" << endl;
              LOGE << buf << endl;

              glDeleteShader(vertexShaderId);
              vertexShaderId = 0;

              glDeleteShader(fragmentShaderId);
              fragmentShaderId = 0;

              glDeleteProgram(programId);
              programId = 0;
            }
          }
        }
        else
        {
          LOGE << "ERROR: UNABLE TO LOAD FRAGMENT SHADER" << endl;

          glDeleteShader(vertexShaderId);
          vertexShaderId = 0;
        }
      }
      else
      {
        LOGE << "ERROR: UNABLE TO LOAD VERTEX SHADER" << endl;
      }

      return make_tuple(programId, vertexShaderId, fragmentShaderId);
    }

    void ShaderHelper::unloadProgram(GLuint programId, GLuint vertexShaderId, GLuint fragmentShaderId)
    {
      glDetachShader(programId, fragmentShaderId);
      glDeleteShader(fragmentShaderId);

      glDetachShader(programId, vertexShaderId);
      glDeleteShader(vertexShaderId);

      glDeleteProgram(programId);
    }

    // ---

    template <>
    void ShaderHelper::bindAttributes<Vertex<XYZ>>(const ShaderProgram &shader)
    {
      glEnableVertexAttribArray(shader.element->positionLocation);
      glVertexAttribPointer(shader.element->positionLocation, 3, GL_FLOAT, GL_FALSE, 0, 0);
    }

    template <>
    void ShaderHelper::bindAttributes<Vertex<XYZ.UV>>(const ShaderProgram &shader)
    {
      glEnableVertexAttribArray(shader.element->positionLocation);
      glVertexAttribPointer(shader.element->positionLocation, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex<XYZ.UV>), 0);

      glEnableVertexAttribArray(shader.element->coordLocation);
      glVertexAttribPointer(shader.element->coordLocation, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex<XYZ.UV>), (GLvoid *) sizeof(Vertex<>));
    }

    template <>
    void ShaderHelper::bindAttributes<Vertex<XYZ.RGBA>>(const ShaderProgram &shader)
    {
      glEnableVertexAttribArray(shader.element->positionLocation);
      glVertexAttribPointer(shader.element->positionLocation, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex<XYZ.RGBA>), 0);

      glEnableVertexAttribArray(shader.element->colorLocation);
      glVertexAttribPointer(shader.element->colorLocation, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex<XYZ.RGBA>), (GLvoid *) sizeof(Vertex<XYZ>));
    }

    template <>
    void ShaderHelper::bindAttributes<Vertex<XYZ.UV.RGBA>>(const ShaderProgram &shader)
    {
      glEnableVertexAttribArray(shader.element->positionLocation);
      glVertexAttribPointer(shader.element->positionLocation, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex<XYZ.UV.RGBA>), 0);

      glEnableVertexAttribArray(shader.element->coordLocation);
      glVertexAttribPointer(shader.element->coordLocation, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex<XYZ.UV.RGBA>), (GLvoid *) sizeof(Vertex<>));

      glEnableVertexAttribArray(shader.element->colorLocation);
      glVertexAttribPointer(shader.element->colorLocation, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex<XYZ.UV.RGBA>), (GLvoid *) sizeof(Vertex<XYZ.UV>));
    }

    template <>
    void ShaderHelper::bindAttributes<Vertex<N>>(const ShaderProgram &shader)
    {
      glEnableVertexAttribArray(shader.element->normalLocation);
      glVertexAttribPointer(shader.element->normalLocation, 3, GL_FLOAT, GL_FALSE, 0, 0);
    }

    template <>
    void ShaderHelper::bindAttributes<Vertex<UV>>(const ShaderProgram &shader)
    {
      glEnableVertexAttribArray(shader.element->coordLocation);
      glVertexAttribPointer(shader.element->coordLocation, 2, GL_FLOAT, GL_FALSE, 0, 0);
    }

    template <>
    void ShaderHelper::bindAttributes<Vertex<RGBA>>(const ShaderProgram &shader)
    {
      glEnableVertexAttribArray(shader.element->colorLocation);
      glVertexAttribPointer(shader.element->colorLocation, 4, GL_FLOAT, GL_FALSE, 0, 0);
    }

    // ---

    template <>
    void ShaderHelper::unbindAttributes<Vertex<XYZ>>(const ShaderProgram &shader)
    {
      glDisableVertexAttribArray(shader.element->positionLocation);
    }

    template <>
    void ShaderHelper::unbindAttributes<Vertex<XYZ.UV>>(const ShaderProgram &shader)
    {
      glDisableVertexAttribArray(shader.element->positionLocation);
      glDisableVertexAttribArray(shader.element->coordLocation);
    }

    template <>
    void ShaderHelper::unbindAttributes<Vertex<XYZ.RGBA>>(const ShaderProgram &shader)
    {
      glDisableVertexAttribArray(shader.element->positionLocation);
      glDisableVertexAttribArray(shader.element->colorLocation);
    }

    template <>
    void ShaderHelper::unbindAttributes<Vertex<XYZ.UV.RGBA>>(const ShaderProgram &shader)
    {
      glDisableVertexAttribArray(shader.element->positionLocation);
      glDisableVertexAttribArray(shader.element->coordLocation);
      glDisableVertexAttribArray(shader.element->colorLocation);
    }

    template <>
    void ShaderHelper::unbindAttributes<Vertex<N>>(const ShaderProgram &shader)
    {
      glDisableVertexAttribArray(shader.element->normalLocation);
    }

    template <>
    void ShaderHelper::unbindAttributes<Vertex<UV>>(const ShaderProgram &shader)
    {
      glDisableVertexAttribArray(shader.element->coordLocation);
    }

    template <>
    void ShaderHelper::unbindAttributes<Vertex<RGBA>>(const ShaderProgram &shader)
    {
      glDisableVertexAttribArray(shader.element->colorLocation);
    }
  }
}
<commit_msg>Quad AND Vertex: COMPLETING SUPPORT FOR XYZ.N.RGBA<commit_after>#include "gl/ShaderHelper.h"

#include "Log.h"

using namespace std;

namespace chr
{
  namespace gl
  {
    GLuint ShaderHelper::createShader(GLenum type, const char *source)
    {
     GLuint shader = 0;
     shader = glCreateShader(type);

     if (shader != 0)
     {
       glShaderSource(shader, 1, reinterpret_cast<const GLchar**>(&source), NULL);
       glCompileShader(shader);

       GLint success;
       glGetShaderiv(shader, GL_COMPILE_STATUS, &success);

       if (success != GL_TRUE)
       {
         GLint maxLength = 0;
         glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &maxLength);
         string buf(maxLength, 0);
         glGetShaderInfoLog(shader, maxLength, &maxLength, &buf[0]);

         LOGE << "ERROR: FAILED TO COMPILE " << ((type == GL_FRAGMENT_SHADER) ? "FRAGMENT" : "VERTEX") << " SHADER" << endl;
         LOGE << buf << endl;

         glDeleteShader(shader);
         shader = 0;
       }
     }

     return shader;
    }

    tuple<GLuint, GLuint, GLuint> ShaderHelper::loadProgram(experimental::string_view vertexShaderSource, experimental::string_view fragmentShaderSource)
    {
      GLuint programId = 0;
      GLuint vertexShaderId = 0;
      GLuint fragmentShaderId = 0;

      vertexShaderId = createShader(GL_VERTEX_SHADER, vertexShaderSource.data());

      if (vertexShaderId != 0)
      {
        fragmentShaderId = createShader(GL_FRAGMENT_SHADER, fragmentShaderSource.data());

        if (fragmentShaderId != 0)
        {
          programId = glCreateProgram();

          if (programId != 0)
          {
            glAttachShader(programId, vertexShaderId);
            glAttachShader(programId, fragmentShaderId);
            glLinkProgram(programId);

            GLint success;
            glGetProgramiv(programId, GL_LINK_STATUS, &success);

            if (success != GL_TRUE)
            {
              GLint maxLength = 0;
              glGetShaderiv(programId, GL_INFO_LOG_LENGTH, &maxLength);
              string buf(maxLength, 0);
              glGetShaderInfoLog(programId, maxLength, &maxLength, &buf[0]);

              LOGE << "ERROR: FAILED TO LINK SHADER PROGRAM" << endl;
              LOGE << buf << endl;

              glDeleteShader(vertexShaderId);
              vertexShaderId = 0;

              glDeleteShader(fragmentShaderId);
              fragmentShaderId = 0;

              glDeleteProgram(programId);
              programId = 0;
            }
          }
        }
        else
        {
          LOGE << "ERROR: UNABLE TO LOAD FRAGMENT SHADER" << endl;

          glDeleteShader(vertexShaderId);
          vertexShaderId = 0;
        }
      }
      else
      {
        LOGE << "ERROR: UNABLE TO LOAD VERTEX SHADER" << endl;
      }

      return make_tuple(programId, vertexShaderId, fragmentShaderId);
    }

    void ShaderHelper::unloadProgram(GLuint programId, GLuint vertexShaderId, GLuint fragmentShaderId)
    {
      glDetachShader(programId, fragmentShaderId);
      glDeleteShader(fragmentShaderId);

      glDetachShader(programId, vertexShaderId);
      glDeleteShader(vertexShaderId);

      glDeleteProgram(programId);
    }

    // ---

    template <>
    void ShaderHelper::bindAttributes<Vertex<XYZ>>(const ShaderProgram &shader)
    {
      glEnableVertexAttribArray(shader.element->positionLocation);
      glVertexAttribPointer(shader.element->positionLocation, 3, GL_FLOAT, GL_FALSE, 0, 0);
    }

    template <>
    void ShaderHelper::bindAttributes<Vertex<XYZ.UV>>(const ShaderProgram &shader)
    {
      glEnableVertexAttribArray(shader.element->positionLocation);
      glVertexAttribPointer(shader.element->positionLocation, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex<XYZ.UV>), 0);

      glEnableVertexAttribArray(shader.element->coordLocation);
      glVertexAttribPointer(shader.element->coordLocation, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex<XYZ.UV>), (GLvoid *) sizeof(Vertex<>));
    }

    template <>
    void ShaderHelper::bindAttributes<Vertex<XYZ.RGBA>>(const ShaderProgram &shader)
    {
      glEnableVertexAttribArray(shader.element->positionLocation);
      glVertexAttribPointer(shader.element->positionLocation, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex<XYZ.RGBA>), 0);

      glEnableVertexAttribArray(shader.element->colorLocation);
      glVertexAttribPointer(shader.element->colorLocation, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex<XYZ.RGBA>), (GLvoid *) sizeof(Vertex<XYZ>));
    }

    template <>
    void ShaderHelper::bindAttributes<Vertex<XYZ.N.RGBA>>(const ShaderProgram &shader)
    {
      glEnableVertexAttribArray(shader.element->positionLocation);
      glVertexAttribPointer(shader.element->positionLocation, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex<XYZ.N.RGBA>), 0);

      glEnableVertexAttribArray(shader.element->normalLocation);
      glVertexAttribPointer(shader.element->normalLocation, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex<XYZ.N.RGBA>), (GLvoid *) sizeof(Vertex<>));

      glEnableVertexAttribArray(shader.element->colorLocation);
      glVertexAttribPointer(shader.element->colorLocation, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex<XYZ.N.RGBA>), (GLvoid *) sizeof(Vertex<XYZ.N>));
    }

    template <>
    void ShaderHelper::bindAttributes<Vertex<XYZ.UV.RGBA>>(const ShaderProgram &shader)
    {
      glEnableVertexAttribArray(shader.element->positionLocation);
      glVertexAttribPointer(shader.element->positionLocation, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex<XYZ.UV.RGBA>), 0);

      glEnableVertexAttribArray(shader.element->coordLocation);
      glVertexAttribPointer(shader.element->coordLocation, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex<XYZ.UV.RGBA>), (GLvoid *) sizeof(Vertex<>));

      glEnableVertexAttribArray(shader.element->colorLocation);
      glVertexAttribPointer(shader.element->colorLocation, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex<XYZ.UV.RGBA>), (GLvoid *) sizeof(Vertex<XYZ.UV>));
    }

    template <>
    void ShaderHelper::bindAttributes<Vertex<N>>(const ShaderProgram &shader)
    {
      glEnableVertexAttribArray(shader.element->normalLocation);
      glVertexAttribPointer(shader.element->normalLocation, 3, GL_FLOAT, GL_FALSE, 0, 0);
    }

    template <>
    void ShaderHelper::bindAttributes<Vertex<UV>>(const ShaderProgram &shader)
    {
      glEnableVertexAttribArray(shader.element->coordLocation);
      glVertexAttribPointer(shader.element->coordLocation, 2, GL_FLOAT, GL_FALSE, 0, 0);
    }

    template <>
    void ShaderHelper::bindAttributes<Vertex<RGBA>>(const ShaderProgram &shader)
    {
      glEnableVertexAttribArray(shader.element->colorLocation);
      glVertexAttribPointer(shader.element->colorLocation, 4, GL_FLOAT, GL_FALSE, 0, 0);
    }

    // ---

    template <>
    void ShaderHelper::unbindAttributes<Vertex<XYZ>>(const ShaderProgram &shader)
    {
      glDisableVertexAttribArray(shader.element->positionLocation);
    }

    template <>
    void ShaderHelper::unbindAttributes<Vertex<XYZ.UV>>(const ShaderProgram &shader)
    {
      glDisableVertexAttribArray(shader.element->positionLocation);
      glDisableVertexAttribArray(shader.element->coordLocation);
    }

    template <>
    void ShaderHelper::unbindAttributes<Vertex<XYZ.RGBA>>(const ShaderProgram &shader)
    {
      glDisableVertexAttribArray(shader.element->positionLocation);
      glDisableVertexAttribArray(shader.element->colorLocation);
    }

    template <>
    void ShaderHelper::unbindAttributes<Vertex<XYZ.N.RGBA>>(const ShaderProgram &shader)
    {
      glDisableVertexAttribArray(shader.element->positionLocation);
      glDisableVertexAttribArray(shader.element->normalLocation);
      glDisableVertexAttribArray(shader.element->colorLocation);
    }

    template <>
    void ShaderHelper::unbindAttributes<Vertex<XYZ.UV.RGBA>>(const ShaderProgram &shader)
    {
      glDisableVertexAttribArray(shader.element->positionLocation);
      glDisableVertexAttribArray(shader.element->coordLocation);
      glDisableVertexAttribArray(shader.element->colorLocation);
    }

    template <>
    void ShaderHelper::unbindAttributes<Vertex<N>>(const ShaderProgram &shader)
    {
      glDisableVertexAttribArray(shader.element->normalLocation);
    }

    template <>
    void ShaderHelper::unbindAttributes<Vertex<UV>>(const ShaderProgram &shader)
    {
      glDisableVertexAttribArray(shader.element->coordLocation);
    }

    template <>
    void ShaderHelper::unbindAttributes<Vertex<RGBA>>(const ShaderProgram &shader)
    {
      glDisableVertexAttribArray(shader.element->colorLocation);
    }
  }
}
<|endoftext|>


<commit_before>int f1()
{
    try
    {
        return 42;
    }
    catch (int e)
    {
    }

    return 2;
}
<commit_msg>Added some more cases to try test.<commit_after>struct s { s(); ~s(); };

void e1();
void e2(int);
void e3();

void f1()
{
    throw 1;
}

void f2()
{
    throw s();
}

void f3()
{
    try
    {
        e1();
    }
    /*catch (int & e)
    {
        e2(e);
    }*/
    catch (long)
    {
        e3();
    }
    catch (...)
    {
        e3();
    }
}

int f4()
{
    try
    {
        return 42;
    }
    catch (int e)
    {
    }

    return 2;
}
<|endoftext|>


<commit_before><commit_msg>Forgot to remove these debug statements.<commit_after><|endoftext|>


<commit_before><commit_msg>coverity#1213244 Dereference null return value<commit_after><|endoftext|>


<commit_before>#include "IR.h"
#include "ObjectInstanceRegistry.h"
#include "Parameter.h"

namespace Halide {
namespace Internal {

struct ParameterContents {
    mutable RefCount ref_count;
    Type type;
    const bool is_buffer;
    int dimensions;
    const bool is_explicit_name;
    const bool is_registered;
    std::string name;
    Buffer buffer;
    uint64_t data;
    Expr min_constraint[4];
    Expr extent_constraint[4];
    Expr stride_constraint[4];
    Expr min_value, max_value;
    ParameterContents(Type t, bool b, int d, const std::string &n, bool e, bool r)
        : type(t), is_buffer(b), dimensions(d), is_explicit_name(e), is_registered(r), name(n), buffer(Buffer()), data(0) {
        // stride_constraint[0] defaults to 1. This is important for
        // dense vectorization. You can unset it by setting it to a
        // null expression. (param.set_stride(0, Expr());)
        stride_constraint[0] = 1;
    }
};

template<>
EXPORT RefCount &ref_count<Halide::Internal::ParameterContents>(const ParameterContents *p) {return p->ref_count;}

template<>
EXPORT void destroy<Halide::Internal::ParameterContents>(const ParameterContents *p) {delete p;}

void Parameter::check_defined() const {
    user_assert(defined()) << "Parameter is undefined\n";
}

void Parameter::check_is_buffer() const {
    check_defined();
    user_assert(contents.ptr->is_buffer) << "Parameter " << name() << " is not a Buffer\n";
}

void Parameter::check_is_scalar() const {
    check_defined();
    user_assert(!contents.ptr->is_buffer) << "Parameter " << name() << " is a Buffer\n";
}

void Parameter::check_dim_ok(int dim) const {
    user_assert(dim >= 0 && dim < dimensions())
        << "Dimension " << dim << " is not in the range [0, " << dimensions() - 1 << "]\n";
}

Parameter::Parameter() : contents(NULL) {
    // Undefined Parameters are never registered.
}

Parameter::Parameter(Type t, bool is_buffer, int d) :
    contents(new ParameterContents(t, is_buffer, d, unique_name('p'), false, true)) {
    internal_assert(is_buffer || d == 0) << "Scalar parameters should be zero-dimensional";
    // Note that is_registered is always true here; this is just using a parallel code structure for clarity.
    if (contents.defined() && contents.ptr->is_registered) {
        ObjectInstanceRegistry::register_instance(this, 0, ObjectInstanceRegistry::FilterParam, this);
    }
}

Parameter::Parameter(Type t, bool is_buffer, int d, const std::string &name, bool is_explicit_name, bool register_instance) :
    contents(new ParameterContents(t, is_buffer, d, name, is_explicit_name, register_instance)) {
    internal_assert(is_buffer || d == 0) << "Scalar parameters should be zero-dimensional";
    if (contents.defined() && contents.ptr->is_registered) {
        ObjectInstanceRegistry::register_instance(this, 0, ObjectInstanceRegistry::FilterParam, this);
    }
}

Parameter::Parameter(const Parameter& that) : contents(that.contents) {
    if (contents.defined() && contents.ptr->is_registered) {
        ObjectInstanceRegistry::register_instance(this, 0, ObjectInstanceRegistry::FilterParam, this);
    }
}

Parameter& Parameter::operator=(const Parameter& that) {
    bool was_registered = contents.defined() && contents.ptr->is_registered;
    contents = that.contents;
    bool should_be_registered = contents.defined() && contents.ptr->is_registered;
    if (should_be_registered && !was_registered) {
        // This can happen if you do:
        // Param p; // undefined
        // p = make_interesting_param();
        ObjectInstanceRegistry::register_instance(this, 0, ObjectInstanceRegistry::FilterParam, this);
    } else if (!should_be_registered && was_registered) {
        // This can happen if you do:
        // Param p = make_interesting_param();
        // p = Param();
        ObjectInstanceRegistry::unregister_instance(this);
    }
    return *this;
}

Parameter::~Parameter() {
    if (contents.defined() && contents.ptr->is_registered) {
        ObjectInstanceRegistry::unregister_instance(this);
    }
}

Type Parameter::type() const {
    check_defined();
    return contents.ptr->type;
}

int Parameter::dimensions() const {
    check_defined();
    return contents.ptr->dimensions;
}

const std::string &Parameter::name() const {
    check_defined();
    return contents.ptr->name;
}

bool Parameter::is_explicit_name() const {
    check_defined();
    return contents.ptr->is_explicit_name;
}

bool Parameter::is_buffer() const {
    check_defined();
    return contents.ptr->is_buffer;
}

Buffer Parameter::get_buffer() const {
    check_is_buffer();
    return contents.ptr->buffer;
}

void Parameter::set_buffer(Buffer b) {
    check_is_buffer();
    if (b.defined()) {
        user_assert(contents.ptr->type == b.type())
            << "Can't bind Parameter " << name()
            << " of type " << contents.ptr->type
            << " to Buffer " << b.name()
            << " of type " << b.type() << "\n";
    }
    contents.ptr->buffer = b;
}

void *Parameter::get_scalar_address() const {
    check_is_scalar();
    return &contents.ptr->data;
}

/** Tests if this handle is the same as another handle */
bool Parameter::same_as(const Parameter &other) const {
    return contents.ptr == other.contents.ptr;
}

/** Tests if this handle is non-NULL */
bool Parameter::defined() const {
    return contents.defined();
}

void Parameter::set_min_constraint(int dim, Expr e) {
    check_is_buffer();
    check_dim_ok(dim);
    contents.ptr->min_constraint[dim] = e;
}

void Parameter::set_extent_constraint(int dim, Expr e) {
    check_is_buffer();
    check_dim_ok(dim);
    contents.ptr->extent_constraint[dim] = e;
}

void Parameter::set_stride_constraint(int dim, Expr e) {
    check_is_buffer();
    check_dim_ok(dim);
    contents.ptr->stride_constraint[dim] = e;
}

Expr Parameter::min_constraint(int dim) const {
    check_is_buffer();
    check_dim_ok(dim);
    return contents.ptr->min_constraint[dim];
}

Expr Parameter::extent_constraint(int dim) const {
    check_is_buffer();
    check_dim_ok(dim);
    return contents.ptr->extent_constraint[dim];
}

Expr Parameter::stride_constraint(int dim) const {
    check_is_buffer();
    check_dim_ok(dim);
    return contents.ptr->stride_constraint[dim];
}

void Parameter::set_min_value(Expr e) {
    check_is_scalar();
    user_assert(e.type() == contents.ptr->type)
        << "Can't set parameter " << name()
        << " of type " << contents.ptr->type
        << " to have min value " << e
        << " of type " << e.type() << "\n";
    contents.ptr->min_value = e;
}

Expr Parameter::get_min_value() const {
    check_is_scalar();
    return contents.ptr->min_value;
}

void Parameter::set_max_value(Expr e) {
    check_is_scalar();
    user_assert(e.type() == contents.ptr->type)
        << "Can't set parameter " << name()
        << " of type " << contents.ptr->type
        << " to have max value " << e
        << " of type " << e.type() << "\n";
    contents.ptr->max_value = e;
}

Expr Parameter::get_max_value() const {
    check_is_scalar();
    return contents.ptr->max_value;
}

void check_call_arg_types(const std::string &name, std::vector<Expr> *args, int dims) {
    user_assert(args->size() == (size_t)dims)
        << args->size() << "-argument call to \""
        << name << "\", which has " << dims << " dimensions.\n";

    for (size_t i = 0; i < args->size(); i++) {
        user_assert((*args)[i].defined())
            << "Argument " << i << " to call to \"" << name << "\" is an undefined Expr\n";
        Type t = (*args)[i].type();
        if (t.is_float() || (t.is_uint() && t.bits >= 32) || (t.is_int() && t.bits > 32)) {
            user_error << "Implicit cast from " << t << " to int in argument " << (i+1)
                       << " in call to \"" << name << "\" is not allowed. Use an explicit cast.\n";
        }
        // We're allowed to implicitly cast from other varieties of int
        if (t != Int(32)) {
            (*args)[i] = Internal::Cast::make(Int(32), (*args)[i]);
        }
    }
}

}
}

<commit_msg>s/Param/Parameter/<commit_after>#include "IR.h"
#include "ObjectInstanceRegistry.h"
#include "Parameter.h"

namespace Halide {
namespace Internal {

struct ParameterContents {
    mutable RefCount ref_count;
    Type type;
    const bool is_buffer;
    int dimensions;
    const bool is_explicit_name;
    const bool is_registered;
    std::string name;
    Buffer buffer;
    uint64_t data;
    Expr min_constraint[4];
    Expr extent_constraint[4];
    Expr stride_constraint[4];
    Expr min_value, max_value;
    ParameterContents(Type t, bool b, int d, const std::string &n, bool e, bool r)
        : type(t), is_buffer(b), dimensions(d), is_explicit_name(e), is_registered(r), name(n), buffer(Buffer()), data(0) {
        // stride_constraint[0] defaults to 1. This is important for
        // dense vectorization. You can unset it by setting it to a
        // null expression. (param.set_stride(0, Expr());)
        stride_constraint[0] = 1;
    }
};

template<>
EXPORT RefCount &ref_count<Halide::Internal::ParameterContents>(const ParameterContents *p) {return p->ref_count;}

template<>
EXPORT void destroy<Halide::Internal::ParameterContents>(const ParameterContents *p) {delete p;}

void Parameter::check_defined() const {
    user_assert(defined()) << "Parameter is undefined\n";
}

void Parameter::check_is_buffer() const {
    check_defined();
    user_assert(contents.ptr->is_buffer) << "Parameter " << name() << " is not a Buffer\n";
}

void Parameter::check_is_scalar() const {
    check_defined();
    user_assert(!contents.ptr->is_buffer) << "Parameter " << name() << " is a Buffer\n";
}

void Parameter::check_dim_ok(int dim) const {
    user_assert(dim >= 0 && dim < dimensions())
        << "Dimension " << dim << " is not in the range [0, " << dimensions() - 1 << "]\n";
}

Parameter::Parameter() : contents(NULL) {
    // Undefined Parameters are never registered.
}

Parameter::Parameter(Type t, bool is_buffer, int d) :
    contents(new ParameterContents(t, is_buffer, d, unique_name('p'), false, true)) {
    internal_assert(is_buffer || d == 0) << "Scalar parameters should be zero-dimensional";
    // Note that is_registered is always true here; this is just using a parallel code structure for clarity.
    if (contents.defined() && contents.ptr->is_registered) {
        ObjectInstanceRegistry::register_instance(this, 0, ObjectInstanceRegistry::FilterParam, this);
    }
}

Parameter::Parameter(Type t, bool is_buffer, int d, const std::string &name, bool is_explicit_name, bool register_instance) :
    contents(new ParameterContents(t, is_buffer, d, name, is_explicit_name, register_instance)) {
    internal_assert(is_buffer || d == 0) << "Scalar parameters should be zero-dimensional";
    if (contents.defined() && contents.ptr->is_registered) {
        ObjectInstanceRegistry::register_instance(this, 0, ObjectInstanceRegistry::FilterParam, this);
    }
}

Parameter::Parameter(const Parameter& that) : contents(that.contents) {
    if (contents.defined() && contents.ptr->is_registered) {
        ObjectInstanceRegistry::register_instance(this, 0, ObjectInstanceRegistry::FilterParam, this);
    }
}

Parameter& Parameter::operator=(const Parameter& that) {
    bool was_registered = contents.defined() && contents.ptr->is_registered;
    contents = that.contents;
    bool should_be_registered = contents.defined() && contents.ptr->is_registered;
    if (should_be_registered && !was_registered) {
        // This can happen if you do:
        // Parameter p; // undefined
        // p = make_interesting_parameter();
        ObjectInstanceRegistry::register_instance(this, 0, ObjectInstanceRegistry::FilterParam, this);
    } else if (!should_be_registered && was_registered) {
        // This can happen if you do:
        // Parameter p = make_interesting_parameter();
        // p = Parameter();
        ObjectInstanceRegistry::unregister_instance(this);
    }
    return *this;
}

Parameter::~Parameter() {
    if (contents.defined() && contents.ptr->is_registered) {
        ObjectInstanceRegistry::unregister_instance(this);
    }
}

Type Parameter::type() const {
    check_defined();
    return contents.ptr->type;
}

int Parameter::dimensions() const {
    check_defined();
    return contents.ptr->dimensions;
}

const std::string &Parameter::name() const {
    check_defined();
    return contents.ptr->name;
}

bool Parameter::is_explicit_name() const {
    check_defined();
    return contents.ptr->is_explicit_name;
}

bool Parameter::is_buffer() const {
    check_defined();
    return contents.ptr->is_buffer;
}

Buffer Parameter::get_buffer() const {
    check_is_buffer();
    return contents.ptr->buffer;
}

void Parameter::set_buffer(Buffer b) {
    check_is_buffer();
    if (b.defined()) {
        user_assert(contents.ptr->type == b.type())
            << "Can't bind Parameter " << name()
            << " of type " << contents.ptr->type
            << " to Buffer " << b.name()
            << " of type " << b.type() << "\n";
    }
    contents.ptr->buffer = b;
}

void *Parameter::get_scalar_address() const {
    check_is_scalar();
    return &contents.ptr->data;
}

/** Tests if this handle is the same as another handle */
bool Parameter::same_as(const Parameter &other) const {
    return contents.ptr == other.contents.ptr;
}

/** Tests if this handle is non-NULL */
bool Parameter::defined() const {
    return contents.defined();
}

void Parameter::set_min_constraint(int dim, Expr e) {
    check_is_buffer();
    check_dim_ok(dim);
    contents.ptr->min_constraint[dim] = e;
}

void Parameter::set_extent_constraint(int dim, Expr e) {
    check_is_buffer();
    check_dim_ok(dim);
    contents.ptr->extent_constraint[dim] = e;
}

void Parameter::set_stride_constraint(int dim, Expr e) {
    check_is_buffer();
    check_dim_ok(dim);
    contents.ptr->stride_constraint[dim] = e;
}

Expr Parameter::min_constraint(int dim) const {
    check_is_buffer();
    check_dim_ok(dim);
    return contents.ptr->min_constraint[dim];
}

Expr Parameter::extent_constraint(int dim) const {
    check_is_buffer();
    check_dim_ok(dim);
    return contents.ptr->extent_constraint[dim];
}

Expr Parameter::stride_constraint(int dim) const {
    check_is_buffer();
    check_dim_ok(dim);
    return contents.ptr->stride_constraint[dim];
}

void Parameter::set_min_value(Expr e) {
    check_is_scalar();
    user_assert(e.type() == contents.ptr->type)
        << "Can't set parameter " << name()
        << " of type " << contents.ptr->type
        << " to have min value " << e
        << " of type " << e.type() << "\n";
    contents.ptr->min_value = e;
}

Expr Parameter::get_min_value() const {
    check_is_scalar();
    return contents.ptr->min_value;
}

void Parameter::set_max_value(Expr e) {
    check_is_scalar();
    user_assert(e.type() == contents.ptr->type)
        << "Can't set parameter " << name()
        << " of type " << contents.ptr->type
        << " to have max value " << e
        << " of type " << e.type() << "\n";
    contents.ptr->max_value = e;
}

Expr Parameter::get_max_value() const {
    check_is_scalar();
    return contents.ptr->max_value;
}

void check_call_arg_types(const std::string &name, std::vector<Expr> *args, int dims) {
    user_assert(args->size() == (size_t)dims)
        << args->size() << "-argument call to \""
        << name << "\", which has " << dims << " dimensions.\n";

    for (size_t i = 0; i < args->size(); i++) {
        user_assert((*args)[i].defined())
            << "Argument " << i << " to call to \"" << name << "\" is an undefined Expr\n";
        Type t = (*args)[i].type();
        if (t.is_float() || (t.is_uint() && t.bits >= 32) || (t.is_int() && t.bits > 32)) {
            user_error << "Implicit cast from " << t << " to int in argument " << (i+1)
                       << " in call to \"" << name << "\" is not allowed. Use an explicit cast.\n";
        }
        // We're allowed to implicitly cast from other varieties of int
        if (t != Int(32)) {
            (*args)[i] = Internal::Cast::make(Int(32), (*args)[i]);
        }
    }
}

}
}

<|endoftext|>


<commit_before>/*
Copyright (C) 2012 Kolibre

This file is part of kolibre-narrator.

Kolibre-narrator is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2.1 of the License, or
(at your option) any later version.

Kolibre-narrator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License
along with kolibre-narrator. If not, see <http://www.gnu.org/licenses/>.
*/

#include "PortAudio.h"
#include "RingBuffer.h"

#include <iostream>
#include <unistd.h>
#include <cassert>
#include <cstring>
#include <log4cxx/logger.h>

#define RINGBUFFERSIZE 1024*16

// create logger which will become a child to logger kolibre.narrator
log4cxx::LoggerPtr narratorPaLog(log4cxx::Logger::getLogger("kolibre.narrator.portaudio"));

int pa_stream_callback(
        const void *input,
        void *output,
        unsigned long frameCount,
        const PaStreamCallbackTimeInfo* timeInfo,
        PaStreamCallbackFlags statusFlags,
        void *userData );

void pa_stream_finished_callback( void *userData );

PortAudio::PortAudio():
    ringbuf(RINGBUFFERSIZE)
{
    isInitialized = true;
    isOpen = false;
    isStarted = false;
    mChannels = 0;
    mRate = 0;
    mLatency = 0;
    pStream = NULL;

    LOG4CXX_INFO(narratorPaLog, "Initializing portaudio");

    mError = Pa_Initialize();
    if(mError != paNoError) {
        LOG4CXX_ERROR(narratorPaLog, "Failed to initialize portaudio: " << Pa_GetErrorText(mError));
        isInitialized = false;
    }
}

PortAudio::~PortAudio()
{
    close();

    if(isInitialized)
        Pa_Terminate();
}

bool PortAudio::open(long rate, int channels)
{

    if(mRate != rate || mChannels != mChannels) {
        close();
    }

    if(!isOpen) {
        //PaDeviceIndex default_device = 2;
        PaDeviceIndex default_device = Pa_GetDefaultOutputDevice();
        if( default_device == paNoDevice ) {
            LOG4CXX_ERROR(narratorPaLog, "Pa_GetDefaultOutputDevice failed, however, device count is: " << Pa_GetDeviceCount() );
            return false;
        }

        mOutputParameters.device = default_device; /* default output device */
        mOutputParameters.channelCount = channels;
        mOutputParameters.sampleFormat = paFloat32;
        mOutputParameters.suggestedLatency = 0; //Pa_GetDeviceInfo( mOutputParameters.device )->defaultLowOutputLatency;
        mOutputParameters.hostApiSpecificStreamInfo = NULL;

        const PaDeviceInfo* devinfo = Pa_GetDeviceInfo(mOutputParameters.device);
        const PaHostApiInfo* hostapiinfo = Pa_GetHostApiInfo(devinfo->hostApi);

        LOG4CXX_DEBUG(narratorPaLog, "Opening device: " << devinfo->name << " (" << hostapiinfo->name << "), channels: " << channels << ", rate: " << rate <<" (" << devinfo->defaultSampleRate << ")");

        unsigned long framesPerBuffer = 1024;

#ifdef WIN32
        framesPerBuffer = 4096;
#endif

        mError = Pa_OpenStream(&pStream, NULL, &mOutputParameters, rate, framesPerBuffer,
                paNoFlag, pa_stream_callback, this);

        if(mError != paNoError) {
            LOG4CXX_ERROR(narratorPaLog, "Failed to open stream: " << Pa_GetErrorText(mError));
            return false;
        }

        mRate = rate;
        mChannels = channels;
        isOpen = true;
        isStarted = false;

        mError = Pa_SetStreamFinishedCallback(pStream, pa_stream_finished_callback);
        if(mError != paNoError) {
            LOG4CXX_ERROR(narratorPaLog, "Failed to set FinishedCallback: " << Pa_GetErrorText(mError));
        }

    }
    return true;
}

long PortAudio::stop()
{
    if(isStarted) {
        mError = Pa_StopStream(pStream);
        if(mError != paNoError)
            LOG4CXX_ERROR(narratorPaLog, "Failed to stop stream: " << Pa_GetErrorText(mError));

        ringbuf.flush();
        isStarted = false;
    }

    return mLatency;
}

long PortAudio::abort()
{
    if(isStarted) {
        LOG4CXX_DEBUG(narratorPaLog, "Aborting stream");
        mError = Pa_AbortStream(pStream);
        if(mError != paNoError)
            LOG4CXX_ERROR(narratorPaLog, "Failed to abort stream: " << Pa_GetErrorText(mError));

        ringbuf.flush();
        isStarted = false;
    }

    return mLatency;
}

bool PortAudio::close()
{
    stop();
    if(isOpen) {
        mError = Pa_CloseStream(pStream);
        if(mError != paNoError)
        {
            LOG4CXX_ERROR(narratorPaLog, "Failed to close stream: " << Pa_GetErrorText(mError));
        }
        else
        {
            isOpen = false;
        }
    }
    return true;
}

long PortAudio::getRemainingms()
{
    size_t bufferedData = ringbuf.getReadAvailable();

    long bufferms = 0;

    if(mChannels != 0 && mRate != 0)
        bufferms = (long) (1000.0 * bufferedData) / mChannels / mRate;

    return bufferms + mLatency;
}

/*
   Blocks until data can be written.
   If no data can be written in 2ms*100 the stream gets restarted.
   returns the amount of data that can be written.
*/
unsigned int PortAudio::getWriteAvailable()
{
    size_t writeAvailable = 0;
    int waitCount = 0;

    while( writeAvailable == 0 ) {
        writeAvailable = ringbuf.getWriteAvailable();

        if( writeAvailable == 0 ) {
            usleep(200000); //200 ms
            //LOG4CXX_DEBUG(narratorPaLog, "getWriteAvailable = 0, Pa_IsStreamActive = " << Pa_IsStreamActive(pStream));
        }

        if(waitCount++ > 10) {
            LOG4CXX_ERROR(narratorPaLog, "getWriteAvailable waittime exceeded, restarting stream");

            mError = Pa_AbortStream(pStream);
            // If abortstream fails, try reopening
            if(mError != paNoError && mError != paStreamIsStopped) {
                LOG4CXX_ERROR(narratorPaLog, "Failed to abort stream, trying to close and reopen: " << Pa_GetErrorText(mError));

                mError = Pa_CloseStream(pStream);
                if(mError != paNoError)
                    LOG4CXX_ERROR(narratorPaLog, "Failed to close stream: " << Pa_GetErrorText(mError));

                isOpen = false;
                open(mRate, mChannels);
            }

            mError = Pa_StartStream(pStream);
            if(mError != paNoError) LOG4CXX_ERROR(narratorPaLog, "Failed to start stream: " << Pa_GetErrorText(mError));

            waitCount = 0;
        }
    }

    //LOG4CXX_DEBUG(narratorPaLog, "getWriteAvailable = " << writeAvailable << ", Pa_IsStreamActive = " << Pa_IsStreamActive(pStream));
    return writeAvailable;
}

bool PortAudio::write(float *buffer, unsigned int samples)
{
    size_t elemWritten = ringbuf.writeElements(buffer, samples*mChannels);

    // Try starting the stream
    if(!isStarted) {
        //LOG4CXX_INFO(narratorPaLog, "Starting stream");
        mError = Pa_StartStream(pStream);
        if(mError != paNoError) {
            LOG4CXX_ERROR(narratorPaLog, "Failed to start stream: " << Pa_GetErrorText(mError));
        }
        mLatency = (long) (Pa_GetStreamInfo(pStream)->outputLatency * 1000.0);
        isStarted = true;
    }

    if( elemWritten < samples)
        return false;

    return false;
}

long unsigned int min( long unsigned int a, long unsigned int b )
{
    if( a < b )
        return a;
    return b;
}

/*
   @note With the exception of Pa_GetStreamCpuLoad() it is not permissable to call
   PortAudio API functions from within the stream callback.
*/
int pa_stream_callback(
        const void *input,
        void *output, // Write frameCount*channels here
        unsigned long frameCount,
        const PaStreamCallbackTimeInfo* timeInfo,
        PaStreamCallbackFlags statusFlags,
        void *userData )
{
    (void) timeInfo;    /* Prevent unused variable warning. */
    (void) input; /* Prevent unused variable warning. */

    static long underrunms = 0;

    RingBuffer *ringbuf = &((PortAudio*)userData)->ringbuf;

    size_t availableElements = ringbuf->getReadAvailable();

    int channels = ((PortAudio*)userData)->mChannels;
    long rate = ((PortAudio*)userData)->mRate;
    size_t elementsToRead = min( frameCount * channels, availableElements);

    float* outbuf = (float*)output;

    size_t elementsRead = ringbuf->readElements(outbuf, elementsToRead);

    if( elementsRead < frameCount*channels ) {
        memset( (outbuf+(elementsRead*channels)), 0, (frameCount*channels-elementsRead)*sizeof(float) );
        underrunms += (long) (frameCount * 1000.0) / rate;
        //std::cout << __FUNCTION__ << " Less read than requested, underrun ms:" << underrunms << std::endl;
    } else {
        //std::cout << __FUNCTION__ << " availableElements: " << availableElements << " elementsToRead: " << elementsToRead << " elementsRead:" << elementsRead << std::endl;
        underrunms = 0;
    }

    if(underrunms > 500) return paComplete;

    return paContinue; // paAbort, paComplete
}

void pa_stream_finished_callback( void *userData )
{
    ((PortAudio*)userData)->isStarted = false;
    //std::cout << __FUNCTION__ << " Finished" << std::endl;
}
<commit_msg>Recover narrator on portaudio fail<commit_after>/*
Copyright (C) 2012 Kolibre

This file is part of kolibre-narrator.

Kolibre-narrator is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2.1 of the License, or
(at your option) any later version.

Kolibre-narrator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License
along with kolibre-narrator. If not, see <http://www.gnu.org/licenses/>.
*/

#include "PortAudio.h"
#include "RingBuffer.h"

#include <iostream>
#include <unistd.h>
#include <cassert>
#include <cstring>
#include <log4cxx/logger.h>

#define RINGBUFFERSIZE 1024*16

// create logger which will become a child to logger kolibre.narrator
log4cxx::LoggerPtr narratorPaLog(log4cxx::Logger::getLogger("kolibre.narrator.portaudio"));

int pa_stream_callback(
        const void *input,
        void *output,
        unsigned long frameCount,
        const PaStreamCallbackTimeInfo* timeInfo,
        PaStreamCallbackFlags statusFlags,
        void *userData );

void pa_stream_finished_callback( void *userData );

PortAudio::PortAudio():
    ringbuf(RINGBUFFERSIZE)
{
    isInitialized = true;
    isOpen = false;
    isStarted = false;
    mChannels = 0;
    mRate = 0;
    mLatency = 0;
    pStream = NULL;

    LOG4CXX_INFO(narratorPaLog, "Initializing portaudio");

    mError = Pa_Initialize();
    if(mError != paNoError) {
        LOG4CXX_ERROR(narratorPaLog, "Failed to initialize portaudio: " << Pa_GetErrorText(mError));
        isInitialized = false;
    }
}

PortAudio::~PortAudio()
{
    close();

    if(isInitialized)
        Pa_Terminate();
}

bool PortAudio::open(long rate, int channels)
{

    if(mRate != rate || mChannels != mChannels) {
        close();
    }

    if(!isOpen) {
        //PaDeviceIndex default_device = 2;
        PaDeviceIndex default_device = Pa_GetDefaultOutputDevice();
        if( default_device == paNoDevice ) {
            LOG4CXX_ERROR(narratorPaLog, "Pa_GetDefaultOutputDevice failed, however, device count is: " << Pa_GetDeviceCount() );
            return false;
        }

        mOutputParameters.device = default_device; /* default output device */
        mOutputParameters.channelCount = channels;
        mOutputParameters.sampleFormat = paFloat32;
        mOutputParameters.suggestedLatency = 0; //Pa_GetDeviceInfo( mOutputParameters.device )->defaultLowOutputLatency;
        mOutputParameters.hostApiSpecificStreamInfo = NULL;

        const PaDeviceInfo* devinfo = Pa_GetDeviceInfo(mOutputParameters.device);
        const PaHostApiInfo* hostapiinfo = Pa_GetHostApiInfo(devinfo->hostApi);

        LOG4CXX_DEBUG(narratorPaLog, "Opening device: " << devinfo->name << " (" << hostapiinfo->name << "), channels: " << channels << ", rate: " << rate <<" (" << devinfo->defaultSampleRate << ")");

        unsigned long framesPerBuffer = 1024;

#ifdef WIN32
        framesPerBuffer = 4096;
#endif

        mError = Pa_OpenStream(&pStream, NULL, &mOutputParameters, rate, framesPerBuffer,
                paNoFlag, pa_stream_callback, this);

        if(mError != paNoError) {
            LOG4CXX_ERROR(narratorPaLog, "Failed to open stream: " << Pa_GetErrorText(mError));
            return false;
        }

        mRate = rate;
        mChannels = channels;
        isOpen = true;
        isStarted = false;

        mError = Pa_SetStreamFinishedCallback(pStream, pa_stream_finished_callback);
        if(mError != paNoError) {
            LOG4CXX_ERROR(narratorPaLog, "Failed to set FinishedCallback: " << Pa_GetErrorText(mError));
        }

    }
    return true;
}

long PortAudio::stop()
{
    if(isStarted) {
        mError = Pa_StopStream(pStream);
        if(mError != paNoError)
            LOG4CXX_ERROR(narratorPaLog, "Failed to stop stream: " << Pa_GetErrorText(mError));

        ringbuf.flush();
        isStarted = false;
    }

    return mLatency;
}

long PortAudio::abort()
{
    if(isStarted) {
        LOG4CXX_DEBUG(narratorPaLog, "Aborting stream");
        mError = Pa_AbortStream(pStream);
        if(mError != paNoError)
            LOG4CXX_ERROR(narratorPaLog, "Failed to abort stream: " << Pa_GetErrorText(mError));

        ringbuf.flush();
        isStarted = false;
    }

    return mLatency;
}

bool PortAudio::close()
{
    stop();
    if(isOpen) {
        mError = Pa_CloseStream(pStream);
        if(mError != paNoError)
        {
            LOG4CXX_ERROR(narratorPaLog, "Failed to close stream: " << Pa_GetErrorText(mError));
        }
        else
        {
            isOpen = false;
        }
    }
    return true;
}

long PortAudio::getRemainingms()
{
    size_t bufferedData = ringbuf.getReadAvailable();

    long bufferms = 0;

    if(mChannels != 0 && mRate != 0)
        bufferms = (long) (1000.0 * bufferedData) / mChannels / mRate;

    return bufferms + mLatency;
}

/*
   Blocks until data can be written.
   If no data can be written in 2ms*100 the stream gets restarted.
   returns the amount of data that can be written.
*/
unsigned int PortAudio::getWriteAvailable()
{
    size_t writeAvailable = 0;
    int waitCount = 0;
    int failCount = 0;

    while( writeAvailable == 0 ) {
        writeAvailable = ringbuf.getWriteAvailable();

        if( writeAvailable == 0 ) {
            usleep(200000); //200 ms
            //LOG4CXX_DEBUG(narratorPaLog, "getWriteAvailable = 0, Pa_IsStreamActive = " << Pa_IsStreamActive(pStream));
        }

        if(waitCount++ > 10) {
            LOG4CXX_ERROR(narratorPaLog, "getWriteAvailable waittime exceeded, restarting stream");

            mError = Pa_AbortStream(pStream);
            // If abortstream fails, try reopening
            if(failCount++ > 0 || ( mError != paNoError && mError != paStreamIsStopped )) {
                LOG4CXX_ERROR(narratorPaLog, "Failed to abort stream, trying to close and reopen: " << Pa_GetErrorText(mError));

                mError = Pa_CloseStream(pStream);
                if(mError != paNoError)
                    LOG4CXX_ERROR(narratorPaLog, "Failed to close stream: " << Pa_GetErrorText(mError));

                isOpen = false;
                open(mRate, mChannels);
            }

            mError = Pa_StartStream(pStream);
            if(mError != paNoError) LOG4CXX_ERROR(narratorPaLog, "Failed to start stream: " << Pa_GetErrorText(mError));

            waitCount = 0;
        }
    }

    //LOG4CXX_DEBUG(narratorPaLog, "getWriteAvailable = " << writeAvailable << ", Pa_IsStreamActive = " << Pa_IsStreamActive(pStream));
    return writeAvailable;
}

bool PortAudio::write(float *buffer, unsigned int samples)
{
    size_t elemWritten = ringbuf.writeElements(buffer, samples*mChannels);

    // Try starting the stream
    if(!isStarted) {
        //LOG4CXX_INFO(narratorPaLog, "Starting stream");
        mError = Pa_StartStream(pStream);
        if(mError != paNoError) {
            LOG4CXX_ERROR(narratorPaLog, "Failed to start stream: " << Pa_GetErrorText(mError));
        }
        mLatency = (long) (Pa_GetStreamInfo(pStream)->outputLatency * 1000.0);
        isStarted = true;
    }

    if( elemWritten < samples)
        return false;

    return false;
}

long unsigned int min( long unsigned int a, long unsigned int b )
{
    if( a < b )
        return a;
    return b;
}

/*
   @note With the exception of Pa_GetStreamCpuLoad() it is not permissable to call
   PortAudio API functions from within the stream callback.
*/
int pa_stream_callback(
        const void *input,
        void *output, // Write frameCount*channels here
        unsigned long frameCount,
        const PaStreamCallbackTimeInfo* timeInfo,
        PaStreamCallbackFlags statusFlags,
        void *userData )
{
    (void) timeInfo;    /* Prevent unused variable warning. */
    (void) input; /* Prevent unused variable warning. */

    static long underrunms = 0;

    RingBuffer *ringbuf = &((PortAudio*)userData)->ringbuf;

    size_t availableElements = ringbuf->getReadAvailable();

    int channels = ((PortAudio*)userData)->mChannels;
    long rate = ((PortAudio*)userData)->mRate;
    size_t elementsToRead = min( frameCount * channels, availableElements);

    float* outbuf = (float*)output;

    size_t elementsRead = ringbuf->readElements(outbuf, elementsToRead);

    if( elementsRead < frameCount*channels ) {
        memset( (outbuf+(elementsRead*channels)), 0, (frameCount*channels-elementsRead)*sizeof(float) );
        underrunms += (long) (frameCount * 1000.0) / rate;
        //std::cout << __FUNCTION__ << " Less read than requested, underrun ms:" << underrunms << std::endl;
    } else {
        //std::cout << __FUNCTION__ << " availableElements: " << availableElements << " elementsToRead: " << elementsToRead << " elementsRead:" << elementsRead << std::endl;
        underrunms = 0;
    }

    if(underrunms > 500) return paComplete;

    return paContinue; // paAbort, paComplete
}

void pa_stream_finished_callback( void *userData )
{
    ((PortAudio*)userData)->isStarted = false;
    //std::cout << __FUNCTION__ << " Finished" << std::endl;
}
<|endoftext|>


<commit_before>/*
 * (C) Copyright 2015 ETH Zurich Systems Group (http://www.systems.ethz.ch/) and others.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * Contributors:
 *     Markus Pilman <mpilman@inf.ethz.ch>
 *     Simon Loesing <sloesing@inf.ethz.ch>
 *     Thomas Etter <etterth@gmail.com>
 *     Kevin Bocksrocker <kevin.bocksrocker@gmail.com>
 *     Lucas Braun <braunl@inf.ethz.ch>
 */

#include "CuckooHash.hpp"

#include <crossbow/logger.hpp>

namespace tell {
namespace store {

CuckooTable::CuckooTable(PageManager& pageManager)
    : mPageManager(pageManager)
      , hash1(ENTRIES_PER_PAGE)
      , hash2(ENTRIES_PER_PAGE)
      , hash3(ENTRIES_PER_PAGE)
      , mSize(0)
{
    mPages.reserve(3);
    for (size_t i = 0; i < 3; ++i) {
        mPages.emplace_back(reinterpret_cast<EntryT*>(pageManager.alloc()));
    }
}

CuckooTable::~CuckooTable() {
}

void CuckooTable::destroy() {
    for (auto p : mPages) {
        mPageManager.free(p);
    }
}

const void* CuckooTable::get(uint64_t key) const {
    unsigned cnt = 0;
    for (auto& hasher : {hash1, hash2, hash3}) {
        auto idx = hasher(key);
        const EntryT& entry = at(cnt, idx);
        if (entry.first == key) return entry.second;
        ++cnt;
    }
    return nullptr;
}

auto CuckooTable::at(unsigned h, size_t idx) const -> const EntryT& {
    auto tIdx = idx / ENTRIES_PER_PAGE;
    auto pIdx = idx - tIdx * ENTRIES_PER_PAGE;
    return mPages[3*tIdx+h][pIdx];
}

Modifier CuckooTable::modifier() {
    return Modifier(*this);
}

CuckooTable::CuckooTable(PageManager& pageManager,
                         std::vector<EntryT*>&& pages,
                         cuckoo_hash_function hash1,
                         cuckoo_hash_function hash2,
                         cuckoo_hash_function hash3,
                         size_t size)
    : mPageManager(pageManager), mPages(std::move(pages)), hash1(hash1), hash2(hash2), hash3(hash3), mSize(size) {
}

size_t CuckooTable::capacity() const {
    return ENTRIES_PER_PAGE * mPages.size();
}

Modifier::~Modifier() {
    if (mToDelete.empty()) {
        return;
    }

    auto pages = std::move(mToDelete);
    auto& pageManager = mTable.mPageManager;
    crossbow::allocator::invoke([pages, &pageManager]() {
        for (auto page : pages) {
            pageManager.free(page);
        }
    });
}

CuckooTable* Modifier::done() const {
    return crossbow::allocator::construct<CuckooTable>(mTable.mPageManager, std::move(mPages), hash1, hash2, hash3,
            mSize);
}

const void* Modifier::get(uint64_t key) const
{
    unsigned cnt = 0;
    for (auto& h : {hash1, hash2, hash3}) {
        size_t pageIdx;
        auto idx = h(key);
        auto& entry = at(cnt, idx, pageIdx);
        if (entry.first == key) {
            return entry.second;
            break;
        }
        ++cnt;
    }
    return nullptr;
}

bool Modifier::insert(uint64_t key, void* value, bool replace /*= false*/) {
    LOG_ASSERT(value != nullptr, "Value must not be null");

    // we first check, whether the value exists
    bool res = false;
    bool increment = true;
    unsigned cnt = 0;
    for (auto& h : {hash1, hash2, hash3}) {
        size_t pageIdx;
        auto idx = h(key);
        auto& entry = at(cnt, idx, pageIdx);
        if (entry.first == key && entry.second != nullptr) {
            if (replace) {
                auto e = &entry;
                if (cow(cnt, pageIdx)) {
                    e = &at(cnt, idx, pageIdx);
                }
                e->second = value;
                increment = false;
                res = true;
            }
            goto END;
        }
        ++cnt;
    }
    if (replace) {
        goto END;
    }
    // actual insert comes here
    while (true) {
        // we retry 20 times at the moment
        for (int i = 0; i < 20; ++i) {
            cnt = 0;
            for (auto& h : {hash1, hash2, hash3}) {
                size_t pageIdx;
                auto idx = h(key);
                auto& entry = at(cnt, idx, pageIdx);
                auto e = &entry;
                if (e->second == nullptr) {
                    if (cow(cnt, pageIdx)) {
                        e = &at(cnt, idx, pageIdx);
                    }
                    e->first = key;
                    e->second = value;
                    res = true;
                    goto END;
                } else {
                    assert(e->first != key);
                    if (cow(cnt, pageIdx)) {
                        e = &at(cnt, idx, pageIdx);
                    }
                    std::pair<uint64_t, void*> p = *e;
                    e->first = key;
                    e->second = value;
                    key = p.first;
                    value = p.second;
                }
                ++cnt;
            }
        }
        rehash();
    }
END:
    if (res && increment)
        ++mSize;
    return res;
}

bool Modifier::remove(uint64_t key) {
    size_t pIdx;
    unsigned cnt = 0;
    for (auto& hash : {hash1, hash2, hash3}) {
        auto idx = hash(key);
        auto& entry = at(cnt, idx, pIdx);
        auto e = &entry;
        if (e->first == key && e->second != nullptr) {
            if (cow(cnt, pIdx)) {
                e = &at(cnt, idx, pIdx);
            }
            e->second = nullptr;
            --mSize;
            return true;
        }
        ++cnt;
    }
    return false;
}

Modifier::EntryT& Modifier::at(unsigned h, size_t idx, size_t& pageIdx) {
    pageIdx = idx / ENTRIES_PER_PAGE;
    auto pIdx = idx - pageIdx * ENTRIES_PER_PAGE;
    return mPages[3*pageIdx + h][pIdx];
}

auto Modifier::at(unsigned h, size_t idx, size_t& pageIdx) const -> const EntryT&
{
    return const_cast<Modifier*>(this)->at(h, idx, pageIdx);
}

bool Modifier::cow(unsigned h, size_t idx) {
    if (pageWasModified[3*idx + h]) return false;
    pageWasModified[3*idx + h] = true;
    auto oldPage = mPages[3*idx + h];
    auto newPage = reinterpret_cast<EntryT*>(mTable.mPageManager.alloc());
    memcpy(newPage, oldPage, TELL_PAGE_SIZE);
    mToDelete.push_back(oldPage);
    mPages[3*idx + h] = newPage;
    return true;
}

void Modifier::rehash() {
    auto totalCapacity = mPages.size() * ENTRIES_PER_PAGE;
    auto numPages = mPages.size();
    // Double number of pages if usage is above 80 percent, halve if below 20 percent
    if (4 * mSize / 5 > totalCapacity) {
        numPages *= 2;
    } else if (numPages > 3 && mSize / 5 < totalCapacity) {
        numPages /= 2;
    }
    LOG_ASSERT(numPages >= 3, "Need at least 3 pages");
    LOG_ASSERT(numPages % 3 == 0, "Number of pages must be divisible by 3");

    // Allocate pages
    std::vector<EntryT*> oldPages;
    oldPages.swap(mPages);
    mPages.reserve(numPages);
    for (decltype(numPages) i = 0; i < numPages; ++i) {
        mPages.emplace_back(reinterpret_cast<EntryT*>(mTable.mPageManager.alloc()));
    }

    // Allocate hash functions
    auto hashCapacity = (numPages / 3) * ENTRIES_PER_PAGE;
    LOG_ASSERT(isPowerOf2(hashCapacity), "Hash capacity must be power of 2");
    hash1 = cuckoo_hash_function(hashCapacity);
    hash2 = cuckoo_hash_function(hashCapacity);
    hash3 = cuckoo_hash_function(hashCapacity);

    // Copy elements from old pages into new pages
    for (decltype(oldPages.size()) i = 0; i < oldPages.size(); ++i) {
        auto page = oldPages[i];
        for (size_t j = 0; j < ENTRIES_PER_PAGE; ++j) {
            if (page[j].second != nullptr)
                insert(page[j].first, page[j].second);
        }
        // If the page was modified it can be freed immediately (only the modifier had access to it)
        if (pageWasModified[i]) {
            mTable.mPageManager.free(page);
        } else {
            mToDelete.push_back(page);
            pageWasModified[i] = true;
        }
    }
    pageWasModified.resize(numPages, true);
}

size_t Modifier::capacity() const {
    return mPages.size() * ENTRIES_PER_PAGE;
}

size_t Modifier::size() const {
    return mSize;
}
} // namespace store
} // namespace tell
<commit_msg>Fix wrong page modification status information<commit_after>/*
 * (C) Copyright 2015 ETH Zurich Systems Group (http://www.systems.ethz.ch/) and others.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * Contributors:
 *     Markus Pilman <mpilman@inf.ethz.ch>
 *     Simon Loesing <sloesing@inf.ethz.ch>
 *     Thomas Etter <etterth@gmail.com>
 *     Kevin Bocksrocker <kevin.bocksrocker@gmail.com>
 *     Lucas Braun <braunl@inf.ethz.ch>
 */

#include "CuckooHash.hpp"

#include <crossbow/logger.hpp>

namespace tell {
namespace store {

CuckooTable::CuckooTable(PageManager& pageManager)
    : mPageManager(pageManager)
      , hash1(ENTRIES_PER_PAGE)
      , hash2(ENTRIES_PER_PAGE)
      , hash3(ENTRIES_PER_PAGE)
      , mSize(0)
{
    mPages.reserve(3);
    for (size_t i = 0; i < 3; ++i) {
        mPages.emplace_back(reinterpret_cast<EntryT*>(pageManager.alloc()));
    }
}

CuckooTable::~CuckooTable() {
}

void CuckooTable::destroy() {
    for (auto p : mPages) {
        mPageManager.free(p);
    }
}

const void* CuckooTable::get(uint64_t key) const {
    unsigned cnt = 0;
    for (auto& hasher : {hash1, hash2, hash3}) {
        auto idx = hasher(key);
        const EntryT& entry = at(cnt, idx);
        if (entry.first == key) return entry.second;
        ++cnt;
    }
    return nullptr;
}

auto CuckooTable::at(unsigned h, size_t idx) const -> const EntryT& {
    auto tIdx = idx / ENTRIES_PER_PAGE;
    auto pIdx = idx - tIdx * ENTRIES_PER_PAGE;
    return mPages[3*tIdx+h][pIdx];
}

Modifier CuckooTable::modifier() {
    return Modifier(*this);
}

CuckooTable::CuckooTable(PageManager& pageManager,
                         std::vector<EntryT*>&& pages,
                         cuckoo_hash_function hash1,
                         cuckoo_hash_function hash2,
                         cuckoo_hash_function hash3,
                         size_t size)
    : mPageManager(pageManager), mPages(std::move(pages)), hash1(hash1), hash2(hash2), hash3(hash3), mSize(size) {
}

size_t CuckooTable::capacity() const {
    return ENTRIES_PER_PAGE * mPages.size();
}

Modifier::~Modifier() {
    if (mToDelete.empty()) {
        return;
    }

    auto pages = std::move(mToDelete);
    auto& pageManager = mTable.mPageManager;
    crossbow::allocator::invoke([pages, &pageManager]() {
        for (auto page : pages) {
            pageManager.free(page);
        }
    });
}

CuckooTable* Modifier::done() const {
    return crossbow::allocator::construct<CuckooTable>(mTable.mPageManager, std::move(mPages), hash1, hash2, hash3,
            mSize);
}

const void* Modifier::get(uint64_t key) const
{
    unsigned cnt = 0;
    for (auto& h : {hash1, hash2, hash3}) {
        size_t pageIdx;
        auto idx = h(key);
        auto& entry = at(cnt, idx, pageIdx);
        if (entry.first == key) {
            return entry.second;
            break;
        }
        ++cnt;
    }
    return nullptr;
}

bool Modifier::insert(uint64_t key, void* value, bool replace /*= false*/) {
    LOG_ASSERT(value != nullptr, "Value must not be null");

    // we first check, whether the value exists
    bool res = false;
    bool increment = true;
    unsigned cnt = 0;
    for (auto& h : {hash1, hash2, hash3}) {
        size_t pageIdx;
        auto idx = h(key);
        auto& entry = at(cnt, idx, pageIdx);
        if (entry.first == key && entry.second != nullptr) {
            if (replace) {
                auto e = &entry;
                if (cow(cnt, pageIdx)) {
                    e = &at(cnt, idx, pageIdx);
                }
                e->second = value;
                increment = false;
                res = true;
            }
            goto END;
        }
        ++cnt;
    }
    if (replace) {
        goto END;
    }
    // actual insert comes here
    while (true) {
        // we retry 20 times at the moment
        for (int i = 0; i < 20; ++i) {
            cnt = 0;
            for (auto& h : {hash1, hash2, hash3}) {
                size_t pageIdx;
                auto idx = h(key);
                auto& entry = at(cnt, idx, pageIdx);
                auto e = &entry;
                if (e->second == nullptr) {
                    if (cow(cnt, pageIdx)) {
                        e = &at(cnt, idx, pageIdx);
                    }
                    e->first = key;
                    e->second = value;
                    res = true;
                    goto END;
                } else {
                    assert(e->first != key);
                    if (cow(cnt, pageIdx)) {
                        e = &at(cnt, idx, pageIdx);
                    }
                    std::pair<uint64_t, void*> p = *e;
                    e->first = key;
                    e->second = value;
                    key = p.first;
                    value = p.second;
                }
                ++cnt;
            }
        }
        rehash();
    }
END:
    if (res && increment)
        ++mSize;
    return res;
}

bool Modifier::remove(uint64_t key) {
    size_t pIdx;
    unsigned cnt = 0;
    for (auto& hash : {hash1, hash2, hash3}) {
        auto idx = hash(key);
        auto& entry = at(cnt, idx, pIdx);
        auto e = &entry;
        if (e->first == key && e->second != nullptr) {
            if (cow(cnt, pIdx)) {
                e = &at(cnt, idx, pIdx);
            }
            e->second = nullptr;
            --mSize;
            return true;
        }
        ++cnt;
    }
    return false;
}

Modifier::EntryT& Modifier::at(unsigned h, size_t idx, size_t& pageIdx) {
    pageIdx = idx / ENTRIES_PER_PAGE;
    auto pIdx = idx - pageIdx * ENTRIES_PER_PAGE;
    return mPages[3*pageIdx + h][pIdx];
}

auto Modifier::at(unsigned h, size_t idx, size_t& pageIdx) const -> const EntryT&
{
    return const_cast<Modifier*>(this)->at(h, idx, pageIdx);
}

bool Modifier::cow(unsigned h, size_t idx) {
    if (pageWasModified[3*idx + h]) return false;
    pageWasModified[3*idx + h] = true;
    auto oldPage = mPages[3*idx + h];
    auto newPage = reinterpret_cast<EntryT*>(mTable.mPageManager.alloc());
    memcpy(newPage, oldPage, TELL_PAGE_SIZE);
    mToDelete.push_back(oldPage);
    mPages[3*idx + h] = newPage;
    return true;
}

void Modifier::rehash() {
    auto totalCapacity = mPages.size() * ENTRIES_PER_PAGE;
    auto numPages = mPages.size();
    // Double number of pages if usage is above 80 percent, halve if below 20 percent
    if (4 * mSize / 5 > totalCapacity) {
        numPages *= 2;
    } else if (numPages > 3 && mSize / 5 < totalCapacity) {
        numPages /= 2;
    }
    LOG_ASSERT(numPages >= 3, "Need at least 3 pages");
    LOG_ASSERT(numPages % 3 == 0, "Number of pages must be divisible by 3");

    // Allocate pages
    std::vector<EntryT*> oldPages;
    oldPages.swap(mPages);
    mPages.reserve(numPages);
    for (decltype(numPages) i = 0; i < numPages; ++i) {
        mPages.emplace_back(reinterpret_cast<EntryT*>(mTable.mPageManager.alloc()));
    }

    std::vector<bool> oldPageWasModified(numPages, true);
    oldPageWasModified.swap(pageWasModified);

    // Allocate hash functions
    auto hashCapacity = (numPages / 3) * ENTRIES_PER_PAGE;
    LOG_ASSERT(isPowerOf2(hashCapacity), "Hash capacity must be power of 2");
    hash1 = cuckoo_hash_function(hashCapacity);
    hash2 = cuckoo_hash_function(hashCapacity);
    hash3 = cuckoo_hash_function(hashCapacity);

    // Copy elements from old pages into new pages
    for (decltype(oldPages.size()) i = 0; i < oldPages.size(); ++i) {
        auto page = oldPages[i];
        for (size_t j = 0; j < ENTRIES_PER_PAGE; ++j) {
            if (page[j].second != nullptr)
                insert(page[j].first, page[j].second);
        }
        // If the page was modified it can be freed immediately (only the modifier had access to it)
        if (oldPageWasModified[i]) {
            mTable.mPageManager.free(page);
        } else {
            mToDelete.push_back(page);
        }
    }
}

size_t Modifier::capacity() const {
    return mPages.size() * ENTRIES_PER_PAGE;
}

size_t Modifier::size() const {
    return mSize;
}
} // namespace store
} // namespace tell
<|endoftext|>


<commit_before><commit_msg>Added Rainback.TextEdit, and cleaned up other widget funcs<commit_after><|endoftext|>