<commit_before>#include <ctime>
#include <ncurses.h>
#include <stdlib.h> //random
#include <unistd.h> //getopt
#include <iostream>

//----------------------------------------[Global variables]----------------------------------------

int WIDTH, HEIGHT;  //Set by ncurses
int maxtemp;        //maximum flame temperature
int framerate;      //framerate
char dispch;        //the character used to draw the flames

//------------------------------[Memory Management and Initialization]------------------------------

int** init(int y, int x){
    int** out = new int*[y];
    for(int i = 0; i < y; i++){
        out[i] = new int[x];
    }

    for(int i = 0; i < y; i++){
        for(int j = 0; j < x; j++){
            out[i][j] = 0;
        }
    }
    return out;
}

void deallocate(int** in, int rows){
    for(int i = 0; i < rows; i++){
        delete[] in[i];
    }
    delete[] in;
}

void start_ncurses(){
    initscr();
    start_color();
    init_color(COLOR_RED,      300,   0,     0);
    init_color(COLOR_GREEN,    500,   0,     0);
    init_color(COLOR_BLUE,     700,   100,   0);
    init_color(COLOR_YELLOW,   900,   300,   0);
    init_color(COLOR_MAGENTA,  1000,  500,   100);
    init_color(COLOR_CYAN,     1000,  800,   500);
    init_color(COLOR_WHITE,    1000,  1000,  1000);

    init_pair(1,  COLOR_RED,      COLOR_BLACK);
    init_pair(2,  COLOR_GREEN,    COLOR_BLACK);
    init_pair(3,  COLOR_BLUE,     COLOR_BLACK);
    init_pair(4,  COLOR_YELLOW,   COLOR_BLACK);
    init_pair(5,  COLOR_MAGENTA,  COLOR_BLACK);
    init_pair(6,  COLOR_CYAN,     COLOR_BLACK);
    init_pair(7,  COLOR_WHITE,    COLOR_BLACK);
    curs_set(0);
    timeout(0);
    cbreak();
    noecho();
    keypad(stdscr, TRUE);
    getmaxyx(stdscr, HEIGHT, WIDTH);
}

//---------------------------------------[Cellular Automata]---------------------------------------

//As a cell cools it has a higher chance of cooling again on the next frame.
int cooldown(int max){
    if(max == 0) return 0;
    int r = (rand() % max);
    if(r == 0) max--;
    return max;
}

float hotplate_temp_at(int* hotplate, int x){
    float total = 0;
    for(int i = -9; i <= 9; i++){
        int j = x + i;
        if(j <0 || j >= WIDTH){
            total +=0;
        }
        else{
            total += hotplate[j];
        }
    }
    total = (total * maxtemp) / 9.0;
    return total > maxtemp ? maxtemp : total;
}

void nextframe(int** field, int** count, int* hotplate){
    for(int i = 1; i <= HEIGHT; i++){
        for(int j = 0; j < WIDTH; j++){
            float avg = 0;
            //int temp = rand() % maxtemp * 4;
            int counter = 0;

            //the search space around the current cell is as follows
            //    .......
            //    ...X...
            //    .......
            //    .......
            //    .......
            for(int xoff = -3; xoff <= 3; xoff++){
                for(int yoff = -1; yoff <= 3; yoff++){
                    int y = i + yoff;
                    y = y < 0 ? 0 : y; //if y is less than zero, clamp it to zero.
                    int x = j + xoff;
                    //if the search has gon beyond the left or right, no heat is added
                    if(x < 0 || x >= WIDTH) avg += 0;
                    //if the search goes below the screen, add the hotplate value.
                    //the hotplate has infinite depth.
                    else if(y >= HEIGHT)  avg += hotplate_temp_at(hotplate,x);
                    else avg += field[y][x];
                    counter++;
                }
            }
            avg /= counter;
            //see if the cell cools or not
            //we add the value at (i-1) so that an upward motion will be created.
            count[i - 1][j] = cooldown(avg);
        }
    }

    for(int i = 0; i < HEIGHT; i++){
        for(int j = 0; j < WIDTH; j++){
            field[i][j] = count[i][j];
        }
    }
}

void wolfram(int* world, const int rule){
    int l,c,r;
    int* next = new int[WIDTH];
    int current;
    for(int i = 0; i < WIDTH; i++){
        l = world[(i-1)%WIDTH];
        c = world[i];
        r = world[(i+1)%WIDTH];
        current = (l<<2) | (c<<1) | r;
        next[i] = ((1<<current) & rule) > 0 ? 1 : 0;
    }

    for(int i = 0; i < WIDTH; i++){
        world[i] = next[i];
    }
    delete[] next;
}

void animate(int** field, int** count, int* hotplate){
    char disp;
    for(int i = 0; i < HEIGHT; i++){
        for(int j = 0; j < WIDTH; j++){
            move(i,j);
            //if the cell is cold, print a space, otherwise print [dispch]
            disp = field[i][j] == 0 ? ' ' : dispch;
            int color = (7 * field[i][j] / maxtemp) + 1;
            attron(COLOR_PAIR(color));
            addch(disp);
            attroff(COLOR_PAIR(color));
        }
    }
    nextframe(field, count, hotplate);
    wolfram(hotplate, 60);
    refresh();
}

//-------------------------------------------[Main Loop]-------------------------------------------

unsigned long int time_in_ms(){
    return std::clock() / (double)(CLOCKS_PER_SEC / 1000);
}

void flames(){
    int** field = init(HEIGHT, WIDTH); //The cells that will be displayed
    int** count = init(HEIGHT, WIDTH); //A grid of cells used to tally neighbors for CA purposes
    int* hotplate = new int[WIDTH]; //these special cells provide "heat" at the bottom of the screen.

    for(int i = 0; i < WIDTH; i++){
        hotplate[i] = rand() % 2;
    }

    char c = 0;

    unsigned long int prevframe = time_in_ms();

    while((c = getch()) != 'q'){
        if(time_in_ms() >= prevframe + framerate){
            animate(field, count, hotplate);
            prevframe = time_in_ms();
        }
    }

    refresh();
    delete[] hotplate;
    deallocate(field, HEIGHT);
    deallocate(count, HEIGHT);
}

void printhelp(char progname[]){
    std::cout << "Usage: " << progname << " [options]\n"
        << "\t-c character\tAn ASCII character to draw the flames. Default is '@'\n"
        << "\t-h\t\tPrint this message.\n"
        << "\t-f framerate\tSet the framerate in frames/sec. Default is 20.\n"
        << "\t-t temp\t\tSet the maximum temperature of the flames. Default is 10. A higher temp means taller flames.\n"
        << "\n"
        << "Press q at any time to douse the flames.";
}

int main(int argc, char** argv){
    srand(time(NULL));
    framerate = 1000 / 20;
    maxtemp = 10;
    dispch = '@';

    int c;
    opterr = 0;
    while((c = getopt(argc, argv, "c:hf:t:")) != -1){
        switch (c){
            case 'c':
                dispch = optarg[0];
                break;
            case 'h':
                printhelp(argv[0]);
                return 1;
            case 'f':
                framerate = 1000 / atoi(optarg);
                break;
            case 't':
                maxtemp = atoi(optarg);
                break;
            case '?':
                std::cout << "You've really bunged this one up. Here, this may help:\n\n";
                printhelp(argv[0]);
                return 1;
            default:
                std::cerr << "What the hell?!";
                return 2;
        }
    }

    start_ncurses();
    flames();
    endwin();
    return 0;
}
<commit_msg>Attempted optimization.<commit_after>#include <ctime>
#include <ncurses.h>
#include <stdlib.h> //random
#include <unistd.h> //getopt
#include <iostream>

//----------------------------------------[Global variables]----------------------------------------

int WIDTH, HEIGHT;  //Set by ncurses
int maxtemp;        //maximum flame temperature
int framerate;      //framerate
char dispch;        //the character used to draw the flames
int heightrecord;   //max height reached by the flames

//------------------------------[Memory Management and Initialization]------------------------------

int** init(int y, int x){
    int** out = new int*[y];
    for(int i = 0; i < y; i++){
        out[i] = new int[x];
    }

    for(int i = 0; i < y; i++){
        for(int j = 0; j < x; j++){
            out[i][j] = 0;
        }
    }
    return out;
}

void deallocate(int** in, int rows){
    for(int i = 0; i < rows; i++){
        delete[] in[i];
    }
    delete[] in;
}

void start_ncurses(){
    initscr();
    start_color();
    init_color(COLOR_RED,      300,   0,     0);
    init_color(COLOR_GREEN,    500,   0,     0);
    init_color(COLOR_BLUE,     700,   100,   0);
    init_color(COLOR_YELLOW,   900,   300,   0);
    init_color(COLOR_MAGENTA,  1000,  500,   100);
    init_color(COLOR_CYAN,     1000,  800,   500);
    init_color(COLOR_WHITE,    1000,  1000,  1000);

    init_pair(1,  COLOR_RED,      COLOR_BLACK);
    init_pair(2,  COLOR_GREEN,    COLOR_BLACK);
    init_pair(3,  COLOR_BLUE,     COLOR_BLACK);
    init_pair(4,  COLOR_YELLOW,   COLOR_BLACK);
    init_pair(5,  COLOR_MAGENTA,  COLOR_BLACK);
    init_pair(6,  COLOR_CYAN,     COLOR_BLACK);
    init_pair(7,  COLOR_WHITE,    COLOR_BLACK);
    curs_set(0);
    timeout(0);
    cbreak();
    noecho();
    keypad(stdscr, TRUE);
    getmaxyx(stdscr, HEIGHT, WIDTH);
    heightrecord = HEIGHT;
}

//---------------------------------------[Cellular Automata]---------------------------------------

//As a cell cools it has a higher chance of cooling again on the next frame.
int cooldown(int max){
    if(max == 0) return 0;
    int r = (rand() % max);
    if(r == 0) max--;
    return max;
}

float hotplate_temp_at(int* hotplate, int x){
    float total = 0;
    for(int i = -9; i <= 9; i++){
        int j = x + i;
        if(j < 0 || j >= WIDTH){
            total +=0;
        }
        else{
            total += hotplate[j];
        }
    }
    total = (total * maxtemp) / 9.0;
    return total > maxtemp ? maxtemp : total;
}

void cleargrid(int** grid){
    for(int i = 0; i < HEIGHT; i++){
        for(int j = 0; j < WIDTH; j++){
            grid[i][j] = 0;
        }
    }
}

void nextframe(int** field, int** count, int* hotplate){
    cleargrid(count);
    int rowsum = 0;
    int h = heightrecord - 3;
    h = h < 1 ? 1 : h;  //we can ignore the vast majority of cold cells
                        //and skip down to the bottom of the window
    for(int i = h; i <= HEIGHT; i++){
        for(int j = 0; j < WIDTH; j++){
            float avg = 0;
            //int temp = rand() % maxtemp * 4;
            int counter = 0;

            //the search space around the current cell is as follows
            //    .......
            //    ...X...
            //    .......
            //    .......
            //    .......
            for(int xoff = -3; xoff <= 3; xoff++){
                for(int yoff = -1; yoff <= 3; yoff++){
                    int y = i + yoff;
                    y = y < 0 ? 0 : y; //if y is less than zero, clamp it to zero.
                    int x = j + xoff;
                    //if the search has gon beyond the left or right, no heat is added
                    if(x < 0 || x >= WIDTH) avg += 0;
                    //if the search goes below the screen, add the hotplate value.
                    //the hotplate has infinite depth.
                    else if(y >= HEIGHT)  avg += hotplate_temp_at(hotplate,x);
                    else avg += field[y][x];
                    counter++;
                }
            }
            avg /= counter;
            //see if the cell cools or not
            //we add the value at (i-1) so that an upward motion will be created.
            count[i - 1][j] = cooldown(avg);
            rowsum += count[i-1][j];
        }
        if(rowsum > 0 && i < heightrecord) heightrecord = i;
        rowsum = 0;
    }

    for(int i = 0; i < HEIGHT; i++){
        for(int j = 0; j < WIDTH; j++){
            field[i][j] = count[i][j];
        }
    }
}

