<commit_before>/*
 * Copyright (C) 2018 ScyllaDB
 */

/*
 * This file is part of Scylla.
 *
 * Scylla is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Scylla is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Scylla.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <seastar/core/thread.hh>
#include <seastar/testing/test_case.hh>
#include <seastar/testing/thread_test_case.hh>
#include <seastar/util/bool_class.hh>

#include "mutation_fragment.hh"
#include "test/lib/mutation_source_test.hh"
#include "flat_mutation_reader.hh"
#include "mutation_writer/multishard_writer.hh"
#include "mutation_writer/timestamp_based_splitting_writer.hh"
#include "test/lib/cql_test_env.hh"
#include "test/lib/mutation_assertions.hh"
#include "test/lib/random_utils.hh"
#include "test/lib/random_schema.hh"

using namespace mutation_writer;

logging::logger tlog("mutation_writer_test");

struct generate_error_tag { };
using generate_error = bool_class<generate_error_tag>;

constexpr unsigned many_partitions() {
    return
#ifndef SEASTAR_DEBUG
	300
#else
	10
#endif
	;
}

SEASTAR_TEST_CASE(test_multishard_writer) {
    return do_with_cql_env_thread([] (cql_test_env& e) {
        auto test_random_streams = [] (random_mutation_generator&& gen, size_t partition_nr, generate_error error = generate_error::no) {
            for (auto i = 0; i < 3; i++) {
                auto muts = gen(partition_nr);
                std::vector<size_t> shards_before(smp::count, 0);
                std::vector<size_t> shards_after(smp::count, 0);

                for (auto& m : muts) {
                    auto shard = dht::global_partitioner().shard_of(m.token());
                    shards_before[shard]++;
                }
                schema_ptr s = gen.schema();
                auto source_reader = partition_nr > 0 ? flat_mutation_reader_from_mutations(muts) : make_empty_flat_reader(s);
                size_t partitions_received = distribute_reader_and_consume_on_shards(s,
                    std::move(source_reader),
                    [&shards_after, error] (flat_mutation_reader reader) mutable {
                        if (error) {
                            return make_exception_future<>(std::runtime_error("Failed to write"));
                        }
                        return repeat([&shards_after, reader = std::move(reader), error] () mutable {
                            return reader(db::no_timeout).then([&shards_after, error] (mutation_fragment_opt mf_opt) mutable {
                                if (mf_opt) {
                                    if (mf_opt->is_partition_start()) {
                                        auto shard = dht::global_partitioner().shard_of(mf_opt->as_partition_start().key().token());
                                        BOOST_REQUIRE_EQUAL(shard, engine().cpu_id());
                                        shards_after[shard]++;
                                    }
                                    return make_ready_future<stop_iteration>(stop_iteration::no);
                                } else {
                                    return make_ready_future<stop_iteration>(stop_iteration::yes);
                                }
                            });
                        });
                    }
                ).get0();
                BOOST_REQUIRE_EQUAL(partitions_received, partition_nr);
                BOOST_REQUIRE_EQUAL(shards_after, shards_before);
            }
        };

        test_random_streams(random_mutation_generator(random_mutation_generator::generate_counters::no, local_shard_only::no), 0);
        test_random_streams(random_mutation_generator(random_mutation_generator::generate_counters::yes, local_shard_only::no), 0);

        test_random_streams(random_mutation_generator(random_mutation_generator::generate_counters::no, local_shard_only::no), 1);
        test_random_streams(random_mutation_generator(random_mutation_generator::generate_counters::yes, local_shard_only::no), 1);

        test_random_streams(random_mutation_generator(random_mutation_generator::generate_counters::no, local_shard_only::no), many_partitions());
        test_random_streams(random_mutation_generator(random_mutation_generator::generate_counters::yes, local_shard_only::no), many_partitions());

        try {
            test_random_streams(random_mutation_generator(random_mutation_generator::generate_counters::no, local_shard_only::no), many_partitions(), generate_error::yes);
            BOOST_ASSERT(false);
        } catch (...) {
        }

        try {
            test_random_streams(random_mutation_generator(random_mutation_generator::generate_counters::yes, local_shard_only::no), many_partitions(), generate_error::yes);
            BOOST_ASSERT(false);
        } catch (...) {
        }
    });
}

namespace {

class bucket_writer {
    schema_ptr _schema;
    classify_by_timestamp _classify;
    std::unordered_map<int64_t, std::vector<mutation>>& _buckets;

    std::optional<int64_t> _bucket_id;
    mutation_opt _current_mutation;
    bool _is_first_mutation = true;

private:
    void check_timestamp(api::timestamp_type ts) {
        const auto bucket_id = _classify(ts);
        if (_bucket_id) {
            BOOST_REQUIRE_EQUAL(bucket_id, *_bucket_id);
        } else {
            _bucket_id = bucket_id;
        }
    }
    void verify_column_bucket_id(const atomic_cell_or_collection& cell, const column_definition& cdef) {
        if (cdef.is_atomic()) {
            check_timestamp(cell.as_atomic_cell(cdef).timestamp());
        } else if (cdef.type->is_collection() || cdef.type->is_user_type()) {
            cell.as_collection_mutation().with_deserialized(*cdef.type, [this] (collection_mutation_view_description mv) {
                for (const auto& c: mv.cells) {
                    check_timestamp(c.second.timestamp());
                }
            });
        } else {
            BOOST_FAIL(fmt::format("Failed to verify column bucket id: column {} is of unknown type {}", cdef.name_as_text(), cdef.type->name()));
        }
    }
    void verify_row_bucket_id(const row& r, column_kind kind) {
        r.for_each_cell([this, kind] (column_id id, const atomic_cell_or_collection& cell) {
            verify_column_bucket_id(cell, _schema->column_at(kind, id));
        });
    }
    void verify_partition_tombstone(tombstone tomb) {
        if (tomb) {
            check_timestamp(tomb.timestamp);
        }
    }
    void verify_static_row(const static_row& sr) {
        verify_row_bucket_id(sr.cells(), column_kind::static_column);
    }
    void verify_clustering_row(const clustering_row& cr) {
        if (!cr.marker().is_missing()) {
            check_timestamp(cr.marker().timestamp());
        }
        if (cr.tomb()) {
            check_timestamp(cr.tomb().tomb().timestamp);
        }
        verify_row_bucket_id(cr.cells(), column_kind::regular_column);
    }
    void verify_range_tombstone(const range_tombstone& rt) {
        check_timestamp(rt.tomb.timestamp);
    }

public:
    bucket_writer(schema_ptr schema, classify_by_timestamp classify, std::unordered_map<int64_t, std::vector<mutation>>& buckets)
        : _schema(std::move(schema))
        , _classify(std::move(classify))
        , _buckets(buckets) {
    }
    void consume_new_partition(const dht::decorated_key& dk) {
        BOOST_REQUIRE(!_current_mutation);
        _current_mutation = mutation(_schema, dk);
    }
    void consume(tombstone partition_tombstone) {
        BOOST_REQUIRE(_current_mutation);
        verify_partition_tombstone(partition_tombstone);
        _current_mutation->partition().apply(partition_tombstone);
    }
    stop_iteration consume(static_row&& sr) {
        BOOST_REQUIRE(_current_mutation);
        verify_static_row(sr);
        _current_mutation->apply(std::move(sr));
        return stop_iteration::no;
    }
    stop_iteration consume(clustering_row&& cr) {
        BOOST_REQUIRE(_current_mutation);
        verify_clustering_row(cr);
        _current_mutation->apply(std::move(cr));
        return stop_iteration::no;
    }
    stop_iteration consume(range_tombstone&& rt) {
        BOOST_REQUIRE(_current_mutation);
        verify_range_tombstone(rt);
        _current_mutation->apply(std::move(rt));
        return stop_iteration::no;
    }
    stop_iteration consume_end_of_partition() {
        BOOST_REQUIRE(_current_mutation);
        BOOST_REQUIRE(_bucket_id);
        auto& bucket = _buckets[*_bucket_id];

        if (_is_first_mutation) {
            BOOST_REQUIRE(bucket.empty());
            _is_first_mutation = false;
        }

        bucket.emplace_back(std::move(*_current_mutation));
        _current_mutation = std::nullopt;
        return stop_iteration::no;
    }
    void consume_end_of_stream() {
        BOOST_REQUIRE(!_current_mutation);
    }
};

} // anonymous namespace

SEASTAR_THREAD_TEST_CASE(test_timestamp_based_splitting_mutation_writer) {
    auto random_spec = tests::make_random_schema_specification(
            get_name(),
            std::uniform_int_distribution<size_t>(1, 4),
            std::uniform_int_distribution<size_t>(2, 4),
            std::uniform_int_distribution<size_t>(2, 8),
            std::uniform_int_distribution<size_t>(2, 8));
    auto random_schema = tests::random_schema{tests::random::get_int<uint32_t>(), *random_spec};

    tlog.info("Random schema:\n{}", random_schema.cql());

    auto ts_gen = [&, underlying = tests::default_timestamp_generator()] (std::mt19937& engine,
            tests::timestamp_destination ts_dest, api::timestamp_type min_timestamp) -> api::timestamp_type {
        if (ts_dest == tests::timestamp_destination::partition_tombstone ||
                ts_dest == tests::timestamp_destination::row_marker ||
                ts_dest == tests::timestamp_destination::row_tombstone ||
                ts_dest == tests::timestamp_destination::collection_tombstone) {
            if (tests::random::get_int<int>(0, 10, engine)) {
                return api::missing_timestamp;
            }
        }
        return underlying(engine, ts_dest, min_timestamp);
    };

    auto muts = tests::generate_random_mutations(random_schema, ts_gen).get0();

    auto classify_fn = [] (api::timestamp_type ts) {
        return int64_t(ts % 2);
    };

    std::unordered_map<int64_t, std::vector<mutation>> buckets;

    auto consumer = [&] (flat_mutation_reader bucket_reader) {
        return do_with(std::move(bucket_reader), [&] (flat_mutation_reader& rd) {
            return rd.consume(bucket_writer(random_schema.schema(), classify_fn, buckets), db::no_timeout);
        });
    };

    segregate_by_timestamp(flat_mutation_reader_from_mutations(muts), classify_fn, std::move(consumer)).get();

    tlog.debug("Data split into {} buckets: {}", buckets.size(), boost::copy_range<std::vector<int64_t>>(buckets | boost::adaptors::map_keys));

    auto bucket_readers = boost::copy_range<std::vector<flat_mutation_reader>>(buckets | boost::adaptors::map_values |
            boost::adaptors::transformed([] (std::vector<mutation> muts) { return flat_mutation_reader_from_mutations(std::move(muts)); }));
    auto reader = make_combined_reader(random_schema.schema(), std::move(bucket_readers), streamed_mutation::forwarding::no,
            mutation_reader::forwarding::no);

    const auto now = gc_clock::now();
    for (auto& m : muts) {
        m.partition().compact_for_compaction(*random_schema.schema(), always_gc, now);
    }

    std::vector<mutation> combined_mutations;
    while (auto m = read_mutation_from_flat_mutation_reader(reader, db::no_timeout).get0()) {
        m->partition().compact_for_compaction(*random_schema.schema(), always_gc, now);
        combined_mutations.emplace_back(std::move(*m));
    }

    BOOST_REQUIRE_EQUAL(combined_mutations.size(), muts.size());
    for (size_t i = 0; i < muts.size(); ++i) {
        tlog.debug("Comparing mutation #{}", i);
        assert_that(combined_mutations[i]).is_equal_to(muts[i]);
    }

}
<commit_msg>mutation_writer_test: stop calling global_partitioner()<commit_after>/*
 * Copyright (C) 2018 ScyllaDB
 */

