<commit_before>#include <iostream>
#include <string.h>
#include <boost/tokenizer.hpp>
#include <vector>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
using namespace std;
using namespace boost;

class Connector
{
    public:
        Connector();
        ~Connector();
        bool run;
        int type;
        bool runNext();
};

Connector::Connector()
{
    // the semicolon connector allows the next command to be execute always
    run = true;
    type = 0; // semicolon connector set to 0
}

Connector::~Connector()
{
}
bool Connector::runNext()
{
    if (type == 1) // && connector
    {
        if (!run) // if the first command doesn't succeed  it wont run the next command
        {
            return false;
        }
    }
    else if (type == 2) // || connector
    {
        if (run) // if the first command succeed it wont run the next command
        {
            return false;
        }
    }
    return true;
}
void parseinator(vector<string> input, string& command, string& argument, int& position, Connector& connect);
void commandifier(string command, string argument, Connector& connect);
int main()
{

    string str;
    string command;
    string argument;
    Connector connect;
    char hostname[100];
    int position = 0;
    while (true)
    {
        cout << getlogin(); // returns string containing name of user logged in
        gethostname(hostname, sizeof hostname); // returns the standard host name for the current machine
        cout << "@" << hostname << "$ ";
        getline(cin, str);
        if (str == "exit") // special command of exit
        {
            break;
        }
        vector<string> instruction; // vector of strings
        typedef tokenizer<boost::char_separator<char> > Tok;
        char_separator<char> sep; // default constructed
        Tok tok(str, sep);
        for (Tok::iterator tok_iter = tok.begin(); tok_iter != tok.end(); ++tok_iter) // parses the input into a command and argument
        {
	    instruction.push_back(*tok_iter);
	}

        if (instruction[0] != "#") // checks for user input except for comments (#)
        {
            parseinator(instruction, command, argument, position, connect); // parses the input into a command and argument
            commandifier(command, argument, connect); // runs a command with a given argument
            command = "";
            argument = "";
            for (; position < instruction.size();) // run until the end of the instruction
            {
                parseinator(instruction, command, argument, position, connect);
                if (connect.runNext()) // checks connector to see if the next command should be ran
		{
                    commandifier(command, argument, connect);
                }
		else
		{
		    connect.run = false;
		    command = "";
		}
		command = "";
                argument = "";
            }
            position = 0;
        }
    }
    return 0;
}
void parseinator(vector<string> input, string& command, string& argument, int& position, Connector& connect)
{
    if (input[position] == "&") // check if string is equal to & connector
    {
        connect.type = 1; // set & connector to 1
        position += 2; // +=2 in order to take && connect
    }
    if (input[position] == "|")
    {
        connect.type = 2; // set | connector to 2
        position += 2; // +=2 in order to take || connector
    }
    if (input[position] == ";")
    {
        connect.type = 0; // set ; connector to 0
        position ++;
    }
    if (input[position] != "#")
    {
        command = input[position];
        position++;
    }
    for (; position < input.size(); position++)
    {
        if (input[position] == "#")
        {
            position = input.size();
            connect.type = 1;
            connect.run = false;
            break;
        }
        if (input[position] == "&" || input[position] == "|" || input[position] == ";")
	{
            break;
	}
        if (input[position] == "-")
        {
            while (input[position] != ";" && input[position] != "|" && input[position] != "&" && position+1 < input.size())
            {
                argument += input[position];
                position++;
            }
            if (position+1 == input.size() || input[position] == ";" || input[position] == "|" || input[position] == "&");
            {
                position++;
                break;
            }
            break;
        }
        argument += input[position];
        if (position+1 != input.size() && command == "echo")
  	{
            argument += " ";
	}
    }
}
void commandifier(string command, string argument, Connector& connect)
{
    const char * const_command = command.c_str();
    char char_command[command.size()];
    const char * const_argument = argument.c_str();
    char char_argument[argument.size()];
    strcpy (char_command, const_command);
    strcpy (char_argument, const_argument);
    char * args[2]; // char pointer array that holds command, and NULL
    char * args1[3]; // char pointer array that holds command, argument, and NULL
    bool no_arg = true;
    bool failed = false;

    if (argument.size() == 0) // no arguments
    {
        args[0] = char_command; // args will contain the command
        args[1] = NULL;
    }
    else
    {
	no_arg = false; // sets bool no_arg to false
	args1[0] = char_command; // args1 contains command and argument
        args1[1] = char_argument;
        args1[2] = NULL;
    }
    pid_t c_pid, pid; // data type to reporesent process ID's
    int status;
    c_pid = fork(); // create our fork

    if (c_pid < 0) // indicates the fork has fail if its less than 0
    {
        perror("fork failed");
        exit(1);
    }
    else if (c_pid == 0) // in child process
    {
 	if (no_arg) // no argument
	{
        	execvp( args[0], args); // execvp the char pointer array that holds the command only
        	if (execvp(args[0], args) == -1) // if it returns -1 it failed
                {
                    perror("execvp failed");
                    connect.run = false;
                    failed = true;
                }
	}
	else // with command and argument
	{
 		execvp(args1[0], args1); // execvp the char pointer array that holds the command, and argument
		if (execvp(args1[0], args1) == -1) // if it returns -1 it failed
                {
                    perror("execvp failed");
                    connect.run = false;
                    failed = true;
                }
	}
    }
    else if (c_pid > 0) // parent process
    {
        if ((pid = wait(&status)) < 0)
        {
            perror("wait");
            exit(1);
        }
    }
    if (!failed)
    {
        connect.run = true;
    }
}
<commit_msg>Updated with everything in it<commit_after>#include <iostream>
#include <string.h>
#include <boost/tokenizer.hpp>
#include <vector>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
using namespace std;
using namespace boost;

class Connector
{
    public:
        Connector();
        ~Connector();
        bool run;
        int type;
        bool runNext();
};

Connector::Connector()
{
    // the semicolon connector allows the next command to be execute always
    run = true;
    type = 0; // semicolon connector set to 0
}

Connector::~Connector()
{
}
bool Connector::runNext()
{
    if (type == 1) // && connector
    {
        if (!run) // if the first command doesn't succeed  it wont run the next command
        {
            return false;
        }
    }
    else if (type == 2) // || connector
    {
        if (run) // if the first command succeed it wont run the next command
        {
            return false;
        }
    }
    return true;
}
void parseinator(vector<string> input, string& command, string& argument, int& position, Connector& connect);
void commandifier(string command, string argument, Connector& connect);
int main()
{

    string str;
    string command;
    string argument;
    Connector connect;
    char hostname[100];
    int position = 0;
    while (true)
    {
        cout << getlogin(); // returns string containing name of user logged in
        gethostname(hostname, sizeof hostname); // returns the standard host name for the current machine
        cout << "@" << hostname << "$ ";
        getline(cin, str);
        if (str == "exit") // special command of exit
        {
            break;
        }
        vector<string> instruction; // vector of strings
        typedef tokenizer<boost::char_separator<char> > Tok;
        char_separator<char> sep; // default constructed
        Tok tok(str, sep);
        for (Tok::iterator tok_iter = tok.begin(); tok_iter != tok.end(); ++tok_iter) // parses the input into a command and argument
        {
	    instruction.push_back(*tok_iter);
	}

        if (instruction[0] != "#") // checks for user input except for comments (#)
        {
            parseinator(instruction, command, argument, position, connect); // parses the input into a command and argument
            commandifier(command, argument, connect); // runs a command with a given argument
            command = "";
            argument = "";
            for (; position < instruction.size();) // run until the end of the instruction
            {
                parseinator(instruction, command, argument, position, connect);
                if (connect.runNext()) // checks connector to see if the next command should be ran
		{
                    commandifier(command, argument, connect);
                }
		else
		{
		    connect.run = false;
		    command = "";
		}
		command = "";
                argument = "";
            }
            position = 0;
        }
    }
    return 0;
}
void parseinator(vector<string> input, string& command, string& argument, int& position, Connector& connect)
{
    if (input[position] == "&") // check if string is equal to & connector
    {
        connect.type = 1; // set & connector to 1
        position += 2; // +=2 in order to take && connect
    }
    if (input[position] == "|")
    {
        connect.type = 2; // set | connector to 2
        position += 2; // +=2 in order to take || connector
    }
    if (input[position] == ";")
    {
        connect.type = 0; // set ; connector to 0
        position ++;
    }
    if (input[position] != "#")
    {
        command = input[position];
        position++;
    }
    for (; position < input.size(); position++)
    {
        if (input[position] == "#")
        {
            position = input.size();
            connect.type = 1;
            connect.run = false;
            break;
        }
        if (input[position] == "&" || input[position] == "|" || input[position] == ";")
	{
            break;
	}
        argument += input[position];
        if (position+1 != input.size() && command == "echo")
  	{
            argument += " ";
	}
    }
}
void commandifier(string command, string argument, Connector& connect)
{
    const char * const_command = command.c_str();
    char char_command[command.size()];
    const char * const_argument = argument.c_str();
    char char_argument[argument.size()];
    strcpy (char_command, const_command);
    strcpy (char_argument, const_argument);
    char * args[2]; // char pointer array that holds command, and NULL
    char * args1[3]; // char pointer array that holds command, argument, and NULL
    bool no_arg = true;
    bool failed = false;

    if (argument.size() == 0) // no arguments
    {
        args[0] = char_command; // args will contain the command
        args[1] = NULL;
    }
    else
    {
	no_arg = false; // sets bool no_arg to false
	args1[0] = char_command; // args1 contains command and argument
        args1[1] = char_argument;
        args1[2] = NULL;
    }
    pid_t c_pid, pid; // data type to reporesent process ID's
    int status;
    c_pid = fork(); // create our fork

    if (c_pid < 0) // indicates the fork has fail if its less than 0
    {
        perror("fork failed");
        exit(1);
    }
    else if (c_pid == 0) // in child process
    {
 	if (no_arg) // no argument
	{
        	execvp( args[0], args); // execvp the char pointer array that holds the command only
        	if (execvp(args[0], args) == -1) // if it returns -1 it failed
                {
                    perror("execvp failed");
                    connect.run = false;
                    failed = true;
                }
	}
	else // with command and argument
	{
 		execvp(args1[0], args1); // execvp the char pointer array that holds the command, and argument
		if (execvp(args1[0], args1) == -1) // if it returns -1 it failed
                {
                    perror("execvp failed");
                    connect.run = false;
                    failed = true;
                }
	}
    }
    else if (c_pid > 0) // parent process
    {
        if ((pid = wait(&status)) < 0)
        {
            perror("wait");
            exit(1);
        }
    }
    if (!failed)
    {
        connect.run = true;
    }
}
<|endoftext|>


<commit_before>
#include <assert.h>
#include <thread>
#include <boost/atomic.hpp>

#include "LinkedList.hpp"

void test0() {
	LinkedList<int> l;
	assert(l.empty());
	l._checkSanity();

	auto item = l.push_back();
	item->value = 42;
	item = NULL;
	l._checkSanity();
	assert(!l.empty());
	assert(l.size() == 1);

	auto ret = l.pop_front();
	assert(ret);
	assert(ret->value == 42);
	ret = NULL;

	assert(l.empty());
	assert(l.size() == 0);
	l._checkSanity();
}

void test1() {
	LinkedList<int> l;
	l._checkSanity();

	for(int i = 0; i < 100; ++i) {
		auto item = l.push_back();
		l._checkSanity();
		item->value = i;
	}

	assert(!l.empty());
	assert(l.size() == 100);

	for(int i = 0; i < 100; ++i) {
		auto ret = l.pop_front();
		l._checkSanity();
		assert(ret);
		assert(ret->value == i);
	}

	assert(l.empty());
}

void test2() {
	LinkedList<int> l;
	l._checkSanity();

	auto producer = [&l](){
		for(int i = 0; i < 100; ++i) {
			LinkedList<int>::ItemPtr item(new LinkedList<int>::Item);
			item->value = i;
			l.push_back(item);
		}
	};

	auto consumer = [&l](){
		for(int i = 0; i < 100; ++i) {
			while(l.empty()) {} // wait for entry
			auto ret = l.pop_front();
			assert(ret);
			assert(ret->value == i);
		}
	};

	for(int i = 0; i < 1000; ++i) {
		std::thread t1(producer), t2(consumer);
		t1.join();
		t2.join();
		assert(l.empty());
		l._checkSanity();
	}
}