void wolfram(int* world, const int rule){
    int l,c,r;
    int* next = new int[WIDTH];
    int current;
    for(int i = 0; i < WIDTH; i++){
        l = world[(i-1)%WIDTH];
        c = world[i];
        r = world[(i+1)%WIDTH];
        current = (l<<2) | (c<<1) | r;
        next[i] = ((1<<current) & rule) > 0 ? 1 : 0;
    }

    for(int i = 0; i < WIDTH; i++){
        world[i] = next[i];
    }
    delete[] next;
}

void animate(int** field, int** count, int* hotplate){
    char disp;
    for(int i = 0; i < HEIGHT; i++){
        for(int j = 0; j < WIDTH; j++){
            move(i,j);
            //if the cell is cold, print a space, otherwise print [dispch]
            disp = field[i][j] == 0 ? ' ' : dispch;
            int color = (7 * field[i][j] / maxtemp) + 1;
            attron(COLOR_PAIR(color));
            addch(disp);
            attroff(COLOR_PAIR(color));
        }
    }
    nextframe(field, count, hotplate);
    wolfram(hotplate, 60);
    refresh();
}

//-------------------------------------------[Main Loop]-------------------------------------------

unsigned long int time_in_ms(){
    return std::clock() / (double)(CLOCKS_PER_SEC / 1000);
}

void flames(){
    int** field = init(HEIGHT, WIDTH); //The cells that will be displayed
    int** count = init(HEIGHT, WIDTH); //A grid of cells used to tally neighbors for CA purposes
    int* hotplate = new int[WIDTH]; //these special cells provide "heat" at the bottom of the screen.

    for(int i = 0; i < WIDTH; i++){
        hotplate[i] = rand() % 2;
    }

    char c = 0;

    unsigned long int prevframe = time_in_ms();

    while((c = getch()) != 'q'){
        if(time_in_ms() >= prevframe + framerate){
            animate(field, count, hotplate);
            prevframe = time_in_ms();
        }
    }

    refresh();
    delete[] hotplate;
    deallocate(field, HEIGHT);
    deallocate(count, HEIGHT);
}

void printhelp(char progname[]){
    std::cout << "\nUsage: " << progname << " [options]\n"
        << "\t-c character\tAn ASCII character to draw the flames. Default is '@'.\n"
        << "\t-h\t\tPrint this message.\n"
        << "\t-f framerate\tSet the framerate in frames/sec. Default is 20.\n"
        << "\t\t\tA framerate of zero will make frames spit out as soon as they are ready.\n"
        << "\t-t temp\t\tSet the maximum temperature of the flames. Default is 10.\n"
        << "\t\t\tA higher temp means taller flames.\n"
        << "\n"
        << "Press q at any time to douse the flames.\n\n";
}

int main(int argc, char** argv){
    srand(time(NULL));
    framerate = 1000 / 20;
    maxtemp = 10;
    dispch = '@';

    int c;
    opterr = 0;
    while((c = getopt(argc, argv, "c:hf:t:")) != -1){
        switch (c){
            case 'c':
                dispch = optarg[0];
                break;
            case 'h':
                printhelp(argv[0]);
                return 1;
            case 'f':
                if(atoi(optarg) < 1) framerate = 0;
                else framerate = 1000 / atoi(optarg);
                break;
            case 't':
                maxtemp = atoi(optarg);
                break;
            case '?':
                std::cout << "\nYou've really bunged this one up. Here, this may help:\n";
                printhelp(argv[0]);
                return 1;
            default:
                std::cerr << "What the hell?!";
                return 2;
        }
    }

    start_ncurses();
    flames();
    endwin();
    return 0;
}
<|endoftext|>


<commit_before>#include "mavlink_commands.h"
#include "mavlink_system.h"
#include <future>
#include <memory>

namespace dronecore {

// TODO: Currently the mavlink command handling is made in a way to only
//       process one command at any time. Therefore, the work state is global
//       for this whole class.
//       The limitation is made because:
//       - We are not sure what exactly will happen if the commands are sent in parallel
//         and what kind of edge cases we might run into.
//       - The timeout handler only supports the (void *)this cookie and therefore only
//         really supports one timeout per object. We could use (void *)this of the work
//         item but it also seems a bit dodgy.
//       - The queue used does not support going through and checking each and every
//         item yet.

MAVLinkCommands::MAVLinkCommands(MAVLinkSystem &parent) :
    _parent(parent)
{
    _parent.register_mavlink_message_handler(
        MAVLINK_MSG_ID_COMMAND_ACK,
        std::bind(&MAVLinkCommands::receive_command_ack,
                  this, std::placeholders::_1), this);
}

MAVLinkCommands::~MAVLinkCommands()
{
    _parent.unregister_all_mavlink_message_handlers(this);
}

MAVLinkCommands::Result
MAVLinkCommands::send_command(const MAVLinkCommands::CommandInt &command)
{
    struct PromiseResult {
        Result result;
        float progress;
    };

    // We wrap the async call with a promise and future.
    std::shared_ptr<std::promise<PromiseResult>> prom =
                                                  std::make_shared<std::promise<PromiseResult>>();

    queue_command_async(command,
    [prom](Result result, float progress) {
        PromiseResult promise_result {};
        promise_result.result = result;
        promise_result.progress = progress;
        prom->set_value(promise_result);
    });

    std::future<PromiseResult> res = prom->get_future();
    while (true) {
        // Block now to wait for result.
        res.wait();

        PromiseResult promise_result = res.get();

        if (promise_result.result == Result::IN_PROGRESS) {
            LogInfo() << "In progress: " << promise_result.progress;
            continue;
        }
        return promise_result.result;
    }
}

MAVLinkCommands::Result
MAVLinkCommands::send_command(const MAVLinkCommands::CommandLong &command)
{
    struct PromiseResult {
        Result result;
        float progress;
    };

    // We wrap the async call with a promise and future.
    std::shared_ptr<std::promise<PromiseResult>> prom =
                                                  std::make_shared<std::promise<PromiseResult>>();

    queue_command_async(command,
    [prom](Result result, float progress) {
        PromiseResult promise_result {};
        promise_result.result = result;
        promise_result.progress = progress;
        prom->set_value(promise_result);
    });

    std::future<PromiseResult> res = prom->get_future();
    while (true) {
        // Block now to wait for result.
        res.wait();

        PromiseResult promise_result = res.get();

        if (promise_result.result == Result::IN_PROGRESS) {
            LogInfo() << "In progress: " << promise_result.progress;
            continue;
        }
        return promise_result.result;
    }
}

void
MAVLinkCommands::queue_command_async(const CommandInt &command,
                                     command_result_callback_t callback)
{
    // LogDebug() << "Command " << (int)(command.command) << " to send to "
    //  << (int)(command.target_system_id)<< ", " << (int)(command.target_component_id;

    Work new_work {};
    mavlink_msg_command_int_pack(GCSClient::system_id,
                                 GCSClient::component_id,
                                 &new_work.mavlink_message,
                                 command.target_system_id,
                                 command.target_component_id,
                                 command.frame,
                                 command.command,
                                 command.current,
                                 command.autocontinue,
                                 command.params.param1,
                                 command.params.param2,
                                 command.params.param3,
                                 command.params.param4,
                                 command.params.x,
                                 command.params.y,
                                 command.params.z);

    new_work.callback = callback;
    new_work.mavlink_command = command.command;
    _work_queue.push_back(new_work);
}

void
MAVLinkCommands::queue_command_async(const CommandLong &command,
                                     command_result_callback_t callback)
{
    // LogDebug() << "Command " << (int)(command.command) << " to send to "
    //  << (int)(command.target_system_id)<< ", " << (int)(command.target_component_id;

    Work new_work {};
    mavlink_msg_command_long_pack(GCSClient::system_id,
                                  GCSClient::component_id,
                                  &new_work.mavlink_message,
                                  command.target_system_id,
                                  command.target_component_id,
                                  command.command,
                                  command.confirmation,
                                  command.params.param1,
                                  command.params.param2,
                                  command.params.param3,
                                  command.params.param4,
                                  command.params.param5,
                                  command.params.param6,
                                  command.params.param7);

    new_work.callback = callback;
    new_work.mavlink_command = command.command;
    _work_queue.push_back(new_work);
}

void MAVLinkCommands::receive_command_ack(mavlink_message_t message)
{
    // If nothing is in the queue, we ignore the message all together.
    if (_work_queue.size() == 0) {
        return;
    }

    Work &work = _work_queue.front();

    mavlink_command_ack_t command_ack;
    mavlink_msg_command_ack_decode(&message, &command_ack);

    // LogDebug() << "We got an ack: " << command_ack.command;

    if (work.mavlink_command != command_ack.command) {
        // If the command does not match with our current command, ignore it.
        LogWarn() << "Command ack not matching our current command: " << work.mavlink_command;
        return;
    }

    std::lock_guard<std::mutex> lock(_state_mutex);
    switch (command_ack.result) {
        case MAV_RESULT_ACCEPTED:
            _state = State::DONE;
            if (work.callback) {
                work.callback(Result::SUCCESS, 1.0f);
            }
            break;

        case MAV_RESULT_DENIED:
            LogWarn() << "command denied (" << work.mavlink_command << ").";
            _state = State::FAILED;
            if (work.callback) {
                work.callback(Result::COMMAND_DENIED, NAN);
            }
            break;

        case MAV_RESULT_UNSUPPORTED:
            LogWarn() << "command unsupported (" << work.mavlink_command << ").";
            _state = State::FAILED;
            if (work.callback) {
                work.callback(Result::COMMAND_DENIED, NAN);
            }
            break;

        case MAV_RESULT_TEMPORARILY_REJECTED:
            LogWarn() << "command temporarily rejected (" << work.mavlink_command << ").";
            _state = State::FAILED;
            if (work.callback) {
                work.callback(Result::COMMAND_DENIED, NAN);
            }
            break;

        case MAV_RESULT_FAILED:
            LogWarn() << "command failed (" << work.mavlink_command << ").";
            _state = State::FAILED;
            if (work.callback) {
                work.callback(Result::COMMAND_DENIED, NAN);
            }
            break;

        case MAV_RESULT_IN_PROGRESS:
            if (static_cast<int>(command_ack.progress) != 255) {
                LogInfo() << "progress: " << static_cast<int>(command_ack.progress)
                          << " % (" << work.mavlink_command << ").";
            }
            // FIXME: We can only call callbacks with promises once, so let's not do it
            //        on IN_PROGRESS.
            //if (work.callback) {
            //    work.callback(Result::IN_PROGRESS, command_ack.progress / 100.0f);
            //}
            _state = State::IN_PROGRESS;
            // If we get a progress update, we can raise the timeout
            // to something higher because we know the initial command
            // has arrived. A possible timeout for this case is the initial
            // timeout * the possible retries because this should match the
            // case where there is no progress update and we keep trying.
            _parent.unregister_timeout_handler(_timeout_cookie);
            _parent.register_timeout_handler(
                std::bind(&MAVLinkCommands::receive_timeout, this),
                work.retries_to_do * work.timeout_s, &_timeout_cookie);
            break;
    }
}

void MAVLinkCommands::receive_timeout()
{
    // If nothing is in the queue, we ignore the timeout.
    if (_work_queue.size() == 0) {
        return;
    }

    Work &work = _work_queue.front();

    std::lock_guard<std::mutex> lock(_state_mutex);

    if (_state == State::WAITING) {

        if (work.retries_to_do > 0) {

            LogInfo() << "sending again, retries to do: " << work.retries_to_do
                      << "  (" << work.mavlink_command << ").";
            // We're not sure the command arrived, let's retransmit.
            if (!_parent.send_message(work.mavlink_message)) {
                LogErr() << "connection send error in retransmit (" << work.mavlink_command << ").";
                if (work.callback) {
                    work.callback(Result::CONNECTION_ERROR, NAN);
                }
                _state = State::FAILED;
            } else {
                --work.retries_to_do;
                _parent.register_timeout_handler(
                    std::bind(&MAVLinkCommands::receive_timeout, this),
                    work.timeout_s, &_timeout_cookie);
            }

        } else  {
            // We have tried retransmitting, giving up now.
            LogErr() << "Retrying failed (" << work.mavlink_command << ")";

            if (work.callback) {
                if (_state == State::WAITING) {
                    work.callback(Result::TIMEOUT, NAN);
                }
            }
            _state = State::FAILED;
        }
    }
}

void MAVLinkCommands::do_work()
{
    std::lock_guard<std::mutex> lock(_state_mutex);

    // Clean up first
    switch (_state) {
        case State::NONE:
        // FALLTHROUGH
        case State::WAITING:
        // FALLTHROUGH
        case State::IN_PROGRESS:
            break;
        case State::DONE:
        // FALLTHROUGH
        case State::FAILED:
            _parent.unregister_timeout_handler(_timeout_cookie);
            _work_queue.pop_front();
            _state = State::NONE;
            break;
    }

    // Check if there is work to do.
    if (_work_queue.size() == 0) {
        // Nothing to do.
        return;
    }

    // If so, let's get the latest.
    Work &work = _work_queue.front();

    // If the work state is none, we can start the next command.
    switch (_state) {
        case State::NONE:
            // LogDebug() << "sending it the first time (" << work.mavlink_command << ")";
            if (!_parent.send_message(work.mavlink_message)) {
                LogErr() << "connection send error (" << work.mavlink_command << ")";
                if (work.callback) {
                    work.callback(Result::CONNECTION_ERROR, NAN);
                }
                _state = State::FAILED;
                break;
            } else {
                _state = State::WAITING;
                _parent.register_timeout_handler(
                    std::bind(&MAVLinkCommands::receive_timeout, this),
                    work.timeout_s, &_timeout_cookie);
            }
            break;
        case State::WAITING:
        case State::IN_PROGRESS:
            // LogWarn() << "wait until we can deal with this";
            break;
        case State::DONE:
        // FALLTHROUGH
        case State::FAILED:
            break;
    }
}

} // namespace dronecore
<commit_msg>core: mavlink command sync fixes<commit_after>#include "mavlink_commands.h"
#include "mavlink_system.h"
#include <future>
#include <memory>

