<commit_before>// License: Apache 2.0. See LICENSE file in root directory.
// Copyright(c) 2018 Intel Corporation. All Rights Reserved.

#include <iostream>

#include "librealsense2/rs.hpp"

#include "tclap/CmdLine.h"

#include "converters/converter-csv.hpp"
#include "converters/converter-png.hpp"
#include "converters/converter-raw.hpp"
#include "converters/converter-ply.hpp"
#include "converters/converter-bin.hpp"

#include <mutex>

#define SECTONSEC 1000000000

using namespace std;
using namespace TCLAP;

int main(int argc, char** argv) try
{
    rs2::log_to_file(RS2_LOG_SEVERITY_WARN);

    // Parse command line arguments
    CmdLine cmd("librealsense rs-convert tool", ' ');
    ValueArg<string> inputFilename("i", "input", "ROS-bag filename", true, "", "ros-bag-file");
    ValueArg<string> outputFilenamePng("p", "output-png", "output PNG file(s) path", false, "", "png-path");
    ValueArg<string> outputFilenameCsv("v", "output-csv", "output CSV (depth matrix) file(s) path", false, "", "csv-path");
    ValueArg<string> outputFilenameRaw("r", "output-raw", "output RAW file(s) path", false, "", "raw-path");
    ValueArg<string> outputFilenamePly("l", "output-ply", "output PLY file(s) path", false, "", "ply-path");
    ValueArg<string> outputFilenameBin("b", "output-bin", "output BIN (depth matrix) file(s) path", false, "", "bin-path");
    SwitchArg switchDepth("d", "depth", "convert depth frames (default - all supported)", false);
    SwitchArg switchColor("c", "color", "convert color frames (default - all supported)", false);
    ValueArg <string> frameNumberStart("f", "first-frame", "convert frame number range - start point", false, "", "first-framenumber");
    ValueArg <string> frameNumberEnd("t", "last-frame", "convert frame number range - end point", false, "", "last-framenumber");
    ValueArg <string> StartTime("s", "start-time", "convert time range - start point (in seconds)", false, "", "start-time");
    ValueArg <string> EndTime("e", "end-time", "convert time range - end point (in seconds)", false, "", "end-time");

    cmd.add(inputFilename);
    cmd.add(frameNumberEnd);
    cmd.add(frameNumberStart);
    cmd.add(EndTime);
    cmd.add(StartTime);
    cmd.add(outputFilenamePng);
    cmd.add(outputFilenameCsv);
    cmd.add(outputFilenameRaw);
    cmd.add(outputFilenamePly);
    cmd.add(outputFilenameBin);
    cmd.add(switchDepth);
    cmd.add(switchColor);
    cmd.parse(argc, argv);

    vector<shared_ptr<rs2::tools::converter::converter_base>> converters;
    shared_ptr<rs2::tools::converter::converter_ply> plyconverter;

    rs2_stream streamType = switchDepth.isSet() ? rs2_stream::RS2_STREAM_DEPTH
        : switchColor.isSet() ? rs2_stream::RS2_STREAM_COLOR
        : rs2_stream::RS2_STREAM_ANY;

    if (outputFilenameCsv.isSet()) {
        converters.push_back(
            make_shared<rs2::tools::converter::converter_csv>(
                outputFilenameCsv.getValue()
                , streamType));
    }

    if (outputFilenamePng.isSet()) {
        converters.push_back(
            make_shared<rs2::tools::converter::converter_png>(
                outputFilenamePng.getValue()
                , streamType));
    }

    if (outputFilenameRaw.isSet()) {
        converters.push_back(
            make_shared<rs2::tools::converter::converter_raw>(
                outputFilenameRaw.getValue()
                , streamType));
    }

    if (outputFilenameBin.isSet()) {
        converters.push_back(
            make_shared<rs2::tools::converter::converter_bin>(
                outputFilenameBin.getValue()));
    }

    if (converters.empty() && !outputFilenamePly.isSet()) {
        throw runtime_error("output not defined");
    }

    auto first_frame = 0;
    auto last_frame = 0;
    auto start_time = 0;
    auto end_time = 0;

    if (frameNumberStart.isSet()) {
        first_frame = stoi(frameNumberStart.getValue());
    }
    if (frameNumberEnd.isSet()) {
        last_frame = stoi(frameNumberEnd.getValue());
    }
    if (StartTime.isSet()) {
        start_time = SECTONSEC *(atoi(StartTime.getValue().c_str())); //seconds to nanoseconds
        std::cout << start_time << endl;
    }
    if (EndTime.isSet()) {
        end_time = SECTONSEC *(atoi(EndTime.getValue().c_str())); //seconds to nanoseconds
        std::cout << end_time << endl;
    }

    //in order to convert frames into ply we need synced depth and color frames,
    //therefore we use pipeline
    if (outputFilenamePly.isSet()) {

        // Since we are running in blocking "non-real-time" mode,
        // we don't want to prevent process termination if some of the frames
        // did not find a match and hence were not serviced
        auto pipe = std::shared_ptr<rs2::pipeline>(
            new rs2::pipeline(), [](rs2::pipeline*) {});

        plyconverter = make_shared<rs2::tools::converter::converter_ply>(
            outputFilenamePly.getValue());

        rs2::config cfg;
        cfg.enable_device_from_file(inputFilename.getValue());
        pipe->start(cfg);

        auto device = pipe->get_active_profile().get_device();
        rs2::playback playback = device.as<rs2::playback>();
        playback.set_real_time(false);

        auto duration = playback.get_duration();
        int progress = 0;
        auto frameNumber = 0ULL;

        rs2::frameset frameset;
        uint64_t posLast = playback.get_position();

        while (pipe->try_wait_for_frames(&frameset, 1000))
        {
            int posP = static_cast<int>(posLast * 100. / duration.count());

            if (posP > progress) {
                progress = posP;
                cout << posP << "%" << "\r" << flush;
            }

            frameNumber = frameset[0].get_frame_number();
            auto frame_position = playback.get_position();

            if (frameNumberStart.isSet() && frameNumber < first_frame) {
                break;
            }
            if (frameNumberEnd.isSet() && frameNumber > last_frame) {
                break;
            }
            if (StartTime.isSet() && frame_position < start_time) {
                break;
            }
            if (EndTime.isSet() && frame_position > end_time) {
                break;
            }

            plyconverter->convert(frameset);
            plyconverter->wait();

            const uint64_t posCurr = playback.get_position();
            if (static_cast<int64_t>(posCurr - posLast) < 0) {
                break;
            }
            posLast = posCurr;
        }
    }

    // for every converter other than ply,
    // we get the frames from playback sensors
    // and convert them one by one
    if (!converters.empty()) {
        rs2::context ctx;
        auto playback = ctx.load_device(inputFilename.getValue());
        playback.set_real_time(false);
        std::vector<rs2::sensor> sensors = playback.query_sensors();
        std::mutex mutex;

        auto duration = playback.get_duration();
        int progress = 0;
        uint64_t posLast = playback.get_position();

        for (auto sensor : sensors) {
            if (!sensor.get_stream_profiles().size())
            {
                continue;
            }

            sensor.open(sensor.get_stream_profiles());
            sensor.start([&](rs2::frame frame)
            {
                std::lock_guard<std::mutex> lock(mutex);

                auto frameNumber = frame.get_frame_number();
                auto frame_position = playback.get_position();

                if (frameNumberStart.isSet() && frameNumber < first_frame) {
                    return;
                }
                if (frameNumberEnd.isSet() && frameNumber > last_frame) {
                    return;
                }
                if (StartTime.isSet() && frame_position < start_time) {
                    return;
                }
                if (EndTime.isSet() && frame_position > end_time) {
                    return;
                }

                for_each(converters.begin(), converters.end(),
                    [&frame](shared_ptr<rs2::tools::converter::converter_base>& converter) {
                    converter->convert(frame);
                });

                for_each(converters.begin(), converters.end(),
                    [](shared_ptr<rs2::tools::converter::converter_base>& converter) {
                    converter->wait();
                });
            });

        }

        //we need to clear the output of ply progress ("100%") before writing
        //the progress of the other converters in the same line
        cout << "\r    \r";

        while (true)
        {
            int posP = static_cast<int>(posLast * 100. / duration.count());

            if (posP > progress) {
                progress = posP;
                cout << posP << "%" << "\r" << flush;
            }

            const uint64_t posCurr = playback.get_position();
            if (static_cast<int64_t>(posCurr - posLast) < 0) {
                break;
            }
            posLast = posCurr;
        }

        for (auto sensor : sensors) {
            if (!sensor.get_stream_profiles().size())
            {
                continue;
            }
            sensor.stop();
            sensor.close();
        }
    }

    cout << endl;

    //print statistics for ply converter.
    if (outputFilenamePly.isSet()) {
        cout << plyconverter->get_statistics() << endl;
    }

    for_each(converters.begin(), converters.end(),
        [](shared_ptr<rs2::tools::converter::converter_base>& converter) {
        cout << converter->get_statistics() << endl;
    });

    return EXIT_SUCCESS;
}
catch (const rs2::error & e)
{
    cerr << "RealSense error calling " << e.get_failed_function()
        << "(" << e.get_failed_args() << "):\n    " << e.what() << endl;

    return EXIT_FAILURE;
}
catch (const exception & e)
{
    cerr << e.what() << endl;
    return EXIT_FAILURE;
}
catch (...)
{
    cerr << "some error" << endl;
    return EXIT_FAILURE;
}
<commit_msg>explicit types<commit_after>// License: Apache 2.0. See LICENSE file in root directory.
// Copyright(c) 2018 Intel Corporation. All Rights Reserved.

#include <iostream>

#include "librealsense2/rs.hpp"

#include "tclap/CmdLine.h"

#include "converters/converter-csv.hpp"
#include "converters/converter-png.hpp"
#include "converters/converter-raw.hpp"
#include "converters/converter-ply.hpp"
#include "converters/converter-bin.hpp"

#include <mutex>

#define SECTONSEC 1000000000

using namespace std;
using namespace TCLAP;

