<commit_before>
#include "GameCtrl.h"
#include "util/util.h"
#include <stdexcept>
#include <cstdio>
#include <chrono>
#include <cstdlib>
#ifdef _WIN32
#include <Windows.h>
#endif

using std::string;
using std::list;

// Error statements
const string GameCtrl::MSG_BAD_ALLOC = "Not enough memory to run the game.";
const string GameCtrl::MSG_LOSE = "Oops! You lose!";
const string GameCtrl::MSG_WIN = "Congratulations! You Win!";
const string GameCtrl::MSG_ESC = "Game ended.";
const string GameCtrl::MAP_INFO_FILENAME = "movements.txt";
int select_path=0; //Select Min Path or Max Path

GameCtrl::GameCtrl() {}

// class Destroyer
GameCtrl::~GameCtrl() {
    delete map;
    map = nullptr;
    if (movementFile) {
        fclose(movementFile);
        movementFile = nullptr;
    }
}

// Return the class instance's address
// Return instance will be used for creating and running game in main function
GameCtrl* GameCtrl::getInstance() {
=======
GameCtrl* GameCtrl::getInstance(int n) {

    static GameCtrl instance;
	select_path=n;
    return &instance;
}

void GameCtrl::setFPS(const double fps_) {
    fps = fps_;
}

// set program enable AI based search to decide next point to visit
// enableAI_(parameter) is boolean type whether the program use AI based search or not
void GameCtrl::setEnableAI(const bool enableAI_) {
    enableAI = enableAI_;
}

// set program enable Hamilton search to decide next point to visit
// enableHamilton_(parameter) is boolean type whether the program use Hamilton based search or not
void GameCtrl::setEnableHamilton(const bool enableHamilton_) {
    enableHamilton = enableHamilton_;
}

void GameCtrl::setMoveInterval(const long ms) {
    moveInterval = ms;
}

// set program records the snake's movement in text file
// b(parameter) is boolean type whether the program record snake's movement or not
void GameCtrl::setRecordMovements(const bool b) {
    recordMovements = b;
}

void GameCtrl::setRunTest(const bool b) {
    runTest = b;
}

void GameCtrl::setMapRow(const SizeType n) {
    mapRowCnt = n;
}

void GameCtrl::setMapCol(const SizeType n) {
    mapColCnt = n;
}

// essential function to working the program. this function call init() to initialize some variables to start game
// return 0 : successfully exit game
// return 1 : error is occured during game is running
int GameCtrl::run() {
    try {
        init();
        if (runTest) {
            test();
        }
        while (runMainThread) {}
        return 0;
    } catch (const std::exception &e) {
        exitGameErr(e.what());
        return -1;
    }
}

void GameCtrl::sleepFPS() const {
    util::sleep((long)((1.0 / fps) * 1000));
}

// this function is called when the program is needed to end
// msg(parameter) contains error message
// this function use mutex lock because when a thread access critical section, other two thread must not access same crtical section.
// ex) when draw thread is errored, and at the same time , search thread is also errored,
//     the first errored thread access critical section, and then search thread access critical section
void GameCtrl::exitGame(const std::string &msg) {
    mutexExit.lock();
    if (runMainThread) {
        util::sleep(100);
        runSubThread = false;
        util::sleep(100);
        printMsg(msg);
    }
    mutexExit.unlock();
    runMainThread = false;
}

void GameCtrl::exitGameErr(const std::string &err) {
    exitGame("ERROR: " + err);
}

void GameCtrl::printMsg(const std::string &msg) {
    Console::setCursor(0, (int)mapRowCnt);
    Console::writeWithColor(msg + "\n", ConsoleColor(WHITE, BLACK, true, false));
}

// this function is for moving snake one step more closer to food
// this function also use mutex lock because manually moving mode,
// user input many keys, but the process must execute move(), writeMapToFile(), CreateRandFood() one by one
void GameCtrl::moveSnake() {
    mutexMove.lock();
    if (map->isAllBody()) {
        mutexMove.unlock();
        exitGame(MSG_WIN);
    } else if (snake.isDead()) {
        mutexMove.unlock();
        exitGame(MSG_LOSE);
    } else {
        try {
            snake.move();
            if (recordMovements && snake.getDirection() != NONE) {
                writeMapToFile();
            }
            if (!map->hasFood()) {
                map->createRandFood();
            }
            mutexMove.unlock();
        } catch (const std::exception) {
            mutexMove.unlock();
            throw;
        }
    }
}

// this function is for writing snake's movement to text file format
void GameCtrl::writeMapToFile() const {
    if (!movementFile) {
        return;
    }
    SizeType rows = map->getRowCount();
    SizeType cols = map->getColCount();
    for (SizeType i = 0; i < rows; ++i) {
        for (SizeType j = 0; j < cols; ++j) {
            switch (map->getPoint(Pos(i, j)).getType()) {
                case Point::Type::EMPTY:
                    fwrite("  ", sizeof(char), 2, movementFile); break;
                case Point::Type::WALL:
                    fwrite("# ", sizeof(char), 2, movementFile); break;
                case Point::Type::FOOD:
                    fwrite("F ", sizeof(char), 2, movementFile); break;
                case Point::Type::SNAKE_HEAD:
                    fwrite("H ", sizeof(char), 2, movementFile); break;
                case Point::Type::SNAKE_BODY:
                    fwrite("B ", sizeof(char), 2, movementFile); break;
                case Point::Type::SNAKE_TAIL:
                    fwrite("T ", sizeof(char), 2, movementFile); break;
                default:
                    break;
            }
        }
        fwrite("\n", sizeof(char), 1, movementFile);
    }
    fwrite("\n", sizeof(char), 1, movementFile);
}

// this function is calling functions to initialize map and snake, and optionally calling initFiles()
void GameCtrl::init() {
    Console::clear();
    initMap();
    if (!runTest) {
        initSnake();
        if (recordMovements) {
            initFiles();
        }
    }
    startThreads();
}

// this function is for initialize map
void GameCtrl::initMap() {
    if (mapRowCnt < 5 || mapColCnt < 5) {
        string msg = "GameCtrl.initMap(): Map size at least 5*5. Current size "
            + util::toString(mapRowCnt) + "*" + util::toString(mapColCnt) + ".";
        throw std::range_error(msg.c_str());
    }
    map = new Map(mapRowCnt, mapColCnt);
    if (!map) {
        exitGameErr(MSG_BAD_ALLOC);
    } else {
        // Add some extra walls manully
    }
}

// this function is for initialize snake and optionally call snake.hamilton search
void GameCtrl::initSnake() {
    snake.setMap(map);
    snake.addBody(Pos(1, 3));
    snake.addBody(Pos(1, 2));
    snake.addBody(Pos(1, 1));
    if (enableHamilton) {
        snake.enableHamilton();
    }
}

// this function is for initialize movement file to record snake's movement
// initializing file's main work is write content type description to file
void GameCtrl::initFiles() {
    movementFile = fopen(MAP_INFO_FILENAME.c_str(), "w");
    if (!movementFile) {
        throw std::runtime_error("GameCtrl.initFiles(): Fail to open file: " + MAP_INFO_FILENAME);
    } else {
        // Write content description to the file
        string str = "Content description:\n";
        str += "#: wall\nH: snake head\nB: snake body\nT: snake tail\nF: food\n\n";
        str += "Movements:\n\n";
        fwrite(str.c_str(), sizeof(char), str.length(), movementFile);
    }
}

// this function creates three thrads which are necessary to run the game
// the three thread is drawThread, keyboardThread, moveThread
// drawThread's main work is draw map in console
// keyboardThread's main work is inputing key and calling function to move snake position
// moveThread's main work is to move snake position based on auto search methods(Hamilton, graph search, BFS)
// runSubThread is very important thing to run program. as long as this value is true, each thread is keep running and it means no error is occured.
void GameCtrl::startThreads() {
    runSubThread = true;
    drawThread = std::thread(&GameCtrl::draw, this);
    drawThread.detach();
    keyboardThread = std::thread(&GameCtrl::keyboard, this);
    keyboardThread.detach();
    if (!runTest) {
        moveThread = std::thread(&GameCtrl::autoMove, this);
        moveThread.detach();
    }
}
// this function calls drawMapContent() function to draw Map
void GameCtrl::draw() {
    try {
        while (runSubThread) {
            drawMapContent();
            sleepFPS();
        }
    } catch (const std::exception &e) {
        exitGameErr(e.what());
    }
}
// this function draws Map in console according to point type
// setCursor needs x,y parameter, but when parameter is no, the default is x,y = 0 and this function just position console's cursor to write map
void GameCtrl::drawMapContent() const {
    Console::setCursor();
    SizeType row = map->getRowCount();
	SizeType col = map->getColCount();
    for (SizeType i = 0; i < row; ++i) {
        for (SizeType j = 0; j < col; ++j) {
            const Point &point = map->getPoint(Pos(i, j));
            switch (point.getType()) {
                case Point::Type::EMPTY:
                    Console::writeWithColor("  ", ConsoleColor(BLACK, BLACK));
                    break;
                case Point::Type::WALL:
                    Console::writeWithColor("  ", ConsoleColor(WHITE, WHITE, true, true));
                    break;
                case Point::Type::FOOD:
                    Console::writeWithColor("  ", ConsoleColor(YELLOW, YELLOW, true, true));
                    break;
                case Point::Type::SNAKE_HEAD:
                    Console::writeWithColor("  ", ConsoleColor(RED, RED, true, true));
                    break;
                case Point::Type::SNAKE_BODY:
                    Console::writeWithColor("  ", ConsoleColor(GREEN, GREEN, true, true));
                    break;
                case Point::Type::SNAKE_TAIL:
                    Console::writeWithColor("  ", ConsoleColor(BLUE, BLUE, true, true));
                    break;
                case Point::Type::TEST_VISIT:
                    drawTestPoint(point, ConsoleColor(BLUE, GREEN, true, true));
                    break;
                case Point::Type::TEST_PATH:
                    drawTestPoint(point, ConsoleColor(BLUE, RED, true, true));
                    break;
                default:
                    break;
            }
        }
        Console::write("\n");
    }
}

// this function is for drawing point in test mode
// after debugging, I notice that the code never enter the MAX_VALUE
void GameCtrl::drawTestPoint(const Point &p, const ConsoleColor &consoleColor) const {
    string pointStr = "";
    if (p.getDist() == Point::MAX_VALUE) {
        pointStr = "In";
    } else if (p.getDist() == EMPTY_VALUE) {
        pointStr = "  ";
    } else {
        Point::ValueType dist = p.getDist();
        pointStr = util::toString(p.getDist());
        if (dist / 10 == 0) {
            pointStr.insert(0, " ");
        }
    }
    Console::writeWithColor(pointStr, consoleColor);
}

// this function is call keyboardMove function according to key
void GameCtrl::keyboard() {
    try {
        while (runSubThread) {
            if (Console::kbhit()) {
                switch (Console::getch()) {
                    case 'w':
                        keyboardMove(snake, Direction::UP);
                        break;
                    case 'a':
                        keyboardMove(snake, Direction::LEFT);
                        break;
                    case 's':
                        keyboardMove(snake, Direction::DOWN);
                        break;
                    case 'd':
                        keyboardMove(snake, Direction::RIGHT);
                        break;
                    case ' ':
                        pause = !pause;  // Pause or resume game
                        break;
                    case 27:  // Esc
                        exitGame(MSG_ESC);
                        break;
                    default:
                        break;
                }
            }
            sleepFPS();
        }
    } catch (const std::exception &e) {
        exitGameErr(e.what());
    }
}

// this function is for moving snake 1 step toward manual input key
// when pause mode, snake moves toward manual input key or
// when not pause mode and when BFS mode(not AI search mode), move snake toward input key
void GameCtrl::keyboardMove(Snake &s, const Direction d) {
    if (pause) {
        s.setDirection(d);
        moveSnake();
    } else if (!enableAI) {
        if (s.getDirection() == d) {
            moveSnake();  // Accelerate
        } else {
            s.setDirection(d);
        }
    }
}

// this function is for auto moving snake 1 step toward AI based search path
// snake.cpp's decideNext function is only for AI based search(hamilton, graph)
// when enableAI == False, it determine path using BFS search method.
void GameCtrl::autoMove() {
    try {
        while (runSubThread) {
            util::sleep(moveInterval);
            if (!pause) {
                if (enableAI) {
                    snake.decideNext();
                }
                moveSnake();
            }
        }
    } catch (const std::exception &e) {
        exitGameErr(e.what());
    }
}

// this function is for testing several tests such as Food creating test, searching minimum/maximum path test, search using Hamilton test
void GameCtrl::test() {
    //testFood();
    testSearch();
    //testHamilton();
}

// this function is for creating food randomly test
void GameCtrl::testFood() {
    SizeType cnt = 0;
    while (runMainThread && cnt++ < map->getSize()) {
        map->createRandFood();
        sleepFPS();
    }
    exitGame("testFood() finished.");
}

// this function is for searching minimum/maximum path test calling snake.testMinPath
// you can choose BFS method with minimum path search or search maximum path using BFS method and extra extension algorithm.
void GameCtrl::testSearch() {
    if (mapRowCnt != 20 || mapColCnt != 20) {
        throw std::range_error("GameCtrl.testSearch(): Require map size 20*20.");
    }

    list<Direction> path;
    snake.setMap(map);

    // Add walls for testing
    for (int i = 4; i < 16; ++i) {
        map->getPoint(Pos(i, 9)).setType(Point::Type::WALL);   // vertical
        map->getPoint(Pos(4, i)).setType(Point::Type::WALL);   // horizontal #1
        map->getPoint(Pos(15, i)).setType(Point::Type::WALL);  // horizontal #2
    }

    Pos from(6, 7);
	Pos to(14, 13);
	if(select_path==0) snake.testMinPath(from, to, path);
    else if(select_path==1) snake.testMaxPath(from, to, path);

    // Print path info
    string info = "Path from " + from.toString() + " to " + to.toString()
        + " of length " + util::toString(path.size()) + ":\n";
    for (const Direction &d : path) {
        switch (d) {
            case LEFT:
                info += "L "; break;
            case UP:
                info += "U "; break;
            case RIGHT:
                info += "R "; break;
            case DOWN:
                info += "D "; break;
            case NONE:
            default:
                info += "NONE "; break;
        }
    }
    info += "\ntestSearch() finished.";
    exitGame(info);
}

// this function is for testing hamilton path search
void GameCtrl::testHamilton() {
    snake.setMap(map);
    snake.addBody(Pos(1, 3));
    snake.addBody(Pos(1, 2));
    snake.addBody(Pos(1, 1));
    snake.testHamilton();
    exitGame("testHamilton() finished.");
}
<commit_msg>Modify direction key to code #1<commit_after>#include "GameCtrl.h"
#include "util/util.h"
#include <stdexcept>
#include <cstdio>
#include <chrono>
#include <cstdlib>
#ifdef _WIN32
#include <Windows.h>
#endif

using std::string;
using std::list;

// Error statements
const string GameCtrl::MSG_BAD_ALLOC = "Not enough memory to run the game.";
const string GameCtrl::MSG_LOSE = "Oops! You lose!";
const string GameCtrl::MSG_WIN = "Congratulations! You Win!";
const string GameCtrl::MSG_ESC = "Game ended.";
const string GameCtrl::MAP_INFO_FILENAME = "movements.txt";
int select_path=0; //Select Min Path or Max Path

GameCtrl::GameCtrl() {}

// class Destroyer
GameCtrl::~GameCtrl() {
    delete map;
    map = nullptr;
    if (movementFile) {
        fclose(movementFile);
        movementFile = nullptr;
    }
}

// Return the class instance's address
// Return instance will be used for creating and running game in main function
GameCtrl* GameCtrl::getInstance() {
=======
GameCtrl* GameCtrl::getInstance(int n) {

    static GameCtrl instance;
	select_path=n;
    return &instance;
}

void GameCtrl::setFPS(const double fps_) {
    fps = fps_;
}

// set program enable AI based search to decide next point to visit
// enableAI_(parameter) is boolean type whether the program use AI based search or not
void GameCtrl::setEnableAI(const bool enableAI_) {
    enableAI = enableAI_;
}

// set program enable Hamilton search to decide next point to visit
// enableHamilton_(parameter) is boolean type whether the program use Hamilton based search or not
void GameCtrl::setEnableHamilton(const bool enableHamilton_) {
    enableHamilton = enableHamilton_;
}

void GameCtrl::setMoveInterval(const long ms) {
    moveInterval = ms;
}

// set program records the snake's movement in text file
// b(parameter) is boolean type whether the program record snake's movement or not
void GameCtrl::setRecordMovements(const bool b) {
    recordMovements = b;
}

void GameCtrl::setRunTest(const bool b) {
    runTest = b;
}

void GameCtrl::setMapRow(const SizeType n) {
    mapRowCnt = n;
}

void GameCtrl::setMapCol(const SizeType n) {
    mapColCnt = n;
}

// essential function to working the program. this function call init() to initialize some variables to start game
// return 0 : successfully exit game
// return 1 : error is occured during game is running
int GameCtrl::run() {
    try {
        init();
        if (runTest) {
            test();
        }
        while (runMainThread) {}
        return 0;
    } catch (const std::exception &e) {
        exitGameErr(e.what());
        return -1;
    }
}

void GameCtrl::sleepFPS() const {
    util::sleep((long)((1.0 / fps) * 1000));
}

// this function is called when the program is needed to end
// msg(parameter) contains error message
// this function use mutex lock because when a thread access critical section, other two thread must not access same crtical section.
// ex) when draw thread is errored, and at the same time , search thread is also errored,
//     the first errored thread access critical section, and then search thread access critical section
void GameCtrl::exitGame(const std::string &msg) {
    mutexExit.lock();
    if (runMainThread) {
        util::sleep(100);
        runSubThread = false;
        util::sleep(100);
        printMsg(msg);
    }
    mutexExit.unlock();
    runMainThread = false;
}

void GameCtrl::exitGameErr(const std::string &err) {
    exitGame("ERROR: " + err);
}

void GameCtrl::printMsg(const std::string &msg) {
    Console::setCursor(0, (int)mapRowCnt);
    Console::writeWithColor(msg + "\n", ConsoleColor(WHITE, BLACK, true, false));
}

// this function is for moving snake one step more closer to food
// this function also use mutex lock because manually moving mode,
// user input many keys, but the process must execute move(), writeMapToFile(), CreateRandFood() one by one
void GameCtrl::moveSnake() {
    mutexMove.lock();
    if (map->isAllBody()) {
        mutexMove.unlock();
        exitGame(MSG_WIN);
    } else if (snake.isDead()) {
        mutexMove.unlock();
        exitGame(MSG_LOSE);
    } else {
        try {
            snake.move();
            if (recordMovements && snake.getDirection() != NONE) {
                writeMapToFile();
            }
            if (!map->hasFood()) {
                map->createRandFood();
            }
            mutexMove.unlock();
        } catch (const std::exception) {
            mutexMove.unlock();
            throw;
        }
    }
}

// this function is for writing snake's movement to text file format
void GameCtrl::writeMapToFile() const {
    if (!movementFile) {
        return;
    }
    SizeType rows = map->getRowCount();
    SizeType cols = map->getColCount();
    for (SizeType i = 0; i < rows; ++i) {
        for (SizeType j = 0; j < cols; ++j) {
            switch (map->getPoint(Pos(i, j)).getType()) {
                case Point::Type::EMPTY:
                    fwrite("  ", sizeof(char), 2, movementFile); break;
                case Point::Type::WALL:
                    fwrite("# ", sizeof(char), 2, movementFile); break;
                case Point::Type::FOOD:
                    fwrite("F ", sizeof(char), 2, movementFile); break;
                case Point::Type::SNAKE_HEAD:
                    fwrite("H ", sizeof(char), 2, movementFile); break;
                case Point::Type::SNAKE_BODY:
                    fwrite("B ", sizeof(char), 2, movementFile); break;
                case Point::Type::SNAKE_TAIL:
                    fwrite("T ", sizeof(char), 2, movementFile); break;
                default:
                    break;
            }
        }
        fwrite("\n", sizeof(char), 1, movementFile);
    }
    fwrite("\n", sizeof(char), 1, movementFile);
}

// this function is calling functions to initialize map and snake, and optionally calling initFiles()
void GameCtrl::init() {
    Console::clear();
    initMap();
    if (!runTest) {
        initSnake();
        if (recordMovements) {
            initFiles();
        }
    }
    startThreads();
}

// this function is for initialize map
void GameCtrl::initMap() {
    if (mapRowCnt < 5 || mapColCnt < 5) {
        string msg = "GameCtrl.initMap(): Map size at least 5*5. Current size "
            + util::toString(mapRowCnt) + "*" + util::toString(mapColCnt) + ".";
        throw std::range_error(msg.c_str());
    }
    map = new Map(mapRowCnt, mapColCnt);
    if (!map) {
        exitGameErr(MSG_BAD_ALLOC);
    } else {
        // Add some extra walls manully
    }
}

// this function is for initialize snake and optionally call snake.hamilton search
void GameCtrl::initSnake() {
    snake.setMap(map);
    snake.addBody(Pos(1, 3));
    snake.addBody(Pos(1, 2));
    snake.addBody(Pos(1, 1));
    if (enableHamilton) {
        snake.enableHamilton();
    }
}

// this function is for initialize movement file to record snake's movement
// initializing file's main work is write content type description to file
void GameCtrl::initFiles() {
    movementFile = fopen(MAP_INFO_FILENAME.c_str(), "w");
    if (!movementFile) {
        throw std::runtime_error("GameCtrl.initFiles(): Fail to open file: " + MAP_INFO_FILENAME);
    } else {
        // Write content description to the file
        string str = "Content description:\n";
        str += "#: wall\nH: snake head\nB: snake body\nT: snake tail\nF: food\n\n";
        str += "Movements:\n\n";
        fwrite(str.c_str(), sizeof(char), str.length(), movementFile);
    }
}

// this function creates three thrads which are necessary to run the game
// the three thread is drawThread, keyboardThread, moveThread
// drawThread's main work is draw map in console
// keyboardThread's main work is inputing key and calling function to move snake position
// moveThread's main work is to move snake position based on auto search methods(Hamilton, graph search, BFS)
// runSubThread is very important thing to run program. as long as this value is true, each thread is keep running and it means no error is occured.
void GameCtrl::startThreads() {
    runSubThread = true;
    drawThread = std::thread(&GameCtrl::draw, this);
    drawThread.detach();
    keyboardThread = std::thread(&GameCtrl::keyboard, this);
    keyboardThread.detach();
    if (!runTest) {
        moveThread = std::thread(&GameCtrl::autoMove, this);
        moveThread.detach();
    }
}
// this function calls drawMapContent() function to draw Map
void GameCtrl::draw() {
    try {
        while (runSubThread) {
            drawMapContent();
            sleepFPS();
        }
    } catch (const std::exception &e) {
        exitGameErr(e.what());
    }
}
// this function draws Map in console according to point type
// setCursor needs x,y parameter, but when parameter is no, the default is x,y = 0 and this function just position console's cursor to write map
void GameCtrl::drawMapContent() const {
    Console::setCursor();
    SizeType row = map->getRowCount();
	SizeType col = map->getColCount();
    for (SizeType i = 0; i < row; ++i) {
        for (SizeType j = 0; j < col; ++j) {
            const Point &point = map->getPoint(Pos(i, j));
            switch (point.getType()) {
                case Point::Type::EMPTY:
                    Console::writeWithColor("  ", ConsoleColor(BLACK, BLACK));
                    break;
                case Point::Type::WALL:
                    Console::writeWithColor("  ", ConsoleColor(WHITE, WHITE, true, true));
                    break;
                case Point::Type::FOOD:
                    Console::writeWithColor("  ", ConsoleColor(YELLOW, YELLOW, true, true));
                    break;
                case Point::Type::SNAKE_HEAD:
                    Console::writeWithColor("  ", ConsoleColor(RED, RED, true, true));
                    break;
                case Point::Type::SNAKE_BODY:
                    Console::writeWithColor("  ", ConsoleColor(GREEN, GREEN, true, true));
                    break;
                case Point::Type::SNAKE_TAIL:
                    Console::writeWithColor("  ", ConsoleColor(BLUE, BLUE, true, true));
                    break;
                case Point::Type::TEST_VISIT:
                    drawTestPoint(point, ConsoleColor(BLUE, GREEN, true, true));
                    break;
                case Point::Type::TEST_PATH:
                    drawTestPoint(point, ConsoleColor(BLUE, RED, true, true));
                    break;
                default:
                    break;
            }
        }
        Console::write("\n");
    }
}

// this function is for drawing point in test mode
// after debugging, I notice that the code never enter the MAX_VALUE
void GameCtrl::drawTestPoint(const Point &p, const ConsoleColor &consoleColor) const {
    string pointStr = "";
    if (p.getDist() == Point::MAX_VALUE) {
        pointStr = "In";
    } else if (p.getDist() == EMPTY_VALUE) {
        pointStr = "  ";
    } else {
        Point::ValueType dist = p.getDist();
        pointStr = util::toString(p.getDist());
        if (dist / 10 == 0) {
            pointStr.insert(0, " ");
        }
    }
    Console::writeWithColor(pointStr, consoleColor);
}

// this function is call keyboardMove function according to key
void GameCtrl::keyboard() {
    try {
        while (runSubThread) {
            if (Console::kbhit()) {
                switch (Console::getch()) {
                    case 'w':
                        keyboardMove(snake, Direction::UP);
                        break;
                    case 'a':
                        keyboardMove(snake, Direction::LEFT);
                        break;
                    case 's':
                        keyboardMove(snake, Direction::DOWN);
                        break;
                    case 'd':
                        keyboardMove(snake, Direction::RIGHT);
                        break;
                    case ' ':
                        pause = !pause;  // Pause or resume game
                        break;
                    case 27:  // Esc
                        exitGame(MSG_ESC);
                        break;
                    default:
                        break;
                }
            }
            sleepFPS();
        }
    } catch (const std::exception &e) {
        exitGameErr(e.what());
    }
}

// this function is for moving snake 1 step toward manual input key
// when pause mode, snake moves toward manual input key or
// when not pause mode and when BFS mode(not AI search mode), move snake toward input key
void GameCtrl::keyboardMove(Snake &s, const Direction d) {
    if (pause) {
        s.setDirection(d);
        moveSnake();
    } else if (!enableAI) {
        if (s.getDirection() == d) {
            moveSnake();  // Accelerate
        } else {
            s.setDirection(d);
        }
    }
}

// this function is for auto moving snake 1 step toward AI based search path
// snake.cpp's decideNext function is only for AI based search(hamilton, graph)
// when enableAI == False, it determine path using BFS search method.
void GameCtrl::autoMove() {
    try {
        while (runSubThread) {
            util::sleep(moveInterval);
            if (!pause) {
                if (enableAI) {
                    snake.decideNext();
                }
                moveSnake();
            }
        }
    } catch (const std::exception &e) {
        exitGameErr(e.what());
    }
}

// this function is for testing several tests such as Food creating test, searching minimum/maximum path test, search using Hamilton test
void GameCtrl::test() {
    //testFood();
    testSearch();
    //testHamilton();
}

// this function is for creating food randomly test
void GameCtrl::testFood() {
    SizeType cnt = 0;
    while (runMainThread && cnt++ < map->getSize()) {
        map->createRandFood();
        sleepFPS();
    }
    exitGame("testFood() finished.");
}

// this function is for searching minimum/maximum path test calling snake.testMinPath
// you can choose BFS method with minimum path search or search maximum path using BFS method and extra extension algorithm.
void GameCtrl::testSearch() {
    if (mapRowCnt != 20 || mapColCnt != 20) {
        throw std::range_error("GameCtrl.testSearch(): Require map size 20*20.");
    }

    list<Direction> path;
    snake.setMap(map);

    // Add walls for testing
    for (int i = 4; i < 16; ++i) {
        map->getPoint(Pos(i, 9)).setType(Point::Type::WALL);   // vertical
        map->getPoint(Pos(4, i)).setType(Point::Type::WALL);   // horizontal #1
        map->getPoint(Pos(15, i)).setType(Point::Type::WALL);  // horizontal #2
    }

    Pos from(6, 7);
	Pos to(14, 13);
	if(select_path==0) snake.testMinPath(from, to, path);
    else if(select_path==1) snake.testMaxPath(from, to, path);

    // Print path info
    string info = "Path from " + from.toString() + " to " + to.toString()
        + " of length " + util::toString(path.size()) + ":\n";
    for (const Direction &d : path) {
        switch (d) {
            case LEFT:
                info += "L "; break;
            case UP:
                info += "U "; break;
            case RIGHT:
                info += "R "; break;
            case DOWN:
                info += "D "; break;
            case NONE:
            default:
                info += "NONE "; break;
        }
    }
    info += "\ntestSearch() finished.";
    exitGame(info);
}

// this function is for testing hamilton path search
void GameCtrl::testHamilton() {
    snake.setMap(map);
    snake.addBody(Pos(1, 3));
    snake.addBody(Pos(1, 2));
    snake.addBody(Pos(1, 1));
    snake.testHamilton();
    exitGame("testHamilton() finished.");
}
<|endoftext|>


<commit_before>#include <limits>
#include <iostream>
#include <iomanip>
#include <unistd.h>
#include <libpstack/util.h>
#include <libpstack/elf.h>
#include <algorithm>

using std::string;
using std::make_shared;
using std::shared_ptr;

std::ostream *debug;
static uint32_t elf_hash(string);
bool noDebugLibs;

GlobalDebugDirectories globalDebugDirectories;
GlobalDebugDirectories::GlobalDebugDirectories()
{
   add("/usr/lib/debug");
   add("/usr/lib/debug/usr"); // Add as a hack for when linker loads from /lib, but package has /usr/lib
}

void
GlobalDebugDirectories::add(const std::string &str)
{
   dirs.push_back(str);
}

ElfNoteIter
ElfNotes::begin() const
{
   return ElfNoteIter(object, object->getSegments().begin());
}

ElfNoteIter
ElfNotes::end() const
{
   return ElfNoteIter(object, object->getSegments().end());
}

std::string
ElfNoteDesc::name() const
{
   char *buf = new char[note.n_namesz + 1];
   object->io->readObj(offset + sizeof note, buf, note.n_namesz);
   buf[note.n_namesz] = 0;
   std::string s = buf;
   delete[] buf;
   return s;
}

const unsigned char *
ElfNoteDesc::data() const
{
   if (databuf == 0) {
      databuf = new unsigned char[note.n_descsz];
      object->io->readObj(roundup2(offset + sizeof note + note.n_namesz, 4),
            databuf, note.n_descsz);
   }
   return databuf;
}

size_t
ElfNoteDesc::size() const
{
   return note.n_descsz;
}

/*
 * Parse out an ELF file into an ElfObject structure.
 */

const Elf_Phdr *
ElfObject::findHeaderForAddress(Elf_Off a) const
{
    for (auto &hdr : programHeaders)
        if (hdr.p_vaddr <= a && hdr.p_vaddr + hdr.p_memsz > a && hdr.p_type == PT_LOAD)
            return &hdr;
    return 0;
}

ElfObject::ElfObject(const string &name_)
    : name(name_)
    , notes(this)
{
    init(make_shared<CacheReader>(make_shared<FileReader>(name)));
}

ElfObject::ElfObject(shared_ptr<Reader> io_)
   : notes(this)
{
    name = io_->describe();
    init(io_);
}

Elf_Addr
ElfObject::getBase() const
{
    auto base = std::numeric_limits<Elf_Off>::max();
    auto &segments = getSegments();
    for (auto &seg : segments)
        if (seg.p_type == PT_LOAD && Elf_Off(seg.p_vaddr) <= base)
            base = Elf_Off(seg.p_vaddr);
    return base;
}

std::string
ElfObject::getInterpreter() const
{
    for (auto &seg : getSegments())
        if (seg.p_type == PT_INTERP)
            return io->readString(seg.p_offset);
    return "";
}

void
ElfObject::init(const shared_ptr<Reader> &io_)
{
    debugLoaded = false;
    io = io_;
    int i;
    size_t off;
    io->readObj(0, &elfHeader);

    /* Validate the ELF header */
    if (!IS_ELF(elfHeader) || elfHeader.e_ident[EI_VERSION] != EV_CURRENT)
        throw Exception() << io->describe() << ": content is not an ELF image";

    for (off = elfHeader.e_phoff, i = 0; i < elfHeader.e_phnum; i++) {
        programHeaders.push_back(Elf_Phdr());
        io->readObj(off, &programHeaders.back());
        off += elfHeader.e_phentsize;
    }

    for (off = elfHeader.e_shoff, i = 0; i < elfHeader.e_shnum; i++) {
        sectionHeaders.push_back(Elf_Shdr());
        io->readObj(off, &sectionHeaders.back());
        off += elfHeader.e_shentsize;
    }

    if (elfHeader.e_shstrndx != SHN_UNDEF) {
        auto sshdr = sectionHeaders[elfHeader.e_shstrndx];
        for (auto &h : sectionHeaders) {
            auto name = io->readString(sshdr.sh_offset + h.sh_name);
            namedSection[name] = &h;
        }
        auto tab = getSection(".hash", SHT_HASH);
        if (tab)
            hash.reset(new ElfSymHash(tab));
    } else {
        hash = 0;
    }
}

std::pair<const Elf_Sym, const string>
SymbolIterator::operator *()
{
        Elf_Sym sym;
        io->readObj(off, &sym);
        string name = io->readString(sym.st_name + stroff);
        return std::make_pair(sym, name);
}

/*
 * Find the symbol that represents a particular address.
 * If we fail to find a symbol whose virtual range includes our target address
 * we will accept a symbol with the highest address less than or equal to our
 * target. This allows us to match the dynamic "stubs" in code.
 * A side-effect is a few false-positives: A stripped, dynamically linked,
 * executable will typically report functions as being "_init", because it is
 * the only symbol in the image, and it has no size.
 */
bool
ElfObject::findSymbolByAddress(Elf_Addr addr, int type, Elf_Sym &sym, string &name)
{
    /* Try to find symbols in these sections */
    static const char *sectionNames[] = {
        ".symtab", ".dynsym", 0
    };
    bool exact = false;
    Elf_Addr lowest = 0;
    for (size_t i = 0; sectionNames[i] && !exact; i++) {
        const auto symSection = getSection(sectionNames[i], SHT_NULL);
        if (symSection == 0 || symSection->sh_type == SHT_NOBITS)
            continue;
        SymbolSection syms(symSection);
        for (auto syminfo : syms) {
            auto &candidate = syminfo.first;
            if (candidate.st_shndx >= sectionHeaders.size())
                continue;
            auto shdr = sectionHeaders[candidate.st_shndx];
            if (!(shdr.sh_flags & SHF_ALLOC))
                continue;
            if (type != STT_NOTYPE && ELF_ST_TYPE(candidate.st_info) != type)
                continue;
            if (candidate.st_value > addr)
                continue;
            if (candidate.st_size) {
                // symbol has a size: we can check if our address lies within it.
                if (candidate.st_size + candidate.st_value > addr) {
                    // yep: return this one.
                    sym = candidate;
                    name = syminfo.second;
                    return true;
                }
            } else if (lowest < candidate.st_value) {
                /*
                 * No size, but hold on to it as a possibility. We'll return
                 * the symbol with the highest value not aabove the required
                 * value
                 */
                sym = candidate;
                name = syminfo.second;
                lowest = candidate.st_value;
            }
        }
    }
    return lowest != 0;
}

const ElfSection
ElfObject::getSection(const std::string &name, Elf_Word type)
{

    auto s = namedSection.find(name);
    return ElfSection(*this, s != namedSection.end() && (s->second->sh_type == type || type == SHT_NULL) ? s->second : 0);
}

SymbolSection
ElfObject::getSymbols(const std::string &table)
{
    return SymbolSection(getSection(table, SHT_NULL));
}

bool
linearSymSearch(ElfSection &section, const string &name, Elf_Sym &sym)
{
    SymbolSection sec(section);
    for (const auto &info : sec) {
        if (name == info.second) {
            sym = info.first;
            return true;
        }
    }
    return false;
}

ElfSymHash::ElfSymHash(ElfSection &hash_)
    : hash(hash_)
    , syms(hash.obj, hash.getLink())
{
    // read the hash table into local memory.
    size_t words = hash->sh_size / sizeof (Elf_Word);
    data.resize(words);
    hash.obj.io->readObj(hash->sh_offset, &data[0], words);
    nbucket = data[0];
    nchain = data[1];
    buckets = &data[0] + 2;
    chains = buckets + nbucket;
    strings = syms.getLink()->sh_offset;
}

bool
ElfSymHash::findSymbol(Elf_Sym &sym, const string &name)
{
    uint32_t bucket = elf_hash(name) % nbucket;
    for (Elf_Word i = buckets[bucket]; i != STN_UNDEF; i = chains[i]) {
        Elf_Sym candidate;
        syms.obj.io->readObj(syms->sh_offset + i * sizeof candidate, &candidate);
        string candidateName = syms.obj.io->readString(strings + candidate.st_name);
        if (candidateName == name) {
            sym = candidate;
            return true;
        }
    }
    return false;
}

/*
 * Locate a named symbol in an ELF image.
 */
bool
ElfObject::findSymbolByName(const string &name, Elf_Sym &sym)
{
    if (hash && hash->findSymbol(sym, name))
        return true;
    auto dyn = getSection(".dynsym", SHT_DYNSYM);
    if (dyn && linearSymSearch(dyn, name, sym))
        return true;
    auto symtab = getSection(".symtab", SHT_SYMTAB);
    return symtab && linearSymSearch(symtab, name, sym);
}

/*
 * Get the data and length from a specific "note" in the ELF file
 */
#ifdef __FreeBSD__
/*
 * Try to work out the name of the executable from a core file
 * XXX: This is not particularly useful, because the pathname appears to get
 * stripped.
 */
static enum NoteIter
elfImageNote(void *cookie, const char *name, u_int32_t type,
        const void *data, size_t len)
{
    const char **exename;
    const prpsinfo_t *psinfo;

    exename = (const char **)cookie;
    psinfo = (const prpsinfo_t *)data;

    if (!strcmp(name, "FreeBSD") && type == NT_PRPSINFO &&
        psinfo->pr_version == PRPSINFO_VERSION) {
        *exename = psinfo->pr_fname;
        return NOTE_DONE;
    }
    return NOTE_CONTIN;
}

#endif

ElfObject::~ElfObject()
{
}

std::shared_ptr<ElfObject>
ElfObject::getDebug(std::shared_ptr<ElfObject> &in)
{
   auto sp = in->getDebug();
   return sp ? sp : in;
}

static std::shared_ptr<ElfObject>
tryLoad(const std::string &name) {
    // XXX: verify checksum.
    for (auto dir : globalDebugDirectories.dirs) {
        try {
           auto debugObject = make_shared<ElfObject>(dir + "/" + name);
           return debugObject;
        }
        catch (const std::exception &ex) {
            continue;
        }
    }
    return std::shared_ptr<ElfObject>();
}

std::shared_ptr<ElfObject>
ElfObject::getDebug()
{
    if (noDebugLibs)
        return std::shared_ptr<ElfObject>();

    if (!debugLoaded) {
        debugLoaded = true;
        if (debug) {
            for (auto note : notes) {
               if (note.name() == "GNU" && note.type() == GNU_BUILD_ID) {
                  *debug << "GNU buildID: ";
                  auto data = note.data();
                  for (size_t i = 0; i < note.size(); ++i)
                     *debug << std::hex << std::setw(2) << std::setfill('0') << int(data[i]);
                  *debug << "\n";
                  break;
               }
            }
        }

        std::ostringstream stream;
        stream << io->describe();
        std::string oldname = stream.str();

        auto hdr = getSection(".gnu_debuglink", SHT_PROGBITS);
        if (hdr == 0)
            return std::shared_ptr<ElfObject>();
        std::vector<char> buf(hdr->sh_size);
        std::string link = io->readString(hdr->sh_offset);

        auto dir = dirname(oldname);
        debugObject = tryLoad(dir + "/" + link);
        if (!debugObject) {
            for (auto note : notes) {
               if (note.name() == "GNU" && note.type() == GNU_BUILD_ID) {
                   std::ostringstream dir;
                   dir << ".build-id/";
                   size_t i;
                   auto data = note.data();
                   dir << std::hex << std::setw(2) << std::setfill('0') << int(data[0]);
                   dir << "/";
                   for (i = 1; i < note.size(); ++i)
                       dir << std::hex << std::setw(2) << std::setfill('0') << int(data[i]);
                   dir << ".debug";
                   debugObject = tryLoad(dir.str());
                   break;
               }
            }
        }
    }
    return debugObject;
}

/*
 * Culled from System V Application Binary Interface
 */
static uint32_t
elf_hash(string name)
{
    uint32_t h = 0, g;
    for (auto c : name) {
        h = (h << 4) + c;
        if ((g = h & 0xf0000000) != 0)
            h ^= g >> 24;
        h &= ~g;
    }
    return (h);
}

const Elf_Shdr *ElfSection::getLink() const
{
    return &obj.sectionHeaders[shdr->sh_link];
}
<commit_msg>Show name of lib we calculate gnu hash for<commit_after>#include <limits>
#include <iostream>
#include <iomanip>
#include <unistd.h>
#include <libpstack/util.h>
#include <libpstack/elf.h>
#include <algorithm>

using std::string;
using std::make_shared;
using std::shared_ptr;

std::ostream *debug;
static uint32_t elf_hash(string);
bool noDebugLibs;

GlobalDebugDirectories globalDebugDirectories;
GlobalDebugDirectories::GlobalDebugDirectories()
{
   add("/usr/lib/debug");
   add("/usr/lib/debug/usr"); // Add as a hack for when linker loads from /lib, but package has /usr/lib
}

void
GlobalDebugDirectories::add(const std::string &str)
{
   dirs.push_back(str);
}

ElfNoteIter
ElfNotes::begin() const
{
   return ElfNoteIter(object, object->getSegments().begin());
}

ElfNoteIter
ElfNotes::end() const
{
   return ElfNoteIter(object, object->getSegments().end());
}

std::string
ElfNoteDesc::name() const
{
   char *buf = new char[note.n_namesz + 1];
   object->io->readObj(offset + sizeof note, buf, note.n_namesz);
   buf[note.n_namesz] = 0;
   std::string s = buf;
   delete[] buf;
   return s;
}

const unsigned char *
ElfNoteDesc::data() const
{
   if (databuf == 0) {
      databuf = new unsigned char[note.n_descsz];
      object->io->readObj(roundup2(offset + sizeof note + note.n_namesz, 4),
            databuf, note.n_descsz);
   }
   return databuf;
}

size_t
ElfNoteDesc::size() const
{
   return note.n_descsz;
}

/*
 * Parse out an ELF file into an ElfObject structure.
 */

const Elf_Phdr *
ElfObject::findHeaderForAddress(Elf_Off a) const
{
    for (auto &hdr : programHeaders)
        if (hdr.p_vaddr <= a && hdr.p_vaddr + hdr.p_memsz > a && hdr.p_type == PT_LOAD)
            return &hdr;
    return 0;
}

ElfObject::ElfObject(const string &name_)
    : name(name_)
    , notes(this)
{
    init(make_shared<CacheReader>(make_shared<FileReader>(name)));
}

ElfObject::ElfObject(shared_ptr<Reader> io_)
   : notes(this)
{
    name = io_->describe();
    init(io_);
}

Elf_Addr
ElfObject::getBase() const
{
    auto base = std::numeric_limits<Elf_Off>::max();
    auto &segments = getSegments();
    for (auto &seg : segments)
        if (seg.p_type == PT_LOAD && Elf_Off(seg.p_vaddr) <= base)
            base = Elf_Off(seg.p_vaddr);
    return base;
}

std::string
ElfObject::getInterpreter() const
{
    for (auto &seg : getSegments())
        if (seg.p_type == PT_INTERP)
            return io->readString(seg.p_offset);
    return "";
}

void
ElfObject::init(const shared_ptr<Reader> &io_)
{
    debugLoaded = false;
    io = io_;
    int i;
    size_t off;
    io->readObj(0, &elfHeader);

    /* Validate the ELF header */
    if (!IS_ELF(elfHeader) || elfHeader.e_ident[EI_VERSION] != EV_CURRENT)
        throw Exception() << io->describe() << ": content is not an ELF image";

    for (off = elfHeader.e_phoff, i = 0; i < elfHeader.e_phnum; i++) {
        programHeaders.push_back(Elf_Phdr());
        io->readObj(off, &programHeaders.back());
        off += elfHeader.e_phentsize;
    }

    for (off = elfHeader.e_shoff, i = 0; i < elfHeader.e_shnum; i++) {
        sectionHeaders.push_back(Elf_Shdr());
        io->readObj(off, &sectionHeaders.back());
        off += elfHeader.e_shentsize;
    }

    if (elfHeader.e_shstrndx != SHN_UNDEF) {
        auto sshdr = sectionHeaders[elfHeader.e_shstrndx];
        for (auto &h : sectionHeaders) {
            auto name = io->readString(sshdr.sh_offset + h.sh_name);
            namedSection[name] = &h;
        }
        auto tab = getSection(".hash", SHT_HASH);
        if (tab)
            hash.reset(new ElfSymHash(tab));
    } else {
        hash = 0;
    }
}

std::pair<const Elf_Sym, const string>
SymbolIterator::operator *()
{
        Elf_Sym sym;
        io->readObj(off, &sym);
        string name = io->readString(sym.st_name + stroff);
        return std::make_pair(sym, name);
}

/*
 * Find the symbol that represents a particular address.
 * If we fail to find a symbol whose virtual range includes our target address
 * we will accept a symbol with the highest address less than or equal to our
 * target. This allows us to match the dynamic "stubs" in code.
 * A side-effect is a few false-positives: A stripped, dynamically linked,
 * executable will typically report functions as being "_init", because it is
 * the only symbol in the image, and it has no size.
 */
bool
ElfObject::findSymbolByAddress(Elf_Addr addr, int type, Elf_Sym &sym, string &name)
{
    /* Try to find symbols in these sections */
    static const char *sectionNames[] = {
        ".symtab", ".dynsym", 0
    };
    bool exact = false;
    Elf_Addr lowest = 0;
    for (size_t i = 0; sectionNames[i] && !exact; i++) {
        const auto symSection = getSection(sectionNames[i], SHT_NULL);
        if (symSection == 0 || symSection->sh_type == SHT_NOBITS)
            continue;
        SymbolSection syms(symSection);
        for (auto syminfo : syms) {
            auto &candidate = syminfo.first;
            if (candidate.st_shndx >= sectionHeaders.size())
                continue;
            auto shdr = sectionHeaders[candidate.st_shndx];
            if (!(shdr.sh_flags & SHF_ALLOC))
                continue;
            if (type != STT_NOTYPE && ELF_ST_TYPE(candidate.st_info) != type)
                continue;
            if (candidate.st_value > addr)
                continue;
            if (candidate.st_size) {
                // symbol has a size: we can check if our address lies within it.
                if (candidate.st_size + candidate.st_value > addr) {
                    // yep: return this one.
                    sym = candidate;
                    name = syminfo.second;
                    return true;
                }
            } else if (lowest < candidate.st_value) {
                /*
                 * No size, but hold on to it as a possibility. We'll return
                 * the symbol with the highest value not aabove the required
                 * value
                 */
                sym = candidate;
                name = syminfo.second;
                lowest = candidate.st_value;
            }
        }
    }
    return lowest != 0;
}

const ElfSection
ElfObject::getSection(const std::string &name, Elf_Word type)
{

    auto s = namedSection.find(name);
    return ElfSection(*this, s != namedSection.end() && (s->second->sh_type == type || type == SHT_NULL) ? s->second : 0);
}

SymbolSection
ElfObject::getSymbols(const std::string &table)
{
    return SymbolSection(getSection(table, SHT_NULL));
}

bool
linearSymSearch(ElfSection &section, const string &name, Elf_Sym &sym)
{
    SymbolSection sec(section);
    for (const auto &info : sec) {
        if (name == info.second) {
            sym = info.first;
            return true;
        }
    }
    return false;
}

ElfSymHash::ElfSymHash(ElfSection &hash_)
    : hash(hash_)
    , syms(hash.obj, hash.getLink())
{
    // read the hash table into local memory.
    size_t words = hash->sh_size / sizeof (Elf_Word);
    data.resize(words);
    hash.obj.io->readObj(hash->sh_offset, &data[0], words);
    nbucket = data[0];
    nchain = data[1];
    buckets = &data[0] + 2;
    chains = buckets + nbucket;
    strings = syms.getLink()->sh_offset;
}

bool
ElfSymHash::findSymbol(Elf_Sym &sym, const string &name)
{
    uint32_t bucket = elf_hash(name) % nbucket;
    for (Elf_Word i = buckets[bucket]; i != STN_UNDEF; i = chains[i]) {
        Elf_Sym candidate;
        syms.obj.io->readObj(syms->sh_offset + i * sizeof candidate, &candidate);
        string candidateName = syms.obj.io->readString(strings + candidate.st_name);
        if (candidateName == name) {
            sym = candidate;
            return true;
        }
    }
    return false;
}

/*
 * Locate a named symbol in an ELF image.
 */
bool
ElfObject::findSymbolByName(const string &name, Elf_Sym &sym)
{
    if (hash && hash->findSymbol(sym, name))
        return true;
    auto dyn = getSection(".dynsym", SHT_DYNSYM);
    if (dyn && linearSymSearch(dyn, name, sym))
        return true;
    auto symtab = getSection(".symtab", SHT_SYMTAB);
    return symtab && linearSymSearch(symtab, name, sym);
}

/*
 * Get the data and length from a specific "note" in the ELF file
 */
#ifdef __FreeBSD__
/*
 * Try to work out the name of the executable from a core file
 * XXX: This is not particularly useful, because the pathname appears to get
 * stripped.
 */
static enum NoteIter
elfImageNote(void *cookie, const char *name, u_int32_t type,
        const void *data, size_t len)
{
    const char **exename;
    const prpsinfo_t *psinfo;

    exename = (const char **)cookie;
    psinfo = (const prpsinfo_t *)data;

    if (!strcmp(name, "FreeBSD") && type == NT_PRPSINFO &&
        psinfo->pr_version == PRPSINFO_VERSION) {
        *exename = psinfo->pr_fname;
        return NOTE_DONE;
    }
    return NOTE_CONTIN;
}

#endif

ElfObject::~ElfObject()
{
}

std::shared_ptr<ElfObject>
ElfObject::getDebug(std::shared_ptr<ElfObject> &in)
{
   auto sp = in->getDebug();
   return sp ? sp : in;
}

static std::shared_ptr<ElfObject>
tryLoad(const std::string &name) {
    // XXX: verify checksum.
    for (auto dir : globalDebugDirectories.dirs) {
        try {
           auto debugObject = make_shared<ElfObject>(dir + "/" + name);
           return debugObject;
        }
        catch (const std::exception &ex) {
            continue;
        }
    }
    return std::shared_ptr<ElfObject>();
}

std::shared_ptr<ElfObject>
ElfObject::getDebug()
{
    if (noDebugLibs)
        return std::shared_ptr<ElfObject>();

    if (!debugLoaded) {
        debugLoaded = true;
        if (debug) {
            for (auto note : notes) {
               if (note.name() == "GNU" && note.type() == GNU_BUILD_ID) {
                  *debug << "GNU buildID for " << io->describe() << ": ";
                  auto data = note.data();
                  for (size_t i = 0; i < note.size(); ++i)
                     *debug << std::hex << std::setw(2) << std::setfill('0') << int(data[i]);
                  *debug << "\n";
                  break;
               }
            }
        }

        std::ostringstream stream;
        stream << io->describe();
        std::string oldname = stream.str();

        auto hdr = getSection(".gnu_debuglink", SHT_PROGBITS);
        if (hdr == 0)
            return std::shared_ptr<ElfObject>();
        std::vector<char> buf(hdr->sh_size);
        std::string link = io->readString(hdr->sh_offset);

        auto dir = dirname(oldname);
        debugObject = tryLoad(dir + "/" + link);
        if (!debugObject) {
            for (auto note : notes) {
               if (note.name() == "GNU" && note.type() == GNU_BUILD_ID) {
                   std::ostringstream dir;
                   dir << ".build-id/";
                   size_t i;
                   auto data = note.data();
                   dir << std::hex << std::setw(2) << std::setfill('0') << int(data[0]);
                   dir << "/";
                   for (i = 1; i < note.size(); ++i)
                       dir << std::hex << std::setw(2) << std::setfill('0') << int(data[i]);
                   dir << ".debug";
                   debugObject = tryLoad(dir.str());
                   break;
               }
            }
        }
    }
    return debugObject;
}

/*
 * Culled from System V Application Binary Interface
 */
static uint32_t
elf_hash(string name)
{
    uint32_t h = 0, g;
    for (auto c : name) {
        h = (h << 4) + c;
        if ((g = h & 0xf0000000) != 0)
            h ^= g >> 24;
        h &= ~g;
    }
    return (h);
}

const Elf_Shdr *ElfSection::getLink() const
{
    return &obj.sectionHeaders[shdr->sh_link];
}
<|endoftext|>


<commit_before>/**
 * A class which describes the properties of a MySQL database connection.
 *
 * @date                    Jul 21, 2013
 * @author                  Joeri HERMANS
 * @version                 0.1
 *
 * Copyright 2013 Joeri HERMANS
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

// BEGIN Includes. ///////////////////////////////////////////////////

// System dependencies.
#include <assert.h>
#include <stdio.h>
#include <syslog.h>

// Application dependencies.
#include <ias/database/interface/database_connection.h>
#include <ias/database/mysql/mysql_connection.h>
#include <ias/database/mysql/mysql_statement.h>
#include <ias/database/mysql/mysql_driver.h>

// END Includes. /////////////////////////////////////////////////////

inline void MySqlConnection::initialize( void ) {
    // Initialize the members of the class.
    mConnection = mysql_init( nullptr );
}

MySqlConnection::MySqlConnection( const std::string & username,
                                  const std::string & password,
                                  const std::string & schema,
                                  const std::string & host )
    : DatabaseConnection(username,password,schema,host) {
    // Nothing to do here.
    initialize();
}

MySqlConnection::~MySqlConnection( void ) {
    mysql_close( mConnection );
    mysql_library_end();
}

bool MySqlConnection::isConnected( void ) const
{
    return ( mConnection != nullptr );
}

bool MySqlConnection::connect( void ) {
    bool connected;
    my_bool argument;

    // Enable automatic reconnection by default.
    argument = 1;
    // Enable the automatic reconnect option.
    mysql_options(mConnection,MYSQL_OPT_RECONNECT,&argument);
    // Try to connect to the MySQL server.
    connected = ( mysql_real_connect(mConnection,
                                     getHost().c_str(),
                                     getUsername().c_str(),
                                     getPassword().c_str(),
                                     getSchema().c_str(),
                                     3306,nullptr,0) != nullptr );

    return ( connected );
}

DatabaseStatement * MySqlConnection::createStatement( const std::string & sql ) {
    DatabaseStatement * statement;

    // Checking the precondition.
    assert( sql.length() > 0 );

//    // Check if the connection with the server is still active.
//    if( mysql_ping(mConnection) == 0 )
        // Allocate a new mysql statement.
        statement = new MySqlStatement(this,sql);
//    else
        // No statement should be allocated.
//        statement = nullptr;

    return ( statement );
}

DatabasePreparedStatement * MySqlConnection::prepareStatement(
    const std::string & sql ) {
    // Checking the precondition.
    assert( sql.length() > 0 );

    // TODO Implement.
    return ( nullptr );
}

void * MySqlConnection::getLink( void )
{
    return ( static_cast<void *>(mConnection) );
}
<commit_msg>Add debugging information.<commit_after>/**
 * A class which describes the properties of a MySQL database connection.
 *
 * @date                    Jul 21, 2013
 * @author                  Joeri HERMANS
 * @version                 0.1
 *
 * Copyright 2013 Joeri HERMANS
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

// BEGIN Includes. ///////////////////////////////////////////////////

// System dependencies.
#include <cassert>
#include <cstdio>
#include <syslog.h>
#include <iostream>

// Application dependencies.
#include <ias/database/interface/database_connection.h>
#include <ias/database/mysql/mysql_connection.h>
#include <ias/database/mysql/mysql_statement.h>
#include <ias/database/mysql/mysql_driver.h>

// END Includes. /////////////////////////////////////////////////////

inline void MySqlConnection::initialize( void ) {
    // Initialize the members of the class.
    mConnection = mysql_init( nullptr );
}

MySqlConnection::MySqlConnection( const std::string & username,
                                  const std::string & password,
                                  const std::string & schema,
                                  const std::string & host )
    : DatabaseConnection(username,password,schema,host) {
    // Nothing to do here.
    initialize();
}

MySqlConnection::~MySqlConnection( void ) {
    mysql_close( mConnection );
    mysql_library_end();
}

bool MySqlConnection::isConnected( void ) const
{
    return ( mConnection != nullptr );
}

bool MySqlConnection::connect( void ) {
    bool connected;
    my_bool argument;

    // Enable automatic reconnection by default.
    argument = 1;
    // Enable the automatic reconnect option.
    mysql_options(mConnection,MYSQL_OPT_RECONNECT,&argument);
    // Try to connect to the MySQL server.
    connected = ( mysql_real_connect(mConnection,
                                     getHost().c_str(),
                                     getUsername().c_str(),
                                     getPassword().c_str(),
                                     getSchema().c_str(),
                                     3306,nullptr,0) != nullptr );

    return ( connected );
}

DatabaseStatement * MySqlConnection::createStatement( const std::string & sql ) {
    DatabaseStatement * statement;

    // Checking the precondition.
    assert( sql.length() > 0 );

    // Check if the connection with the server is still active.
    if( mysql_ping(mConnection) == 0 ) {
        std::cout << "Statement allocated" << std::endl;
        statement = new MySqlStatement(this,sql);
    } else {
        statement = nullptr;
    }

    return ( statement );
}

DatabasePreparedStatement * MySqlConnection::prepareStatement(
    const std::string & sql ) {
    // Checking the precondition.
    assert( sql.length() > 0 );

    // TODO Implement.
    return ( nullptr );
}

void * MySqlConnection::getLink( void )
{
    return ( static_cast<void *>(mConnection) );
}
<|endoftext|>


<commit_before>/*
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * Written (W) 1999-2007 Gunnar Raetsch
 * Written (W) 1999-2007 Soeren Sonnenburg
 * Copyright (C) 1999-2007 Fraunhofer Institute FIRST and Max-Planck-Society
 */

#include "lib/common.h"
#include "lib/io.h"
#include "kernel/LocalityImprovedStringKernel.h"
#include "features/Features.h"
#include "features/StringFeatures.h"

CLocalityImprovedStringKernel::CLocalityImprovedStringKernel(LONG size, INT l, INT d1, INT d2)
  : CStringKernel<CHAR>(size),length(l),inner_degree(d1),outer_degree(d2),match(NULL)
{
	SG_INFO( "LIK with parms: l=%d, d1=%d, d2=%d created!\n", l, d1, d2);
}

CLocalityImprovedStringKernel::~CLocalityImprovedStringKernel()
{
	cleanup();
}

bool CLocalityImprovedStringKernel::init(CFeatures* l, CFeatures* r)
{
	bool result = CStringKernel<CHAR>::init(l,r);

	if (!result)
		return false;
	match = new CHAR[((CStringFeatures<CHAR>*) l)->get_max_vector_length()];
	return match? true : false;
}

void CLocalityImprovedStringKernel::cleanup()
{
	delete[] match;
	match = NULL;
}

bool CLocalityImprovedStringKernel::load_init(FILE* src)
{
	return false;
}

bool CLocalityImprovedStringKernel::save_init(FILE* dest)
{
	return false;
}

DREAL CLocalityImprovedStringKernel::compute(INT idx_a, INT idx_b)
{
	INT alen, blen;

	CHAR* avec=((CStringFeatures<CHAR>*) lhs)->get_feature_vector(idx_a, alen);
	CHAR* bvec=((CStringFeatures<CHAR>*) rhs)->get_feature_vector(idx_b, blen);

	// can only deal with strings of same length
	ASSERT(alen==blen);

	INT i,j,t;

	// initialize match table 1 -> match;  0 -> no match
	for (i=0; i<alen; i++)
	{
	  if (avec[i]==bvec[i])
		  match[i]=1;
	  else
		  match[i]=0;
	}

	DREAL outer_sum=0;

	for (t=0; t<alen-length; t++)
	{
	  INT sum=0;
	  for (i=0; i<length; i++)
		  sum+=(i+1)*match[t+i]+(length-i)*match[t+i+length+1];

	  //add middle element + normalize with sum_i=0^2l+1 i = (2l+1)(l+1)
	  DREAL inner_sum= ((DREAL) sum + (length+1)*match[t+length]) / ((2*length+1)*(length+1));
	  DREAL s=inner_sum;

	  for (j=1; j<inner_degree; j++)
		  inner_sum*=s;

	  outer_sum+=inner_sum;
	}

	double result=outer_sum;

	for (i=1; i<outer_degree; i++)
	  result*=outer_sum;

	return (double) result;
}
<commit_msg>LocalityImprovedStringKernel: Simplify for loop<commit_after>/*
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * Written (W) 1999-2007 Gunnar Raetsch
 * Written (W) 1999-2007 Soeren Sonnenburg
 * Copyright (C) 1999-2007 Fraunhofer Institute FIRST and Max-Planck-Society
 */

#include "lib/common.h"
#include "lib/io.h"
#include "kernel/LocalityImprovedStringKernel.h"
#include "features/Features.h"
#include "features/StringFeatures.h"

CLocalityImprovedStringKernel::CLocalityImprovedStringKernel(LONG size, INT l, INT d1, INT d2)
  : CStringKernel<CHAR>(size),length(l),inner_degree(d1),outer_degree(d2),match(NULL)
{
	SG_INFO( "LIK with parms: l=%d, d1=%d, d2=%d created!\n", l, d1, d2);
}

CLocalityImprovedStringKernel::~CLocalityImprovedStringKernel()
{
	cleanup();
}

bool CLocalityImprovedStringKernel::init(CFeatures* l, CFeatures* r)
{
	bool result = CStringKernel<CHAR>::init(l,r);

	if (!result)
		return false;
	match = new CHAR[((CStringFeatures<CHAR>*) l)->get_max_vector_length()];
	return match? true : false;
}

void CLocalityImprovedStringKernel::cleanup()
{
	delete[] match;
	match = NULL;
}

bool CLocalityImprovedStringKernel::load_init(FILE* src)
{
	return false;
}

bool CLocalityImprovedStringKernel::save_init(FILE* dest)
{
	return false;
}

DREAL CLocalityImprovedStringKernel::compute(INT idx_a, INT idx_b)
{
	INT alen, blen;

	CHAR* avec=((CStringFeatures<CHAR>*) lhs)->get_feature_vector(idx_a, alen);
	CHAR* bvec=((CStringFeatures<CHAR>*) rhs)->get_feature_vector(idx_b, blen);

	// can only deal with strings of same length
	ASSERT(alen==blen);

	INT i,j,t;

	// initialize match table 1 -> match;  0 -> no match
	for (i=0; i<alen; i++)
		match[i] = (avec[i] == bvec[i])? 1 : 0;

	DREAL outer_sum=0;

	for (t=0; t<alen-length; t++)
	{
	  INT sum=0;
	  for (i=0; i<length; i++)
		  sum+=(i+1)*match[t+i]+(length-i)*match[t+i+length+1];

	  //add middle element + normalize with sum_i=0^2l+1 i = (2l+1)(l+1)
	  DREAL inner_sum= ((DREAL) sum + (length+1)*match[t+length]) / ((2*length+1)*(length+1));
	  DREAL s=inner_sum;

	  for (j=1; j<inner_degree; j++)
		  inner_sum*=s;

	  outer_sum+=inner_sum;
	}

	double result=outer_sum;

	for (i=1; i<outer_degree; i++)
	  result*=outer_sum;

	return (double) result;
}
<|endoftext|>


<commit_before>#include "batch-downloader.h"
#include <QCoreApplication>
#include <QSettings>
#include <QTimer>
#include "commands/commands.h"
#include "downloader/download-query-group.h"
#include "downloader/download-query-image.h"
#include "downloader/image-downloader.h"
#include "loader/pack-loader.h"
#include "models/profile.h"
#include "models/site.h"

BatchDownloader::BatchDownloader(DownloadQuery *query, Profile *profile, QObject *parent)
	: QObject(parent), m_query(query), m_profile(profile), m_settings(profile->getSettings()), m_step(BatchDownloadStep::NotStarted)
{}

void BatchDownloader::setCurrentStep(BatchDownloadStep step)
{
	emit stepChanged(step);
	m_step = step;
}

BatchDownloader::BatchDownloadStep BatchDownloader::currentStep() const
{
	return m_step;
}

int BatchDownloader::totalCount() const
{
	return m_totalCount;
}

int BatchDownloader::downloadedCount() const
{
	return m_counterSum;
}

int BatchDownloader::downloadedCount(Counter counter) const
{
	return m_counters[counter];
}

DownloadQuery *BatchDownloader::query() const
{
	return m_query;
}

void BatchDownloader::start()
{
	// Resume download
	if (m_step == BatchDownloadStep::Aborted) {
		if (!m_imageDownloaders.isEmpty()) {
			setCurrentStep(BatchDownloadStep::ImageDownload);
			for (auto it = m_imageDownloaders.constBegin(); it != m_imageDownloaders.constEnd(); ++it) {
				it.value()->save();
			}
			return;
		} else if (m_packLoader != nullptr) {
			nextPack();
			return;
		}
	}
	// Invalid step
	else if (m_step != BatchDownloadStep::NotStarted) {
		return;
	}

	// Reset counters
	m_counters.clear();
	m_counterSum = 0;

	// Reset total
	auto *group = dynamic_cast<DownloadQueryGroup*>(m_query);
	m_totalCount = group != nullptr ? group->total : 1;

	// m_profile->getCommands().before();
	login();
}

void BatchDownloader::abort()
{
	setCurrentStep(BatchDownloadStep::Aborted);

	if (!m_imageDownloaders.isEmpty()) {
		for (auto it = m_imageDownloaders.constBegin(); it != m_imageDownloaders.constEnd(); ++it) {
			it.value()->abort();
		}
	} else if (m_packLoader != nullptr) {
		m_packLoader->abort();
	}
}

void BatchDownloader::login()
{
	setCurrentStep(BatchDownloadStep::Login);

	Site *site = m_query->site;
	connect(site, &Site::loggedIn, this, &BatchDownloader::loginFinished, Qt::QueuedConnection);
	site->login();
}

void BatchDownloader::loginFinished()
{
	disconnect(m_query->site, &Site::loggedIn, this, &BatchDownloader::loginFinished);

	auto *group = dynamic_cast<DownloadQueryGroup*>(m_query);
	if (group != nullptr) {
		bool usePacking = m_settings->value("packing_enable", true).toBool();
		int imagesPerPack = m_settings->value("packing_size", 1000).toInt();
		m_packLoader = new PackLoader(m_profile, *group, usePacking ? imagesPerPack : -1, this);
		m_packLoader->start();
		nextPack();
	} else {
		auto *img = dynamic_cast<DownloadQueryImage*>(m_query);
		m_pendingDownloads.append(img->image);
		nextImages();
	}
}

void BatchDownloader::nextPack()
{
	if (!m_packLoader->hasNext()) {
		allFinished();
		return;
	}

	setCurrentStep(BatchDownloadStep::PageDownload);

	int packSize = m_packLoader->nextPackSize();
	auto images = m_packLoader->next();

	// Check missing images from the pack (if we expected 1000 but only got 900, we should consider 100 missing)
	m_counters[Counter::Missing] += packSize - images.count();

	m_pendingDownloads.append(images);
	nextImages();
}

void BatchDownloader::nextImages()
{
	setCurrentStep(BatchDownloadStep::ImageDownload);

	// Start the simultaneous downloads
	int count = qMax(1, qMin(m_settings->value("Save/simultaneous").toInt(), 10));
	m_currentlyProcessing.storeRelaxed(count); // TODO: this should be shared amongst instances
	for (int i = 0; i < count; ++i) {
		nextImage();
	}
}

void BatchDownloader::nextImage()
{
	// We quit as soon as the user cancels
	if (m_step != BatchDownloadStep::ImageDownload) {
		return;
	}

	// If we already finished
	if (m_pendingDownloads.empty()) {
		if (m_currentlyProcessing.fetchAndAddRelaxed(-1) == 1) {
			allFinished();
		}
		return;
	}

	// We take the first image to download
	QSharedPointer<Image> img = m_pendingDownloads.dequeue();
	loadImage(img);
}

void BatchDownloader::loadImage(QSharedPointer<Image> img)
{
	// If there is already a downloader for this image, we simply restart it
	if (m_imageDownloaders.contains(img)) {
		m_imageDownloaders[img]->save();
		return;
	}

	// Path
	QString filename = m_query->filename;
	QString path = m_query->path;
	auto *group = dynamic_cast<DownloadQueryGroup*>(m_query);

	// Start loading and saving image
	int count = m_counterSum + 1;
	bool getBlacklisted = group == nullptr || group->getBlacklisted;
	auto imgDownloader = new ImageDownloader(m_profile, img, filename, path, count, true, false, this);
	if (!getBlacklisted) {
		imgDownloader->setBlacklist(&m_profile->getBlacklist());
	}
	connect(imgDownloader, &ImageDownloader::saved, this, &BatchDownloader::loadImageFinished, Qt::UniqueConnection);
	m_imageDownloaders[img] = imgDownloader;
	imgDownloader->save();
}

void BatchDownloader::loadImageFinished(const QSharedPointer<Image> &img, QList<ImageSaveResult> result)
{
	// Delete ImageDownloader to prevent leaks
	m_imageDownloaders[img]->deleteLater();
	m_imageDownloaders.remove(img);

	// Save error count to compare it later on
	bool diskError = false;
	const auto res = result.first().result;

	// Disk writing errors
	for (const ImageSaveResult &re : result) {
		if (re.result == Image::SaveResult::Error) {
			// TODO: detect disk error
			// TODO: report errors somehos
		}
	}

	// Increase counters
	if (res == Image::SaveResult::NetworkError) {
		m_counters[Counter::Errors]++;
		m_failedDownloads.append(img);
	} else if (res == Image::SaveResult::NotFound) {
		m_counters[Counter::NotFound]++;
	} else if (res == Image::SaveResult::AlreadyExistsDisk) {
		m_counters[Counter::AlreadyExists]++;
	} else if (res == Image::SaveResult::Blacklisted || res == Image::SaveResult::AlreadyExistsMd5 || res == Image::SaveResult::AlreadyExistsDeletedMd5) {
		m_counters[Counter::Ignored]++;
	} else if (!diskError) {
		m_counters[Counter::Downloaded]++;
	}

	// Start downloading the next image
	if (!diskError) {
		m_counterSum++;

		QCoreApplication::processEvents();
		QTimer::singleShot(0, this, SLOT(nextImage()));
	}
}

void BatchDownloader::allFinished()
{
	// If we didn't really finish yet
	if (m_packLoader != nullptr && m_packLoader->hasNext()) {
		nextPack();
		return;
	}

	// Cleanup
	if (m_packLoader != nullptr) {
		m_packLoader->deleteLater();
		m_packLoader = nullptr;
	}

	// m_profile->getCommands().after();
	setCurrentStep(BatchDownloadStep::Finished);

	emit finished();
}
<commit_msg>Fix issue with dynamic cast in BatchDownloader class (PR #2538)<commit_after>#include "batch-downloader.h"
#include <QCoreApplication>
#include <QSettings>
#include <QTimer>
#include "commands/commands.h"
#include "downloader/download-query-group.h"
#include "downloader/download-query-image.h"
#include "downloader/image-downloader.h"
#include "loader/pack-loader.h"
#include "models/profile.h"
#include "models/site.h"

BatchDownloader::BatchDownloader(DownloadQuery *query, Profile *profile, QObject *parent)
	: QObject(parent), m_query(query), m_profile(profile), m_settings(profile->getSettings()), m_step(BatchDownloadStep::NotStarted)
{}

void BatchDownloader::setCurrentStep(BatchDownloadStep step)
{
	emit stepChanged(step);
	m_step = step;
}

BatchDownloader::BatchDownloadStep BatchDownloader::currentStep() const
{
	return m_step;
}

int BatchDownloader::totalCount() const
{
	return m_totalCount;
}

int BatchDownloader::downloadedCount() const
{
	return m_counterSum;
}

int BatchDownloader::downloadedCount(Counter counter) const
{
	return m_counters[counter];
}

DownloadQuery *BatchDownloader::query() const
{
	return m_query;
}

void BatchDownloader::start()
{
	// Resume download
	if (m_step == BatchDownloadStep::Aborted) {
		if (!m_imageDownloaders.isEmpty()) {
			setCurrentStep(BatchDownloadStep::ImageDownload);
			for (auto it = m_imageDownloaders.constBegin(); it != m_imageDownloaders.constEnd(); ++it) {
				it.value()->save();
			}
			return;
		} else if (m_packLoader != nullptr) {
			nextPack();
			return;
		}
	}
	// Invalid step
	else if (m_step != BatchDownloadStep::NotStarted) {
		return;
	}

	// Reset counters
	m_counters.clear();
	m_counterSum = 0;

	// Reset total
	auto *group = dynamic_cast<DownloadQueryGroup*>(m_query);
	m_totalCount = group != nullptr ? group->total : 1;

	// m_profile->getCommands().before();
	login();
}

void BatchDownloader::abort()
{
	setCurrentStep(BatchDownloadStep::Aborted);

	if (!m_imageDownloaders.isEmpty()) {
		for (auto it = m_imageDownloaders.constBegin(); it != m_imageDownloaders.constEnd(); ++it) {
			it.value()->abort();
		}
	} else if (m_packLoader != nullptr) {
		m_packLoader->abort();
	}
}

void BatchDownloader::login()
{
	setCurrentStep(BatchDownloadStep::Login);

	Site *site = m_query->site;
	connect(site, &Site::loggedIn, this, &BatchDownloader::loginFinished, Qt::QueuedConnection);
	site->login();
}

void BatchDownloader::loginFinished()
{
	disconnect(m_query->site, &Site::loggedIn, this, &BatchDownloader::loginFinished);

	auto *group = dynamic_cast<DownloadQueryGroup*>(m_query);
	if (group != nullptr) {
		bool usePacking = m_settings->value("packing_enable", true).toBool();
		int imagesPerPack = m_settings->value("packing_size", 1000).toInt();
		m_packLoader = new PackLoader(m_profile, *group, usePacking ? imagesPerPack : -1, this);
		m_packLoader->start();
		nextPack();
	} else {
		auto *img = dynamic_cast<DownloadQueryImage*>(m_query);
		if (!img) {
			return;
		}
		m_pendingDownloads.append(img->image);
		nextImages();
	}
}

void BatchDownloader::nextPack()
{
	if (!m_packLoader->hasNext()) {
		allFinished();
		return;
	}

	setCurrentStep(BatchDownloadStep::PageDownload);

	int packSize = m_packLoader->nextPackSize();
	auto images = m_packLoader->next();

	// Check missing images from the pack (if we expected 1000 but only got 900, we should consider 100 missing)
	m_counters[Counter::Missing] += packSize - images.count();

	m_pendingDownloads.append(images);
	nextImages();
}

void BatchDownloader::nextImages()
{
	setCurrentStep(BatchDownloadStep::ImageDownload);

	// Start the simultaneous downloads
	int count = qMax(1, qMin(m_settings->value("Save/simultaneous").toInt(), 10));
	m_currentlyProcessing.storeRelaxed(count); // TODO: this should be shared amongst instances
	for (int i = 0; i < count; ++i) {
		nextImage();
	}
}

void BatchDownloader::nextImage()
{
	// We quit as soon as the user cancels
	if (m_step != BatchDownloadStep::ImageDownload) {
		return;
	}

	// If we already finished
	if (m_pendingDownloads.empty()) {
		if (m_currentlyProcessing.fetchAndAddRelaxed(-1) == 1) {
			allFinished();
		}
		return;
	}

	// We take the first image to download
	QSharedPointer<Image> img = m_pendingDownloads.dequeue();
	loadImage(img);
}

void BatchDownloader::loadImage(QSharedPointer<Image> img)
{
	// If there is already a downloader for this image, we simply restart it
	if (m_imageDownloaders.contains(img)) {
		m_imageDownloaders[img]->save();
		return;
	}

	// Path
	QString filename = m_query->filename;
	QString path = m_query->path;
	auto *group = dynamic_cast<DownloadQueryGroup*>(m_query);

	// Start loading and saving image
	int count = m_counterSum + 1;
	bool getBlacklisted = group == nullptr || group->getBlacklisted;
	auto imgDownloader = new ImageDownloader(m_profile, img, filename, path, count, true, false, this);
	if (!getBlacklisted) {
		imgDownloader->setBlacklist(&m_profile->getBlacklist());
	}
	connect(imgDownloader, &ImageDownloader::saved, this, &BatchDownloader::loadImageFinished, Qt::UniqueConnection);
	m_imageDownloaders[img] = imgDownloader;
	imgDownloader->save();
}

void BatchDownloader::loadImageFinished(const QSharedPointer<Image> &img, QList<ImageSaveResult> result)
{
	// Delete ImageDownloader to prevent leaks
	m_imageDownloaders[img]->deleteLater();
	m_imageDownloaders.remove(img);

	// Save error count to compare it later on
	bool diskError = false;
	const auto res = result.first().result;

	// Disk writing errors
	for (const ImageSaveResult &re : result) {
		if (re.result == Image::SaveResult::Error) {
			// TODO: detect disk error
			// TODO: report errors somehos
		}
	}

	// Increase counters
	if (res == Image::SaveResult::NetworkError) {
		m_counters[Counter::Errors]++;
		m_failedDownloads.append(img);
	} else if (res == Image::SaveResult::NotFound) {
		m_counters[Counter::NotFound]++;
	} else if (res == Image::SaveResult::AlreadyExistsDisk) {
		m_counters[Counter::AlreadyExists]++;
	} else if (res == Image::SaveResult::Blacklisted || res == Image::SaveResult::AlreadyExistsMd5 || res == Image::SaveResult::AlreadyExistsDeletedMd5) {
		m_counters[Counter::Ignored]++;
	} else if (!diskError) {
		m_counters[Counter::Downloaded]++;
	}

	// Start downloading the next image
	if (!diskError) {
		m_counterSum++;

		QCoreApplication::processEvents();
		QTimer::singleShot(0, this, SLOT(nextImage()));
	}
}

void BatchDownloader::allFinished()
{
	// If we didn't really finish yet
	if (m_packLoader != nullptr && m_packLoader->hasNext()) {
		nextPack();
		return;
	}

	// Cleanup
	if (m_packLoader != nullptr) {
		m_packLoader->deleteLater();
		m_packLoader = nullptr;
	}

	// m_profile->getCommands().after();
	setCurrentStep(BatchDownloadStep::Finished);

	emit finished();
}
<|endoftext|>


<commit_before>#include <algorithm>
#include <cmath>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <map>
#include <sstream>
#include <string>
#include <vector>

#include <popt.h>

#include "defs.hh"
#include "StringSet.hh"
#include "FactorEncoder.hh"
#include "Unigrams.hh"
#include "Bigrams.hh"

using namespace std;

void
assert_single_chars(map<string, flt_type> &vocab,
                    const map<string, flt_type> &chars,
                    flt_type val)
{
    for (auto it = chars.cbegin(); it != chars.cend(); ++it)
        if (vocab.find(it->first) == vocab.end())
            vocab[it->first] = val;
}

int main(int argc, char* argv[]) {

    int iter_amount = 10;
    int least_common = 0;
    int target_vocab_size = 30000;
    float cutoff_value = 0.0;
    flt_type one_char_min_lp = -25.0;
    string vocab_fname;
    string wordlist_fname;
    string msfg_fname;
    string transition_fname;

    // Popt documentation:
    // http://linux.die.net/man/3/popt
    // http://privatemisc.blogspot.fi/2012/12/popt-basic-example.html
    poptContext pc;
    struct poptOption po[] = {
        {"iter", 'i', POPT_ARG_INT, &iter_amount, 11001, NULL, "How many iterations"},
        {"least-common", 'l', POPT_ARG_INT, &least_common, 11001, NULL, "Remove least common strings"},
        {"vocab_size", 'g', POPT_ARG_INT, &target_vocab_size, 11007, NULL, "Target vocabulary size (stopping criterion)"},
        {"cutoff", 'u', POPT_ARG_FLOAT, &cutoff_value, 11001, NULL, "Cutoff value for each iteration"},
        POPT_AUTOHELP
        {NULL}
    };

    pc = poptGetContext(NULL, argc, (const char **)argv, po, 0);
    poptSetOtherOptionHelp(pc, "[INITIAL VOCABULARY] [WORDLIST] [MSFG_IN] [TRANSITIONS]");

    int val;
    while ((val = poptGetNextOpt(pc)) >= 0)
        continue;

    // poptGetNextOpt returns -1 when the final argument has been parsed
    // otherwise an error occured
    if (val != -1) {
        switch (val) {
        case POPT_ERROR_NOARG:
            cerr << "Argument missing for an option" << endl;
            exit(1);
        case POPT_ERROR_BADOPT:
            cerr << "Option's argument could not be parsed" << endl;
            exit(1);
        case POPT_ERROR_BADNUMBER:
        case POPT_ERROR_OVERFLOW:
            cerr << "Option could not be converted to number" << endl;
            exit(1);
        default:
            cerr << "Unknown error in option processing" << endl;
            exit(1);
        }
    }

    // Handle ARG part of command line
    if (poptPeekArg(pc) != NULL)
        vocab_fname.assign((char*)poptGetArg(pc));
    else {
        cerr << "Initial vocabulary file not set" << endl;
        exit(1);
    }

    if (poptPeekArg(pc) != NULL)
        wordlist_fname.assign((char*)poptGetArg(pc));
    else {
        cerr << "Wordlist file not set" << endl;
        exit(1);
    }

    if (poptPeekArg(pc) != NULL)
        msfg_fname.assign((char*)poptGetArg(pc));
    else {
        cerr << "Input MSFG file not set" << endl;
        exit(1);
    }

    if (poptPeekArg(pc) != NULL)
        transition_fname.assign((char*)poptGetArg(pc));
    else {
        cerr << "Transition file not set" << endl;
        exit(1);
    }

    cerr << "parameters, initial vocabulary: " << vocab_fname << endl;
    cerr << "parameters, wordlist: " << wordlist_fname << endl;
    cerr << "parameters, msfg: " << msfg_fname << endl;
    cerr << "parameters, transitions: " << transition_fname << endl;
    cerr << "parameters, cutoff: " << setprecision(15) << cutoff_value << endl;
    cerr << "parameters, iterations: " << iter_amount << endl;
    cerr << "parameters, target vocab size: " << target_vocab_size << endl;
    cerr << "parameters, least common subwords to remove: " << least_common << endl;

    int maxlen, word_maxlen;
    map<string, flt_type> all_chars;
    map<string, flt_type> vocab;
    map<string, flt_type> freqs;
    map<string, flt_type> words;

    cerr << "Reading vocabulary " << vocab_fname << endl;
    int retval = read_vocab(vocab_fname, vocab, maxlen);
    if (retval < 0) {
        cerr << "something went wrong reading vocabulary" << endl;
        exit(0);
    }
    cerr << "\t" << "size: " << vocab.size() << endl;
    cerr << "\t" << "maximum string length: " << maxlen << endl;
    for (auto it = vocab.cbegin(); it != vocab.end(); ++it)
        if (it->first.length() == 1)
            all_chars[it->first] = 0.0;

    cerr << "Reading word list " << wordlist_fname << endl;
    retval = read_vocab(wordlist_fname, words, word_maxlen);
    if (retval < 0) {
        cerr << "something went wrong reading word list" << endl;
        exit(0);
    }
    cerr << "\t" << "wordlist size: " << words.size() << endl;
    cerr << "\t" << "maximum word length: " << word_maxlen << endl;

    Unigrams ug;
    ug.set_segmentation_method(forward_backward);

    cerr << "Initial cutoff" << endl;
    ug.resegment_words(words, vocab, freqs);
    flt_type densum = ug.get_sum(freqs);
    flt_type cost = ug.get_cost(freqs, densum);
    cerr << "unigram cost without word end symbols: " << cost << endl;

    ug.cutoff(freqs, (flt_type)cutoff_value);
    cerr << "\tcutoff: " << cutoff_value << "\t" << "vocabulary size: " << freqs.size() << endl;
    vocab = freqs;
    densum = ug.get_sum(vocab);
    ug.freqs_to_logprobs(vocab, densum);
    assert_single_chars(vocab, all_chars, one_char_min_lp);

    string start_end_symbol("*");
    StringSet<flt_type> ss_vocab(vocab);
    map<string, FactorGraph*> fg_words;
    MultiStringFactorGraph msfg(start_end_symbol);
    msfg.read(msfg_fname);
    transitions_t transitions;
    transitions_t trans_stats;
    map<string, flt_type> trans_normalizers;
    map<string, flt_type> unigram_stats;
    vocab[start_end_symbol] = 0.0;

    // Initial segmentation using unigram model
    Bigrams::collect_trans_stats(vocab, words, msfg, trans_stats, trans_normalizers, unigram_stats);

    // Unigram cost with word end markers
    densum = ug.get_sum(unigram_stats);
    cost = ug.get_cost(unigram_stats, densum);
    cerr << "unigram cost with word end symbols: " << cost << endl;

    // Initial bigram cost
    transitions = trans_stats;
    Bigrams::normalize(transitions, trans_normalizers);
    cerr << "bigram cost: " << Bigrams::bigram_cost(transitions, trans_stats) << endl;
    cerr << "\tamount of transitions: " << Bigrams::transition_count(transitions) << endl;
    cerr << "\tvocab size: " << unigram_stats.size() << endl;
    int co_removed = Bigrams::cutoff(unigram_stats, cutoff_value, transitions, msfg);
    cerr << "\tremoved by cutoff: " << co_removed << endl;

    // Re-estimate using bigram stats
    int vocab_size = unigram_stats.size();
    for (int i=0; i<iter_amount; i++) {

        Bigrams::collect_trans_stats(transitions, words, msfg, trans_stats, trans_normalizers, unigram_stats);
        transitions = trans_stats;
        Bigrams::normalize(transitions, trans_normalizers);
        vocab_size = unigram_stats.size();
        cerr << "bigram cost: " << Bigrams::bigram_cost(transitions, trans_stats) << endl;
        cerr << "\tamount of transitions: " << Bigrams::transition_count(transitions) << endl;
        cerr << "\tvocab size: " << vocab_size << endl;

        // Write temp transitions
        ostringstream transitions_temp;
        transitions_temp << "transitions.iter" << i+1;
        Bigrams::write_transitions(transitions, transitions_temp.str());

        int curr_least_common = least_common + (vocab_size % 1000);
        int lc_removed = Bigrams::remove_least_common(unigram_stats, curr_least_common, transitions, msfg);
        cerr << "\tremoved " << lc_removed << " least common subwords" << endl;
        vocab_size = transitions.size();

        if  (vocab_size < target_vocab_size) break;
    }

    // Write transitions
    Bigrams::write_transitions(transitions, transition_fname);
    Bigrams::write_transitions(trans_stats, "debug.trans_stats.out");
    write_vocab("debug.unigram.out", unigram_stats);

    exit(1);
}
<commit_msg>Minor fix.<commit_after>#include <algorithm>
#include <cmath>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <map>
#include <sstream>
#include <string>
#include <vector>

#include <popt.h>

#include "defs.hh"
#include "StringSet.hh"
#include "FactorEncoder.hh"
#include "Unigrams.hh"
#include "Bigrams.hh"

using namespace std;

void
assert_single_chars(map<string, flt_type> &vocab,
                    const map<string, flt_type> &chars,
                    flt_type val)
{
    for (auto it = chars.cbegin(); it != chars.cend(); ++it)
        if (vocab.find(it->first) == vocab.end())
            vocab[it->first] = val;
}

int main(int argc, char* argv[]) {

    int iter_amount = 10;
    int least_common = 0;
    int target_vocab_size = 30000;
    float cutoff_value = 0.0;
    flt_type one_char_min_lp = -25.0;
    string vocab_fname;
    string wordlist_fname;
    string msfg_fname;
    string transition_fname;

    // Popt documentation:
    // http://linux.die.net/man/3/popt
    // http://privatemisc.blogspot.fi/2012/12/popt-basic-example.html
    poptContext pc;
    struct poptOption po[] = {
        {"iter", 'i', POPT_ARG_INT, &iter_amount, 11001, NULL, "How many iterations"},
        {"least-common", 'l', POPT_ARG_INT, &least_common, 11001, NULL, "Remove least common strings"},
        {"vocab_size", 'g', POPT_ARG_INT, &target_vocab_size, 11007, NULL, "Target vocabulary size (stopping criterion)"},
        {"cutoff", 'u', POPT_ARG_FLOAT, &cutoff_value, 11001, NULL, "Cutoff value for each iteration"},
        POPT_AUTOHELP
        {NULL}
    };

    pc = poptGetContext(NULL, argc, (const char **)argv, po, 0);
    poptSetOtherOptionHelp(pc, "[INITIAL VOCABULARY] [WORDLIST] [MSFG_IN] [TRANSITIONS]");

    int val;
    while ((val = poptGetNextOpt(pc)) >= 0)
        continue;

    // poptGetNextOpt returns -1 when the final argument has been parsed
    // otherwise an error occured
    if (val != -1) {
        switch (val) {
        case POPT_ERROR_NOARG:
            cerr << "Argument missing for an option" << endl;
            exit(1);
        case POPT_ERROR_BADOPT:
            cerr << "Option's argument could not be parsed" << endl;
            exit(1);
        case POPT_ERROR_BADNUMBER:
        case POPT_ERROR_OVERFLOW:
            cerr << "Option could not be converted to number" << endl;
            exit(1);
        default:
            cerr << "Unknown error in option processing" << endl;
            exit(1);
        }
    }

    // Handle ARG part of command line
    if (poptPeekArg(pc) != NULL)
        vocab_fname.assign((char*)poptGetArg(pc));
    else {
        cerr << "Initial vocabulary file not set" << endl;
        exit(1);
    }

    if (poptPeekArg(pc) != NULL)
        wordlist_fname.assign((char*)poptGetArg(pc));
    else {
        cerr << "Wordlist file not set" << endl;
        exit(1);
    }

    if (poptPeekArg(pc) != NULL)
        msfg_fname.assign((char*)poptGetArg(pc));
    else {
        cerr << "Input MSFG file not set" << endl;
        exit(1);
    }

    if (poptPeekArg(pc) != NULL)
        transition_fname.assign((char*)poptGetArg(pc));
    else {
        cerr << "Transition file not set" << endl;
        exit(1);
    }

    cerr << "parameters, initial vocabulary: " << vocab_fname << endl;
    cerr << "parameters, wordlist: " << wordlist_fname << endl;
    cerr << "parameters, msfg: " << msfg_fname << endl;
    cerr << "parameters, transitions: " << transition_fname << endl;
    cerr << "parameters, cutoff: " << setprecision(15) << cutoff_value << endl;
    cerr << "parameters, iterations: " << iter_amount << endl;
    cerr << "parameters, target vocab size: " << target_vocab_size << endl;
    cerr << "parameters, least common subwords to remove: " << least_common << endl;

    int maxlen, word_maxlen;
    map<string, flt_type> all_chars;
    map<string, flt_type> vocab;
    map<string, flt_type> freqs;
    map<string, flt_type> words;

    cerr << "Reading vocabulary " << vocab_fname << endl;
    int retval = read_vocab(vocab_fname, vocab, maxlen);
    if (retval < 0) {
        cerr << "something went wrong reading vocabulary" << endl;
        exit(0);
    }
    cerr << "\t" << "size: " << vocab.size() << endl;
    cerr << "\t" << "maximum string length: " << maxlen << endl;
    for (auto it = vocab.cbegin(); it != vocab.end(); ++it)
        if (it->first.length() == 1)
            all_chars[it->first] = 0.0;

    cerr << "Reading word list " << wordlist_fname << endl;
    retval = read_vocab(wordlist_fname, words, word_maxlen);
    if (retval < 0) {
        cerr << "something went wrong reading word list" << endl;
        exit(0);
    }
    cerr << "\t" << "wordlist size: " << words.size() << endl;
    cerr << "\t" << "maximum word length: " << word_maxlen << endl;

    Unigrams ug;
    ug.set_segmentation_method(forward_backward);

    cerr << "Initial cutoff" << endl;
    ug.resegment_words(words, vocab, freqs);
    flt_type densum = ug.get_sum(freqs);
    flt_type cost = ug.get_cost(freqs, densum);
    cerr << "unigram cost without word end symbols: " << cost << endl;

    ug.cutoff(freqs, (flt_type)cutoff_value);
    cerr << "\tcutoff: " << cutoff_value << "\t" << "vocabulary size: " << freqs.size() << endl;
    vocab = freqs;
    densum = ug.get_sum(vocab);
    ug.freqs_to_logprobs(vocab, densum);
    assert_single_chars(vocab, all_chars, one_char_min_lp);

    string start_end_symbol("*");
    StringSet<flt_type> ss_vocab(vocab);
    map<string, FactorGraph*> fg_words;
    MultiStringFactorGraph msfg(start_end_symbol);
    msfg.read(msfg_fname);
    transitions_t transitions;
    transitions_t trans_stats;
    map<string, flt_type> trans_normalizers;
    map<string, flt_type> unigram_stats;
    vocab[start_end_symbol] = 0.0;

    // Initial segmentation using unigram model
    Bigrams::collect_trans_stats(vocab, words, msfg, trans_stats, trans_normalizers, unigram_stats);

    // Unigram cost with word end markers
    densum = ug.get_sum(unigram_stats);
    cost = ug.get_cost(unigram_stats, densum);
    cerr << "unigram cost with word end symbols: " << cost << endl;

    // Initial bigram cost
    transitions = trans_stats;
    Bigrams::normalize(transitions, trans_normalizers);
    cerr << "bigram cost: " << Bigrams::bigram_cost(transitions, trans_stats) << endl;
    cerr << "\tamount of transitions: " << Bigrams::transition_count(transitions) << endl;
    cerr << "\tvocab size: " << unigram_stats.size() << endl;
    int co_removed = Bigrams::cutoff(unigram_stats, cutoff_value, transitions, msfg);
    cerr << "\tremoved by cutoff: " << co_removed << endl;

    // Re-estimate using bigram stats
    int vocab_size = unigram_stats.size();
    for (int i=0; i<iter_amount; i++) {

        Bigrams::collect_trans_stats(transitions, words, msfg, trans_stats, trans_normalizers, unigram_stats);
        transitions = trans_stats;
        Bigrams::normalize(transitions, trans_normalizers);
        vocab_size = unigram_stats.size();
        cerr << "bigram cost: " << Bigrams::bigram_cost(transitions, trans_stats) << endl;
        cerr << "\tamount of transitions: " << Bigrams::transition_count(transitions) << endl;
        cerr << "\tvocab size: " << vocab_size << endl;

        // Write temp transitions
        ostringstream transitions_temp;
        transitions_temp << "transitions.iter" << i+1;
        Bigrams::write_transitions(transitions, transitions_temp.str());

        if (least_common > 0) {
            int curr_least_common = least_common + (vocab_size % 1000);
            int lc_removed = Bigrams::remove_least_common(unigram_stats, curr_least_common, transitions, msfg);
            cerr << "\tremoved " << lc_removed << " least common subwords" << endl;
            vocab_size = transitions.size();
        }

        if  (vocab_size < target_vocab_size) break;
    }

    // Write transitions
    Bigrams::write_transitions(transitions, transition_fname);
    Bigrams::write_transitions(trans_stats, "debug.trans_stats.out");
    write_vocab("debug.unigram.out", unigram_stats);

    exit(1);
}
<|endoftext|>