<commit_before>/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
 * Copyright (C) 2015 ScyllaDB
 *
 * Modified by 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 "create_index_statement.hh"
#include "prepared_statement.hh"
#include "validation.hh"
#include "service/storage_proxy.hh"
#include "service/migration_manager.hh"
#include "schema.hh"
#include "schema_builder.hh"
#include "request_validations.hh"
#include "database.hh"
#include "index/target_parser.hh"
#include "gms/feature_service.hh"
#include "cql3/query_processor.hh"

#include <boost/range/adaptor/transformed.hpp>
#include <boost/algorithm/string/join.hpp>

namespace cql3 {

namespace statements {

create_index_statement::create_index_statement(cf_name name,
                                               ::shared_ptr<index_name> index_name,
                                               std::vector<::shared_ptr<index_target::raw>> raw_targets,
                                               ::shared_ptr<index_prop_defs> properties,
                                               bool if_not_exists)
    : schema_altering_statement(name)
    , _index_name(index_name->get_idx())
    , _raw_targets(raw_targets)
    , _properties(properties)
    , _if_not_exists(if_not_exists)
{
}

future<>
create_index_statement::check_access(service::storage_proxy& proxy, const service::client_state& state) const {
    return state.has_column_family_access(proxy.local_db(), keyspace(), column_family(), auth::permission::ALTER);
}

void
create_index_statement::validate(service::storage_proxy& proxy, const service::client_state& state) const
{
    auto& db = proxy.get_db().local();
    auto schema = validation::validate_column_family(db, keyspace(), column_family());

    if (schema->is_counter()) {
        throw exceptions::invalid_request_exception("Secondary indexes are not supported on counter tables");
    }

    if (schema->is_view()) {
        throw exceptions::invalid_request_exception("Secondary indexes are not supported on materialized views");
    }

    if (schema->is_dense()) {
        throw exceptions::invalid_request_exception(
                "Secondary indexes are not supported on COMPACT STORAGE tables that have clustering columns");
    }

    validate_for_local_index(*schema);

    std::vector<::shared_ptr<index_target>> targets;
    for (auto& raw_target : _raw_targets) {
        targets.emplace_back(raw_target->prepare(*schema));
    }

    if (targets.empty() && !_properties->is_custom) {
        throw exceptions::invalid_request_exception("Only CUSTOM indexes can be created without specifying a target column");
    }

    if (targets.size() > 1) {
        validate_targets_for_multi_column_index(targets);
    }

    for (auto& target : targets) {
        auto* ident = std::get_if<::shared_ptr<column_identifier>>(&target->value);
        if (!ident) {
            continue;
        }
        auto cd = schema->get_column_definition((*ident)->name());

        if (cd == nullptr) {
            throw exceptions::invalid_request_exception(
                    format("No column definition found for column {}", target->as_string()));
        }

        //NOTICE(sarna): Should be lifted after resolving issue #2963
        if (cd->is_static()) {
            throw exceptions::invalid_request_exception("Indexing static columns is not implemented yet.");
        }

        if (cd->type->references_duration()) {
            using request_validations::check_false;
            const auto& ty = *cd->type;

            check_false(ty.is_collection(), "Secondary indexes are not supported on collections containing durations");
            check_false(ty.is_user_type(), "Secondary indexes are not supported on UDTs containing durations");
            check_false(ty.is_tuple(), "Secondary indexes are not supported on tuples containing durations");

            // We're a duration.
            throw exceptions::invalid_request_exception("Secondary indexes are not supported on duration columns");
        }

        // Origin TODO: we could lift that limitation
        if ((schema->is_dense() || !schema->thrift().has_compound_comparator()) && cd->is_primary_key()) {
            throw exceptions::invalid_request_exception(
                    "Secondary indexes are not supported on PRIMARY KEY columns in COMPACT STORAGE tables");
        }

        if (cd->kind == column_kind::partition_key && cd->is_on_all_components()) {
            throw exceptions::invalid_request_exception(
                    format("Cannot create secondary index on partition key column {}",
                            target->as_string()));
        }

        if (cd->type->is_multi_cell()) {
            // NOTICE(sarna): should be lifted after #2962 (indexes on non-frozen collections) is implemented
            // NOTICE(kbraun): don't forget about non-frozen user defined types
            throw exceptions::invalid_request_exception(
                    format("Cannot create secondary index on non-frozen collection or UDT column {}", cd->name_as_text()));
        } else if (cd->type->is_collection()) {
            validate_for_frozen_collection(*target);
        } else {
            validate_not_full_index(*target);
            validate_is_values_index_if_target_column_not_collection(cd, *target);
            validate_target_column_is_map_if_index_involves_keys(cd->type->is_map(), *target);
        }
    }

    if (db.existing_index_names(keyspace()).contains(_index_name)) {
        if (_if_not_exists) {
            return;
        } else {
            throw exceptions::invalid_request_exception("Index already exists");
        }
    }

    _properties->validate();
}

void create_index_statement::validate_for_local_index(const schema& schema) const {
    if (!_raw_targets.empty()) {
            if (const auto* index_pk = std::get_if<std::vector<::shared_ptr<column_identifier::raw>>>(&_raw_targets.front()->value)) {
                auto base_pk_identifiers = *index_pk | boost::adaptors::transformed([&schema] (const ::shared_ptr<column_identifier::raw>& raw_ident) {
                    return raw_ident->prepare_column_identifier(schema);
                });
                auto remaining_base_pk_columns = schema.partition_key_columns();
                auto next_expected_base_column = remaining_base_pk_columns.begin();
                for (const auto& ident : base_pk_identifiers) {
                    auto it = schema.columns_by_name().find(ident->name());
                    if (it == schema.columns_by_name().end() || !it->second->is_partition_key()) {
                        throw exceptions::invalid_request_exception(format("Local index definition must contain full partition key only. Redundant column: {}", ident->to_string()));
                    }
                    if (next_expected_base_column == remaining_base_pk_columns.end()) {
                        throw exceptions::invalid_request_exception(format("Duplicate column definition in local index: {}", it->first));
                    }
                    if (&*next_expected_base_column != it->second) {
                        break;
                    }
                    ++next_expected_base_column;
                }
                if (next_expected_base_column != remaining_base_pk_columns.end()) {
                    throw exceptions::invalid_request_exception(format("Local index definition must contain full partition key only. Missing column: {}", next_expected_base_column->name_as_text()));
                }
                if (_raw_targets.size() == 1) {
                    throw exceptions::invalid_request_exception(format("Local index definition must provide an indexed column, not just partition key"));
                }
            }
        }
        for (unsigned i = 1; i < _raw_targets.size(); ++i) {
            if (std::holds_alternative<index_target::raw::multiple_columns>(_raw_targets[i]->value)) {
                throw exceptions::invalid_request_exception(format("Multi-column index targets are currently only supported for partition key"));
            }
        }
}

void create_index_statement::validate_for_frozen_collection(const index_target& target) const
{
    if (target.type != index_target::target_type::full) {
        throw exceptions::invalid_request_exception(
                format("Cannot create index on {} of frozen<map> column {}",
                        index_target::index_option(target.type),
                        target.as_string()));
    }
}

void create_index_statement::validate_not_full_index(const index_target& target) const
{
    if (target.type == index_target::target_type::full) {
        throw exceptions::invalid_request_exception("full() indexes can only be created on frozen collections");
    }
}

void create_index_statement::validate_is_values_index_if_target_column_not_collection(
        const column_definition* cd, const index_target& target) const
{
    if (!cd->type->is_collection()
            && target.type != index_target::target_type::values) {
        throw exceptions::invalid_request_exception(
                format("Cannot create index on {} of column {}; only non-frozen collections support {} indexes",
                       index_target::index_option(target.type),
                       target.as_string(),
                       index_target::index_option(target.type)));
    }
}

void create_index_statement::validate_target_column_is_map_if_index_involves_keys(bool is_map, const index_target& target) const
{
    if (target.type == index_target::target_type::keys
            || target.type == index_target::target_type::keys_and_values) {
        if (!is_map) {
            throw exceptions::invalid_request_exception(
                    format("Cannot create index on {} of column {} with non-map type",
                           index_target::index_option(target.type), target.as_string()));
        }
    }
}

void create_index_statement::validate_targets_for_multi_column_index(std::vector<::shared_ptr<index_target>> targets) const
{
    if (!_properties->is_custom) {
        if (targets.size() > 2 || (targets.size() == 2 && std::holds_alternative<index_target::single_column>(targets.front()->value))) {
            throw exceptions::invalid_request_exception("Only CUSTOM indexes support multiple columns");
        }
    }
    std::unordered_set<sstring> columns;
    for (auto& target : targets) {
        if (columns.contains(target->as_string())) {
            throw exceptions::invalid_request_exception(format("Duplicate column {} in index target list", target->as_string()));
        }
        columns.emplace(target->as_string());
    }
}

future<::shared_ptr<cql_transport::event::schema_change>>
create_index_statement::announce_migration(query_processor& qp) const {
    database& db = qp.db();
    auto schema = db.find_schema(keyspace(), column_family());
    std::vector<::shared_ptr<index_target>> targets;
    for (auto& raw_target : _raw_targets) {
        targets.emplace_back(raw_target->prepare(*schema));
    }
    sstring accepted_name = _index_name;
    if (accepted_name.empty()) {
        std::optional<sstring> index_name_root;
        if (targets.size() == 1) {
           index_name_root = targets[0]->as_string();
        } else if ((targets.size() == 2 && std::holds_alternative<index_target::multiple_columns>(targets.front()->value))) {
            index_name_root = targets[1]->as_string();
        }
        accepted_name = db.get_available_index_name(keyspace(), column_family(), index_name_root);
    }
    index_metadata_kind kind;
    index_options_map index_options;
    if (_properties->is_custom) {
        kind = index_metadata_kind::custom;
        index_options = _properties->get_options();
    } else {
        kind = schema->is_compound() ? index_metadata_kind::composites : index_metadata_kind::keys;
    }
    auto index = make_index_metadata(targets, accepted_name, kind, index_options);
    auto existing_index = schema->find_index_noname(index);
    if (existing_index) {
        if (_if_not_exists) {
            return make_ready_future<::shared_ptr<cql_transport::event::schema_change>>(nullptr);
        } else {
            throw exceptions::invalid_request_exception(
                    format("Index {} is a duplicate of existing index {}", index.name(), existing_index.value().name()));
        }
    }
    auto index_table_name = secondary_index::index_table_name(accepted_name);
    if (db.has_schema(keyspace(), index_table_name)) {
        return make_exception_future<::shared_ptr<cql_transport::event::schema_change>>(
            exceptions::invalid_request_exception(format("Index {} cannot be created, because table {} already exists",
                accepted_name, index_table_name))
        );
    }
    ++_cql_stats->secondary_index_creates;
    schema_builder builder{schema};
    builder.with_index(index);
    return qp.get_migration_manager().announce_column_family_update(
            builder.build(), false, {}, std::nullopt).then([this]() {
        using namespace cql_transport;
        return ::make_shared<event::schema_change>(
                event::schema_change::change_type::UPDATED,
                event::schema_change::target_type::TABLE,
                keyspace(),
                column_family());
    });
}

std::unique_ptr<cql3::statements::prepared_statement>
create_index_statement::prepare(database& db, cql_stats& stats) {
    _cql_stats = &stats;
    return std::make_unique<prepared_statement>(make_shared<create_index_statement>(*this));
}

index_metadata create_index_statement::make_index_metadata(const std::vector<::shared_ptr<index_target>>& targets,
                                                           const sstring& name,
                                                           index_metadata_kind kind,
                                                           const index_options_map& options)
{
    index_options_map new_options = options;
    auto target_option = secondary_index::target_parser::serialize_targets(targets);
    new_options.emplace(index_target::target_option_name, target_option);

    const auto& first_target = targets.front()->value;
    return index_metadata{name, new_options, kind, index_metadata::is_local_index(std::holds_alternative<index_target::multiple_columns>(first_target))};
}

}

}
<commit_msg>secondary index: fix error message which erroneously refered to "map"<commit_after>/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
 * Copyright (C) 2015 ScyllaDB
 *
 * Modified by 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 "create_index_statement.hh"