int main(int argc, char** argv) try
{
    rs2::log_to_file(RS2_LOG_SEVERITY_WARN);

    // Parse command line arguments
    CmdLine cmd("librealsense rs-convert tool", ' ');
    ValueArg<string> inputFilename("i", "input", "ROS-bag filename", true, "", "ros-bag-file");
    ValueArg<string> outputFilenamePng("p", "output-png", "output PNG file(s) path", false, "", "png-path");
    ValueArg<string> outputFilenameCsv("v", "output-csv", "output CSV (depth matrix) file(s) path", false, "", "csv-path");
    ValueArg<string> outputFilenameRaw("r", "output-raw", "output RAW file(s) path", false, "", "raw-path");
    ValueArg<string> outputFilenamePly("l", "output-ply", "output PLY file(s) path", false, "", "ply-path");
    ValueArg<string> outputFilenameBin("b", "output-bin", "output BIN (depth matrix) file(s) path", false, "", "bin-path");
    SwitchArg switchDepth("d", "depth", "convert depth frames (default - all supported)", false);
    SwitchArg switchColor("c", "color", "convert color frames (default - all supported)", false);
    ValueArg <string> frameNumberStart("f", "first-frame", "convert frame number range - start point", false, "", "first-framenumber");
    ValueArg <string> frameNumberEnd("t", "last-frame", "convert frame number range - end point", false, "", "last-framenumber");
    ValueArg <string> StartTime("s", "start-time", "convert time range - start point (in seconds)", false, "", "start-time");
    ValueArg <string> EndTime("e", "end-time", "convert time range - end point (in seconds)", false, "", "end-time");

    cmd.add(inputFilename);
    cmd.add(frameNumberEnd);
    cmd.add(frameNumberStart);
    cmd.add(EndTime);
    cmd.add(StartTime);
    cmd.add(outputFilenamePng);
    cmd.add(outputFilenameCsv);
    cmd.add(outputFilenameRaw);
    cmd.add(outputFilenamePly);
    cmd.add(outputFilenameBin);
    cmd.add(switchDepth);
    cmd.add(switchColor);
    cmd.parse(argc, argv);

    vector<shared_ptr<rs2::tools::converter::converter_base>> converters;
    shared_ptr<rs2::tools::converter::converter_ply> plyconverter;

    rs2_stream streamType = switchDepth.isSet() ? rs2_stream::RS2_STREAM_DEPTH
        : switchColor.isSet() ? rs2_stream::RS2_STREAM_COLOR
        : rs2_stream::RS2_STREAM_ANY;

    if (outputFilenameCsv.isSet()) {
        converters.push_back(
            make_shared<rs2::tools::converter::converter_csv>(
                outputFilenameCsv.getValue()
                , streamType));
    }

    if (outputFilenamePng.isSet()) {
        converters.push_back(
            make_shared<rs2::tools::converter::converter_png>(
                outputFilenamePng.getValue()
                , streamType));
    }

    if (outputFilenameRaw.isSet()) {
        converters.push_back(
            make_shared<rs2::tools::converter::converter_raw>(
                outputFilenameRaw.getValue()
                , streamType));
    }

    if (outputFilenameBin.isSet()) {
        converters.push_back(
            make_shared<rs2::tools::converter::converter_bin>(
                outputFilenameBin.getValue()));
    }

    if (converters.empty() && !outputFilenamePly.isSet()) {
        throw runtime_error("output not defined");
    }

    unsigned long long first_frame = 0;
    unsigned long long last_frame = 0;
    uint64_t start_time = 0;
    uint64_t end_time = 0;

    if (frameNumberStart.isSet()) {
        first_frame = stoi(frameNumberStart.getValue());
    }
    if (frameNumberEnd.isSet()) {
        last_frame = stoi(frameNumberEnd.getValue());
    }
    if (StartTime.isSet()) {
        start_time = SECTONSEC *(atoi(StartTime.getValue().c_str())); //seconds to nanoseconds
        std::cout << start_time << endl;
    }
    if (EndTime.isSet()) {
        end_time = SECTONSEC *(atoi(EndTime.getValue().c_str())); //seconds to nanoseconds
        std::cout << end_time << endl;
    }

    //in order to convert frames into ply we need synced depth and color frames,
    //therefore we use pipeline
    if (outputFilenamePly.isSet()) {

        // Since we are running in blocking "non-real-time" mode,
        // we don't want to prevent process termination if some of the frames
        // did not find a match and hence were not serviced
        auto pipe = std::shared_ptr<rs2::pipeline>(
            new rs2::pipeline(), [](rs2::pipeline*) {});

        plyconverter = make_shared<rs2::tools::converter::converter_ply>(
            outputFilenamePly.getValue());

        rs2::config cfg;
        cfg.enable_device_from_file(inputFilename.getValue());
        pipe->start(cfg);

        auto device = pipe->get_active_profile().get_device();
        rs2::playback playback = device.as<rs2::playback>();
        playback.set_real_time(false);

        auto duration = playback.get_duration();
        int progress = 0;
        auto frameNumber = 0ULL;

        rs2::frameset frameset;
        uint64_t posLast = playback.get_position();

        while (pipe->try_wait_for_frames(&frameset, 1000))
        {
            int posP = static_cast<int>(posLast * 100. / duration.count());

            if (posP > progress) {
                progress = posP;
                cout << posP << "%" << "\r" << flush;
            }

            frameNumber = frameset[0].get_frame_number();
            auto frame_position = playback.get_position();

            if (frameNumberStart.isSet() && frameNumber < first_frame) {
                break;
            }
            if (frameNumberEnd.isSet() && frameNumber > last_frame) {
                break;
            }
            if (StartTime.isSet() && frame_position < start_time) {
                break;
            }
            if (EndTime.isSet() && frame_position > end_time) {
                break;
            }

            plyconverter->convert(frameset);
            plyconverter->wait();

            const uint64_t posCurr = playback.get_position();
            if (static_cast<int64_t>(posCurr - posLast) < 0) {
                break;
            }
            posLast = posCurr;
        }
    }

    // for every converter other than ply,
    // we get the frames from playback sensors
    // and convert them one by one
    if (!converters.empty()) {
        rs2::context ctx;
        auto playback = ctx.load_device(inputFilename.getValue());
        playback.set_real_time(false);
        std::vector<rs2::sensor> sensors = playback.query_sensors();
        std::mutex mutex;

        auto duration = playback.get_duration();
        int progress = 0;
        uint64_t posLast = playback.get_position();

        for (auto sensor : sensors) {
            if (!sensor.get_stream_profiles().size())
            {
                continue;
            }

            sensor.open(sensor.get_stream_profiles());
            sensor.start([&](rs2::frame frame)
            {
                std::lock_guard<std::mutex> lock(mutex);

                auto frameNumber = frame.get_frame_number();
                auto frame_position = playback.get_position();

                if (frameNumberStart.isSet() && frameNumber < first_frame) {
                    return;
                }
                if (frameNumberEnd.isSet() && frameNumber > last_frame) {
                    return;
                }
                if (StartTime.isSet() && frame_position < start_time) {
                    return;
                }
                if (EndTime.isSet() && frame_position > end_time) {
                    return;
                }

                for_each(converters.begin(), converters.end(),
                    [&frame](shared_ptr<rs2::tools::converter::converter_base>& converter) {
                    converter->convert(frame);
                });

                for_each(converters.begin(), converters.end(),
                    [](shared_ptr<rs2::tools::converter::converter_base>& converter) {
                    converter->wait();
                });
            });

        }

        //we need to clear the output of ply progress ("100%") before writing
        //the progress of the other converters in the same line
        cout << "\r    \r";

        while (true)
        {
            int posP = static_cast<int>(posLast * 100. / duration.count());

            if (posP > progress) {
                progress = posP;
                cout << posP << "%" << "\r" << flush;
            }

            const uint64_t posCurr = playback.get_position();
            if (static_cast<int64_t>(posCurr - posLast) < 0) {
                break;
            }
            posLast = posCurr;
        }

        for (auto sensor : sensors) {
            if (!sensor.get_stream_profiles().size())
            {
                continue;
            }
            sensor.stop();
            sensor.close();
        }
    }

    cout << endl;

    //print statistics for ply converter.
    if (outputFilenamePly.isSet()) {
        cout << plyconverter->get_statistics() << endl;
    }

    for_each(converters.begin(), converters.end(),
        [](shared_ptr<rs2::tools::converter::converter_base>& converter) {
        cout << converter->get_statistics() << endl;
    });

    return EXIT_SUCCESS;
}
catch (const rs2::error & e)
{
    cerr << "RealSense error calling " << e.get_failed_function()
        << "(" << e.get_failed_args() << "):\n    " << e.what() << endl;

    return EXIT_FAILURE;
}
catch (const exception & e)
{
    cerr << e.what() << endl;
    return EXIT_FAILURE;
}
catch (...)
{
    cerr << "some error" << endl;
    return EXIT_FAILURE;
}
<|endoftext|>


<commit_before>/*
 * helix-svm - Order book events for libsvm
 *
 * This is a program that listens to a market data feed and outputs order book
 * events in libsvm compatible format.
 *
 * Feature extraction and classification model is based on the following paper:
 *
 *   Kercheval, Alec N., and Yuan Zhang. "Modeling high-frequency limit order
 *   book dynamics with support vector machines." (2013).
 */

#include <helix-c/helix.h>
#include <getopt.h>
#include <libgen.h>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cerrno>
#include <cstdio>
#include <vector>
#include <deque>
#include <cmath>
#include <tuple>
#include <uv.h>

using namespace std;

static const char *program;

FILE* output;

struct config {
	const char *symbol;
	const char *multicast_proto;
	const char *multicast_addr;
	int multicast_port;
	const char *output;
};

class svm_session {
	static constexpr size_t nr_levels = 5;

	struct features {
		std::vector<double> values;

		void add(double v) {
			values.push_back(v);
		}
	};

	// Type of a time point (in number of events):
	using time_point = uint64_t;

	// Type of duration (in number of events):
	using duration = uint64_t;

	// Type of mid price movement:
	enum class label_type : int {
		upward		= +1,
		downward	= -1,
		stationary	= 0,
	};

	// Current time:
	time_point current_time = 0;

	// Look-ahead interval ahead of current time:
	static constexpr duration lookahead_interval = 5;

	// Lookback buffer of (midprice, features) tuple before current time:
	deque<tuple<uint64_t, features>> lookback;

public:
	void process_ob_event(helix_order_book_t ob) {
		if (helix_order_book_ask_levels(ob) < 5 && helix_order_book_bid_levels(ob) < 5) {
			return;
		}
		uint64_t midprice = helix_order_book_midprice(ob, 0);
		auto curr_features = std::move(extract(ob));
		if (lookback.empty()) {
			lookback.emplace_back(make_tuple(midprice, curr_features));
			return;
		}
		uint64_t prev_midprice = helix_order_book_midprice(ob, 0);
		features prev_features;
		tie(prev_midprice, prev_features) = lookback.front();

		if (curr_features.values == prev_features.values) {
			return;
		}
		lookback.emplace_back(make_tuple(midprice, curr_features));

		if (lookback.size() < lookahead_interval) {
			return;
		}
		lookback.pop_front();

		fmt(label(prev_midprice, midprice), prev_features);
	}
private:
	features extract(helix_order_book_t ob) {
		features result{};
		for (size_t i = 0; i < nr_levels; i++) {
			auto bid_price = helix_order_book_bid_price(ob, i) / 10000.0;
			auto ask_price = helix_order_book_ask_price(ob, i) / 10000.0;
			auto bid_size = helix_order_book_bid_size(ob, i);
			auto ask_size = helix_order_book_ask_size(ob, i);
			auto midprice = helix_order_book_midprice(ob, i) / 10000.0;
			// Price and volume (n levels):
			result.add(ask_price);
			result.add(ask_size);
			result.add(bid_price);
			result.add(bid_size);
			// Bid-ask spread and mid-prices:
			result.add(ask_price - bid_price);
			result.add(midprice);
		}
		for (size_t i = 1; i < nr_levels; i++) {
			// Price differences:
			auto bid0 = helix_order_book_midprice(ob, i-1) / 10000.0;
			auto bid1 = helix_order_book_midprice(ob, i) / 10000.0;
			auto ask0 = helix_order_book_midprice(ob, i-1) / 10000.0;
			auto ask1 = helix_order_book_midprice(ob, i) / 10000.0;
			result.add(abs(ask1-ask0));
			result.add(abs(bid1-bid0));
		}
		return result;
	}

	label_type label(uint64_t prev_midprice, uint64_t midprice) {
		if (midprice > prev_midprice) {
			return label_type::upward;
		} else if (midprice < prev_midprice) {
			return label_type::downward;
		} else {
			return label_type::stationary;
		}
	}

	void fmt(label_type label, const features& features) {
		cout << int(label);
		for (size_t i = 0; i < features.values.size(); i++) {
			cout << " " << i+1 << ":" << features.values[i];
		}
		cout << endl;
	}
};

static struct svm_session svm_session;

static uv_buf_t alloc_packet(uv_handle_t* handle, size_t suggested_size)
{
	static char rx_buffer[65536];

	return uv_buf_init(rx_buffer, sizeof(rx_buffer));
}

struct svm_fmt_ops *fmt_ops;

static void process_ob_event(helix_session_t session, helix_order_book_t ob)
{
	struct svm_session* s = reinterpret_cast<struct svm_session*>(helix_session_data(session));

	s->process_ob_event(ob);
}

static void process_trade_event(helix_session_t session, helix_trade_t trade)
{
}

static void recv_packet(uv_udp_t* handle, ssize_t nread, uv_buf_t buf, struct sockaddr* addr, unsigned flags)
{
	if (nread > 0) {
		helix_session_process_packet(reinterpret_cast<helix_session_t>(handle->data), buf.base, nread);
	}
}

static void libuv_error(const char *s)
{
	uv_err_t err;

	err = uv_last_error(uv_default_loop());

	fprintf(stderr, "error: %s: %s (%s)\n", s, uv_strerror(err), uv_err_name(err));
	exit(1);
}

static void usage(void)
{
	fprintf(stdout,
		"usage: %s [options]\n"
		"  options:\n"
		"    -s, --symbol symbol          Ticker symbol to listen to.\n"
		"    -c, --multicast-proto proto  UDP multicast protocol listen to.\n"
		"    -a, --multicast-addr addr    UDP multicast address to listen to.\n"
		"    -p, --multicast-port port    UDP multicast port to listen to.\n"
		"    -o, --output filename        Output filename.\n"
		"    -h, --help                   display this help and exit\n",
		program);
	exit(1);
}

static struct option svm_options[] = {
	{"symbol",          required_argument, 0, 's'},
	{"multicast-proto", required_argument, 0, 'c'},
	{"multicast-addr",  required_argument, 0, 'a'},
	{"multicast-port",  required_argument, 0, 'p'},
	{"output",          required_argument, 0, 'o'},
	{"help",            no_argument,       0, 'h'},
	{0, 0, 0, 0}
};

static void parse_options(struct config *cfg, int argc, char *argv[])
{
	for (;;) {
		int opt_idx = 0;
		int c;

		c = getopt_long(argc, argv, "s:c:a:o:p:h", svm_options, &opt_idx);
		if (c == -1)
			break;

		switch (c) {
		case 's':
			cfg->symbol = optarg;
			break;
		case 'c':
			cfg->multicast_proto = optarg;
			break;
		case 'a':
			cfg->multicast_addr = optarg;
			break;
		case 'o':
			cfg->output = optarg;
			break;
		case 'p':
			cfg->multicast_port = strtol(optarg, NULL, 10);
			break;
		case 'h':
			usage();
		default:
			usage();
		}
	}
}