namespace dronecore {

// TODO: Currently the mavlink command handling is made in a way to only
//       process one command at any time. Therefore, the work state is global
//       for this whole class.
//       The limitation is made because:
//       - We are not sure what exactly will happen if the commands are sent in parallel
//         and what kind of edge cases we might run into.
//       - The timeout handler only supports the (void *)this cookie and therefore only
//         really supports one timeout per object. We could use (void *)this of the work
//         item but it also seems a bit dodgy.
//       - The queue used does not support going through and checking each and every
//         item yet.

MAVLinkCommands::MAVLinkCommands(MAVLinkSystem &parent) :
    _parent(parent)
{
    _parent.register_mavlink_message_handler(
        MAVLINK_MSG_ID_COMMAND_ACK,
        std::bind(&MAVLinkCommands::receive_command_ack,
                  this, std::placeholders::_1), this);
}

MAVLinkCommands::~MAVLinkCommands()
{
    _parent.unregister_all_mavlink_message_handlers(this);
}

MAVLinkCommands::Result
MAVLinkCommands::send_command(const MAVLinkCommands::CommandInt &command)
{
    struct PromiseResult {
        Result result;
        float progress;
    };

    // We wrap the async call with a promise and future.
    std::shared_ptr<std::promise<PromiseResult>> prom =
                                                  std::make_shared<std::promise<PromiseResult>>();

    std::future<PromiseResult> res = prom->get_future();

    queue_command_async(command,
    [&prom](Result result, float progress) {
        PromiseResult promise_result {};
        promise_result.result = result;
        promise_result.progress = progress;
        prom->set_value(promise_result);
    });

    while (true) {
        // Block now to wait for result.
        PromiseResult promise_result = res.get();

        if (promise_result.result == Result::IN_PROGRESS) {
            LogInfo() << "In progress: " << promise_result.progress;
            continue;
        }
        return promise_result.result;
    }
}

MAVLinkCommands::Result
MAVLinkCommands::send_command(const MAVLinkCommands::CommandLong &command)
{
    struct PromiseResult {
        Result result;
        float progress;
    };

    // We wrap the async call with a promise and future.
    std::shared_ptr<std::promise<PromiseResult>> prom =
                                                  std::make_shared<std::promise<PromiseResult>>();

    std::future<PromiseResult> res = prom->get_future();

    queue_command_async(command,
    [&prom](Result result, float progress) {
        PromiseResult promise_result {};
        promise_result.result = result;
        promise_result.progress = progress;
        prom->set_value(promise_result);
    });

    while (true) {
        // Block now to wait for result.
        PromiseResult promise_result = res.get();

        if (promise_result.result == Result::IN_PROGRESS) {
            LogInfo() << "In progress: " << promise_result.progress;
            continue;
        }
        return promise_result.result;
    }
}

void
MAVLinkCommands::queue_command_async(const CommandInt &command,
                                     command_result_callback_t callback)
{
    // LogDebug() << "Command " << (int)(command.command) << " to send to "
    //  << (int)(command.target_system_id)<< ", " << (int)(command.target_component_id;

    Work new_work {};
    mavlink_msg_command_int_pack(GCSClient::system_id,
                                 GCSClient::component_id,
                                 &new_work.mavlink_message,
                                 command.target_system_id,
                                 command.target_component_id,
                                 command.frame,
                                 command.command,
                                 command.current,
                                 command.autocontinue,
                                 command.params.param1,
                                 command.params.param2,
                                 command.params.param3,
                                 command.params.param4,
                                 command.params.x,
                                 command.params.y,
                                 command.params.z);

    new_work.callback = callback;
    new_work.mavlink_command = command.command;
    _work_queue.push_back(new_work);
}

void
MAVLinkCommands::queue_command_async(const CommandLong &command,
                                     command_result_callback_t callback)
{
    // LogDebug() << "Command " << (int)(command.command) << " to send to "
    //  << (int)(command.target_system_id)<< ", " << (int)(command.target_component_id;

    Work new_work {};
    mavlink_msg_command_long_pack(GCSClient::system_id,
                                  GCSClient::component_id,
                                  &new_work.mavlink_message,
                                  command.target_system_id,
                                  command.target_component_id,
                                  command.command,
                                  command.confirmation,
                                  command.params.param1,
                                  command.params.param2,
                                  command.params.param3,
                                  command.params.param4,
                                  command.params.param5,
                                  command.params.param6,
                                  command.params.param7);

    new_work.callback = callback;
    new_work.mavlink_command = command.command;
    _work_queue.push_back(new_work);
}

void MAVLinkCommands::receive_command_ack(mavlink_message_t message)
{
    // If nothing is in the queue, we ignore the message all together.
    if (_work_queue.size() == 0) {
        return;
    }

    Work &work = _work_queue.front();

    mavlink_command_ack_t command_ack;
    mavlink_msg_command_ack_decode(&message, &command_ack);

    // LogDebug() << "We got an ack: " << command_ack.command;

    if (work.mavlink_command != command_ack.command) {
        // If the command does not match with our current command, ignore it.
        LogWarn() << "Command ack not matching our current command: " << work.mavlink_command;
        return;
    }

    std::lock_guard<std::mutex> lock(_state_mutex);
    switch (command_ack.result) {
        case MAV_RESULT_ACCEPTED:
            _state = State::DONE;
            if (work.callback) {
                work.callback(Result::SUCCESS, 1.0f);
            }
            _work_queue.pop_front();
            break;

        case MAV_RESULT_DENIED:
            LogWarn() << "command denied (" << work.mavlink_command << ").";
            _state = State::FAILED;
            if (work.callback) {
                work.callback(Result::COMMAND_DENIED, NAN);
            }
            _work_queue.pop_front();
            break;

        case MAV_RESULT_UNSUPPORTED:
            LogWarn() << "command unsupported (" << work.mavlink_command << ").";
            _state = State::FAILED;
            if (work.callback) {
                work.callback(Result::COMMAND_DENIED, NAN);
            }
            _work_queue.pop_front();
            break;

        case MAV_RESULT_TEMPORARILY_REJECTED:
            LogWarn() << "command temporarily rejected (" << work.mavlink_command << ").";
            _state = State::FAILED;
            if (work.callback) {
                work.callback(Result::COMMAND_DENIED, NAN);
            }
            _work_queue.pop_front();
            break;

        case MAV_RESULT_FAILED:
            LogWarn() << "command failed (" << work.mavlink_command << ").";
            _state = State::FAILED;
            if (work.callback) {
                work.callback(Result::COMMAND_DENIED, NAN);
            }
            _work_queue.pop_front();
            break;

        case MAV_RESULT_IN_PROGRESS:
            if (static_cast<int>(command_ack.progress) != 255) {
                LogInfo() << "progress: " << static_cast<int>(command_ack.progress)
                          << " % (" << work.mavlink_command << ").";
            }
            // FIXME: We can only call callbacks with promises once, so let's not do it
            //        on IN_PROGRESS.
            //if (work.callback) {
            //    work.callback(Result::IN_PROGRESS, command_ack.progress / 100.0f);
            //}
            _state = State::IN_PROGRESS;
            // If we get a progress update, we can raise the timeout
            // to something higher because we know the initial command
            // has arrived. A possible timeout for this case is the initial
            // timeout * the possible retries because this should match the
            // case where there is no progress update and we keep trying.
            _parent.unregister_timeout_handler(_timeout_cookie);
            _parent.register_timeout_handler(
                std::bind(&MAVLinkCommands::receive_timeout, this),
                work.retries_to_do * work.timeout_s, &_timeout_cookie);
            break;
    }
}

void MAVLinkCommands::receive_timeout()
{
    // If nothing is in the queue, we ignore the timeout.
    if (_work_queue.size() == 0) {
        return;
    }

    Work &work = _work_queue.front();

    std::lock_guard<std::mutex> lock(_state_mutex);

    if (_state == State::WAITING) {

        if (work.retries_to_do > 0) {

            LogInfo() << "sending again, retries to do: " << work.retries_to_do
                      << "  (" << work.mavlink_command << ").";
            // We're not sure the command arrived, let's retransmit.
            if (!_parent.send_message(work.mavlink_message)) {
                LogErr() << "connection send error in retransmit (" << work.mavlink_command << ").";
                if (work.callback) {
                    work.callback(Result::CONNECTION_ERROR, NAN);
                }
                _state = State::FAILED;
            } else {
                --work.retries_to_do;
                _parent.register_timeout_handler(
                    std::bind(&MAVLinkCommands::receive_timeout, this),
                    work.timeout_s, &_timeout_cookie);
            }

        } else  {
            // We have tried retransmitting, giving up now.
            LogErr() << "Retrying failed (" << work.mavlink_command << ")";

            if (work.callback) {
                if (_state == State::WAITING) {
                    work.callback(Result::TIMEOUT, NAN);
                }
            }
            _state = State::FAILED;
            _work_queue.pop_front();
        }
    }
}

void MAVLinkCommands::do_work()
{
    std::lock_guard<std::mutex> lock(_state_mutex);

    // Clean up first
    switch (_state) {
        case State::NONE:
        // FALLTHROUGH
        case State::WAITING:
        // FALLTHROUGH
        case State::IN_PROGRESS:
            break;
        case State::DONE:
        // FALLTHROUGH
        case State::FAILED:
            _parent.unregister_timeout_handler(_timeout_cookie);
            _state = State::NONE;
            break;
    }

    // Check if there is work to do.
    if (_work_queue.size() == 0) {
        // Nothing to do.
        return;
    }

    // If so, let's get the latest.
    Work &work = _work_queue.front();

    // If the work state is none, we can start the next command.
    switch (_state) {
        case State::NONE:
            // LogDebug() << "sending it the first time (" << work.mavlink_command << ")";
            if (!_parent.send_message(work.mavlink_message)) {
                LogErr() << "connection send error (" << work.mavlink_command << ")";
                if (work.callback) {
                    work.callback(Result::CONNECTION_ERROR, NAN);
                }
                _state = State::FAILED;
                break;
            } else {
                _state = State::WAITING;
                _parent.register_timeout_handler(
                    std::bind(&MAVLinkCommands::receive_timeout, this),
                    work.timeout_s, &_timeout_cookie);
            }
            break;
        case State::WAITING:
        case State::IN_PROGRESS:
            // LogWarn() << "wait until we can deal with this";
            break;
        case State::DONE:
        // FALLTHROUGH
        case State::FAILED:
            _work_queue.pop_front();
            _state = State::NONE;
            break;
    }
}

} // namespace dronecore
<|endoftext|>