/*
 * This file is part of Scylla.
 *
 * Scylla is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Scylla is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Scylla.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <seastar/core/thread.hh>
#include <seastar/testing/test_case.hh>
#include <seastar/testing/thread_test_case.hh>
#include <seastar/util/bool_class.hh>

#include "mutation_fragment.hh"
#include "test/lib/mutation_source_test.hh"
#include "flat_mutation_reader.hh"
#include "mutation_writer/multishard_writer.hh"
#include "mutation_writer/timestamp_based_splitting_writer.hh"
#include "test/lib/cql_test_env.hh"
#include "test/lib/mutation_assertions.hh"
#include "test/lib/random_utils.hh"
#include "test/lib/random_schema.hh"

using namespace mutation_writer;

logging::logger tlog("mutation_writer_test");

struct generate_error_tag { };
using generate_error = bool_class<generate_error_tag>;

constexpr unsigned many_partitions() {
    return
#ifndef SEASTAR_DEBUG
	300
#else
	10
#endif
	;
}

SEASTAR_TEST_CASE(test_multishard_writer) {
    return do_with_cql_env_thread([] (cql_test_env& e) {
        auto test_random_streams = [] (random_mutation_generator&& gen, size_t partition_nr, generate_error error = generate_error::no) {
            for (auto i = 0; i < 3; i++) {
                auto muts = gen(partition_nr);
                std::vector<size_t> shards_before(smp::count, 0);
                std::vector<size_t> shards_after(smp::count, 0);
                schema_ptr s = gen.schema();

                for (auto& m : muts) {
                    auto shard = s->get_partitioner().shard_of(m.token());
                    shards_before[shard]++;
                }
                auto source_reader = partition_nr > 0 ? flat_mutation_reader_from_mutations(muts) : make_empty_flat_reader(s);
                auto& partitioner = s->get_partitioner();
                size_t partitions_received = distribute_reader_and_consume_on_shards(s,
                    std::move(source_reader),
                    [&partitioner, &shards_after, error] (flat_mutation_reader reader) mutable {
                        if (error) {
                            return make_exception_future<>(std::runtime_error("Failed to write"));
                        }
                        return repeat([&partitioner, &shards_after, reader = std::move(reader), error] () mutable {
                            return reader(db::no_timeout).then([&partitioner, &shards_after, error] (mutation_fragment_opt mf_opt) mutable {
                                if (mf_opt) {
                                    if (mf_opt->is_partition_start()) {
                                        auto shard = partitioner.shard_of(mf_opt->as_partition_start().key().token());
                                        BOOST_REQUIRE_EQUAL(shard, engine().cpu_id());
                                        shards_after[shard]++;
                                    }
                                    return make_ready_future<stop_iteration>(stop_iteration::no);
                                } else {
                                    return make_ready_future<stop_iteration>(stop_iteration::yes);
                                }
                            });
                        });
                    }
                ).get0();
                BOOST_REQUIRE_EQUAL(partitions_received, partition_nr);
                BOOST_REQUIRE_EQUAL(shards_after, shards_before);
            }
        };

        test_random_streams(random_mutation_generator(random_mutation_generator::generate_counters::no, local_shard_only::no), 0);
        test_random_streams(random_mutation_generator(random_mutation_generator::generate_counters::yes, local_shard_only::no), 0);

        test_random_streams(random_mutation_generator(random_mutation_generator::generate_counters::no, local_shard_only::no), 1);
        test_random_streams(random_mutation_generator(random_mutation_generator::generate_counters::yes, local_shard_only::no), 1);

        test_random_streams(random_mutation_generator(random_mutation_generator::generate_counters::no, local_shard_only::no), many_partitions());
        test_random_streams(random_mutation_generator(random_mutation_generator::generate_counters::yes, local_shard_only::no), many_partitions());

        try {
            test_random_streams(random_mutation_generator(random_mutation_generator::generate_counters::no, local_shard_only::no), many_partitions(), generate_error::yes);
            BOOST_ASSERT(false);
        } catch (...) {
        }

        try {
            test_random_streams(random_mutation_generator(random_mutation_generator::generate_counters::yes, local_shard_only::no), many_partitions(), generate_error::yes);
            BOOST_ASSERT(false);
        } catch (...) {
        }
    });
}

namespace {

class bucket_writer {
    schema_ptr _schema;
    classify_by_timestamp _classify;
    std::unordered_map<int64_t, std::vector<mutation>>& _buckets;

    std::optional<int64_t> _bucket_id;
    mutation_opt _current_mutation;
    bool _is_first_mutation = true;

private:
    void check_timestamp(api::timestamp_type ts) {
        const auto bucket_id = _classify(ts);
        if (_bucket_id) {
            BOOST_REQUIRE_EQUAL(bucket_id, *_bucket_id);
        } else {
            _bucket_id = bucket_id;
        }
    }
    void verify_column_bucket_id(const atomic_cell_or_collection& cell, const column_definition& cdef) {
        if (cdef.is_atomic()) {
            check_timestamp(cell.as_atomic_cell(cdef).timestamp());
        } else if (cdef.type->is_collection() || cdef.type->is_user_type()) {
            cell.as_collection_mutation().with_deserialized(*cdef.type, [this] (collection_mutation_view_description mv) {
                for (const auto& c: mv.cells) {
                    check_timestamp(c.second.timestamp());
                }
            });
        } else {
            BOOST_FAIL(fmt::format("Failed to verify column bucket id: column {} is of unknown type {}", cdef.name_as_text(), cdef.type->name()));
        }
    }
    void verify_row_bucket_id(const row& r, column_kind kind) {
        r.for_each_cell([this, kind] (column_id id, const atomic_cell_or_collection& cell) {
            verify_column_bucket_id(cell, _schema->column_at(kind, id));
        });
    }
    void verify_partition_tombstone(tombstone tomb) {
        if (tomb) {
            check_timestamp(tomb.timestamp);
        }
    }
    void verify_static_row(const static_row& sr) {
        verify_row_bucket_id(sr.cells(), column_kind::static_column);
    }
    void verify_clustering_row(const clustering_row& cr) {
        if (!cr.marker().is_missing()) {
            check_timestamp(cr.marker().timestamp());
        }
        if (cr.tomb()) {
            check_timestamp(cr.tomb().tomb().timestamp);
        }
        verify_row_bucket_id(cr.cells(), column_kind::regular_column);
    }
    void verify_range_tombstone(const range_tombstone& rt) {
        check_timestamp(rt.tomb.timestamp);
    }

public:
    bucket_writer(schema_ptr schema, classify_by_timestamp classify, std::unordered_map<int64_t, std::vector<mutation>>& buckets)
        : _schema(std::move(schema))
        , _classify(std::move(classify))
        , _buckets(buckets) {
    }
    void consume_new_partition(const dht::decorated_key& dk) {
        BOOST_REQUIRE(!_current_mutation);
        _current_mutation = mutation(_schema, dk);
    }
    void consume(tombstone partition_tombstone) {
        BOOST_REQUIRE(_current_mutation);
        verify_partition_tombstone(partition_tombstone);
        _current_mutation->partition().apply(partition_tombstone);
    }
    stop_iteration consume(static_row&& sr) {
        BOOST_REQUIRE(_current_mutation);
        verify_static_row(sr);
        _current_mutation->apply(std::move(sr));
        return stop_iteration::no;
    }
    stop_iteration consume(clustering_row&& cr) {
        BOOST_REQUIRE(_current_mutation);
        verify_clustering_row(cr);
        _current_mutation->apply(std::move(cr));
        return stop_iteration::no;
    }
    stop_iteration consume(range_tombstone&& rt) {
        BOOST_REQUIRE(_current_mutation);
        verify_range_tombstone(rt);
        _current_mutation->apply(std::move(rt));
        return stop_iteration::no;
    }
    stop_iteration consume_end_of_partition() {
        BOOST_REQUIRE(_current_mutation);
        BOOST_REQUIRE(_bucket_id);
        auto& bucket = _buckets[*_bucket_id];

        if (_is_first_mutation) {
            BOOST_REQUIRE(bucket.empty());
            _is_first_mutation = false;
        }

        bucket.emplace_back(std::move(*_current_mutation));
        _current_mutation = std::nullopt;
        return stop_iteration::no;
    }
    void consume_end_of_stream() {
        BOOST_REQUIRE(!_current_mutation);
    }
};

} // anonymous namespace

SEASTAR_THREAD_TEST_CASE(test_timestamp_based_splitting_mutation_writer) {
    auto random_spec = tests::make_random_schema_specification(
            get_name(),
            std::uniform_int_distribution<size_t>(1, 4),
            std::uniform_int_distribution<size_t>(2, 4),
            std::uniform_int_distribution<size_t>(2, 8),
            std::uniform_int_distribution<size_t>(2, 8));
    auto random_schema = tests::random_schema{tests::random::get_int<uint32_t>(), *random_spec};

    tlog.info("Random schema:\n{}", random_schema.cql());

    auto ts_gen = [&, underlying = tests::default_timestamp_generator()] (std::mt19937& engine,
            tests::timestamp_destination ts_dest, api::timestamp_type min_timestamp) -> api::timestamp_type {
        if (ts_dest == tests::timestamp_destination::partition_tombstone ||
                ts_dest == tests::timestamp_destination::row_marker ||
                ts_dest == tests::timestamp_destination::row_tombstone ||
                ts_dest == tests::timestamp_destination::collection_tombstone) {
            if (tests::random::get_int<int>(0, 10, engine)) {
                return api::missing_timestamp;
            }
        }
        return underlying(engine, ts_dest, min_timestamp);
    };

    auto muts = tests::generate_random_mutations(random_schema, ts_gen).get0();

    auto classify_fn = [] (api::timestamp_type ts) {
        return int64_t(ts % 2);
    };

    std::unordered_map<int64_t, std::vector<mutation>> buckets;

    auto consumer = [&] (flat_mutation_reader bucket_reader) {
        return do_with(std::move(bucket_reader), [&] (flat_mutation_reader& rd) {
            return rd.consume(bucket_writer(random_schema.schema(), classify_fn, buckets), db::no_timeout);
        });
    };

    segregate_by_timestamp(flat_mutation_reader_from_mutations(muts), classify_fn, std::move(consumer)).get();

    tlog.debug("Data split into {} buckets: {}", buckets.size(), boost::copy_range<std::vector<int64_t>>(buckets | boost::adaptors::map_keys));

    auto bucket_readers = boost::copy_range<std::vector<flat_mutation_reader>>(buckets | boost::adaptors::map_values |
            boost::adaptors::transformed([] (std::vector<mutation> muts) { return flat_mutation_reader_from_mutations(std::move(muts)); }));
    auto reader = make_combined_reader(random_schema.schema(), std::move(bucket_readers), streamed_mutation::forwarding::no,
            mutation_reader::forwarding::no);

    const auto now = gc_clock::now();
    for (auto& m : muts) {
        m.partition().compact_for_compaction(*random_schema.schema(), always_gc, now);
    }

    std::vector<mutation> combined_mutations;
    while (auto m = read_mutation_from_flat_mutation_reader(reader, db::no_timeout).get0()) {
        m->partition().compact_for_compaction(*random_schema.schema(), always_gc, now);
        combined_mutations.emplace_back(std::move(*m));
    }

    BOOST_REQUIRE_EQUAL(combined_mutations.size(), muts.size());
    for (size_t i = 0; i < muts.size(); ++i) {
        tlog.debug("Comparing mutation #{}", i);
        assert_that(combined_mutations[i]).is_equal_to(muts[i]);
    }

}
<|endoftext|>


<commit_before>#include <primitives/version_range.h>

#include <range_parser.h>

#include <fmt/format.h>
#include <primitives/constants.h>
#include <primitives/exceptions.h>
#include <primitives/hash_combine.h>
#include <primitives/string.h>

#include <pystring.h>

#include <algorithm>
#include <map>

const primitives::version::Version::Level minimum_level = 3;

namespace primitives::version
{

bool detail::RangePair::Side::operator<(const Version &rhs) const
{
    if (strong_relation)
        return v < rhs;
    else
        return v <= rhs;
}

bool detail::RangePair::Side::operator>(const Version &rhs) const
{
    if (strong_relation)
        return v > rhs;
    else
        return v >= rhs;
}

std::string detail::RangePair::Side::toString(VersionRangePairStringRepresentationType t) const
{
    switch (t)
    {
    case VersionRangePairStringRepresentationType::SameDefaultLevel:
        return v.toString(v.getDefaultLevel());
        break;
    case VersionRangePairStringRepresentationType::SameRealLevel:
        return v.toString(v.getRealLevel());
        break;
    case VersionRangePairStringRepresentationType::IndividualRealLevel:
        return v.toString(v.getRealLevel());
    }
    SW_UNREACHABLE;
}

detail::RangePair::RangePair(const Version &v1, bool strong_relation1, const Version &v2, bool strong_relation2)
    : first{ v1, strong_relation1 }, second{ v2, strong_relation2 }
{
    if (getFirst() > getSecond())
        throw SW_RUNTIME_ERROR("Left version must be <= than right: " + toStringInterval());
    if (getFirst() == getSecond() && (strong_relation1 || strong_relation2))
        throw SW_RUNTIME_ERROR("Pair does not contain any versions: " + toStringInterval());
}

detail::RangePair::RangePair(const Side &l, const Side &r)
    : first(l), second(r)
{
}

bool detail::RangePair::contains(const Version &v) const
{
    // TODO: rename < and > here
    return first < v && second > v;
}

std::optional<detail::RangePair> detail::RangePair::operator&(const detail::RangePair &rhs) const
{
    auto &f = std::max(first, rhs.first);
    auto &s = std::min(second, rhs.second);
    if (0
        || f.v < s.v
        || f.v == s.v && (!f.strong_relation && !s.strong_relation)
        )
        return detail::RangePair(f, s);
    return {};
}

std::optional<detail::RangePair> detail::RangePair::operator|(const detail::RangePair &rhs) const
{
    // find intersection first
    auto &f = std::max(first, rhs.first);
    auto &s = std::min(second, rhs.second);
    if (0
        || f.v < s.v
        || f.v == s.v && (!f.strong_relation || !s.strong_relation)
        )
    {
        // now make a union
        auto &fmin = std::min(first, rhs.first);
        auto &smax = std::max(second, rhs.second);
        return detail::RangePair(fmin, smax);
    }
    return {};
}

std::string detail::RangePair::toString(VersionRangePairStringRepresentationType t) const
{
    /*
    auto level = std::max(getFirst().getLevel(), getSecond().getLevel());
    if (t == VersionRangePairStringRepresentationType::SameRealLevel)
        level = std::max(getFirst().getRealLevel(), getSecond().getRealLevel());
    if (getFirst() == getSecond())
    {
        if (t == VersionRangePairStringRepresentationType::IndividualRealLevel)
            return getFirst().toString(getFirst().getRealLevel());
        else
            return getFirst().toString(level);
    }
    std::string s;
    if (getFirst() != Version::min(getFirst()))
    {
        s += ">=";
        if (t == VersionRangePairStringRepresentationType::IndividualRealLevel)
            s += getFirst().toString(getFirst().getRealLevel());
        else
            s += getFirst().toString(level);
    }
    // reconsider?
    // probably wrong now: 1.MAX.3
    if (std::any_of(getSecond().numbers.begin(), getSecond().numbers.end(),
        [](const auto &n) {return n == Version::maxNumber(); }))
    {
        if (getSecond() == Version::max(getSecond()))
        {
            if (s.empty())
                return "*";
            return s;
        }
        if (getSecond().getPatch() == Version::maxNumber() ||
            getSecond().getTweak() == Version::maxNumber())
        {
            if (!s.empty())
                s += " ";
            if (t == VersionRangePairStringRepresentationType::IndividualRealLevel)
            {
                auto nv = getSecond().getNextVersion(getSecond().getRealLevel());
                return s + "<" + nv.toString(nv.getRealLevel());
            }
            auto nv = getSecond().getNextVersion(level);
            if (t == VersionRangePairStringRepresentationType::SameRealLevel)
            {
                level = std::max(getFirst().getRealLevel(), nv.getRealLevel());
                s.clear();
                if (getFirst() != Version::min(getFirst()))
                {
                    s += ">=";
                    s += getFirst().toString(level);
                    if (!s.empty())
                        s += " ";
                }
            }
            return s + "<" + nv.toString(level);
        }
    }
    if (!s.empty())
        s += " ";
    if (t == VersionRangePairStringRepresentationType::IndividualRealLevel)
        return s + "<=" + getSecond().toString(getSecond().getRealLevel());
    else
        return s + "<=" + getSecond().toString(level);*/

    bool print_left = first.v > Version::min();
    bool print_right = second.v < Version::max();

    std::string s;
    if (print_left)
    {
        s += ">";
        if (!first.strong_relation)
            s += "=";
        s += first.toString(t);
    }
    if (print_left && print_right)
        s += " ";
    if (print_right)
    {
        s += "<";
        if (!second.strong_relation)
            s += "=";
        s += second.toString(t);
    }
    if (s.empty())
        return "*";
    return s;
}