void test3() {
	LinkedList<int> l;
	boost::atomic<int> state;

	auto producer = [&l, &state](){
		for(int i = 0; i < 100; ++i) {
			if(i == 50) state++;
			LinkedList<int>::ItemPtr item(new LinkedList<int>::Item);
			item->value = i;
			l.push_back(item);
		}
		state++;
	};

	auto consumer = [&l, &state](){
		while(state == 0) {}
		for(int i = 0; i < 40; ++i) {
			while(l.empty()); // wait for entry
			auto ret = l.pop_front();
			assert(ret);
			assert(ret->value == i);
		}
		state++;
	};

	auto reader = [&l, &state]() {
		while(state < 3) {
			if(state == 0) continue;
			int first = -1;
			int old = -1;
			int count = 0;
			for(auto v : l) {
				assert(v >= 0);
				if(first < 0) first = v;
				if(old >= 0) assert(old < v);
				old = v;
				++count;
			}
			assert(count > 10);
		}
	};

	for(int i = 0; i < 1000; ++i) {
		state = 0;

		std::thread t1(producer), t2(consumer), t3(reader);
		t1.join();
		t2.join();
		t3.join();

		l._checkSanity();
		l.clear();
	}
}

void test4() {
	LinkedList<int> l;

	auto producer = [&l, &state](){
		for(int i = 3; i <= 100; ++i) {
			LinkedList<int>::ItemPtr item(new LinkedList<int>::Item);
			item->value = i;
			if(i < 50)
				l.push_back(item);
			else
				l.push_front(item);
		}
		state++;
	};

	auto reader = [&l, &state]() {
		int endCount = 0;
		while(true) {
			int old = -1;
			int m = 0;
			for(auto v : l) {
				assert(v >= 1);
				if(old == 100) assert(v == 1);
				else if(old >= 0) assert(old + 1 == v);
				old = v;
				if(v > m) m = v;
				++count;
			}
			assert(count >= 2);
			assert(count <= 100);
			assert(count == m);
			if(count < 100) assert(endCount == 0);
			if(count == 100) endCount++;
			if(endCount >= 10) break;
		}
	};

	for(int i = 0; i < 1000; ++i) {
		l.push_back()->value = 1;
		l.push_back()->value = 2;

		std::thread t1(producer), t3(reader);
		t1.join();
		t3.join();

		l._checkSanity();
		l.clear();
	}
}

int main() {
	test0();
	test1();
	test2();
	test3();
	test4();
}
<commit_msg>small fixes<commit_after>
#include <assert.h>
#include <thread>
#include <boost/atomic.hpp>

#include "LinkedList.hpp"

void test0() {
	LinkedList<int> l;
	assert(l.empty());
	l._checkSanity();

	auto item = l.push_back();
	item->value = 42;
	item = NULL;
	l._checkSanity();
	assert(!l.empty());
	assert(l.size() == 1);

	auto ret = l.pop_front();
	assert(ret);
	assert(ret->value == 42);
	ret = NULL;

	assert(l.empty());
	assert(l.size() == 0);
	l._checkSanity();
}

void test1() {
	LinkedList<int> l;
	l._checkSanity();

	for(int i = 0; i < 100; ++i) {
		auto item = l.push_back();
		l._checkSanity();
		item->value = i;
	}

	assert(!l.empty());
	assert(l.size() == 100);

	for(int i = 0; i < 100; ++i) {
		auto ret = l.pop_front();
		l._checkSanity();
		assert(ret);
		assert(ret->value == i);
	}

	assert(l.empty());
}

void test2() {
	LinkedList<int> l;
	l._checkSanity();

	auto producer = [&l](){
		for(int i = 0; i < 100; ++i) {
			LinkedList<int>::ItemPtr item(new LinkedList<int>::Item);
			item->value = i;
			l.push_back(item);
		}
	};

	auto consumer = [&l](){
		for(int i = 0; i < 100; ++i) {
			while(l.empty()) {} // wait for entry
			auto ret = l.pop_front();
			assert(ret);
			assert(ret->value == i);
		}
	};

	for(int i = 0; i < 1000; ++i) {
		std::thread t1(producer), t2(consumer);
		t1.join();
		t2.join();
		assert(l.empty());
		l._checkSanity();
	}
}

void test3() {
	LinkedList<int> l;
	boost::atomic<int> state;

	auto producer = [&l, &state](){
		for(int i = 0; i < 100; ++i) {
			if(i == 50) state++;
			LinkedList<int>::ItemPtr item(new LinkedList<int>::Item);
			item->value = i;
			l.push_back(item);
		}
		state++;
	};

	auto consumer = [&l, &state](){
		while(state == 0) {}
		for(int i = 0; i < 40; ++i) {
			while(l.empty()); // wait for entry
			auto ret = l.pop_front();
			assert(ret);
			assert(ret->value == i);
		}
		state++;
	};

	auto reader = [&l, &state]() {
		while(state < 3) {
			if(state == 0) continue;
			int first = -1;
			int old = -1;
			int count = 0;
			for(auto v : l) {
				assert(v >= 0);
				if(first < 0) first = v;
				if(old >= 0) assert(old < v);
				old = v;
				++count;
			}
			assert(count > 10);
		}
	};

	for(int i = 0; i < 1000; ++i) {
		state = 0;

		std::thread t1(producer), t2(consumer), t3(reader);
		t1.join();
		t2.join();
		t3.join();

		l._checkSanity();
		l.clear();
	}
}

void test4() {
	LinkedList<int> l;

	auto producer = [&l](){
		for(int i = 3; i <= 100; ++i) {
			LinkedList<int>::ItemPtr item(new LinkedList<int>::Item);
			item->value = i;
			if(i < 50)
				l.push_back(item);
			else
				l.push_front(item);
		}
	};

	auto reader = [&l]() {
		int endCount = 0;
		while(true) {
			int old = -1;
			int m = 0;
			int count = 0;
			for(auto v : l) {
				assert(v >= 1);
				if(old == 100) assert(v == 1);
				else if(old >= 0) assert(old + 1 == v);
				old = v;
				if(v > m) m = v;
				++count;
			}
			assert(count >= 2);
			assert(count <= 100);
			assert(count == m);
			if(count < 100) assert(endCount == 0);
			if(count == 100) endCount++;
			if(endCount >= 10) break;
		}
	};

	for(int i = 0; i < 1000; ++i) {
		l.push_back()->value = 1;
		l.push_back()->value = 2;

		std::thread t1(producer), t3(reader);
		t1.join();
		t3.join();

		l._checkSanity();
		l.clear();
	}
}

int main() {
	test0();
	test1();
	test2();
	test3();
	test4();
}
<|endoftext|>


<commit_before>#include "rtkfdk_ggo.h"
#include "rtkGgoFunctions.h"
#include "rtkConfiguration.h"

#include "itkThreeDCircularProjectionGeometryXMLFile.h"
#include "itkProjectionsReader.h"
#include "itkDisplacedDetectorImageFilter.h"
#include "itkParkerShortScanImageFilter.h"
#include "itkFDKWeightProjectionFilter.h"

#include "itkFFTRampImageFilter.h"
#include "itkFDKBackProjectionImageFilter.h"
#if CUDA_FOUND
#  include "itkCudaFFTRampImageFilter.h"
#  include "itkCudaFDKBackProjectionImageFilter.h"
#endif

#include <itkImageFileWriter.h>
#include <itkRegularExpressionSeriesFileNames.h>
#include <itkTimeProbe.h>
#include <itkStreamingImageFilter.h>

int main(int argc, char * argv[])
{
  GGO(rtkfdk, args_info);

  typedef float OutputPixelType;
  const unsigned int Dimension = 3;

  typedef itk::Image< OutputPixelType, Dimension > OutputImageType;

  // Generate file names
  itk::RegularExpressionSeriesFileNames::Pointer names = itk::RegularExpressionSeriesFileNames::New();
  names->SetDirectory(args_info.path_arg);
  names->SetNumericSort(false);
  names->SetRegularExpression(args_info.regexp_arg);
  names->SetSubMatch(0);

  if(args_info.verbose_flag)
    std::cout << "Regular expression matches "
              << names->GetFileNames().size()
              << " file(s)..."
              << std::endl;

  // Projections reader
  typedef itk::ProjectionsReader< OutputImageType > ReaderType;
  ReaderType::Pointer reader = ReaderType::New();
  reader->SetFileNames( names->GetFileNames() );
  reader->GenerateOutputInformation();

  itk::TimeProbe readerProbe;
  if(!args_info.lowmem_flag)
    {
    if(args_info.verbose_flag)
      std::cout << "Reading... " << std::flush;
    try {
      readerProbe.Start();
      reader->Update();
      readerProbe.Stop();
    } catch( itk::ExceptionObject & err ) {
      std::cerr << "ExceptionObject caught !" << std::endl;
      std::cerr << err << std::endl;
      return EXIT_FAILURE;
    }
    if(args_info.verbose_flag)
      std::cout << "It took " << readerProbe.GetMeanTime() << ' ' << readerProbe.GetUnit() << std::endl;
    }

  // Geometry
  if(args_info.verbose_flag)
    std::cout << "Reading geometry information from "
              << args_info.geometry_arg
              << "..."
              << std::endl;
  itk::ThreeDCircularProjectionGeometryXMLFileReader::Pointer geometryReader = itk::ThreeDCircularProjectionGeometryXMLFileReader::New();
  geometryReader->SetFilename(args_info.geometry_arg);
  geometryReader->GenerateOutputInformation();

  // Displaced detector weighting
  typedef itk::DisplacedDetectorImageFilter< OutputImageType > DDFType;
  DDFType::Pointer ddf = DDFType::New();
  ddf->SetInput( reader->GetOutput() );
  ddf->SetGeometry( geometryReader->GetOutputObject() );

  // Short scan image filter
  typedef itk::ParkerShortScanImageFilter< OutputImageType > PSSFType;
  PSSFType::Pointer pssf = PSSFType::New();
  pssf->SetInput( ddf->GetOutput() );
  pssf->SetGeometry( geometryReader->GetOutputObject() );
  pssf->SetNumberOfThreads(1);
  pssf->InPlaceOff();

  // Weight projections according to fdk algorithm
  typedef itk::FDKWeightProjectionFilter< OutputImageType > WeightFilterType;
  WeightFilterType::Pointer weightFilter = WeightFilterType::New();
  weightFilter->SetInput( pssf->GetOutput() );
  weightFilter->SetSourceToDetectorDistance( geometryReader->GetOutputObject()->GetSourceToDetectorDistance() );
  weightFilter->SetInPlace(false); //SR: there seems to be a bug in ITK: incompatibility between InPlace and streaming?

  // Ramp filter
  itk::ImageToImageFilter<OutputImageType, OutputImageType>::Pointer rampFilter;
  if(!strcmp(args_info.hardware_arg, "cuda"))
    {
#if !CUDA_FOUND
    std::cerr << "The program has not been compiled with cuda option" << std::endl;
    return EXIT_FAILURE;
#else
    typedef itk::CudaFFTRampImageFilter CUDARampFilterType;
    CUDARampFilterType::Pointer cudaRampFilter = CUDARampFilterType::New();
    cudaRampFilter->SetInput( weightFilter->GetOutput() );
    cudaRampFilter->SetTruncationCorrection(args_info.pad_arg);
    cudaRampFilter->SetHannCutFrequency(args_info.hann_arg);
    rampFilter = cudaRampFilter;
#endif
    }
  else
    {
    typedef itk::FFTRampImageFilter<OutputImageType> CPURampFilterType;
    CPURampFilterType::Pointer cpuRampFilter = CPURampFilterType::New();
    cpuRampFilter->SetInput( weightFilter->GetOutput() );
    cpuRampFilter->SetTruncationCorrection(args_info.pad_arg);
    cpuRampFilter->SetHannCutFrequency(args_info.hann_arg);
    rampFilter = cpuRampFilter;
    }

  // Streaming filter
  typedef itk::StreamingImageFilter<OutputImageType, OutputImageType> StreamerType;
  StreamerType::Pointer streamer = StreamerType::New();
  streamer->SetInput( rampFilter->GetOutput() );
  streamer->SetNumberOfStreamDivisions( geometryReader->GetOutputObject()->GetMatrices().size() );

  // Try to do all 2D pre-processing
  itk::TimeProbe streamerProbe;
  if(!args_info.lowmem_flag)
    {
    try
      {
      if(args_info.verbose_flag)
        std::cout << "Weighting and filtering projections... " << std::flush;
      streamerProbe.Start();
      streamer->Update();
      streamerProbe.Stop();
      if(args_info.verbose_flag)
        std::cout << "It took " << streamerProbe.GetMeanTime() << ' ' << readerProbe.GetUnit() << std::endl;
      }
    catch( itk::ExceptionObject & err )
      {
      std::cerr << "ExceptionObject caught !" << std::endl;
      std::cerr << err << std::endl;
      return EXIT_FAILURE;
      }
    }

  // Create reconstructed volume
  OutputImageType::Pointer tomography = rtk::CreateImageFromGgo<OutputImageType>(args_info);

  // Backprojection
  typedef itk::FDKBackProjectionImageFilter<OutputImageType, OutputImageType> BackProjectionFilterType;
  BackProjectionFilterType::Pointer bpFilter;
  if(!strcmp(args_info.hardware_arg, "cpu"))
    bpFilter = BackProjectionFilterType::New();
  else if(!strcmp(args_info.hardware_arg, "cuda"))
    {
#if CUDA_FOUND
    bpFilter = itk::CudaFDKBackProjectionImageFilter::New();
#else
    std::cerr << "The program has not been compiled with cuda option" << std::endl;
    return EXIT_FAILURE;
#endif
    }
  bpFilter->SetInput( 0, tomography );
  bpFilter->SetUpdateProjectionPerProjection(args_info.lowmem_flag);
  if(args_info.lowmem_flag)
    bpFilter->SetInput( 1, rampFilter->GetOutput() );
  else
    bpFilter->SetInput( 1, streamer->GetOutput() );
  bpFilter->SetGeometry( geometryReader->GetOutputObject() );
  bpFilter->SetInPlace( true );

  // SR: this appears to trigger 2 updates in cuda mode with the lowmem option
  //     and an off-centered geometry. No clue why... Disable this update
  //     until the problem is understood and solved.
  if(!args_info.lowmem_flag && args_info.divisions_arg==1)
    {
    if(args_info.verbose_flag)
      std::cout << "Backprojecting using "
                << args_info.hardware_arg
                << "... "  << std::flush;

    itk::TimeProbe bpProbe;
    try {
      bpProbe.Start();
      bpFilter->Update();
      bpProbe.Stop();
    } catch( itk::ExceptionObject & err ) {
      std::cerr << "ExceptionObject caught !" << std::endl;
      std::cerr << err << std::endl;
      return EXIT_FAILURE;
    }

    if(args_info.verbose_flag)
      std::cout << "It took " << bpProbe.GetMeanTime() << ' ' << readerProbe.GetUnit() << std::endl;
    }

  // Write
  typedef itk::ImageFileWriter<  OutputImageType >  WriterType;
  WriterType::Pointer writer = WriterType::New();
  writer->SetFileName( args_info.output_arg );
  writer->SetInput( bpFilter->GetOutput() );
  writer->SetNumberOfStreamDivisions( args_info.divisions_arg );

  if(args_info.verbose_flag)
    std::cout << "Writing... " << std::flush;
  itk::TimeProbe writerProbe;
  try {
    writerProbe.Start();
    writer->Update();
    writerProbe.Stop();
  } catch( itk::ExceptionObject & err ) {
    std::cerr << "ExceptionObject caught !" << std::endl;
    std::cerr << err << std::endl;
    return EXIT_FAILURE;
  }

  if(args_info.verbose_flag)
    std::cout << "It took " << writerProbe.GetMeanTime() << ' ' << readerProbe.GetUnit() << std::endl;

  return EXIT_SUCCESS;
}
<commit_msg>ITK bug for inplace image filters hits backprojection when using --divisions<commit_after>#include "rtkfdk_ggo.h"
#include "rtkGgoFunctions.h"
#include "rtkConfiguration.h"