#include "prepared_statement.hh"
#include "validation.hh"
#include "service/storage_proxy.hh"
#include "service/migration_manager.hh"
#include "schema.hh"
#include "schema_builder.hh"
#include "request_validations.hh"
#include "database.hh"
#include "index/target_parser.hh"
#include "gms/feature_service.hh"
#include "cql3/query_processor.hh"

#include <boost/range/adaptor/transformed.hpp>
#include <boost/algorithm/string/join.hpp>

namespace cql3 {

namespace statements {

create_index_statement::create_index_statement(cf_name name,
                                               ::shared_ptr<index_name> index_name,
                                               std::vector<::shared_ptr<index_target::raw>> raw_targets,
                                               ::shared_ptr<index_prop_defs> properties,
                                               bool if_not_exists)
    : schema_altering_statement(name)
    , _index_name(index_name->get_idx())
    , _raw_targets(raw_targets)
    , _properties(properties)
    , _if_not_exists(if_not_exists)
{
}

future<>
create_index_statement::check_access(service::storage_proxy& proxy, const service::client_state& state) const {
    return state.has_column_family_access(proxy.local_db(), keyspace(), column_family(), auth::permission::ALTER);
}

void
create_index_statement::validate(service::storage_proxy& proxy, const service::client_state& state) const
{
    auto& db = proxy.get_db().local();
    auto schema = validation::validate_column_family(db, keyspace(), column_family());

    if (schema->is_counter()) {
        throw exceptions::invalid_request_exception("Secondary indexes are not supported on counter tables");
    }

    if (schema->is_view()) {
        throw exceptions::invalid_request_exception("Secondary indexes are not supported on materialized views");
    }

    if (schema->is_dense()) {
        throw exceptions::invalid_request_exception(
                "Secondary indexes are not supported on COMPACT STORAGE tables that have clustering columns");
    }

    validate_for_local_index(*schema);

    std::vector<::shared_ptr<index_target>> targets;
    for (auto& raw_target : _raw_targets) {
        targets.emplace_back(raw_target->prepare(*schema));
    }

    if (targets.empty() && !_properties->is_custom) {
        throw exceptions::invalid_request_exception("Only CUSTOM indexes can be created without specifying a target column");
    }

    if (targets.size() > 1) {
        validate_targets_for_multi_column_index(targets);
    }

    for (auto& target : targets) {
        auto* ident = std::get_if<::shared_ptr<column_identifier>>(&target->value);
        if (!ident) {
            continue;
        }
        auto cd = schema->get_column_definition((*ident)->name());

        if (cd == nullptr) {
            throw exceptions::invalid_request_exception(
                    format("No column definition found for column {}", target->as_string()));
        }

        //NOTICE(sarna): Should be lifted after resolving issue #2963
        if (cd->is_static()) {
            throw exceptions::invalid_request_exception("Indexing static columns is not implemented yet.");
        }

        if (cd->type->references_duration()) {
            using request_validations::check_false;
            const auto& ty = *cd->type;

            check_false(ty.is_collection(), "Secondary indexes are not supported on collections containing durations");
            check_false(ty.is_user_type(), "Secondary indexes are not supported on UDTs containing durations");
            check_false(ty.is_tuple(), "Secondary indexes are not supported on tuples containing durations");

            // We're a duration.
            throw exceptions::invalid_request_exception("Secondary indexes are not supported on duration columns");
        }

        // Origin TODO: we could lift that limitation
        if ((schema->is_dense() || !schema->thrift().has_compound_comparator()) && cd->is_primary_key()) {
            throw exceptions::invalid_request_exception(
                    "Secondary indexes are not supported on PRIMARY KEY columns in COMPACT STORAGE tables");
        }

        if (cd->kind == column_kind::partition_key && cd->is_on_all_components()) {
            throw exceptions::invalid_request_exception(
                    format("Cannot create secondary index on partition key column {}",
                            target->as_string()));
        }

        if (cd->type->is_multi_cell()) {
            // NOTICE(sarna): should be lifted after #2962 (indexes on non-frozen collections) is implemented
            // NOTICE(kbraun): don't forget about non-frozen user defined types
            throw exceptions::invalid_request_exception(
                    format("Cannot create secondary index on non-frozen collection or UDT column {}", cd->name_as_text()));
        } else if (cd->type->is_collection()) {
            validate_for_frozen_collection(*target);
        } else {
            validate_not_full_index(*target);
            validate_is_values_index_if_target_column_not_collection(cd, *target);
            validate_target_column_is_map_if_index_involves_keys(cd->type->is_map(), *target);
        }
    }

    if (db.existing_index_names(keyspace()).contains(_index_name)) {
        if (_if_not_exists) {
            return;
        } else {
            throw exceptions::invalid_request_exception("Index already exists");
        }
    }

    _properties->validate();
}

void create_index_statement::validate_for_local_index(const schema& schema) const {
    if (!_raw_targets.empty()) {
            if (const auto* index_pk = std::get_if<std::vector<::shared_ptr<column_identifier::raw>>>(&_raw_targets.front()->value)) {
                auto base_pk_identifiers = *index_pk | boost::adaptors::transformed([&schema] (const ::shared_ptr<column_identifier::raw>& raw_ident) {
                    return raw_ident->prepare_column_identifier(schema);
                });
                auto remaining_base_pk_columns = schema.partition_key_columns();
                auto next_expected_base_column = remaining_base_pk_columns.begin();
                for (const auto& ident : base_pk_identifiers) {
                    auto it = schema.columns_by_name().find(ident->name());
                    if (it == schema.columns_by_name().end() || !it->second->is_partition_key()) {
                        throw exceptions::invalid_request_exception(format("Local index definition must contain full partition key only. Redundant column: {}", ident->to_string()));
                    }
                    if (next_expected_base_column == remaining_base_pk_columns.end()) {
                        throw exceptions::invalid_request_exception(format("Duplicate column definition in local index: {}", it->first));
                    }
                    if (&*next_expected_base_column != it->second) {
                        break;
                    }
                    ++next_expected_base_column;
                }
                if (next_expected_base_column != remaining_base_pk_columns.end()) {
                    throw exceptions::invalid_request_exception(format("Local index definition must contain full partition key only. Missing column: {}", next_expected_base_column->name_as_text()));
                }
                if (_raw_targets.size() == 1) {
                    throw exceptions::invalid_request_exception(format("Local index definition must provide an indexed column, not just partition key"));
                }
            }
        }
        for (unsigned i = 1; i < _raw_targets.size(); ++i) {
            if (std::holds_alternative<index_target::raw::multiple_columns>(_raw_targets[i]->value)) {
                throw exceptions::invalid_request_exception(format("Multi-column index targets are currently only supported for partition key"));
            }
        }
}

void create_index_statement::validate_for_frozen_collection(const index_target& target) const
{
    if (target.type != index_target::target_type::full) {
        throw exceptions::invalid_request_exception(
                format("Cannot create index on {} of frozen collection column {}",
                        index_target::index_option(target.type),
                        target.as_string()));
    }
}

void create_index_statement::validate_not_full_index(const index_target& target) const
{
    if (target.type == index_target::target_type::full) {
        throw exceptions::invalid_request_exception("full() indexes can only be created on frozen collections");
    }
}

void create_index_statement::validate_is_values_index_if_target_column_not_collection(
        const column_definition* cd, const index_target& target) const
{
    if (!cd->type->is_collection()
            && target.type != index_target::target_type::values) {
        throw exceptions::invalid_request_exception(
                format("Cannot create index on {} of column {}; only non-frozen collections support {} indexes",
                       index_target::index_option(target.type),
                       target.as_string(),
                       index_target::index_option(target.type)));
    }
}

void create_index_statement::validate_target_column_is_map_if_index_involves_keys(bool is_map, const index_target& target) const
{
    if (target.type == index_target::target_type::keys
            || target.type == index_target::target_type::keys_and_values) {
        if (!is_map) {
            throw exceptions::invalid_request_exception(
                    format("Cannot create index on {} of column {} with non-map type",
                           index_target::index_option(target.type), target.as_string()));
        }
    }
}

void create_index_statement::validate_targets_for_multi_column_index(std::vector<::shared_ptr<index_target>> targets) const
{
    if (!_properties->is_custom) {
        if (targets.size() > 2 || (targets.size() == 2 && std::holds_alternative<index_target::single_column>(targets.front()->value))) {
            throw exceptions::invalid_request_exception("Only CUSTOM indexes support multiple columns");
        }
    }
    std::unordered_set<sstring> columns;
    for (auto& target : targets) {
        if (columns.contains(target->as_string())) {
            throw exceptions::invalid_request_exception(format("Duplicate column {} in index target list", target->as_string()));
        }
        columns.emplace(target->as_string());
    }
}

future<::shared_ptr<cql_transport::event::schema_change>>
create_index_statement::announce_migration(query_processor& qp) const {
    database& db = qp.db();
    auto schema = db.find_schema(keyspace(), column_family());
    std::vector<::shared_ptr<index_target>> targets;
    for (auto& raw_target : _raw_targets) {
        targets.emplace_back(raw_target->prepare(*schema));
    }
    sstring accepted_name = _index_name;
    if (accepted_name.empty()) {
        std::optional<sstring> index_name_root;
        if (targets.size() == 1) {
           index_name_root = targets[0]->as_string();
        } else if ((targets.size() == 2 && std::holds_alternative<index_target::multiple_columns>(targets.front()->value))) {
            index_name_root = targets[1]->as_string();
        }
        accepted_name = db.get_available_index_name(keyspace(), column_family(), index_name_root);
    }
    index_metadata_kind kind;
    index_options_map index_options;
    if (_properties->is_custom) {
        kind = index_metadata_kind::custom;
        index_options = _properties->get_options();
    } else {
        kind = schema->is_compound() ? index_metadata_kind::composites : index_metadata_kind::keys;
    }
    auto index = make_index_metadata(targets, accepted_name, kind, index_options);
    auto existing_index = schema->find_index_noname(index);
    if (existing_index) {
        if (_if_not_exists) {
            return make_ready_future<::shared_ptr<cql_transport::event::schema_change>>(nullptr);
        } else {
            throw exceptions::invalid_request_exception(
                    format("Index {} is a duplicate of existing index {}", index.name(), existing_index.value().name()));
        }
    }
    auto index_table_name = secondary_index::index_table_name(accepted_name);
    if (db.has_schema(keyspace(), index_table_name)) {
        return make_exception_future<::shared_ptr<cql_transport::event::schema_change>>(
            exceptions::invalid_request_exception(format("Index {} cannot be created, because table {} already exists",
                accepted_name, index_table_name))
        );
    }
    ++_cql_stats->secondary_index_creates;
    schema_builder builder{schema};
    builder.with_index(index);
    return qp.get_migration_manager().announce_column_family_update(
            builder.build(), false, {}, std::nullopt).then([this]() {
        using namespace cql_transport;
        return ::make_shared<event::schema_change>(
                event::schema_change::change_type::UPDATED,
                event::schema_change::target_type::TABLE,
                keyspace(),
                column_family());
    });
}

std::unique_ptr<cql3::statements::prepared_statement>
create_index_statement::prepare(database& db, cql_stats& stats) {
    _cql_stats = &stats;
    return std::make_unique<prepared_statement>(make_shared<create_index_statement>(*this));
}

index_metadata create_index_statement::make_index_metadata(const std::vector<::shared_ptr<index_target>>& targets,
                                                           const sstring& name,
                                                           index_metadata_kind kind,
                                                           const index_options_map& options)
{
    index_options_map new_options = options;
    auto target_option = secondary_index::target_parser::serialize_targets(targets);
    new_options.emplace(index_target::target_option_name, target_option);

    const auto& first_target = targets.front()->value;
    return index_metadata{name, new_options, kind, index_metadata::is_local_index(std::holds_alternative<index_target::multiple_columns>(first_target))};
}

}

}
<|endoftext|>