std::string detail::RangePair::toStringInterval() const
{
    std::string s;
    if (first.strong_relation)
        s += "(";
    else
        s += "[";
    s += first.toString(VersionRangePairStringRepresentationType::SameDefaultLevel);
    s += ", ";
    s += second.toString(VersionRangePairStringRepresentationType::SameDefaultLevel);
    if (second.strong_relation)
        s += ")";
    else
        s += "]";
    return s;
}

std::optional<Version> detail::RangePair::toVersion() const
{
    if (getFirst() != getSecond())
        return {};
    return getFirst();
}

size_t detail::RangePair::getHash() const
{
    size_t h = 0;
    hash_combine(h, getFirst().getHash());
    hash_combine(h, getSecond().getHash());
    return h;
}

VersionRange::VersionRange()
{
}

/*VersionRange::VersionRange(const Version &v1, const Version &v2)
{
    range.emplace_back(v1, false, v2, false);
}*/

VersionRange::VersionRange(const char *v)
{
    if (!v)
        throw SW_RUNTIME_ERROR("Empty version range");
    *this = std::string{ v };
}

VersionRange::VersionRange(const std::string &s)
{
    auto in = detail::preprocess_input(s);
    if (in.empty())
        // * or throw error?
        *this = VersionRange{"*"};
    else if (auto r = parse(*this, in); r)
        throw SW_RUNTIME_ERROR("Invalid version range: " + in + ", error: " + r.value());
}

std::optional<VersionRange> VersionRange::parse(const std::string &s)
{
    VersionRange vr;
    auto in = detail::preprocess_input(s);
    if (in.empty())
        vr = VersionRange{"*"};
    else if (auto r = parse(vr, in); r)
        return {};
    return vr;
}

std::optional<std::string> VersionRange::parse(VersionRange &vr, const std::string &s)
{
    if (s.size() > 32_KB)
        return { "Range string length must be less than 32K" };

    RangeParser d;
    auto r = d.parse(s);
    auto error = d.bb.error_msg;
    if (r == 0)
    {
        if (auto res = d.bb.getResult<VersionRange>(); res)
        {
            vr = res.value();
            return {};
        }
        else
            error = "parser error: empty result";
    }
    return error;
}

size_t VersionRange::size() const
{
    return range.size();
}

bool VersionRange::isEmpty() const
{
    return range.empty();
}

bool VersionRange::contains(const Version &v) const
{
    return std::any_of(range.begin(), range.end(),
        [&v](const auto &r) { return r.contains(v); });
}

bool VersionRange::contains(const VersionRange &r) const
{
    return (*this & r) == r;
}

std::optional<Version> VersionRange::toVersion() const
{
    if (size() != 1)
        return {};
    return range.begin()->toVersion();
}

std::string VersionRange::toString() const
{
    return toString(VersionRangePairStringRepresentationType::SameDefaultLevel);
}

std::string VersionRange::toString(VersionRangePairStringRepresentationType t) const
{
    std::string s;
    for (auto &p : range)
        s += p.toString(t) + " || ";
    if (!s.empty())
        s.resize(s.size() - 4);
    return s;
}

size_t VersionRange::getHash() const
{
    size_t h = 0;
    for (auto &n : range)
        hash_combine(h, n.getHash());
    return h;
}

bool VersionRange::operator==(const VersionRange &rhs) const
{
    return range == rhs.range;
}

VersionRange &VersionRange::operator|=(const detail::RangePair &rhs)
{
    // we can do pairs |= p
    // but we still need to merge overlapped after
    // so we choose simple iterative approach instead

    bool added = false;
    for (auto i = range.begin(); i < range.end(); i++)
    {
        if (!added)
        {
            // skip add, p is greater
            if (i->getSecond() < rhs.getFirst())
                continue;
            // insert as is BEFORE current
            else if (i->getFirst() > rhs.getSecond())
            {
                i = range.insert(i, rhs);
                break; // no further merges requires
            }
            else
            {
                // TRY to merge overlapped
                // we can fail on (1,2)|(2,3)
                if (auto u = *i | rhs)
                    *i = *u;
                else
                    // insert as is AFTER current
                    i = range.insert(++i, rhs);
                added = true;
            }
        }
        else
        {
            // after merging with existing entry we must ensure that
            // following intervals do not require merges too
            if ((i - 1)->getSecond() < i->getFirst())
                break;
            else if (auto u = *(i - 1) | *i)
            {
                *(i - 1) = *u;
                i = range.erase(i) - 1;
            }
        }
    }
    if (!added)
        range.push_back(rhs);
    return *this;
}

VersionRange &VersionRange::operator|=(const VersionRange &rhs)
{
    for (auto &rp : rhs.range)
        operator|=(rp);
    return *this;
}

VersionRange &VersionRange::operator&=(const VersionRange &rhs)
{
    VersionRange vr;
    for (auto &rp1 : range)
    {
        for (auto &rp2 : rhs.range)
        {
            if (auto o = rp1 & rp2; o)
                vr |= o.value();
        }
    }
    return *this = vr;
}

VersionRange VersionRange::operator|(const VersionRange &rhs) const
{
    auto tmp = *this;
    tmp |= rhs;
    return tmp;
}

VersionRange VersionRange::operator&(const VersionRange &rhs) const
{
    auto tmp = *this;
    tmp &= rhs;
    return tmp;
}

/*VersionRange VersionRange::operator~() const
{
    auto tmp = *this;
    throw SW_RUNTIME_ERROR("not implemented");
    return tmp;
}*/

}
<commit_msg>Print equal range pair as '=version'.<commit_after>#include <primitives/version_range.h>

#include <range_parser.h>

#include <fmt/format.h>
#include <primitives/constants.h>
#include <primitives/exceptions.h>
#include <primitives/hash_combine.h>
#include <primitives/string.h>

#include <pystring.h>

#include <algorithm>
#include <map>

const primitives::version::Version::Level minimum_level = 3;