int main(int argc, char *argv[])
{
	helix_session_t session;
	struct sockaddr_in addr;
	helix_protocol_t proto;
	struct config cfg = {};
	uv_udp_t socket;
	int err;

	program = basename(argv[0]);

	parse_options(&cfg, argc, argv);

	if (!cfg.symbol) {
		fprintf(stderr, "error: symbol is not specified. Use the '-s' option to specify it.\n");
		exit(1);
	}

	if (!cfg.multicast_proto) {
		fprintf(stderr, "error: multicast protocol is not specified. Use the '-c' option to specify it.\n");
		exit(1);
	}

	if (!cfg.multicast_addr) {
		fprintf(stderr, "error: multicast address is not specified. Use the '-a' option to specify it.\n");
		exit(1);
	}

	if (!cfg.multicast_port) {
		fprintf(stderr, "error: multicast port is not specified. Use the '-p' option to specify it.\n");
		exit(1);
	}

	if (cfg.output) {
		output = fopen(cfg.output, "w");
		if (!output) {
			fprintf(stderr, "error: %s: %s\n", cfg.output, strerror(errno));
			exit(1);
		}
	} else {
		output = stdout;
	}

	proto = helix_protocol_lookup(cfg.multicast_proto);
	if (!proto) {
		fprintf(stderr, "error: protocol '%s' is not supported\n", cfg.multicast_proto);
		exit(1);
	}

	session = helix_session_create(proto, cfg.symbol, process_ob_event, process_trade_event, &svm_session);
	if (!session) {
		fprintf(stderr, "error: unable to create new session\n");
		exit(1);
	}

	err = uv_udp_init(uv_default_loop(), &socket);
	if (err) {
		libuv_error("uv_udp_init");
	}
	socket.data = session;

	addr = uv_ip4_addr("0.0.0.0", cfg.multicast_port);

	err = uv_udp_bind(&socket, addr, 0);
	if (err) {
		libuv_error("uv_udp_bind");
	}

	err = uv_udp_set_membership(&socket, cfg.multicast_addr, NULL, UV_JOIN_GROUP);
	if (err) {
		libuv_error("uv_udp_set_membership");
	}

	err = uv_udp_recv_start(&socket, alloc_packet, recv_packet);
	if (err) {
		libuv_error("uv_udp_recv_start");
	}

	uv_run(uv_default_loop(), UV_RUN_DEFAULT);
}
<commit_msg>tools/helix-svm: Fix open-coded price level constants<commit_after>/*
 * helix-svm - Order book events for libsvm
 *
 * This is a program that listens to a market data feed and outputs order book
 * events in libsvm compatible format.
 *
 * Feature extraction and classification model is based on the following paper:
 *
 *   Kercheval, Alec N., and Yuan Zhang. "Modeling high-frequency limit order
 *   book dynamics with support vector machines." (2013).
 */

#include <helix-c/helix.h>
#include <getopt.h>
#include <libgen.h>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cerrno>
#include <cstdio>
#include <vector>
#include <deque>
#include <cmath>
#include <tuple>
#include <uv.h>

using namespace std;

static const char *program;

FILE* output;

struct config {
	const char *symbol;
	const char *multicast_proto;
	const char *multicast_addr;
	int multicast_port;
	const char *output;
};

class svm_session {
	static constexpr size_t nr_levels = 5;

	struct features {
		std::vector<double> values;

		void add(double v) {
			values.push_back(v);
		}
	};

	// Type of a time point (in number of events):
	using time_point = uint64_t;

	// Type of duration (in number of events):
	using duration = uint64_t;

	// Type of mid price movement:
	enum class label_type : int {
		upward		= +1,
		downward	= -1,
		stationary	= 0,
	};

	// Current time:
	time_point current_time = 0;

	// Look-ahead interval ahead of current time:
	static constexpr duration lookahead_interval = 5;

	// Lookback buffer of (midprice, features) tuple before current time:
	deque<tuple<uint64_t, features>> lookback;

public:
	void process_ob_event(helix_order_book_t ob) {
		if (helix_order_book_ask_levels(ob) < nr_levels && helix_order_book_bid_levels(ob) < nr_levels) {
			return;
		}
		uint64_t midprice = helix_order_book_midprice(ob, 0);
		auto curr_features = std::move(extract(ob));
		if (lookback.empty()) {
			lookback.emplace_back(make_tuple(midprice, curr_features));
			return;
		}
		uint64_t prev_midprice = helix_order_book_midprice(ob, 0);
		features prev_features;
		tie(prev_midprice, prev_features) = lookback.front();

		if (curr_features.values == prev_features.values) {
			return;
		}
		lookback.emplace_back(make_tuple(midprice, curr_features));

		if (lookback.size() < lookahead_interval) {
			return;
		}
		lookback.pop_front();

		fmt(label(prev_midprice, midprice), prev_features);
	}
private:
	features extract(helix_order_book_t ob) {
		features result{};
		for (size_t i = 0; i < nr_levels; i++) {
			auto bid_price = helix_order_book_bid_price(ob, i) / 10000.0;
			auto ask_price = helix_order_book_ask_price(ob, i) / 10000.0;
			auto bid_size = helix_order_book_bid_size(ob, i);
			auto ask_size = helix_order_book_ask_size(ob, i);
			auto midprice = helix_order_book_midprice(ob, i) / 10000.0;
			// Price and volume (n levels):
			result.add(ask_price);
			result.add(ask_size);
			result.add(bid_price);
			result.add(bid_size);
			// Bid-ask spread and mid-prices:
			result.add(ask_price - bid_price);
			result.add(midprice);
		}
		for (size_t i = 1; i < nr_levels; i++) {
			// Price differences:
			auto bid0 = helix_order_book_midprice(ob, i-1) / 10000.0;
			auto bid1 = helix_order_book_midprice(ob, i) / 10000.0;
			auto ask0 = helix_order_book_midprice(ob, i-1) / 10000.0;
			auto ask1 = helix_order_book_midprice(ob, i) / 10000.0;
			result.add(abs(ask1-ask0));
			result.add(abs(bid1-bid0));
		}
		return result;
	}

	label_type label(uint64_t prev_midprice, uint64_t midprice) {
		if (midprice > prev_midprice) {
			return label_type::upward;
		} else if (midprice < prev_midprice) {
			return label_type::downward;
		} else {
			return label_type::stationary;
		}
	}

	void fmt(label_type label, const features& features) {
		cout << int(label);
		for (size_t i = 0; i < features.values.size(); i++) {
			cout << " " << i+1 << ":" << features.values[i];
		}
		cout << endl;
	}
};

static struct svm_session svm_session;

static uv_buf_t alloc_packet(uv_handle_t* handle, size_t suggested_size)
{
	static char rx_buffer[65536];

	return uv_buf_init(rx_buffer, sizeof(rx_buffer));
}

struct svm_fmt_ops *fmt_ops;

static void process_ob_event(helix_session_t session, helix_order_book_t ob)
{
	struct svm_session* s = reinterpret_cast<struct svm_session*>(helix_session_data(session));

	s->process_ob_event(ob);
}

static void process_trade_event(helix_session_t session, helix_trade_t trade)
{
}

static void recv_packet(uv_udp_t* handle, ssize_t nread, uv_buf_t buf, struct sockaddr* addr, unsigned flags)
{
	if (nread > 0) {
		helix_session_process_packet(reinterpret_cast<helix_session_t>(handle->data), buf.base, nread);
	}
}

static void libuv_error(const char *s)
{
	uv_err_t err;

	err = uv_last_error(uv_default_loop());

	fprintf(stderr, "error: %s: %s (%s)\n", s, uv_strerror(err), uv_err_name(err));
	exit(1);
}

static void usage(void)
{
	fprintf(stdout,
		"usage: %s [options]\n"
		"  options:\n"
		"    -s, --symbol symbol          Ticker symbol to listen to.\n"
		"    -c, --multicast-proto proto  UDP multicast protocol listen to.\n"
		"    -a, --multicast-addr addr    UDP multicast address to listen to.\n"
		"    -p, --multicast-port port    UDP multicast port to listen to.\n"
		"    -o, --output filename        Output filename.\n"
		"    -h, --help                   display this help and exit\n",
		program);
	exit(1);
}

static struct option svm_options[] = {
	{"symbol",          required_argument, 0, 's'},
	{"multicast-proto", required_argument, 0, 'c'},
	{"multicast-addr",  required_argument, 0, 'a'},
	{"multicast-port",  required_argument, 0, 'p'},
	{"output",          required_argument, 0, 'o'},
	{"help",            no_argument,       0, 'h'},
	{0, 0, 0, 0}
};

static void parse_options(struct config *cfg, int argc, char *argv[])
{
	for (;;) {
		int opt_idx = 0;
		int c;

		c = getopt_long(argc, argv, "s:c:a:o:p:h", svm_options, &opt_idx);
		if (c == -1)
			break;

		switch (c) {
		case 's':
			cfg->symbol = optarg;
			break;
		case 'c':
			cfg->multicast_proto = optarg;
			break;
		case 'a':
			cfg->multicast_addr = optarg;
			break;
		case 'o':
			cfg->output = optarg;
			break;
		case 'p':
			cfg->multicast_port = strtol(optarg, NULL, 10);
			break;
		case 'h':
			usage();
		default:
			usage();
		}
	}
}

int main(int argc, char *argv[])
{
	helix_session_t session;
	struct sockaddr_in addr;
	helix_protocol_t proto;
	struct config cfg = {};
	uv_udp_t socket;
	int err;

	program = basename(argv[0]);

	parse_options(&cfg, argc, argv);

	if (!cfg.symbol) {
		fprintf(stderr, "error: symbol is not specified. Use the '-s' option to specify it.\n");
		exit(1);
	}

	if (!cfg.multicast_proto) {
		fprintf(stderr, "error: multicast protocol is not specified. Use the '-c' option to specify it.\n");
		exit(1);
	}

	if (!cfg.multicast_addr) {
		fprintf(stderr, "error: multicast address is not specified. Use the '-a' option to specify it.\n");
		exit(1);
	}

	if (!cfg.multicast_port) {
		fprintf(stderr, "error: multicast port is not specified. Use the '-p' option to specify it.\n");
		exit(1);
	}

	if (cfg.output) {
		output = fopen(cfg.output, "w");
		if (!output) {
			fprintf(stderr, "error: %s: %s\n", cfg.output, strerror(errno));
			exit(1);
		}
	} else {
		output = stdout;
	}

	proto = helix_protocol_lookup(cfg.multicast_proto);
	if (!proto) {
		fprintf(stderr, "error: protocol '%s' is not supported\n", cfg.multicast_proto);
		exit(1);
	}

	session = helix_session_create(proto, cfg.symbol, process_ob_event, process_trade_event, &svm_session);
	if (!session) {
		fprintf(stderr, "error: unable to create new session\n");
		exit(1);
	}

	err = uv_udp_init(uv_default_loop(), &socket);
	if (err) {
		libuv_error("uv_udp_init");
	}
	socket.data = session;

	addr = uv_ip4_addr("0.0.0.0", cfg.multicast_port);

	err = uv_udp_bind(&socket, addr, 0);
	if (err) {
		libuv_error("uv_udp_bind");
	}

	err = uv_udp_set_membership(&socket, cfg.multicast_addr, NULL, UV_JOIN_GROUP);
	if (err) {
		libuv_error("uv_udp_set_membership");
	}

	err = uv_udp_recv_start(&socket, alloc_packet, recv_packet);
	if (err) {
		libuv_error("uv_udp_recv_start");
	}

	uv_run(uv_default_loop(), UV_RUN_DEFAULT);
}
<|endoftext|>


<commit_before>// Copyright 2015-2020 Elviss Strazdins. All rights reserved.

#ifndef OUZEL_OUZELPROJECT_HPP
#define OUZEL_OUZELPROJECT_HPP

#include <fstream>
#include "storage/FileSystem.hpp"
#include "utils/Json.hpp"

namespace ouzel
{
    class OuzelProject final
    {
    public:
        OuzelProject(const storage::Path& path)
        {
            storage::Path directory = path.getDirectory();

            std::ifstream f(path, std::ios::binary);
            std::vector<char> data{std::istreambuf_iterator<char>(f), std::istreambuf_iterator<char>()};

            json::Data j(data);
            name = j["name"].as<std::string>();

            storage::Path assetsPath = directory + "/" + j["assetsPath"].as<std::string>();
            storage::Path resourcesPath = directory + "/Resources/";

            for (const auto& asset : j["assets"])
            {
                storage::Path assetPath(assetsPath + asset["path"].as<std::string>());

                storage::Path resourceName = asset["path"].as<std::string>();
                storage::Path resourcePath = resourcesPath + resourceName.getStem() + ".otexture";

                // TODO: check if input file exists
                // TODO: check if output file exists and is older than the input file
                // TODO: export input file to output file
            }
        }

        const std::string& getName() const noexcept { return name; }

    private:
        std::string name;
    };
}

#endif // OUZELPROJECT_HPP
<commit_msg>Use slash operator<commit_after>// Copyright 2015-2020 Elviss Strazdins. All rights reserved.

#ifndef OUZEL_OUZELPROJECT_HPP
#define OUZEL_OUZELPROJECT_HPP

#include <fstream>
#include "storage/FileSystem.hpp"
#include "utils/Json.hpp"