<commit_before>#define FPL_IMPLEMENTATION
#include <final_platform_layer.h>

#define STB_TRUETYPE_IMPLEMENTATION
#define STBTT_STATIC
#include "stb_truetype.h"

#include <GL/GL.h>

struct BakedCodePoint {
	float s0;
	float t0;
	float s1;
	float t1;
	float w;
	float h;
	float xoffset;
	float yoffset;
	float xadvance;
	char c;
};

int main(int argc, char *argv[]) {
	fplSettings settings = {};
	fplSetDefaultSettings(&settings);
	settings.video.driver = fplVideoDriverType_OpenGL;
	settings.video.graphics.opengl.compabilityFlags = fplOpenGLCompabilityFlags_Legacy;
	if(fplPlatformInit(fplInitFlags_Video, &settings)) {
		glEnable(GL_DEPTH_TEST);
		glDepthFunc(GL_LEQUAL);

		glEnable(GL_BLEND);
		glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

		glEnable(GL_TEXTURE_2D);

		constexpr int AtlasWidth = 512;
		constexpr int AtlasHeight = 512;
		constexpr int CharFirst = 32;
		constexpr int CharLast = 128;
		constexpr float FontHeight = 36.0f;
		constexpr float PixelToUnits = 1.0f / FontHeight;
		constexpr int CharCount = (CharLast - CharFirst) + 1;
		stbtt_bakedchar *bakedChars = (stbtt_bakedchar *)fplMemoryAllocate(CharCount * sizeof(stbtt_bakedchar));
		BakedCodePoint *bakedCodePoints = (BakedCodePoint *)fplMemoryAllocate(CharCount * sizeof(BakedCodePoint));

		GLuint ftex = 0;

		fplFileHandle fontFile;
		if(fplOpenAnsiBinaryFile("c:/windows/fonts/times.ttf", &fontFile)) {
			uint32_t fileSize = fplGetFileSizeFromHandle32(&fontFile);
			uint8_t *ttf_buffer = (uint8_t *)fplMemoryAllocate(fileSize);
			fplReadFileBlock32(&fontFile, fileSize, ttf_buffer, fileSize);
			fplCloseFile(&fontFile);

			uint8_t *temp_bitmap = (uint8_t *)fplMemoryAllocate(AtlasWidth * AtlasHeight);
			stbtt_BakeFontBitmap(ttf_buffer, 0, FontHeight, temp_bitmap, AtlasWidth, AtlasHeight, CharFirst, CharLast, bakedChars);

			float ipw = 1.0f / AtlasWidth, iph = 1.0f / AtlasHeight;
			for(int charIndex = 0; charIndex < CharCount; ++charIndex) {
				const stbtt_bakedchar *b = bakedChars + charIndex;
				BakedCodePoint *cp = bakedCodePoints + charIndex;
				cp->c = charIndex + CharFirst;
				cp->w = (b->x1 - b->x0) * PixelToUnits;
				cp->h = (b->y1 - b->y0) * PixelToUnits;
				cp->xoffset = b->xoff * PixelToUnits;
				cp->yoffset = b->yoff * PixelToUnits;
				cp->xadvance = b->xadvance * PixelToUnits;
				cp->s0 = b->x0 * ipw;
				cp->t0 = b->y0 * iph;
				cp->s1 = b->x1 * ipw;
				cp->t1 = b->y1 * iph;
			}

			glGenTextures(1, &ftex);
			glBindTexture(GL_TEXTURE_2D, ftex);
			glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, 512, 512, 0, GL_ALPHA, GL_UNSIGNED_BYTE, temp_bitmap);
			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
			fplMemoryFree(temp_bitmap);
		}

		bool topDown = false;

		while(fplWindowUpdate()) {
			fplEvent ev;
			while(fplPollEvent(&ev)) {
				switch(ev.type) {
					case fplEventType_Keyboard:
					{
						switch(ev.keyboard.type) {
							case fplKeyboardEventType_KeyUp:
							{
								if(ev.keyboard.mappedKey == fplKey_Space) {
									topDown = !topDown;
								}
							} break;
						}
					} break;
				}
			}

			fplWindowSize winSize;
			if(!fplGetWindowArea(&winSize)) {
				winSize = { 0,0 };
			}

			float w = (float)winSize.width;
			float h = (float)winSize.height;

			glClear(GL_COLOR_BUFFER_BIT);
			glViewport(0, 0, winSize.width, winSize.height);

			glMatrixMode(GL_PROJECTION);
			glLoadIdentity();

			if(topDown) {
				glOrtho(0.0f, w, h, 0, 0.0f, 1.0f);
			} else {
				glOrtho(0.0f, w, 0, h, 0.0f, 1.0f);
			}

			glMatrixMode(GL_MODELVIEW);
			glLoadIdentity();

			float lw = FPL_MIN(w, h) * 0.25f;
			glColor4f(1, 1, 0, 0.25f);
			glLineWidth(1);
			glBegin(GL_LINES);
			glVertex2f(w * 0.5f - lw * 0.5f, h * 0.5f);
			glVertex2f(w * 0.5f + lw * 0.5f, h * 0.5f);
			glVertex2f(w * 0.5f, h * 0.5f - lw * 0.5f);
			glVertex2f(w * 0.5f, h * 0.5f + lw * 0.5f);
			glEnd();
			glLineWidth(1);

			float fontScale = 64.0f;

			const char *text = "Hello World!";

			float x = w * 0.5f;
			float y = h * 0.5f;
			while(*text) {
				if(*text >= CharFirst && *text <= CharLast) {
					char c = *text;
					int charIndex = c - CharFirst;
					stbtt_aligned_quad q0, q1;

					const stbtt_bakedchar *b0 = bakedChars + charIndex;
					float ipw = 1.0f / AtlasWidth, iph = 1.0f / AtlasHeight;
					float xoffset = b0->xoff * PixelToUnits;
					float yoffset = b0->yoff * PixelToUnits;
					float boundW = (b0->x1 - b0->x0) * PixelToUnits;
					float boundH = (b0->y1 - b0->y0) * PixelToUnits;
					float advance0 = b0->xadvance * PixelToUnits;

					q0.x0 = xoffset * fontScale;
					q0.x1 = q0.x0 + boundW * fontScale;
					if(topDown) {
						q0.y0 = yoffset * fontScale;
						q0.y1 = q0.y0 + boundH * fontScale;
					} else {
						q0.y0 = -yoffset * fontScale;
						q0.y1 = q0.y0 - boundH * fontScale;
					}
					q0.s0 = b0->x0 * ipw;
					q0.t0 = b0->y0 * iph;
					q0.s1 = b0->x1 * ipw;
					q0.t1 = b0->y1 * iph;

					const BakedCodePoint *b1 = bakedCodePoints + charIndex;
					q1.s0 = b1->s0;
					q1.t0 = b1->t0;
					q1.s1 = b1->s1;
					q1.t1 = b1->t1;
					q1.x0 = b1->xoffset * fontScale;
					q1.x1 = q1.x0 + b1->w * fontScale;
					if(topDown) {
						q1.y0 = b1->yoffset * fontScale;
						q1.y1 = q1.y0 + b1->h * fontScale;
					} else {
						q1.y0 = -b1->yoffset * fontScale;
						q1.y1 = q1.y0 - b1->h * fontScale;
					}
					float advance1 = b1->xadvance;

					assert(b1->c == c);
					assert(q0.s0 == q1.s0);
					assert(q0.s1 == q1.s1);
					assert(q0.t0 == q1.t0);
					assert(q0.t1 == q1.t1);
					assert(q0.x0 == q1.x0);
					assert(q0.x1 == q1.x1);
					assert(q0.y0 == q1.y0);
					assert(q0.y1 == q1.y1);

#if 0
					q1 = q0;
					advance1 = advance0;
#endif

					glBindTexture(GL_TEXTURE_2D, ftex);
					glColor4f(1, 1, 1, 1);
					glBegin(GL_QUADS);
					glTexCoord2f(q1.s1, q1.t1); glVertex2f(x + q1.x1, y + q1.y1);
					glTexCoord2f(q1.s0, q1.t1); glVertex2f(x + q1.x0, y + q1.y1);
					glTexCoord2f(q1.s0, q1.t0); glVertex2f(x + q1.x0, y + q1.y0);
					glTexCoord2f(q1.s1, q1.t0); glVertex2f(x + q1.x1, y + q1.y0);
					glEnd();
					glBindTexture(GL_TEXTURE_2D, 0);

					glColor4f(0, 1.0f, 0.0f, 1.0f);
					glLineWidth(1.0f);
					glBegin(GL_LINE_LOOP);
					glVertex2f(x + q1.x1, y + q1.y1);
					glVertex2f(x + q1.x0, y + q1.y1);
					glVertex2f(x + q1.x0, y + q1.y0);
					glVertex2f(x + q1.x1, y + q1.y0);
					glEnd();
					glLineWidth(1.0f);

					x += advance1 * fontScale;
				}
				++text;
			}

			fplVideoFlip();
		}
		fplPlatformRelease();
	}
	return 0;
}<commit_msg>Fixed wrong event type for keyboard button in font rendering app<commit_after>#define FPL_IMPLEMENTATION
#include <final_platform_layer.h>

#define STB_TRUETYPE_IMPLEMENTATION
#define STBTT_STATIC
#include "stb_truetype.h"

#include <GL/GL.h>

struct BakedCodePoint {
	float s0;
	float t0;
	float s1;
	float t1;
	float w;
	float h;
	float xoffset;
	float yoffset;
	float xadvance;
	char c;
};