namespace primitives::version
{

bool detail::RangePair::Side::operator<(const Version &rhs) const
{
    if (strong_relation)
        return v < rhs;
    else
        return v <= rhs;
}

bool detail::RangePair::Side::operator>(const Version &rhs) const
{
    if (strong_relation)
        return v > rhs;
    else
        return v >= rhs;
}

std::string detail::RangePair::Side::toString(VersionRangePairStringRepresentationType t) const
{
    switch (t)
    {
    case VersionRangePairStringRepresentationType::SameDefaultLevel:
        return v.toString(v.getDefaultLevel());
        break;
    case VersionRangePairStringRepresentationType::SameRealLevel:
        return v.toString(v.getRealLevel());
        break;
    case VersionRangePairStringRepresentationType::IndividualRealLevel:
        return v.toString(v.getRealLevel());
    }
    SW_UNREACHABLE;
}

detail::RangePair::RangePair(const Version &v1, bool strong_relation1, const Version &v2, bool strong_relation2)
    : first{ v1, strong_relation1 }, second{ v2, strong_relation2 }
{
    if (getFirst() > getSecond())
        throw SW_RUNTIME_ERROR("Left version must be <= than right: " + toStringInterval());
    if (getFirst() == getSecond() && (strong_relation1 || strong_relation2))
        throw SW_RUNTIME_ERROR("Pair does not contain any versions: " + toStringInterval());
}

detail::RangePair::RangePair(const Side &l, const Side &r)
    : first(l), second(r)
{
}

bool detail::RangePair::contains(const Version &v) const
{
    // TODO: rename < and > here
    return first < v && second > v;
}

std::optional<detail::RangePair> detail::RangePair::operator&(const detail::RangePair &rhs) const
{
    auto &f = std::max(first, rhs.first);
    auto &s = std::min(second, rhs.second);
    if (0
        || f.v < s.v
        || f.v == s.v && (!f.strong_relation && !s.strong_relation)
        )
        return detail::RangePair(f, s);
    return {};
}

std::optional<detail::RangePair> detail::RangePair::operator|(const detail::RangePair &rhs) const
{
    // find intersection first
    auto &f = std::max(first, rhs.first);
    auto &s = std::min(second, rhs.second);
    if (0
        || f.v < s.v
        || f.v == s.v && (!f.strong_relation || !s.strong_relation)
        )
    {
        // now make a union
        auto &fmin = std::min(first, rhs.first);
        auto &smax = std::max(second, rhs.second);
        return detail::RangePair(fmin, smax);
    }
    return {};
}

std::string detail::RangePair::toString(VersionRangePairStringRepresentationType t) const
{
    /*
    auto level = std::max(getFirst().getLevel(), getSecond().getLevel());
    if (t == VersionRangePairStringRepresentationType::SameRealLevel)
        level = std::max(getFirst().getRealLevel(), getSecond().getRealLevel());
    if (getFirst() == getSecond())
    {
        if (t == VersionRangePairStringRepresentationType::IndividualRealLevel)
            return getFirst().toString(getFirst().getRealLevel());
        else
            return getFirst().toString(level);
    }
    std::string s;
    if (getFirst() != Version::min(getFirst()))
    {
        s += ">=";
        if (t == VersionRangePairStringRepresentationType::IndividualRealLevel)
            s += getFirst().toString(getFirst().getRealLevel());
        else
            s += getFirst().toString(level);
    }
    // reconsider?
    // probably wrong now: 1.MAX.3
    if (std::any_of(getSecond().numbers.begin(), getSecond().numbers.end(),
        [](const auto &n) {return n == Version::maxNumber(); }))
    {
        if (getSecond() == Version::max(getSecond()))
        {
            if (s.empty())
                return "*";
            return s;
        }
        if (getSecond().getPatch() == Version::maxNumber() ||
            getSecond().getTweak() == Version::maxNumber())
        {
            if (!s.empty())
                s += " ";
            if (t == VersionRangePairStringRepresentationType::IndividualRealLevel)
            {
                auto nv = getSecond().getNextVersion(getSecond().getRealLevel());
                return s + "<" + nv.toString(nv.getRealLevel());
            }
            auto nv = getSecond().getNextVersion(level);
            if (t == VersionRangePairStringRepresentationType::SameRealLevel)
            {
                level = std::max(getFirst().getRealLevel(), nv.getRealLevel());
                s.clear();
                if (getFirst() != Version::min(getFirst()))
                {
                    s += ">=";
                    s += getFirst().toString(level);
                    if (!s.empty())
                        s += " ";
                }
            }
            return s + "<" + nv.toString(level);
        }
    }
    if (!s.empty())
        s += " ";
    if (t == VersionRangePairStringRepresentationType::IndividualRealLevel)
        return s + "<=" + getSecond().toString(getSecond().getRealLevel());
    else
        return s + "<=" + getSecond().toString(level);*/

    bool print_left = first.v > Version::min();
    bool print_right = second.v < Version::max();

    if (print_left && print_right
        && !first.strong_relation && !second.strong_relation
        && first.v == second.v)
    {
        return "=" + first.toString(t);
    }

    std::string s;
    if (print_left)
    {
        s += ">";
        if (!first.strong_relation)
            s += "=";
        s += first.toString(t);
    }
    if (print_left && print_right)
        s += " ";
    if (print_right)
    {
        s += "<";
        if (!second.strong_relation)
            s += "=";
        s += second.toString(t);
    }
    if (s.empty())
        return "*";
    return s;
}

std::string detail::RangePair::toStringInterval() const
{
    std::string s;
    if (first.strong_relation)
        s += "(";
    else
        s += "[";
    s += first.toString(VersionRangePairStringRepresentationType::SameDefaultLevel);
    s += ", ";
    s += second.toString(VersionRangePairStringRepresentationType::SameDefaultLevel);
    if (second.strong_relation)
        s += ")";
    else
        s += "]";
    return s;
}

std::optional<Version> detail::RangePair::toVersion() const
{
    if (getFirst() != getSecond())
        return {};
    return getFirst();
}

size_t detail::RangePair::getHash() const
{
    size_t h = 0;
    hash_combine(h, getFirst().getHash());
    hash_combine(h, getSecond().getHash());
    return h;
}

VersionRange::VersionRange()
{
}

/*VersionRange::VersionRange(const Version &v1, const Version &v2)
{
    range.emplace_back(v1, false, v2, false);
}*/

VersionRange::VersionRange(const char *v)
{
    if (!v)
        throw SW_RUNTIME_ERROR("Empty version range");
    *this = std::string{ v };
}

VersionRange::VersionRange(const std::string &s)
{
    auto in = detail::preprocess_input(s);
    if (in.empty())
        // * or throw error?
        *this = VersionRange{"*"};
    else if (auto r = parse(*this, in); r)
        throw SW_RUNTIME_ERROR("Invalid version range: " + in + ", error: " + r.value());
}

std::optional<VersionRange> VersionRange::parse(const std::string &s)
{
    VersionRange vr;
    auto in = detail::preprocess_input(s);
    if (in.empty())
        vr = VersionRange{"*"};
    else if (auto r = parse(vr, in); r)
        return {};
    return vr;
}

std::optional<std::string> VersionRange::parse(VersionRange &vr, const std::string &s)
{
    if (s.size() > 32_KB)
        return { "Range string length must be less than 32K" };

    RangeParser d;
    auto r = d.parse(s);
    auto error = d.bb.error_msg;
    if (r == 0)
    {
        if (auto res = d.bb.getResult<VersionRange>(); res)
        {
            vr = res.value();
            return {};
        }
        else
            error = "parser error: empty result";
    }
    return error;
}

size_t VersionRange::size() const
{
    return range.size();
}

bool VersionRange::isEmpty() const
{
    return range.empty();
}

bool VersionRange::contains(const Version &v) const
{
    return std::any_of(range.begin(), range.end(),
        [&v](const auto &r) { return r.contains(v); });
}

bool VersionRange::contains(const VersionRange &r) const
{
    return (*this & r) == r;
}

std::optional<Version> VersionRange::toVersion() const
{
    if (size() != 1)
        return {};
    return range.begin()->toVersion();
}

std::string VersionRange::toString() const
{
    return toString(VersionRangePairStringRepresentationType::SameDefaultLevel);
}

std::string VersionRange::toString(VersionRangePairStringRepresentationType t) const
{
    std::string s;
    for (auto &p : range)
        s += p.toString(t) + " || ";
    if (!s.empty())
        s.resize(s.size() - 4);
    return s;
}

size_t VersionRange::getHash() const
{
    size_t h = 0;
    for (auto &n : range)
        hash_combine(h, n.getHash());
    return h;
}

bool VersionRange::operator==(const VersionRange &rhs) const
{
    return range == rhs.range;
}

VersionRange &VersionRange::operator|=(const detail::RangePair &rhs)
{
    // we can do pairs |= p
    // but we still need to merge overlapped after
    // so we choose simple iterative approach instead

    bool added = false;
    for (auto i = range.begin(); i < range.end(); i++)
    {
        if (!added)
        {
            // skip add, p is greater
            if (i->getSecond() < rhs.getFirst())
                continue;
            // insert as is BEFORE current
            else if (i->getFirst() > rhs.getSecond())
            {
                i = range.insert(i, rhs);
                break; // no further merges requires
            }
            else
            {
                // TRY to merge overlapped
                // we can fail on (1,2)|(2,3)
                if (auto u = *i | rhs)
                    *i = *u;
                else
                    // insert as is AFTER current
                    i = range.insert(++i, rhs);
                added = true;
            }
        }
        else
        {
            // after merging with existing entry we must ensure that
            // following intervals do not require merges too
            if ((i - 1)->getSecond() < i->getFirst())
                break;
            else if (auto u = *(i - 1) | *i)
            {
                *(i - 1) = *u;
                i = range.erase(i) - 1;
            }
        }
    }
    if (!added)
        range.push_back(rhs);
    return *this;
}

VersionRange &VersionRange::operator|=(const VersionRange &rhs)
{
    for (auto &rp : rhs.range)
        operator|=(rp);
    return *this;
}

VersionRange &VersionRange::operator&=(const VersionRange &rhs)
{
    VersionRange vr;
    for (auto &rp1 : range)
    {
        for (auto &rp2 : rhs.range)
        {
            if (auto o = rp1 & rp2; o)
                vr |= o.value();
        }
    }
    return *this = vr;
}

VersionRange VersionRange::operator|(const VersionRange &rhs) const
{
    auto tmp = *this;
    tmp |= rhs;
    return tmp;
}

VersionRange VersionRange::operator&(const VersionRange &rhs) const
{
    auto tmp = *this;
    tmp &= rhs;
    return tmp;
}

/*VersionRange VersionRange::operator~() const
{
    auto tmp = *this;
    throw SW_RUNTIME_ERROR("not implemented");
    return tmp;
}*/

}
<|endoftext|>


<commit_before>#include <iostream>
#include <string>

#include "gtest/gtest.h"

#include <nomlib/graphics.hpp>
#include <nomlib/system.hpp>

namespace nom {

/// \note These unit tests target the non-typical usage of the FontCache class --
/// the non-static variant.
class FontCacheTest: public ::testing::Test
{
  public:
    FontCacheTest( void )
    {
      // ...
    }

    ~FontCacheTest( void )
    {
      // ...
    }

  protected:
    Path p;
    File fp;
    ResourceCache<IFont::shared_ptr> fonts_;
};

/// \fixme This test is failing on WindowsOS (Unknown file error: SEH exception).
TEST_F( FontCacheTest, CoreAPI )
{
  p = Path( fp.resource_path( "org.i8degrees.nomlib" ) + p.native() + "fonts" );

  #if defined( NOM_PLATFORM_OSX )
    Path sys( "/System/Library/Fonts" );
    Path lib( "/Library/Fonts" );
    ASSERT_TRUE( this->fonts_.append_resource( ResourceFile( "LucidaGrande", sys.prepend("LucidaGrande.ttc"), ResourceFile::Type::TrueTypeFont ) ) );
  #elif defined( NOM_PLATFORM_WINDOWS )
    Path sys( "C:\\Windows\\Fonts" );
    ASSERT_TRUE( this->fonts_.append_resource( ResourceFile( "Arial", sys.prepend("Arial.ttf"), ResourceFile::Type::TrueTypeFont ) ) );
    ASSERT_TRUE( this->fonts_.append_resource( ResourceFile( "TimesNewRoman", sys.prepend("times.ttf"), ResourceFile::Type::TrueTypeFont ) ) );
  #endif

  ASSERT_TRUE( this->fonts_.append_resource( ResourceFile( "LiberationSans-Regular", p.prepend("LiberationSans-Regular.ttf"), ResourceFile::Type::TrueTypeFont ) ) );
  ASSERT_TRUE( this->fonts_.append_resource( ResourceFile( "LiberationSerif-Regular", p.prepend("LiberationSerif-Regular.ttf"), ResourceFile::Type::TrueTypeFont ) ) );
  ASSERT_TRUE( this->fonts_.append_resource( ResourceFile( "VIII", p.prepend("VIII.png"), ResourceFile::Type::BitmapFont ) ) );
  ASSERT_TRUE( this->fonts_.append_resource( ResourceFile( "VIII_small", p.prepend("VIII_small.png"), ResourceFile::Type::BitmapFont ) ) );

  // Should not exist
  ASSERT_FALSE( this->fonts_.append_resource( ResourceFile( "IX", p.prepend("IX.png") ) ) );

  // Should already exist.
  ASSERT_FALSE( this->fonts_.append_resource( ResourceFile( "VIII", p.prepend("VIII.png") ) ) );

  ResourceFile res;

  ASSERT_TRUE( res == ResourceFile::null );
  ASSERT_FALSE( res.exists() );

  res = this->fonts_.find_resource( "VIII" );
  ASSERT_TRUE( res.exists() );
  EXPECT_EQ( "VIII", res.name() );

  res = this->fonts_.find_resource( "VIII_small" );
  ASSERT_TRUE( res.exists() );
  EXPECT_EQ( "VIII_small", res.name() );

  // Should not exist
  res = this->fonts_.find_resource( "IX" );
  ASSERT_FALSE( res.exists() );
  EXPECT_EQ( "", res.name() );

  EXPECT_EQ( 5, this->fonts_.size() );

  this->fonts_.clear();

  EXPECT_EQ( 0, this->fonts_.size() );
}

} // namespace nom

int main( int argc, char** argv )
{
  ::testing::InitGoogleTest( &argc, argv );

  return RUN_ALL_TESTS();
}
<commit_msg>FontCacheTest: Change ResourceCache type to Font<commit_after>#include <iostream>
#include <string>

#include "gtest/gtest.h"

#include <nomlib/graphics.hpp>
#include <nomlib/system.hpp>

namespace nom {

/// \note These unit tests target the non-typical usage of the FontCache class --
/// the non-static variant.
class FontCacheTest: public ::testing::Test
{
  public:
    FontCacheTest( void )
    {
      // ...
    }

    ~FontCacheTest( void )
    {
      // ...
    }

  protected:
    Path p;
    File fp;