#include "itkThreeDCircularProjectionGeometryXMLFile.h"
#include "itkProjectionsReader.h"
#include "itkDisplacedDetectorImageFilter.h"
#include "itkParkerShortScanImageFilter.h"
#include "itkFDKWeightProjectionFilter.h"

#include "itkFFTRampImageFilter.h"
#include "itkFDKBackProjectionImageFilter.h"
#if CUDA_FOUND
#  include "itkCudaFFTRampImageFilter.h"
#  include "itkCudaFDKBackProjectionImageFilter.h"
#endif

#include <itkImageFileWriter.h>
#include <itkRegularExpressionSeriesFileNames.h>
#include <itkTimeProbe.h>
#include <itkStreamingImageFilter.h>

int main(int argc, char * argv[])
{
  GGO(rtkfdk, args_info);

  typedef float OutputPixelType;
  const unsigned int Dimension = 3;

  typedef itk::Image< OutputPixelType, Dimension > OutputImageType;

  // Generate file names
  itk::RegularExpressionSeriesFileNames::Pointer names = itk::RegularExpressionSeriesFileNames::New();
  names->SetDirectory(args_info.path_arg);
  names->SetNumericSort(false);
  names->SetRegularExpression(args_info.regexp_arg);
  names->SetSubMatch(0);

  if(args_info.verbose_flag)
    std::cout << "Regular expression matches "
              << names->GetFileNames().size()
              << " file(s)..."
              << std::endl;

  // Projections reader
  typedef itk::ProjectionsReader< OutputImageType > ReaderType;
  ReaderType::Pointer reader = ReaderType::New();
  reader->SetFileNames( names->GetFileNames() );
  reader->GenerateOutputInformation();

  itk::TimeProbe readerProbe;
  if(!args_info.lowmem_flag)
    {
    if(args_info.verbose_flag)
      std::cout << "Reading... " << std::flush;
    try {
      readerProbe.Start();
      reader->Update();
      readerProbe.Stop();
    } catch( itk::ExceptionObject & err ) {
      std::cerr << "ExceptionObject caught !" << std::endl;
      std::cerr << err << std::endl;
      return EXIT_FAILURE;
    }
    if(args_info.verbose_flag)
      std::cout << "It took " << readerProbe.GetMeanTime() << ' ' << readerProbe.GetUnit() << std::endl;
    }

  // Geometry
  if(args_info.verbose_flag)
    std::cout << "Reading geometry information from "
              << args_info.geometry_arg
              << "..."
              << std::endl;
  itk::ThreeDCircularProjectionGeometryXMLFileReader::Pointer geometryReader = itk::ThreeDCircularProjectionGeometryXMLFileReader::New();
  geometryReader->SetFilename(args_info.geometry_arg);
  geometryReader->GenerateOutputInformation();

  // Displaced detector weighting
  typedef itk::DisplacedDetectorImageFilter< OutputImageType > DDFType;
  DDFType::Pointer ddf = DDFType::New();
  ddf->SetInput( reader->GetOutput() );
  ddf->SetGeometry( geometryReader->GetOutputObject() );

  // Short scan image filter
  typedef itk::ParkerShortScanImageFilter< OutputImageType > PSSFType;
  PSSFType::Pointer pssf = PSSFType::New();
  pssf->SetInput( ddf->GetOutput() );
  pssf->SetGeometry( geometryReader->GetOutputObject() );
  pssf->SetNumberOfThreads(1);
  pssf->InPlaceOff();

  // Weight projections according to fdk algorithm
  typedef itk::FDKWeightProjectionFilter< OutputImageType > WeightFilterType;
  WeightFilterType::Pointer weightFilter = WeightFilterType::New();
  weightFilter->SetInput( pssf->GetOutput() );
  weightFilter->SetSourceToDetectorDistance( geometryReader->GetOutputObject()->GetSourceToDetectorDistance() );
  weightFilter->SetInPlace(false); //SR: there seems to be a bug in ITK: incompatibility between InPlace and streaming?

  // Ramp filter
  itk::ImageToImageFilter<OutputImageType, OutputImageType>::Pointer rampFilter;
  if(!strcmp(args_info.hardware_arg, "cuda"))
    {
#if !CUDA_FOUND
    std::cerr << "The program has not been compiled with cuda option" << std::endl;
    return EXIT_FAILURE;
#else
    typedef itk::CudaFFTRampImageFilter CUDARampFilterType;
    CUDARampFilterType::Pointer cudaRampFilter = CUDARampFilterType::New();
    cudaRampFilter->SetInput( weightFilter->GetOutput() );
    cudaRampFilter->SetTruncationCorrection(args_info.pad_arg);
    cudaRampFilter->SetHannCutFrequency(args_info.hann_arg);
    rampFilter = cudaRampFilter;
#endif
    }
  else
    {
    typedef itk::FFTRampImageFilter<OutputImageType> CPURampFilterType;
    CPURampFilterType::Pointer cpuRampFilter = CPURampFilterType::New();
    cpuRampFilter->SetInput( weightFilter->GetOutput() );
    cpuRampFilter->SetTruncationCorrection(args_info.pad_arg);
    cpuRampFilter->SetHannCutFrequency(args_info.hann_arg);
    rampFilter = cpuRampFilter;
    }

  // Streaming filter
  typedef itk::StreamingImageFilter<OutputImageType, OutputImageType> StreamerType;
  StreamerType::Pointer streamer = StreamerType::New();
  streamer->SetInput( rampFilter->GetOutput() );
  streamer->SetNumberOfStreamDivisions( geometryReader->GetOutputObject()->GetMatrices().size() );

  // Try to do all 2D pre-processing
  itk::TimeProbe streamerProbe;
  if(!args_info.lowmem_flag)
    {
    try
      {
      if(args_info.verbose_flag)
        std::cout << "Weighting and filtering projections... " << std::flush;
      streamerProbe.Start();
      streamer->Update();
      streamerProbe.Stop();
      if(args_info.verbose_flag)
        std::cout << "It took " << streamerProbe.GetMeanTime() << ' ' << readerProbe.GetUnit() << std::endl;
      }
    catch( itk::ExceptionObject & err )
      {
      std::cerr << "ExceptionObject caught !" << std::endl;
      std::cerr << err << std::endl;
      return EXIT_FAILURE;
      }
    }

  // Create reconstructed volume
  OutputImageType::Pointer tomography = rtk::CreateImageFromGgo<OutputImageType>(args_info);

  // Backprojection
  typedef itk::FDKBackProjectionImageFilter<OutputImageType, OutputImageType> BackProjectionFilterType;
  BackProjectionFilterType::Pointer bpFilter;
  if(!strcmp(args_info.hardware_arg, "cpu"))
    bpFilter = BackProjectionFilterType::New();
  else if(!strcmp(args_info.hardware_arg, "cuda"))
    {
#if CUDA_FOUND
    bpFilter = itk::CudaFDKBackProjectionImageFilter::New();
#else
    std::cerr << "The program has not been compiled with cuda option" << std::endl;
    return EXIT_FAILURE;
#endif
    }
  bpFilter->SetInput( 0, tomography );
  bpFilter->SetUpdateProjectionPerProjection(args_info.lowmem_flag);
  if(args_info.lowmem_flag)
    bpFilter->SetInput( 1, rampFilter->GetOutput() );
  else
    bpFilter->SetInput( 1, streamer->GetOutput() );
  bpFilter->SetGeometry( geometryReader->GetOutputObject() );
  bpFilter->SetInPlace(false);

  // SR: this appears to trigger 2 updates in cuda mode with the lowmem option
  //     and an off-centered geometry. No clue why... Disable this update
  //     until the problem is understood and solved.
  if(!args_info.lowmem_flag && args_info.divisions_arg==1)
    {
    bpFilter->SetInPlace( true );
    if(args_info.verbose_flag)
      std::cout << "Backprojecting using "
                << args_info.hardware_arg
                << "... "  << std::flush;

    itk::TimeProbe bpProbe;
    try {
      bpProbe.Start();
      bpFilter->Update();
      bpProbe.Stop();
    } catch( itk::ExceptionObject & err ) {
      std::cerr << "ExceptionObject caught !" << std::endl;
      std::cerr << err << std::endl;
      return EXIT_FAILURE;
    }

    if(args_info.verbose_flag)
      std::cout << "It took " << bpProbe.GetMeanTime() << ' ' << readerProbe.GetUnit() << std::endl;
    }

  // Write
  typedef itk::ImageFileWriter<  OutputImageType >  WriterType;
  WriterType::Pointer writer = WriterType::New();
  writer->SetFileName( args_info.output_arg );
  writer->SetInput( bpFilter->GetOutput() );
  writer->SetNumberOfStreamDivisions( args_info.divisions_arg );

  if(args_info.verbose_flag)
    std::cout << "Writing... " << std::flush;
  itk::TimeProbe writerProbe;
  try {
    writerProbe.Start();
    writer->Update();
    writerProbe.Stop();
  } catch( itk::ExceptionObject & err ) {
    std::cerr << "ExceptionObject caught !" << std::endl;
    std::cerr << err << std::endl;
    return EXIT_FAILURE;
  }

  if(args_info.verbose_flag)
    std::cout << "It took " << writerProbe.GetMeanTime() << ' ' << readerProbe.GetUnit() << std::endl;

  return EXIT_SUCCESS;
}
<|endoftext|>


<commit_before>// $Id: parameters.C,v 1.3 2000/09/18 12:26:57 oliver Exp $
//