<commit_before>#include "helix/order_book.hh"

#include <boost/version.hpp>
#include <stdexcept>
#include <limits>

namespace helix {

execution::execution(uint64_t price, side_type side, uint64_t remaining)
    : price{price}
    , side{side}
    , remaining{remaining}
{
}

order_book::order_book(std::string symbol, uint64_t timestamp, size_t max_orders)
    : _symbol{std::move(symbol)}
    , _timestamp{timestamp}
    , _state{trading_state::unknown}
{
#if BOOST_VERSION >= 105600
    _orders.reserve(max_orders);
#endif
}

void order_book::add(order order)
{
    switch (order.side) {
    case side_type::buy: {
        auto&& level = lookup_or_create(_bids, order.price);
        order.level = &level;
        level.size += order.quantity;
        break;
    }
    case side_type::sell: {
        auto&& level = lookup_or_create(_asks, order.price);
        order.level = &level;
        level.size += order.quantity;
        break;
    }
    default:
        throw std::invalid_argument(std::string("invalid side: ") + static_cast<char>(order.side));
    }
    _orders.emplace(std::move(order));
}

void order_book::replace(uint64_t order_id, order order)
{
    remove(order_id);
    add(std::move(order));
}

void order_book::cancel(uint64_t order_id, uint64_t quantity)
{
    auto it = _orders.find(order_id);
    if (it == _orders.end()) {
        throw std::invalid_argument(std::string("invalid order id: ") + std::to_string(order_id));
    }
    _orders.modify(it, [quantity](order& order) {
        order.quantity -= quantity;
        order.level->size -= quantity;
    });
    if (!it->quantity) {
        remove(it);
    }
}

execution order_book::execute(uint64_t order_id, uint64_t quantity)
{
    auto it = _orders.find(order_id);
    if (it == _orders.end()) {
        throw std::invalid_argument(std::string("invalid order id: ") + std::to_string(order_id));
    }
    _orders.modify(it, [quantity](order& order) {
        order.quantity -= quantity;
        order.level->size -= quantity;
    });
    auto result = execution(it->price, it->side, it->level->size);
    if (!it->quantity) {
        remove(it);
    }
    return result;
}

void order_book::remove(uint64_t order_id)
{
    auto it = _orders.find(order_id);
    if (it == _orders.end()) {
        throw std::invalid_argument(std::string("invalid order id: ") + std::to_string(order_id));
    }
    remove(it);
}

void order_book::remove(iterator& iter)
{
    auto && order = *iter;
    switch (order.side) {
    case side_type::buy: {
        remove(order, _bids);
        break;
    }
    case side_type::sell: {
        remove(order, _asks);
        break;
    }
    default:
        throw std::invalid_argument(std::string("invalid side: ") + static_cast<char>(order.side));
    }
    _orders.erase(iter);
}

template<typename T>
void order_book::remove(const order& o, T& levels)
{
    auto it = levels.find(o.price);
    if (it == levels.end()) {
        throw std::invalid_argument(std::string("invalid price: ") + std::to_string(o.price));
    }
    auto&& level = it->second;
    o.level->size -= o.quantity;
    if (level.size == 0) {
        levels.erase(it);
    }
}

template<typename T>
price_level& order_book::lookup_or_create(T& levels, uint64_t price)
{
    auto it = levels.find(price);
    if (it != levels.end()) {
        return it->second;
    }
    price_level level{price};
    levels.emplace(price, level);
    it = levels.find(price);
    return it->second;
}

side_type order_book::side(uint64_t order_id) const
{
    auto it = _orders.find(order_id);
    if (it == _orders.end()) {
        throw std::invalid_argument(std::string("invalid order id: ") + std::to_string(order_id));
    }
    return it->side;
}

size_t order_book::bid_levels() const
{
    return _bids.size();
}

size_t order_book::ask_levels() const
{
    return _asks.size();
}

size_t order_book::order_count() const
{
    return _orders.size();
}

uint64_t order_book::bid_price(size_t level) const
{
    auto it = _bids.begin();
    while (it != _bids.end() && level--) {
        it++;
    }
    if (it != _bids.end()) {
        auto&& level = it->second;
        return level.price;
    }
    return std::numeric_limits<uint64_t>::min();
}

uint64_t order_book::bid_size(size_t level) const
{
    auto it = _bids.begin();
    while (it != _bids.end() && level--) {
        it++;
    }
    if (it != _bids.end()) {
        auto&& level = it->second;
        return level.size;
    }
    return 0;
}

uint64_t order_book::ask_price(size_t level) const
{
    auto it = _asks.begin();
    while (it != _asks.end() && level--) {
        it++;
    }
    if (it != _asks.end()) {
        auto&& level = it->second;
        return level.price;
    }
    return std::numeric_limits<uint64_t>::max();
}

uint64_t order_book::ask_size(size_t level) const
{
    auto it = _asks.begin();
    while (it != _asks.end() && level--) {
        it++;
    }
    if (it != _asks.end()) {
        auto&& level = it->second;
        return level.size;
    }
    return 0;
}

uint64_t order_book::midprice(size_t level) const
{
    auto bid = bid_price(level);
    auto ask = ask_price(level);
    return (bid + ask) / 2;
}

}
<commit_msg>order_book.cc: removed redundant calls to find() from lookup_or_create<commit_after>#include "helix/order_book.hh"

#include <boost/version.hpp>
#include <stdexcept>
#include <limits>

namespace helix {

execution::execution(uint64_t price, side_type side, uint64_t remaining)
    : price{price}
    , side{side}
    , remaining{remaining}
{
}

order_book::order_book(std::string symbol, uint64_t timestamp, size_t max_orders)
    : _symbol{std::move(symbol)}
    , _timestamp{timestamp}
    , _state{trading_state::unknown}
{
#if BOOST_VERSION >= 105600
    _orders.reserve(max_orders);
#endif
}

void order_book::add(order order)
{
    switch (order.side) {
    case side_type::buy: {
        auto&& level = lookup_or_create(_bids, order.price);
        order.level = &level;
        level.size += order.quantity;
        break;
    }
    case side_type::sell: {
        auto&& level = lookup_or_create(_asks, order.price);
        order.level = &level;
        level.size += order.quantity;
        break;
    }
    default:
        throw std::invalid_argument(std::string("invalid side: ") + static_cast<char>(order.side));
    }
    _orders.emplace(std::move(order));
}

void order_book::replace(uint64_t order_id, order order)
{
    remove(order_id);
    add(std::move(order));
}

void order_book::cancel(uint64_t order_id, uint64_t quantity)
{
    auto it = _orders.find(order_id);
    if (it == _orders.end()) {
        throw std::invalid_argument(std::string("invalid order id: ") + std::to_string(order_id));
    }
    _orders.modify(it, [quantity](order& order) {
        order.quantity -= quantity;
        order.level->size -= quantity;
    });
    if (!it->quantity) {
        remove(it);
    }
}

execution order_book::execute(uint64_t order_id, uint64_t quantity)
{
    auto it = _orders.find(order_id);
    if (it == _orders.end()) {
        throw std::invalid_argument(std::string("invalid order id: ") + std::to_string(order_id));
    }
    _orders.modify(it, [quantity](order& order) {
        order.quantity -= quantity;
        order.level->size -= quantity;
    });
    auto result = execution(it->price, it->side, it->level->size);
    if (!it->quantity) {
        remove(it);
    }
    return result;
}

void order_book::remove(uint64_t order_id)
{
    auto it = _orders.find(order_id);
    if (it == _orders.end()) {
        throw std::invalid_argument(std::string("invalid order id: ") + std::to_string(order_id));
    }
    remove(it);
}

void order_book::remove(iterator& iter)
{
    auto && order = *iter;
    switch (order.side) {
    case side_type::buy: {
        remove(order, _bids);
        break;
    }
    case side_type::sell: {
        remove(order, _asks);
        break;
    }
    default:
        throw std::invalid_argument(std::string("invalid side: ") + static_cast<char>(order.side));
    }
    _orders.erase(iter);
}

template<typename T>
void order_book::remove(const order& o, T& levels)
{
    auto it = levels.find(o.price);
    if (it == levels.end()) {
        throw std::invalid_argument(std::string("invalid price: ") + std::to_string(o.price));
    }
    auto&& level = it->second;
    o.level->size -= o.quantity;
    if (level.size == 0) {
        levels.erase(it);
    }
}

template<typename T>
price_level& order_book::lookup_or_create(T& levels, uint64_t price)
{
    price_level level{price};
    auto it = levels.emplace(price, level).first;

    return it->second;
}

side_type order_book::side(uint64_t order_id) const
{
    auto it = _orders.find(order_id);
    if (it == _orders.end()) {
        throw std::invalid_argument(std::string("invalid order id: ") + std::to_string(order_id));
    }
    return it->side;
}

size_t order_book::bid_levels() const
{
    return _bids.size();
}

size_t order_book::ask_levels() const
{
    return _asks.size();
}

size_t order_book::order_count() const
{
    return _orders.size();
}

uint64_t order_book::bid_price(size_t level) const
{
    auto it = _bids.begin();
    while (it != _bids.end() && level--) {
        it++;
    }
    if (it != _bids.end()) {
        auto&& level = it->second;
        return level.price;
    }
    return std::numeric_limits<uint64_t>::min();
}

uint64_t order_book::bid_size(size_t level) const
{
    auto it = _bids.begin();
    while (it != _bids.end() && level--) {
        it++;
    }
    if (it != _bids.end()) {
        auto&& level = it->second;
        return level.size;
    }
    return 0;
}

uint64_t order_book::ask_price(size_t level) const
{
    auto it = _asks.begin();
    while (it != _asks.end() && level--) {
        it++;
    }
    if (it != _asks.end()) {
        auto&& level = it->second;
        return level.price;
    }
    return std::numeric_limits<uint64_t>::max();
}

uint64_t order_book::ask_size(size_t level) const
{
    auto it = _asks.begin();
    while (it != _asks.end() && level--) {
        it++;
    }
    if (it != _asks.end()) {
        auto&& level = it->second;
        return level.size;
    }
    return 0;
}

uint64_t order_book::midprice(size_t level) const
{
    auto bid = bid_price(level);
    auto ask = ask_price(level);
    return (bid + ask) / 2;
}

}
<|endoftext|>