    // ResourceCache<IFont::shared_ptr> fonts_;
    ResourceCache<Font> fonts_;
};

/// \fixme This test is failing on WindowsOS (Unknown file error: SEH exception).
TEST_F( FontCacheTest, CoreAPI )
{
  p = Path( fp.resource_path( "org.i8degrees.nomlib" ) + p.native() + "fonts" );

  #if defined( NOM_PLATFORM_OSX )
    Path sys( "/System/Library/Fonts" );
    Path lib( "/Library/Fonts" );
    ASSERT_TRUE( this->fonts_.append_resource( ResourceFile( "LucidaGrande", sys.prepend("LucidaGrande.ttc"), ResourceFile::Type::TrueTypeFont ) ) );
  #elif defined( NOM_PLATFORM_WINDOWS )
    Path sys( "C:\\Windows\\Fonts" );
    ASSERT_TRUE( this->fonts_.append_resource( ResourceFile( "Arial", sys.prepend("Arial.ttf"), ResourceFile::Type::TrueTypeFont ) ) );
    ASSERT_TRUE( this->fonts_.append_resource( ResourceFile( "TimesNewRoman", sys.prepend("times.ttf"), ResourceFile::Type::TrueTypeFont ) ) );
  #endif

  ASSERT_TRUE( this->fonts_.append_resource( ResourceFile( "LiberationSans-Regular", p.prepend("LiberationSans-Regular.ttf"), ResourceFile::Type::TrueTypeFont ) ) );
  ASSERT_TRUE( this->fonts_.append_resource( ResourceFile( "LiberationSerif-Regular", p.prepend("LiberationSerif-Regular.ttf"), ResourceFile::Type::TrueTypeFont ) ) );
  ASSERT_TRUE( this->fonts_.append_resource( ResourceFile( "VIII", p.prepend("VIII.png"), ResourceFile::Type::BitmapFont ) ) );
  ASSERT_TRUE( this->fonts_.append_resource( ResourceFile( "VIII_small", p.prepend("VIII_small.png"), ResourceFile::Type::BitmapFont ) ) );

  // Should not exist
  ASSERT_FALSE( this->fonts_.append_resource( ResourceFile( "IX", p.prepend("IX.png") ) ) );

  // Should already exist.
  ASSERT_FALSE( this->fonts_.append_resource( ResourceFile( "VIII", p.prepend("VIII.png") ) ) );

  ResourceFile res;

  ASSERT_TRUE( res == ResourceFile::null );
  ASSERT_FALSE( res.exists() );

  res = this->fonts_.find_resource( "VIII" );
  ASSERT_TRUE( res.exists() );
  EXPECT_EQ( "VIII", res.name() );

  res = this->fonts_.find_resource( "VIII_small" );
  ASSERT_TRUE( res.exists() );
  EXPECT_EQ( "VIII_small", res.name() );

  // Should not exist
  res = this->fonts_.find_resource( "IX" );
  ASSERT_FALSE( res.exists() );
  EXPECT_EQ( "", res.name() );

  EXPECT_EQ( 5, this->fonts_.size() );

  this->fonts_.clear();

  EXPECT_EQ( 0, this->fonts_.size() );
}

} // namespace nom

int main( int argc, char** argv )
{
  ::testing::InitGoogleTest( &argc, argv );

  return RUN_ALL_TESTS();
}
<|endoftext|>


<commit_before>/*=========================================================================
 *
 *  Copyright Insight Software Consortium
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0.txt
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 *=========================================================================*/

#include "itkMultiThreader.h"
#include "itkTimeProbe.h"

void* execute(void *ptr)
{
  // Here - get any args from ptr.
  //std::cout << std::endl << "Thread fn starting.." << std::endl;
  int *data = reinterpret_cast<int *>(ptr);
  int m = 100;
  std::cout << "Ptr received  :" << ptr
            << "Value " << *data << std::endl;
  int    n = 10;
  double sum = 1.0;

  for( int j = 0; j < m; j++ )
    {
    sum = 1.0;
    for( int i = 1; i <= n; i++ )
      {
      sum = sum + i + j;
      }
    }
  //std::cout << std::endl << "Thread fn DONE" << std::endl;
  return ITK_NULLPTR;
}

int itkThreadPoolTest(int argc, char* argv[])
{
#if defined(_WIN32) || defined(_WIN64)
#else
  int count = 1000000;
  if( argc > 1 )
    {
    const int nt = atoi( argv[1] );
    if(nt > 1)
      {
      count = nt;
      }
    }

  itk::MultiThreader::Pointer    threader = itk::MultiThreader::New();
  if(threader.IsNull())
    {
    return EXIT_FAILURE;
    }
  itk::TimeProbe timeProbe;
  itk::TimeProbe::TimeStampType startTime = timeProbe.GetInstantValue();
  int data = 100;
  for(int i=0; i<count;i++)
    {

    threader->SetSingleMethod(&execute,(void *)&data);
    threader->SingleMethodExecute();
    }
  itk::TimeProbe::TimeStampType elapsed = timeProbe.GetInstantValue() - startTime;
  std::cout<<std::endl <<" Thread pool test : Time elapsed : " << elapsed << std::endl;
#endif
  return EXIT_SUCCESS;
}
<commit_msg>COMP: Removed unused parameters in itkThreadPoolTest.<commit_after>/*=========================================================================
 *
 *  Copyright Insight Software Consortium
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0.txt
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 *=========================================================================*/

#include "itkMultiThreader.h"
#include "itkTimeProbe.h"

void* execute(void *ptr)
{
  // Here - get any args from ptr.
  //std::cout << std::endl << "Thread fn starting.." << std::endl;
  int *data = reinterpret_cast<int *>(ptr);
  int m = 100;
  std::cout << "Ptr received  :" << ptr
            << "Value " << *data << std::endl;
  int    n = 10;
  double sum = 1.0;

  for( int j = 0; j < m; j++ )
    {
    sum = 1.0;
    for( int i = 1; i <= n; i++ )
      {
      sum = sum + i + j;
      }
    }
  //std::cout << std::endl << "Thread fn DONE" << std::endl;
  return ITK_NULLPTR;
}