#include <BALL/FORMAT/parameters.h>

namespace BALL
{

	Parameters::Parameters()
	{
		valid_ = false;
	}

	Parameters::Parameters(const String& filename)
	{
		// try to read the parameter file
		INI_file_.setFilename(filename);
		init();
	}

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

	void Parameters::clear()
	{
		// clean up everything
		INI_file_.destroy();
		valid_ = false;
	}

	const String& Parameters::getFilename() const
	{
		return INI_file_.getFilename();
	}

	void Parameters::setFilename(const String& filename)
	{
		INI_file_.setFilename(filename);
	}

	INIFile& Parameters::getParameterFile()
	{
		return INI_file_;
	}

	bool Parameters::init()
	{
		// read the parameter file
		if (!INI_file_.read())
		{
			throw Exception::FileNotFound(__FILE__, __LINE__, INI_file_.getFilename().c_str());
		}

		// extract the AtomTypes section
		// set valid_ as extractSection checks for valid parameters!
		valid_ = INI_file_.isValid();

		return valid_;
	}

	bool Parameters::isValid() const
	{
		return (valid_ && INI_file_.isValid());
	}

} // namespace BALL
<commit_msg>added: assignment op, copy ctor<commit_after>// $Id: parameters.C,v 1.4 2000/09/18 14:38:07 oliver Exp $
//

#include <BALL/FORMAT/parameters.h>

namespace BALL
{

	Parameters::Parameters()
		:	valid_(false)
	{
	}

	Parameters::Parameters(const Parameters& parameters)
		:	valid_(parameters.valid_),
			INI_file_(parameters.INI_file_)
	{
		init();
	}

	Parameters::Parameters(const String& filename)
	{
		// try to read the parameter file
		INI_file_.setFilename(filename);
		init();
	}

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

	void Parameters::clear()
	{
		// clean up everything
		INI_file_.destroy();
		valid_ = false;
	}

	const Parameters& Parameters::operator = (const Parameters& parameters)
	{
		INI_file_ = parameters.INI_file_;
		valid_ = parameters.valid_;
		init();

		return *this;
	}

	const String& Parameters::getFilename() const
	{
		return INI_file_.getFilename();
	}

	void Parameters::setFilename(const String& filename)
	{
		INI_file_.setFilename(filename);
		init();
	}

	INIFile& Parameters::getParameterFile()
	{
		return INI_file_;
	}

	bool Parameters::init()
	{
		// read the parameter file
		if (!INI_file_.read())
		{
			throw Exception::FileNotFound(__FILE__, __LINE__, INI_file_.getFilename().c_str());
		}

		// extract the AtomTypes section
		// set valid_ as extractSection checks for valid parameters!
		valid_ = INI_file_.isValid();

		return valid_;
	}

	bool Parameters::isValid() const
	{
		return (valid_ && INI_file_.isValid());
	}

} // namespace BALL
<|endoftext|>


<commit_before>/*
 *
 */

#include "IPyraNet2DLayer.h"
#include <assert.h>

template<class OutType>
IPyraNet2DLayer<OutType>::IPyraNet2DLayer()
    : IPyraNetLayer<OutType>(),
    width(0),
    height(0),
    receptiveSize(0),
    overlap(0),
    inhibitorySize(0)
{

}

template<class OutType>
IPyraNet2DLayer<OutType>::IPyraNet2DLayer(int receptive, int inhibitory, int overlap, IPyraNetActivationFunction<OutType>* activationFunc)
    : IPyraNetLayer<OutType>(),
    width(0),
    height(0),
    receptiveSize(receptive),
    overlap(overlap),
    inhibitorySize(inhibitory)
{
    setActivationFunction(activationFunc);
}

template<class OutType>
IPyraNet2DLayer<OutType>::~IPyraNet2DLayer() {

}

template<class OutType>
OutType IPyraNet2DLayer<OutType>::getWeightedSumInput(int dimensions, int* neuronLocation) {

    // sanity checks
    assert (dimensions == 2);
    assert (neuronLocation != NULL);
    assert (neuronLocation[0] >= 0 && neuronLocation[1] >= 0);
    assert (getParentLayer() != NULL);
    assert (getActivationFunction() != NULL);

    // parent layer pointer
    IPyraNetLayer<OutType>* parent = getParentLayer();

    // compute the gap
    const int gap = receptiveSize - overlap;

    // just for compliance with the article (which uses 1..n indices)
    const int u = neuronLocation[0] + 1;
    const int v = neuronLocation[1] + 1;

    OutType receptiveAccumulator = 0;
    OutType bias = biases[neuronLocation[0]][neuronLocation[1]];

    int parentLoc[2];

    int parentSize[2];
    parent->getSize(parentSize);

    // iterate through the neurons inside the receptive field of the previous layer
    //
    // ******
    // **uv**
    // ******
    //
    const int min_u = (u - 1) * gap + 1;
    const int max_u = (u - 1) * gap + receptiveSize;
    const int min_v = (v - 1) * gap + 1;
    const int max_v = (v - 1) * gap + receptiveSize;

    for (int i = min_u; i <= max_u; ++i) {

        // indices on the paper (2) go from 1 to n. Our indices go from 0 to n-1
        parentLoc[0] = i - 1;

        // ignore neuron indices which fall outside of the layer
        if (parentLoc[0] < 0)
            continue;

        if (parentLoc[0] > parentSize[0])
            continue;

        for (int j = min_v; j <= max_v; ++j) {

            parentLoc[1] = j - 1;

            // ignore neuron indices which fall outside of the layer
            if (parentLoc[1] < 0)
                continue;

            if (parentLoc[1] > parentSize[1])
                continue;

            OutType parentOutput = parent->getNeuronOutput(2, parentLoc);
            OutType weight = weights[parentLoc[0]][parentLoc[1]];

            receptiveAccumulator += parentOutput * weight;
        }
    }

    // iterate through the neurons inside the inhibitory field
    //
    // xxxxxxxx
    // x******x
    // x**uv**x
    // x******x
    // xxxxxxxx
    //
    // the inhibitory field is 'x' and the receptive field is '*'
    OutType inhibitoryAccumulator = 0;
    const int inhibitory_min_u = min_u - inhibitorySize;
    const int inhibitory_max_u = max_u + inhibitorySize;
    const int inhibitory_min_v = min_v - inhibitorySize;
    const int inhibitory_max_v = max_v + inhibitorySize;

    for (int i = inhibitory_min_u; i <= inhibitory_max_u; ++i) {

        // indices on the paper (2) go from 1 to n. Our indices go from 0 to n-1
        parentLoc[0] = i - 1;

        // ignore neuron indices which fall outside of the layer
        if (parentLoc[0] < 0)
            continue;

        if (parentLoc[0] > parentSize[0])
            continue;

        for (int j = inhibitory_min_v; j <= inhibitory_max_v; ++j) {

            parentLoc[1] = j - 1;

            // ignore neuron indices which fall outside of the layer
            if (parentLoc[1] < 0)
                continue;

            if (parentLoc[1] > parentSize[1])
                continue;

            // ignore neurons in the receptive field!
            if (i >= min_u && i <= max_u)
                continue;

            if (j >= min_v && j <= max_v)
                continue;

            OutType parentOutput = parent->getNeuronOutput(2, parentLoc);
            OutType weight = weights[parentLoc[0]][parentLoc[1]];

            inhibitoryAccumulator += parentOutput * weight;
        }
    }

    return receptiveAccumulator - inhibitoryAccumulator + bias;
}

template<class OutType>
void IPyraNet2DLayer<OutType>::initWeights() {

    assert(getParentLayer() != NULL);

    IPyraNetLayer<OutType>* parent = getParentLayer();

    // get parent size
    int parentSize[2];
    parent->getSize(parentSize);

    // we need to have as many weights as the number of neurons in last
    // layer.
    weights.resize(parentSize[0]);

    for (int u = 0; u < parentSize[0]; ++u) {

        weights[u].resize(parentSize[1]);

        for (int v = 0; v < parentSize[1]; ++v) {
            weights[u][v] = UNIFORM_PLUS_MINUS_ONE;
        }
    }
}

template<class OutType>
void IPyraNet2DLayer<OutType>::initBiases() {

    biases.resize(width);

    for (int u = 0; u < width; ++u) {

        biases[u].resize(height);

        for (int v = 0; v < height; ++v) {
            biases[u][v] = UNIFORM_PLUS_MINUS_ONE;
        }
    }
}

template<class OutType>
OutType IPyraNet2DLayer<OutType>::getErrorSensitivity(int dimensions, int* neuronLocation, OutType multiplier) {

    OutType accumulator = getWeightedSumInput(dimensions, neuronLocation);

    // apply the activation function
    OutType result = getActivationFunction()->derivative(accumulator);

    return result * multiplier;
}

template<class OutType>
OutType IPyraNet2DLayer<OutType>::getNeuronOutput(int dimensions, int* neuronLocation) {

    OutType accumulator = getWeightedSumInput(dimensions, neuronLocation);

    OutType result = getActivationFunction()->compute(accumulator);

    return result;
}

template<class OutType>
int IPyraNet2DLayer<OutType>::getDimensions() const {
    return 2;
}

template<class OutType>
void IPyraNet2DLayer<OutType>::getSize(int* size) {
    assert(size != NULL);

    size[0] = width;
    size[1] = height;
}

template<class OutType>
OutType IPyraNet2DLayer<OutType>::getNeuronWeight(int dimensions, int* neuronLocation) {
    // weights are "per neuron", so there is a weight for each parent's neuron
    assert (dimensions == 2);
    assert (neuronLocation != NULL);
    assert (neuronLocation[0] >= 0 && neuronLocation[1] >= 0);

    return weights[neuronLocation[0]][neuronLocation[1]];
}

template<class OutType>
void IPyraNet2DLayer<OutType>::setNeuronWeight(int dimensions, int* neuronLocation, OutType value) {
    // weights are "per neuron", so there is a weight for each parent's neuron
    assert (dimensions == 2);
    assert (neuronLocation != NULL);
    assert (neuronLocation[0] >= 0 && neuronLocation[1] >= 0);

    weights[neuronLocation[0]][neuronLocation[1]] = value;
}

template<class OutType>
OutType IPyraNet2DLayer<OutType>::getNeuronBias(int dimensions, int* neuronLocation) {
    // one bias for each neuron in this layer
    assert (dimensions == 2);
    assert (neuronLocation != NULL);
    assert (neuronLocation[0] >= 0 && neuronLocation[1] >= 0);

    return biases[neuronLocation[0]][neuronLocation[1]];
}

template<class OutType>
void IPyraNet2DLayer<OutType>::setNeuronBias(int dimensions, int* neuronLocation, OutType value) {
    // one bias for each neuron in this layer
    assert (dimensions == 2);
    assert (neuronLocation != NULL);
    assert (neuronLocation[0] >= 0 && neuronLocation[1] >= 0);

    biases[neuronLocation[0]][neuronLocation[1]] = value;
}

template<class OutType>
void IPyraNet2DLayer<OutType>::setParentLayer(IPyraNetLayer<OutType>* parent, bool init) {

    assert(parent != NULL);
    assert(receptiveSize != 0);
    assert(overlap != 0);

    // calls base class
    IPyraNetLayer<OutType>::setParentLayer(parent);

    const int dims = parent->getDimensions();

    // we can just connect 2d layers to 2d layers
    assert(dims == 2);

    // get parent size
    int parentSize[2];
    parent->getSize(parentSize);

    // compute the gap
    const float gap = static_cast<float>(receptiveSize - overlap);

    int newWidth = static_cast<int>(floor(static_cast<float>(parentSize[0] - overlap) / gap));
    int newHeight = static_cast<int>(floor(static_cast<float>(parentSize[1] - overlap) / gap));

    // init weights and biases
    if (init) {
        width = newWidth;
        height = newHeight;

        initWeights();
        initBiases();
    } /*else {
        assert (width == newWidth);
        assert (height == newHeight);
    }*/
}