namespace ouzel
{
    class OuzelProject final
    {
    public:
        OuzelProject(const storage::Path& path)
        {
            storage::Path directory = path.getDirectory();

            std::ifstream f(path, std::ios::binary);
            std::vector<char> data{std::istreambuf_iterator<char>(f), std::istreambuf_iterator<char>()};

            json::Data j(data);
            name = j["name"].as<std::string>();

            storage::Path assetsPath = directory / j["assetsPath"].as<std::string>();
            storage::Path resourcesPath = directory / storage::Path("Resources");

            for (const auto& asset : j["assets"])
            {
                storage::Path assetPath(assetsPath / asset["path"].as<std::string>());

                storage::Path resourceName = asset["path"].as<std::string>();
                storage::Path resourcePath = resourcesPath / resourceName.getStem() + ".otexture";

                // TODO: check if input file exists
                // TODO: check if output file exists and is older than the input file
                // TODO: export input file to output file
            }
        }

        const std::string& getName() const noexcept { return name; }

    private:
        std::string name;
    };
}

#endif // OUZELPROJECT_HPP
<|endoftext|>


<commit_before>// main.cpp
#include "SDL.h"
#include "SDL_image.h"
#include <memory>
#include <functional>
#include <chrono>
#include <cmath>
#include <string>
#include <vector>
#include <algorithm>
#include <utility>
#include "vector2d.h"

using window_ptr   = std::unique_ptr<SDL_Window   , decltype(&SDL_DestroyWindow)>;
using renderer_ptr = std::unique_ptr<SDL_Renderer , decltype(&SDL_DestroyRenderer)>;
using texture_ptr  = std::unique_ptr<SDL_Texture  , decltype(&SDL_DestroyTexture)>;
using surface_ptr  = std::unique_ptr<SDL_Surface  , decltype(&SDL_FreeSurface)>;
using v2d          = vector2d<float>;

template<typename T, size_t N = 10>
struct window_avg {
    static const size_t SIZE = N;
    T e_[SIZE] = { 0 };
    T sum_ = { 0 };
    size_t pos_ { 0 };
    void insert(T i) {
        sum_ +=  i - e_[pos_];
        e_[pos_] = i;
        ++pos_;
        if (pos_ >= SIZE)
            pos_ = 0;
    }
    T avg() const {
        // T tmp = 0;
        // for(auto e : e_)
        //     tmp += e;
        // return tmp / SIZE;
        return sum_ / SIZE;
    }
};

texture_ptr load_texture(char const * filename, SDL_Renderer* renderer)
{
    surface_ptr surface
    (
        IMG_Load(filename)
        , &SDL_FreeSurface
    );
    if (!surface)
        throw "Cannot load surface)";
    texture_ptr texture
    (
        SDL_CreateTextureFromSurface(renderer, surface.get())
        , &SDL_DestroyTexture
    );
    return texture;
}

struct frame_source {
    SDL_Texture* texture_;
    SDL_Rect     src_rect_;
};

struct animation_cycle {
    explicit animation_cycle( unsigned cycle_duration_ms
                            , std::vector<std::string>&& filenames
                            , SDL_Renderer* renderer)
    : cycle_duration_ms_{cycle_duration_ms}
    , filenames_{std::forward<std::vector<std::string>>(filenames)}
    , texture_intervall_ms_{cycle_duration_ms / static_cast<unsigned>(filenames_.size())}
    {
        for(auto const& f: filenames_) {
            textures_.emplace_back(load_texture(f.c_str(), renderer));
        }
    }
    frame_source get_frame(unsigned milliseconds) {
        auto frame = (milliseconds / texture_intervall_ms_) % filenames_.size();
        return {
            textures_[frame].get(),
            { 0, 0, 640, 480 }
        };
    }
    std::vector<std::string> filenames_;
    std::vector<texture_ptr> textures_;
    unsigned cycle_duration_ms_;
    unsigned texture_intervall_ms_;
};

const int SCREEN_WIDTH  =1280;
const int SCREEN_HEIGHT = 800;

int main(int argc, char const *argv[])
{
    int ret = 0;
    SDL_Init(SDL_INIT_EVERYTHING);
    window_ptr window
    (
        SDL_CreateWindow("Hallo SDL", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED
         , SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_FULLSCREEN | SDL_WINDOW_OPENGL)
        , &SDL_DestroyWindow);
    renderer_ptr renderer
    (
        SDL_CreateRenderer(window.get(), -1, SDL_RENDERER_ACCELERATED)
        , &SDL_DestroyRenderer
    );
    SDL_SetRenderDrawColor(renderer.get(), 0xff, 0xff, 0xff, 0xa0);
    animation_cycle dino_walk_cycle(600, {
                            "../assets/dino/Walk (1).png"
                          , "../assets/dino/Walk (2).png"
                          , "../assets/dino/Walk (3).png"
                          , "../assets/dino/Walk (4).png"
                          , "../assets/dino/Walk (5).png"
                          , "../assets/dino/Walk (6).png"
                          , "../assets/dino/Walk (7).png"
                          , "../assets/dino/Walk (8).png"
                          , "../assets/dino/Walk (9).png"
                          , "../assets/dino/Walk (10).png"}
                          , renderer.get());
    animation_cycle dino_idle_cycle(2400, {
                            "../assets/dino/Idle (1).png"
                          , "../assets/dino/Idle (2).png"
                          , "../assets/dino/Idle (3).png"
                          , "../assets/dino/Idle (4).png"
                          , "../assets/dino/Idle (5).png"
                          , "../assets/dino/Idle (6).png"
                          , "../assets/dino/Idle (7).png"
                          , "../assets/dino/Idle (8).png"
                          , "../assets/dino/Idle (9).png"
                          , "../assets/dino/Idle (10).png"}
                          , renderer.get());
    animation_cycle dino_run_cycle(600, {
                            "../assets/dino/Run (1).png"
                          , "../assets/dino/Run (2).png"
                          , "../assets/dino/Run (3).png"
                          , "../assets/dino/Run (4).png"
                          , "../assets/dino/Run (5).png"
                          , "../assets/dino/Run (6).png"
                          , "../assets/dino/Run (7).png"
                          , "../assets/dino/Run (8).png"}
                          , renderer.get());
    animation_cycle dino_jump_cycle(1200, {
                            "../assets/dino/Jump (1).png"
                          , "../assets/dino/Jump (2).png"
                          , "../assets/dino/Jump (3).png"
                          , "../assets/dino/Jump (4).png"
                          , "../assets/dino/Jump (5).png"
                          , "../assets/dino/Jump (6).png"
                          , "../assets/dino/Jump (7).png"
                          , "../assets/dino/Jump (8).png"
                          , "../assets/dino/Jump (9).png"
                          , "../assets/dino/Jump (10).png"
                          , "../assets/dino/Jump (11).png"
                          , "../assets/dino/Jump (12).png"}
                          , renderer.get());
    texture_ptr background = load_texture("../assets/background.jpg", renderer.get());
    v2d dino_pos { 50, SCREEN_HEIGHT - 480 };
    v2d dino_speed;
    bool running { true };

    int i = 0;
    auto last_ms = SDL_GetTicks();
    auto last_clk = std::chrono::high_resolution_clock::now();
    animation_cycle* current_cycle = &dino_idle_cycle;
    animation_cycle* next_cycle    = current_cycle;

    unsigned cycle_start_ms = last_ms;
    // unsigned cycle_end_ms   = cycle_start_ms +

    window_avg<int, 100> wa;
    window_avg<float_t, 100> waf;
    while(running) {
        SDL_Event event;
        while(SDL_PollEvent(&event) == 1) {
            switch(event.type) {
                case SDL_QUIT:
                    running = false;
                    break;
                // default:
            }
        }

        // advance time and frame counters
        auto now_ms = SDL_GetTicks();
        auto diff = now_ms - last_ms;
        wa.insert(diff);
        last_ms = now_ms;
        auto now_clk = std::chrono::high_resolution_clock::now();
        std::chrono::duration<float> diff_clk = now_clk - last_clk;
        waf.insert(diff_clk.count());
        last_clk = now_clk;
        ++i;

        auto key_state = SDL_GetKeyboardState(nullptr);
        bool walking = key_state[SDL_SCANCODE_RIGHT] == 1;
        bool running = key_state[SDL_SCANCODE_LSHIFT] + key_state[SDL_SCANCODE_RSHIFT] >= 1;
        bool jumping = key_state[SDL_SCANCODE_SPACE] == 1;

        if (jumping) {
            current_cycle = &dino_jump_cycle;
            cycle_start_ms = now_ms;
        }
        else if ( walking && running) {
            current_cycle = &dino_run_cycle;
            dino_speed = { 300.0f, 0.0f };
            cycle_start_ms = now_ms;
        }
        else if (walking) {
            current_cycle = &dino_walk_cycle;
            dino_speed = { 100.0f, 0.0f };
            cycle_start_ms = now_ms;
        } else if ( current_cycle != &dino_idle_cycle) {
            current_cycle = &dino_idle_cycle;
            dino_speed = { 0.0f, 0.0f };
            cycle_start_ms = now_ms;
        }
        auto frame = current_cycle->get_frame(now_ms);
        dino_pos += dino_speed * diff_clk.count();

        SDL_RenderClear(renderer.get());
        SDL_Rect back_src { 0, 0, 1280, 720 };
        SDL_Rect back_dst { 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT };
        SDL_RenderCopy(renderer.get(), background.get(), &back_src, &back_dst);
        SDL_Rect dst {static_cast<int>(dino_pos.getX()) % SCREEN_WIDTH, SCREEN_HEIGHT - 480, 640 / 2, 480 / 2};
        SDL_RenderCopy(renderer.get(), frame.texture_, &frame.src_rect_, &dst);
        SDL_Rect br { 50 - 5, 700 - 5, 10 + static_cast<int>(40.0f * waf.avg() * 1000.0f), 50 + 10};
        SDL_BlendMode bm;
        SDL_GetRenderDrawBlendMode(renderer.get(), &bm);
        SDL_SetRenderDrawBlendMode(renderer.get(), SDL_BLENDMODE_BLEND);
        SDL_SetRenderDrawColor(renderer.get(), 0xa0, 0xa0, 0xff, 0xa0);
        SDL_RenderFillRect(renderer.get(), &br);
        SDL_SetRenderDrawColor(renderer.get(), 0xff, 0xff, 0xff, 0xa0);
        for(int nn = 0, dd = wa.avg(); nn < dd; ++nn) {
            SDL_Rect rect { 50 + 40 * static_cast<int>(nn), 700, 30 , 50 };
            SDL_RenderFillRect(renderer.get(), &rect);
        }
        SDL_SetRenderDrawBlendMode(renderer.get(), bm);
        SDL_RenderPresent(renderer.get());
        if (diff < 1000/60)
            SDL_Delay(1000/60 - diff);
    }

    // SDL_DestroyTexture(tex);
    SDL_Quit();
    return ret;
}<commit_msg>Now background moves, dino stands still<commit_after>// main.cpp
#include "SDL.h"
#include "SDL_image.h"
#include <memory>
#include <functional>
#include <chrono>
#include <cmath>
#include <string>
#include <vector>
#include <algorithm>
#include <utility>
#include "vector2d.h"

using window_ptr   = std::unique_ptr<SDL_Window   , decltype(&SDL_DestroyWindow)>;
using renderer_ptr = std::unique_ptr<SDL_Renderer , decltype(&SDL_DestroyRenderer)>;
using texture_ptr  = std::unique_ptr<SDL_Texture  , decltype(&SDL_DestroyTexture)>;
using surface_ptr  = std::unique_ptr<SDL_Surface  , decltype(&SDL_FreeSurface)>;
using v2d          = vector2d<float>;

template<typename T, size_t N = 10>
struct window_avg {
    static const size_t SIZE = N;
    T e_[SIZE] = { 0 };
    T sum_ = { 0 };
    size_t pos_ { 0 };
    void insert(T i) {
        sum_ +=  i - e_[pos_];
        e_[pos_] = i;
        ++pos_;
        if (pos_ >= SIZE)
            pos_ = 0;
    }
    T avg() const {
        // T tmp = 0;
        // for(auto e : e_)
        //     tmp += e;
        // return tmp / SIZE;
        return sum_ / SIZE;
    }
};

texture_ptr load_texture(char const * filename, SDL_Renderer* renderer)
{
    surface_ptr surface
    (
        IMG_Load(filename)
        , &SDL_FreeSurface
    );
    if (!surface)
        throw "Cannot load surface)";
    texture_ptr texture
    (
        SDL_CreateTextureFromSurface(renderer, surface.get())
        , &SDL_DestroyTexture
    );
    return texture;
}

struct frame_source {
    SDL_Texture* texture_;
    SDL_Rect     src_rect_;
};

struct animation_cycle {
    explicit animation_cycle( unsigned cycle_duration_ms
                            , std::vector<std::string>&& filenames
                            , v2d speed
                            , SDL_Renderer* renderer)
    : cycle_duration_ms_{cycle_duration_ms}
    , filenames_{std::forward<std::vector<std::string>>(filenames)}
    , texture_intervall_ms_{cycle_duration_ms / static_cast<unsigned>(filenames_.size())}
    , speed_ { speed }
    {
        for(auto const& f: filenames_) {
            textures_.emplace_back(load_texture(f.c_str(), renderer));
        }
    }
    frame_source get_frame(unsigned milliseconds) {
        auto frame = (milliseconds / texture_intervall_ms_) % filenames_.size();
        return {
            textures_[frame].get(),
            { 0, 0, 640, 480 }
        };
    }
    std::vector<std::string> filenames_;
    std::vector<texture_ptr> textures_;
    unsigned cycle_duration_ms_;
    unsigned texture_intervall_ms_;
    v2d speed_;
};