#if defined(_WIN32) || defined(_WIN64)
int itkThreadPoolTest(int, char* [])
{
#else
int itkThreadPoolTest(int argc, char* argv[])
{
  int count = 1000000;
  if( argc > 1 )
    {
    const int nt = atoi( argv[1] );
    if(nt > 1)
      {
      count = nt;
      }
    }

  itk::MultiThreader::Pointer    threader = itk::MultiThreader::New();
  if(threader.IsNull())
    {
    return EXIT_FAILURE;
    }
  itk::TimeProbe timeProbe;
  itk::TimeProbe::TimeStampType startTime = timeProbe.GetInstantValue();
  int data = 100;
  for(int i=0; i<count;i++)
    {

    threader->SetSingleMethod(&execute,(void *)&data);
    threader->SingleMethodExecute();
    }
  itk::TimeProbe::TimeStampType elapsed = timeProbe.GetInstantValue() - startTime;
  std::cout<<std::endl <<" Thread pool test : Time elapsed : " << elapsed << std::endl;
#endif
  return EXIT_SUCCESS;
}
<|endoftext|>


<commit_before>// Copyright (c) 2009 - Mozy, Inc.

#include "namedpipe.h"

#include "mordor/assert.h"
#include "mordor/exception.h"
#include "mordor/log.h"
#include "mordor/runtime_linking.h"
#include "mordor/string.h"

namespace Mordor {

static Logger::ptr g_log = Log::lookup("mordor:streams::namedpipe");

NamedPipeStream::NamedPipeStream(const std::string &name, Flags flags, IOManager *ioManager, Scheduler *scheduler, DWORD pipeModeFlags)
{
    init(toUtf16(name), flags, pipeModeFlags,ioManager, scheduler);
}

NamedPipeStream::NamedPipeStream(const std::wstring &name, Flags flags, IOManager *ioManager, Scheduler *scheduler, DWORD pipeModeFlags)
{
    init(name, flags, pipeModeFlags, ioManager, scheduler);
}

void NamedPipeStream::init(const std::wstring &name, Flags flags, DWORD pipeModeFlags, IOManager *ioManager, Scheduler *scheduler)
{
    if (pipeModeFlags == (DWORD)-1) {
        pipeModeFlags = PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT;
    }

    HANDLE hPipe = CreateNamedPipeW(name.c_str(),
        (DWORD)flags | (ioManager ? FILE_FLAG_OVERLAPPED : 0),
        pipeModeFlags,
        PIPE_UNLIMITED_INSTANCES,
        0, 0, 0, NULL);
    error_t error = lastError();
    MORDOR_LOG_LEVEL(g_log, hPipe == INVALID_HANDLE_VALUE ? Log::ERROR : Log::VERBOSE)
        << this << " CreateNamedPipeW(" << toUtf8(name) << ", " << flags
        << "): " << hPipe << " (" << error << ")";
    if (hPipe == INVALID_HANDLE_VALUE)
        MORDOR_THROW_EXCEPTION_FROM_ERROR_API(error, "CreateNamedPipeW");
    HandleStream::init(hPipe, ioManager, scheduler);
    m_supportsRead = !!(flags & READ);
    m_supportsWrite = !!(flags & WRITE);
}

void
NamedPipeStream::close(CloseType type)
{
    if (m_hFile != INVALID_HANDLE_VALUE) {
        SchedulerSwitcher switcher(m_scheduler);
        BOOL ret = DisconnectNamedPipe(m_hFile);
        error_t error = lastError();
        MORDOR_LOG_VERBOSE(g_log) << this << " DisconnectNamedPipe(" << m_hFile
            << "): " << ret << " (" << error << ")";
        if (!ret)
            MORDOR_THROW_EXCEPTION_FROM_ERROR_API(error, "DisconnectNamedPipe");
    }
    HandleStream::close(type);
}

void
NamedPipeStream::accept()
{
    if (m_cancelRead)
        MORDOR_THROW_EXCEPTION(OperationAbortedException());
    SchedulerSwitcher switcher(m_ioManager ? NULL : m_scheduler);
    OVERLAPPED *overlapped = NULL;
    if (m_ioManager) {
        MORDOR_ASSERT(Scheduler::getThis());
        m_ioManager->registerEvent(&m_readEvent);
        overlapped = &m_readEvent.overlapped;
    }
    BOOL ret = ConnectNamedPipe(m_hFile, overlapped);
    Log::Level level = Log::INFO;
    if (!ret) {
        if (lastError() == ERROR_PIPE_CONNECTED) {
        } else if (m_ioManager) {
            if (lastError() == ERROR_IO_PENDING)
                level = Log::TRACE;
            else
                level = Log::ERROR;
        } else {
            level = Log::ERROR;
        }
    }
    error_t error = lastError();
    MORDOR_LOG_LEVEL(g_log, level) << this
        << " ConnectNamedPipe(" << m_hFile << "): " << ret << " ("
        << error << ")";
    if (m_ioManager) {
        if (!ret && error == ERROR_PIPE_CONNECTED) {
            m_ioManager->unregisterEvent(&m_readEvent);
            return;
        }
        if (!ret && error != ERROR_IO_PENDING) {
            m_ioManager->unregisterEvent(&m_readEvent);
            MORDOR_THROW_EXCEPTION_FROM_LAST_ERROR_API("ConnectNamedPipe");
        }
        if (ret && m_skipCompletionPortOnSuccess)
            m_ioManager->unregisterEvent(&m_readEvent);
        else
            Scheduler::yieldTo();
        error_t error = pRtlNtStatusToDosError((NTSTATUS)m_readEvent.overlapped.Internal);
        MORDOR_LOG_LEVEL(g_log, error ? Log::ERROR : Log::INFO) << this
            << " ConnectNamedPipe(" << m_hFile << "): (" << error
            << ")";
        if (error)
            MORDOR_THROW_EXCEPTION_FROM_ERROR_API(error, "ConnectNamedPipe");
    } else {
        if (!ret && error == ERROR_PIPE_CONNECTED)
            return;
        if (!ret)
            MORDOR_THROW_EXCEPTION_FROM_ERROR_API(error, "ConnectNamedPipe");
    }
}

void
NamedPipeStream::cancelAccept()
{
    m_cancelRead = true;
    if (m_ioManager) {
        m_ioManager->cancelEvent(m_hFile, &m_readEvent);
    } else {
        MORDOR_ASSERT(supportsCancel());
        if (!pCancelIoEx(m_hFile, NULL))
            MORDOR_THROW_EXCEPTION_FROM_LAST_ERROR_API("CancelIoEx");
    }
}

void
NamedPipeStream::disconnectClient()
{
    // Prepare for reuse of the named pipe handle
    FlushFileBuffers(m_hFile);
    DisconnectNamedPipe(m_hFile);
}

}
<commit_msg>Dial down logging from NamedPipe<commit_after>// Copyright (c) 2009 - Mozy, Inc.

#include "namedpipe.h"

#include "mordor/assert.h"
#include "mordor/exception.h"
#include "mordor/log.h"
#include "mordor/runtime_linking.h"
#include "mordor/string.h"

namespace Mordor {

static Logger::ptr g_log = Log::lookup("mordor:streams::namedpipe");

NamedPipeStream::NamedPipeStream(const std::string &name, Flags flags, IOManager *ioManager, Scheduler *scheduler, DWORD pipeModeFlags)
{
    init(toUtf16(name), flags, pipeModeFlags,ioManager, scheduler);
}

NamedPipeStream::NamedPipeStream(const std::wstring &name, Flags flags, IOManager *ioManager, Scheduler *scheduler, DWORD pipeModeFlags)
{
    init(name, flags, pipeModeFlags, ioManager, scheduler);
}

void NamedPipeStream::init(const std::wstring &name, Flags flags, DWORD pipeModeFlags, IOManager *ioManager, Scheduler *scheduler)
{
    if (pipeModeFlags == (DWORD)-1) {
        pipeModeFlags = PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT;
    }

    HANDLE hPipe = CreateNamedPipeW(name.c_str(),
        (DWORD)flags | (ioManager ? FILE_FLAG_OVERLAPPED : 0),
        pipeModeFlags,
        PIPE_UNLIMITED_INSTANCES,
        0, 0, 0, NULL);
    error_t error = lastError();
    MORDOR_LOG_LEVEL(g_log, hPipe == INVALID_HANDLE_VALUE ? Log::ERROR : Log::VERBOSE)
        << this << " CreateNamedPipeW(" << toUtf8(name) << ", " << flags
        << "): " << hPipe << " (" << error << ")";
    if (hPipe == INVALID_HANDLE_VALUE)
        MORDOR_THROW_EXCEPTION_FROM_ERROR_API(error, "CreateNamedPipeW");
    HandleStream::init(hPipe, ioManager, scheduler);
    m_supportsRead = !!(flags & READ);
    m_supportsWrite = !!(flags & WRITE);
}

void
NamedPipeStream::close(CloseType type)
{
    if (m_hFile != INVALID_HANDLE_VALUE) {
        SchedulerSwitcher switcher(m_scheduler);
        BOOL ret = DisconnectNamedPipe(m_hFile);
        error_t error = lastError();
        MORDOR_LOG_VERBOSE(g_log) << this << " DisconnectNamedPipe(" << m_hFile
            << "): " << ret << " (" << error << ")";
        if (!ret)
            MORDOR_THROW_EXCEPTION_FROM_ERROR_API(error, "DisconnectNamedPipe");
    }
    HandleStream::close(type);
}

void
NamedPipeStream::accept()
{
    if (m_cancelRead)
        MORDOR_THROW_EXCEPTION(OperationAbortedException());
    SchedulerSwitcher switcher(m_ioManager ? NULL : m_scheduler);
    OVERLAPPED *overlapped = NULL;
    if (m_ioManager) {
        MORDOR_ASSERT(Scheduler::getThis());
        m_ioManager->registerEvent(&m_readEvent);
        overlapped = &m_readEvent.overlapped;
    }
    BOOL ret = ConnectNamedPipe(m_hFile, overlapped);
    Log::Level level = Log::DEBUG;
    if (!ret) {
        if (lastError() == ERROR_PIPE_CONNECTED) {
        } else if (m_ioManager) {
            if (lastError() == ERROR_IO_PENDING)
                level = Log::TRACE;
            else
                level = Log::ERROR;
        } else {
            level = Log::ERROR;
        }
    }
    error_t error = lastError();
    MORDOR_LOG_LEVEL(g_log, level) << this
        << " ConnectNamedPipe(" << m_hFile << "): " << ret << " ("
        << error << ")";
    if (m_ioManager) {
        if (!ret && error == ERROR_PIPE_CONNECTED) {
            m_ioManager->unregisterEvent(&m_readEvent);
            return;
        }
        if (!ret && error != ERROR_IO_PENDING) {
            m_ioManager->unregisterEvent(&m_readEvent);
            MORDOR_THROW_EXCEPTION_FROM_LAST_ERROR_API("ConnectNamedPipe");
        }
        if (ret && m_skipCompletionPortOnSuccess)
            m_ioManager->unregisterEvent(&m_readEvent);
        else
            Scheduler::yieldTo();
        error_t error = pRtlNtStatusToDosError((NTSTATUS)m_readEvent.overlapped.Internal);
        MORDOR_LOG_LEVEL(g_log, error ? Log::ERROR : Log::DEBUG) << this
            << " ConnectNamedPipe(" << m_hFile << "): (" << error
            << ")";
        if (error)
            MORDOR_THROW_EXCEPTION_FROM_ERROR_API(error, "ConnectNamedPipe");
    } else {
        if (!ret && error == ERROR_PIPE_CONNECTED)
            return;
        if (!ret)
            MORDOR_THROW_EXCEPTION_FROM_ERROR_API(error, "ConnectNamedPipe");
    }
}

void
NamedPipeStream::cancelAccept()
{
    m_cancelRead = true;
    if (m_ioManager) {
        m_ioManager->cancelEvent(m_hFile, &m_readEvent);
    } else {
        MORDOR_ASSERT(supportsCancel());
        if (!pCancelIoEx(m_hFile, NULL))
            MORDOR_THROW_EXCEPTION_FROM_LAST_ERROR_API("CancelIoEx");
    }
}

void
NamedPipeStream::disconnectClient()
{
    // Prepare for reuse of the named pipe handle
    FlushFileBuffers(m_hFile);
    DisconnectNamedPipe(m_hFile);
}

}
<|endoftext|>


<commit_before>/*
 * qmqtt_network.cpp - qmqtt network
 *
 * Copyright (c) 2013  Ery Lee <ery.lee at gmail dot com>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *   * Redistributions of source code must retain the above copyright notice,
 *     this list of conditions and the following disclaimer.
 *   * Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *   * Neither the name of mqttc nor the names of its contributors may be used
 *     to endorse or promote products derived from this software without
 *     specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 */
#include <QDataStream>
#include "qmqtt_network_p.h"
#include "qmqtt_socket_p.h"
#include "qmqtt_ssl_socket_p.h"
#include "qmqtt_timer_p.h"

const QString DEFAULT_HOST_NAME = QStringLiteral("localhost");
const QHostAddress DEFAULT_HOST = QHostAddress::LocalHost;
const quint16 DEFAULT_PORT = 1883;
const quint16 DEFAULT_SSL_PORT = 8883;
const bool DEFAULT_AUTORECONNECT = false;
const int DEFAULT_AUTORECONNECT_INTERVAL_MS = 5000;

QMQTT::Network::Network(QObject* parent)
    : NetworkInterface(parent)
    , _port(DEFAULT_PORT)
    , _host(DEFAULT_HOST)
    , _autoReconnect(DEFAULT_AUTORECONNECT)
    , _autoReconnectInterval(DEFAULT_AUTORECONNECT_INTERVAL_MS)
    , _socket(new QMQTT::Socket)
    , _autoReconnectTimer(new QMQTT::Timer)
    , _readState(Header)
{
    initialize();
}

#ifndef QT_NO_SSL
QMQTT::Network::Network(const QSslConfiguration &config, bool ignoreSelfSigned, QObject *parent)
    : NetworkInterface(parent)
    , _port(DEFAULT_SSL_PORT)
    , _hostName(DEFAULT_HOST_NAME)
    , _autoReconnect(DEFAULT_AUTORECONNECT)
    , _autoReconnectInterval(DEFAULT_AUTORECONNECT_INTERVAL_MS)
    , _socket(new QMQTT::SslSocket(config, ignoreSelfSigned))
    , _autoReconnectTimer(new QMQTT::Timer)
    , _readState(Header)
{
    initialize();
}
#endif // QT_NO_SSL

QMQTT::Network::Network(SocketInterface* socketInterface, TimerInterface* timerInterface,
                        QObject* parent)
    : NetworkInterface(parent)
    , _port(DEFAULT_PORT)
    , _host(DEFAULT_HOST)
    , _autoReconnect(DEFAULT_AUTORECONNECT)
    , _autoReconnectInterval(DEFAULT_AUTORECONNECT_INTERVAL_MS)
    , _socket(socketInterface)
    , _autoReconnectTimer(timerInterface)
    , _readState(Header)
{
    initialize();
}

void QMQTT::Network::initialize()
{
    _socket->setParent(this);
    _autoReconnectTimer->setParent(this);
    _autoReconnectTimer->setSingleShot(true);
    _autoReconnectTimer->setInterval(_autoReconnectInterval);

    QObject::connect(_socket, &SocketInterface::connected, this, &Network::connected);
    QObject::connect(_socket, &SocketInterface::disconnected, this, &Network::onDisconnected);
    QObject::connect(_socket->ioDevice(), &QIODevice::readyRead, this, &Network::onSocketReadReady);
    QObject::connect(
        _autoReconnectTimer, &TimerInterface::timeout,
        this, static_cast<void (Network::*)()>(&Network::connectToHost));
    QObject::connect(_socket,
        static_cast<void (SocketInterface::*)(QAbstractSocket::SocketError)>(&SocketInterface::error),
        this, &Network::onSocketError);
}

QMQTT::Network::~Network()
{
}

bool QMQTT::Network::isConnectedToHost() const
{
    return _socket->state() == QAbstractSocket::ConnectedState;
}

void QMQTT::Network::connectToHost(const QHostAddress& host, const quint16 port)
{
    _host = host;
    _port = port;
    connectToHost();
}

void QMQTT::Network::connectToHost(const QString& hostName, const quint16 port)
{
    _hostName = hostName;
    _port = port;
    connectToHost();
}

void QMQTT::Network::connectToHost()
{
    _readState = Header;
    if (_hostName.isEmpty())
    {
        _socket->connectToHost(_host, _port);
    }
    else
    {
        _socket->connectToHost(_hostName, _port);
    }
}

void QMQTT::Network::onSocketError(QAbstractSocket::SocketError socketError)
{
    emit error(socketError);
    if(_autoReconnect)
    {
        _autoReconnectTimer->start();
    }
}

void QMQTT::Network::sendFrame(Frame& frame)
{
    if(_socket->state() == QAbstractSocket::ConnectedState)
    {
        QDataStream out(_socket->ioDevice());
        frame.write(out);
    }
}

void QMQTT::Network::disconnectFromHost()
{
    _socket->disconnectFromHost();
}

QAbstractSocket::SocketState QMQTT::Network::state() const
{
    return _socket->state();
}

bool QMQTT::Network::autoReconnect() const
{
    return _autoReconnect;
}

void QMQTT::Network::setAutoReconnect(const bool autoReconnect)
{
    _autoReconnect = autoReconnect;
}

int QMQTT::Network::autoReconnectInterval() const
{
    return _autoReconnectInterval;
}

void QMQTT::Network::setAutoReconnectInterval(const int autoReconnectInterval)
{
    _autoReconnectInterval = autoReconnectInterval;
    _autoReconnectTimer->setInterval(_autoReconnectInterval);
}

void QMQTT::Network::onSocketReadReady()
{
    QIODevice *ioDevice = _socket->ioDevice();
    // Only read the available (cached) bytes, so the read will never block.
    QByteArray data = ioDevice->read(ioDevice->bytesAvailable());
    foreach(char byte, data) {
        switch (_readState) {
        case Header:
            _header = static_cast<quint8>(byte);
            _readState = Length;
            _length = 0;
            _shift = 0;
            break;
        case Length:
            _length |= (byte & 0x7F) << _shift;
            _shift += 7;
            if ((byte & 0x80) != 0)
                break;
            if (_length == 0) {
                _readState = Header;
                Frame frame(_header, _data);
                emit received(frame);
                break;
            }
            _readState = PayLoad;
            _data.clear();
            break;
        case PayLoad:
            _data.append(byte);
            --_length;
            if (_length > 0)
                break;
            _readState = Header;
            Frame frame(_header, _data);
            emit received(frame);
            break;
        }
    }
}

void QMQTT::Network::onDisconnected()
{
    emit disconnected();
    if(_autoReconnect)
    {
        _autoReconnectTimer->start();
    }
}
<commit_msg>Fix the received Frame parser<commit_after>/*
 * qmqtt_network.cpp - qmqtt network
 *
 * Copyright (c) 2013  Ery Lee <ery.lee at gmail dot com>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *   * Redistributions of source code must retain the above copyright notice,
 *     this list of conditions and the following disclaimer.
 *   * Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *   * Neither the name of mqttc nor the names of its contributors may be used
 *     to endorse or promote products derived from this software without
 *     specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 */
#include <QDataStream>
#include "qmqtt_network_p.h"
#include "qmqtt_socket_p.h"
#include "qmqtt_ssl_socket_p.h"
#include "qmqtt_timer_p.h"

const QString DEFAULT_HOST_NAME = QStringLiteral("localhost");
const QHostAddress DEFAULT_HOST = QHostAddress::LocalHost;
const quint16 DEFAULT_PORT = 1883;
const quint16 DEFAULT_SSL_PORT = 8883;
const bool DEFAULT_AUTORECONNECT = false;
const int DEFAULT_AUTORECONNECT_INTERVAL_MS = 5000;

QMQTT::Network::Network(QObject* parent)
    : NetworkInterface(parent)
    , _port(DEFAULT_PORT)
    , _host(DEFAULT_HOST)
    , _autoReconnect(DEFAULT_AUTORECONNECT)
    , _autoReconnectInterval(DEFAULT_AUTORECONNECT_INTERVAL_MS)
    , _socket(new QMQTT::Socket)
    , _autoReconnectTimer(new QMQTT::Timer)
    , _readState(Header)
{
    initialize();
}

#ifndef QT_NO_SSL
QMQTT::Network::Network(const QSslConfiguration &config, bool ignoreSelfSigned, QObject *parent)
    : NetworkInterface(parent)
    , _port(DEFAULT_SSL_PORT)
    , _hostName(DEFAULT_HOST_NAME)
    , _autoReconnect(DEFAULT_AUTORECONNECT)
    , _autoReconnectInterval(DEFAULT_AUTORECONNECT_INTERVAL_MS)
    , _socket(new QMQTT::SslSocket(config, ignoreSelfSigned))
    , _autoReconnectTimer(new QMQTT::Timer)
    , _readState(Header)
{
    initialize();
}
#endif // QT_NO_SSL

QMQTT::Network::Network(SocketInterface* socketInterface, TimerInterface* timerInterface,
                        QObject* parent)
    : NetworkInterface(parent)
    , _port(DEFAULT_PORT)
    , _host(DEFAULT_HOST)
    , _autoReconnect(DEFAULT_AUTORECONNECT)
    , _autoReconnectInterval(DEFAULT_AUTORECONNECT_INTERVAL_MS)
    , _socket(socketInterface)
    , _autoReconnectTimer(timerInterface)
    , _readState(Header)
{
    initialize();
}

void QMQTT::Network::initialize()
{
    _socket->setParent(this);
    _autoReconnectTimer->setParent(this);
    _autoReconnectTimer->setSingleShot(true);
    _autoReconnectTimer->setInterval(_autoReconnectInterval);

    QObject::connect(_socket, &SocketInterface::connected, this, &Network::connected);
    QObject::connect(_socket, &SocketInterface::disconnected, this, &Network::onDisconnected);
    QObject::connect(_socket->ioDevice(), &QIODevice::readyRead, this, &Network::onSocketReadReady);
    QObject::connect(
        _autoReconnectTimer, &TimerInterface::timeout,
        this, static_cast<void (Network::*)()>(&Network::connectToHost));
    QObject::connect(_socket,
        static_cast<void (SocketInterface::*)(QAbstractSocket::SocketError)>(&SocketInterface::error),
        this, &Network::onSocketError);
}

QMQTT::Network::~Network()
{
}

bool QMQTT::Network::isConnectedToHost() const
{
    return _socket->state() == QAbstractSocket::ConnectedState;
}

void QMQTT::Network::connectToHost(const QHostAddress& host, const quint16 port)
{
    _host = host;
    _port = port;
    connectToHost();
}

void QMQTT::Network::connectToHost(const QString& hostName, const quint16 port)
{
    _hostName = hostName;
    _port = port;
    connectToHost();
}

void QMQTT::Network::connectToHost()
{
    _readState = Header;
    if (_hostName.isEmpty())
    {
        _socket->connectToHost(_host, _port);
    }
    else
    {
        _socket->connectToHost(_hostName, _port);
    }
}

void QMQTT::Network::onSocketError(QAbstractSocket::SocketError socketError)
{
    emit error(socketError);
    if(_autoReconnect)
    {
        _autoReconnectTimer->start();
    }
}

void QMQTT::Network::sendFrame(Frame& frame)
{
    if(_socket->state() == QAbstractSocket::ConnectedState)
    {
        QDataStream out(_socket->ioDevice());
        frame.write(out);
    }
}

void QMQTT::Network::disconnectFromHost()
{
    _socket->disconnectFromHost();
}

QAbstractSocket::SocketState QMQTT::Network::state() const
{
    return _socket->state();
}

bool QMQTT::Network::autoReconnect() const
{
    return _autoReconnect;
}

void QMQTT::Network::setAutoReconnect(const bool autoReconnect)
{
    _autoReconnect = autoReconnect;
}

int QMQTT::Network::autoReconnectInterval() const
{
    return _autoReconnectInterval;
}

void QMQTT::Network::setAutoReconnectInterval(const int autoReconnectInterval)
{
    _autoReconnectInterval = autoReconnectInterval;
    _autoReconnectTimer->setInterval(_autoReconnectInterval);
}

void QMQTT::Network::onSocketReadReady()
{
    QIODevice *ioDevice = _socket->ioDevice();
    // Only read the available (cached) bytes, so the read will never block.
    QByteArray data = ioDevice->read(ioDevice->bytesAvailable());
    foreach(char byte, data) {
        switch (_readState) {
        case Header:
            _header = static_cast<quint8>(byte);
            _readState = Length;
            _length = 0;
            _shift = 0;
            _data.clear();
            break;
        case Length:
            _length |= (byte & 0x7F) << _shift;
            _shift += 7;
            if ((byte & 0x80) != 0)
                break;
            if (_length == 0) {
                _readState = Header;
                Frame frame(_header, _data);
                emit received(frame);
                break;
            }
            _readState = PayLoad;
            break;
        case PayLoad:
            _data.append(byte);
            --_length;
            if (_length > 0)
                break;
            _readState = Header;
            Frame frame(_header, _data);
            emit received(frame);
            break;
        }
    }
}

void QMQTT::Network::onDisconnected()
{
    emit disconnected();
    if(_autoReconnect)
    {
        _autoReconnectTimer->start();
    }
}
<|endoftext|>


<commit_before>#include "glyphcellrenderer.h"
#include "wxcontext.h"

#include <wx/dcclient.h>
#include <wx/graphics.h>
#include <wx/settings.h>

#include <memory>

GlyphCellRenderer::GlyphCellRenderer(const std::map<wxString, SvgGlyph> &glyphs, int fontSize): glyphs(glyphs)
{
	labelFont = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
	glyphColor = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT);
	hlColor = wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE);
	labelColor = glyphColor.ChangeLightness(150);

	SetFontSize(fontSize);
}