<commit_before>#include "geoJson.h"

#include "platform.h"
#include "data/propertyItem.h"
#include "glm/glm.hpp"

namespace Tangram {

bool GeoJson::isFeatureCollection(const JsonValue& _in) {

    // A FeatureCollection must have a "type" of "FeatureCollection"
    // and a member named "features" that is an array.
    // http://geojson.org/geojson-spec.html#feature-collection-objects

    auto type = _in.FindMember("type");
    if (type == _in.MemberEnd() || !type->value.IsString() ||
        std::strcmp(type->value.GetString(), "FeatureCollection") != 0) {
        return false;
    }

    auto features = _in.FindMember("features");
    if (features == _in.MemberEnd() || !features->value.IsArray()) {
        return false;
    }

    return true;

}

Point GeoJson::getPoint(const JsonValue& _in, const Transform& _proj) {
    return _proj(glm::dvec2(_in[0].GetDouble(), _in[1].GetDouble()));
}

Line GeoJson::getLine(const JsonValue& _in, const Transform& _proj) {

    Line line;
    for (auto itr = _in.Begin(); itr != _in.End(); ++itr) {
        line.push_back(getPoint(*itr, _proj));
    }
    return line;

}

Polygon GeoJson::getPolygon(const JsonValue& _in, const Transform& _proj) {

    Polygon polygon;
    for (auto itr = _in.Begin(); itr != _in.End(); ++itr) {
        polygon.push_back(getLine(*itr, _proj));
    }
    return polygon;

}

Properties GeoJson::getProperties(const JsonValue& _in, int32_t _sourceId) {

    std::vector<PropertyItem> items;
    items.reserve(_in.MemberCount());

    for (auto it = _in.MemberBegin(); it != _in.MemberEnd(); ++it) {

        const auto& name = it->name.GetString();
        const auto& value = it->value;
        if (value.IsNumber()) {
            items.emplace_back(name, value.GetDouble());
        } else if (it->value.IsString()) {
            items.emplace_back(name, value.GetString());
        }

    }

    Properties properties;
    properties.sourceId = _sourceId;
    properties.setSorted(std::move(items));
    properties.sort();

    return properties;

}

Feature GeoJson::getFeature(const JsonValue& _in, const Transform& _proj, int32_t _sourceId) {

    Feature feature;

    // Copy properties into tile data
    auto properties = _in.FindMember("properties");
    if (properties != _in.MemberEnd()) {
        feature.props = std::move(getProperties(properties->value, _sourceId));
    }

    // Copy geometry into tile data
    const JsonValue& geometry = _in["geometry"];
    const JsonValue& coords = geometry["coordinates"];
    const std::string& geometryType = geometry["type"].GetString();

    if (geometryType.compare("Point") == 0) {

        feature.geometryType = GeometryType::points;
        feature.points.push_back(getPoint(coords, _proj));

    } else if (geometryType.compare("MultiPoint") == 0) {

        feature.geometryType = GeometryType::points;
        for (auto pointCoords = coords.Begin(); pointCoords != coords.End(); ++pointCoords) {
            feature.points.push_back(getPoint(*pointCoords, _proj));
        }

    } else if (geometryType.compare("LineString") == 0) {

        feature.geometryType = GeometryType::lines;
        feature.lines.push_back(getLine(coords, _proj));

    } else if (geometryType.compare("MultiLineString") == 0) {

        feature.geometryType = GeometryType::lines;
        for (auto lineCoords = coords.Begin(); lineCoords != coords.End(); ++lineCoords) {
            feature.lines.push_back(getLine(*lineCoords, _proj));
        }

    } else if (geometryType.compare("Polygon") == 0) {

        feature.geometryType = GeometryType::polygons;
        feature.polygons.push_back(getPolygon(coords, _proj));

    } else if (geometryType.compare("MultiPolygon") == 0) {

        feature.geometryType = GeometryType::polygons;
        for (auto polyCoords = coords.Begin(); polyCoords != coords.End(); ++polyCoords) {
            feature.polygons.push_back(getPolygon(*polyCoords, _proj));
        }

    }

    return feature;

}

Layer GeoJson::getLayer(const JsonValue& _in, const Transform& _proj, int32_t _sourceId) {

    Layer layer("");

    auto features = _in.FindMember("features");

    if (features == _in.MemberEnd()) {
        LOGE("GeoJSON missing 'features' member");
        return layer;
    }

    for (auto featureIt = features->value.Begin(); featureIt != features->value.End(); ++featureIt) {
        layer.features.push_back(getFeature(*featureIt, _proj, _sourceId));
    }

    return layer;

}

}
<commit_msg>Remove std::move to allow copy elision<commit_after>#include "geoJson.h"

#include "platform.h"
#include "data/propertyItem.h"
#include "glm/glm.hpp"

namespace Tangram {

bool GeoJson::isFeatureCollection(const JsonValue& _in) {

    // A FeatureCollection must have a "type" of "FeatureCollection"
    // and a member named "features" that is an array.
    // http://geojson.org/geojson-spec.html#feature-collection-objects

    auto type = _in.FindMember("type");
    if (type == _in.MemberEnd() || !type->value.IsString() ||
        std::strcmp(type->value.GetString(), "FeatureCollection") != 0) {
        return false;
    }

    auto features = _in.FindMember("features");
    if (features == _in.MemberEnd() || !features->value.IsArray()) {
        return false;
    }

    return true;

}

Point GeoJson::getPoint(const JsonValue& _in, const Transform& _proj) {
    return _proj(glm::dvec2(_in[0].GetDouble(), _in[1].GetDouble()));
}

Line GeoJson::getLine(const JsonValue& _in, const Transform& _proj) {

    Line line;
    for (auto itr = _in.Begin(); itr != _in.End(); ++itr) {
        line.push_back(getPoint(*itr, _proj));
    }
    return line;

}

Polygon GeoJson::getPolygon(const JsonValue& _in, const Transform& _proj) {

    Polygon polygon;
    for (auto itr = _in.Begin(); itr != _in.End(); ++itr) {
        polygon.push_back(getLine(*itr, _proj));
    }
    return polygon;

}

Properties GeoJson::getProperties(const JsonValue& _in, int32_t _sourceId) {

    std::vector<PropertyItem> items;
    items.reserve(_in.MemberCount());

    for (auto it = _in.MemberBegin(); it != _in.MemberEnd(); ++it) {

        const auto& name = it->name.GetString();
        const auto& value = it->value;
        if (value.IsNumber()) {
            items.emplace_back(name, value.GetDouble());
        } else if (it->value.IsString()) {
            items.emplace_back(name, value.GetString());
        }

    }

    Properties properties;
    properties.sourceId = _sourceId;
    properties.setSorted(std::move(items));
    properties.sort();

    return properties;

}

Feature GeoJson::getFeature(const JsonValue& _in, const Transform& _proj, int32_t _sourceId) {

    Feature feature;

    // Copy properties into tile data
    auto properties = _in.FindMember("properties");
    if (properties != _in.MemberEnd()) {
        feature.props = getProperties(properties->value, _sourceId);
    }

    // Copy geometry into tile data
    const JsonValue& geometry = _in["geometry"];
    const JsonValue& coords = geometry["coordinates"];
    const std::string& geometryType = geometry["type"].GetString();

    if (geometryType.compare("Point") == 0) {

        feature.geometryType = GeometryType::points;
        feature.points.push_back(getPoint(coords, _proj));

    } else if (geometryType.compare("MultiPoint") == 0) {

        feature.geometryType = GeometryType::points;
        for (auto pointCoords = coords.Begin(); pointCoords != coords.End(); ++pointCoords) {
            feature.points.push_back(getPoint(*pointCoords, _proj));
        }

    } else if (geometryType.compare("LineString") == 0) {

        feature.geometryType = GeometryType::lines;
        feature.lines.push_back(getLine(coords, _proj));

    } else if (geometryType.compare("MultiLineString") == 0) {

        feature.geometryType = GeometryType::lines;
        for (auto lineCoords = coords.Begin(); lineCoords != coords.End(); ++lineCoords) {
            feature.lines.push_back(getLine(*lineCoords, _proj));
        }

    } else if (geometryType.compare("Polygon") == 0) {

        feature.geometryType = GeometryType::polygons;
        feature.polygons.push_back(getPolygon(coords, _proj));

    } else if (geometryType.compare("MultiPolygon") == 0) {

        feature.geometryType = GeometryType::polygons;
        for (auto polyCoords = coords.Begin(); polyCoords != coords.End(); ++polyCoords) {
            feature.polygons.push_back(getPolygon(*polyCoords, _proj));
        }

    }

    return feature;

}

Layer GeoJson::getLayer(const JsonValue& _in, const Transform& _proj, int32_t _sourceId) {

    Layer layer("");

    auto features = _in.FindMember("features");

    if (features == _in.MemberEnd()) {
        LOGE("GeoJSON missing 'features' member");
        return layer;
    }

    for (auto featureIt = features->value.Begin(); featureIt != features->value.End(); ++featureIt) {
        layer.features.push_back(getFeature(*featureIt, _proj, _sourceId));
    }

    return layer;

}

}
<|endoftext|>


<commit_before>// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
// vim: ts=8 sw=2 smarttab
/*
 * Ceph - scalable distributed file system
 *
 * Copyright (C) 2004-2006 Sage Weil <sage@newdream.net>
 *
 * This is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License version 2.1, as published by the Free Software
 * Foundation.  See file COPYING.
 *
 */

#include "Filer.h"
#include "osd/OSDMap.h"

#include "messages/MOSDOp.h"
#include "messages/MOSDOpReply.h"
#include "messages/MOSDMap.h"

#include "msg/Messenger.h"

#include "include/Context.h"

#include "config.h"

#define DOUT_SUBSYS filer
#undef dout_prefix
#define dout_prefix *_dout << objecter->messenger->get_myname() << ".filer "

class Filer::C_Probe : public Context {
public:
  Filer *filer;
  Probe *probe;
  object_t oid;
  uint64_t size;
  utime_t mtime;
  C_Probe(Filer *f, Probe *p, object_t o) : filer(f), probe(p), oid(o), size(0) {}
  void finish(int r) {
    filer->_probed(probe, oid, size, mtime);
  }
};