struct animation_intervall {
    animation_cycle* cycle;
    unsigned start_ms_;
    unsigned end_ms_;
    v2d speed_;
};

const int SCREEN_WIDTH  =1280;
const int SCREEN_HEIGHT = 800;

int main(int argc, char const *argv[])
{
    int ret = 0;
    SDL_Init(SDL_INIT_EVERYTHING);
    window_ptr window
    (
        SDL_CreateWindow("Hallo SDL", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED
         , SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_FULLSCREEN | SDL_WINDOW_OPENGL)
        , &SDL_DestroyWindow);
    renderer_ptr renderer
    (
        SDL_CreateRenderer(window.get(), -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC)
        , &SDL_DestroyRenderer
    );
    SDL_SetRenderDrawColor(renderer.get(), 0xff, 0xff, 0xff, 0xa0);
    animation_cycle dino_walk_cycle(600, {
                            "../assets/dino/Walk (1).png"
                          , "../assets/dino/Walk (2).png"
                          , "../assets/dino/Walk (3).png"
                          , "../assets/dino/Walk (4).png"
                          , "../assets/dino/Walk (5).png"
                          , "../assets/dino/Walk (6).png"
                          , "../assets/dino/Walk (7).png"
                          , "../assets/dino/Walk (8).png"
                          , "../assets/dino/Walk (9).png"
                          , "../assets/dino/Walk (10).png"}
                          , { 100.0f, 0.0f }
                          , renderer.get());
    animation_cycle dino_idle_cycle(2400, {
                            "../assets/dino/Idle (1).png"
                          , "../assets/dino/Idle (2).png"
                          , "../assets/dino/Idle (3).png"
                          , "../assets/dino/Idle (4).png"
                          , "../assets/dino/Idle (5).png"
                          , "../assets/dino/Idle (6).png"
                          , "../assets/dino/Idle (7).png"
                          , "../assets/dino/Idle (8).png"
                          , "../assets/dino/Idle (9).png"
                          , "../assets/dino/Idle (10).png"}
                          , { 0.0f, 0.0f }
                          , renderer.get());
    animation_cycle dino_run_cycle(600, {
                            "../assets/dino/Run (1).png"
                          , "../assets/dino/Run (2).png"
                          , "../assets/dino/Run (3).png"
                          , "../assets/dino/Run (4).png"
                          , "../assets/dino/Run (5).png"
                          , "../assets/dino/Run (6).png"
                          , "../assets/dino/Run (7).png"
                          , "../assets/dino/Run (8).png"}
                          , { 300.0f, 0.0f }
                          , renderer.get());
    animation_cycle dino_jump_cycle(1200, {
                            "../assets/dino/Jump (1).png"
                          , "../assets/dino/Jump (2).png"
                          , "../assets/dino/Jump (3).png"
                          , "../assets/dino/Jump (4).png"
                          , "../assets/dino/Jump (5).png"
                          , "../assets/dino/Jump (6).png"
                          , "../assets/dino/Jump (7).png"
                          , "../assets/dino/Jump (8).png"
                          , "../assets/dino/Jump (9).png"
                          , "../assets/dino/Jump (10).png"
                          , "../assets/dino/Jump (11).png"
                          , "../assets/dino/Jump (12).png"}
                          , { 300.0f, 0.0f }
                          , renderer.get());

    auto new_intervall = [&](unsigned start_ms, animation_cycle* ac) -> animation_intervall {
        return { ac, start_ms, start_ms + ac->cycle_duration_ms_, ac->speed_};
    };

    texture_ptr background = load_texture("../assets/background.jpg", renderer.get());
    v2d dino_pos { 50, SCREEN_HEIGHT - 480 };
    v2d dino_speed;
    bool running { true };

    int i = 0;
    auto last_ms = SDL_GetTicks();
    auto last_clk = std::chrono::high_resolution_clock::now();
    animation_intervall current_intervall = new_intervall(last_ms, &dino_idle_cycle);
    animation_cycle* next_cycle = current_intervall.cycle;

    unsigned cycle_start_ms = last_ms;
    // unsigned cycle_end_ms   = cycle_start_ms +

    window_avg<int, 100> wa;
    window_avg<float_t, 100> waf;
    while(running) {
        SDL_Event event;
        while(SDL_PollEvent(&event) == 1) {
            switch(event.type) {
                case SDL_QUIT:
                    running = false;
                    break;
                // default:
            }
        }

        // advance time and frame counters
        auto now_ms = SDL_GetTicks();
        auto diff = now_ms - last_ms;
        wa.insert(diff);
        last_ms = now_ms;
        auto now_clk = std::chrono::high_resolution_clock::now();
        std::chrono::duration<float> diff_clk = now_clk - last_clk;
        waf.insert(diff_clk.count());
        last_clk = now_clk;
        ++i;

        auto key_state = SDL_GetKeyboardState(nullptr);
        bool walking = key_state[SDL_SCANCODE_RIGHT] == 1;
        bool running = key_state[SDL_SCANCODE_LSHIFT] + key_state[SDL_SCANCODE_RSHIFT] >= 1;
        bool jumping = key_state[SDL_SCANCODE_SPACE] == 1;

        if (jumping) {
            next_cycle = &dino_jump_cycle;
        }
        else if ( walking && running) {
            next_cycle = &dino_run_cycle;
        }
        else if (walking) {
            next_cycle = &dino_walk_cycle;
        } else {
            next_cycle = &dino_idle_cycle;
        }
        if (   current_intervall.cycle == & dino_idle_cycle
             || now_ms > current_intervall.end_ms_) {
            // next animation intervall
            current_intervall = new_intervall(now_ms, next_cycle);
        }
        auto frame = current_intervall.cycle->get_frame(now_ms);
        dino_pos += current_intervall.speed_ * diff_clk.count();

        SDL_RenderClear(renderer.get());
        // SDL_Rect back_src { 0, 0, 1280, 720 };
        int back_x = static_cast<int>(dino_pos.getX()) % SCREEN_WIDTH;
        // SDL_Rect back_dst { 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT };
        SDL_Rect back_src { back_x, 0, 1280 - back_x, 720 };
        SDL_Rect back_dst { 0, 0, SCREEN_WIDTH - back_x, SCREEN_HEIGHT };
        SDL_RenderCopy(renderer.get(), background.get(), &back_src, &back_dst);
        SDL_Rect back_src2 { 0, 0, back_x, 720 };
        SDL_Rect back_dst2 { SCREEN_WIDTH - back_x, 0, back_x, SCREEN_HEIGHT };
        SDL_RenderCopy(renderer.get(), background.get(), &back_src2, &back_dst2);
        // SDL_Rect dst {static_cast<int>(dino_pos.getX()) % SCREEN_WIDTH, SCREEN_HEIGHT - 480, 640 / 2, 480 / 2};
        SDL_Rect dst {100, SCREEN_HEIGHT - 480, 640 / 2, 480 / 2};
        SDL_RenderCopy(renderer.get(), frame.texture_, &frame.src_rect_, &dst);
        SDL_Rect br { 50 - 5, 700 - 5, 10 + static_cast<int>(40.0f * waf.avg() * 1000.0f), 50 + 10};
        SDL_BlendMode bm;
        SDL_GetRenderDrawBlendMode(renderer.get(), &bm);
        SDL_SetRenderDrawBlendMode(renderer.get(), SDL_BLENDMODE_BLEND);
        SDL_SetRenderDrawColor(renderer.get(), 0xa0, 0xa0, 0xff, 0xa0);
        SDL_RenderFillRect(renderer.get(), &br);
        SDL_SetRenderDrawColor(renderer.get(), 0xff, 0xff, 0xff, 0xa0);
        for(int nn = 0, dd = wa.avg(); nn < dd; ++nn) {
            SDL_Rect rect { 50 + 40 * static_cast<int>(nn), 700, 30 , 50 };
            SDL_RenderFillRect(renderer.get(), &rect);
        }
        SDL_SetRenderDrawBlendMode(renderer.get(), bm);
        SDL_RenderPresent(renderer.get());
        if (diff < 1000/60)
            SDL_Delay(1000/60 - diff);
    }

    // SDL_DestroyTexture(tex);
    SDL_Quit();
    return ret;
}<|endoftext|>


<commit_before>#include "../../utils/utils.hpp"
#include "../../rdb/rdb.hpp"
#include <cassert>

// Sorry, this is some of the ugliest code
// that I have ever written.

// If this is not the empty string then spt-it-out
// code showing the least squares fit is dumped
// to the given file.
std::string sptpath = "lms.spt";

extern "C" void dgels_( char* trans, int* m, int* n, int* nrhs, double* a, int* lda, double* b, int* ldb, double* work, int* lwork, int* info );

// h, g, d, D
enum { NFeatures = 4 };

// A is the column-major array of features
// from the sample of instances, and b is
// a vector of target h* values.
double *A, *b;

// Acopy is a copy of A which is trashed by dgels.
// If sptpath == "" then Acopy is not used.
double *Acopy;

// Hstar is a copy of b, because dgels trashes b.
// If sptpath == "" then hstar is not used.
double *hstar;

// Filled marks the rows of A that have been
// filled.  Just used for a sanity check.
bool *filled;

// sz is the sample size.
unsigned long sz;

// t is the count of records seen so far.
// It is used by the reservoire sampling.
unsigned long t;

// setrow sets the rth row of the A matrix to h,g,d,D
static void setrow(unsigned int r, double h, double g, double d, double D);

static void dfline(std::vector<std::string>&, void*);
static void sptheader(FILE*);
static void sptpoints(FILE*);
static void sptfooter(FILE*);

int main(int argc, const char *argv[]) {
	if (argc < 2)
		fatal("usage: lms <sample size> <rdbroot> [key=value*]");

	char *end = NULL;
	sz = strtol(argv[1], &end, 10);
	if (end == argv[1])
		fatal("Invalid sample size: %s", argv[1]);

	A = new double[NFeatures * sz];
	b = new double[sz];
	if (sptpath != "") {
		Acopy = new double[NFeatures * sz];
		hstar = new double[sz];
	}

	filled = new bool[sz];
	for (unsigned int i = 0; i < sz; i++)
		filled[i] = false;

	const char *root = argv[2];
	RdbAttrs attrs = attrargs(argc-3, argv+3);

	fprintf(stderr, "sample size: %lu\n", sz);
	fprintf(stderr, "root: %s\n", root);
	fputs(attrs.string().c_str(), stderr);
	fputc('\n', stderr);

	auto paths = withattrs(root, attrs);
	for (auto p = paths.begin(); p != paths.end(); p++) {
		FILE *f = fopen(p->c_str(), "r");
		if (!f) {
			warnx(errno, "Failed to open %s for reading", p->c_str());
			continue;
		}
		dfread(f, dfline);
		fclose(f);
	}

	fprintf(stderr, "%lu solutions\n", t);

	int m = t < sz ? t : sz;
	for (unsigned int i = 0; i < (unsigned int) m; i++) {
		if (!filled[i])
			fatal("Row %u of %lu was never set", i, m);
	}
	if (m < NFeatures)
		fatal("Require at least %u solutions", NFeatures);

	FILE *sptfile = NULL;
	if (sptpath != "") {
		sptfile = fopen(sptpath.c_str(), "w");
		if (!sptfile) {
			fatalx(errno, "Failed to open %s for writing",
				sptpath.c_str());
		}
	}
	sptheader(sptfile);

	int n = NFeatures;
	int nrhs = 1;
	int lda = m;
	int ldb = m;
	int info;
	double wksz, *work;

	// Get a good work size.
	int lwork = -1;
	char trans[] = "No transpose";
	dgels_(trans, &m, &n, &nrhs, A, &lda, b, &ldb, &wksz,
		&lwork, &info);
	lwork = (int) wksz;
	fprintf(stderr, "got lwork: %d\n", lwork);

	work = new double[lwork];

	// Solve.
	dgels_(trans, &m, &n, &nrhs, A, &lda, b, &ldb, work,
		&lwork, &info);
	if (info > 0)
		fatal("info=%d", info);

	sptpoints(sptfile);
	fprintf(stderr, "h=%g\ng=%g\nd=%g\nD=%g\n", b[0], b[1], b[2], b[3]);
	printf( "%g;%g;%g;%g\n", b[0], b[1], b[2], b[3]);

	sptfooter(sptfile);

	return 0;
}

static void dfline(std::vector<std::string> &l, void*) {
	if (l[0] != "#altrow" || l[1] != "path")
		return;

	t++;
	unsigned long i = 0;
	if (t-1 < sz)
		i = t-1;
	else
		i = randgen.integer(0, t);
	if (i >= sz)
		return;

	double g = strtod(l[2].c_str(), NULL);
	double h = strtod(l[3].c_str(), NULL);
	double D = strtod(l[4].c_str(), NULL);
	double d = strtod(l[5].c_str(), NULL);
	setrow(i, h, g, d, D);

	b[i] = strtod(l[6].c_str(), NULL);
	if (hstar)
		hstar[i] = b[i];
}