int main(int argc, char *argv[]) {
	fplSettings settings = {};
	fplSetDefaultSettings(&settings);
	settings.video.driver = fplVideoDriverType_OpenGL;
	settings.video.graphics.opengl.compabilityFlags = fplOpenGLCompabilityFlags_Legacy;
	if(fplPlatformInit(fplInitFlags_Video, &settings)) {
		glEnable(GL_DEPTH_TEST);
		glDepthFunc(GL_LEQUAL);

		glEnable(GL_BLEND);
		glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

		glEnable(GL_TEXTURE_2D);

		constexpr int AtlasWidth = 512;
		constexpr int AtlasHeight = 512;
		constexpr int CharFirst = 32;
		constexpr int CharLast = 128;
		constexpr float FontHeight = 36.0f;
		constexpr float PixelToUnits = 1.0f / FontHeight;
		constexpr int CharCount = (CharLast - CharFirst) + 1;
		stbtt_bakedchar *bakedChars = (stbtt_bakedchar *)fplMemoryAllocate(CharCount * sizeof(stbtt_bakedchar));
		BakedCodePoint *bakedCodePoints = (BakedCodePoint *)fplMemoryAllocate(CharCount * sizeof(BakedCodePoint));

		GLuint ftex = 0;

		fplFileHandle fontFile;
		if(fplOpenAnsiBinaryFile("c:/windows/fonts/times.ttf", &fontFile)) {
			uint32_t fileSize = fplGetFileSizeFromHandle32(&fontFile);
			uint8_t *ttf_buffer = (uint8_t *)fplMemoryAllocate(fileSize);
			fplReadFileBlock32(&fontFile, fileSize, ttf_buffer, fileSize);
			fplCloseFile(&fontFile);

			uint8_t *temp_bitmap = (uint8_t *)fplMemoryAllocate(AtlasWidth * AtlasHeight);
			stbtt_BakeFontBitmap(ttf_buffer, 0, FontHeight, temp_bitmap, AtlasWidth, AtlasHeight, CharFirst, CharLast, bakedChars);

			float ipw = 1.0f / AtlasWidth, iph = 1.0f / AtlasHeight;
			for(int charIndex = 0; charIndex < CharCount; ++charIndex) {
				const stbtt_bakedchar *b = bakedChars + charIndex;
				BakedCodePoint *cp = bakedCodePoints + charIndex;
				cp->c = charIndex + CharFirst;
				cp->w = (b->x1 - b->x0) * PixelToUnits;
				cp->h = (b->y1 - b->y0) * PixelToUnits;
				cp->xoffset = b->xoff * PixelToUnits;
				cp->yoffset = b->yoff * PixelToUnits;
				cp->xadvance = b->xadvance * PixelToUnits;
				cp->s0 = b->x0 * ipw;
				cp->t0 = b->y0 * iph;
				cp->s1 = b->x1 * ipw;
				cp->t1 = b->y1 * iph;
			}

			glGenTextures(1, &ftex);
			glBindTexture(GL_TEXTURE_2D, ftex);
			glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, 512, 512, 0, GL_ALPHA, GL_UNSIGNED_BYTE, temp_bitmap);
			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
			fplMemoryFree(temp_bitmap);
		}

		bool topDown = false;

		while(fplWindowUpdate()) {
			fplEvent ev;
			while(fplPollEvent(&ev)) {
				switch(ev.type) {
					case fplEventType_Keyboard:
					{
						switch(ev.keyboard.type) {
							case fplKeyboardEventType_Button:
							{
								if(ev.keyboard.buttonState == fplButtonState_Release) {
									if(ev.keyboard.mappedKey == fplKey_Space) {
										topDown = !topDown;
									}
								}
							} break;
						}
					} break;
				}
			}

			fplWindowSize winSize;
			if(!fplGetWindowArea(&winSize)) {
				winSize = { 0,0 };
			}

			float w = (float)winSize.width;
			float h = (float)winSize.height;

			glClear(GL_COLOR_BUFFER_BIT);
			glViewport(0, 0, winSize.width, winSize.height);

			glMatrixMode(GL_PROJECTION);
			glLoadIdentity();

			if(topDown) {
				glOrtho(0.0f, w, h, 0, 0.0f, 1.0f);
			} else {
				glOrtho(0.0f, w, 0, h, 0.0f, 1.0f);
			}

			glMatrixMode(GL_MODELVIEW);
			glLoadIdentity();

			float lw = FPL_MIN(w, h) * 0.25f;
			glColor4f(1, 1, 0, 0.25f);
			glLineWidth(1);
			glBegin(GL_LINES);
			glVertex2f(w * 0.5f - lw * 0.5f, h * 0.5f);
			glVertex2f(w * 0.5f + lw * 0.5f, h * 0.5f);
			glVertex2f(w * 0.5f, h * 0.5f - lw * 0.5f);
			glVertex2f(w * 0.5f, h * 0.5f + lw * 0.5f);
			glEnd();
			glLineWidth(1);

			float fontScale = 64.0f;

			const char *text = "Hello World!";

			float x = w * 0.5f;
			float y = h * 0.5f;
			while(*text) {
				if(*text >= CharFirst && *text <= CharLast) {
					char c = *text;
					int charIndex = c - CharFirst;
					stbtt_aligned_quad q0, q1;

					const stbtt_bakedchar *b0 = bakedChars + charIndex;
					float ipw = 1.0f / AtlasWidth, iph = 1.0f / AtlasHeight;
					float xoffset = b0->xoff * PixelToUnits;
					float yoffset = b0->yoff * PixelToUnits;
					float boundW = (b0->x1 - b0->x0) * PixelToUnits;
					float boundH = (b0->y1 - b0->y0) * PixelToUnits;
					float advance0 = b0->xadvance * PixelToUnits;

					q0.x0 = xoffset * fontScale;
					q0.x1 = q0.x0 + boundW * fontScale;
					if(topDown) {
						q0.y0 = yoffset * fontScale;
						q0.y1 = q0.y0 + boundH * fontScale;
					} else {
						q0.y0 = -yoffset * fontScale;
						q0.y1 = q0.y0 - boundH * fontScale;
					}
					q0.s0 = b0->x0 * ipw;
					q0.t0 = b0->y0 * iph;
					q0.s1 = b0->x1 * ipw;
					q0.t1 = b0->y1 * iph;

					const BakedCodePoint *b1 = bakedCodePoints + charIndex;
					q1.s0 = b1->s0;
					q1.t0 = b1->t0;
					q1.s1 = b1->s1;
					q1.t1 = b1->t1;
					q1.x0 = b1->xoffset * fontScale;
					q1.x1 = q1.x0 + b1->w * fontScale;
					if(topDown) {
						q1.y0 = b1->yoffset * fontScale;
						q1.y1 = q1.y0 + b1->h * fontScale;
					} else {
						q1.y0 = -b1->yoffset * fontScale;
						q1.y1 = q1.y0 - b1->h * fontScale;
					}
					float advance1 = b1->xadvance;

					assert(b1->c == c);
					assert(q0.s0 == q1.s0);
					assert(q0.s1 == q1.s1);
					assert(q0.t0 == q1.t0);
					assert(q0.t1 == q1.t1);
					assert(q0.x0 == q1.x0);
					assert(q0.x1 == q1.x1);
					assert(q0.y0 == q1.y0);
					assert(q0.y1 == q1.y1);

#if 0
					q1 = q0;
					advance1 = advance0;
#endif

					glBindTexture(GL_TEXTURE_2D, ftex);
					glColor4f(1, 1, 1, 1);
					glBegin(GL_QUADS);
					glTexCoord2f(q1.s1, q1.t1); glVertex2f(x + q1.x1, y + q1.y1);
					glTexCoord2f(q1.s0, q1.t1); glVertex2f(x + q1.x0, y + q1.y1);
					glTexCoord2f(q1.s0, q1.t0); glVertex2f(x + q1.x0, y + q1.y0);
					glTexCoord2f(q1.s1, q1.t0); glVertex2f(x + q1.x1, y + q1.y0);
					glEnd();
					glBindTexture(GL_TEXTURE_2D, 0);

					glColor4f(0, 1.0f, 0.0f, 1.0f);
					glLineWidth(1.0f);
					glBegin(GL_LINE_LOOP);
					glVertex2f(x + q1.x1, y + q1.y1);
					glVertex2f(x + q1.x0, y + q1.y1);
					glVertex2f(x + q1.x0, y + q1.y0);
					glVertex2f(x + q1.x1, y + q1.y0);
					glEnd();
					glLineWidth(1.0f);

					x += advance1 * fontScale;
				}
				++text;
			}

			fplVideoFlip();
		}
		fplPlatformRelease();
	}
	return 0;
}<|endoftext|>


<commit_before>#include <common/CaselessStringComparer.h>

#include <intrin.h>

namespace caprica {

alignas(128) const uint64_t charToLowerMap[] = {
  ' ', '!', '"', '#', '$', '%', '&', '\'',
  '(', ')', '*', '+', ',', '-', '.', '/',
  '0', '1', '2', '3', '4', '5', '6', '7',
  '8', '9', ':', ';', '<', '=', '>', '?',
  '@', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
  'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
  'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
  'x', 'y', 'z', '[', '\\', ']', '^', '_',
  '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
  'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
  'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
  'x', 'y', 'z', '{', '|', '}', '~'
};

void identifierToLower(std::string& str) {
  char* data = (char*)str.c_str();
  auto end = str.size();
  for (size_t i = 0; i < end; i++) {
    *data = (char)(charToLowerMap - 0x20)[*data];
  }
}

bool caselessEq(boost::string_ref a, boost::string_ref b) {
  if (a.size() != b.size())
    return false;
  const char* __restrict strA = a.data();
  const int64_t strBOff = b.data() - strA;
  size_t lenLeft = a.size();
  while (lenLeft) {
    if ((charToLowerMap - 0x20)[*strA] != (charToLowerMap - 0x20)[*(strA + strBOff)])
      return false;
    strA++;
    lenLeft--;
  }

  return true;
}

bool pathEq(boost::string_ref a, boost::string_ref b) {
  // TODO: Ensure in lower-ascii range.
  return caselessEq(a, b);
}

bool pathEq(boost::string_ref a, const char* b) {
  // TODO: Ensure in lower-ascii range.
  return caselessEq(a, boost::string_ref(b));
}

bool pathEq(boost::string_ref a, const identifier_ref& b) {
  // TODO: Ensure in lower-ascii range.
  return caselessEq(a, boost::string_ref(b.data(), b.size()));
}

bool pathEq(const identifier_ref& a, const identifier_ref& b) {
  // TODO: Ensure in lower-ascii range.
  return caselessEq(boost::string_ref(a.data(), a.size()), boost::string_ref(b.data(), b.size()));
}

alignas(128) static const __m128i spaces{
  ' ', ' ', ' ', ' ',
  ' ', ' ', ' ', ' ',
  ' ', ' ', ' ', ' ',
  ' ', ' ', ' ', ' '
};

template<bool isNullTerminated>
ALWAYS_INLINE
bool CaselessIdentifierEqual::equal(const char* a, const char* b, size_t len) {
  // This uses the SSE2 instructions movdqa, movdqu, pcmpeqb, por, pmovmskb,
  // and is safe to use on any 64-bit CPU.
  // This returns via goto's because of how MSVC does codegen.
  if (a == b)
    goto ReturnTrue;
  const char* __restrict strA = a;
  const int64_t strBOff = b - a;
  size_t lenLeft = len;
  if (isNullTerminated) {
    // We know the string is null-terminated, so we can align to 2.
    lenLeft = ((len + 1) & 0xFFFFFFFFFFFFFFFEULL);
  }
  while (lenLeft >= 16) {
    auto vA = _mm_or_si128(_mm_loadu_si128((__m128i*)strA), spaces);
    auto vB = _mm_or_si128(_mm_loadu_si128((__m128i*)(strA + strBOff)), spaces);
    if (_mm_movemask_epi8(_mm_cmpeq_epi8(vA, vB)) != 0xFFFF)
      goto ReturnFalse;
    strA += 16;
    lenLeft -= 16;
  }
  // We don't need to adjust the lenLeft for each of these.
  if (lenLeft & 8) {
    if ((*(uint64_t*)strA | 0x2020202020202020ULL) != (*(uint64_t*)(strA + strBOff) | 0x2020202020202020ULL))
      goto ReturnFalse;
    strA += 8;
  }
  if (lenLeft & 4) {
    if ((*(uint32_t*)strA | 0x20202020U) != (*(uint32_t*)(strA + strBOff) | 0x20202020U))
      goto ReturnFalse;
    strA += 4;
  }
  if (lenLeft & 2) {
    if ((*(uint16_t*)strA | 0x2020) != (*(uint16_t*)(strA + strBOff) | 0x2020))
      goto ReturnFalse;
    if (!isNullTerminated)
      strA += 2;
  }
  if (!isNullTerminated) {
    if (lenLeft & 1) {
      if ((*(uint8_t*)strA | 0x20) != (*(uint8_t*)(strA + strBOff) | 0x20))
        goto ReturnFalse;
    }
  }
ReturnTrue:
  return true;
ReturnFalse:
  return false;
}
template bool CaselessIdentifierEqual::equal<true>(const char*, const char*, size_t);
template bool CaselessIdentifierEqual::equal<false>(const char*, const char*, size_t);

template<bool isNullTerminated>
static bool idEq(const char* a, size_t sA, const char* b, size_t sB) {
  if (sA != sB)
    return false;
  return CaselessIdentifierEqual::equal<isNullTerminated>(a, b, sB);
}

bool idEq(const char* a, const char* b) {
  return idEq<true>(a, strlen(a), b, strlen(b));
}
bool idEq(const char* a, const std::string& b) {
  return idEq<true>(a, strlen(a), b.c_str(), b.size());
}
bool idEq(const std::string& a, const char* b) {
  return idEq<true>(a.c_str(), a.size(), b, strlen(b));
}
NEVER_INLINE
bool idEq(const std::string& a, const std::string& b) {
  return idEq<true>(a.c_str(), a.size(), b.c_str(), b.size());
}
NEVER_INLINE
bool idEq(boost::string_ref a, boost::string_ref b) {
  return idEq<false>(a.data(), a.size(), b.data(), b.size());
}

NEVER_INLINE
size_t CaselessStringHasher::doCaselessHash(const char* k, size_t len) {
  // Using FNV-1a hash, the same as the MSVC std lib hash of strings.
  static_assert(sizeof(size_t) == 8, "This is 64-bit only!");
  constexpr size_t offsetBasis = 0xcbf29ce484222325ULL;
  constexpr size_t prime = 0x100000001B3ULL;
  const char* cStr = k;
  const char* eStr = k + len;

  size_t val = offsetBasis;
#if 0
  for (; cStr < eStr; cStr++) {
    // This is safe only because we are hashing
    // identifiers with a known set of characters.
    val ^= (size_t)(*cStr | 32);
    val *= prime;
  }
#else
  for (; cStr < eStr; cStr++) {
    val ^= (size_t)(charToLowerMap - 0x20)[*cStr];
    val *= prime;
  }
#endif
  return val;
}

size_t CaselessPathHasher::doPathHash(const char* k, size_t len) {
  // TODO: Ensure in ascii.
  return CaselessStringHasher::doCaselessHash(k, len);
}

template<bool isNullTerminated>
ALWAYS_INLINE
uint32_t CaselessIdentifierHasher::hash(const char* s, size_t len) {
  const char* cStr = s;

  size_t lenLeft = len;
  if (isNullTerminated) {
    // We know the string is null-terminated, so we can align to 2.
    lenLeft = ((len + 1) & 0xFFFFFFFFFFFFFFFEULL);
  }
  size_t iterCount = lenLeft >> 2;
  uint32_t val = 0x84222325U;
  size_t i = iterCount;
  while (i)
    val = _mm_crc32_u32(val, ((uint32_t*)cStr)[--i] | 0x20202020);
  if (lenLeft & 2) {
    val = _mm_crc32_u16(val, *(uint16_t*)(cStr + (iterCount * 4)) | (uint16_t)0x2020);
    if (!isNullTerminated) {
      // This is duplicated like this because it ends up cost-free when compared to
      // any other methods.
      if (lenLeft & 1)
        val = _mm_crc32_u8(val, *(uint8_t*)(cStr + (iterCount * 4) + 2) | (uint8_t)0x20);
    }
  } else if (!isNullTerminated && (lenLeft & 1)) {
    val = _mm_crc32_u8(val, *(uint8_t*)(cStr + (iterCount * 4)) | (uint8_t)0x20);
  }
  return val;
}

template uint32_t CaselessIdentifierHasher::hash<true>(const char*, size_t);
template uint32_t CaselessIdentifierHasher::hash<false>(const char*, size_t);

NEVER_INLINE
size_t CaselessIdentifierHasher::operator()(const std::string& k) const {
  auto r = CaselessIdentifierHasher::hash<true>(k.c_str(), k.size());
  return ((size_t)r << 32) | r;
}

NEVER_INLINE
size_t CaselessIdentifierHasher::operator()(boost::string_ref k) const {
  auto r = CaselessIdentifierHasher::hash<false>(k.data(), k.size());
  return ((size_t)r << 32) | r;
}

}
<commit_msg>Adjust CaselessStringComparer to not trigger warnings on warning level 4.<commit_after>#include <common/CaselessStringComparer.h>

