<commit_before>// Copyright Hugh Perkins (hughperkins at gmail), Josef Moudrik 2015
//
// 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 <iostream>
//#include <algorithm>

#include "DeepCL.h"
#include "loss/SoftMaxLayer.h"
//#include "test/Sampler.h"

using namespace std;

/* [[[cog
    # These are used in the later cog sections in this file:
    # format:
    # ( name, type, description, default, ispublicapi )
    options = [
        ('gpuIndex', 'int', 'gpu device index; default value is gpu if present, cpu otw.', -1, True),

        ('weightsFile', 'string', 'file to read weights from','weights.dat', True),
        # removing loadondemand for now, let's always load exactly one batch at a time for now
        # ('loadOnDemand', 'int', 'load data on demand [1|0]', 0, True),
        ('batchSize', 'int', 'batch size', 128, True),

        # lets go with pipe for now, and then somehow shoehorn files in later?
        ('inputFile',  'string', 'file to read inputs from, if empty, read stdin (default)', '', False),
        ('outputFile', 'string', 'file to write outputs to, if empty, write to stdout', '', False),
        ('writeIntLabels', 'int', 'write integer labels, instead of probabilities etc (default 0)', 0, False)
        # ('outputFormat', 'string', 'output format [binary|text]', 'text', False)
    ]
*///]]]
// [[[end]]]

class Config {
public:
    /* [[[cog
        cog.outl('// generated using cog:')
        for (name,type,description,default,_) in options:
            cog.outl( type + ' ' + name + ';')
    */// ]]]
    // generated using cog:
    int gpuIndex;
    string weightsFile;
    int batchSize;
    string inputFile;
    string outputFile;
    int writeIntLabels;
    // [[[end]]]

    Config() {
        /* [[[cog
            cog.outl('// generated using cog:')
            for (name,type,description,default,_) in options:
                defaultString = ''
                if type == 'string':
                    defaultString = '"' + default + '"'
                elif type == 'int':
                    defaultString = str(default)
                elif type == 'float':
                    defaultString = str(default)
                    if '.' not in defaultString:
                        defaultString += '.0'
                    defaultString += 'f'
                cog.outl( name + ' = ' + defaultString + ';')
        */// ]]]
        // generated using cog:
        gpuIndex = -1;
        weightsFile = "weights.dat";
        batchSize = 128;
        inputFile = "";
        outputFile = "";
        writeIntLabels = 0;
        // [[[end]]]
    }
};

void go(Config config) {
    int N = -1;
    int numPlanes;
    int imageSize;
    int imageSizeCheck;
    if( config.inputFile == "" ) {
        int dims[3];
        cin.read( reinterpret_cast< char * >( dims ), 3 * 4l );
        numPlanes = dims[0];
        imageSize = dims[1];
        imageSizeCheck = dims[2];
        if( imageSize != imageSizeCheck ) {
            throw std::runtime_error( "imageSize doesnt match imageSizeCheck, image not square" );
        }
    } else {
        GenericLoader::getDimensions( config.inputFile, &N, &numPlanes, &imageSize );
    }
//    cout << "planes " << numPlanes << " size " << imageSize << " sizecheck " << imageSizeCheck << endl;

    const long inputCubeSize = numPlanes * imageSize * imageSize ;

    //
    // ## Set up the Network
    //

    EasyCL *cl = 0;
    if( config.gpuIndex >= 0 ) {
        cl = EasyCL::createForIndexedGpu( config.gpuIndex, false );
    } else {
        cl = EasyCL::createForFirstGpuOtherwiseCpu(false);
    }

    NeuralNet *net;
    net = new NeuralNet(cl);

    // just use the default for net creation, weights are overriden from the weightsFile
    WeightsInitializer *weightsInitializer = new OriginalInitializer();

    if( config.weightsFile == "" ) {
        cout << "weightsFile not specified" << endl;
        return;
    }

    string netDef;
    if ( !WeightsPersister::loadConfigString( config.weightsFile, netDef ) ){
        cout << "Cannot load network definition from weightsFile." << endl;
        return;
    }
//    cout << "net def from weights file: " << netDef << endl;

    net->addLayer( InputLayerMaker::instance()->numPlanes(numPlanes)->imageSize(imageSize) );
    net->addLayer( NormalizationLayerMaker::instance()->translate( 0.0f )->scale( 1.0f ) ); // This will be read from weights file

    if( !NetdefToNet::createNetFromNetdef( net, netDef, weightsInitializer ) ) {
        return;
    }

    // ignored int and float, s.t. we can use loadWeights
    int ignI;
    float ignF;

    // weights file contains normalization layer parameters as 'weights' now.  We should probably rename weights to parameters
    // sooner or later ,but anyway, tehcnically, works for onw
    if( !WeightsPersister::loadWeights( config.weightsFile, string("netDef=")+netDef, net, &ignI, &ignI, &ignF, &ignI, &ignF ) ){
        cout << "Cannot load network weights from weightsFile." << endl;
        return;
    }

    //net->print();
    net->setBatchSize(config.batchSize);
//    cout << "batchSize: " << config.batchSize << endl;

    //
    // ## All is set up now
    //

    // ideally, this could go in GenericReader somehow I reckon, but putting it here is ok for now :-)   - Hugh
    float *inputData = new float[ inputCubeSize * config.batchSize];

// jm: this makes it impossible to select iofiles outside the datadir,
//     which we should be able to do
//
//
//    if( config.dataDir != "" ) {
//        config.inputFile = config.dataDir + "/" + config.inputFile;
//        config.outputFile = config.dataDir + "/" + config.outputFile;
//    }
//    cout << "Reading inputs from:  '" << config.inputFile << "'" << endl;
//    cout << "Writing outputs to: '" << config.outputFile << "'" << endl;
//    ifstream fin(config.inputFile, ios::in | ios::binary);
//    ofstream fout(config.outputFile, ios::out | ios::binary);

//    if( ! fin ){
//        cout << "Cannot open input file: '"<< config.inputFile <<"'" << endl;
//        return;
//    }

//    if( ! fout ){
//        cout << "Cannot open output file: '"<< config.outputFile <<"'" << endl;
//        return;
//    }

//    cout << "Input cube size is: " << inputCubeSize * 4 << " B" << endl;
//    cout << "Output image size is: " << net->getOutputCubeSize() * 4 << " B" << endl;

    int *labels = new int[config.batchSize];
    int n = 0;
    bool more = true;
    if( config.inputFile == "" ) {
        #ifdef _WIN32
        // refs:
        // http://www.thecodingforums.com/threads/binary-output-to-stdout-in-windows.317367/
        // http://www.cplusplus.com/forum/windows/77812/
        _setmode( _fileno( stdout ), _O_BINARY );
        #endif
        cin.read( reinterpret_cast< char * >( inputData ), inputCubeSize * config.batchSize * 4l );
        more = cin;
    } else {
        // pass 0 for labels, and this will cause GenericLoader to simply not try to load any labels
        // now, after modifying GenericLoader to have this new behavior
        GenericLoader::load( config.inputFile, inputData, 0, n, config.batchSize );
    }
    while( more ) {
//        fin.read( reinterpret_cast<char *>(inputData), config.batchSize * inputCubeSize * 4);
//        FileHelper::readBinaryChunk( reinterpret_cast<char *>(inputData), config.inputFile, (long)n * inputCubeSize * 4, (long)inputCubeSize * config.batchSize * 4 );
//        if( !fin ){
//            break;
//        }

//    	cout << "Read " << config.batchSize << " input cubes." << endl;

        net->forward(inputData);  // seems ok...   - Hugh

        if( !config.writeIntLabels ) {
//            FileHelper::writeBinaryChunk( config.outputFile, reinterpret_cast<const char *>(net->getOutput()),
//                n * 4 * net->getOutputCubeSize(),
//                config.batchSize * 4 * net->getOutputCubeSize() );
//            fout.write( reinterpret_cast<const char *>(net->getOutput()), net->getOutputSize() * 4 * config.batchSize);
            cout.write( reinterpret_cast<const char *>(net->getOutput()), net->getOutputSize() * 4 * config.batchSize);
        } else {
            // calculate the labels somehow...
            // ... added 'getLabels' to SoftMaxLayer
            SoftMaxLayer *softMaxLayer = dynamic_cast< SoftMaxLayer *>(net->getLastLayer() );
            if( softMaxLayer == 0 ) {
                cout << "must have softmaxlayer as last layer, if want to output labels" << endl;
                return;
            }
            softMaxLayer->getLabels(labels);
//            fout.write( reinterpret_cast<const char *>(labels), config.batchSize * 4);
//            if( n == 0 ) {
//                for( int i = 0; i < config.batchSize / 4; i++ ) {
//                    cout << "out[" << i << "]=" << labels[i] << endl;
//                }
//            }
            cout.write( reinterpret_cast< char * >( labels ), config.batchSize * 4l );
//            FileHelper::writeBinaryChunk( config.outputFile, reinterpret_cast<const char *>(labels), n * 4, config.batchSize * 4);
        }
        n += config.batchSize;
//        if( ( n + config.batchSize > totalN ) && ( n != totalN ) ) {
//            cout << "breaking prematurely, since file is not an exact multiple of batchsize, and we didnt handle this yet" << endl;
//            break;
//        }
//        if( !fout ){
//            break;
//        }

//	fout.flush();
//        cout << "Written output image." << endl;
        n += config.batchSize;
        if( config.inputFile == "" ) {
            cin.read( reinterpret_cast< char * >( inputData ), inputCubeSize * config.batchSize * 4l );
            more = cin;
        } else {
            if( n + config.batchSize < N ) {
                GenericLoader::load( config.inputFile, inputData, 0, n, config.batchSize );
            } else {
                more = false;
                if( n != N ) {
                    cout << "breaking prematurely, since file is not an exact multiple of batchsize, and we didnt handle this yet" << endl;
                }
            }
        }
    }

//    cout << "Exiting." << endl;

    delete[] inputData;
    delete[] labels;
    delete weightsInitializer;
    delete net;
    delete cl;
}