static void setrow(unsigned int r, double h, double g, double d, double D) {
	assert (r + 3*sz < NFeatures*sz);
	A[r+0*sz] = h;
	A[r+1*sz] = g;
	A[r+2*sz] = d;
	A[r+3*sz] = D;
	if (Acopy) {
		Acopy[r+0*sz] = h;
		Acopy[r+1*sz] = g;
		Acopy[r+2*sz] = d;
		Acopy[r+3*sz] = D;
	}
	filled[r] = true;
}

static void sptheader(FILE *f) {
	if (!f)
		return;
	fputs("(let* (\n", f);
}

static void sptpoints(FILE *f) {
	if (!f)
		return;

	fputs("\t(points	(\n", f);
	int m = t < sz ? t : sz;
	for (unsigned int i = 0; i < (unsigned int) m; i++) {
		double htrue = hstar[i];
		double h = Acopy[i+0*sz];
		double g = Acopy[i+1*sz];
		double d = Acopy[i+2*sz];
		double D = Acopy[i+3*sz];
		double hest = h*b[0] + g*b[1] + d*b[2] + D*b[3];
		fprintf(f, "\t(%f %f)\n", htrue, hest);
	}
	fputs("\t))\n", f);
}

static void sptfooter(FILE *f) {
	if (!f)
		return;
	fputs("\t(scatter (scatter-dataset :points points))\n", f);
	fputs("\t(plot (num-by-num-plot\n", f);
	fputs("\t\t:x-label \"h*\"\n", f);
	fputs("\t\t:y-label \"hhat\"\n", f);
	fputs("\t\t:dataset scatter))\n", f);
	fputs(") (display plot))\n", f);
}<commit_msg>search: use %6.2f for lms weights.<commit_after>#include "../../utils/utils.hpp"
#include "../../rdb/rdb.hpp"
#include <cassert>

// Sorry, this is some of the ugliest code
// that I have ever written.

// If this is not the empty string then spt-it-out
// code showing the least squares fit is dumped
// to the given file.
std::string sptpath = "lms.spt";

extern "C" void dgels_( char* trans, int* m, int* n, int* nrhs, double* a, int* lda, double* b, int* ldb, double* work, int* lwork, int* info );

// h, g, d, D
enum { NFeatures = 4 };

// A is the column-major array of features
// from the sample of instances, and b is
// a vector of target h* values.
double *A, *b;

// Acopy is a copy of A which is trashed by dgels.
// If sptpath == "" then Acopy is not used.
double *Acopy;

// Hstar is a copy of b, because dgels trashes b.
// If sptpath == "" then hstar is not used.
double *hstar;

// Filled marks the rows of A that have been
// filled.  Just used for a sanity check.
bool *filled;

// sz is the sample size.
unsigned long sz;

// t is the count of records seen so far.
// It is used by the reservoire sampling.
unsigned long t;

// setrow sets the rth row of the A matrix to h,g,d,D
static void setrow(unsigned int r, double h, double g, double d, double D);

static void dfline(std::vector<std::string>&, void*);
static void sptheader(FILE*);
static void sptpoints(FILE*);
static void sptfooter(FILE*);

int main(int argc, const char *argv[]) {
	if (argc < 2)
		fatal("usage: lms <sample size> <rdbroot> [key=value*]");

	char *end = NULL;
	sz = strtol(argv[1], &end, 10);
	if (end == argv[1])
		fatal("Invalid sample size: %s", argv[1]);

	A = new double[NFeatures * sz];
	b = new double[sz];
	if (sptpath != "") {
		Acopy = new double[NFeatures * sz];
		hstar = new double[sz];
	}

	filled = new bool[sz];
	for (unsigned int i = 0; i < sz; i++)
		filled[i] = false;

	const char *root = argv[2];
	RdbAttrs attrs = attrargs(argc-3, argv+3);

	fprintf(stderr, "sample size: %lu\n", sz);
	fprintf(stderr, "root: %s\n", root);
	fputs(attrs.string().c_str(), stderr);
	fputc('\n', stderr);

	auto paths = withattrs(root, attrs);
	for (auto p = paths.begin(); p != paths.end(); p++) {
		FILE *f = fopen(p->c_str(), "r");
		if (!f) {
			warnx(errno, "Failed to open %s for reading", p->c_str());
			continue;
		}
		dfread(f, dfline);
		fclose(f);
	}

	fprintf(stderr, "%lu solutions\n", t);

	int m = t < sz ? t : sz;
	for (unsigned int i = 0; i < (unsigned int) m; i++) {
		if (!filled[i])
			fatal("Row %u of %lu was never set", i, m);
	}
	if (m < NFeatures)
		fatal("Require at least %u solutions", NFeatures);

	FILE *sptfile = NULL;
	if (sptpath != "") {
		sptfile = fopen(sptpath.c_str(), "w");
		if (!sptfile) {
			fatalx(errno, "Failed to open %s for writing",
				sptpath.c_str());
		}
	}
	sptheader(sptfile);

	int n = NFeatures;
	int nrhs = 1;
	int lda = m;
	int ldb = m;
	int info;
	double wksz, *work;

	// Get a good work size.
	int lwork = -1;
	char trans[] = "No transpose";
	dgels_(trans, &m, &n, &nrhs, A, &lda, b, &ldb, &wksz,
		&lwork, &info);
	lwork = (int) wksz;
	fprintf(stderr, "got lwork: %d\n", lwork);

	work = new double[lwork];

	// Solve.
	dgels_(trans, &m, &n, &nrhs, A, &lda, b, &ldb, work,
		&lwork, &info);
	if (info > 0)
		fatal("info=%d", info);

	sptpoints(sptfile);
	fprintf(stderr, "h=%g\ng=%g\nd=%g\nD=%g\n", b[0], b[1], b[2], b[3]);
	printf( "%6.2f;%6.2f;%6.2f;%6.2f\n", b[0], b[1], b[2], b[3]);

	sptfooter(sptfile);

	return 0;
}

static void dfline(std::vector<std::string> &l, void*) {
	if (l[0] != "#altrow" || l[1] != "path")
		return;

	t++;
	unsigned long i = 0;
	if (t-1 < sz)
		i = t-1;
	else
		i = randgen.integer(0, t);
	if (i >= sz)
		return;

	double g = strtod(l[2].c_str(), NULL);
	double h = strtod(l[3].c_str(), NULL);
	double D = strtod(l[4].c_str(), NULL);
	double d = strtod(l[5].c_str(), NULL);
	setrow(i, h, g, d, D);

	b[i] = strtod(l[6].c_str(), NULL);
	if (hstar)
		hstar[i] = b[i];
}

static void setrow(unsigned int r, double h, double g, double d, double D) {
	assert (r + 3*sz < NFeatures*sz);
	A[r+0*sz] = h;
	A[r+1*sz] = g;
	A[r+2*sz] = d;
	A[r+3*sz] = D;
	if (Acopy) {
		Acopy[r+0*sz] = h;
		Acopy[r+1*sz] = g;
		Acopy[r+2*sz] = d;
		Acopy[r+3*sz] = D;
	}
	filled[r] = true;
}

static void sptheader(FILE *f) {
	if (!f)
		return;
	fputs("(let* (\n", f);
}

static void sptpoints(FILE *f) {
	if (!f)
		return;

	fputs("\t(points	(\n", f);
	int m = t < sz ? t : sz;
	for (unsigned int i = 0; i < (unsigned int) m; i++) {
		double htrue = hstar[i];
		double h = Acopy[i+0*sz];
		double g = Acopy[i+1*sz];
		double d = Acopy[i+2*sz];
		double D = Acopy[i+3*sz];
		double hest = h*b[0] + g*b[1] + d*b[2] + D*b[3];
		fprintf(f, "\t(%f %f)\n", htrue, hest);
	}
	fputs("\t))\n", f);
}

static void sptfooter(FILE *f) {
	if (!f)
		return;
	fputs("\t(scatter (scatter-dataset :points points))\n", f);
	fputs("\t(plot (num-by-num-plot\n", f);
	fputs("\t\t:x-label \"h*\"\n", f);
	fputs("\t\t:y-label \"hhat\"\n", f);
	fputs("\t\t:dataset scatter))\n", f);
	fputs(") (display plot))\n", f);
}<|endoftext|>


<commit_before>#include "retrieval.hpp"

#include "cancel_exception.hpp"
#include "feature_offset_match.hpp"
#include "interval_set.hpp"
#include "search_index_values.hpp"
#include "search_trie.hpp"

#include "v2/mwm_context.hpp"

#include "indexer/feature.hpp"
#include "indexer/feature_algo.hpp"
#include "indexer/index.hpp"
#include "indexer/osm_editor.hpp"
#include "indexer/scales.hpp"
#include "indexer/search_string_utils.hpp"
#include "indexer/trie_reader.hpp"

#include "platform/mwm_traits.hpp"
#include "platform/mwm_version.hpp"

#include "coding/compressed_bit_vector.hpp"
#include "coding/reader_wrapper.hpp"

#include "std/algorithm.hpp"

using osm::Editor;

namespace search
{
namespace
{

unique_ptr<coding::CompressedBitVector> SortFeaturesAndBuildCBV(vector<uint64_t> && features)
{
  my::SortUnique(features);
  return coding::CompressedBitVectorBuilder::FromBitPositions(move(features));
}

bool MatchFeatureByName(FeatureType const & ft, SearchQueryParams const & params)
{
  bool matched = false;
  auto const matcher = [&](int8_t /*lang*/, string const & utf8Name) -> bool
  {
    auto const name = search::NormalizeAndSimplifyString(utf8Name);
    for (auto const & prefix : params.m_prefixTokens)
    {
      if (strings::StartsWith(name, prefix))
      {
        matched = true;
        return false;
      }
    }
    for (auto const & synonyms : params.m_tokens)
    {
      for (auto const & token : synonyms)
      {
        if (token == name)
        {
          matched = true;
          return false;
        }
      }
    }
    return true;  // To iterate all names.
  };
  ft.ForEachName(matcher);
  return matched;
}

// Retrieves from the search index corresponding to |value| all
// features matching to |params|.
template <typename TValue>
unique_ptr<coding::CompressedBitVector> RetrieveAddressFeaturesImpl(
    MwmSet::MwmId const & id, MwmValue & value, my::Cancellable const & cancellable,
    SearchQueryParams const & params)
{
  serial::CodingParams codingParams(trie::GetCodingParams(value.GetHeader().GetDefCodingParams()));
  ModelReaderPtr searchReader = value.m_cont.GetReader(SEARCH_INDEX_FILE_TAG);

  // Exclude from search all deleted/modified features and match all edited/created features separately.
  Editor & editor = Editor::Instance();
  auto const deleted = editor.GetFeaturesByStatus(id, Editor::FeatureStatus::Deleted);
  // Modified features should be re-matched again.
  auto const modified = editor.GetFeaturesByStatus(id, Editor::FeatureStatus::Modified);
  auto const filter = [&](uint32_t featureIndex) -> bool
  {
    if (binary_search(deleted.begin(), deleted.end(), featureIndex))
      return false;
    if (binary_search(modified.begin(), modified.end(), featureIndex))
      return false;
    return true;
  };

  auto const trieRoot = trie::ReadTrie<SubReaderWrapper<Reader>, ValueList<TValue>>(
      SubReaderWrapper<Reader>(searchReader.GetPtr()), SingleValueSerializer<TValue>(codingParams));

  // TODO (@y, @m): This code may be optimized in the case where
  // bit vectors are sorted in the search index.
  vector<uint64_t> features;
  uint32_t counter = 0;
  auto const collector = [&](TValue const & value)
  {
    if ((++counter & 0xFF) == 0)
      BailIfCancelled(cancellable);
    features.push_back(value.m_featureId);
  };

  MatchFeaturesInTrie(params, *trieRoot, filter, collector);

  // Match all edited/created features separately.
  auto const matcher = [&](uint32_t featureIndex)
  {
    FeatureType ft;
    VERIFY(editor.GetEditedFeature(id, featureIndex, ft), ());
    // TODO(AlexZ): Should we match by some feature's metafields too?
    if (MatchFeatureByName(ft, params))
      features.push_back(featureIndex);
  };
  for_each(modified.begin(), modified.end(), matcher);
  auto const created = editor.GetFeaturesByStatus(id, Editor::FeatureStatus::Created);
  for_each(created.begin(), created.end(), matcher);

  return SortFeaturesAndBuildCBV(move(features));
}

// Retrieves from the geometry index corresponding to handle all
// features from |coverage|.
unique_ptr<coding::CompressedBitVector> RetrieveGeometryFeaturesImpl(
    v2::MwmContext const & context, my::Cancellable const & cancellable,
    covering::IntervalsT const & coverage, int scale)
{
  uint32_t counter = 0;
  vector<uint64_t> features;

  context.ForEachIndex(coverage, scale, [&](uint64_t featureId)
  {
    if ((++counter & 0xFF) == 0)
      BailIfCancelled(cancellable);
    features.push_back(featureId);
  });

  return SortFeaturesAndBuildCBV(move(features));
}

}  // namespace

namespace v2
{
unique_ptr<coding::CompressedBitVector> RetrieveAddressFeatures(
    MwmSet::MwmId const & id, MwmValue & value, my::Cancellable const & cancellable,
    SearchQueryParams const & params)
{
  version::MwmTraits mwmTraits(value.GetMwmVersion().GetFormat());

  if (mwmTraits.GetSearchIndexFormat() ==
      version::MwmTraits::SearchIndexFormat::FeaturesWithRankAndCenter)
  {
    using TValue = FeatureWithRankAndCenter;
    return RetrieveAddressFeaturesImpl<TValue>(id, value, cancellable, params);
  }
  else if (mwmTraits.GetSearchIndexFormat() ==
           version::MwmTraits::SearchIndexFormat::CompressedBitVector)
  {
    using TValue = FeatureIndexValue;
    return RetrieveAddressFeaturesImpl<TValue>(id, value, cancellable, params);
  }
  return unique_ptr<coding::CompressedBitVector>();
}

unique_ptr<coding::CompressedBitVector> RetrieveGeometryFeatures(
    MwmContext const & context, my::Cancellable const & cancellable,
    m2::RectD const & rect, int scale)
{
  covering::IntervalsT coverage;
  v2::CoverRect(rect, scale, coverage);
  return RetrieveGeometryFeaturesImpl(context, cancellable, coverage, scale);
}

} // namespace v2
} // namespace search
<commit_msg>[search] Fixed edited features matching.<commit_after>#include "retrieval.hpp"