#include <intrin.h>

namespace caprica {

alignas(128) const uint64_t charToLowerMap[] = {
  ' ', '!', '"', '#', '$', '%', '&', '\'',
  '(', ')', '*', '+', ',', '-', '.', '/',
  '0', '1', '2', '3', '4', '5', '6', '7',
  '8', '9', ':', ';', '<', '=', '>', '?',
  '@', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
  'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
  'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
  'x', 'y', 'z', '[', '\\', ']', '^', '_',
  '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
  'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
  'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
  'x', 'y', 'z', '{', '|', '}', '~'
};

void identifierToLower(std::string& str) {
  char* data = (char*)str.c_str();
  auto end = str.size();
  for (size_t i = 0; i < end; i++) {
    *data = (char)(charToLowerMap - 0x20)[*data];
  }
}

bool caselessEq(boost::string_ref a, boost::string_ref b) {
  if (a.size() != b.size())
    return false;
  const char* __restrict strA = a.data();
  const int64_t strBOff = b.data() - strA;
  size_t lenLeft = a.size();
  while (lenLeft) {
    if ((charToLowerMap - 0x20)[*strA] != (charToLowerMap - 0x20)[*(strA + strBOff)])
      return false;
    strA++;
    lenLeft--;
  }

  return true;
}

bool pathEq(boost::string_ref a, boost::string_ref b) {
  // TODO: Ensure in lower-ascii range.
  return caselessEq(a, b);
}

bool pathEq(boost::string_ref a, const char* b) {
  // TODO: Ensure in lower-ascii range.
  return caselessEq(a, boost::string_ref(b));
}

bool pathEq(boost::string_ref a, const identifier_ref& b) {
  // TODO: Ensure in lower-ascii range.
  return caselessEq(a, boost::string_ref(b.data(), b.size()));
}

bool pathEq(const identifier_ref& a, const identifier_ref& b) {
  // TODO: Ensure in lower-ascii range.
  return caselessEq(boost::string_ref(a.data(), a.size()), boost::string_ref(b.data(), b.size()));
}

alignas(128) static const __m128i spaces{
  ' ', ' ', ' ', ' ',
  ' ', ' ', ' ', ' ',
  ' ', ' ', ' ', ' ',
  ' ', ' ', ' ', ' '
};

template<bool isNullTerminated>
ALWAYS_INLINE
bool CaselessIdentifierEqual::equal(const char* a, const char* b, size_t len) {
  // This uses the SSE2 instructions movdqa, movdqu, pcmpeqb, por, pmovmskb,
  // and is safe to use on any 64-bit CPU.
  // This returns via goto's because of how MSVC does codegen.
  if (a == b)
    goto ReturnTrue;
  const char* __restrict strA = a;
  const int64_t strBOff = b - a;
  size_t lenLeft = len;
  if (isNullTerminated) {
    // We know the string is null-terminated, so we can align to 2.
    lenLeft = ((len + 1) & 0xFFFFFFFFFFFFFFFEULL);
  }
  while (lenLeft >= 16) {
    auto vA = _mm_or_si128(_mm_loadu_si128((__m128i*)strA), spaces);
    auto vB = _mm_or_si128(_mm_loadu_si128((__m128i*)(strA + strBOff)), spaces);
    if (_mm_movemask_epi8(_mm_cmpeq_epi8(vA, vB)) != 0xFFFF)
      goto ReturnFalse;
    strA += 16;
    lenLeft -= 16;
  }
  // We don't need to adjust the lenLeft for each of these.
  if (lenLeft & 8) {
    if ((*(uint64_t*)strA | 0x2020202020202020ULL) != (*(uint64_t*)(strA + strBOff) | 0x2020202020202020ULL))
      goto ReturnFalse;
    strA += 8;
  }
  if (lenLeft & 4) {
    if ((*(uint32_t*)strA | 0x20202020U) != (*(uint32_t*)(strA + strBOff) | 0x20202020U))
      goto ReturnFalse;
    strA += 4;
  }
  if (lenLeft & 2) {
    if ((*(uint16_t*)strA | 0x2020) != (*(uint16_t*)(strA + strBOff) | 0x2020))
      goto ReturnFalse;
    if (!isNullTerminated)
      strA += 2;
  }
  if (!isNullTerminated) {
    if (lenLeft & 1) {
      if ((*(uint8_t*)strA | 0x20) != (*(uint8_t*)(strA + strBOff) | 0x20))
        goto ReturnFalse;
    }
  }
ReturnTrue:
  return true;
ReturnFalse:
  return false;
}
template bool CaselessIdentifierEqual::equal<true>(const char*, const char*, size_t);
template bool CaselessIdentifierEqual::equal<false>(const char*, const char*, size_t);

template<bool isNullTerminated>
static bool idEq(const char* a, size_t sA, const char* b, size_t sB) {
  if (sA != sB)
    return false;
  return CaselessIdentifierEqual::equal<isNullTerminated>(a, b, sB);
}

bool idEq(const char* a, const char* b) {
  return idEq<true>(a, strlen(a), b, strlen(b));
}
bool idEq(const char* a, const std::string& b) {
  return idEq<true>(a, strlen(a), b.c_str(), b.size());
}
bool idEq(const std::string& a, const char* b) {
  return idEq<true>(a.c_str(), a.size(), b, strlen(b));
}
NEVER_INLINE
bool idEq(const std::string& a, const std::string& b) {
  return idEq<true>(a.c_str(), a.size(), b.c_str(), b.size());
}
NEVER_INLINE
bool idEq(boost::string_ref a, boost::string_ref b) {
  return idEq<false>(a.data(), a.size(), b.data(), b.size());
}

NEVER_INLINE
size_t CaselessStringHasher::doCaselessHash(const char* k, size_t len) {
  // Using FNV-1a hash, the same as the MSVC std lib hash of strings.
  static_assert(sizeof(size_t) == 8, "This is 64-bit only!");
  constexpr size_t offsetBasis = 0xcbf29ce484222325ULL;
  constexpr size_t prime = 0x100000001B3ULL;
  const char* cStr = k;
  const char* eStr = k + len;

  size_t val = offsetBasis;
#if 0
  for (; cStr < eStr; cStr++) {
    // This is safe only because we are hashing
    // identifiers with a known set of characters.
    val ^= (size_t)(*cStr | 32);
    val *= prime;
  }
#else
  for (; cStr < eStr; cStr++) {
    val ^= (size_t)(charToLowerMap - 0x20)[*cStr];
    val *= prime;
  }
#endif
  return val;
}

size_t CaselessPathHasher::doPathHash(const char* k, size_t len) {
  // TODO: Ensure in ascii.
  return CaselessStringHasher::doCaselessHash(k, len);
}

template<bool isNullTerminated>
ALWAYS_INLINE
uint32_t CaselessIdentifierHasher::hash(const char* s, size_t len) {
  const char* cStr = s;

  size_t lenLeft = len;
  if (isNullTerminated) {
    // We know the string is null-terminated, so we can align to 2.
    lenLeft = ((len + 1) & 0xFFFFFFFFFFFFFFFEULL);
  }
  size_t iterCount = lenLeft >> 2;
  uint32_t val = 0x84222325U;
  size_t i = iterCount;
  while (i)
    val = _mm_crc32_u32(val, ((uint32_t*)cStr)[--i] | 0x20202020);
  if (lenLeft & 2) {
    val = _mm_crc32_u16(val, *(uint16_t*)(cStr + (iterCount * 4)) | (uint16_t)0x2020);
    if (!isNullTerminated) {
      // This is duplicated like this because it ends up cost-free when compared to
      // any other methods.
      if (lenLeft & 1)
        val = _mm_crc32_u8(val, *(uint8_t*)(cStr + (iterCount * 4) + 2) | (uint8_t)0x20);
    }
  } else if (!isNullTerminated) {
    if (lenLeft & 1)
      val = _mm_crc32_u8(val, *(uint8_t*)(cStr + (iterCount * 4)) | (uint8_t)0x20);
  }
  return val;
}

template uint32_t CaselessIdentifierHasher::hash<true>(const char*, size_t);
template uint32_t CaselessIdentifierHasher::hash<false>(const char*, size_t);

NEVER_INLINE
size_t CaselessIdentifierHasher::operator()(const std::string& k) const {
  auto r = CaselessIdentifierHasher::hash<true>(k.c_str(), k.size());
  return ((size_t)r << 32) | r;
}

NEVER_INLINE
size_t CaselessIdentifierHasher::operator()(boost::string_ref k) const {
  auto r = CaselessIdentifierHasher::hash<false>(k.data(), k.size());
  return ((size_t)r << 32) | r;
}

}
<|endoftext|>