void printUsage( char *argv[], Config config ) {
    cout << "Usage: " << argv[0] << " [key]=[value] [[key]=[value]] ..." << endl;
    cout << endl;
    cout << "Possible key=value pairs:" << endl;
    /* [[[cog
        cog.outl('// generated using cog:')
        cog.outl('cout << "public api, shouldnt change within major version:" << endl;')
        for (name,type,description,_, is_public_api) in options:
            if is_public_api:
                cog.outl( 'cout << "    ' + name.lower() + '=[' + description + '] (" << config.' + name + ' << ")" << endl;')
        cog.outl('cout << "" << endl; ')
        cog.outl('cout << "unstable, might change within major version:" << endl; ')
        for (name,type,description,_, is_public_api) in options:
            if not is_public_api:
                cog.outl( 'cout << "    ' + name.lower() + '=[' + description + '] (" << config.' + name + ' << ")" << endl;')
    *///]]]
    // generated using cog:
    cout << "public api, shouldnt change within major version:" << endl;
    cout << "    gpuindex=[gpu device index; default value is gpu if present, cpu otw.] (" << config.gpuIndex << ")" << endl;
    cout << "    weightsfile=[file to read weights from] (" << config.weightsFile << ")" << endl;
    cout << "    batchsize=[batch size] (" << config.batchSize << ")" << endl;
    cout << "" << endl;
    cout << "unstable, might change within major version:" << endl;
    cout << "    inputfile=[file to read inputs from, if empty, read stdin (default)] (" << config.inputFile << ")" << endl;
    cout << "    outputfile=[file to write outputs to, if empty, write to stdout] (" << config.outputFile << ")" << endl;
    cout << "    writeintlabels=[write integer labels, instead of probabilities etc (default 0)] (" << config.writeIntLabels << ")" << endl;
    // [[[end]]]
}

int main( int argc, char *argv[] ) {
    Config config;
    if( argc == 2 && ( string(argv[1]) == "--help" || string(argv[1]) == "--?" || string(argv[1]) == "-?" || string(argv[1]) == "-h" ) ) {
        printUsage( argv, config );
    }
    for( int i = 1; i < argc; i++ ) {
        vector<string> splitkeyval = split( argv[i], "=" );
        if( splitkeyval.size() != 2 ) {
          cout << "Usage: " << argv[0] << " [key]=[value] [[key]=[value]] ..." << endl;
          exit(1);
        } else {
            string key = splitkeyval[0];
            string value = splitkeyval[1];
//            cout << "key [" << key << "]" << endl;
            /* [[[cog
                cog.outl('// generated using cog:')
                cog.outl('if( false ) {')
                for (name,type,description,_,_) in options:
                    cog.outl( '} else if( key == "' + name.lower() + '" ) {')
                    converter = '';
                    if type == 'int':
                        converter = 'atoi';
                    elif type == 'float':
                        converter = 'atof';
                    cog.outl( '    config.' + name + ' = ' + converter + '(value);')
            */// ]]]
            // generated using cog:
            if( false ) {
            } else if( key == "gpuindex" ) {
                config.gpuIndex = atoi(value);
            } else if( key == "weightsfile" ) {
                config.weightsFile = (value);
            } else if( key == "batchsize" ) {
                config.batchSize = atoi(value);
            } else if( key == "inputfile" ) {
                config.inputFile = (value);
            } else if( key == "outputfile" ) {
                config.outputFile = (value);
            } else if( key == "writeintlabels" ) {
                config.writeIntLabels = atoi(value);
            // [[[end]]]
            } else {
                cout << endl;
                cout << "Error: key '" << key << "' not recognised" << endl;
                cout << endl;
                printUsage( argv, config );
                cout << endl;
                return -1;
            }
        }
    }
    try {
        go( config );
    } catch( runtime_error e ) {
        cout << "Something went wrong: " << e.what() << endl;
        return -1;
    }
}

<commit_msg>output file can be stdout or file now; and can be binary or text; for predict<commit_after>// Copyright Hugh Perkins (hughperkins at gmail), Josef Moudrik 2015
//
// 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 "DeepCL.h"
#include "loss/SoftMaxLayer.h"

using namespace std;

/* [[[cog
    # These are used in the later cog sections in this file:
    # format:
    # ( name, type, description, default, ispublicapi )
    options = [
        ('gpuIndex', 'int', 'gpu device index; default value is gpu if present, cpu otw.', -1, True),

        ('weightsFile', 'string', 'file to read weights from','weights.dat', True),
        # removing loadondemand for now, let's always load exactly one batch at a time for now
        # ('loadOnDemand', 'int', 'load data on demand [1|0]', 0, True),
        ('batchSize', 'int', 'batch size', 128, True),

        # lets go with pipe for now, and then somehow shoehorn files in later?
        ('inputFile',  'string', 'file to read inputs from, if empty, read stdin (default)', '', False),
        ('outputFile', 'string', 'file to write outputs to, if empty, write to stdout', '', False),
        ('writeLabels', 'int', 'write integer labels, instead of probabilities etc (default 0)', 0, False),
        ('outputFormat', 'string', 'output format [binary|text]', 'text', False)
    ]
*///]]]
// [[[end]]]

class Config {
public:
    /* [[[cog
        cog.outl('// generated using cog:')
        for (name,type,description,default,_) in options:
            cog.outl( type + ' ' + name + ';')
    */// ]]]
    // generated using cog:
    int gpuIndex;
    string weightsFile;
    int batchSize;
    string inputFile;
    string outputFile;
    int writeLabels;
    string outputFormat;
    // [[[end]]]

    Config() {
        /* [[[cog
            cog.outl('// generated using cog:')
            for (name,type,description,default,_) in options:
                defaultString = ''
                if type == 'string':
                    defaultString = '"' + default + '"'
                elif type == 'int':
                    defaultString = str(default)
                elif type == 'float':
                    defaultString = str(default)
                    if '.' not in defaultString:
                        defaultString += '.0'
                    defaultString += 'f'
                cog.outl( name + ' = ' + defaultString + ';')
        */// ]]]
        // generated using cog:
        gpuIndex = -1;
        weightsFile = "weights.dat";
        batchSize = 128;
        inputFile = "";
        outputFile = "";
        writeLabels = 0;
        outputFormat = "text";
        // [[[end]]]
    }
};

void go(Config config) {
    int N = -1;
    int numPlanes;
    int imageSize;
    int imageSizeCheck;
    if( config.inputFile == "" ) {
        int dims[3];
        cin.read( reinterpret_cast< char * >( dims ), 3 * 4l );
        numPlanes = dims[0];
        imageSize = dims[1];
        imageSizeCheck = dims[2];
        if( imageSize != imageSizeCheck ) {
            throw std::runtime_error( "imageSize doesnt match imageSizeCheck, image not square" );
        }
    } else {
        GenericLoader::getDimensions( config.inputFile, &N, &numPlanes, &imageSize );
    }
//    cout << "planes " << numPlanes << " size " << imageSize << " sizecheck " << imageSizeCheck << endl;

    const long inputCubeSize = numPlanes * imageSize * imageSize ;

    //
    // ## Set up the Network
    //

    EasyCL *cl = 0;
    if( config.gpuIndex >= 0 ) {
        cl = EasyCL::createForIndexedGpu( config.gpuIndex, false );
    } else {
        cl = EasyCL::createForFirstGpuOtherwiseCpu(false);
    }

    NeuralNet *net;
    net = new NeuralNet(cl);

    // just use the default for net creation, weights are overriden from the weightsFile
    WeightsInitializer *weightsInitializer = new OriginalInitializer();

    if( config.weightsFile == "" ) {
        cout << "weightsFile not specified" << endl;
        return;
    }

    string netDef;
    if ( !WeightsPersister::loadConfigString( config.weightsFile, netDef ) ){
        cout << "Cannot load network definition from weightsFile." << endl;
        return;
    }
//    cout << "net def from weights file: " << netDef << endl;

    net->addLayer( InputLayerMaker::instance()->numPlanes(numPlanes)->imageSize(imageSize) );
    net->addLayer( NormalizationLayerMaker::instance()->translate( 0.0f )->scale( 1.0f ) ); // This will be read from weights file

    if( !NetdefToNet::createNetFromNetdef( net, netDef, weightsInitializer ) ) {
        return;
    }

    // ignored int and float, s.t. we can use loadWeights
    int ignI;
    float ignF;

    // weights file contains normalization layer parameters as 'weights' now.  We should probably rename weights to parameters
    // sooner or later ,but anyway, tehcnically, works for onw
    if( !WeightsPersister::loadWeights( config.weightsFile, string("netDef=")+netDef, net, &ignI, &ignI, &ignF, &ignI, &ignF ) ){
        cout << "Cannot load network weights from weightsFile." << endl;
        return;
    }

    //net->print();
    net->setBatchSize(config.batchSize);
//    cout << "batchSize: " << config.batchSize << endl;

    //
    // ## All is set up now
    //

    float *inputData = new float[ inputCubeSize * config.batchSize];

    int *labels = new int[config.batchSize];
    int n = 0;
    bool more = true;
    if( config.inputFile == "" ) {
        #ifdef _WIN32
        // refs:
        // http://www.thecodingforums.com/threads/binary-output-to-stdout-in-windows.317367/
        // http://www.cplusplus.com/forum/windows/77812/
        _setmode( _fileno( stdout ), _O_BINARY );
        #endif
        cin.read( reinterpret_cast< char * >( inputData ), inputCubeSize * config.batchSize * 4l );
        more = cin;
    } else {
        // pass 0 for labels, and this will cause GenericLoader to simply not try to load any labels
        // now, after modifying GenericLoader to have this new behavior
        GenericLoader::load( config.inputFile, inputData, 0, n, config.batchSize );
    }
    ostream *outFile = 0;
    if( config.outputFile == "" ) {
        outFile = &cout;
    } else {
        if( config.outputFormat == "text" ) {
            outFile = new ofstream( config.outputFile, ios::out );
        } else {
            outFile = new ofstream( config.outputFile, ios::out | std::ios::binary );
        }
    }
    while( more ) {
        net->forward(inputData);

        if( !config.writeLabels ) {
            if( config.outputFormat == "text" ) {
                float const*output = net->getOutput();
                const int numFields = net->getLastLayer()->getOutputCubeSize();
                for( int i = 0; i < config.batchSize; i++ ) {
                    for( int f = 0; f < numFields; f++ ) {
                        if( f > 0 ) {
                            *outFile << " ";
                        }
                        *outFile << output[ i * numFields + f ];
                    }
                }
                *outFile << "\n";
            } else {
                outFile->write( reinterpret_cast<const char *>(net->getOutput()), net->getOutputSize() * 4 * config.batchSize);
            }
        } else {
            SoftMaxLayer *softMaxLayer = dynamic_cast< SoftMaxLayer *>(net->getLastLayer() );
            if( softMaxLayer == 0 ) {
                cout << "must have softmaxlayer as last layer, if want to output labels" << endl;
                return;
            }
            softMaxLayer->getLabels(labels);
            if( config.outputFormat == "text" ) {
                for( int i = 0; i < config.batchSize; i++ ) {
                    *outFile << labels[i] << "\n";
                }
            } else {
                outFile->write( reinterpret_cast< char * >( labels ), config.batchSize * 4l );
            }
            outFile->flush();
        }
        n += config.batchSize;
        n += config.batchSize;
        if( config.inputFile == "" ) {
            cin.read( reinterpret_cast< char * >( inputData ), inputCubeSize * config.batchSize * 4l );
            more = cin;
        } else {
            if( n + config.batchSize < N ) {
                GenericLoader::load( config.inputFile, inputData, 0, n, config.batchSize );
            } else {
                more = false;
                if( n != N ) {
                    cout << "breaking prematurely, since file is not an exact multiple of batchsize, and we didnt handle this yet" << endl;
                }
            }
        }
    }
    if( config.outputFile != "" ) {
        delete outFile;
    }

    delete[] inputData;
    delete[] labels;
    delete weightsInitializer;
    delete net;
    delete cl;
}