#include "cancel_exception.hpp"
#include "feature_offset_match.hpp"
#include "interval_set.hpp"
#include "search_index_values.hpp"
#include "search_trie.hpp"

#include "v2/mwm_context.hpp"

#include "indexer/feature.hpp"
#include "indexer/feature_algo.hpp"
#include "indexer/index.hpp"
#include "indexer/osm_editor.hpp"
#include "indexer/scales.hpp"
#include "indexer/search_delimiters.hpp"
#include "indexer/search_string_utils.hpp"
#include "indexer/trie_reader.hpp"

#include "platform/mwm_traits.hpp"
#include "platform/mwm_version.hpp"

#include "coding/compressed_bit_vector.hpp"
#include "coding/reader_wrapper.hpp"

#include "std/algorithm.hpp"

using osm::Editor;

namespace search
{
namespace
{

unique_ptr<coding::CompressedBitVector> SortFeaturesAndBuildCBV(vector<uint64_t> && features)
{
  my::SortUnique(features);
  return coding::CompressedBitVectorBuilder::FromBitPositions(move(features));
}

/// Check that any from first matches any from second.
template <class TComp, class T>
bool IsFirstMatchesSecond(vector<T> const & first, vector<T> const & second, TComp const & comp)
{
  if (second.empty())
    return true;

  for (auto const & s : second)
  {
    for (auto const & f : first)
    {
      if (comp(f, s))
        return true;
    }
  }
  return false;
}

bool MatchFeatureByName(FeatureType const & ft, SearchQueryParams const & params)
{
  using namespace strings;

  bool matched = false;
  ft.ForEachName([&](int8_t lang, string const & utf8Name)
  {
    if (utf8Name.empty() || params.m_langs.count(lang) == 0)
      return true;

    vector<UniString> nameTokens;
    SplitUniString(NormalizeAndSimplifyString(utf8Name), MakeBackInsertFunctor(nameTokens), Delimiters());

    auto const matchPrefix = [](UniString const & s1, UniString const & s2)
    {
      return StartsWith(s1, s2);
    };
    if (!IsFirstMatchesSecond(nameTokens, params.m_prefixTokens, matchPrefix))
      return true;

    for (auto const & synonyms : params.m_tokens)
    {
      if (!IsFirstMatchesSecond(nameTokens, synonyms, equal_to<UniString>()))
        return true;
    }

    matched = true;
    return false;
  });

  return matched;
}

// Retrieves from the search index corresponding to |value| all
// features matching to |params|.
template <typename TValue>
unique_ptr<coding::CompressedBitVector> RetrieveAddressFeaturesImpl(
    MwmSet::MwmId const & id, MwmValue & value, my::Cancellable const & cancellable,
    SearchQueryParams const & params)
{
  // Exclude from search all deleted/modified features and match all edited/created features separately.
  Editor & editor = Editor::Instance();

  auto const deleted = editor.GetFeaturesByStatus(id, Editor::FeatureStatus::Deleted);
  auto const modified = editor.GetFeaturesByStatus(id, Editor::FeatureStatus::Modified);
  auto const filter = [&](uint32_t featureIndex) -> bool
  {
    return (!binary_search(deleted.begin(), deleted.end(), featureIndex) &&
            !binary_search(modified.begin(), modified.end(), featureIndex));
  };

  serial::CodingParams codingParams(trie::GetCodingParams(value.GetHeader().GetDefCodingParams()));
  ModelReaderPtr searchReader = value.m_cont.GetReader(SEARCH_INDEX_FILE_TAG);

  auto const trieRoot = trie::ReadTrie<SubReaderWrapper<Reader>, ValueList<TValue>>(
      SubReaderWrapper<Reader>(searchReader.GetPtr()), SingleValueSerializer<TValue>(codingParams));

  // TODO (@y, @m): This code may be optimized in the case where
  // bit vectors are sorted in the search index.
  vector<uint64_t> features;
  uint32_t counter = 0;
  auto const collector = [&](TValue const & value)
  {
    if ((++counter & 0xFF) == 0)
      BailIfCancelled(cancellable);
    features.push_back(value.m_featureId);
  };

  MatchFeaturesInTrie(params, *trieRoot, filter, collector);

  // Match all edited/created features separately.
  auto const matcher = [&](uint32_t featureIndex)
  {
    FeatureType ft;
    VERIFY(editor.GetEditedFeature(id, featureIndex, ft), ());
    // TODO(AlexZ): Should we match by some feature's metafields too?
    if (MatchFeatureByName(ft, params))
      features.push_back(featureIndex);
  };

  for_each(modified.begin(), modified.end(), matcher);
  auto const created = editor.GetFeaturesByStatus(id, Editor::FeatureStatus::Created);
  for_each(created.begin(), created.end(), matcher);

  return SortFeaturesAndBuildCBV(move(features));
}

// Retrieves from the geometry index corresponding to handle all
// features from |coverage|.
unique_ptr<coding::CompressedBitVector> RetrieveGeometryFeaturesImpl(
    v2::MwmContext const & context, my::Cancellable const & cancellable,
    covering::IntervalsT const & coverage, int scale)
{
  uint32_t counter = 0;
  vector<uint64_t> features;

  context.ForEachIndex(coverage, scale, [&](uint64_t featureId)
  {
    if ((++counter & 0xFF) == 0)
      BailIfCancelled(cancellable);
    features.push_back(featureId);
  });

  return SortFeaturesAndBuildCBV(move(features));
}

}  // namespace

namespace v2
{
unique_ptr<coding::CompressedBitVector> RetrieveAddressFeatures(
    MwmSet::MwmId const & id, MwmValue & value, my::Cancellable const & cancellable,
    SearchQueryParams const & params)
{
  version::MwmTraits mwmTraits(value.GetMwmVersion().GetFormat());

  if (mwmTraits.GetSearchIndexFormat() ==
      version::MwmTraits::SearchIndexFormat::FeaturesWithRankAndCenter)
  {
    using TValue = FeatureWithRankAndCenter;
    return RetrieveAddressFeaturesImpl<TValue>(id, value, cancellable, params);
  }
  else if (mwmTraits.GetSearchIndexFormat() ==
           version::MwmTraits::SearchIndexFormat::CompressedBitVector)
  {
    using TValue = FeatureIndexValue;
    return RetrieveAddressFeaturesImpl<TValue>(id, value, cancellable, params);
  }
  return unique_ptr<coding::CompressedBitVector>();
}

unique_ptr<coding::CompressedBitVector> RetrieveGeometryFeatures(
    MwmContext const & context, my::Cancellable const & cancellable,
    m2::RectD const & rect, int scale)
{
  covering::IntervalsT coverage;
  v2::CoverRect(rect, scale, coverage);
  return RetrieveGeometryFeaturesImpl(context, cancellable, coverage, scale);
}

} // namespace v2
} // namespace search
<|endoftext|>


<commit_before>/*
 * Copyright(c) Sophist Solutions, Inc. 1990-2015.  All rights reserved
 */
#include    "../../StroikaPreComp.h"

#if     qPlatform_Windows
#include    <windows.h>
#include    <shellapi.h>
#include    <shlobj.h>
#elif   qPlatform_POSIX
#include    <unistd.h>
#endif

#if     qPlatform_Windows
#include    "../../Execution/Platform/Windows/Exception.h"
#include    "../../Execution/Platform/Windows/HRESULTErrorException.h"
#endif
#include    "../../Execution/ErrNoException.h"

#include    "../../IO/FileAccessException.h"
#include    "../../IO/FileBusyException.h"
#include    "../../IO/FileFormatException.h"
#include    "../../Memory/SmallStackBuffer.h"

#include    "FileUtils.h"

#include    "FileSystem.h"

using   namespace   Stroika::Foundation;
using   namespace   Stroika::Foundation::IO;
using   namespace   Stroika::Foundation::IO::FileSystem;

#if     qPlatform_Windows
using   Execution::Platform::Windows::ThrowIfFalseGetLastError;
#endif

// Comment this in to turn on aggressive noisy DbgTrace in this module
//#define   USE_NOISY_TRACE_IN_THIS_MODULE_       1

IO::FileSystem::FileSystem  IO::FileSystem::FileSystem::Default ()
{
    static  IO::FileSystem::FileSystem  sThe_;
    return sThe_;
}

bool    IO::FileSystem::FileSystem::Access (const String& fileFullPath, FileAccessMode accessMode) const
{
#if     qPlatform_Windows
    if ((accessMode & FileAccessMode::eRead) == FileAccessMode::eRead) {
        DWORD attribs = ::GetFileAttributesW (fileFullPath.c_str ());
        if (attribs == INVALID_FILE_ATTRIBUTES) {
            return false;
        }
    }
    if ((accessMode & FileAccessMode::eWrite) == FileAccessMode::eWrite) {
        DWORD attribs = ::GetFileAttributesW (fileFullPath.c_str ());
        if ((attribs == INVALID_FILE_ATTRIBUTES) or (attribs & FILE_ATTRIBUTE_READONLY)) {
            return false;
        }
    }
    return true;
#elif   qPlatform_POSIX
    // Not REALLY right - but an OK hack for now... -- LGP 2011-09-26
    //http://linux.die.net/man/2/access
    if ((accessMode & FileAccessMode::eRead) == FileAccessMode::eRead) {
        if (access (fileFullPath.AsSDKString().c_str (), R_OK) != 0) {
            return false;
        }
    }
    if ((accessMode & FileAccessMode::eWrite) == FileAccessMode::eWrite) {
        if (access (fileFullPath.AsSDKString().c_str (), W_OK) != 0) {
            return false;
        }
    }
    return true;
#else
    AssertNotImplemented ();
    return false;
#endif
}

void    IO::FileSystem::FileSystem::CheckAccess (const String& fileFullPath, FileAccessMode accessMode)
{
    // quick hack - not fully implemented - but since advsiory only - not too important...

    if (not Access (fileFullPath, accessMode)) {
        // FOR NOW - MIMIC OLD CODE - BUT FIX TO CHECK READ AND WRITE (AND BOTH) ACCESS DEPENDING ON ARGS) -- LGP 2009-08-15
        Execution::DoThrow (FileAccessException (fileFullPath, accessMode));
    }
}

void    IO::FileSystem::FileSystem::CheckFileAccess (const String& fileFullPath, bool checkCanRead, bool checkCanWrite)
{
    if (checkCanRead and checkCanWrite) {
        CheckAccess (fileFullPath, IO::FileAccessMode::eReadWrite);
    }
    else if (checkCanRead) {
        CheckAccess (fileFullPath, IO::FileAccessMode::eRead);
    }
    else if (checkCanWrite) {
        CheckAccess (fileFullPath, IO::FileAccessMode::eWrite);
    }
}

/*
 ********************************************************************************
 ************************* FileSystem::ResolveShortcut **************************
 ********************************************************************************
 */