<commit_before>/*=========================================================================

  Program:   Monteverdi2
  Language:  C++

  Copyright (c) Centre National d'Etudes Spatiales. All rights reserved.
  See Copyright.txt for details.

  Monteverdi2 is distributed under the CeCILL licence version 2. See
  Licence_CeCILL_V2-en.txt or
  http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt for more details.

  This software is distributed WITHOUT ANY WARRANTY; without even
  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
  PURPOSE.  See the above copyright notices for more information.

=========================================================================*/
#include "mvdStackedLayerModel.h"

/*****************************************************************************/
/* INCLUDE SECTION                                                           */

//
// Qt includes (sorted by alphabetic order)
//// Must be included before system/custom includes.

//
// System includes (sorted by alphabetic order)
#include <algorithm>
#include <sstream>

//
// ITK includes (sorted by alphabetic order)

//
// OTB includes (sorted by alphabetic order)

//
// Monteverdi includes (sorted by alphabetic order)
#include "Core/mvdAbstractLayerModel.h"
#include "Core/mvdAlgorithm.h"

namespace mvd
{
/*
  TRANSLATOR mvd::StackedLayerModel

  Necessary for lupdate to be aware of C++ namespaces.

  Context comment for translator.
*/

/*****************************************************************************/
/* CONSTANTS                                                                 */

namespace
{
} // end of anonymous namespace.

/*****************************************************************************/
/* STATIC IMPLEMENTATION SECTION                                             */

StackedLayerModel::SizeType
StackedLayerModel::m_LayerCount = 0;

const StackedLayerModel::KeyType
StackedLayerModel::NIL_KEY;

const StackedLayerModel::SizeType
StackedLayerModel::NIL_INDEX = -1;

/*****************************************************************************/
/* CLASS IMPLEMENTATION SECTION                                              */

/*******************************************************************************/
StackedLayerModel
::StackedLayerModel( QObject* parent ) :
  AbstractModel( parent ),
  m_LayerModels(),
  m_Keys(),
  m_Current( StackedLayerModel::NIL_INDEX ),
  m_Reference( StackedLayerModel::NIL_INDEX )
{
}

/*******************************************************************************/
StackedLayerModel
::~StackedLayerModel()
{
}

/*****************************************************************************/
std::string
StackedLayerModel
::Add( AbstractLayerModel * model )
{
  assert( model!=NULL );

  if( model==NULL )
    {
    throw
      std::runtime_error(
        ToStdString(
          tr( "Cannot insert NULL AbstractLayerModel." )
        )
      );
    }

  std::string key( StackedLayerModel::GenerateKey( model ) );
  assert( !key.empty() );

  if( key.empty() )
    {
    throw
      std::runtime_error(
        ToStdString(
          tr( "Failed to generate string key for '%1'." )
          .arg( model->metaObject()->className() )
        )
      );
    }

  emit ContentAboutToBeChanged();

  ClearPixelInfos();

  m_LayerModels.insert( LayerModelMap::value_type( key, model ) );
  m_Keys.push_back( key );

  emit LayerAdded( m_Keys.size() - 1 );
  emit ContentChanged();

  return key;
}

/*****************************************************************************/
void
StackedLayerModel
::Clear()
{
  bool emitSignal0 = !IsEmpty();
  bool emitSignal1 = !IsEmpty() && m_Current<GetCount();
  bool emitSignal2 = !IsEmpty() && m_Reference<GetCount();

  if( emitSignal0 )
    emit ContentAboutToBeReset();

  //
  // Clear current.
  if( emitSignal1 )
    {
    emit CurrentAboutToBeChanged( StackedLayerModel::NIL_INDEX );
    emit AboutToChangeSelectedLayerModel( KeyType() );
    }

  m_Current = StackedLayerModel::NIL_INDEX;

  if( emitSignal1 )
    {
    emit CurrentChanged( m_Current );
    emit SelectedLayerModelChanged( KeyType() );
    }

  //
  // Clear reference.
  if( emitSignal2 )
    emit ReferenceAboutToBeChanged( StackedLayerModel::NIL_INDEX );

  m_Reference = StackedLayerModel::NIL_INDEX;

  if( emitSignal2 )
    emit ReferenceChanged( m_Reference );

  //
  // Clear content.
  ClearPixelInfos();

  for( LayerModelMap::iterator it( m_LayerModels.begin() );
       it!=m_LayerModels.end();
       ++it )
    {
    assert( it->second!=NULL );

    if( it->second->parent()==this )
      {
      delete it->second;
      it->second = NULL;
      }
    }

  m_LayerModels.clear();
  m_Keys.clear();

  if( emitSignal0 )
    emit ContentReset();
}

/*****************************************************************************/
bool
StackedLayerModel
::Contains( const AbstractLayerModel * layerModel ) const
{
  for( LayerModelMap::const_iterator it( m_LayerModels.begin() );
       it!=m_LayerModels.end();
       ++it )
    if( it->second==layerModel )
      return true;

  return false;
}

/*****************************************************************************/
void
StackedLayerModel
::CountSRT( SizeType & unk,
	    SizeType & crt,
	    SizeType & geo,
	    SizeType & ssr ) const
{
  for( LayerModelMap::const_iterator it( m_LayerModels.begin() );
       it!=m_LayerModels.end();
       ++it )
    {
    assert( it->second!=NULL );

    switch( it->second->GetSpatialReferenceType() )
      {
      case SRT_UNKNOWN:
	++ unk;
	break;

      case SRT_CARTO:
	++ crt;
	break;

      case SRT_GEO:
	++ geo;
	break;

      case SRT_SENSOR:
	++ ssr;
	break;
      }
    }
}

/*****************************************************************************/
void
StackedLayerModel
::Delete( SizeType index )
{
  // qDebug() << this << "::Delete(" << index << ")";

  //
  // Check content.
  if( IsEmpty() )
    return;

  if( index>=GetCount() )
    return;

  assert( !m_Keys[ index ].empty() );

  //
  // Find item.
  LayerModelMap::iterator it(
    m_LayerModels.find( m_Keys[ index ] )
  );

  assert( it!=m_LayerModels.end() );

  //
  // Check if signals have to be emitted.
  bool emitCurrentChanged = m_Current<GetCount() && index<=m_Current;
  bool emitReferenceChanged = m_Reference<GetCount() && index<=m_Reference;

  //
  // Remember new current index.
  SizeType current =
    index>=m_Current
    ? m_Current
    : ( m_Current>0
        ? m_Current - 1
        : StackedLayerModel::NIL_INDEX );

  //
  // Emit signals.
  emit ContentAboutToBeChanged();
  emit LayerAboutToBeDeleted( index );

  //
  // Clear satellite date.
  ClearPixelInfos();

  //
  // Remove layer-model.
  AbstractLayerModel * layer = it->second;

  m_LayerModels.erase( it );

  m_Keys.erase( m_Keys.begin() + index );

  it = m_LayerModels.end();

  //
<<<<<<< Updated upstream
  // Emit about to change current item.
=======
  // Update current pointer.
>>>>>>> Stashed changes
  if( emitCurrentChanged )
    SetCurrent( current, true );

  //
<<<<<<< Updated upstream
  // Emit about to change reference item.
=======
  // Update reference pointer.
>>>>>>> Stashed changes
  if( emitReferenceChanged )
    SetReference(
      index>=m_Reference
      ? m_Reference
      : m_Reference - 1,
      true
    );

  //
  // Eventually delete layer.
  if( layer->parent()==this )
    {
    delete layer;
    layer = NULL;
    }

  //
  // Emit signals.
  emit LayerDeleted( index );
  emit ContentChanged();
}

/*******************************************************************************/
std::string
StackedLayerModel
::GenerateKey( AbstractLayerModel * layerModel )
{
  std::ostringstream oss;

  oss << "Layer #" << m_LayerCount++;

#ifdef _DEBUG
  oss << " (" << layerModel->metaObject()->className()
      << "@" << std::hex << layerModel << ")";
#endif

  return oss.str();
}

/*****************************************************************************/
void
StackedLayerModel
::LowerLayer( SizeType index )
{
  assert( GetCount()>1 );
  assert( index<GetCount() );

  SizeType next = Next( index );

  emit OrderAboutToBeChanged();
  {
  ClearPixelInfos();

  std::swap(
    *( m_Keys.begin() + index ),
    *( m_Keys.begin() + next )
  );
  }
  emit OrderChanged();

  // WARNING: This may be buggy if index!=m_Current
  emit CurrentAboutToBeChanged( next );
  {
  m_Current = next;
  }
  emit CurrentChanged( next );

  // WARNING: This may be buggy if index!=m_Reference
  SetReference(
    m_Reference==next
    ? index
    : ( m_Reference==index
        ? next
        : m_Reference )
  );
}

/*****************************************************************************/
void
StackedLayerModel
::MoveTo( SizeType index, SizeType position )
{
  assert( GetCount()>1 );
  assert( index<GetCount() );
  assert( position<GetCount() );

  if( index==position )
    return;

  //
  // Move element.
  emit OrderAboutToBeChanged();
  {
  ClearPixelInfos();

  KeyType key( m_Keys[ index ] );

  m_Keys.erase( m_Keys.begin() + index );
  m_Keys.insert( m_Keys.begin() + position, key );
  }
  emit OrderChanged();

  //
  // Compute new current element.
  SizeType current( m_Current );

  if( index==m_Current )
    current = position;

  if( index>m_Current && position<=m_Current )
    ++ current;

  else if( index<m_Current && position>=m_Current )
    -- current;

  //
  // Signal new current element.
  SetCurrent( current );

  //
  // Compute new reference element.
  SizeType reference( m_Reference );

  if( index==m_Reference )
    reference = position;

  if( index>m_Reference && position<=m_Reference )
    ++ reference;

  else if( index<m_Reference && position>=m_Reference )
    -- reference;

  SetReference( reference );
}

/*****************************************************************************/
void
StackedLayerModel
::MoveToBottom( SizeType index )
{
  MoveTo( index, m_Keys.size() - 1 );
}

/*****************************************************************************/
void
StackedLayerModel
::MoveToTop( SizeType index )
{
  MoveTo( index, 0 );
}

/*****************************************************************************/
void
StackedLayerModel
::RaiseLayer( SizeType index )
{
  assert( GetCount()>1 );
  assert( index<GetCount() );

  SizeType prev = Prev( index );

  emit OrderAboutToBeChanged();
  {
  ClearPixelInfos();

  std::swap(
    *( m_Keys.begin() + index ),
    *( m_Keys.begin() + prev )
  );
  }
  emit OrderChanged();

  // WARNING: This may be buggy if index!=m_Current.
  emit CurrentAboutToBeChanged( prev );
  {
  m_Current = prev;
  }
  emit CurrentChanged( prev );

  // WARNING: This may be buggy if index!=m_Reference.
  SetReference(
    m_Reference==prev
    ? index
    : ( m_Reference==index
        ? prev
        : m_Reference )
  );
}

/*****************************************************************************/
void
StackedLayerModel
::RotateLayerUp( SizeType index )
{
  if( GetCount()<2 )
    return;

  if( index>=GetCount() )
    return;

  KeyType currentKey( GetKey( m_Current ) );
  KeyType referenceKey( GetKey( m_Reference ) );

  emit OrderAboutToBeChanged();
  {
  ClearPixelInfos();

  std::rotate( m_Keys.begin(), m_Keys.begin() + index, m_Keys.end() );
  }
  emit OrderChanged();

  if( !currentKey.empty() )
    {
    SizeType current = FindKey( currentKey );

    assert( current!=StackedLayerModel::NIL_INDEX );

    emit CurrentAboutToBeChanged( current );
    {
      m_Current = current;
    }
    emit CurrentChanged( m_Current );
    }

  if( !referenceKey.empty() )
    {
    SizeType reference = FindKey( referenceKey );

    assert( reference!=StackedLayerModel::NIL_INDEX );

    emit ReferenceAboutToBeChanged( reference );
    {
      m_Reference = reference;
    }
    emit ReferenceChanged( m_Reference );
    }

  // qDebug() << "current:" << index;
}

/*****************************************************************************/
void
StackedLayerModel
::RotateLayerDown( SizeType index )
{
  if( GetCount()<2 )
    return;

  if( index>=GetCount() )
    return;

  KeyType currentKey( GetKey( m_Current ) );
  KeyType referenceKey( GetKey( m_Reference ) );

  emit OrderAboutToBeChanged();
  {
  ClearPixelInfos();

  std::rotate( m_Keys.rbegin(), m_Keys.rbegin() + index, m_Keys.rend()  );
  }
  emit OrderChanged();

  if( !currentKey.empty() )
    {
    SizeType current = FindKey( currentKey );

    assert( current!=StackedLayerModel::NIL_INDEX );

    emit CurrentAboutToBeChanged( current );
    {
    m_Current = current;
    }
    emit CurrentChanged( current );
    }

  if( !referenceKey.empty() )
    {
    SizeType reference = FindKey( referenceKey );

    assert( reference!=StackedLayerModel::NIL_INDEX );

    emit ReferenceAboutToBeChanged( reference );
    {
    m_Reference = reference;
    }
    emit ReferenceChanged( reference );
    }

  // qDebug() << "current:" << index;
}

/*****************************************************************************/
void
StackedLayerModel
::SetCurrent( const AbstractLayerModel * layerModel )
{
  SetCurrent( KeyOf( layerModel ) );
}

/*****************************************************************************/
void
StackedLayerModel
::SetCurrent( const KeyType & key )
{
  if( key==GetCurrentKey() )
    return;

  if( key==StackedLayerModel::NIL_KEY )
    {
    SetCurrent( StackedLayerModel::NIL_INDEX );

    return;
    }

  for( SizeType i=0; i<m_Keys.size(); ++i )
    if( m_Keys[ i ]==key )
      {
      SetCurrent( i );
      return;
      }
}

/*****************************************************************************/
void
StackedLayerModel
::SetReference( const KeyType & key )
{
  if( key==GetKey( m_Reference ) )
    return;

  if( key==StackedLayerModel::NIL_KEY )
    {
    SetReference( StackedLayerModel::NIL_INDEX );

    return;
    }

  for( SizeType i=0; i<m_Keys.size(); ++i )
    if( m_Keys[ i ]==key )
      {
      SetReference( i );
      return;
      }
}

/*****************************************************************************/
void
StackedLayerModel
::SetReference( const AbstractLayerModel * layerModel )
{
  SetReference( KeyOf( layerModel ) );
}

/*******************************************************************************/
/* SLOTS                                                                       */
/*******************************************************************************/

} // end namespace 'mvd'
<commit_msg>COMP: Fixed conflicts.<commit_after>/*=========================================================================

  Program:   Monteverdi2
  Language:  C++

  Copyright (c) Centre National d'Etudes Spatiales. All rights reserved.
  See Copyright.txt for details.

  Monteverdi2 is distributed under the CeCILL licence version 2. See
  Licence_CeCILL_V2-en.txt or
  http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt for more details.

  This software is distributed WITHOUT ANY WARRANTY; without even
  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
  PURPOSE.  See the above copyright notices for more information.

=========================================================================*/
#include "mvdStackedLayerModel.h"