void printUsage( char *argv[], Config config ) {
    cout << "Usage: " << argv[0] << " [key]=[value] [[key]=[value]] ..." << endl;
    cout << endl;
    cout << "Possible key=value pairs:" << endl;
    /* [[[cog
        cog.outl('// generated using cog:')
        cog.outl('cout << "public api, shouldnt change within major version:" << endl;')
        for (name,type,description,_, is_public_api) in options:
            if is_public_api:
                cog.outl( 'cout << "    ' + name.lower() + '=[' + description + '] (" << config.' + name + ' << ")" << endl;')
        cog.outl('cout << "" << endl; ')
        cog.outl('cout << "unstable, might change within major version:" << endl; ')
        for (name,type,description,_, is_public_api) in options:
            if not is_public_api:
                cog.outl( 'cout << "    ' + name.lower() + '=[' + description + '] (" << config.' + name + ' << ")" << endl;')
    *///]]]
    // generated using cog:
    cout << "public api, shouldnt change within major version:" << endl;
    cout << "    gpuindex=[gpu device index; default value is gpu if present, cpu otw.] (" << config.gpuIndex << ")" << endl;
    cout << "    weightsfile=[file to read weights from] (" << config.weightsFile << ")" << endl;
    cout << "    batchsize=[batch size] (" << config.batchSize << ")" << endl;
    cout << "" << endl;
    cout << "unstable, might change within major version:" << endl;
    cout << "    inputfile=[file to read inputs from, if empty, read stdin (default)] (" << config.inputFile << ")" << endl;
    cout << "    outputfile=[file to write outputs to, if empty, write to stdout] (" << config.outputFile << ")" << endl;
    cout << "    writelabels=[write integer labels, instead of probabilities etc (default 0)] (" << config.writeLabels << ")" << endl;
    cout << "    outputformat=[output format [binary|text]] (" << config.outputFormat << ")" << endl;
    // [[[end]]]
}

int main( int argc, char *argv[] ) {
    Config config;
    if( argc == 2 && ( string(argv[1]) == "--help" || string(argv[1]) == "--?" || string(argv[1]) == "-?" || string(argv[1]) == "-h" ) ) {
        printUsage( argv, config );
    }
    for( int i = 1; i < argc; i++ ) {
        vector<string> splitkeyval = split( argv[i], "=" );
        if( splitkeyval.size() != 2 ) {
          cout << "Usage: " << argv[0] << " [key]=[value] [[key]=[value]] ..." << endl;
          exit(1);
        } else {
            string key = splitkeyval[0];
            string value = splitkeyval[1];
//            cout << "key [" << key << "]" << endl;
            /* [[[cog
                cog.outl('// generated using cog:')
                cog.outl('if( false ) {')
                for (name,type,description,_,_) in options:
                    cog.outl( '} else if( key == "' + name.lower() + '" ) {')
                    converter = '';
                    if type == 'int':
                        converter = 'atoi';
                    elif type == 'float':
                        converter = 'atof';
                    cog.outl( '    config.' + name + ' = ' + converter + '(value);')
            */// ]]]
            // generated using cog:
            if( false ) {
            } else if( key == "gpuindex" ) {
                config.gpuIndex = atoi(value);
            } else if( key == "weightsfile" ) {
                config.weightsFile = (value);
            } else if( key == "batchsize" ) {
                config.batchSize = atoi(value);
            } else if( key == "inputfile" ) {
                config.inputFile = (value);
            } else if( key == "outputfile" ) {
                config.outputFile = (value);
            } else if( key == "writelabels" ) {
                config.writeLabels = atoi(value);
            } else if( key == "outputformat" ) {
                config.outputFormat = (value);
            // [[[end]]]
            } else {
                cout << endl;
                cout << "Error: key '" << key << "' not recognised" << endl;
                cout << endl;
                printUsage( argv, config );
                cout << endl;
                return -1;
            }
        }
    }
    try {
        go( config );
    } catch( runtime_error e ) {
        cout << "Something went wrong: " << e.what() << endl;
        return -1;
    }
}

<|endoftext|>


<commit_before>///////////////////////////////////////////////////////////////////////////////
// Copyright (c) Lewis Baker
// Licenced under MIT license. See LICENSE.txt for details.
///////////////////////////////////////////////////////////////////////////////
#ifndef CPPCORO_CANCELLATION_REGISTRATION_HPP_INCLUDED
#define CPPCORO_CANCELLATION_REGISTRATION_HPP_INCLUDED

#include <functional>
#include <utility>
#include <type_traits>
#include <atomic>

namespace cppcoro
{
	class cancellation_token;

	namespace detail
	{
		class cancellation_state;
		struct cancellation_registration_list_chunk;
		struct cancellation_registration_state;
	}

	class cancellation_registration
	{
	public:

		/// Registers the callback to be executed when cancellation is requested
		/// on the cancellation_token.
		///
		/// The callback will be executed if cancellation is requested for the
		/// specified cancellation token. If cancellation has already been requested
		/// then the callback will be executed immediately, before the constructor
		/// returns. If cancellation has not yet been requested then the callback
		/// will be executed on the first thread to request cancellation inside
		/// the call to cancellation_source::request_cancellation().
		///
		/// \param token
		/// The cancellation token to register the callback with.
		///
		/// \param callback
		/// The callback to be executed when cancellation is requested on the
		/// the cancellation_token. Note that callback must not throw an exception
		/// if called when cancellation is requested otherwise std::terminate()
		/// will be called.
		///
		/// \throw std::bad_alloc
		/// If registration failed due to insufficient memory available.
		template<
			typename FUNC,
			typename = std::enable_if_t<std::is_constructible_v<std::function<void()>, FUNC&&>>>
		cancellation_registration(cancellation_token token, FUNC&& callback)
			: m_callback(std::forward<FUNC>(callback))
		{
			register_callback(std::move(token));
		}

		cancellation_registration(const cancellation_registration& other) = delete;
		cancellation_registration& operator=(const cancellation_registration& other) = delete;

		/// Deregisters the callback.
		///
		/// After the destructor returns it is guaranteed that the callback
		/// will not be subsequently called during a call to request_cancellation()
		/// on the cancellation_source.
		///
		/// This may block if cancellation has been requested on another thread
		/// is it will need to wait until this callback has finished executing
		/// before the callback can be destroyed.
		~cancellation_registration();

	private:

		friend class detail::cancellation_state;
		friend struct detail::cancellation_registration_state;

		void register_callback(cancellation_token&& token);

		detail::cancellation_state* m_state;
		std::function<void()> m_callback;
		detail::cancellation_registration_list_chunk* m_chunk;
		std::uint32_t m_entryIndex;
	};
}

#endif
<commit_msg>Add missing #include in cancellation_registration.hpp<commit_after>///////////////////////////////////////////////////////////////////////////////
// Copyright (c) Lewis Baker
// Licenced under MIT license. See LICENSE.txt for details.
///////////////////////////////////////////////////////////////////////////////
#ifndef CPPCORO_CANCELLATION_REGISTRATION_HPP_INCLUDED
#define CPPCORO_CANCELLATION_REGISTRATION_HPP_INCLUDED

#include <cppcoro/cancellation_token.hpp>

#include <functional>
#include <utility>
#include <type_traits>
#include <atomic>

namespace cppcoro
{
	namespace detail
	{
		class cancellation_state;
		struct cancellation_registration_list_chunk;
		struct cancellation_registration_state;
	}

	class cancellation_registration
	{
	public:

		/// Registers the callback to be executed when cancellation is requested
		/// on the cancellation_token.
		///
		/// The callback will be executed if cancellation is requested for the
		/// specified cancellation token. If cancellation has already been requested
		/// then the callback will be executed immediately, before the constructor
		/// returns. If cancellation has not yet been requested then the callback
		/// will be executed on the first thread to request cancellation inside
		/// the call to cancellation_source::request_cancellation().
		///
		/// \param token
		/// The cancellation token to register the callback with.
		///
		/// \param callback
		/// The callback to be executed when cancellation is requested on the
		/// the cancellation_token. Note that callback must not throw an exception
		/// if called when cancellation is requested otherwise std::terminate()
		/// will be called.
		///
		/// \throw std::bad_alloc
		/// If registration failed due to insufficient memory available.
		template<
			typename FUNC,
			typename = std::enable_if_t<std::is_constructible_v<std::function<void()>, FUNC&&>>>
		cancellation_registration(cancellation_token token, FUNC&& callback)
			: m_callback(std::forward<FUNC>(callback))
		{
			register_callback(std::move(token));
		}

		cancellation_registration(const cancellation_registration& other) = delete;
		cancellation_registration& operator=(const cancellation_registration& other) = delete;

		/// Deregisters the callback.
		///
		/// After the destructor returns it is guaranteed that the callback
		/// will not be subsequently called during a call to request_cancellation()
		/// on the cancellation_source.
		///
		/// This may block if cancellation has been requested on another thread
		/// is it will need to wait until this callback has finished executing
		/// before the callback can be destroyed.
		~cancellation_registration();

	private:

		friend class detail::cancellation_state;
		friend struct detail::cancellation_registration_state;

		void register_callback(cancellation_token&& token);

		detail::cancellation_state* m_state;
		std::function<void()> m_callback;
		detail::cancellation_registration_list_chunk* m_chunk;
		std::uint32_t m_entryIndex;
	};
}