template<class OutType>
void IPyraNet2DLayer<OutType>::saveToXML(pugi::xml_node& node) {

    // save the size
    pugi::xml_attribute widthAttr = node.append_attribute("width");
    widthAttr.set_value(width);

    pugi::xml_attribute heightAttr = node.append_attribute("height");
    heightAttr.set_value(height);

    // receptive, overlap and inhibitory
    pugi::xml_attribute receptiveAttr = node.append_attribute("receptive");
    receptiveAttr.set_value(receptiveSize);

    pugi::xml_attribute overlapAttr = node.append_attribute("overlap");
    overlapAttr.set_value(overlap);

    pugi::xml_attribute inhibitoryAttr = node.append_attribute("inhibitory");
    inhibitoryAttr.set_value(inhibitorySize);

    // dump the weights
    pugi::xml_node weightsNode = node.append_child("weights");
    weightsNode.append_attribute("dim1").set_value(weights.size());
    weightsNode.append_attribute("dim2").set_value(weights[0].size());

    for (unsigned int u = 0; u < weights.size(); ++u) {
        for (unsigned int v = 0; v < weights[u].size(); ++v) {
            pugi::xml_node weightNode = weightsNode.append_child("weight");

            // weight indices as attributes
            pugi::xml_attribute index1Attr = weightNode.append_attribute("index1");
            index1Attr.set_value(u);

            pugi::xml_attribute index2Attr = weightNode.append_attribute("index2");
            index2Attr.set_value(v);

            pugi::xml_attribute weightAttr = weightNode.append_attribute("value");
            weightAttr.set_value(weights[u][v]);
        }
    }

    // dump the biases
    pugi::xml_node biasesNode = node.append_child("biases");
    biasesNode.append_attribute("dim1").set_value(biases.size());
    biasesNode.append_attribute("dim2").set_value(biases[0].size());
    for (int u = 0; u < width; ++u) {
        for (int v = 0; v < height; ++v) {
            pugi::xml_node biasNode = biasesNode.append_child("bias");

            // weight indices as attributes
            pugi::xml_attribute index1Attr = biasNode.append_attribute("index1");
            index1Attr.set_value(u);

            pugi::xml_attribute index2Attr = biasNode.append_attribute("index2");
            index2Attr.set_value(v);

            pugi::xml_attribute biasAttr = biasNode.append_attribute("value");
            biasAttr.set_value(biases[u][v]);
        }
    }
}

template<class OutType>
void IPyraNet2DLayer<OutType>::loadFromXML(pugi::xml_node& node) {

    // load the size
    width = node.attribute("width").as_int();
    height = node.attribute("height").as_int();

    // receptive, inhibitory and overlaps
    receptiveSize = node.attribute("receptive").as_int();
    overlap = node.attribute("overlap").as_int();
    inhibitorySize = node.attribute("inhibitory").as_int();

    // reshape weights buffer and load weights
    size_t dim1 = node.child("weights").attribute("dim1").as_uint();
    size_t dim2 = node.child("weights").attribute("dim2").as_uint();

    weights.resize(dim1);
    for (size_t k = 0; k < dim1; ++k)
        weights[k].resize(dim2);

    // actual load from XML
    for (pugi::xml_node weight = node.child("weights").child("weight"); weight; weight = weight.next_sibling("weight")) {

        size_t weightIndex1 = weight.attribute("index1").as_uint();
        size_t weightIndex2 = weight.attribute("index2").as_uint();

        weights[weightIndex1][weightIndex2] = static_cast<OutType>(weight.attribute("value").as_double());
    }

    // load biases
    dim1 = node.child("biases").attribute("dim1").as_uint();
    dim2 = node.child("biases").attribute("dim2").as_uint();

    biases.resize(dim1);
    for (size_t k = 0; k < dim1; ++k)
        biases[k].resize(dim2);

    for (pugi::xml_node bias = node.child("biases").child("bias"); bias; bias = bias.next_sibling("bias")) {

        size_t biasIndex1 = bias.attribute("index1").as_uint();
        size_t biasIndex2 = bias.attribute("index2").as_uint();

        biases[biasIndex1][biasIndex2] = static_cast<OutType>(bias.attribute("value").as_double());
    }
}

// explicit instantiations
template class IPyraNet2DLayer<float>;
template class IPyraNet2DLayer<double>;<commit_msg>Fixed a bug which made the training crash when using inhibitory field sizes greater than 1.<commit_after>/*
 *
 */

#include "IPyraNet2DLayer.h"
#include <assert.h>

template<class OutType>
IPyraNet2DLayer<OutType>::IPyraNet2DLayer()
    : IPyraNetLayer<OutType>(),
    width(0),
    height(0),
    receptiveSize(0),
    overlap(0),
    inhibitorySize(0)
{

}

template<class OutType>
IPyraNet2DLayer<OutType>::IPyraNet2DLayer(int receptive, int inhibitory, int overlap, IPyraNetActivationFunction<OutType>* activationFunc)
    : IPyraNetLayer<OutType>(),
    width(0),
    height(0),
    receptiveSize(receptive),
    overlap(overlap),
    inhibitorySize(inhibitory)
{
    setActivationFunction(activationFunc);
}

template<class OutType>
IPyraNet2DLayer<OutType>::~IPyraNet2DLayer() {

}

template<class OutType>
OutType IPyraNet2DLayer<OutType>::getWeightedSumInput(int dimensions, int* neuronLocation) {

    // sanity checks
    assert (dimensions == 2);
    assert (neuronLocation != NULL);
    assert (neuronLocation[0] >= 0 && neuronLocation[1] >= 0);
    assert (getParentLayer() != NULL);
    assert (getActivationFunction() != NULL);

    // parent layer pointer
    IPyraNetLayer<OutType>* parent = getParentLayer();

    // compute the gap
    const int gap = receptiveSize - overlap;

    // just for compliance with the article (which uses 1..n indices)
    const int u = neuronLocation[0] + 1;
    const int v = neuronLocation[1] + 1;

    OutType receptiveAccumulator = 0;
    OutType bias = biases[neuronLocation[0]][neuronLocation[1]];

    int parentLoc[2];

    int parentSize[2];
    parent->getSize(parentSize);

    // iterate through the neurons inside the receptive field of the previous layer
    //
    // ******
    // **uv**
    // ******
    //
    const int min_u = (u - 1) * gap + 1;
    const int max_u = (u - 1) * gap + receptiveSize;
    const int min_v = (v - 1) * gap + 1;
    const int max_v = (v - 1) * gap + receptiveSize;

    for (int i = min_u; i <= max_u; ++i) {

        // indices on the paper (2) go from 1 to n. Our indices go from 0 to n-1
        parentLoc[0] = i - 1;

        // ignore neuron indices which fall outside of the layer
        if (parentLoc[0] < 0)
            continue;

        if (parentLoc[0] >= parentSize[0])
            continue;

        for (int j = min_v; j <= max_v; ++j) {

            parentLoc[1] = j - 1;

            // ignore neuron indices which fall outside of the layer
            if (parentLoc[1] < 0)
                continue;

            if (parentLoc[1] >= parentSize[1])
                continue;

            OutType parentOutput = parent->getNeuronOutput(2, parentLoc);
            OutType weight = weights[parentLoc[0]][parentLoc[1]];

            receptiveAccumulator += parentOutput * weight;
        }
    }

    // iterate through the neurons inside the inhibitory field
    //
    // xxxxxxxx
    // x******x
    // x**uv**x
    // x******x
    // xxxxxxxx
    //
    // the inhibitory field is 'x' and the receptive field is '*'
    OutType inhibitoryAccumulator = 0;
    const int inhibitory_min_u = min_u - inhibitorySize;
    const int inhibitory_max_u = max_u + inhibitorySize;
    const int inhibitory_min_v = min_v - inhibitorySize;
    const int inhibitory_max_v = max_v + inhibitorySize;

    for (int i = inhibitory_min_u; i <= inhibitory_max_u; ++i) {

        // indices on the paper (2) go from 1 to n. Our indices go from 0 to n-1
        parentLoc[0] = i - 1;

        // ignore neuron indices which fall outside of the layer
        if (parentLoc[0] < 0)
            continue;

        if (parentLoc[0] >= parentSize[0])
            continue;

        for (int j = inhibitory_min_v; j <= inhibitory_max_v; ++j) {

            parentLoc[1] = j - 1;

            // ignore neuron indices which fall outside of the layer
            if (parentLoc[1] < 0)
                continue;

            if (parentLoc[1] >= parentSize[1])
                continue;

            // ignore neurons in the receptive field!
            if (i >= min_u && i <= max_u)
                continue;

            if (j >= min_v && j <= max_v)
                continue;

            OutType parentOutput = parent->getNeuronOutput(2, parentLoc);
            OutType weight = weights[parentLoc[0]][parentLoc[1]];

            inhibitoryAccumulator += parentOutput * weight;
        }
    }

    return receptiveAccumulator - inhibitoryAccumulator + bias;
}

template<class OutType>
void IPyraNet2DLayer<OutType>::initWeights() {

    assert(getParentLayer() != NULL);

    IPyraNetLayer<OutType>* parent = getParentLayer();

    // get parent size
    int parentSize[2];
    parent->getSize(parentSize);

    // we need to have as many weights as the number of neurons in last
    // layer.
    weights.resize(parentSize[0]);

    for (int u = 0; u < parentSize[0]; ++u) {

        weights[u].resize(parentSize[1]);

        for (int v = 0; v < parentSize[1]; ++v) {
            weights[u][v] = UNIFORM_PLUS_MINUS_ONE;
        }
    }
}

template<class OutType>
void IPyraNet2DLayer<OutType>::initBiases() {

    biases.resize(width);

    for (int u = 0; u < width; ++u) {

        biases[u].resize(height);

        for (int v = 0; v < height; ++v) {
            biases[u][v] = UNIFORM_PLUS_MINUS_ONE;
        }
    }
}

template<class OutType>
OutType IPyraNet2DLayer<OutType>::getErrorSensitivity(int dimensions, int* neuronLocation, OutType multiplier) {

    OutType accumulator = getWeightedSumInput(dimensions, neuronLocation);

    // apply the activation function
    OutType result = getActivationFunction()->derivative(accumulator);

    return result * multiplier;
}

template<class OutType>
OutType IPyraNet2DLayer<OutType>::getNeuronOutput(int dimensions, int* neuronLocation) {

    OutType accumulator = getWeightedSumInput(dimensions, neuronLocation);

    OutType result = getActivationFunction()->compute(accumulator);

    return result;
}

template<class OutType>
int IPyraNet2DLayer<OutType>::getDimensions() const {
    return 2;
}

template<class OutType>
void IPyraNet2DLayer<OutType>::getSize(int* size) {
    assert(size != NULL);

    size[0] = width;
    size[1] = height;
}

template<class OutType>
OutType IPyraNet2DLayer<OutType>::getNeuronWeight(int dimensions, int* neuronLocation) {
    // weights are "per neuron", so there is a weight for each parent's neuron
    assert (dimensions == 2);
    assert (neuronLocation != NULL);
    assert (neuronLocation[0] >= 0 && neuronLocation[1] >= 0);

    return weights[neuronLocation[0]][neuronLocation[1]];
}

template<class OutType>
void IPyraNet2DLayer<OutType>::setNeuronWeight(int dimensions, int* neuronLocation, OutType value) {
    // weights are "per neuron", so there is a weight for each parent's neuron
    assert (dimensions == 2);
    assert (neuronLocation != NULL);
    assert (neuronLocation[0] >= 0 && neuronLocation[1] >= 0);

    weights[neuronLocation[0]][neuronLocation[1]] = value;
}

template<class OutType>
OutType IPyraNet2DLayer<OutType>::getNeuronBias(int dimensions, int* neuronLocation) {
    // one bias for each neuron in this layer
    assert (dimensions == 2);
    assert (neuronLocation != NULL);
    assert (neuronLocation[0] >= 0 && neuronLocation[1] >= 0);

    return biases[neuronLocation[0]][neuronLocation[1]];
}

template<class OutType>
void IPyraNet2DLayer<OutType>::setNeuronBias(int dimensions, int* neuronLocation, OutType value) {
    // one bias for each neuron in this layer
    assert (dimensions == 2);
    assert (neuronLocation != NULL);
    assert (neuronLocation[0] >= 0 && neuronLocation[1] >= 0);

    biases[neuronLocation[0]][neuronLocation[1]] = value;
}

template<class OutType>
void IPyraNet2DLayer<OutType>::setParentLayer(IPyraNetLayer<OutType>* parent, bool init) {

    assert(parent != NULL);
    assert(receptiveSize != 0);
    assert(overlap != 0);

    // calls base class
    IPyraNetLayer<OutType>::setParentLayer(parent);

    const int dims = parent->getDimensions();

    // we can just connect 2d layers to 2d layers
    assert(dims == 2);

    // get parent size
    int parentSize[2];
    parent->getSize(parentSize);

    // compute the gap
    const float gap = static_cast<float>(receptiveSize - overlap);

    int newWidth = static_cast<int>(floor(static_cast<float>(parentSize[0] - overlap) / gap));
    int newHeight = static_cast<int>(floor(static_cast<float>(parentSize[1] - overlap) / gap));

    // init weights and biases
    if (init) {
        width = newWidth;
        height = newHeight;

        initWeights();
        initBiases();
    } /*else {
        assert (width == newWidth);
        assert (height == newHeight);
    }*/
}