/*****************************************************************************/
/* INCLUDE SECTION                                                           */

//
// Qt includes (sorted by alphabetic order)
//// Must be included before system/custom includes.

//
// System includes (sorted by alphabetic order)
#include <algorithm>
#include <sstream>

//
// ITK includes (sorted by alphabetic order)

//
// OTB includes (sorted by alphabetic order)

//
// Monteverdi includes (sorted by alphabetic order)
#include "Core/mvdAbstractLayerModel.h"
#include "Core/mvdAlgorithm.h"

namespace mvd
{
/*
  TRANSLATOR mvd::StackedLayerModel

  Necessary for lupdate to be aware of C++ namespaces.

  Context comment for translator.
*/

/*****************************************************************************/
/* CONSTANTS                                                                 */

namespace
{
} // end of anonymous namespace.

/*****************************************************************************/
/* STATIC IMPLEMENTATION SECTION                                             */

StackedLayerModel::SizeType
StackedLayerModel::m_LayerCount = 0;

const StackedLayerModel::KeyType
StackedLayerModel::NIL_KEY;

const StackedLayerModel::SizeType
StackedLayerModel::NIL_INDEX = -1;

/*****************************************************************************/
/* CLASS IMPLEMENTATION SECTION                                              */

/*******************************************************************************/
StackedLayerModel
::StackedLayerModel( QObject* parent ) :
  AbstractModel( parent ),
  m_LayerModels(),
  m_Keys(),
  m_Current( StackedLayerModel::NIL_INDEX ),
  m_Reference( StackedLayerModel::NIL_INDEX )
{
}

/*******************************************************************************/
StackedLayerModel
::~StackedLayerModel()
{
}

/*****************************************************************************/
std::string
StackedLayerModel
::Add( AbstractLayerModel * model )
{
  assert( model!=NULL );

  if( model==NULL )
    {
    throw
      std::runtime_error(
        ToStdString(
          tr( "Cannot insert NULL AbstractLayerModel." )
        )
      );
    }

  std::string key( StackedLayerModel::GenerateKey( model ) );
  assert( !key.empty() );

  if( key.empty() )
    {
    throw
      std::runtime_error(
        ToStdString(
          tr( "Failed to generate string key for '%1'." )
          .arg( model->metaObject()->className() )
        )
      );
    }

  emit ContentAboutToBeChanged();

  ClearPixelInfos();

  m_LayerModels.insert( LayerModelMap::value_type( key, model ) );
  m_Keys.push_back( key );

  emit LayerAdded( m_Keys.size() - 1 );
  emit ContentChanged();

  return key;
}

/*****************************************************************************/
void
StackedLayerModel
::Clear()
{
  bool emitSignal0 = !IsEmpty();
  bool emitSignal1 = !IsEmpty() && m_Current<GetCount();
  bool emitSignal2 = !IsEmpty() && m_Reference<GetCount();

  if( emitSignal0 )
    emit ContentAboutToBeReset();

  //
  // Clear current.
  if( emitSignal1 )
    {
    emit CurrentAboutToBeChanged( StackedLayerModel::NIL_INDEX );
    emit AboutToChangeSelectedLayerModel( KeyType() );
    }

  m_Current = StackedLayerModel::NIL_INDEX;

  if( emitSignal1 )
    {
    emit CurrentChanged( m_Current );
    emit SelectedLayerModelChanged( KeyType() );
    }

  //
  // Clear reference.
  if( emitSignal2 )
    emit ReferenceAboutToBeChanged( StackedLayerModel::NIL_INDEX );

  m_Reference = StackedLayerModel::NIL_INDEX;

  if( emitSignal2 )
    emit ReferenceChanged( m_Reference );

  //
  // Clear content.
  ClearPixelInfos();

  for( LayerModelMap::iterator it( m_LayerModels.begin() );
       it!=m_LayerModels.end();
       ++it )
    {
    assert( it->second!=NULL );

    if( it->second->parent()==this )
      {
      delete it->second;
      it->second = NULL;
      }
    }

  m_LayerModels.clear();
  m_Keys.clear();

  if( emitSignal0 )
    emit ContentReset();
}

/*****************************************************************************/
bool
StackedLayerModel
::Contains( const AbstractLayerModel * layerModel ) const
{
  for( LayerModelMap::const_iterator it( m_LayerModels.begin() );
       it!=m_LayerModels.end();
       ++it )
    if( it->second==layerModel )
      return true;

  return false;
}

/*****************************************************************************/
void
StackedLayerModel
::CountSRT( SizeType & unk,
	    SizeType & crt,
	    SizeType & geo,
	    SizeType & ssr ) const
{
  for( LayerModelMap::const_iterator it( m_LayerModels.begin() );
       it!=m_LayerModels.end();
       ++it )
    {
    assert( it->second!=NULL );

    switch( it->second->GetSpatialReferenceType() )
      {
      case SRT_UNKNOWN:
	++ unk;
	break;

      case SRT_CARTO:
	++ crt;
	break;

      case SRT_GEO:
	++ geo;
	break;

      case SRT_SENSOR:
	++ ssr;
	break;
      }
    }
}

/*****************************************************************************/
void
StackedLayerModel
::Delete( SizeType index )
{
  // qDebug() << this << "::Delete(" << index << ")";

  //
  // Check content.
  if( IsEmpty() )
    return;

  if( index>=GetCount() )
    return;

  assert( !m_Keys[ index ].empty() );

  //
  // Find item.
  LayerModelMap::iterator it(
    m_LayerModels.find( m_Keys[ index ] )
  );

  assert( it!=m_LayerModels.end() );

  //
  // Check if signals have to be emitted.
  bool emitCurrentChanged = m_Current<GetCount() && index<=m_Current;
  bool emitReferenceChanged = m_Reference<GetCount() && index<=m_Reference;

  //
  // Remember new current index.
  SizeType current =
    index>=m_Current
    ? m_Current
    : ( m_Current>0
        ? m_Current - 1
        : StackedLayerModel::NIL_INDEX );

  //
  // Emit signals.
  emit ContentAboutToBeChanged();
  emit LayerAboutToBeDeleted( index );

  //
  // Clear satellite date.
  ClearPixelInfos();

  //
  // Remove layer-model.
  AbstractLayerModel * layer = it->second;

  m_LayerModels.erase( it );

  m_Keys.erase( m_Keys.begin() + index );

  it = m_LayerModels.end();

  //
  // Update current pointer.
  if( emitCurrentChanged )
    SetCurrent( current, true );

  //
  // Update reference pointer.
  if( emitReferenceChanged )
    SetReference(
      index>=m_Reference
      ? m_Reference
      : m_Reference - 1,
      true
    );

  //
  // Eventually delete layer.
  if( layer->parent()==this )
    {
    delete layer;
    layer = NULL;
    }

  //
  // Emit signals.
  emit LayerDeleted( index );
  emit ContentChanged();
}

/*******************************************************************************/
std::string
StackedLayerModel
::GenerateKey( AbstractLayerModel * layerModel )
{
  std::ostringstream oss;

  oss << "Layer #" << m_LayerCount++;

#ifdef _DEBUG
  oss << " (" << layerModel->metaObject()->className()
      << "@" << std::hex << layerModel << ")";
#endif

  return oss.str();
}

/*****************************************************************************/
void
StackedLayerModel
::LowerLayer( SizeType index )
{
  assert( GetCount()>1 );
  assert( index<GetCount() );

  SizeType next = Next( index );

  emit OrderAboutToBeChanged();
  {
  ClearPixelInfos();

  std::swap(
    *( m_Keys.begin() + index ),
    *( m_Keys.begin() + next )
  );
  }
  emit OrderChanged();

  // WARNING: This may be buggy if index!=m_Current
  emit CurrentAboutToBeChanged( next );
  {
  m_Current = next;
  }
  emit CurrentChanged( next );

  // WARNING: This may be buggy if index!=m_Reference
  SetReference(
    m_Reference==next
    ? index
    : ( m_Reference==index
        ? next
        : m_Reference )
  );
}

/*****************************************************************************/
void
StackedLayerModel
::MoveTo( SizeType index, SizeType position )
{
  assert( GetCount()>1 );
  assert( index<GetCount() );
  assert( position<GetCount() );

  if( index==position )
    return;

  //
  // Move element.
  emit OrderAboutToBeChanged();
  {
  ClearPixelInfos();

  KeyType key( m_Keys[ index ] );

  m_Keys.erase( m_Keys.begin() + index );
  m_Keys.insert( m_Keys.begin() + position, key );
  }
  emit OrderChanged();

  //
  // Compute new current element.
  SizeType current( m_Current );

  if( index==m_Current )
    current = position;

  if( index>m_Current && position<=m_Current )
    ++ current;

  else if( index<m_Current && position>=m_Current )
    -- current;

  //
  // Signal new current element.
  SetCurrent( current );

  //
  // Compute new reference element.
  SizeType reference( m_Reference );

  if( index==m_Reference )
    reference = position;

  if( index>m_Reference && position<=m_Reference )
    ++ reference;

  else if( index<m_Reference && position>=m_Reference )
    -- reference;

  SetReference( reference );
}

/*****************************************************************************/
void
StackedLayerModel
::MoveToBottom( SizeType index )
{
  MoveTo( index, m_Keys.size() - 1 );
}

/*****************************************************************************/
void
StackedLayerModel
::MoveToTop( SizeType index )
{
  MoveTo( index, 0 );
}

/*****************************************************************************/
void
StackedLayerModel
::RaiseLayer( SizeType index )
{
  assert( GetCount()>1 );
  assert( index<GetCount() );

  SizeType prev = Prev( index );

  emit OrderAboutToBeChanged();
  {
  ClearPixelInfos();

  std::swap(
    *( m_Keys.begin() + index ),
    *( m_Keys.begin() + prev )
  );
  }
  emit OrderChanged();

  // WARNING: This may be buggy if index!=m_Current.
  emit CurrentAboutToBeChanged( prev );
  {
  m_Current = prev;
  }
  emit CurrentChanged( prev );

  // WARNING: This may be buggy if index!=m_Reference.
  SetReference(
    m_Reference==prev
    ? index
    : ( m_Reference==index
        ? prev
        : m_Reference )
  );
}

/*****************************************************************************/
void
StackedLayerModel
::RotateLayerUp( SizeType index )
{
  if( GetCount()<2 )
    return;

  if( index>=GetCount() )
    return;

  KeyType currentKey( GetKey( m_Current ) );
  KeyType referenceKey( GetKey( m_Reference ) );

  emit OrderAboutToBeChanged();
  {
  ClearPixelInfos();

  std::rotate( m_Keys.begin(), m_Keys.begin() + index, m_Keys.end() );
  }
  emit OrderChanged();

  if( !currentKey.empty() )
    {
    SizeType current = FindKey( currentKey );

    assert( current!=StackedLayerModel::NIL_INDEX );

    emit CurrentAboutToBeChanged( current );
    {
      m_Current = current;
    }
    emit CurrentChanged( m_Current );
    }

  if( !referenceKey.empty() )
    {
    SizeType reference = FindKey( referenceKey );

    assert( reference!=StackedLayerModel::NIL_INDEX );

    emit ReferenceAboutToBeChanged( reference );
    {
      m_Reference = reference;
    }
    emit ReferenceChanged( m_Reference );
    }

  // qDebug() << "current:" << index;
}

/*****************************************************************************/
void
StackedLayerModel
::RotateLayerDown( SizeType index )
{
  if( GetCount()<2 )
    return;

  if( index>=GetCount() )
    return;

  KeyType currentKey( GetKey( m_Current ) );
  KeyType referenceKey( GetKey( m_Reference ) );

  emit OrderAboutToBeChanged();
  {
  ClearPixelInfos();

  std::rotate( m_Keys.rbegin(), m_Keys.rbegin() + index, m_Keys.rend()  );
  }
  emit OrderChanged();

  if( !currentKey.empty() )
    {
    SizeType current = FindKey( currentKey );

    assert( current!=StackedLayerModel::NIL_INDEX );

    emit CurrentAboutToBeChanged( current );
    {
    m_Current = current;
    }
    emit CurrentChanged( current );
    }

  if( !referenceKey.empty() )
    {
    SizeType reference = FindKey( referenceKey );

    assert( reference!=StackedLayerModel::NIL_INDEX );

    emit ReferenceAboutToBeChanged( reference );
    {
    m_Reference = reference;
    }
    emit ReferenceChanged( reference );
    }

  // qDebug() << "current:" << index;
}

/*****************************************************************************/
void
StackedLayerModel
::SetCurrent( const AbstractLayerModel * layerModel )
{
  SetCurrent( KeyOf( layerModel ) );
}

/*****************************************************************************/
void
StackedLayerModel
::SetCurrent( const KeyType & key )
{
  if( key==GetCurrentKey() )
    return;

  if( key==StackedLayerModel::NIL_KEY )
    {
    SetCurrent( StackedLayerModel::NIL_INDEX );

    return;
    }

  for( SizeType i=0; i<m_Keys.size(); ++i )
    if( m_Keys[ i ]==key )
      {
      SetCurrent( i );
      return;
      }
}

/*****************************************************************************/
void
StackedLayerModel
::SetReference( const KeyType & key )
{
  if( key==GetKey( m_Reference ) )
    return;

  if( key==StackedLayerModel::NIL_KEY )
    {
    SetReference( StackedLayerModel::NIL_INDEX );

    return;
    }

  for( SizeType i=0; i<m_Keys.size(); ++i )
    if( m_Keys[ i ]==key )
      {
      SetReference( i );
      return;
      }
}

/*****************************************************************************/
void
StackedLayerModel
::SetReference( const AbstractLayerModel * layerModel )
{
  SetReference( KeyOf( layerModel ) );
}

/*******************************************************************************/
/* SLOTS                                                                       */
/*******************************************************************************/

} // end namespace 'mvd'
<|endoftext|>