int Filer::probe(inodeno_t ino,
		 ceph_file_layout *layout,
		 snapid_t snapid,
		 uint64_t start_from,
		 uint64_t *end,           // LB, when !fwd
		 utime_t *pmtime,
		 bool fwd,
		 int flags,
		 Context *onfinish)
{
  dout(10) << "probe " << (fwd ? "fwd ":"bwd ")
	   << hex << ino << dec
	   << " starting from " << start_from
	   << dendl;

  assert(snapid);  // (until there is a non-NOSNAP write)

  Probe *probe = new Probe(ino, *layout, snapid, start_from, end, pmtime, flags, fwd, onfinish);

  // period (bytes before we jump unto a new set of object(s))
  uint64_t period = layout->fl_stripe_count * layout->fl_object_size;

  // start with 1+ periods.
  probe->probing_len = period;
  if (probe->fwd) {
    if (start_from % period)
      probe->probing_len += period - (start_from % period);
  } else {
    assert(start_from > *end);
    if (start_from % period)
      probe->probing_len -= period - (start_from % period);
    probe->probing_off -= probe->probing_len;
  }

  _probe(probe);
  return 0;
}

void Filer::_probe(Probe *probe)
{
  dout(10) << "_probe " << hex << probe->ino << dec
	   << " " << probe->probing_off << "~" << probe->probing_len
	   << dendl;

  // map range onto objects
  probe->known_size.clear();
  probe->probing.clear();
  file_to_extents(probe->ino, &probe->layout,
		  probe->probing_off, probe->probing_len, probe->probing);

  for (vector<ObjectExtent>::iterator p = probe->probing.begin();
       p != probe->probing.end();
       p++) {
    dout(10) << "_probe  probing " << p->oid << dendl;
    C_Probe *c = new C_Probe(this, probe, p->oid);
    probe->ops[p->oid] = objecter->stat(p->oid, p->oloc, probe->snapid, &c->size, &c->mtime, probe->flags, c);
  }
}

void Filer::_probed(Probe *probe, const object_t& oid, uint64_t size, utime_t mtime)
{
  dout(10) << "_probed " << probe->ino << " object " << oid
	   << " has size " << size << " mtime " << mtime << dendl;

  probe->known_size[oid] = size;
  if (mtime > probe->max_mtime)
    probe->max_mtime = mtime;

  assert(probe->ops.count(oid));
  probe->ops.erase(oid);

  if (!probe->ops.empty())
    return;  // waiting for more!

  // analyze!
  uint64_t end = 0;

  if (!probe->fwd) {
    // reverse
    vector<ObjectExtent> r;
    for (vector<ObjectExtent>::reverse_iterator p = probe->probing.rbegin();
	 p != probe->probing.rend();
	 p++)
      r.push_back(*p);
    probe->probing.swap(r);
  }

  for (vector<ObjectExtent>::iterator p = probe->probing.begin();
       p != probe->probing.end();
       p++) {
    uint64_t shouldbe = p->length + p->offset;
    dout(10) << "_probed  " << probe->ino << " object " << hex << p->oid << dec
	     << " should be " << shouldbe
	     << ", actual is " << probe->known_size[p->oid]
	     << dendl;

    // error?
    if (probe->known_size[p->oid] < 0) {
      probe->err = probe->known_size[p->oid];
      break;
    }

    if (!probe->found_size) {
      assert(probe->known_size[p->oid] <= shouldbe);

      if ((probe->fwd && probe->known_size[p->oid] == shouldbe) ||
	  (!probe->fwd && probe->known_size[p->oid] == 0 && probe->probing_off > 0))
	continue;  // keep going

      // aha, we found the end!
      // calc offset into buffer_extent to get distance from probe->from.
      uint64_t oleft = probe->known_size[p->oid] - p->offset;
      for (map<__u32,__u32>::iterator i = p->buffer_extents.begin();
	   i != p->buffer_extents.end();
	   i++) {
	if (oleft <= (uint64_t)i->second) {
	  end = probe->probing_off + i->first + oleft;
	  dout(10) << "_probed  end is in buffer_extent " << i->first << "~" << i->second << " off " << oleft
		   << ", from was " << probe->probing_off << ", end is " << end
		   << dendl;

	  probe->found_size = true;
	  dout(10) << "_probed found size at " << end << dendl;
	  *probe->psize = end;

	  if (!probe->pmtime)  // stop if we don't need mtime too
	    break;
	}
	oleft -= i->second;
      }
    }
    break;
  }

  if (!probe->found_size || (probe->probing_off && probe->pmtime)) {
    // keep probing!
    dout(10) << "_probed probing further" << dendl;

    uint64_t period = probe->layout.fl_stripe_count * probe->layout.fl_object_size;
    if (probe->fwd) {
      probe->probing_off += probe->probing_len;
      assert(probe->probing_off % period == 0);
      probe->probing_len = period;
    } else {
      // previous period.
      assert(probe->probing_off % period == 0);
      probe->probing_len = period;
      probe->probing_off -= period;
    }
    _probe(probe);
    return;
  }

  if (probe->pmtime) {
    dout(10) << "_probed found mtime " << probe->max_mtime << dendl;
    *probe->pmtime = probe->max_mtime;
  }

  // done!  finish and clean up.
  probe->onfinish->finish(probe->err);
  delete probe->onfinish;
  delete probe;
}

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

struct PurgeRange {
  inodeno_t ino;
  ceph_file_layout layout;
  SnapContext snapc;
  uint64_t first, num;
  utime_t mtime;
  int flags;
  Context *oncommit;
  int uncommitted;
};

int Filer::purge_range(inodeno_t ino,
		       ceph_file_layout *layout,
		       const SnapContext& snapc,
		       uint64_t first_obj, uint64_t num_obj,
		       utime_t mtime,
		       int flags,
		       Context *oncommit)
{
  assert(num_obj > 0);

  // single object?  easy!
  if (num_obj == 1) {
    object_t oid = file_object_t(ino, first_obj);
    object_locator_t oloc = objecter->osdmap->file_to_object_locator(*layout);
    objecter->remove(oid, oloc, snapc, mtime, flags, NULL, oncommit);
    return 0;
  }

  // lots!  let's do this in pieces.
  PurgeRange *pr = new PurgeRange;
  pr->ino = ino;
  pr->layout = *layout;
  pr->snapc = snapc;
  pr->first = first_obj;
  pr->num = num_obj;
  pr->mtime = mtime;
  pr->flags = flags;
  pr->oncommit = oncommit;
  pr->uncommitted = 0;

  _do_purge_range(pr, 0);
  return 0;
}

struct C_PurgeRange : public Context {
  Filer *filer;
  PurgeRange *pr;
  C_PurgeRange(Filer *f, PurgeRange *p) : filer(f), pr(p) {}
  void finish(int r) {
    filer->_do_purge_range(pr, 1);
  }
};

void Filer::_do_purge_range(PurgeRange *pr, int fin)
{
  pr->uncommitted -= fin;
  dout(10) << "_do_purge_range " << pr->ino << " objects " << pr->first << "~" << pr->num
	   << " uncommitted " << pr->uncommitted << dendl;

  if (pr->num == 0 && pr->uncommitted == 0) {
    pr->oncommit->finish(0);
    delete pr->oncommit;
    delete pr;
    return;
  }

  int max = 10 - pr->uncommitted;
  while (pr->num > 0 && max > 0) {
    object_t oid = file_object_t(pr->ino, pr->first);
    object_locator_t oloc = objecter->osdmap->file_to_object_locator(pr->layout);
    objecter->remove(oid, oloc, pr->snapc, pr->mtime, pr->flags,
		     NULL, new C_PurgeRange(this, pr));
    pr->uncommitted++;
    pr->first++;
    pr->num--;
    max--;
  }
}

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

void Filer::file_to_extents(inodeno_t ino, ceph_file_layout *layout,
                            uint64_t offset, uint64_t len,
                            vector<ObjectExtent>& extents)
{
  dout(10) << "file_to_extents " << offset << "~" << len
           << " on " << hex << ino << dec
           << dendl;
  assert(len > 0);

  /* we want only one extent per object!
   * this means that each extent we read may map into different bits of the
   * final read buffer.. hence OSDExtent.buffer_extents
   */
  map< object_t, ObjectExtent > object_extents;

  __u32 object_size = layout->fl_object_size;
  __u32 su = layout->fl_stripe_unit;
  __u32 stripe_count = layout->fl_stripe_count;
  assert(object_size >= su);
  uint64_t stripes_per_object = object_size / su;
  dout(20) << " stripes_per_object " << stripes_per_object << dendl;

  uint64_t cur = offset;
  uint64_t left = len;
  while (left > 0) {
    // layout into objects
    uint64_t blockno = cur / su;          // which block
    uint64_t stripeno = blockno / stripe_count;    // which horizontal stripe        (Y)
    uint64_t stripepos = blockno % stripe_count;   // which object in the object set (X)
    uint64_t objectsetno = stripeno / stripes_per_object;       // which object set
    uint64_t objectno = objectsetno * stripe_count + stripepos;  // object id

    // find oid, extent
    ObjectExtent *ex = 0;
    object_t oid = file_object_t(ino, objectno);
    if (object_extents.count(oid))
      ex = &object_extents[oid];
    else {
      ex = &object_extents[oid];
      ex->oid = oid;
      ex->oloc = objecter->osdmap->file_to_object_locator(*layout);
    }

    // map range into object
    uint64_t block_start = (stripeno % stripes_per_object)*su;
    uint64_t block_off = cur % su;
    uint64_t max = su - block_off;

    uint64_t x_offset = block_start + block_off;
    uint64_t x_len;
    if (left > max)
      x_len = max;
    else
      x_len = left;

    if (ex->offset + (uint64_t)ex->length == x_offset) {
      // add to extent
      ex->length += x_len;
    } else {
      // new extent
      assert(ex->length == 0);
      assert(ex->offset == 0);
      ex->offset = x_offset;
      ex->length = x_len;
    }
    ex->buffer_extents[cur-offset] = x_len;

    dout(15) << "file_to_extents  " << *ex << " in " << ex->oloc << dendl;
    //dout(0) << "map: ino " << ino << " oid " << ex.oid << " osd " << ex.osd << " offset " << ex.offset << " len " << ex.len << " ... left " << left << dendl;

    left -= x_len;
    cur += x_len;
  }

  // make final list
  for (map<object_t, ObjectExtent>::iterator it = object_extents.begin();
       it != object_extents.end();
       it++) {
    extents.push_back(it->second);
  }
}
<commit_msg>osdc/Filer: assert(0) on stat error<commit_after>// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
// vim: ts=8 sw=2 smarttab
/*
 * Ceph - scalable distributed file system
 *
 * Copyright (C) 2004-2006 Sage Weil <sage@newdream.net>
 *
 * This is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License version 2.1, as published by the Free Software
 * Foundation.  See file COPYING.
 *
 */

#include "Filer.h"
#include "osd/OSDMap.h"

#include "messages/MOSDOp.h"
#include "messages/MOSDOpReply.h"
#include "messages/MOSDMap.h"

#include "msg/Messenger.h"

#include "include/Context.h"

#include "config.h"

#define DOUT_SUBSYS filer
#undef dout_prefix
#define dout_prefix *_dout << objecter->messenger->get_myname() << ".filer "

class Filer::C_Probe : public Context {
public:
  Filer *filer;
  Probe *probe;
  object_t oid;
  uint64_t size;
  utime_t mtime;
  C_Probe(Filer *f, Probe *p, object_t o) : filer(f), probe(p), oid(o), size(0) {}
  void finish(int r) {
    // TODO: handle this error.
    assert(r == 0);

    filer->_probed(probe, oid, size, mtime);
  }
};