#endif
<|endoftext|>


<commit_before>#include <image_cloud/common/small_helpers.hpp>
#include <opencv2/core/core.hpp>
#include <math.h>

#ifndef FILTER2D_EDGE_H_
#define FILTER2D_EDGE_H_

namespace filter_2d
{

inline
void get_diff(int x, int y, int cx, int cy, int &result, cv::Mat& in) {
	if (between(-1, x, in.rows) && between(-1, y, in.cols)) {
		// hit upper left
		result = std::max(abs(in.at<uchar>(x, y) - in.at<uchar>(cx, cy)),	result);
	}
}

inline
int max_diff(int cx, int cy, cv::Mat &in){
	//Check
	int result = 0;

	get_diff(cx -1, cy -1, cx, cy, result, in);
	get_diff(cx	  , cy -1, cx, cy, result, in);
	get_diff(cx +1, cy -1, cx, cy, result, in);
	get_diff(cx -1, cy   , cx, cy, result, in);
	get_diff(cx +1, cy   , cx, cy, result, in);
	get_diff(cx -1, cy +1, cx, cy, result, in);
	get_diff(cx   , cy +1, cx, cy, result, in);
	get_diff(cx +1, cy +1, cx, cy, result, in);

	return result;
}

void
edge_max(cv::Mat &in, cv::Mat &out)
{
		for(int y = 0; y < in.cols; y++)
		{
			for(int x = 0; x < in.rows; x++)
			{
				out.at<uchar>(x,y) = max_diff(x, y, in);
			}
		}
}

}

#endif
<commit_msg>use row and col instead of x, y, fixed wrong indices because of mat at(r,c) != at(x,y)<commit_after>#include <image_cloud/common/small_helpers.hpp>
#include <opencv2/core/core.hpp>
#include <math.h>

#ifndef FILTER2D_EDGE_H_
#define FILTER2D_EDGE_H_

namespace filter_2d
{

template <typename ImageT>
inline
void get_diff(int col, int row, int col_c, int row_c, int &result, cv::Mat& in) {
	if (between(-1, col, in.cols) && between(-1, row, in.rows)) {
		// hit upper left
		result = std::max(abs(in.at<ImageT>(row, col) - in.at<ImageT>(row_c, col_c)),	result);
	}
}

template <typename ImageT>
inline
int max_diff_neighbors(int row_c, int col_c, cv::Mat &in){
	//Check
	int result = 0;

	get_diff<ImageT>(col_c -1, row_c -1, col_c, row_c, result, in);
	get_diff<ImageT>(col_c	  , row_c -1, col_c, row_c, result, in);
	get_diff<ImageT>(col_c +1, row_c -1, col_c, row_c, result, in);
	get_diff<ImageT>(col_c -1, row_c   , col_c, row_c, result, in);
	get_diff<ImageT>(col_c +1, row_c   , col_c, row_c, result, in);
	get_diff<ImageT>(col_c -1, row_c +1, col_c, row_c, result, in);
	get_diff<ImageT>(col_c   , row_c +1, col_c, row_c, result, in);
	get_diff<ImageT>(col_c +1, row_c +1, col_c, row_c, result, in);

	return result;
}

template <typename ImageT>
void
edge_max(cv::Mat &in, cv::Mat &out)
{

	printf("rows: %d, cols: %d\n", in.rows, in.cols);
	printf("rows: %d, cols: %d\n", out.rows, out.cols);
	assert(in.rows == out.rows);
	assert(in.cols == out.cols);
	assert(in.depth() == out.depth());
	assert(in.channels() == out.channels());

	for(int r = 0; r < in.rows; r++)
	{
		for(int c = 0; c < in.cols; c++)
		{
			out.at<ImageT>(r,c) = max_diff_neighbors<ImageT>(r, c, in);
		}
	}
}

}

#endif
<|endoftext|>


<commit_before>/*

Copyright (c) 2006, Arvid Norberg
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in
      the documentation and/or other materials provided with the distribution.
    * Neither the name of the author nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

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

*/

#ifndef ROUTING_TABLE_HPP
#define ROUTING_TABLE_HPP

#include <vector>
#include <deque>
#include <boost/cstdint.hpp>

#include <boost/iterator/iterator_facade.hpp>
#include <boost/iterator/iterator_categories.hpp>
#include <boost/utility.hpp>
#include <boost/tuple/tuple.hpp>
#include <boost/array.hpp>
#include <set>

#include <libtorrent/kademlia/logging.hpp>

#include <libtorrent/kademlia/node_id.hpp>
#include <libtorrent/kademlia/node_entry.hpp>
#include <libtorrent/session_settings.hpp>
#include <libtorrent/size_type.hpp>
#include <libtorrent/assert.hpp>

namespace libtorrent { namespace dht
{

#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_DECLARE_LOG(table);
#endif

typedef std::vector<node_entry> bucket_t;

// differences in the implementation from the description in
// the paper:
//
// * The routing table tree is not allocated dynamically, there
// 	are always 160 buckets.
// * Nodes are not marked as being stale, they keep a counter
// 	that tells how many times in a row they have failed. When
// 	a new node is to be inserted, the node that has failed
// 	the most times is replaced. If none of the nodes in the
// 	bucket has failed, then it is put in the replacement
// 	cache (just like in the paper).

class routing_table;

namespace aux
{

	// Iterates over a flattened routing_table structure.
	class routing_table_iterator
	: public boost::iterator_facade<
		routing_table_iterator
		, node_entry const
		, boost::forward_traversal_tag
		>
	{
	public:
		routing_table_iterator()
		{
		}

	private:
		friend class libtorrent::dht::routing_table;
		friend class boost::iterator_core_access;

		typedef boost::array<std::pair<bucket_t, bucket_t>, 160>::const_iterator
			bucket_iterator_t;

		routing_table_iterator(
			bucket_iterator_t begin
			, bucket_iterator_t end)
			: m_bucket_iterator(begin)
			, m_bucket_end(end)
			, m_iterator(begin != end ? begin->first.begin() : bucket_t::const_iterator())
		{
			if (m_bucket_iterator == m_bucket_end) return;
			while (m_iterator == m_bucket_iterator->first.end())
			{
				if (++m_bucket_iterator == m_bucket_end)
					break;
				m_iterator = m_bucket_iterator->first.begin();
			}
		}

		bool equal(routing_table_iterator const& other) const
		{
			return m_bucket_iterator == other.m_bucket_iterator
				&& (m_bucket_iterator == m_bucket_end
					|| m_iterator == other.m_iterator);
		}

		void increment()
		{
			TORRENT_ASSERT(m_bucket_iterator != m_bucket_end);
			++m_iterator;
			while (m_iterator == m_bucket_iterator->first.end())
			{
				if (++m_bucket_iterator == m_bucket_end)
					break;
				m_iterator = m_bucket_iterator->first.begin();
			}
		}

		node_entry const& dereference() const
		{
			TORRENT_ASSERT(m_bucket_iterator != m_bucket_end);
			return *m_iterator;
		}

		bucket_iterator_t m_bucket_iterator;
		bucket_iterator_t m_bucket_end;
		bucket_t::const_iterator m_iterator;
	};

} // namespace aux

class routing_table
{
public:
	typedef aux::routing_table_iterator iterator;
	typedef iterator const_iterator;

	routing_table(node_id const& id, int bucket_size
		, dht_settings const& settings);

	void node_failed(node_id const& id);

	// adds an endpoint that will never be added to
	// the routing table
	void add_router_node(udp::endpoint router);

	// iterates over the router nodes added
	typedef std::set<udp::endpoint>::const_iterator router_iterator;
	router_iterator router_begin() const { return m_router_nodes.begin(); }
	router_iterator router_end() const { return m_router_nodes.end(); }

	// this function is called every time the node sees
	// a sign of a node being alive. This node will either
	// be inserted in the k-buckets or be moved to the top
	// of its bucket.
	bool node_seen(node_id const& id, udp::endpoint addr);

	// returns time when the given bucket needs another refresh.
	// if the given bucket is empty but there are nodes
	// in a bucket closer to us, or if the bucket is non-empty and
	// the time from the last activity is more than 15 minutes
	ptime next_refresh(int bucket);

	// fills the vector with the count nodes from our buckets that
	// are nearest to the given id.
	void find_node(node_id const& id, std::vector<node_entry>& l
		, bool include_self, int count = 0);

	// returns true if the given node would be placed in a bucket
	// that is not full. If the node already exists in the table
	// this function returns false
	bool need_node(node_id const& id);

	// this will set the given bucket's latest activity
	// to the current time
	void touch_bucket(int bucket);

	int bucket_size(int bucket)
	{
		TORRENT_ASSERT(bucket >= 0 && bucket < 160);
		return (int)m_buckets[bucket].first.size();
	}
	int bucket_size() const { return m_bucket_size; }

	iterator begin() const;
	iterator end() const;

	boost::tuple<int, int> size() const;
	size_type num_global_nodes() const;

	// returns true if there are no working nodes
	// in the routing table
	bool need_bootstrap() const;
	int num_active_buckets() const
	{ return 160 - m_lowest_active_bucket + 1; }

	void replacement_cache(bucket_t& nodes) const;
#ifdef TORRENT_DHT_VERBOSE_LOGGING
	// used for debug and monitoring purposes. This will print out
	// the state of the routing table to the given stream
	void print_state(std::ostream& os) const;
#endif

private:

	// constant called k in paper
	int m_bucket_size;

	dht_settings const& m_settings;

	// 160 (k-bucket, replacement cache) pairs
	typedef boost::array<std::pair<bucket_t, bucket_t>, 160> table_t;
	table_t m_buckets;
	// timestamps of the last activity in each bucket
	typedef boost::array<ptime, 160> table_activity_t;
	table_activity_t m_bucket_activity;
	node_id m_id; // our own node id

	// this is a set of all the endpoints that have
	// been identified as router nodes. They will
	// be used in searches, but they will never
	// be added to the routing table.
	std::set<udp::endpoint> m_router_nodes;

	// this is the lowest bucket index with nodes in it
	int m_lowest_active_bucket;
};

} } // namespace libtorrent::dht

#endif // ROUTING_TABLE_HPP

<commit_msg>made routing table support safe iterators on gcc<commit_after>/*

Copyright (c) 2006, Arvid Norberg
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in
      the documentation and/or other materials provided with the distribution.
    * Neither the name of the author nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

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

*/

#ifndef ROUTING_TABLE_HPP
#define ROUTING_TABLE_HPP