<commit_before>/*===================================================================

The Medical Imaging Interaction Toolkit (MITK)

Copyright (c) German Cancer Research Center,
Division of Medical and Biological Informatics.
All rights reserved.

This software is distributed WITHOUT ANY WARRANTY; without
even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE.

See LICENSE.txt or http://www.mitk.org for details.

===================================================================*/

#include <mitkUIDGenerator.h>
#include <mitkLogMacros.h>

#include <cstdlib>
#include <sstream>
#include <math.h>
#include <stdexcept>
#include <iostream>

#ifdef WIN32
#include "process.h"
#endif

namespace mitk {

UIDGenerator::UIDGenerator(const char* prefix, unsigned int lengthOfRandomPart)
:m_Prefix(prefix),
 m_LengthOfRandomPart(lengthOfRandomPart)
{
  if (lengthOfRandomPart < 5)
  {
    MITK_ERROR << "To few digits requested (" <<lengthOfRandomPart<< " digits)";
    throw std::invalid_argument("To few digits requested");
  }

  static int instanceID = 0;
  int processID = 0;
  #ifdef WIN32
    processID = _getpid();
  #else
    processID = getpid();
  #endif
  unsigned int hash = seedhash( time(NULL), clock() );
  unsigned int seed = (hash + processID) * 10 + instanceID;
  instanceID++;

  std::srand(seed);
}

std::string UIDGenerator::GetUID()
{
  std::ostringstream s;
  s << m_Prefix;
  time_t tt = time(0);
  tm* t = gmtime(&tt);

  if (t)
  {

    s << t->tm_year + 1900;

    if (t->tm_mon < 9) s << "0"; // add a 0 for months 1 to 9
    s << t->tm_mon + 1;

    if (t->tm_mday < 10) s << "0"; // add a 0 for days 1 to 9
    s << t->tm_mday;

    if (t->tm_hour < 10) s << "0"; // add a 0 for hours 1 to 9
    s << t->tm_hour;

    if (t->tm_min < 10) s << "0"; // add a 0 for minutes 1 to 9
    s << t->tm_min;

    if (t->tm_sec < 10) s << "0"; // add a 0 for seconds 1 to 9
    s << t->tm_sec;

    std::ostringstream rs;
    rs << (long int)( pow(10.0, double(m_LengthOfRandomPart)) / double(RAND_MAX) * double(rand()) );

    for (size_t i = rs.str().length(); i < m_LengthOfRandomPart; ++i)
    {
      s << "X";
    }

    s << rs.str();
  }

  return s.str();
}

}

unsigned int UIDGenerator::seedhash( time_t t, clock_t c )
{
  unsigned int h1 = 0;
  unsigned char *p = (unsigned char *) &t;
  for( size_t i = 0; i < sizeof(t); ++i )
  {
    h1 *= UCHAR_MAX + 2U;
    h1 += p[i];
  }
  unsigned int h2 = 0;
  p = (unsigned char *) &c;
  for( size_t j = 0; j < sizeof(c); ++j )
  {
    h2 *= UCHAR_MAX + 2U;
    h2 += p[j];
  }
  return h1 ^ h2;
}

<commit_msg>COMP (#13980): moved method into namespace<commit_after>/*===================================================================

The Medical Imaging Interaction Toolkit (MITK)

Copyright (c) German Cancer Research Center,
Division of Medical and Biological Informatics.
All rights reserved.

This software is distributed WITHOUT ANY WARRANTY; without
even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE.

See LICENSE.txt or http://www.mitk.org for details.

===================================================================*/

#include <mitkUIDGenerator.h>
#include <mitkLogMacros.h>

#include <cstdlib>
#include <sstream>
#include <math.h>
#include <stdexcept>
#include <iostream>

#ifdef WIN32
#include "process.h"
#endif

namespace mitk {

UIDGenerator::UIDGenerator(const char* prefix, unsigned int lengthOfRandomPart)
:m_Prefix(prefix),
 m_LengthOfRandomPart(lengthOfRandomPart)
{
  if (lengthOfRandomPart < 5)
  {
    MITK_ERROR << "To few digits requested (" <<lengthOfRandomPart<< " digits)";
    throw std::invalid_argument("To few digits requested");
  }

  static int instanceID = 0;
  int processID = 0;
  #ifdef WIN32
    processID = _getpid();
  #else
    processID = getpid();
  #endif
  unsigned int hash = seedhash( time(NULL), clock() );
  unsigned int seed = (hash + processID) * 10 + instanceID;
  instanceID++;

  std::srand(seed);
}

std::string UIDGenerator::GetUID()
{
  std::ostringstream s;
  s << m_Prefix;
  time_t tt = time(0);
  tm* t = gmtime(&tt);

  if (t)
  {

    s << t->tm_year + 1900;

    if (t->tm_mon < 9) s << "0"; // add a 0 for months 1 to 9
    s << t->tm_mon + 1;

    if (t->tm_mday < 10) s << "0"; // add a 0 for days 1 to 9
    s << t->tm_mday;

    if (t->tm_hour < 10) s << "0"; // add a 0 for hours 1 to 9
    s << t->tm_hour;

    if (t->tm_min < 10) s << "0"; // add a 0 for minutes 1 to 9
    s << t->tm_min;

    if (t->tm_sec < 10) s << "0"; // add a 0 for seconds 1 to 9
    s << t->tm_sec;

    std::ostringstream rs;
    rs << (long int)( pow(10.0, double(m_LengthOfRandomPart)) / double(RAND_MAX) * double(rand()) );

    for (size_t i = rs.str().length(); i < m_LengthOfRandomPart; ++i)
    {
      s << "X";
    }

    s << rs.str();
  }

  return s.str();
}

unsigned int UIDGenerator::seedhash( time_t t, clock_t c )
{
  unsigned int h1 = 0;
  unsigned char *p = (unsigned char *) &t;
  for( size_t i = 0; i < sizeof(t); ++i )
  {
    h1 *= UCHAR_MAX + 2U;
    h1 += p[i];
  }
  unsigned int h2 = 0;
  p = (unsigned char *) &c;
  for( size_t j = 0; j < sizeof(c); ++j )
  {
    h2 *= UCHAR_MAX + 2U;
    h2 += p[j];
  }
  return h1 ^ h2;
}

}

<|endoftext|>


<commit_before><commit_msg>[gpadv7] Port to Win64 (replace Long_t by Longptr_t)<commit_after><|endoftext|>