String IO::FileSystem::FileSystem::ResolveShortcut (const String& path2FileOrShortcut)
{
#if     qPlatform_Windows
    // NB: this requires COM, and for now - I don't want the support module depending on the COM module,
    // so just allow this to fail if COM isn't initialized.
    //      -- LGP 2007-09-23
    //
    {
        SHFILEINFO   info;
        memset (&info, 0, sizeof (info));
        if (::SHGetFileInfo (path2FileOrShortcut.AsSDKString ().c_str (), 0, &info, sizeof (info), SHGFI_ATTRIBUTES) == 0)
        {
            return path2FileOrShortcut;
        }
        // not a shortcut?
        if (!(info.dwAttributes & SFGAO_LINK))
        {
            return path2FileOrShortcut;
        }
    }

    // obtain the IShellLink interface
    IShellLink*     psl =   nullptr;
    IPersistFile*   ppf =   nullptr;
    try {
        if (FAILED (::CoCreateInstance (CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_IShellLink, (LPVOID*)&psl))) {
            return path2FileOrShortcut;
        }
        if (SUCCEEDED (psl->QueryInterface (IID_IPersistFile, (LPVOID*)&ppf))) {
            if (SUCCEEDED (ppf->Load (path2FileOrShortcut.c_str (), STGM_READ))) {
                // Resolve the link, this may post UI to find the link
                if (SUCCEEDED (psl->Resolve(0, SLR_NO_UI))) {
                    TCHAR   path[MAX_PATH + 1];
                    memset (path, 0, sizeof (path));
                    if (SUCCEEDED (psl->GetPath (path, NEltsOf (path), nullptr, 0))) {
                        ppf->Release ();
                        ppf = nullptr;
                        psl->Release ();
                        psl = nullptr;
                        return String::FromSDKString (path);
                    }
                }
            }
        }
    }
    catch (...) {
        if (ppf != nullptr) {
            ppf->Release ();
        }
        if (psl != nullptr) {
            psl->Release ();
        }
        Execution::DoReThrow ();
    }
    if (ppf != nullptr) {
        ppf->Release ();
    }
    if (psl != nullptr) {
        psl->Release ();
    }
    return path2FileOrShortcut;
#else
    Memory::SmallStackBuffer<Characters::SDKChar> buf (1024);
    ssize_t n;
    while ( (n = readlink (path2FileOrShortcut.AsSDKString ().c_str (), buf, buf.GetSize ())) == buf.GetSize ()) {
        buf.GrowToSize (buf.GetSize () * 2);
    }
    if (n < 0) {
        Execution::errno_ErrorException::DoThrow (errno);
    }
    Assert (n <= buf.GetSize ());   // could leave no room for NUL-byte, but not needed
    return String::FromSDKString (SDKString (buf.begin (), buf.begin () + n));
#endif
}

/*
 ********************************************************************************
 *************************** FileSystem::GetFileSize ****************************
 ********************************************************************************
 */
FileOffset_t    IO::FileSystem::FileSystem::GetFileSize (const String& fileName)
{
#if     qPlatform_Windows
    WIN32_FILE_ATTRIBUTE_DATA   fileAttrData;
    (void)::memset (&fileAttrData, 0, sizeof (fileAttrData));
    Execution::Platform::Windows::ThrowIfFalseGetLastError (::GetFileAttributesExW (fileName.c_str (), GetFileExInfoStandard, &fileAttrData));
    return fileAttrData.nFileSizeLow + (static_cast<FileOffset_t> (fileAttrData.nFileSizeHigh) << 32);
#else
    AssertNotImplemented ();
    return 0;
#endif
}

/*
 ********************************************************************************
 ********************* FileSystem::GetFileLastModificationDate ******************
 ********************************************************************************
 */
DateTime        IO::FileSystem::FileSystem::GetFileLastModificationDate (const String& fileName)
{
#if     qPlatform_Windows
    WIN32_FILE_ATTRIBUTE_DATA   fileAttrData;
    (void)::memset (&fileAttrData, 0, sizeof (fileAttrData));
    ThrowIfFalseGetLastError (::GetFileAttributesExW (fileName.c_str (), GetFileExInfoStandard, &fileAttrData));
    return DateTime (fileAttrData.ftLastWriteTime);
#else
    AssertNotImplemented ();
    return DateTime ();
#endif
}

/*
 ********************************************************************************
 *********************** FileSystem::GetFileLastAccessDate **********************
 ********************************************************************************
 */
DateTime    IO::FileSystem::FileSystem::GetFileLastAccessDate (const String& fileName)
{
#if     qPlatform_Windows
    WIN32_FILE_ATTRIBUTE_DATA   fileAttrData;
    (void)::memset (&fileAttrData, 0, sizeof (fileAttrData));
    ThrowIfFalseGetLastError (::GetFileAttributesExW (fileName.c_str (), GetFileExInfoStandard, &fileAttrData));
    return DateTime (fileAttrData.ftLastAccessTime);
#else
    AssertNotImplemented ();
    return DateTime ();
#endif
}

<commit_msg>cosmetic<commit_after>/*
 * Copyright(c) Sophist Solutions, Inc. 1990-2015.  All rights reserved
 */
#include    "../../StroikaPreComp.h"

#if     qPlatform_Windows
#include    <windows.h>
#include    <shellapi.h>
#include    <shlobj.h>
#elif   qPlatform_POSIX
#include    <unistd.h>
#endif

#if     qPlatform_Windows
#include    "../../Execution/Platform/Windows/Exception.h"
#include    "../../Execution/Platform/Windows/HRESULTErrorException.h"
#endif
#include    "../../Execution/ErrNoException.h"

#include    "../../IO/FileAccessException.h"
#include    "../../IO/FileBusyException.h"
#include    "../../IO/FileFormatException.h"
#include    "../../Memory/SmallStackBuffer.h"

#include    "FileUtils.h"

#include    "FileSystem.h"

using   namespace   Stroika::Foundation;
using   namespace   Stroika::Foundation::IO;
using   namespace   Stroika::Foundation::IO::FileSystem;

#if     qPlatform_Windows
using   Execution::Platform::Windows::ThrowIfFalseGetLastError;
#endif

// Comment this in to turn on aggressive noisy DbgTrace in this module
//#define   USE_NOISY_TRACE_IN_THIS_MODULE_       1

IO::FileSystem::FileSystem  IO::FileSystem::FileSystem::Default ()
{
    static  IO::FileSystem::FileSystem  sThe_;
    return sThe_;
}

bool    IO::FileSystem::FileSystem::Access (const String& fileFullPath, FileAccessMode accessMode) const
{
#if     qPlatform_Windows
    if ((accessMode & FileAccessMode::eRead) == FileAccessMode::eRead) {
        DWORD attribs = ::GetFileAttributesW (fileFullPath.c_str ());
        if (attribs == INVALID_FILE_ATTRIBUTES) {
            return false;
        }
    }
    if ((accessMode & FileAccessMode::eWrite) == FileAccessMode::eWrite) {
        DWORD attribs = ::GetFileAttributesW (fileFullPath.c_str ());
        if ((attribs == INVALID_FILE_ATTRIBUTES) or (attribs & FILE_ATTRIBUTE_READONLY)) {
            return false;
        }
    }
    return true;
#elif   qPlatform_POSIX
    // Not REALLY right - but an OK hack for now... -- LGP 2011-09-26
    //http://linux.die.net/man/2/access
    if ((accessMode & FileAccessMode::eRead) == FileAccessMode::eRead) {
        if (access (fileFullPath.AsSDKString().c_str (), R_OK) != 0) {
            return false;
        }
    }
    if ((accessMode & FileAccessMode::eWrite) == FileAccessMode::eWrite) {
        if (access (fileFullPath.AsSDKString().c_str (), W_OK) != 0) {
            return false;
        }
    }
    return true;
#else
    AssertNotImplemented ();
    return false;
#endif
}

void    IO::FileSystem::FileSystem::CheckAccess (const String& fileFullPath, FileAccessMode accessMode)
{
    // quick hack - not fully implemented - but since advsiory only - not too important...

    if (not Access (fileFullPath, accessMode)) {
        // FOR NOW - MIMIC OLD CODE - BUT FIX TO CHECK READ AND WRITE (AND BOTH) ACCESS DEPENDING ON ARGS) -- LGP 2009-08-15
        Execution::DoThrow (FileAccessException (fileFullPath, accessMode));
    }
}

void    IO::FileSystem::FileSystem::CheckFileAccess (const String& fileFullPath, bool checkCanRead, bool checkCanWrite)
{
    if (checkCanRead and checkCanWrite) {
        CheckAccess (fileFullPath, IO::FileAccessMode::eReadWrite);
    }
    else if (checkCanRead) {
        CheckAccess (fileFullPath, IO::FileAccessMode::eRead);
    }
    else if (checkCanWrite) {
        CheckAccess (fileFullPath, IO::FileAccessMode::eWrite);
    }
}

/*
 ********************************************************************************
 ************************* FileSystem::ResolveShortcut **************************
 ********************************************************************************
 */
String IO::FileSystem::FileSystem::ResolveShortcut (const String& path2FileOrShortcut)
{
#if     qPlatform_Windows
    // NB: this requires COM, and for now - I don't want the support module depending on the COM module,
    // so just allow this to fail if COM isn't initialized.
    //      -- LGP 2007-09-23
    //
    {
        SHFILEINFO   info;
        memset (&info, 0, sizeof (info));
        if (::SHGetFileInfo (path2FileOrShortcut.AsSDKString ().c_str (), 0, &info, sizeof (info), SHGFI_ATTRIBUTES) == 0)
        {
            return path2FileOrShortcut;
        }
        // not a shortcut?
        if (!(info.dwAttributes & SFGAO_LINK))
        {
            return path2FileOrShortcut;
        }
    }

    // obtain the IShellLink interface
    IShellLink*     psl =   nullptr;
    IPersistFile*   ppf =   nullptr;
    try {
        if (FAILED (::CoCreateInstance (CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_IShellLink, (LPVOID*)&psl))) {
            return path2FileOrShortcut;
        }
        if (SUCCEEDED (psl->QueryInterface (IID_IPersistFile, (LPVOID*)&ppf))) {
            if (SUCCEEDED (ppf->Load (path2FileOrShortcut.c_str (), STGM_READ))) {
                // Resolve the link, this may post UI to find the link
                if (SUCCEEDED (psl->Resolve(0, SLR_NO_UI))) {
                    TCHAR   path[MAX_PATH + 1];
                    memset (path, 0, sizeof (path));
                    if (SUCCEEDED (psl->GetPath (path, NEltsOf (path), nullptr, 0))) {
                        ppf->Release ();
                        ppf = nullptr;
                        psl->Release ();
                        psl = nullptr;
                        return String::FromSDKString (path);
                    }
                }
            }
        }
    }
    catch (...) {
        if (ppf != nullptr) {
            ppf->Release ();
        }
        if (psl != nullptr) {
            psl->Release ();
        }
        Execution::DoReThrow ();
    }
    if (ppf != nullptr) {
        ppf->Release ();
    }
    if (psl != nullptr) {
        psl->Release ();
    }
    return path2FileOrShortcut;
#else
    Memory::SmallStackBuffer<Characters::SDKChar> buf (1024);
    ssize_t n;
    while ( (n = ::readlink (path2FileOrShortcut.AsSDKString ().c_str (), buf, buf.GetSize ())) == buf.GetSize ()) {
        buf.GrowToSize (buf.GetSize () * 2);
    }
    if (n < 0) {
        Execution::errno_ErrorException::DoThrow (errno);
    }
    Assert (n <= buf.GetSize ());   // could leave no room for NUL-byte, but not needed
    return String::FromSDKString (SDKString (buf.begin (), buf.begin () + n));
#endif
}

/*
 ********************************************************************************
 *************************** FileSystem::GetFileSize ****************************
 ********************************************************************************
 */
FileOffset_t    IO::FileSystem::FileSystem::GetFileSize (const String& fileName)
{
#if     qPlatform_Windows
    WIN32_FILE_ATTRIBUTE_DATA   fileAttrData;
    (void)::memset (&fileAttrData, 0, sizeof (fileAttrData));
    Execution::Platform::Windows::ThrowIfFalseGetLastError (::GetFileAttributesExW (fileName.c_str (), GetFileExInfoStandard, &fileAttrData));
    return fileAttrData.nFileSizeLow + (static_cast<FileOffset_t> (fileAttrData.nFileSizeHigh) << 32);
#else
    AssertNotImplemented ();
    return 0;
#endif
}

/*
 ********************************************************************************
 ********************* FileSystem::GetFileLastModificationDate ******************
 ********************************************************************************
 */
DateTime        IO::FileSystem::FileSystem::GetFileLastModificationDate (const String& fileName)
{
#if     qPlatform_Windows
    WIN32_FILE_ATTRIBUTE_DATA   fileAttrData;
    (void)::memset (&fileAttrData, 0, sizeof (fileAttrData));
    ThrowIfFalseGetLastError (::GetFileAttributesExW (fileName.c_str (), GetFileExInfoStandard, &fileAttrData));
    return DateTime (fileAttrData.ftLastWriteTime);
#else
    AssertNotImplemented ();
    return DateTime ();
#endif
}

/*
 ********************************************************************************
 *********************** FileSystem::GetFileLastAccessDate **********************
 ********************************************************************************
 */
DateTime    IO::FileSystem::FileSystem::GetFileLastAccessDate (const String& fileName)
{
#if     qPlatform_Windows
    WIN32_FILE_ATTRIBUTE_DATA   fileAttrData;
    (void)::memset (&fileAttrData, 0, sizeof (fileAttrData));
    ThrowIfFalseGetLastError (::GetFileAttributesExW (fileName.c_str (), GetFileExInfoStandard, &fileAttrData));
    return DateTime (fileAttrData.ftLastAccessTime);
#else
    AssertNotImplemented ();
    return DateTime ();
#endif
}

<|endoftext|>