int Filer::probe(inodeno_t ino,
		 ceph_file_layout *layout,
		 snapid_t snapid,
		 uint64_t start_from,
		 uint64_t *end,           // LB, when !fwd
		 utime_t *pmtime,
		 bool fwd,
		 int flags,
		 Context *onfinish)
{
  dout(10) << "probe " << (fwd ? "fwd ":"bwd ")
	   << hex << ino << dec
	   << " starting from " << start_from
	   << dendl;

  assert(snapid);  // (until there is a non-NOSNAP write)

  Probe *probe = new Probe(ino, *layout, snapid, start_from, end, pmtime, flags, fwd, onfinish);

  // period (bytes before we jump unto a new set of object(s))
  uint64_t period = layout->fl_stripe_count * layout->fl_object_size;

  // start with 1+ periods.
  probe->probing_len = period;
  if (probe->fwd) {
    if (start_from % period)
      probe->probing_len += period - (start_from % period);
  } else {
    assert(start_from > *end);
    if (start_from % period)
      probe->probing_len -= period - (start_from % period);
    probe->probing_off -= probe->probing_len;
  }

  _probe(probe);
  return 0;
}

void Filer::_probe(Probe *probe)
{
  dout(10) << "_probe " << hex << probe->ino << dec
	   << " " << probe->probing_off << "~" << probe->probing_len
	   << dendl;

  // map range onto objects
  probe->known_size.clear();
  probe->probing.clear();
  file_to_extents(probe->ino, &probe->layout,
		  probe->probing_off, probe->probing_len, probe->probing);

  for (vector<ObjectExtent>::iterator p = probe->probing.begin();
       p != probe->probing.end();
       p++) {
    dout(10) << "_probe  probing " << p->oid << dendl;
    C_Probe *c = new C_Probe(this, probe, p->oid);
    probe->ops[p->oid] = objecter->stat(p->oid, p->oloc, probe->snapid, &c->size, &c->mtime, probe->flags, c);
  }
}

void Filer::_probed(Probe *probe, const object_t& oid, uint64_t size, utime_t mtime)
{
  dout(10) << "_probed " << probe->ino << " object " << oid
	   << " has size " << size << " mtime " << mtime << dendl;

  probe->known_size[oid] = size;
  if (mtime > probe->max_mtime)
    probe->max_mtime = mtime;

  assert(probe->ops.count(oid));
  probe->ops.erase(oid);

  if (!probe->ops.empty())
    return;  // waiting for more!

  // analyze!
  uint64_t end = 0;

  if (!probe->fwd) {
    // reverse
    vector<ObjectExtent> r;
    for (vector<ObjectExtent>::reverse_iterator p = probe->probing.rbegin();
	 p != probe->probing.rend();
	 p++)
      r.push_back(*p);
    probe->probing.swap(r);
  }

  for (vector<ObjectExtent>::iterator p = probe->probing.begin();
       p != probe->probing.end();
       p++) {
    uint64_t shouldbe = p->length + p->offset;
    dout(10) << "_probed  " << probe->ino << " object " << hex << p->oid << dec
	     << " should be " << shouldbe
	     << ", actual is " << probe->known_size[p->oid]
	     << dendl;

    if (!probe->found_size) {
      assert(probe->known_size[p->oid] <= shouldbe);

      if ((probe->fwd && probe->known_size[p->oid] == shouldbe) ||
	  (!probe->fwd && probe->known_size[p->oid] == 0 && probe->probing_off > 0))
	continue;  // keep going

      // aha, we found the end!
      // calc offset into buffer_extent to get distance from probe->from.
      uint64_t oleft = probe->known_size[p->oid] - p->offset;
      for (map<__u32,__u32>::iterator i = p->buffer_extents.begin();
	   i != p->buffer_extents.end();
	   i++) {
	if (oleft <= (uint64_t)i->second) {
	  end = probe->probing_off + i->first + oleft;
	  dout(10) << "_probed  end is in buffer_extent " << i->first << "~" << i->second << " off " << oleft
		   << ", from was " << probe->probing_off << ", end is " << end
		   << dendl;

	  probe->found_size = true;
	  dout(10) << "_probed found size at " << end << dendl;
	  *probe->psize = end;

	  if (!probe->pmtime)  // stop if we don't need mtime too
	    break;
	}
	oleft -= i->second;
      }
    }
    break;
  }

  if (!probe->found_size || (probe->probing_off && probe->pmtime)) {
    // keep probing!
    dout(10) << "_probed probing further" << dendl;

    uint64_t period = probe->layout.fl_stripe_count * probe->layout.fl_object_size;
    if (probe->fwd) {
      probe->probing_off += probe->probing_len;
      assert(probe->probing_off % period == 0);
      probe->probing_len = period;
    } else {
      // previous period.
      assert(probe->probing_off % period == 0);
      probe->probing_len = period;
      probe->probing_off -= period;
    }
    _probe(probe);
    return;
  }

  if (probe->pmtime) {
    dout(10) << "_probed found mtime " << probe->max_mtime << dendl;
    *probe->pmtime = probe->max_mtime;
  }

  // done!  finish and clean up.
  probe->onfinish->finish(probe->err);
  delete probe->onfinish;
  delete probe;
}

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

struct PurgeRange {
  inodeno_t ino;
  ceph_file_layout layout;
  SnapContext snapc;
  uint64_t first, num;
  utime_t mtime;
  int flags;
  Context *oncommit;
  int uncommitted;
};

int Filer::purge_range(inodeno_t ino,
		       ceph_file_layout *layout,
		       const SnapContext& snapc,
		       uint64_t first_obj, uint64_t num_obj,
		       utime_t mtime,
		       int flags,
		       Context *oncommit)
{
  assert(num_obj > 0);

  // single object?  easy!
  if (num_obj == 1) {
    object_t oid = file_object_t(ino, first_obj);
    object_locator_t oloc = objecter->osdmap->file_to_object_locator(*layout);
    objecter->remove(oid, oloc, snapc, mtime, flags, NULL, oncommit);
    return 0;
  }

  // lots!  let's do this in pieces.
  PurgeRange *pr = new PurgeRange;
  pr->ino = ino;
  pr->layout = *layout;
  pr->snapc = snapc;
  pr->first = first_obj;
  pr->num = num_obj;
  pr->mtime = mtime;
  pr->flags = flags;
  pr->oncommit = oncommit;
  pr->uncommitted = 0;

  _do_purge_range(pr, 0);
  return 0;
}

struct C_PurgeRange : public Context {
  Filer *filer;
  PurgeRange *pr;
  C_PurgeRange(Filer *f, PurgeRange *p) : filer(f), pr(p) {}
  void finish(int r) {
    filer->_do_purge_range(pr, 1);
  }
};

void Filer::_do_purge_range(PurgeRange *pr, int fin)
{
  pr->uncommitted -= fin;
  dout(10) << "_do_purge_range " << pr->ino << " objects " << pr->first << "~" << pr->num
	   << " uncommitted " << pr->uncommitted << dendl;

  if (pr->num == 0 && pr->uncommitted == 0) {
    pr->oncommit->finish(0);
    delete pr->oncommit;
    delete pr;
    return;
  }

  int max = 10 - pr->uncommitted;
  while (pr->num > 0 && max > 0) {
    object_t oid = file_object_t(pr->ino, pr->first);
    object_locator_t oloc = objecter->osdmap->file_to_object_locator(pr->layout);
    objecter->remove(oid, oloc, pr->snapc, pr->mtime, pr->flags,
		     NULL, new C_PurgeRange(this, pr));
    pr->uncommitted++;
    pr->first++;
    pr->num--;
    max--;
  }
}

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

void Filer::file_to_extents(inodeno_t ino, ceph_file_layout *layout,
                            uint64_t offset, uint64_t len,
                            vector<ObjectExtent>& extents)
{
  dout(10) << "file_to_extents " << offset << "~" << len
           << " on " << hex << ino << dec
           << dendl;
  assert(len > 0);

  /* we want only one extent per object!
   * this means that each extent we read may map into different bits of the
   * final read buffer.. hence OSDExtent.buffer_extents
   */
  map< object_t, ObjectExtent > object_extents;

  __u32 object_size = layout->fl_object_size;
  __u32 su = layout->fl_stripe_unit;
  __u32 stripe_count = layout->fl_stripe_count;
  assert(object_size >= su);
  uint64_t stripes_per_object = object_size / su;
  dout(20) << " stripes_per_object " << stripes_per_object << dendl;

  uint64_t cur = offset;
  uint64_t left = len;
  while (left > 0) {
    // layout into objects
    uint64_t blockno = cur / su;          // which block
    uint64_t stripeno = blockno / stripe_count;    // which horizontal stripe        (Y)
    uint64_t stripepos = blockno % stripe_count;   // which object in the object set (X)
    uint64_t objectsetno = stripeno / stripes_per_object;       // which object set
    uint64_t objectno = objectsetno * stripe_count + stripepos;  // object id

    // find oid, extent
    ObjectExtent *ex = 0;
    object_t oid = file_object_t(ino, objectno);
    if (object_extents.count(oid))
      ex = &object_extents[oid];
    else {
      ex = &object_extents[oid];
      ex->oid = oid;
      ex->oloc = objecter->osdmap->file_to_object_locator(*layout);
    }

    // map range into object
    uint64_t block_start = (stripeno % stripes_per_object)*su;
    uint64_t block_off = cur % su;
    uint64_t max = su - block_off;

    uint64_t x_offset = block_start + block_off;
    uint64_t x_len;
    if (left > max)
      x_len = max;
    else
      x_len = left;

    if (ex->offset + (uint64_t)ex->length == x_offset) {
      // add to extent
      ex->length += x_len;
    } else {
      // new extent
      assert(ex->length == 0);
      assert(ex->offset == 0);
      ex->offset = x_offset;
      ex->length = x_len;
    }
    ex->buffer_extents[cur-offset] = x_len;

    dout(15) << "file_to_extents  " << *ex << " in " << ex->oloc << dendl;
    //dout(0) << "map: ino " << ino << " oid " << ex.oid << " osd " << ex.osd << " offset " << ex.offset << " len " << ex.len << " ... left " << left << dendl;

    left -= x_len;
    cur += x_len;
  }

  // make final list
  for (map<object_t, ObjectExtent>::iterator it = object_extents.begin();
       it != object_extents.end();
       it++) {
    extents.push_back(it->second);
  }
}
<|endoftext|>


<commit_before>/*************************************
** Tsunagari Tile Engine            **
** main.cpp                         **
** Copyright 2016-2019 Paul Merrill **
*************************************/

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

#include "os/c.h"
#include "os/mutex.h"
#include "os/os.h"
#include "pack/pack-reader.h"
#include "pack/pack-writer.h"
#include "pack/ui.h"
#include "pack/walker.h"
#include "util/int.h"
#include "util/move.h"
#include "util/optional.h"
#include "util/string-view.h"
#include "util/string.h"
#include "util/unique.h"

static String exe;

static void
usage() noexcept {
    fprintf(stderr,
            "usage: %s create [-v] <output-archive> [input-file]...\n",
            exe.null().get());
    fprintf(stderr, "       %s list <input-archive>\n", exe.null().get());
    fprintf(stderr,
            "       %s extract [-v] <input-archive>\n",
            exe.null().get());
}