#include <vector>
#include <deque>
#include <boost/cstdint.hpp>

#include <boost/iterator/iterator_facade.hpp>
#include <boost/iterator/iterator_categories.hpp>
#include <boost/utility.hpp>
#include <boost/tuple/tuple.hpp>
#include <boost/array.hpp>
#include <set>

#include <libtorrent/kademlia/logging.hpp>

#include <libtorrent/kademlia/node_id.hpp>
#include <libtorrent/kademlia/node_entry.hpp>
#include <libtorrent/session_settings.hpp>
#include <libtorrent/size_type.hpp>
#include <libtorrent/assert.hpp>

namespace libtorrent { namespace dht
{

#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_DECLARE_LOG(table);
#endif

typedef std::vector<node_entry> bucket_t;

// differences in the implementation from the description in
// the paper:
//
// * The routing table tree is not allocated dynamically, there
// 	are always 160 buckets.
// * Nodes are not marked as being stale, they keep a counter
// 	that tells how many times in a row they have failed. When
// 	a new node is to be inserted, the node that has failed
// 	the most times is replaced. If none of the nodes in the
// 	bucket has failed, then it is put in the replacement
// 	cache (just like in the paper).

class routing_table;

namespace aux
{

	// Iterates over a flattened routing_table structure.
	class routing_table_iterator
	: public boost::iterator_facade<
		routing_table_iterator
		, node_entry const
		, boost::forward_traversal_tag
		>
	{
	public:
		routing_table_iterator()
		{
		}

	private:
		friend class libtorrent::dht::routing_table;
		friend class boost::iterator_core_access;

		typedef boost::array<std::pair<bucket_t, bucket_t>, 160>::const_iterator
			bucket_iterator_t;

		routing_table_iterator(
			bucket_iterator_t begin
			, bucket_iterator_t end)
			: m_bucket_iterator(begin)
			, m_bucket_end(end)
		{
			if (m_bucket_iterator == m_bucket_end) return;
			m_iterator = begin->first.begin();
			while (m_iterator == m_bucket_iterator->first.end())
			{
				if (++m_bucket_iterator == m_bucket_end)
					break;
				m_iterator = m_bucket_iterator->first.begin();
			}
		}

		bool equal(routing_table_iterator const& other) const
		{
			return m_bucket_iterator == other.m_bucket_iterator
				&& (m_bucket_iterator == m_bucket_end
					|| *m_iterator == other.m_iterator);
		}

		void increment()
		{
			TORRENT_ASSERT(m_bucket_iterator != m_bucket_end);
			++*m_iterator;
			while (*m_iterator == m_bucket_iterator->first.end())
			{
				if (++m_bucket_iterator == m_bucket_end)
					break;
				m_iterator = m_bucket_iterator->first.begin();
			}
		}

		node_entry const& dereference() const
		{
			TORRENT_ASSERT(m_bucket_iterator != m_bucket_end);
			return **m_iterator;
		}

		bucket_iterator_t m_bucket_iterator;
		bucket_iterator_t m_bucket_end;
		// when debug iterators are enabled, default constructed
		// iterators are not allowed to be copied. In the case
		// where the routing table is empty, m_iterator would be
		// default constructed and not copyable.
		boost::optional<bucket_t::const_iterator> m_iterator;
	};

} // namespace aux

class routing_table
{
public:
	typedef aux::routing_table_iterator iterator;
	typedef iterator const_iterator;

	routing_table(node_id const& id, int bucket_size
		, dht_settings const& settings);

	void node_failed(node_id const& id);

	// adds an endpoint that will never be added to
	// the routing table
	void add_router_node(udp::endpoint router);

	// iterates over the router nodes added
	typedef std::set<udp::endpoint>::const_iterator router_iterator;
	router_iterator router_begin() const { return m_router_nodes.begin(); }
	router_iterator router_end() const { return m_router_nodes.end(); }

	// this function is called every time the node sees
	// a sign of a node being alive. This node will either
	// be inserted in the k-buckets or be moved to the top
	// of its bucket.
	bool node_seen(node_id const& id, udp::endpoint addr);

	// returns time when the given bucket needs another refresh.
	// if the given bucket is empty but there are nodes
	// in a bucket closer to us, or if the bucket is non-empty and
	// the time from the last activity is more than 15 minutes
	ptime next_refresh(int bucket);

	// fills the vector with the count nodes from our buckets that
	// are nearest to the given id.
	void find_node(node_id const& id, std::vector<node_entry>& l
		, bool include_self, int count = 0);

	// returns true if the given node would be placed in a bucket
	// that is not full. If the node already exists in the table
	// this function returns false
	bool need_node(node_id const& id);

	// this will set the given bucket's latest activity
	// to the current time
	void touch_bucket(int bucket);

	int bucket_size(int bucket)
	{
		TORRENT_ASSERT(bucket >= 0 && bucket < 160);
		return (int)m_buckets[bucket].first.size();
	}
	int bucket_size() const { return m_bucket_size; }

	iterator begin() const;
	iterator end() const;

	boost::tuple<int, int> size() const;
	size_type num_global_nodes() const;

	// returns true if there are no working nodes
	// in the routing table
	bool need_bootstrap() const;
	int num_active_buckets() const
	{ return 160 - m_lowest_active_bucket + 1; }

	void replacement_cache(bucket_t& nodes) const;
#ifdef TORRENT_DHT_VERBOSE_LOGGING
	// used for debug and monitoring purposes. This will print out
	// the state of the routing table to the given stream
	void print_state(std::ostream& os) const;
#endif

private:

	// constant called k in paper
	int m_bucket_size;

	dht_settings const& m_settings;

	// 160 (k-bucket, replacement cache) pairs
	typedef boost::array<std::pair<bucket_t, bucket_t>, 160> table_t;
	table_t m_buckets;
	// timestamps of the last activity in each bucket
	typedef boost::array<ptime, 160> table_activity_t;
	table_activity_t m_bucket_activity;
	node_id m_id; // our own node id

	// this is a set of all the endpoints that have
	// been identified as router nodes. They will
	// be used in searches, but they will never
	// be added to the routing table.
	std::set<udp::endpoint> m_router_nodes;

	// this is the lowest bucket index with nodes in it
	int m_lowest_active_bucket;
};

} } // namespace libtorrent::dht

#endif // ROUTING_TABLE_HPP

<|endoftext|>


<commit_before>/*
    dn.cpp

    This file is part of qgpgme, the Qt API binding for gpgme
    Copyright (c) 2004 Klarälvdalens Datakonsult AB
    Copyright (c) 2016 Intevation GmbH

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

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

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

    In addition, as a special exception, the copyright holders give
    permission to link the code of this program with any edition of
    the Qt library by Trolltech AS, Norway (or with modified versions
    of Qt that use the same license as Qt), and distribute linked
    combinations including the two.  You must obey the GNU General
    Public License in all respects for all of the code used other than
    Qt.  If you modify this file, you may extend this exception to
    your version of the file, but you are not obligated to do so.  If
    you do not wish to do so, delete this exception statement from
    your version.
*/

#ifdef HAVE_CONFIG_H
 #include "config.h"
#endif

#include "dn.h"

#include <gpg-error.h>

static const struct {
    const char *name;
    const char *oid;
} oidmap[] = {
    // keep them ordered by oid:
    { "SP", "ST" }, // hack to show the Sphinx-required/desired SP for
    // StateOrProvince, otherwise known as ST or even S
    { "NameDistinguisher", "0.2.262.1.10.7.20" },
    { "EMAIL", "1.2.840.113549.1.9.1" },
    { "SN", "2.5.4.4" },
    { "SerialNumber", "2.5.4.5" },
    { "T", "2.5.4.12" },
    { "D", "2.5.4.13" },
    { "BC", "2.5.4.15" },
    { "ADDR", "2.5.4.16" },
    { "PC", "2.5.4.17" },
    { "GN", "2.5.4.42" },
    { "Pseudo", "2.5.4.65" },
};
static const unsigned int numOidMaps = sizeof oidmap / sizeof * oidmap;

class QGpgME::DN::Private
{
public:
    Private() : mRefCount(0) {}
    Private(const Private &other)
        : attributes(other.attributes),
          reorderedAttributes(other.reorderedAttributes),
          order{"CN", "L", "_X_", "OU", "O", "C"},
          mRefCount(0)
    {
    }

    int ref()
    {
        return ++mRefCount;
    }

    int unref()
    {
        if (--mRefCount <= 0) {
            delete this;
            return 0;
        } else {
            return mRefCount;
        }
    }

    int refCount() const
    {
        return mRefCount;
    }

    DN::Attribute::List attributes;
    DN::Attribute::List reorderedAttributes;
    QStringList order;
private:
    int mRefCount;
};

namespace
{
struct DnPair {
    char *key;
    char *value;
};
}

// copied from CryptPlug and adapted to work on DN::Attribute::List:

#define digitp(p)   (*(p) >= '0' && *(p) <= '9')
#define hexdigitp(a) (digitp (a)                     \
                      || (*(a) >= 'A' && *(a) <= 'F')  \
                      || (*(a) >= 'a' && *(a) <= 'f'))
#define xtoi_1(p)   (*(p) <= '9'? (*(p)- '0'): \
                     *(p) <= 'F'? (*(p)-'A'+10):(*(p)-'a'+10))
#define xtoi_2(p)   ((xtoi_1(p) * 16) + xtoi_1((p)+1))

static char *
trim_trailing_spaces(char *string)
{
    char *p, *mark;

    for (mark = NULL, p = string; *p; p++) {
        if (isspace(*p)) {
            if (!mark) {
                mark = p;
            }
        } else {
            mark = NULL;
        }
    }
    if (mark) {
        *mark = '\0';
    }

    return string;
}

/* Parse a DN and return an array-ized one.  This is not a validating
   parser and it does not support any old-stylish syntax; gpgme is
   expected to return only rfc2253 compatible strings. */