template<class OutType>
void IPyraNet2DLayer<OutType>::saveToXML(pugi::xml_node& node) {

    // save the size
    pugi::xml_attribute widthAttr = node.append_attribute("width");
    widthAttr.set_value(width);

    pugi::xml_attribute heightAttr = node.append_attribute("height");
    heightAttr.set_value(height);

    // receptive, overlap and inhibitory
    pugi::xml_attribute receptiveAttr = node.append_attribute("receptive");
    receptiveAttr.set_value(receptiveSize);

    pugi::xml_attribute overlapAttr = node.append_attribute("overlap");
    overlapAttr.set_value(overlap);

    pugi::xml_attribute inhibitoryAttr = node.append_attribute("inhibitory");
    inhibitoryAttr.set_value(inhibitorySize);

    // dump the weights
    pugi::xml_node weightsNode = node.append_child("weights");
    weightsNode.append_attribute("dim1").set_value(weights.size());
    weightsNode.append_attribute("dim2").set_value(weights[0].size());

    for (unsigned int u = 0; u < weights.size(); ++u) {
        for (unsigned int v = 0; v < weights[u].size(); ++v) {
            pugi::xml_node weightNode = weightsNode.append_child("weight");

            // weight indices as attributes
            pugi::xml_attribute index1Attr = weightNode.append_attribute("index1");
            index1Attr.set_value(u);

            pugi::xml_attribute index2Attr = weightNode.append_attribute("index2");
            index2Attr.set_value(v);

            pugi::xml_attribute weightAttr = weightNode.append_attribute("value");
            weightAttr.set_value(weights[u][v]);
        }
    }

    // dump the biases
    pugi::xml_node biasesNode = node.append_child("biases");
    biasesNode.append_attribute("dim1").set_value(biases.size());
    biasesNode.append_attribute("dim2").set_value(biases[0].size());
    for (int u = 0; u < width; ++u) {
        for (int v = 0; v < height; ++v) {
            pugi::xml_node biasNode = biasesNode.append_child("bias");

            // weight indices as attributes
            pugi::xml_attribute index1Attr = biasNode.append_attribute("index1");
            index1Attr.set_value(u);

            pugi::xml_attribute index2Attr = biasNode.append_attribute("index2");
            index2Attr.set_value(v);

            pugi::xml_attribute biasAttr = biasNode.append_attribute("value");
            biasAttr.set_value(biases[u][v]);
        }
    }
}

template<class OutType>
void IPyraNet2DLayer<OutType>::loadFromXML(pugi::xml_node& node) {

    // load the size
    width = node.attribute("width").as_int();
    height = node.attribute("height").as_int();

    // receptive, inhibitory and overlaps
    receptiveSize = node.attribute("receptive").as_int();
    overlap = node.attribute("overlap").as_int();
    inhibitorySize = node.attribute("inhibitory").as_int();

    // reshape weights buffer and load weights
    size_t dim1 = node.child("weights").attribute("dim1").as_uint();
    size_t dim2 = node.child("weights").attribute("dim2").as_uint();

    weights.resize(dim1);
    for (size_t k = 0; k < dim1; ++k)
        weights[k].resize(dim2);

    // actual load from XML
    for (pugi::xml_node weight = node.child("weights").child("weight"); weight; weight = weight.next_sibling("weight")) {

        size_t weightIndex1 = weight.attribute("index1").as_uint();
        size_t weightIndex2 = weight.attribute("index2").as_uint();

        weights[weightIndex1][weightIndex2] = static_cast<OutType>(weight.attribute("value").as_double());
    }

    // load biases
    dim1 = node.child("biases").attribute("dim1").as_uint();
    dim2 = node.child("biases").attribute("dim2").as_uint();

    biases.resize(dim1);
    for (size_t k = 0; k < dim1; ++k)
        biases[k].resize(dim2);

    for (pugi::xml_node bias = node.child("biases").child("bias"); bias; bias = bias.next_sibling("bias")) {

        size_t biasIndex1 = bias.attribute("index1").as_uint();
        size_t biasIndex2 = bias.attribute("index2").as_uint();

        biases[biasIndex1][biasIndex2] = static_cast<OutType>(bias.attribute("value").as_double());
    }
}

// explicit instantiations
template class IPyraNet2DLayer<float>;
template class IPyraNet2DLayer<double>;<|endoftext|>


<commit_before>/*!
    \file filecache.cpp
    \brief File cache implementation
    \author Ivan Shynkarenka
    \date 14.05.2019
    \copyright MIT License
*/

#include "cache/filecache.h"

namespace CppCommon {

bool FileCache::emplace(std::string&& key, std::string&& value, const Timespan& timeout)
{
    std::unique_lock<std::shared_mutex> locker(_lock);

    // Try to find and remove the previous key
    remove_internal(key);

    if (timeout.total() > 0)
    {
        Timestamp current = UtcTimestamp();
        _timestamp = (current <= _timestamp) ? _timestamp + 1 : current;
        _entries_by_key.insert(std::make_pair(key, MemCacheEntry(std::move(value), _timestamp, timeout)));
        _entries_by_timestamp.insert(std::make_pair(_timestamp, key));
    }
    else
        _entries_by_key.emplace(std::make_pair(std::move(key), MemCacheEntry(std::move(value))));

    return true;
}

bool FileCache::insert(const std::string& key, const std::string& value, const Timespan& timeout)
{
    std::unique_lock<std::shared_mutex> locker(_lock);

    // Try to find and remove the previous key
    remove_internal(key);

    if (timeout.total() > 0)
    {
        Timestamp current = UtcTimestamp();
        _timestamp = (current <= _timestamp) ? _timestamp + 1 : current;
        _entries_by_key.insert(std::make_pair(key, MemCacheEntry(value, _timestamp, timeout)));
        _entries_by_timestamp.insert(std::make_pair(_timestamp, key));
    }
    else
        _entries_by_key.insert(std::make_pair(key, MemCacheEntry(value)));

    return true;
}

bool FileCache::insert_path(const CppCommon::Path& path, const std::string& prefix, const Timespan& timeout, const std::function<bool (FileCache& cache, const std::string& key, const std::string& value, const Timespan& timeout)>& handler)
{
    try
    {
        const std::string key_prefix = (prefix.empty() || (prefix == "/")) ? "/" : (prefix + "/");

        // Iterate through all directory entries
        for (const auto& entry : CppCommon::Directory(path))
        {
            const std::string key = prefix + CppCommon::Encoding::URLDecode(entry.filename().string());

            if (entry.IsDirectory())
            {
                // Recursively load sub-directory
                if (!insert_path(entry, key, timeout, handler))
                    return false;
            }
            else
            {
                try
                {
                    // Load file content
                    auto content = CppCommon::File::ReadAllBytes(entry);
                    std::string value(content.begin(), content.end());
                    if (!handler(*this, key, value, timeout))
                        return false;
                }
                catch (const CppCommon::FileSystemException&) { return false; }
            }
        }

        return true;
    }
    catch (const CppCommon::FileSystemException&) { return false; }
}

std::pair<bool, std::string_view> FileCache::find(const std::string& key)
{
    std::shared_lock<std::shared_mutex> locker(_lock);

    // Try to find the given key
    auto it = _entries_by_key.find(key);
    if (it == _entries_by_key.end())
        return std::make_pair(false, std::string_view());

    return std::make_pair(true, std::string_view(it->second.value));
}

std::pair<bool, std::string_view> FileCache::find(const std::string& key, Timestamp& timeout)
{
    std::shared_lock<std::shared_mutex> locker(_lock);

    // Try to find the given key
    auto it = _entries_by_key.find(key);
    if (it == _entries_by_key.end())
        return std::make_pair(false, std::string_view());

    timeout = Timestamp((it->second.timestamp + it->second.timespan).total());
    return std::make_pair(true, std::string_view(it->second.value));
}

bool FileCache::remove(const std::string& key)
{
    std::unique_lock<std::shared_mutex> locker(_lock);

    return remove_internal(key);
}

bool FileCache::remove_internal(const std::string& key)
{
    // Try to find the given key
    auto it = _entries_by_key.find(key);
    if (it == _entries_by_key.end())
        return false;

    // Try to erase cache entry by timestamp
    if (it->second.timestamp.total() > 0)
        _entries_by_timestamp.erase(it->second.timestamp);

    // Erase cache entry
    _entries_by_key.erase(it);

    return true;
}

void FileCache::clear()
{
    std::unique_lock<std::shared_mutex> locker(_lock);

    // Clear all cache entries
    _entries_by_key.clear();
    _entries_by_timestamp.clear();
}

void FileCache::watchdog(const UtcTimestamp& utc)
{
    auto it_by_timestamp = _entries_by_timestamp.begin();
    while (it_by_timestamp != _entries_by_timestamp.end())
    {
        auto it_by_key = _entries_by_key.find(it_by_timestamp->second);

        // Check for the cache entry timeout
        if ((it_by_key->second.timestamp + it_by_key->second.timespan) <= utc)
        {
            _entries_by_key.erase(it_by_key);
            _entries_by_timestamp.erase(it_by_timestamp);
            it_by_timestamp = _entries_by_timestamp.begin();
            continue;
        }
        else
            break;
    }
}

void FileCache::swap(FileCache& cache) noexcept
{
    std::unique_lock<std::shared_mutex> locker1(_lock);
    std::unique_lock<std::shared_mutex> locker2(cache._lock);

    using std::swap;
    swap(_timestamp, cache._timestamp);
    swap(_entries_by_key, cache._entries_by_key);
    swap(_entries_by_timestamp, cache._entries_by_timestamp);
}

} // namespace CppCommon
<commit_msg>update<commit_after>/*!
    \file filecache.cpp
    \brief File cache implementation
    \author Ivan Shynkarenka
    \date 14.05.2019
    \copyright MIT License
*/

#include "cache/filecache.h"

namespace CppCommon {

bool FileCache::emplace(std::string&& key, std::string&& value, const Timespan& timeout)
{
    std::unique_lock<std::shared_mutex> locker(_lock);

    // Try to find and remove the previous key
    remove_internal(key);

    if (timeout.total() > 0)
    {
        Timestamp current = UtcTimestamp();
        _timestamp = (current <= _timestamp) ? _timestamp + 1 : current;
        _entries_by_key.insert(std::make_pair(key, MemCacheEntry(std::move(value), _timestamp, timeout)));
        _entries_by_timestamp.insert(std::make_pair(_timestamp, key));
    }
    else
        _entries_by_key.emplace(std::make_pair(std::move(key), MemCacheEntry(std::move(value))));

    return true;
}

bool FileCache::insert(const std::string& key, const std::string& value, const Timespan& timeout)
{
    std::unique_lock<std::shared_mutex> locker(_lock);

    // Try to find and remove the previous key
    remove_internal(key);

    if (timeout.total() > 0)
    {
        Timestamp current = UtcTimestamp();
        _timestamp = (current <= _timestamp) ? _timestamp + 1 : current;
        _entries_by_key.insert(std::make_pair(key, MemCacheEntry(value, _timestamp, timeout)));
        _entries_by_timestamp.insert(std::make_pair(_timestamp, key));
    }
    else
        _entries_by_key.insert(std::make_pair(key, MemCacheEntry(value)));

    return true;
}

bool FileCache::insert_path(const CppCommon::Path& path, const std::string& prefix, const Timespan& timeout, const std::function<bool (FileCache& cache, const std::string& key, const std::string& value, const Timespan& timeout)>& handler)
{
    try
    {
        const std::string key_prefix = (prefix.empty() || (prefix == "/")) ? "/" : (prefix + "/");

        // Iterate through all directory entries
        for (const auto& entry : CppCommon::Directory(path))
        {
            const std::string key = key_prefix + CppCommon::Encoding::URLDecode(entry.filename().string());

            if (entry.IsDirectory())
            {
                // Recursively load sub-directory
                if (!insert_path(entry, key, timeout, handler))
                    return false;
            }
            else
            {
                try
                {
                    // Load file content
                    auto content = CppCommon::File::ReadAllBytes(entry);
                    std::string value(content.begin(), content.end());
                    if (!handler(*this, key, value, timeout))
                        return false;
                }
                catch (const CppCommon::FileSystemException&) { return false; }
            }
        }

        return true;
    }
    catch (const CppCommon::FileSystemException&) { return false; }
}

std::pair<bool, std::string_view> FileCache::find(const std::string& key)
{
    std::shared_lock<std::shared_mutex> locker(_lock);

    // Try to find the given key
    auto it = _entries_by_key.find(key);
    if (it == _entries_by_key.end())
        return std::make_pair(false, std::string_view());

    return std::make_pair(true, std::string_view(it->second.value));
}

std::pair<bool, std::string_view> FileCache::find(const std::string& key, Timestamp& timeout)
{
    std::shared_lock<std::shared_mutex> locker(_lock);

    // Try to find the given key
    auto it = _entries_by_key.find(key);
    if (it == _entries_by_key.end())
        return std::make_pair(false, std::string_view());

    timeout = Timestamp((it->second.timestamp + it->second.timespan).total());
    return std::make_pair(true, std::string_view(it->second.value));
}

bool FileCache::remove(const std::string& key)
{
    std::unique_lock<std::shared_mutex> locker(_lock);

    return remove_internal(key);
}

bool FileCache::remove_internal(const std::string& key)
{
    // Try to find the given key
    auto it = _entries_by_key.find(key);
    if (it == _entries_by_key.end())
        return false;

    // Try to erase cache entry by timestamp
    if (it->second.timestamp.total() > 0)
        _entries_by_timestamp.erase(it->second.timestamp);

    // Erase cache entry
    _entries_by_key.erase(it);

    return true;
}

void FileCache::clear()
{
    std::unique_lock<std::shared_mutex> locker(_lock);

    // Clear all cache entries
    _entries_by_key.clear();
    _entries_by_timestamp.clear();
}

void FileCache::watchdog(const UtcTimestamp& utc)
{
    auto it_by_timestamp = _entries_by_timestamp.begin();
    while (it_by_timestamp != _entries_by_timestamp.end())
    {
        auto it_by_key = _entries_by_key.find(it_by_timestamp->second);

        // Check for the cache entry timeout
        if ((it_by_key->second.timestamp + it_by_key->second.timespan) <= utc)
        {
            _entries_by_key.erase(it_by_key);
            _entries_by_timestamp.erase(it_by_timestamp);
            it_by_timestamp = _entries_by_timestamp.begin();
            continue;
        }
        else
            break;
    }
}

void FileCache::swap(FileCache& cache) noexcept
{
    std::unique_lock<std::shared_mutex> locker1(_lock);
    std::unique_lock<std::shared_mutex> locker2(cache._lock);

    using std::swap;
    swap(_timestamp, cache._timestamp);
    swap(_entries_by_key, cache._entries_by_key);
    swap(_entries_by_timestamp, cache._entries_by_timestamp);
}

} // namespace CppCommon
<|endoftext|>