wxGridCellRenderer *GlyphCellRenderer::Clone() const
{
	return nullptr;
}

void GlyphCellRenderer::SetHighlightCell(const wxGridCellCoords &coords)
{
	hlCellCoords = coords;
}

const wxGridCellCoords &GlyphCellRenderer::GetHighlightCell() const
{
	return hlCellCoords;
}

void GlyphCellRenderer::SetFontSize(int size)
{
	if (fontSize != size)
	{
		fontSize = size;
		glyphCache.clear();
		padding = std::max(4, size / 4);
	}
}

int GlyphCellRenderer::GetFontSize() const
{
	return fontSize;
}

void GlyphCellRenderer::Draw(wxGrid &grid, wxGridCellAttr &attr, wxDC &dc, const wxRect &rect, int row, int col, bool isSelected)
{
	wxGridCellRenderer::Draw(grid, attr, dc, rect, row, col, false);

	wxString value = grid.GetTable()->GetValue(row, col);
	wxString label;

	auto it = glyphs.find(value);
	if (it == glyphs.end()) return;

	const SvgGlyph &glyph = it->second;

	if (!glyph.IsOk())
		return;

	label.Printf("%04x", glyph.unicode[0]);

	std::unique_ptr<wxGraphicsContext> gc(wxGraphicsContext::Create(static_cast<wxPaintDC&>(dc)));

	wxRect newRect = rect;

	// replace with gc->GetRenderer()->GetName() == L"cairo" after wx 3.1
	bool isCairo = false;

	#if wxUSE_CAIRO
		isCairo = true;
	#endif

	// Oh, crap
	if (isCairo)
	{
		newRect.x += dc.GetDeviceOrigin().x;
		newRect.y += dc.GetDeviceOrigin().y;
	}

	std::map<wxString, wxBitmap>::iterator findIt = glyphCache.find(glyph.unicode);

	if (findIt == glyphCache.end())
	{
		bool result;
		std::tie(findIt, result) = glyphCache.emplace(glyph.unicode, GetBitmapForGlyph(glyph, fontSize, glyphColor));

		if (!result) return;
	}

	if (hlCellCoords.GetCol() == col && hlCellCoords.GetRow() == row)
	{
		gc->SetPen(wxPen(hlColor, 1));
		gc->DrawRoundedRectangle(newRect.x + 1, newRect.y + 1, newRect.width - 2, newRect.height - 2, 5);
	}

	newRect.height -= labelFont.GetPixelSize().GetHeight() + 2 * padding;

	const wxBitmap &glyphBitmap = findIt->second;

	gc->DrawBitmap(glyphBitmap,
		newRect.x + (newRect.width - glyphBitmap.GetWidth()) / 2,
		newRect.y + (newRect.height - glyphBitmap.GetHeight()) / 2,
		glyphBitmap.GetWidth(), glyphBitmap.GetHeight());

	double width, height, descent, externalLeading;

	gc->SetFont(labelFont, labelColor);
	gc->GetTextExtent(label, &width, &height, &descent, &externalLeading);
	gc->DrawText(label, newRect.x + (newRect.width - width) / 2, newRect.y + newRect.height + padding);
}

wxSize GlyphCellRenderer::GetBestSize(wxGrid &grid, wxGridCellAttr &attr, wxDC &dc, int row, int col)
{
	return wxSize(fontSize + padding, fontSize + labelFont.GetPixelSize().GetHeight() + 3 * padding);
}
<commit_msg>draw only good bitmaps in GlyphCellRenderer<commit_after>#include "glyphcellrenderer.h"
#include "wxcontext.h"

#include <wx/dcclient.h>
#include <wx/graphics.h>
#include <wx/settings.h>

#include <memory>

GlyphCellRenderer::GlyphCellRenderer(const std::map<wxString, SvgGlyph> &glyphs, int fontSize): glyphs(glyphs)
{
	labelFont = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
	glyphColor = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT);
	hlColor = wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE);
	labelColor = glyphColor.ChangeLightness(150);

	SetFontSize(fontSize);
}

wxGridCellRenderer *GlyphCellRenderer::Clone() const
{
	return nullptr;
}

void GlyphCellRenderer::SetHighlightCell(const wxGridCellCoords &coords)
{
	hlCellCoords = coords;
}

const wxGridCellCoords &GlyphCellRenderer::GetHighlightCell() const
{
	return hlCellCoords;
}

void GlyphCellRenderer::SetFontSize(int size)
{
	if (fontSize != size)
	{
		fontSize = size;
		glyphCache.clear();
		padding = std::max(4, size / 4);
	}
}

int GlyphCellRenderer::GetFontSize() const
{
	return fontSize;
}

void GlyphCellRenderer::Draw(wxGrid &grid, wxGridCellAttr &attr, wxDC &dc, const wxRect &rect, int row, int col, bool isSelected)
{
	wxGridCellRenderer::Draw(grid, attr, dc, rect, row, col, false);

	wxString value = grid.GetTable()->GetValue(row, col);
	wxString label;

	auto it = glyphs.find(value);
	if (it == glyphs.end()) return;

	const SvgGlyph &glyph = it->second;

	if (!glyph.IsOk())
		return;

	label.Printf("%04x", glyph.unicode[0]);

	std::unique_ptr<wxGraphicsContext> gc(wxGraphicsContext::Create(static_cast<wxPaintDC&>(dc)));

	wxRect newRect = rect;

	// replace with gc->GetRenderer()->GetName() == L"cairo" after wx 3.1
	bool isCairo = false;

	#if wxUSE_CAIRO
		isCairo = true;
	#endif

	// Oh, crap
	if (isCairo)
	{
		newRect.x += dc.GetDeviceOrigin().x;
		newRect.y += dc.GetDeviceOrigin().y;
	}

	std::map<wxString, wxBitmap>::iterator findIt = glyphCache.find(glyph.unicode);

	if (findIt == glyphCache.end())
	{
		bool result;
		std::tie(findIt, result) = glyphCache.emplace(glyph.unicode, GetBitmapForGlyph(glyph, fontSize, glyphColor));

		if (!result) return;
	}

	if (hlCellCoords.GetCol() == col && hlCellCoords.GetRow() == row)
	{
		gc->SetPen(wxPen(hlColor, 1));
		gc->DrawRoundedRectangle(newRect.x + 1, newRect.y + 1, newRect.width - 2, newRect.height - 2, 5);
	}

	newRect.height -= labelFont.GetPixelSize().GetHeight() + 2 * padding;

	const wxBitmap &glyphBitmap = findIt->second;

	if (glyphBitmap.IsOk())
	{
		gc->DrawBitmap(glyphBitmap,
			newRect.x + (newRect.width - glyphBitmap.GetWidth()) / 2,
			newRect.y + (newRect.height - glyphBitmap.GetHeight()) / 2,
			glyphBitmap.GetWidth(), glyphBitmap.GetHeight());
	}

	double width, height, descent, externalLeading;

	gc->SetFont(labelFont, labelColor);
	gc->GetTextExtent(label, &width, &height, &descent, &externalLeading);
	gc->DrawText(label, newRect.x + (newRect.width - width) / 2, newRect.y + newRect.height + padding);
}