struct CreateArchiveContext {
    Unique<PackWriter> pack;
    Mutex packMutex;
};

static void
addFile(CreateArchiveContext& ctx, StringView path) noexcept {
    Optional<String> data = readFile(path);

    if (!data) {
        uiShowSkippedMissingFile(path);
        return;
    }

    String data_ = move_(*data);

    uiShowAddedFile(path, data_.size());

    LockGuard guard(ctx.packMutex);
    ctx.pack->addBlob(move_(path), static_cast<uint32_t>(data_.size()), data_.data());

    data_.reset_lose_memory();  // Don't delete data pointer.
}

static bool
createArchive(StringView archivePath, Vector<StringView> paths) noexcept {
    UI ui;

    CreateArchiveContext ctx;
    ctx.pack = PackWriter::make();

    walk(move_(paths), [&](StringView path) { addFile(ctx, path); });

    uiShowWritingArchive(archivePath);

    return ctx.pack->writeToFile(archivePath);
}

static bool
listArchive(StringView archivePath) noexcept {
    UI ui;

    Unique<PackReader> pack = PackReader::fromFile(archivePath);

    if (pack) {
        String output;

        for (PackReader::BlobIndex i = 0; i < pack->size(); i++) {
            StringView blobPath = pack->getBlobPath(i);
            uint64_t blobSize = pack->getBlobSize(i);

            output.append(blobPath.data, blobPath.size);
            output << ": " << blobSize << " bytes\n";
        }

        printf("%s", output.null().get());
        return true;
    }
    else {
        fprintf(stderr,
                "%s",
                (String() << exe << ": " << archivePath << ": not found\n")
                        .null()
                        .get());
        return false;
    }
}

static Optional<StringView>
getParentPath(StringView path) noexcept {
    Optional<size_t> sep = path.rfind('/');
    if (!sep) {
        return Optional<StringView>();
    }
    else {
        return Optional<StringView>(path.substr(0, *sep));
    }
}

static void
createDirs(StringView path) noexcept {
    Optional<StringView> parentPath = getParentPath(path);
    if (parentPath) {
        // Make sure parentPath's parent exists.
        createDirs(*parentPath);

        makeDirectory(*parentPath);
    }
}

static void
putFile(StringView path, uint32_t size, void* data) noexcept {
    createDirs(path);

    // TODO: Propagate error up.
    writeFile(path, size, data);
}

static bool
extractArchive(StringView archivePath) noexcept {
    UI ui;

    Unique<PackReader> pack = PackReader::fromFile(archivePath);

    if (pack) {
        Vector<PackReader::BlobIndex> blobIndicies;
        for (PackReader::BlobIndex i = 0; i < pack->size(); i++) {
            blobIndicies.push_back(i);
        }

        Vector<void*> blobDatas = pack->getBlobDatas(blobIndicies);

        for (PackReader::BlobIndex i = 0; i < pack->size(); i++) {
            StringView blobPath = pack->getBlobPath(i);
            uint32_t blobSize = pack->getBlobSize(i);
            void* blobData = blobDatas[i];

            uiShowExtractingFile(blobPath, blobSize);

            putFile(blobPath, blobSize, blobData);
        }

        return true;
    }
    else {
        fprintf(stderr,
                "%s: %s: not found\n",
                exe.null().get(),
                String(archivePath).null().get());
        return false;
    }
}

int
main(int argc, char* argv[]) noexcept {
    exe = argv[0];
    Optional<size_t> dir = exe.view().rfind(dirSeparator);
    if (dir) {
        exe = exe.view().substr(*dir + 1);
    }

    if (argc == 1) {
        usage();
        return 0;
    }
    if (argc == 2) {
        usage();
        return 1;
    }

    StringView command = argv[1];
    Vector<StringView> args;

    for (int i = 2; i < argc; i++) {
        args.push_back(argv[i]);
    }

    int exitCode;

    if (command == "create") {
        if (args.size() > 0 && args[0] == "-v") {
            verbose = true;
            args.erase(args.begin());
        }

        if (args.size() < 2) {
            usage();
            return 1;
        }

        // The first argument is the archive, the rest are files to add to it.
        StringView archivePath = args[0];
        args.erase(args.begin());

        return createArchive(archivePath, move_(args)) ? 0 : 1;
    }
    else if (command == "list") {
        verbose = true;

        if (args.size() != 1) {
            usage();
            return 1;
        }

        exitCode = listArchive(args[0]) ? 0 : 1;
    }
    else if (command == "extract") {
        if (args.size() > 0 && args[0] == "-v") {
            verbose = true;
            args.erase(args.begin());
        }

        if (args.size() != 1) {
            usage();
            return 1;
        }

        exitCode = extractArchive(args[0]) ? 0 : 1;
    }
    else {
        usage();
        return 1;
    }

    return exitCode;
}
<commit_msg>pack/main: Add files with '/' separate on Windows<commit_after>/*************************************
** Tsunagari Tile Engine            **
** main.cpp                         **
** Copyright 2016-2019 Paul Merrill **
*************************************/

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

#include "os/c.h"
#include "os/mutex.h"
#include "os/os.h"
#include "pack/pack-reader.h"
#include "pack/pack-writer.h"
#include "pack/ui.h"
#include "pack/walker.h"
#include "util/int.h"
#include "util/move.h"
#include "util/optional.h"
#include "util/string-view.h"
#include "util/string.h"
#include "util/unique.h"

static String exe;

static void
usage() noexcept {
    fprintf(stderr,
            "usage: %s create [-v] <output-archive> [input-file]...\n",
            exe.null().get());
    fprintf(stderr, "       %s list <input-archive>\n", exe.null().get());
    fprintf(stderr,
            "       %s extract [-v] <input-archive>\n",
            exe.null().get());
}

struct CreateArchiveContext {
    Unique<PackWriter> pack;
    Mutex packMutex;
};

static void
addFile(CreateArchiveContext& ctx, StringView path) noexcept {
    Optional<String> data = readFile(path);

    if (!data) {
        uiShowSkippedMissingFile(path);
        return;
    }

    String data_ = move_(*data);

    uiShowAddedFile(path, data_.size());

    // Write the file path to the pack file with '/' instead of '\\' on Windows.
    String standardizedPath;

    if (dirSeparator != '/') {
        standardizedPath = path;

        for (size_t i = 0; i < path.size; i++) {
            if (standardizedPath[i] == dirSeparator) {
                standardizedPath[i] = '/';
            }
        }

        path = standardizedPath;
    }

    LockGuard guard(ctx.packMutex);
    ctx.pack->addBlob(move_(path), static_cast<uint32_t>(data_.size()), data_.data());

    data_.reset_lose_memory();  // Don't delete data pointer.
}

static bool
createArchive(StringView archivePath, Vector<StringView> paths) noexcept {
    UI ui;

    CreateArchiveContext ctx;
    ctx.pack = PackWriter::make();

    walk(move_(paths), [&](StringView path) { addFile(ctx, path); });

    uiShowWritingArchive(archivePath);

    return ctx.pack->writeToFile(archivePath);
}

static bool
listArchive(StringView archivePath) noexcept {
    UI ui;

    Unique<PackReader> pack = PackReader::fromFile(archivePath);

    if (pack) {
        String output;

        for (PackReader::BlobIndex i = 0; i < pack->size(); i++) {
            StringView blobPath = pack->getBlobPath(i);
            uint64_t blobSize = pack->getBlobSize(i);

            // Print file paths with '\\' Windows.
            String standardizedPath;

            if (dirSeparator != '/') {
                standardizedPath = blobPath;

                for (size_t i = 0; i < blobPath.size; i++) {
                    if (standardizedPath[i] == '/') {
                        standardizedPath[i] = dirSeparator;
                    }
                }

                blobPath = standardizedPath;
            }

            output.append(blobPath.data, blobPath.size);
            output << ": " << blobSize << " bytes\n";
        }

        printf("%s", output.null().get());
        return true;
    }
    else {
        fprintf(stderr,
                "%s",
                (String() << exe << ": " << archivePath << ": not found\n")
                        .null()
                        .get());
        return false;
    }
}

static Optional<StringView>
getParentPath(StringView path) noexcept {
    Optional<size_t> sep = path.rfind(dirSeparator);
    if (!sep) {
        return Optional<StringView>();
    }
    else {
        return Optional<StringView>(path.substr(0, *sep));
    }
}

static void
createDirs(StringView path) noexcept {
    Optional<StringView> parentPath = getParentPath(path);
    if (parentPath) {
        // Make sure parentPath's parent exists.
        createDirs(*parentPath);

        makeDirectory(*parentPath);
    }
}

static void
putFile(StringView path, uint32_t size, void* data) noexcept {
    createDirs(path);

    // TODO: Propagate error up.
    writeFile(path, size, data);
}

static bool
extractArchive(StringView archivePath) noexcept {
    UI ui;

    Unique<PackReader> pack = PackReader::fromFile(archivePath);

    if (pack) {
        Vector<PackReader::BlobIndex> blobIndicies;
        for (PackReader::BlobIndex i = 0; i < pack->size(); i++) {
            blobIndicies.push_back(i);
        }

        Vector<void*> blobDatas = pack->getBlobDatas(blobIndicies);

        for (PackReader::BlobIndex i = 0; i < pack->size(); i++) {
            StringView blobPath = pack->getBlobPath(i);
            uint32_t blobSize = pack->getBlobSize(i);
            void* blobData = blobDatas[i];

            // Change file paths to use '\\' on Windows.
            String standardizedPath;

            if (dirSeparator != '/') {
                standardizedPath = blobPath;

                for (size_t i = 0; i < blobPath.size; i++) {
                    if (standardizedPath[i] == '/') {
                        standardizedPath[i] = dirSeparator;
                    }
                }

                blobPath = standardizedPath;
            }

            uiShowExtractingFile(blobPath, blobSize);

            putFile(blobPath, blobSize, blobData);
        }

        return true;
    }
    else {
        fprintf(stderr,
                "%s: %s: not found\n",
                exe.null().get(),
                String(archivePath).null().get());
        return false;
    }
}

int
main(int argc, char* argv[]) noexcept {
    exe = argv[0];
    Optional<size_t> dir = exe.view().rfind(dirSeparator);
    if (dir) {
        exe = exe.view().substr(*dir + 1);
    }

    if (argc == 1) {
        usage();
        return 0;
    }
    if (argc == 2) {
        usage();
        return 1;
    }

    StringView command = argv[1];
    Vector<StringView> args;

    for (int i = 2; i < argc; i++) {
        args.push_back(argv[i]);
    }

    int exitCode;

    if (command == "create") {
        if (args.size() > 0 && args[0] == "-v") {
            verbose = true;
            args.erase(args.begin());
        }

        if (args.size() < 2) {
            usage();
            return 1;
        }

        // The first argument is the archive, the rest are files to add to it.
        StringView archivePath = args[0];
        args.erase(args.begin());

        return createArchive(archivePath, move_(args)) ? 0 : 1;
    }
    else if (command == "list") {
        verbose = true;

        if (args.size() != 1) {
            usage();
            return 1;
        }

        exitCode = listArchive(args[0]) ? 0 : 1;
    }
    else if (command == "extract") {
        if (args.size() > 0 && args[0] == "-v") {
            verbose = true;
            args.erase(args.begin());
        }

        if (args.size() != 1) {
            usage();
            return 1;
        }

        exitCode = extractArchive(args[0]) ? 0 : 1;
    }
    else {
        usage();
        return 1;
    }

    return exitCode;
}
<|endoftext|>