<commit_before>/*!
    \file password.cpp
    \brief Password string implementation
    \author Ivan Shynkarenka
    \date 04.06.2019
    \copyright MIT License
*/

#include "string/password.h"

#if defined(unix) || defined(__unix) || defined(__unix__)
#include <string.h>
#elif defined(_WIN32) || defined(_WIN64)
#include <windows.h>
#endif

namespace CppCommon {

void ZeroPasswordMemory(volatile void* buffer, size_t size)
{
#if defined(unix) || defined(__unix) || defined(__unix__)
    memset(buffer, 0, size);
    volatile char* ptr = (volatile char*)buffer;
    while (size--) *ptr++ = 0;
#elif defined(_WIN32) || defined(_WIN64)
    SecureZeroMemory(buffer, size);
#else
    #error Unsupported platform
#endif
}

} // namespace CppCommon
<commit_msg>update<commit_after>/*!
    \file password.cpp
    \brief Password string implementation
    \author Ivan Shynkarenka
    \date 04.06.2019
    \copyright MIT License
*/

#include "string/password.h"

#if defined(unix) || defined(__unix) || defined(__unix__)
#include <string.h>
#elif defined(_WIN32) || defined(_WIN64)
#include <windows.h>
#endif

namespace CppCommon {

void ZeroPasswordMemory(volatile void* buffer, size_t size)
{
#if defined(unix) || defined(__unix) || defined(__unix__)
    volatile char* ptr = (volatile char*)buffer;
    while (size--) *ptr++ = 0;
#elif defined(_WIN32) || defined(_WIN64)
    SecureZeroMemory(buffer, size);
#else
    #error Unsupported platform
#endif
}

} // namespace CppCommon
<|endoftext|>


<commit_before>/*
 * Worldvisions Weaver Software:
 *   Copyright (C) 1997, 1998 Worldvisions Computer Technology, Inc.
 *
 * Intelligent serial port scanner: try to find a port (or ports)
 * with a working modem, guess a basic startup init string, and find
 * the maximum baud rate.
 */
#include "wvmodemscan.h"
#include "wvmodem.h"
#include "strutils.h"
#include <time.h>
#include <assert.h>
#include <dirent.h>
#include <ctype.h>
#include <sys/stat.h>

// startup at atz atq0 atv1 ate1 ats0 carrier dtr fastdial
// baudstep reinit done
static char *commands[WvModemScan::NUM_STAGES] = {
    NULL, "", "Z", "Q0", "V1", "E1", "S0=0", "&C1", "&D2", "S11=55",
    "+FCLASS=0", NULL,
    NULL, "", NULL
};

WvModemScan::WvModemScan(const char *devname)
	: debug(devname, WvLog::Debug)
{
    stage = Startup;
    memset(status, 0, sizeof(status));

    if (devname[0] == '/')
	file = devname;
    else
	file = WvString("/dev/%s", devname);

    baud = 1200;
    modem = NULL;
    tries = 0;
    broken = false;
}

WvModemScan::~WvModemScan()
{
    if (isok() && isdone())
	debug(WvLog::Info, "Speed %s; init \"%s\"\n", maxbaud(), initstr());

    if (modem)
	delete modem;
}

bool WvModemScan::isok() const
{
    return !broken;
}

WvString WvModemScan::initstr() const
{
    WvString s;
    s.setsize(100);
    strcpy(s.str, "AT");

    for (int i = 0; i < NUM_STAGES; i++)
    {
	if (status[i] != Worked && status[i] != Test)
	    continue;
	if (!commands[i] || !commands[i][0])
	    continue;
	if ((commands[i][0]=='Z' || commands[i][0]=='I') && status[i] != Test)
	    continue;

	strcat(s.str, commands[i]);
	strcat(s.str, " ");
    }

    return trim_string(s.str);
}

void WvModemScan::execute()
{
    if (isdone() || !isok()) return;

    switch ((Stage)stage)
    {
    case Startup:
	assert(!modem);
	modem = new WvModem(file.str, baud);
	modem->die_fast = true;
	if (!modem->isok())
	{
	    if (modem->geterr()
		&& modem->geterr() != EIO
		&& modem->geterr() != ENOENT
		&& modem->geterr() != ENODEV)
	    {
		debug(WvLog::Info, "%s\n", modem->errstr());
	    }
	    broken = true;
	}
	else
	    stage++;
	break;

    case AT:
    case ATZ:
    case ATQ0:
    case ATV1:
    case ATE1:
    case ATS0:
    case Carrier:
    case DTR:
    case FastDial:
    case FCLASS:
    case Reinit:
	assert(modem);
	status[stage] = Test;
	if ( !doresult(WvString("%s\r", initstr()), stage==ATZ ? 3000 : 500)
	    || (stage <= ATZ && status[stage]==Fail) )
	{
	    tries++;

	    if (tries >= 3)
	    {
		debug("nothing.\n");
		broken = true;
	    }

	    // else try again shortly
	}
	else
	{
	    tries = 0;
	    stage++;
	}
	break;

    case GetIdent:
	assert(modem);
	status[stage] = Test;
	debug("Modem Identifier: ");
	if ( !doresult(WvString("ATI\r"), 500) || (status[stage]==Fail) )
	{
	    tries++;

	    if (tries >= 3)
	    {
	    	debug("nothing.\n");
	    }

	    // else try again shortly
	}
	else
	{
	    if (is_isdn())
	    	debug("Looks like an ISDN modem.\n");
	    tries = 0;
	    stage++;
	}

    case BaudStep:
	assert(modem);
	modem->drain();
	modem->speed(baud*2);

	// if we try 2*baud five times without success, or setting 2*baud
	// results in a lower setting than 1*baud, we have reached the
	// top speed of the modem or the serial port, respectively.
	if (tries >= 3 || modem->speed() < baud)
	{
	    // using the absolute maximum baud rate confuses many modems
	    // in obscure ways; step down one.
	    modem->speed(baud);
	    debug("Max speed is %s; ", modem->speed());
	    if (is_isdn())
	    {
	    	//if (modem->speed() != 230400)		// FIXME
	    	    modem->speed(115200);
	    	baud = modem->speed();
	    	debug ("using %s for ISDN modem.\n", baud);
	    }
	    else
	    {
		modem->speed(baud / 2);
		baud = modem->speed(); // get correct value!
		debug("using %s to be safe.\n", baud);
	    }

	    stage++;
	    status[stage] = Worked;
	    break;
	}

	debug("Speed %s: ", modem->speed());

	if (!doresult("AT\r", 500) || status[stage] == Fail)
	{
	    tries++;
	}
	else // got a response
	{
	    baud *= 2;
	    tries = 0;
	    // next time through we try a faster speed
	}
	break;

    case Done:
    case NUM_STAGES:
	assert(0);
	break; // should never happen
    }

    if (stage == Done) // we just incremented stage number to Done
    {
	if (modem)
	    delete modem;
	modem = NULL;
    }
}

bool WvModemScan::doresult(const WvString &s, int msec)
{
    char buf[1024], *cptr;
    size_t len;

    modem->drain();
    usleep(50 * 1000); // delay a bit after emptying the buffer
    modem->write(s);

    debug("%s -- ", trim_string(s.str));

    len = coagulate(buf, sizeof(buf), msec);

    if (!len)
    {
	// debug("(no answer yet)\n");
	return false;
    }

    buf[len] = 0;

    cptr = trim_string(buf);
    while (strchr(cptr, '\r'))
    {
	cptr = trim_string(strchr(cptr, '\r'));
	if (stage == GetIdent && status[stage] == Test)
	{
	    char *p = strpbrk(cptr, "\n\r");
	    if (p) *p=0;
	    identifier = cptr;
	    status[stage] = Worked;
	    debug("%s\n", identifier);
	    return true;
	}
    }
    while (strchr(cptr, '\n'))
	cptr = trim_string(strchr(cptr, '\n'));

    debug("%s\n", cptr);

    if (!strncmp(cptr, "OK", 2)
	|| (stage <= ATV1 && *(strchr(cptr,0) - 1)=='0'))
    {
	status[stage] = Worked;
    }
    else
	status[stage] = Fail;

    return true;
}

size_t WvModemScan::coagulate(char *buf, size_t size, int msec)
{
    size_t len = 0, amt;
    char *cptr = buf;

    assert(modem);

    if (!modem->isok())
    {
	broken = true;
	return 0;
    }

    while (modem->select(msec))
    {
	amt = modem->read(cptr, size-1);
	cptr[amt] = 0;

	len += amt;
	size -= amt;
	cptr += amt;

	if (strstr(buf, "OK") || strstr(buf, "ERROR"))
	    break;
    }

    return len;
}

char *WvModemScan::is_isdn() const
{
    if (!identifier.str[0])
    	return NULL;

    if (identifier == "3C882")		// 3Com Impact IQ
    	return identifier.str;
    if (identifier == "960")		// Motorola BitSurfr
    	return identifier.str;

    return NULL;
}

static int fileselect(const struct dirent *e)
{
    return !strncmp(e->d_name, "ttyS", 4);     // serial
       // (no intenral ISDN support)   || !strncmp(e->d_name, "ttyI", 4);
}

static int filesort(const struct dirent **e1, const struct dirent **e2)
{
    const char *p1, *p2;
    int diff;

    for (p1=(*e1)->d_name, p2=(*e2)->d_name; *p1 || *p2; p1++, p2++)
    {
	if (!isdigit(*p1) || !isdigit(*p2))
	{
	    diff = *p1 - *p2;
	    if (diff) return diff;
	}
	else // both are digits
	{
	    return atoi(p1) - atoi(p2);
	}
    }

    return 0;
}

void WvModemScanList::setup()
{
    struct dirent **namelist;
    struct stat mouse;
    int num, count, statresponse;

    log = new WvLog("Port Scan", WvLog::Debug);
    thisline = -1;
    printed = false;

    statresponse = stat("/dev/mouse", &mouse);
    num = scandir("/dev", &namelist, fileselect, filesort);

    if (num < 0)
	return;

    for (count = 0; count < num; count++)
    {
	// never search the device assigned to /dev/mouse; most mouse-using
	// programs neglect to lock the device, so we could mess up the
	// mouse response!  (We are careful to put things back when done,
	// but X seems to still get confused.)  Anyway the mouse is seldom
	// a modem.
	if (statresponse==0 && mouse.st_ino == (ino_t)namelist[count]->d_ino)
	{
	    log->print("\nIgnoring %s because /dev/mouse is a link to it.\n",
		       namelist[count]->d_name);
	    continue;
	}

	append(new WvModemScan(namelist[count]->d_name), true);
    }

    while (--num >= 0)
	free(namelist[num]);
    free(namelist);
}