wxSize GlyphCellRenderer::GetBestSize(wxGrid &grid, wxGridCellAttr &attr, wxDC &dc, int row, int col)
{
	return wxSize(fontSize + padding, fontSize + labelFont.GetPixelSize().GetHeight() + 3 * padding);
}
<|endoftext|>


<commit_before>//===--- MinGWToolChain.cpp - MinGWToolChain Implementation
//-----------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "ToolChains.h"
#include "clang/Driver/Driver.h"
#include "clang/Driver/Options.h"
#include "llvm/Option/ArgList.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Path.h"

using namespace clang::diag;
using namespace clang::driver;
using namespace clang::driver::toolchains;
using namespace clang;
using namespace llvm::opt;

MinGW::MinGW(const Driver &D, const llvm::Triple &Triple, const ArgList &Args)
    : ToolChain(D, Triple, Args) {
  getProgramPaths().push_back(getDriver().getInstalledDir());

  if (getDriver().SysRoot.size())
    Base = getDriver().SysRoot;
  else if (llvm::ErrorOr<std::string> GPPName =
               llvm::sys::findProgramByName("gcc"))
    Base = llvm::sys::path::parent_path(
        llvm::sys::path::parent_path(GPPName.get()));
  else
    Base = llvm::sys::path::parent_path(getDriver().getInstalledDir());
  Base += llvm::sys::path::get_separator();
  llvm::SmallString<1024> LibDir(Base);
  llvm::sys::path::append(LibDir, "lib", "gcc");
  LibDir += llvm::sys::path::get_separator();

  // First look for mingw-w64.
  Arch = getTriple().getArchName();
  Arch += "-w64-mingw32";
  std::error_code EC;
  llvm::sys::fs::directory_iterator MingW64Entry(LibDir + Arch, EC);
  if (!EC) {
    GccLibDir = MingW64Entry->path();
    Ver = llvm::sys::path::filename(GccLibDir);
  } else {
    // If mingw-w64 not found, try looking for mingw.org.
    Arch = "mingw32";
    llvm::sys::fs::directory_iterator MingwOrgEntry(LibDir + Arch, EC);
    if (!EC)
      GccLibDir = MingwOrgEntry->path();
  }
  Arch += llvm::sys::path::get_separator();
  // GccLibDir must precede Base/lib so that the
  // correct crtbegin.o ,cetend.o would be found.
  getFilePaths().push_back(GccLibDir);
  getFilePaths().push_back(Base + "lib");
  getFilePaths().push_back(Base + Arch + "lib");
}

bool MinGW::IsIntegratedAssemblerDefault() const { return true; }

Tool *MinGW::getTool(Action::ActionClass AC) const {
  switch (AC) {
  case Action::PreprocessJobClass:
    if (!Preprocessor)
      Preprocessor.reset(new tools::gcc::Preprocessor(*this));
    return Preprocessor.get();
  case Action::CompileJobClass:
    if (!Compiler)
      Compiler.reset(new tools::gcc::Compiler(*this));
    return Compiler.get();
  default:
    return ToolChain::getTool(AC);
  }
}

Tool *MinGW::buildAssembler() const {
  return new tools::MinGW::Assembler(*this);
}

Tool *MinGW::buildLinker() const { return new tools::MinGW::Linker(*this); }

bool MinGW::IsUnwindTablesDefault() const {
  return getArch() == llvm::Triple::x86_64;
}

bool MinGW::isPICDefault() const { return getArch() == llvm::Triple::x86_64; }

bool MinGW::isPIEDefault() const { return false; }

bool MinGW::isPICDefaultForced() const {
  return getArch() == llvm::Triple::x86_64;
}

bool MinGW::UseSEHExceptions() const {
  return getArch() == llvm::Triple::x86_64;
}

void MinGW::AddClangSystemIncludeArgs(const ArgList &DriverArgs,
                                      ArgStringList &CC1Args) const {
  if (DriverArgs.hasArg(options::OPT_nostdinc))
    return;

  if (!DriverArgs.hasArg(options::OPT_nobuiltininc)) {
    SmallString<1024> P(getDriver().ResourceDir);
    llvm::sys::path::append(P, "include");
    addSystemInclude(DriverArgs, CC1Args, P.str());
  }

  if (DriverArgs.hasArg(options::OPT_nostdlibinc))
    return;

  llvm::SmallString<1024> IncludeDir(GccLibDir);
  llvm::sys::path::append(IncludeDir, "include");
  addSystemInclude(DriverArgs, CC1Args, IncludeDir.c_str());
  IncludeDir += "-fixed";
  addSystemInclude(DriverArgs, CC1Args, IncludeDir.c_str());
  addSystemInclude(DriverArgs, CC1Args, Base + Arch + "include");
  addSystemInclude(DriverArgs, CC1Args, Base + "include");
}

void MinGW::AddClangCXXStdlibIncludeArgs(const ArgList &DriverArgs,
                                         ArgStringList &CC1Args) const {
  if (DriverArgs.hasArg(options::OPT_nostdlibinc) ||
      DriverArgs.hasArg(options::OPT_nostdincxx))
    return;

  // C++ includes may be found in several locations depending on distribution.
  // mingw-w64 mingw-builds: $sysroot/i686-w64-mingw32/include/c++.
  // mingw-w64 msys2:        $sysroot/include/c++/4.9.2
  // mingw.org:              GccLibDir/include/c++
  llvm::SmallVector<llvm::SmallString<1024>, 3> CppIncludeBases;
  CppIncludeBases.emplace_back(Base);
  llvm::sys::path::append(CppIncludeBases[0], Arch, "include", "c++");
  CppIncludeBases.emplace_back(Base);
  llvm::sys::path::append(CppIncludeBases[1], "include", "c++", Ver);
  CppIncludeBases.emplace_back(GccLibDir);
  llvm::sys::path::append(CppIncludeBases[2], "include", "c++");
  for (auto &CppIncludeBase : CppIncludeBases) {
    CppIncludeBase += llvm::sys::path::get_separator();
    addSystemInclude(DriverArgs, CC1Args, CppIncludeBase);
    addSystemInclude(DriverArgs, CC1Args, CppIncludeBase + Arch);
    addSystemInclude(DriverArgs, CC1Args, CppIncludeBase + "backward");
  }
}
<commit_msg>Fix first line comment format, NFC.<commit_after>//===--- MinGWToolChain.cpp - MinGWToolChain Implementation ---------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "ToolChains.h"
#include "clang/Driver/Driver.h"
#include "clang/Driver/Options.h"
#include "llvm/Option/ArgList.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Path.h"

using namespace clang::diag;
using namespace clang::driver;
using namespace clang::driver::toolchains;
using namespace clang;
using namespace llvm::opt;

MinGW::MinGW(const Driver &D, const llvm::Triple &Triple, const ArgList &Args)
    : ToolChain(D, Triple, Args) {
  getProgramPaths().push_back(getDriver().getInstalledDir());

  if (getDriver().SysRoot.size())
    Base = getDriver().SysRoot;
  else if (llvm::ErrorOr<std::string> GPPName =
               llvm::sys::findProgramByName("gcc"))
    Base = llvm::sys::path::parent_path(
        llvm::sys::path::parent_path(GPPName.get()));
  else
    Base = llvm::sys::path::parent_path(getDriver().getInstalledDir());
  Base += llvm::sys::path::get_separator();
  llvm::SmallString<1024> LibDir(Base);
  llvm::sys::path::append(LibDir, "lib", "gcc");
  LibDir += llvm::sys::path::get_separator();

  // First look for mingw-w64.
  Arch = getTriple().getArchName();
  Arch += "-w64-mingw32";
  std::error_code EC;
  llvm::sys::fs::directory_iterator MingW64Entry(LibDir + Arch, EC);
  if (!EC) {
    GccLibDir = MingW64Entry->path();
    Ver = llvm::sys::path::filename(GccLibDir);
  } else {
    // If mingw-w64 not found, try looking for mingw.org.
    Arch = "mingw32";
    llvm::sys::fs::directory_iterator MingwOrgEntry(LibDir + Arch, EC);
    if (!EC)
      GccLibDir = MingwOrgEntry->path();
  }
  Arch += llvm::sys::path::get_separator();
  // GccLibDir must precede Base/lib so that the
  // correct crtbegin.o ,cetend.o would be found.
  getFilePaths().push_back(GccLibDir);
  getFilePaths().push_back(Base + "lib");
  getFilePaths().push_back(Base + Arch + "lib");
}

bool MinGW::IsIntegratedAssemblerDefault() const { return true; }

Tool *MinGW::getTool(Action::ActionClass AC) const {
  switch (AC) {
  case Action::PreprocessJobClass:
    if (!Preprocessor)
      Preprocessor.reset(new tools::gcc::Preprocessor(*this));
    return Preprocessor.get();
  case Action::CompileJobClass:
    if (!Compiler)
      Compiler.reset(new tools::gcc::Compiler(*this));
    return Compiler.get();
  default:
    return ToolChain::getTool(AC);
  }
}

Tool *MinGW::buildAssembler() const {
  return new tools::MinGW::Assembler(*this);
}

Tool *MinGW::buildLinker() const { return new tools::MinGW::Linker(*this); }

bool MinGW::IsUnwindTablesDefault() const {
  return getArch() == llvm::Triple::x86_64;
}

bool MinGW::isPICDefault() const { return getArch() == llvm::Triple::x86_64; }

bool MinGW::isPIEDefault() const { return false; }

bool MinGW::isPICDefaultForced() const {
  return getArch() == llvm::Triple::x86_64;
}

bool MinGW::UseSEHExceptions() const {
  return getArch() == llvm::Triple::x86_64;
}

void MinGW::AddClangSystemIncludeArgs(const ArgList &DriverArgs,
                                      ArgStringList &CC1Args) const {
  if (DriverArgs.hasArg(options::OPT_nostdinc))
    return;

  if (!DriverArgs.hasArg(options::OPT_nobuiltininc)) {
    SmallString<1024> P(getDriver().ResourceDir);
    llvm::sys::path::append(P, "include");
    addSystemInclude(DriverArgs, CC1Args, P.str());
  }

  if (DriverArgs.hasArg(options::OPT_nostdlibinc))
    return;

  llvm::SmallString<1024> IncludeDir(GccLibDir);
  llvm::sys::path::append(IncludeDir, "include");
  addSystemInclude(DriverArgs, CC1Args, IncludeDir.c_str());
  IncludeDir += "-fixed";
  addSystemInclude(DriverArgs, CC1Args, IncludeDir.c_str());
  addSystemInclude(DriverArgs, CC1Args, Base + Arch + "include");
  addSystemInclude(DriverArgs, CC1Args, Base + "include");
}

void MinGW::AddClangCXXStdlibIncludeArgs(const ArgList &DriverArgs,
                                         ArgStringList &CC1Args) const {
  if (DriverArgs.hasArg(options::OPT_nostdlibinc) ||
      DriverArgs.hasArg(options::OPT_nostdincxx))
    return;

  // C++ includes may be found in several locations depending on distribution.
  // mingw-w64 mingw-builds: $sysroot/i686-w64-mingw32/include/c++.
  // mingw-w64 msys2:        $sysroot/include/c++/4.9.2
  // mingw.org:              GccLibDir/include/c++
  llvm::SmallVector<llvm::SmallString<1024>, 3> CppIncludeBases;
  CppIncludeBases.emplace_back(Base);
  llvm::sys::path::append(CppIncludeBases[0], Arch, "include", "c++");
  CppIncludeBases.emplace_back(Base);
  llvm::sys::path::append(CppIncludeBases[1], "include", "c++", Ver);
  CppIncludeBases.emplace_back(GccLibDir);
  llvm::sys::path::append(CppIncludeBases[2], "include", "c++");
  for (auto &CppIncludeBase : CppIncludeBases) {
    CppIncludeBase += llvm::sys::path::get_separator();
    addSystemInclude(DriverArgs, CC1Args, CppIncludeBase);
    addSystemInclude(DriverArgs, CC1Args, CppIncludeBase + Arch);
    addSystemInclude(DriverArgs, CC1Args, CppIncludeBase + "backward");
  }
}
<|endoftext|>