static const unsigned char *
parse_dn_part(DnPair *array, const unsigned char *string)
{
    const unsigned char *s, *s1;
    size_t n;
    char *p;

    /* parse attributeType */
    for (s = string + 1; *s && *s != '='; s++)
        ;
    if (!*s) {
        return NULL;    /* error */
    }
    n = s - string;
    if (!n) {
        return NULL;    /* empty key */
    }
    p = (char *)malloc(n + 1);

    memcpy(p, string, n);
    p[n] = 0;
    trim_trailing_spaces((char *)p);
    // map OIDs to their names:
    for (unsigned int i = 0; i < numOidMaps; ++i)
        if (!strcasecmp((char *)p, oidmap[i].oid)) {
            free(p);
            gpgrt_asprintf(&p, oidmap[i].name);
            break;
        }
    array->key = p;
    string = s + 1;

    if (*string == '#') {
        /* hexstring */
        string++;
        for (s = string; hexdigitp(s); s++) {
            s++;
        }
        n = s - string;
        if (!n || (n & 1)) {
            return NULL;    /* empty or odd number of digits */
        }
        n /= 2;
        array->value = p = (char *)malloc(n + 1);

        for (s1 = string; n; s1 += 2, n--) {
            *p++ = xtoi_2(s1);
        }
        *p = 0;
    } else {
        /* regular v3 quoted string */
        for (n = 0, s = string; *s; s++) {
            if (*s == '\\') {
                /* pair */
                s++;
                if (*s == ',' || *s == '=' || *s == '+'
                        || *s == '<' || *s == '>' || *s == '#' || *s == ';'
                        || *s == '\\' || *s == '\"' || *s == ' ') {
                    n++;
                } else if (hexdigitp(s) && hexdigitp(s + 1)) {
                    s++;
                    n++;
                } else {
                    return NULL;    /* invalid escape sequence */
                }
            } else if (*s == '\"') {
                return NULL;    /* invalid encoding */
            } else if (*s == ',' || *s == '=' || *s == '+'
                       || *s == '<' || *s == '>' || *s == '#' || *s == ';') {
                break;
            } else {
                n++;
            }
        }

        array->value = p = (char *)malloc(n + 1);

        for (s = string; n; s++, n--) {
            if (*s == '\\') {
                s++;
                if (hexdigitp(s)) {
                    *p++ = xtoi_2(s);
                    s++;
                } else {
                    *p++ = *s;
                }
            } else {
                *p++ = *s;
            }
        }
        *p = 0;
    }
    return s;
}

/* Parse a DN and return an array-ized one.  This is not a validating
   parser and it does not support any old-stylish syntax; gpgme is
   expected to return only rfc2253 compatible strings. */
static QGpgME::DN::Attribute::List
parse_dn(const unsigned char *string)
{
    if (!string) {
        return QVector<QGpgME::DN::Attribute>();
    }

    QVector<QGpgME::DN::Attribute> result;
    while (*string) {
        while (*string == ' ') {
            string++;
        }
        if (!*string) {
            break;    /* ready */
        }

        DnPair pair = { 0, 0 };
        string = parse_dn_part(&pair, string);
        if (!string) {
            goto failure;
        }
        if (pair.key && pair.value)
            result.push_back(QGpgME::DN::Attribute(QString::fromUtf8(pair.key),
                                                 QString::fromUtf8(pair.value)));
        free(pair.key);
        free(pair.value);

        while (*string == ' ') {
            string++;
        }
        if (*string && *string != ',' && *string != ';' && *string != '+') {
            goto failure;    /* invalid delimiter */
        }
        if (*string) {
            string++;
        }
    }
    return result;

failure:
    return QVector<QGpgME::DN::Attribute>();
}

static QVector<QGpgME::DN::Attribute>
parse_dn(const QString &dn)
{
    return parse_dn((const unsigned char *)dn.toUtf8().data());
}

static QString dn_escape(const QString &s)
{
    QString result;
    for (unsigned int i = 0, end = s.length(); i != end; ++i) {
        const QChar ch = s[i];
        switch (ch.unicode()) {
        case ',':
        case '+':
        case '"':
        case '\\':
        case '<':
        case '>':
        case ';':
            result += QLatin1Char('\\');
        // fall through
        default:
            result += ch;
        }
    }
    return result;
}

static QString
serialise(const QVector<QGpgME::DN::Attribute> &dn, const QString &sep)
{
    QStringList result;
    for (QVector<QGpgME::DN::Attribute>::const_iterator it = dn.begin(); it != dn.end(); ++it)
        if (!(*it).name().isEmpty() && !(*it).value().isEmpty()) {
            result.push_back((*it).name().trimmed() + QLatin1Char('=') + dn_escape((*it).value().trimmed()));
        }
    return result.join(sep);
}

static QGpgME::DN::Attribute::List
reorder_dn(const QGpgME::DN::Attribute::List &dn, const QStringList &attrOrder)
{
    QGpgME::DN::Attribute::List unknownEntries;
    QGpgME::DN::Attribute::List result;
    unknownEntries.reserve(dn.size());
    result.reserve(dn.size());

    // find all unknown entries in their order of appearance
    for (QGpgME::DN::const_iterator it = dn.begin(); it != dn.end(); ++it)
        if (!attrOrder.contains((*it).name())) {
            unknownEntries.push_back(*it);
        }

    // process the known attrs in the desired order
    for (QStringList::const_iterator oit = attrOrder.begin(); oit != attrOrder.end(); ++oit)
        if (*oit == QLatin1String("_X_")) {
            // insert the unknown attrs
            std::copy(unknownEntries.begin(), unknownEntries.end(),
                      std::back_inserter(result));
            unknownEntries.clear(); // don't produce dup's
        } else {
            for (QGpgME::DN::const_iterator dnit = dn.begin(); dnit != dn.end(); ++dnit)
                if ((*dnit).name() == *oit) {
                    result.push_back(*dnit);
                }
        }

    return result;
}

//
//
// class DN
//
//

QGpgME::DN::DN()
{
    d = new Private();
    d->ref();
}

QGpgME::DN::DN(const QString &dn)
{
    d = new Private();
    d->ref();
    d->attributes = parse_dn(dn);
}

QGpgME::DN::DN(const char *utf8DN)
{
    d = new Private();
    d->ref();
    if (utf8DN) {
        d->attributes = parse_dn((const unsigned char *)utf8DN);
    }
}

QGpgME::DN::DN(const DN &other)
    : d(other.d)
{
    if (d) {
        d->ref();
    }
}

QGpgME::DN::~DN()
{
    if (d) {
        d->unref();
    }
}

const QGpgME::DN &QGpgME::DN::operator=(const DN &that)
{
    if (this->d == that.d) {
        return *this;
    }

    if (that.d) {
        that.d->ref();
    }
    if (this->d) {
        this->d->unref();
    }

    this->d = that.d;

    return *this;
}

QString QGpgME::DN::prettyDN() const
{
    if (!d) {
        return QString();
    }
    if (d->reorderedAttributes.empty()) {
        d->reorderedAttributes = reorder_dn(d->attributes, d->order);
    }
    return serialise(d->reorderedAttributes, QStringLiteral(","));
}

QString QGpgME::DN::dn() const
{
    return d ? serialise(d->attributes, QStringLiteral(",")) : QString();
}

QString QGpgME::DN::dn(const QString &sep) const
{
    return d ? serialise(d->attributes, sep) : QString();
}

// static
QString QGpgME::DN::escape(const QString &value)
{
    return dn_escape(value);
}

void QGpgME::DN::detach()
{
    if (!d) {
        d = new QGpgME::DN::Private();
        d->ref();
    } else if (d->refCount() > 1) {
        QGpgME::DN::Private *d_save = d;
        d = new QGpgME::DN::Private(*d);
        d->ref();
        d_save->unref();
    }
}

void QGpgME::DN::append(const Attribute &attr)
{
    detach();
    d->attributes.push_back(attr);
    d->reorderedAttributes.clear();
}

QString QGpgME::DN::operator[](const QString &attr) const
{
    if (!d) {
        return QString();
    }
    const QString attrUpper = attr.toUpper();
    for (QVector<Attribute>::const_iterator it = d->attributes.constBegin();
            it != d->attributes.constEnd(); ++it)
        if ((*it).name() == attrUpper) {
            return (*it).value();
        }
    return QString();
}

static QVector<QGpgME::DN::Attribute> empty;

QGpgME::DN::const_iterator QGpgME::DN::begin() const
{
    return d ? d->attributes.constBegin() : empty.constBegin();
}

QGpgME::DN::const_iterator QGpgME::DN::end() const
{
    return d ? d->attributes.constEnd() : empty.constEnd();
}

void QGpgME::DN::setAttributeOrder (const QStringList &order) const
{
    d->order = order;
}

const QStringList & QGpgME::DN::attributeOrder () const
{
    return d->order;
}
<commit_msg>qt: pass fmt to gpgrt_asprintf()<commit_after>/*
    dn.cpp

    This file is part of qgpgme, the Qt API binding for gpgme
    Copyright (c) 2004 Klarälvdalens Datakonsult AB
    Copyright (c) 2016 Intevation GmbH

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

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

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

    In addition, as a special exception, the copyright holders give
    permission to link the code of this program with any edition of
    the Qt library by Trolltech AS, Norway (or with modified versions
    of Qt that use the same license as Qt), and distribute linked
    combinations including the two.  You must obey the GNU General
    Public License in all respects for all of the code used other than
    Qt.  If you modify this file, you may extend this exception to
    your version of the file, but you are not obligated to do so.  If
    you do not wish to do so, delete this exception statement from
    your version.
*/

#ifdef HAVE_CONFIG_H
 #include "config.h"
#endif

#include "dn.h"

#include <gpg-error.h>

static const struct {
    const char *name;
    const char *oid;
} oidmap[] = {
    // keep them ordered by oid:
    { "SP", "ST" }, // hack to show the Sphinx-required/desired SP for
    // StateOrProvince, otherwise known as ST or even S
    { "NameDistinguisher", "0.2.262.1.10.7.20" },
    { "EMAIL", "1.2.840.113549.1.9.1" },
    { "SN", "2.5.4.4" },
    { "SerialNumber", "2.5.4.5" },
    { "T", "2.5.4.12" },
    { "D", "2.5.4.13" },
    { "BC", "2.5.4.15" },
    { "ADDR", "2.5.4.16" },
    { "PC", "2.5.4.17" },
    { "GN", "2.5.4.42" },
    { "Pseudo", "2.5.4.65" },
};
static const unsigned int numOidMaps = sizeof oidmap / sizeof * oidmap;

class QGpgME::DN::Private
{
public:
    Private() : mRefCount(0) {}
    Private(const Private &other)
        : attributes(other.attributes),
          reorderedAttributes(other.reorderedAttributes),
          order{"CN", "L", "_X_", "OU", "O", "C"},
          mRefCount(0)
    {
    }

    int ref()
    {
        return ++mRefCount;
    }

    int unref()
    {
        if (--mRefCount <= 0) {
            delete this;
            return 0;
        } else {
            return mRefCount;
        }
    }