void WvModemScanList::shutdown()
{
    delete log;
}

// we used to try to scan all ports simultaneously; unfortunately, this
// caused problems when people had "noncritical" IRQ conflicts (ie. two
// serial ports with the same IRQ, but they work as long as only one port
// is used at a time).  Also, the log messages looked really confused.
//
// So now we do the scanning sequentially, which is slower.  The port
// being scanned is at the head of the list.  If the probe fails, we
// unlink the element.  If it succeeds, we have found a modem -- so,
// isdone() knows we are done when the _first_ element is done.
//
void WvModemScanList::execute()
{
    assert (!isdone());

    WvModemScanList::Iter i(*this);

    i.rewind();
    if (!i.next()) return; // note: the first next() is the _first_ item!

    WvModemScan *s = i.data();

    if (!s->isok())
    {
	if (!printed)
	{
	    const WvString &f = s->filename();
	    const char *cptr = strrchr(f.str, '/');
	    if (cptr)
		cptr++;
	    else
		cptr = f.str;

	    if (!strncmp(cptr, "tty", 3))
		cptr += 3;

	    ++thisline %= 8;
	    if (!thisline)
		log->print("\n");

	    log->print("%-4s ", cptr);
	}

	i.unlink();
	printed = false;
    }
    else
    {
	s->execute();

	if (s->isok())
	    printed = true;
    }

    if (isdone())
	log->write("\n", 1);
}

bool WvModemScanList::isdone()
{
    WvModemScanList::Iter i(*this);

    i.rewind();
    if (i.next())
	return i.data()->isdone();
    else
	return true; // empty list, so we are done!
}
<commit_msg><commit_after>/*
 * Worldvisions Weaver Software:
 *   Copyright (C) 1997, 1998 Worldvisions Computer Technology, Inc.
 *
 * Intelligent serial port scanner: try to find a port (or ports)
 * with a working modem, guess a basic startup init string, and find
 * the maximum baud rate.
 */
#include "wvmodemscan.h"
#include "wvmodem.h"
#include "strutils.h"
#include <time.h>
#include <assert.h>
#include <dirent.h>
#include <ctype.h>
#include <sys/stat.h>

// startup at atz atq0 atv1 ate1 ats0 carrier dtr fastdial
// baudstep reinit done
static char *commands[WvModemScan::NUM_STAGES] = {
    NULL, "", "Z", "Q0", "V1", "E1", "S0=0", "&C1", "&D2", "S11=55",
    "+FCLASS=0", NULL,
    NULL, "", NULL
};

WvModemScan::WvModemScan(const char *devname)
	: debug(devname, WvLog::Debug)
{
    stage = Startup;
    memset(status, 0, sizeof(status));

    if (devname[0] == '/')
	file = devname;
    else
	file = WvString("/dev/%s", devname);

    baud = 1200;
    modem = NULL;
    tries = 0;
    broken = false;
}

WvModemScan::~WvModemScan()
{
    if (isok() && isdone())
	debug(WvLog::Info, "Speed %s; init \"%s\"\n", maxbaud(), initstr());

    if (modem)
	delete modem;
}

bool WvModemScan::isok() const
{
    return !broken;
}

WvString WvModemScan::initstr() const
{
    WvString s;
    s.setsize(100);
    strcpy(s.str, "AT");

    for (int i = 0; i < NUM_STAGES; i++)
    {
	if (status[i] != Worked && status[i] != Test)
	    continue;
	if (!commands[i] || !commands[i][0])
	    continue;
	if ((commands[i][0]=='Z' || commands[i][0]=='I') && status[i] != Test)
	    continue;

	strcat(s.str, commands[i]);
	strcat(s.str, " ");
    }

    return trim_string(s.str);
}

void WvModemScan::execute()
{
    if (isdone() || !isok()) return;

    switch ((Stage)stage)
    {
    case Startup:
	assert(!modem);
	modem = new WvModem(file.str, baud);
	modem->die_fast = true;
	if (!modem->isok())
	{
	    if (modem->geterr()
		&& modem->geterr() != EIO
		&& modem->geterr() != ENOENT
		&& modem->geterr() != ENODEV)
	    {
		debug(WvLog::Info, "%s\n", modem->errstr());
	    }
	    broken = true;
	}
	else
	    stage++;
	break;

    case AT:
    case ATZ:
    case ATQ0:
    case ATV1:
    case ATE1:
    case ATS0:
    case Carrier:
    case DTR:
    case FastDial:
    case FCLASS:
    case Reinit:
	assert(modem);
	status[stage] = Test;
	if ( !doresult(WvString("%s\r", initstr()), stage==ATZ ? 3000 : 500)
	    || (stage <= ATZ && status[stage]==Fail) )
	{
	    tries++;

	    if (tries >= 3)
	    {
		debug("nothing.\n");
		broken = true;
	    }

	    // else try again shortly
	}
	else
	{
	    tries = 0;
	    stage++;
	}
	break;

    case GetIdent:
	assert(modem);
	status[stage] = Test;
	debug("Modem Identifier: ");
	if ( !doresult(WvString("ATI\r"), 500) || (status[stage]==Fail) )
	{
	    tries++;

	    if (tries >= 3)
	    {
	    	debug("nothing.\n");
	    }

	    // else try again shortly
	}
	else
	{
	    if (is_isdn())
	    	debug("Looks like an ISDN modem.\n");
	    tries = 0;
	    stage++;
	}

    case BaudStep:
	assert(modem);
	modem->drain();
	modem->speed(baud*2);

	// if we try 2*baud five times without success, or setting 2*baud
	// results in a lower setting than 1*baud, we have reached the
	// top speed of the modem or the serial port, respectively.
	if (tries >= 3 || modem->speed() < baud)
	{
	    // using the absolute maximum baud rate confuses many modems
	    // in obscure ways; step down one.
	    modem->speed(baud);
	    debug("Max speed is %s; ", modem->speed());
	    if (is_isdn())
	    {
	    	//if (modem->speed() != 230400)		// FIXME
	    	    modem->speed(115200);
	    	baud = modem->speed();
	    	debug ("using %s for ISDN modem.\n", baud);
	    }
	    else
	    {
		modem->speed(baud / 2);
		baud = modem->speed(); // get correct value!
		debug("using %s to be safe.\n", baud);
	    }

	    stage++;
	    status[stage] = Worked;
	    break;
	}

	debug("Speed %s: ", modem->speed());

	if (!doresult("AT\r", 500) || status[stage] == Fail)
	{
	    tries++;
	}
	else // got a response
	{
	    baud *= 2;
	    tries = 0;
	    // next time through we try a faster speed
	}
	break;

    case Done:
    case NUM_STAGES:
	assert(0);
	break; // should never happen
    }

    if (stage == Done) // we just incremented stage number to Done
    {
	if (modem)
	    delete modem;
	modem = NULL;
    }
}

bool WvModemScan::doresult(const WvString &s, int msec)
{
    char buf[1024], *cptr;
    size_t len;

    modem->drain();
    usleep(50 * 1000); // delay a bit after emptying the buffer
    modem->write(s);

    debug("%s -- ", trim_string(s.str));

    len = coagulate(buf, sizeof(buf), msec);

    if (!len)
    {
	// debug("(no answer yet)\n");
	return false;
    }

    buf[len] = 0;

    cptr = trim_string(buf);
    while (strchr(cptr, '\r'))
    {
	cptr = trim_string(strchr(cptr, '\r'));
	if (stage == GetIdent && status[stage] == Test)
	{
	    char *p = strpbrk(cptr, "\n\r");
	    if (p) *p=0;
	    identifier = cptr;
	    status[stage] = Worked;
	    debug("%s\n", identifier);
	    return true;
	}
    }
    while (strchr(cptr, '\n'))
	cptr = trim_string(strchr(cptr, '\n'));

    debug("%s\n", cptr);

    if (!strncmp(cptr, "OK", 2)
	|| (stage <= ATV1 && *(strchr(cptr,0) - 1)=='0'))
    {
	status[stage] = Worked;
    }
    else
	status[stage] = Fail;

    return true;
}

size_t WvModemScan::coagulate(char *buf, size_t size, int msec)
{
    size_t len = 0, amt;
    char *cptr = buf;

    assert(modem);

    if (!modem->isok())
    {
	broken = true;
	return 0;
    }

    while (modem->select(msec))
    {
	amt = modem->read(cptr, size-1);
	cptr[amt] = 0;

	len += amt;
	size -= amt;
	cptr += amt;

	if (strstr(buf, "OK") || strstr(buf, "ERROR"))
	    break;
    }

    return len;
}

char *WvModemScan::is_isdn() const
{
    if (!identifier.str[0])
    	return NULL;

    if (identifier == "3C882")		// 3Com Impact IQ
    	return identifier.str;
    if (identifier == "960")		// Motorola BitSurfr
    	return identifier.str;

    return NULL;
}

static int fileselect(const struct dirent *e)
{
    return !strncmp(e->d_name, "ttyS", 4);     // serial
       // (no intenral ISDN support)   || !strncmp(e->d_name, "ttyI", 4);
}

static int filesort(const void *_e1, const void *_e2)
{
    dirent const * const *e1 = (dirent const * const *)_e1;
    dirent const * const *e2 = (dirent const * const *)_e2;
    const char *p1, *p2;
    int diff;

    for (p1=(*e1)->d_name, p2=(*e2)->d_name; *p1 || *p2; p1++, p2++)
    {
	if (!isdigit(*p1) || !isdigit(*p2))
	{
	    diff = *p1 - *p2;
	    if (diff) return diff;
	}
	else // both are digits
	{
	    return atoi(p1) - atoi(p2);
	}
    }

    return 0;
}

void WvModemScanList::setup()
{
    struct dirent **namelist;
    struct stat mouse;
    int num, count, statresponse;

    log = new WvLog("Port Scan", WvLog::Debug);
    thisline = -1;
    printed = false;

    statresponse = stat("/dev/mouse", &mouse);
    num = scandir("/dev", &namelist, fileselect, filesort);

    if (num < 0)
	return;

    for (count = 0; count < num; count++)
    {
	// never search the device assigned to /dev/mouse; most mouse-using
	// programs neglect to lock the device, so we could mess up the
	// mouse response!  (We are careful to put things back when done,
	// but X seems to still get confused.)  Anyway the mouse is seldom
	// a modem.
	if (statresponse==0 && mouse.st_ino == (ino_t)namelist[count]->d_ino)
	{
	    log->print("\nIgnoring %s because /dev/mouse is a link to it.\n",
		       namelist[count]->d_name);
	    continue;
	}

	append(new WvModemScan(namelist[count]->d_name), true);
    }

    while (--num >= 0)
	free(namelist[num]);
    free(namelist);
}

void WvModemScanList::shutdown()
{
    delete log;
}

// we used to try to scan all ports simultaneously; unfortunately, this
// caused problems when people had "noncritical" IRQ conflicts (ie. two
// serial ports with the same IRQ, but they work as long as only one port
// is used at a time).  Also, the log messages looked really confused.
//
// So now we do the scanning sequentially, which is slower.  The port
// being scanned is at the head of the list.  If the probe fails, we
// unlink the element.  If it succeeds, we have found a modem -- so,
// isdone() knows we are done when the _first_ element is done.
//
void WvModemScanList::execute()
{
    assert (!isdone());

    WvModemScanList::Iter i(*this);

    i.rewind();
    if (!i.next()) return; // note: the first next() is the _first_ item!

    WvModemScan *s = i.data();

    if (!s->isok())
    {
	if (!printed)
	{
	    const WvString &f = s->filename();
	    const char *cptr = strrchr(f.str, '/');
	    if (cptr)
		cptr++;
	    else
		cptr = f.str;

	    if (!strncmp(cptr, "tty", 3))
		cptr += 3;

	    ++thisline %= 8;
	    if (!thisline)
		log->print("\n");

	    log->print("%-4s ", cptr);
	}

	i.unlink();
	printed = false;
    }
    else
    {
	s->execute();

	if (s->isok())
	    printed = true;
    }

    if (isdone())
	log->write("\n", 1);
}

bool WvModemScanList::isdone()
{
    WvModemScanList::Iter i(*this);

    i.rewind();
    if (i.next())
	return i.data()->isdone();
    else
	return true; // empty list, so we are done!
}
<|endoftext|>