    int refCount() const
    {
        return mRefCount;
    }

    DN::Attribute::List attributes;
    DN::Attribute::List reorderedAttributes;
    QStringList order;
private:
    int mRefCount;
};

namespace
{
struct DnPair {
    char *key;
    char *value;
};
}

// copied from CryptPlug and adapted to work on DN::Attribute::List:

#define digitp(p)   (*(p) >= '0' && *(p) <= '9')
#define hexdigitp(a) (digitp (a)                     \
                      || (*(a) >= 'A' && *(a) <= 'F')  \
                      || (*(a) >= 'a' && *(a) <= 'f'))
#define xtoi_1(p)   (*(p) <= '9'? (*(p)- '0'): \
                     *(p) <= 'F'? (*(p)-'A'+10):(*(p)-'a'+10))
#define xtoi_2(p)   ((xtoi_1(p) * 16) + xtoi_1((p)+1))

static char *
trim_trailing_spaces(char *string)
{
    char *p, *mark;

    for (mark = NULL, p = string; *p; p++) {
        if (isspace(*p)) {
            if (!mark) {
                mark = p;
            }
        } else {
            mark = NULL;
        }
    }
    if (mark) {
        *mark = '\0';
    }

    return string;
}

/* Parse a DN and return an array-ized one.  This is not a validating
   parser and it does not support any old-stylish syntax; gpgme is
   expected to return only rfc2253 compatible strings. */
static const unsigned char *
parse_dn_part(DnPair *array, const unsigned char *string)
{
    const unsigned char *s, *s1;
    size_t n;
    char *p;

    /* parse attributeType */
    for (s = string + 1; *s && *s != '='; s++)
        ;
    if (!*s) {
        return NULL;    /* error */
    }
    n = s - string;
    if (!n) {
        return NULL;    /* empty key */
    }
    p = (char *)malloc(n + 1);

    memcpy(p, string, n);
    p[n] = 0;
    trim_trailing_spaces((char *)p);
    // map OIDs to their names:
    for (unsigned int i = 0; i < numOidMaps; ++i)
        if (!strcasecmp((char *)p, oidmap[i].oid)) {
            free(p);
            gpgrt_asprintf(&p, "%s", oidmap[i].name);
            break;
        }
    array->key = p;
    string = s + 1;

    if (*string == '#') {
        /* hexstring */
        string++;
        for (s = string; hexdigitp(s); s++) {
            s++;
        }
        n = s - string;
        if (!n || (n & 1)) {
            return NULL;    /* empty or odd number of digits */
        }
        n /= 2;
        array->value = p = (char *)malloc(n + 1);

        for (s1 = string; n; s1 += 2, n--) {
            *p++ = xtoi_2(s1);
        }
        *p = 0;
    } else {
        /* regular v3 quoted string */
        for (n = 0, s = string; *s; s++) {
            if (*s == '\\') {
                /* pair */
                s++;
                if (*s == ',' || *s == '=' || *s == '+'
                        || *s == '<' || *s == '>' || *s == '#' || *s == ';'
                        || *s == '\\' || *s == '\"' || *s == ' ') {
                    n++;
                } else if (hexdigitp(s) && hexdigitp(s + 1)) {
                    s++;
                    n++;
                } else {
                    return NULL;    /* invalid escape sequence */
                }
            } else if (*s == '\"') {
                return NULL;    /* invalid encoding */
            } else if (*s == ',' || *s == '=' || *s == '+'
                       || *s == '<' || *s == '>' || *s == '#' || *s == ';') {
                break;
            } else {
                n++;
            }
        }

        array->value = p = (char *)malloc(n + 1);

        for (s = string; n; s++, n--) {
            if (*s == '\\') {
                s++;
                if (hexdigitp(s)) {
                    *p++ = xtoi_2(s);
                    s++;
                } else {
                    *p++ = *s;
                }
            } else {
                *p++ = *s;
            }
        }
        *p = 0;
    }
    return s;
}

/* Parse a DN and return an array-ized one.  This is not a validating
   parser and it does not support any old-stylish syntax; gpgme is
   expected to return only rfc2253 compatible strings. */
static QGpgME::DN::Attribute::List
parse_dn(const unsigned char *string)
{
    if (!string) {
        return QVector<QGpgME::DN::Attribute>();
    }

    QVector<QGpgME::DN::Attribute> result;
    while (*string) {
        while (*string == ' ') {
            string++;
        }
        if (!*string) {
            break;    /* ready */
        }

        DnPair pair = { 0, 0 };
        string = parse_dn_part(&pair, string);
        if (!string) {
            goto failure;
        }
        if (pair.key && pair.value)
            result.push_back(QGpgME::DN::Attribute(QString::fromUtf8(pair.key),
                                                 QString::fromUtf8(pair.value)));
        free(pair.key);
        free(pair.value);

        while (*string == ' ') {
            string++;
        }
        if (*string && *string != ',' && *string != ';' && *string != '+') {
            goto failure;    /* invalid delimiter */
        }
        if (*string) {
            string++;
        }
    }
    return result;

failure:
    return QVector<QGpgME::DN::Attribute>();
}

static QVector<QGpgME::DN::Attribute>
parse_dn(const QString &dn)
{
    return parse_dn((const unsigned char *)dn.toUtf8().data());
}

static QString dn_escape(const QString &s)
{
    QString result;
    for (unsigned int i = 0, end = s.length(); i != end; ++i) {
        const QChar ch = s[i];
        switch (ch.unicode()) {
        case ',':
        case '+':
        case '"':
        case '\\':
        case '<':
        case '>':
        case ';':
            result += QLatin1Char('\\');
        // fall through
        default:
            result += ch;
        }
    }
    return result;
}

static QString
serialise(const QVector<QGpgME::DN::Attribute> &dn, const QString &sep)
{
    QStringList result;
    for (QVector<QGpgME::DN::Attribute>::const_iterator it = dn.begin(); it != dn.end(); ++it)
        if (!(*it).name().isEmpty() && !(*it).value().isEmpty()) {
            result.push_back((*it).name().trimmed() + QLatin1Char('=') + dn_escape((*it).value().trimmed()));
        }
    return result.join(sep);
}

static QGpgME::DN::Attribute::List
reorder_dn(const QGpgME::DN::Attribute::List &dn, const QStringList &attrOrder)
{
    QGpgME::DN::Attribute::List unknownEntries;
    QGpgME::DN::Attribute::List result;
    unknownEntries.reserve(dn.size());
    result.reserve(dn.size());

    // find all unknown entries in their order of appearance
    for (QGpgME::DN::const_iterator it = dn.begin(); it != dn.end(); ++it)
        if (!attrOrder.contains((*it).name())) {
            unknownEntries.push_back(*it);
        }

    // process the known attrs in the desired order
    for (QStringList::const_iterator oit = attrOrder.begin(); oit != attrOrder.end(); ++oit)
        if (*oit == QLatin1String("_X_")) {
            // insert the unknown attrs
            std::copy(unknownEntries.begin(), unknownEntries.end(),
                      std::back_inserter(result));
            unknownEntries.clear(); // don't produce dup's
        } else {
            for (QGpgME::DN::const_iterator dnit = dn.begin(); dnit != dn.end(); ++dnit)
                if ((*dnit).name() == *oit) {
                    result.push_back(*dnit);
                }
        }

    return result;
}

//
//
// class DN
//
//

QGpgME::DN::DN()
{
    d = new Private();
    d->ref();
}

QGpgME::DN::DN(const QString &dn)
{
    d = new Private();
    d->ref();
    d->attributes = parse_dn(dn);
}

QGpgME::DN::DN(const char *utf8DN)
{
    d = new Private();
    d->ref();
    if (utf8DN) {
        d->attributes = parse_dn((const unsigned char *)utf8DN);
    }
}

QGpgME::DN::DN(const DN &other)
    : d(other.d)
{
    if (d) {
        d->ref();
    }
}

QGpgME::DN::~DN()
{
    if (d) {
        d->unref();
    }
}

const QGpgME::DN &QGpgME::DN::operator=(const DN &that)
{
    if (this->d == that.d) {
        return *this;
    }

    if (that.d) {
        that.d->ref();
    }
    if (this->d) {
        this->d->unref();
    }

    this->d = that.d;

    return *this;
}

QString QGpgME::DN::prettyDN() const
{
    if (!d) {
        return QString();
    }
    if (d->reorderedAttributes.empty()) {
        d->reorderedAttributes = reorder_dn(d->attributes, d->order);
    }
    return serialise(d->reorderedAttributes, QStringLiteral(","));
}

QString QGpgME::DN::dn() const
{
    return d ? serialise(d->attributes, QStringLiteral(",")) : QString();
}

QString QGpgME::DN::dn(const QString &sep) const
{
    return d ? serialise(d->attributes, sep) : QString();
}

// static
QString QGpgME::DN::escape(const QString &value)
{
    return dn_escape(value);
}

void QGpgME::DN::detach()
{
    if (!d) {
        d = new QGpgME::DN::Private();
        d->ref();
    } else if (d->refCount() > 1) {
        QGpgME::DN::Private *d_save = d;
        d = new QGpgME::DN::Private(*d);
        d->ref();
        d_save->unref();
    }
}

void QGpgME::DN::append(const Attribute &attr)
{
    detach();
    d->attributes.push_back(attr);
    d->reorderedAttributes.clear();
}

QString QGpgME::DN::operator[](const QString &attr) const
{
    if (!d) {
        return QString();
    }
    const QString attrUpper = attr.toUpper();
    for (QVector<Attribute>::const_iterator it = d->attributes.constBegin();
            it != d->attributes.constEnd(); ++it)
        if ((*it).name() == attrUpper) {
            return (*it).value();
        }
    return QString();
}

static QVector<QGpgME::DN::Attribute> empty;

QGpgME::DN::const_iterator QGpgME::DN::begin() const
{
    return d ? d->attributes.constBegin() : empty.constBegin();
}

QGpgME::DN::const_iterator QGpgME::DN::end() const
{
    return d ? d->attributes.constEnd() : empty.constEnd();
}

void QGpgME::DN::setAttributeOrder (const QStringList &order) const
{
    d->order = order;
}

const QStringList & QGpgME::DN::attributeOrder () const
{
    return d->order;
}
<|endoftext|>


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

#include <mapnik/topojson_grammar.hpp>

//#define BOOST_SPIRIT_USE_PHOENIX_V3 1
//#include <boost/spirit/include/qi.hpp>
//#include <boost/spirit/include/phoenix.hpp>
//
//#include <mapnik/value.hpp>
//#include <mapnik/topology.hpp>
//
//#include <string>

namespace mapnik { namespace topojson {

namespace qi = boost::spirit::qi;
namespace phoenix = boost::phoenix;
namespace fusion = boost::fusion;
namespace standard_wide = boost::spirit::standard_wide;
using standard_wide::space_type;

template <typename Iterator>
topojson_grammar<Iterator>::topojson_grammar()
    : topojson_grammar::base_type(topology, "topojson")
{
    using qi::lit;
    using qi::double_;
    using qi::int_;
    using qi::no_skip;
    using qi::omit;
    using qi::_val;
    using qi::_1;
    using qi::_2;
    using qi::_3;
    using qi::_4;
    using qi::fail;
    using qi::on_error;

    using standard_wide::char_;
    using phoenix::construct;

    // generic json types
    value = object | array | string_ | number
        ;

    pairs = key_value % lit(',')
        ;

    key_value = (string_ >> lit(':') >> value)
        ;

    object = lit('{') >> *pairs >> lit('}')
        ;

    array = lit('[')
        >> value >> *(lit(',') >> value)
        >> lit(']')
        ;

    number %= strict_double
        | int__
        | lit("true")[_val = true]
        | lit("false")[_val = false]
        | lit("null")
        ;

    unesc_char.add
        ("\\\"", '\"') // quotation mark
        ("\\\\", '\\') // reverse solidus
        ("\\/", '/')   // solidus
        ("\\b", '\b')  // backspace
        ("\\f", '\f')  // formfeed
        ("\\n", '\n')  // newline
        ("\\r", '\r')  // carrige return
        ("\\t", '\t')  // tab
        ;

    string_ %= lit('"') >> no_skip[*(unesc_char | "\\u" >> hex4 | (char_ - lit('"')))] >> lit('"')
        ;

    // topo json
    topology = lit('{') >> lit("\"type\"") >> lit(':') >> lit("\"Topology\"")
                        >> ( (lit(',') >> objects) ^ ( lit(',') >> arcs) ^ (lit(',') >> transform) ^ (lit(',') >> bbox))
                        >> lit('}')
        ;

    transform = lit("\"transform\"") >> lit(':') >> lit('{')
                                     >> lit("\"scale\"") >> lit(':')
                                     >> lit('[')
                                     >> double_ >> lit(',')
                                     >> double_ >> lit(']') >> lit(',')
                                     >> lit("\"translate\"") >> lit(':')
                                     >> lit('[') >> double_ >> lit(',') >> double_ >> lit(']')
                                     >> lit('}')
        ;

    bbox = lit("\"bbox\"") >> lit(':')
                           >> lit('[') >> double_ >> lit(',') >> double_
                           >> lit(',') >> double_ >> lit(',') >> double_
                           >> lit(']')
        ;

    objects = lit("\"objects\"") >> lit(':') >> lit('{')
                                 >> omit[string_] >> lit(':') >> lit('{')
                                 >> lit("\"type\"") >> lit(':') >> lit("\"GeometryCollection\"") >> lit(',')
                                 >> lit("\"geometries\"") >> lit(':') >> lit('[') >> -(geometry % lit(','))
                                 >> lit(']') >> lit('}') >> lit('}')
        ;

    geometry = point | linestring | polygon | omit[object]
        ;

    point = lit('{')
        >> lit("\"type\"") >> lit(':') >> lit("\"Point\"")
        >> ((lit(',') >> lit("\"coordinates\"") >> lit(':') >> coordinate) ^ (lit(',') >> properties))
        >> lit('}')
        ;

    linestring = lit('{')
        >> lit("\"type\"") >> lit(':') >> lit("\"LineString\"")
        >> ((lit(',') >> lit("\"arcs\"") >> lit(':') >> lit('[') >> int_ >> lit(']')) ^ (lit(',') >> properties))
        >> lit('}')
        ;

    polygon = lit('{')
        >> lit("\"type\"") >> lit(':') >> lit("\"Polygon\"")
        >> ((lit(',') >> lit("\"arcs\"") >> lit(':') >> lit('[') >> -(ring % lit(',')) >> lit(']')) ^ (lit(',') >> properties))
        >> lit('}')
            ;

    ring = lit('[') >> -(int_ % lit(',')) >> lit(']')
        ;

    properties = lit("\"properties\"")
        >> lit(':')
        >> object
        ;

    arcs = lit("\"arcs\"") >> lit(':')
                           >> lit('[') >> -( arc % lit(',')) >> lit(']') ;

    arc = lit('[') >> -(coordinate % lit(',')) >> lit(']') ;

    coordinate = lit('[') >> double_ >> lit(',') >> double_ >> lit(']');

    topology.name("topology");
    transform.name("transform");
    objects.name("objects");
    arc.name("arc");
    arcs.name("arcs");
    value.name("value");
    coordinate.name("coordinate");

    point.name("point");
    linestring.name("linestring");
    polygon.name("polygon");

    on_error<fail>
        (
            topology
            , std::clog
            << phoenix::val("Error! Expecting ")
            << _4 // what failed?
            << phoenix::val(" here: \"")
            << where_message_(_3, _2, 16) // where? 16 is max chars to output
            << phoenix::val("\"")
            << std::endl
            );

}

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

#include <mapnik/topojson_grammar.hpp>

namespace mapnik { namespace topojson {

namespace qi = boost::spirit::qi;
namespace phoenix = boost::phoenix;
namespace fusion = boost::fusion;
namespace standard_wide = boost::spirit::standard_wide;
using standard_wide::space_type;

template <typename Iterator>
topojson_grammar<Iterator>::topojson_grammar()
    : topojson_grammar::base_type(topology, "topojson")
{
    using qi::lit;
    using qi::double_;
    using qi::int_;
    using qi::no_skip;
    using qi::omit;
    using qi::_val;
    using qi::_1;
    using qi::_2;
    using qi::_3;
    using qi::_4;
    using qi::fail;
    using qi::on_error;

    using standard_wide::char_;
    using phoenix::construct;

    // generic json types
    value = object | array | string_ | number
        ;

    pairs = key_value % lit(',')
        ;

    key_value = (string_ >> lit(':') >> value)
        ;

    object = lit('{') >> *pairs >> lit('}')
        ;

    array = lit('[')
        >> value >> *(lit(',') >> value)
        >> lit(']')
        ;

    number %= strict_double
        | int__
        | lit("true")[_val = true]
        | lit("false")[_val = false]
        | lit("null")
        ;

    unesc_char.add
        ("\\\"", '\"') // quotation mark
        ("\\\\", '\\') // reverse solidus
        ("\\/", '/')   // solidus
        ("\\b", '\b')  // backspace
        ("\\f", '\f')  // formfeed
        ("\\n", '\n')  // newline
        ("\\r", '\r')  // carrige return
        ("\\t", '\t')  // tab
        ;

    string_ %= lit('"') >> no_skip[*(unesc_char | "\\u" >> hex4 | (char_ - lit('"')))] >> lit('"')
        ;

    // topo json
    topology = lit('{') >> lit("\"type\"") >> lit(':') >> lit("\"Topology\"")
                        >> ( (lit(',') >> objects) ^ ( lit(',') >> arcs) ^ (lit(',') >> transform) ^ (lit(',') >> bbox))
                        >> lit('}')
        ;

    transform = lit("\"transform\"") >> lit(':') >> lit('{')
                                     >> lit("\"scale\"") >> lit(':')
                                     >> lit('[')
                                     >> double_ >> lit(',')
                                     >> double_ >> lit(']') >> lit(',')
                                     >> lit("\"translate\"") >> lit(':')
                                     >> lit('[') >> double_ >> lit(',') >> double_ >> lit(']')
                                     >> lit('}')
        ;

    bbox = lit("\"bbox\"") >> lit(':')
                           >> lit('[') >> double_ >> lit(',') >> double_
                           >> lit(',') >> double_ >> lit(',') >> double_
                           >> lit(']')
        ;

    objects = lit("\"objects\"") >> lit(':') >> lit('{')
                                 >> omit[string_] >> lit(':') >> lit('{')
                                 >> lit("\"type\"") >> lit(':') >> lit("\"GeometryCollection\"") >> lit(',')
                                 >> lit("\"geometries\"") >> lit(':') >> lit('[') >> -(geometry % lit(','))
                                 >> lit(']') >> lit('}') >> lit('}')
        ;

    geometry = point | linestring | polygon | omit[object]
        ;

    point = lit('{')
        >> lit("\"type\"") >> lit(':') >> lit("\"Point\"")
        >> ((lit(',') >> lit("\"coordinates\"") >> lit(':') >> coordinate) ^ (lit(',') >> properties))
        >> lit('}')
        ;

    linestring = lit('{')
        >> lit("\"type\"") >> lit(':') >> lit("\"LineString\"")
        >> ((lit(',') >> lit("\"arcs\"") >> lit(':') >> lit('[') >> int_ >> lit(']')) ^ (lit(',') >> properties))
        >> lit('}')
        ;

    polygon = lit('{')
        >> lit("\"type\"") >> lit(':') >> lit("\"Polygon\"")
        >> ((lit(',') >> lit("\"arcs\"") >> lit(':') >> lit('[') >> -(ring % lit(',')) >> lit(']')) ^ (lit(',') >> properties))
        >> lit('}')
            ;

    ring = lit('[') >> -(int_ % lit(',')) >> lit(']')
        ;

    properties = lit("\"properties\"")
        >> lit(':')
        >> object
        ;

    arcs = lit("\"arcs\"") >> lit(':')
                           >> lit('[') >> -( arc % lit(',')) >> lit(']') ;

    arc = lit('[') >> -(coordinate % lit(',')) >> lit(']') ;

    coordinate = lit('[') >> double_ >> lit(',') >> double_ >> lit(']');

    topology.name("topology");
    transform.name("transform");
    objects.name("objects");
    arc.name("arc");
    arcs.name("arcs");
    value.name("value");
    coordinate.name("coordinate");

    point.name("point");
    linestring.name("linestring");
    polygon.name("polygon");

    on_error<fail>
        (
            topology
            , std::clog
            << phoenix::val("Error! Expecting ")
            << _4 // what failed?
            << phoenix::val(" here: \"")
            << where_message_(_3, _2, 16) // where? 16 is max chars to output
            << phoenix::val("\"")
            << std::endl
            );

}

}}
<|endoftext|>