<commit_before>// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.

#include "emptysearch.h"
#include "nearest_neighbor_blueprint.h"
#include "nearest_neighbor_iterator.h"
#include "nns_index_iterator.h"
#include <vespa/eval/eval/fast_value.h>
#include <vespa/searchlib/fef/termfieldmatchdataarray.h>
#include <vespa/searchlib/tensor/dense_tensor_attribute.h>
#include <vespa/searchlib/tensor/distance_function_factory.h>
#include <vespa/log/log.h>

LOG_SETUP(".searchlib.queryeval.nearest_neighbor_blueprint");

using vespalib::eval::CellType;
using vespalib::eval::FastValueBuilderFactory;
using vespalib::eval::TypedCells;
using vespalib::eval::Value;
using vespalib::eval::ValueType;

namespace search::queryeval {

namespace {

template<typename LCT, typename RCT>
std::unique_ptr<Value>
convert_cells(const ValueType &new_type, std::unique_ptr<Value> old_value)
{
    auto old_cells = old_value->cells().typify<LCT>();
    auto builder = FastValueBuilderFactory::get().create_value_builder<RCT>(new_type);
    auto new_cells = builder->add_subspace();
    assert(old_cells.size() == new_cells.size());
    auto p = new_cells.begin();
    for (LCT value : old_cells) {
        RCT conv(value);
        *p++ = conv;
    }
    return builder->build(std::move(builder));
}

struct ConvertCellsSelector
{
    template <typename LCT, typename RCT>
    static auto invoke(const ValueType &new_type, std::unique_ptr<Value> old_value) {
        return convert_cells<LCT, RCT>(new_type, std::move(old_value));
    }
    auto operator() (CellType from, CellType to, std::unique_ptr<Value> old_value) const {
        using MyTypify = vespalib::eval::TypifyCellType;
        ValueType new_type = old_value->type().cell_cast(to);
        return vespalib::typify_invoke<2,MyTypify,ConvertCellsSelector>(from, to, new_type, std::move(old_value));
    }
};

vespalib::string
to_string(NearestNeighborBlueprint::Algorithm algorithm)
{
    using NNBA = NearestNeighborBlueprint::Algorithm;
    switch (algorithm) {
        case NNBA::BRUTE_FORCE: return "brute force";
        case NNBA::BRUTE_FORCE_FALLBACK: return "brute force fallback";
        case NNBA::INDEX_TOP_K: return "index top k";
        case NNBA::INDEX_TOP_K_WITH_FILTER: return "index top k using global filter";
    }
    return "unknown";
}

} // namespace <unnamed>

NearestNeighborBlueprint::NearestNeighborBlueprint(const queryeval::FieldSpec& field,
                                                   const tensor::ITensorAttribute& attr_tensor,
                                                   std::unique_ptr<Value> query_tensor,
                                                   uint32_t target_num_hits,
                                                   bool approximate,
                                                   uint32_t explore_additional_hits,
                                                   double distance_threshold,
                                                   double global_filter_lower_limit,
                                                   double global_filter_upper_limit)
    : ComplexLeafBlueprint(field),
      _attr_tensor(attr_tensor),
      _query_tensor(std::move(query_tensor)),
      _target_num_hits(target_num_hits),
      _approximate(approximate),
      _explore_additional_hits(explore_additional_hits),
      _distance_threshold(std::numeric_limits<double>::max()),
      _global_filter_lower_limit(global_filter_lower_limit),
      _global_filter_upper_limit(global_filter_upper_limit),
      _fallback_dist_fun(),
      _distance_heap(target_num_hits),
      _found_hits(),
      _algorithm(Algorithm::BRUTE_FORCE),
      _global_filter(GlobalFilter::create()),
      _global_filter_set(false),
      _global_filter_hits(),
      _global_filter_hit_ratio()
{
    CellType attr_ct = _attr_tensor.getTensorType().cell_type();
    _fallback_dist_fun = search::tensor::make_distance_function(_attr_tensor.distance_metric(), attr_ct);
    _dist_fun = _fallback_dist_fun.get();
    assert(_dist_fun);
    auto nns_index = _attr_tensor.nearest_neighbor_index();
    if (nns_index) {
        _dist_fun = nns_index->distance_function();
        assert(_dist_fun);
    }
    auto query_ct = _query_tensor->cells().type;
    CellType required_ct = _dist_fun->expected_cell_type();
    if (query_ct != required_ct) {
        ConvertCellsSelector converter;
        _query_tensor = converter(query_ct, required_ct, std::move(_query_tensor));
    }
    if (distance_threshold < std::numeric_limits<double>::max()) {
        _distance_threshold = _dist_fun->convert_threshold(distance_threshold);
        _distance_heap.set_distance_threshold(_distance_threshold);
    }
    uint32_t est_hits = _attr_tensor.get_num_docs();
    setEstimate(HitEstimate(est_hits, false));
    set_want_global_filter(nns_index && _approximate);
}

NearestNeighborBlueprint::~NearestNeighborBlueprint() = default;

void
NearestNeighborBlueprint::set_global_filter(const GlobalFilter &global_filter)
{
    _global_filter = global_filter.shared_from_this();
    _global_filter_set = true;
    auto nns_index = _attr_tensor.nearest_neighbor_index();
    LOG(debug, "set_global_filter with: %s / %s / %s",
        (_approximate ? "approximate" : "exact"),
        (nns_index ? "nns_index" : "no_index"),
        (_global_filter->has_filter() ? "has_filter" : "no_filter"));
    if (_approximate && nns_index) {
        uint32_t est_hits = _attr_tensor.get_num_docs();
        if (_global_filter->has_filter()) {
            uint32_t max_hits = _global_filter->filter()->countTrueBits();
            LOG(debug, "set_global_filter getNumDocs: %u / max_hits %u", est_hits, max_hits);
            double max_hit_ratio = static_cast<double>(max_hits) / est_hits;
            if (max_hit_ratio < _global_filter_lower_limit) {
                _algorithm = Algorithm::BRUTE_FORCE_FALLBACK;
                LOG(debug, "too many hits filtered out, using brute force implementation");
            } else {
                est_hits = std::min(est_hits, max_hits);
            }
            _global_filter_hits = max_hits;
            _global_filter_hit_ratio = max_hit_ratio;
        }
        if (_algorithm != Algorithm::BRUTE_FORCE_FALLBACK) {
            est_hits = std::min(est_hits, _target_num_hits);
            setEstimate(HitEstimate(est_hits, false));
            perform_top_k(nns_index);
            LOG(debug, "perform_top_k found %zu hits", _found_hits.size());
        }
    }
}

void
NearestNeighborBlueprint::perform_top_k(const search::tensor::NearestNeighborIndex* nns_index)
{
    auto lhs = _query_tensor->cells();
    uint32_t k = _target_num_hits;
    if (_global_filter->has_filter()) {
        auto filter = _global_filter->filter();
        _found_hits = nns_index->find_top_k_with_filter(k, lhs, *filter, k + _explore_additional_hits, _distance_threshold);
        _algorithm = Algorithm::INDEX_TOP_K_WITH_FILTER;
    } else {
        _found_hits = nns_index->find_top_k(k, lhs, k + _explore_additional_hits, _distance_threshold);
        _algorithm = Algorithm::INDEX_TOP_K;
    }
}

std::unique_ptr<SearchIterator>
NearestNeighborBlueprint::createLeafSearch(const search::fef::TermFieldMatchDataArray& tfmda, bool strict) const
{
    assert(tfmda.size() == 1);
    fef::TermFieldMatchData &tfmd = *tfmda[0]; // always search in only one field
    if (! _found_hits.empty()) {
        return NnsIndexIterator::create(tfmd, _found_hits, _dist_fun);
    }
    const Value &qT = *_query_tensor;
    return NearestNeighborIterator::create(strict, tfmd, qT, _attr_tensor,
                                           _distance_heap, _global_filter->filter(), _dist_fun);
}

void
NearestNeighborBlueprint::visitMembers(vespalib::ObjectVisitor& visitor) const
{
    ComplexLeafBlueprint::visitMembers(visitor);
    visitor.visitString("attribute_tensor", _attr_tensor.getTensorType().to_spec());
    visitor.visitString("query_tensor", _query_tensor->type().to_spec());
    visitor.visitInt("target_num_hits", _target_num_hits);
    visitor.visitInt("explore_additional_hits", _explore_additional_hits);
    visitor.visitBool("wanted_approximate", _approximate);
    visitor.visitBool("has_index", _attr_tensor.nearest_neighbor_index());
    visitor.visitString("algorithm", to_string(_algorithm));
    visitor.visitInt("top_k_hits", _found_hits.size());

    visitor.openStruct("global_filter", "GlobalFilter");
    visitor.visitBool("wanted", getState().want_global_filter());
    visitor.visitBool("set", _global_filter_set);
    visitor.visitBool("calculated", _global_filter->has_filter());
    visitor.visitFloat("lower_limit", _global_filter_lower_limit);
    visitor.visitFloat("upper_limit", _global_filter_upper_limit);
    if (_global_filter_hits.has_value()) {
        visitor.visitInt("hits", _global_filter_hits.value());
    }
    if (_global_filter_hit_ratio.has_value()) {
        visitor.visitFloat("hit_ratio", _global_filter_hit_ratio.value());
    }
    visitor.closeStruct();
}

bool
NearestNeighborBlueprint::always_needs_unpack() const
{
    return true;
}

std::ostream&
operator<<(std::ostream& out, NearestNeighborBlueprint::Algorithm algorithm)
{
    out << to_string(algorithm);
    return out;
}

}
<commit_msg>Remove debug logging that is part of visit trace.<commit_after>// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.

#include "emptysearch.h"
#include "nearest_neighbor_blueprint.h"
#include "nearest_neighbor_iterator.h"
#include "nns_index_iterator.h"
#include <vespa/eval/eval/fast_value.h>
#include <vespa/searchlib/fef/termfieldmatchdataarray.h>
#include <vespa/searchlib/tensor/dense_tensor_attribute.h>
#include <vespa/searchlib/tensor/distance_function_factory.h>
#include <vespa/log/log.h>

LOG_SETUP(".searchlib.queryeval.nearest_neighbor_blueprint");

using vespalib::eval::CellType;
using vespalib::eval::FastValueBuilderFactory;
using vespalib::eval::TypedCells;
using vespalib::eval::Value;
using vespalib::eval::ValueType;

namespace search::queryeval {

namespace {

template<typename LCT, typename RCT>
std::unique_ptr<Value>
convert_cells(const ValueType &new_type, std::unique_ptr<Value> old_value)
{
    auto old_cells = old_value->cells().typify<LCT>();
    auto builder = FastValueBuilderFactory::get().create_value_builder<RCT>(new_type);
    auto new_cells = builder->add_subspace();
    assert(old_cells.size() == new_cells.size());
    auto p = new_cells.begin();
    for (LCT value : old_cells) {
        RCT conv(value);
        *p++ = conv;
    }
    return builder->build(std::move(builder));
}

struct ConvertCellsSelector
{
    template <typename LCT, typename RCT>
    static auto invoke(const ValueType &new_type, std::unique_ptr<Value> old_value) {
        return convert_cells<LCT, RCT>(new_type, std::move(old_value));
    }
    auto operator() (CellType from, CellType to, std::unique_ptr<Value> old_value) const {
        using MyTypify = vespalib::eval::TypifyCellType;
        ValueType new_type = old_value->type().cell_cast(to);
        return vespalib::typify_invoke<2,MyTypify,ConvertCellsSelector>(from, to, new_type, std::move(old_value));
    }
};

vespalib::string
to_string(NearestNeighborBlueprint::Algorithm algorithm)
{
    using NNBA = NearestNeighborBlueprint::Algorithm;
    switch (algorithm) {
        case NNBA::BRUTE_FORCE: return "brute force";
        case NNBA::BRUTE_FORCE_FALLBACK: return "brute force fallback";
        case NNBA::INDEX_TOP_K: return "index top k";
        case NNBA::INDEX_TOP_K_WITH_FILTER: return "index top k using global filter";
    }
    return "unknown";
}

} // namespace <unnamed>

NearestNeighborBlueprint::NearestNeighborBlueprint(const queryeval::FieldSpec& field,
                                                   const tensor::ITensorAttribute& attr_tensor,
                                                   std::unique_ptr<Value> query_tensor,
                                                   uint32_t target_num_hits,
                                                   bool approximate,
                                                   uint32_t explore_additional_hits,
                                                   double distance_threshold,
                                                   double global_filter_lower_limit,
                                                   double global_filter_upper_limit)
    : ComplexLeafBlueprint(field),
      _attr_tensor(attr_tensor),
      _query_tensor(std::move(query_tensor)),
      _target_num_hits(target_num_hits),
      _approximate(approximate),
      _explore_additional_hits(explore_additional_hits),
      _distance_threshold(std::numeric_limits<double>::max()),
      _global_filter_lower_limit(global_filter_lower_limit),
      _global_filter_upper_limit(global_filter_upper_limit),
      _fallback_dist_fun(),
      _distance_heap(target_num_hits),
      _found_hits(),
      _algorithm(Algorithm::BRUTE_FORCE),
      _global_filter(GlobalFilter::create()),
      _global_filter_set(false),
      _global_filter_hits(),
      _global_filter_hit_ratio()
{
    CellType attr_ct = _attr_tensor.getTensorType().cell_type();
    _fallback_dist_fun = search::tensor::make_distance_function(_attr_tensor.distance_metric(), attr_ct);
    _dist_fun = _fallback_dist_fun.get();
    assert(_dist_fun);
    auto nns_index = _attr_tensor.nearest_neighbor_index();
    if (nns_index) {
        _dist_fun = nns_index->distance_function();
        assert(_dist_fun);
    }
    auto query_ct = _query_tensor->cells().type;
    CellType required_ct = _dist_fun->expected_cell_type();
    if (query_ct != required_ct) {
        ConvertCellsSelector converter;
        _query_tensor = converter(query_ct, required_ct, std::move(_query_tensor));
    }
    if (distance_threshold < std::numeric_limits<double>::max()) {
        _distance_threshold = _dist_fun->convert_threshold(distance_threshold);
        _distance_heap.set_distance_threshold(_distance_threshold);
    }
    uint32_t est_hits = _attr_tensor.get_num_docs();
    setEstimate(HitEstimate(est_hits, false));
    set_want_global_filter(nns_index && _approximate);
}

NearestNeighborBlueprint::~NearestNeighborBlueprint() = default;

void
NearestNeighborBlueprint::set_global_filter(const GlobalFilter &global_filter)
{
    _global_filter = global_filter.shared_from_this();
    _global_filter_set = true;
    auto nns_index = _attr_tensor.nearest_neighbor_index();
    if (_approximate && nns_index) {
        uint32_t est_hits = _attr_tensor.get_num_docs();
        if (_global_filter->has_filter()) {
            uint32_t max_hits = _global_filter->filter()->countTrueBits();
            double max_hit_ratio = static_cast<double>(max_hits) / est_hits;
            if (max_hit_ratio < _global_filter_lower_limit) {
                _algorithm = Algorithm::BRUTE_FORCE_FALLBACK;
            } else {
                est_hits = std::min(est_hits, max_hits);
            }
            _global_filter_hits = max_hits;
            _global_filter_hit_ratio = max_hit_ratio;
        }
        if (_algorithm != Algorithm::BRUTE_FORCE_FALLBACK) {
            est_hits = std::min(est_hits, _target_num_hits);
            setEstimate(HitEstimate(est_hits, false));
            perform_top_k(nns_index);
        }
    }
}

void
NearestNeighborBlueprint::perform_top_k(const search::tensor::NearestNeighborIndex* nns_index)
{
    auto lhs = _query_tensor->cells();
    uint32_t k = _target_num_hits;
    if (_global_filter->has_filter()) {
        auto filter = _global_filter->filter();
        _found_hits = nns_index->find_top_k_with_filter(k, lhs, *filter, k + _explore_additional_hits, _distance_threshold);
        _algorithm = Algorithm::INDEX_TOP_K_WITH_FILTER;
    } else {
        _found_hits = nns_index->find_top_k(k, lhs, k + _explore_additional_hits, _distance_threshold);
        _algorithm = Algorithm::INDEX_TOP_K;
    }
}

std::unique_ptr<SearchIterator>
NearestNeighborBlueprint::createLeafSearch(const search::fef::TermFieldMatchDataArray& tfmda, bool strict) const
{
    assert(tfmda.size() == 1);
    fef::TermFieldMatchData &tfmd = *tfmda[0]; // always search in only one field
    if (! _found_hits.empty()) {
        return NnsIndexIterator::create(tfmd, _found_hits, _dist_fun);
    }
    const Value &qT = *_query_tensor;
    return NearestNeighborIterator::create(strict, tfmd, qT, _attr_tensor,
                                           _distance_heap, _global_filter->filter(), _dist_fun);
}

void
NearestNeighborBlueprint::visitMembers(vespalib::ObjectVisitor& visitor) const
{
    ComplexLeafBlueprint::visitMembers(visitor);
    visitor.visitString("attribute_tensor", _attr_tensor.getTensorType().to_spec());
    visitor.visitString("query_tensor", _query_tensor->type().to_spec());
    visitor.visitInt("target_num_hits", _target_num_hits);
    visitor.visitInt("explore_additional_hits", _explore_additional_hits);
    visitor.visitBool("wanted_approximate", _approximate);
    visitor.visitBool("has_index", _attr_tensor.nearest_neighbor_index());
    visitor.visitString("algorithm", to_string(_algorithm));
    visitor.visitInt("top_k_hits", _found_hits.size());

    visitor.openStruct("global_filter", "GlobalFilter");
    visitor.visitBool("wanted", getState().want_global_filter());
    visitor.visitBool("set", _global_filter_set);
    visitor.visitBool("calculated", _global_filter->has_filter());
    visitor.visitFloat("lower_limit", _global_filter_lower_limit);
    visitor.visitFloat("upper_limit", _global_filter_upper_limit);
    if (_global_filter_hits.has_value()) {
        visitor.visitInt("hits", _global_filter_hits.value());
    }
    if (_global_filter_hit_ratio.has_value()) {
        visitor.visitFloat("hit_ratio", _global_filter_hit_ratio.value());
    }
    visitor.closeStruct();
}

bool
NearestNeighborBlueprint::always_needs_unpack() const
{
    return true;
}

std::ostream&
operator<<(std::ostream& out, NearestNeighborBlueprint::Algorithm algorithm)
{
    out << to_string(algorithm);
    return out;
}

}
<|endoftext|>


<commit_before>// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0

#include "sw/device/silicon_creator/lib/epmp.h"

#include <array>
#include <vector>

#include "gtest/gtest.h"
#include "sw/device/lib/base/bitfield.h"
#include "sw/device/lib/base/csr.h"
#include "sw/device/silicon_creator/lib/base/mock_csr.h"
#include "sw/device/silicon_creator/testing/mask_rom_test.h"

namespace epmp_unittest {

/**
 * Representation of the hardware PMP control register state.
 */
struct PmpCsrs {
  /**
   * The unpacked PMP configuration (`pmpNcfg`) registers, one entry for each
   * PMP entry.
   */
  std::array<uint8_t, kEpmpNumRegions> pmpcfg{};

  /**
   * The PMP address registers, one for each PMP entry.
   */
  std::array<uint32_t, kEpmpNumRegions> pmpaddr{};

  /**
   * The Machine Security Configuration register.
   */
  uint64_t mseccfg = 0;

  /**
   * Pack the individual (`pmpNcfg`) PMP entry configuration register
   * values into hardware register (`pmpcfgN`) values.
   *
   * @returns An array representing the packed `pmpcfgN` values.
   */
  std::array<uint32_t, kEpmpNumRegions / 4> PackCfg() const {
    // There are four 8-bit `pmpNcfg` values packed into each `pmpcfgN`
    // register.
    std::array<uint32_t, kEpmpNumRegions / 4> packed{};
    for (int i = 0; i < pmpcfg.size(); ++i) {
      bitfield_field32_t field = {.mask = 0xff,
                                  .index = (static_cast<uint32_t>(i) % 4) * 8};
      packed.at(i / 4) = bitfield_field32_write(
          packed.at(i / 4), field, static_cast<uint32_t>(pmpcfg.at(i)));
    }
    return packed;
  }

  /**
   * Generate a state that is a snapshot of the current PMP control register
   * values.
   *
   * @returns An `epmp_state_t` value.
   */
  epmp_state_t State() const {
    epmp_state_t state;
    auto packed = PackCfg();
    for (int i = 0; i < packed.size(); ++i) {
      state.pmpcfg[i] = packed.at(i);
    }
    for (int i = 0; i < pmpaddr.size(); ++i) {
      state.pmpaddr[i] = pmpaddr.at(i);
    }
    state.mseccfg = static_cast<uint32_t>(mseccfg);
    return state;
  }
};

/**
 * Map from PMP config register index to CSR_REG_PMPCFGN value.
 */
const std::vector<uint32_t> kPmpcfgMap = {
    CSR_REG_PMPCFG0,
    CSR_REG_PMPCFG1,
    CSR_REG_PMPCFG2,
    CSR_REG_PMPCFG3,
};

/**
 * Map from PMP address register index to CSR_REG_PMPADDRN value.
 */
const std::vector<uint32_t> kPmpaddrMap = {
    CSR_REG_PMPADDR0,  CSR_REG_PMPADDR1,  CSR_REG_PMPADDR2,  CSR_REG_PMPADDR3,
    CSR_REG_PMPADDR4,  CSR_REG_PMPADDR5,  CSR_REG_PMPADDR6,  CSR_REG_PMPADDR7,
    CSR_REG_PMPADDR8,  CSR_REG_PMPADDR9,  CSR_REG_PMPADDR10, CSR_REG_PMPADDR11,
    CSR_REG_PMPADDR12, CSR_REG_PMPADDR13, CSR_REG_PMPADDR14, CSR_REG_PMPADDR15,
};

/**
 * Helper function to call EXPECT_CSR_READ on all pmpcfg registers
 * returning the values provided.
 */
static void ExpectPmpcfgRead(const PmpCsrs &expect) {
  auto packed = expect.PackCfg();
  for (int i = 0; i < packed.size(); ++i) {
    EXPECT_CSR_READ(kPmpcfgMap.at(i), packed.at(i));
  }
}

/**
 * Helper function to call EXPECT_CSR_READ on all pmpaddr registers
 * returning the values provided.
 */
static void ExpectPmpaddrRead(const PmpCsrs &expect) {
  for (int i = 0; i < expect.pmpaddr.size(); ++i) {
    EXPECT_CSR_READ(kPmpaddrMap.at(i), expect.pmpaddr.at(i));
  }
}

/**
 * Helper function to call EXPECT_CSR_READ on the mseccfg register
 * returning the value provided.
 */
static void ExpectMseccfgRead(const PmpCsrs &expect) {
  EXPECT_CSR_READ(CSR_REG_MSECCFG, static_cast<uint32_t>(expect.mseccfg));
  EXPECT_CSR_READ(CSR_REG_MSECCFGH,
                  static_cast<uint32_t>(expect.mseccfg >> 32));
}

/**
 * Helper function to call EXPECT_CSR_READ as necessary for an
 * internal `read_state` call with the expected values provided.
 */
static void ExpectReadState(const PmpCsrs &expect) {
  ExpectPmpaddrRead(expect);
  ExpectPmpcfgRead(expect);
  ExpectMseccfgRead(expect);
}

/**
 * Encode a region using the NAPOT address mode. This does not
 * do any validation of the values provided.
 *
 * Note: other address modes just require the address to be right
 * shifted by 2.
 */
static uint32_t napot(epmp_region_t region) {
  return (region.start >> 2) | ((region.end - region.start - 1) >> 3);
}

class EpmpTest : public mask_rom_test::MaskRomTest {
 protected:
  mock_csr::MockCsr csr_;
  /**
   * Default reset value for the PMP CSRs.
   */
  const PmpCsrs reset_ = PmpCsrs{};
  epmp_state_t state_ = reset_.State();
};

class EpmpCheckTest : public EpmpTest {};

TEST_F(EpmpCheckTest, Default) {
  // Check CSRs are set as expected.
  ExpectReadState(reset_);
  EXPECT_EQ(epmp_state_check(&state_), kErrorOk);
}

TEST_F(EpmpCheckTest, ErrorPmpaddr) {
  // Inject an error.
  PmpCsrs bad = reset_;
  bad.pmpaddr.at(15) ^= 1 << 31;

  // Check should fail.
  ExpectReadState(bad);
  EXPECT_EQ(epmp_state_check(&state_), kErrorEpmpBadCheck);
}

TEST_F(EpmpCheckTest, ErrorPmpcfg) {
  // Inject an error.
  PmpCsrs bad = reset_;
  bad.pmpcfg.at(0) ^= 1;

  // Check should fail.
  ExpectReadState(bad);
  EXPECT_EQ(epmp_state_check(&state_), kErrorEpmpBadCheck);
}

TEST_F(EpmpCheckTest, ErrorMseccfg) {
  // Inject an error.
  PmpCsrs bad = reset_;
  bad.mseccfg ^= 1;

  // Check should fail.
  ExpectReadState(bad);
  EXPECT_EQ(epmp_state_check(&state_), kErrorEpmpBadCheck);
}

class EpmpTorTest : public EpmpTest {};

TEST_F(EpmpTorTest, Entry0) {
  // Region for entry.
  epmp_region_t region = {
      .start = 0,
      .end = 0,
  };

  // Configure registers for entry 0 (base address implicitly 0).
  PmpCsrs csrs = reset_;
  csrs.pmpcfg[0] = EPMP_CFG_L | EPMP_CFG_A_TOR | EPMP_CFG_R;
  csrs.pmpaddr[0] = region.end >> 2;

  // Configure same entry in state.
  epmp_state_configure_tor(&state_, 0, region, kEpmpPermLockedReadOnly);

  // Check CSRs match.
  ExpectReadState(csrs);
  EXPECT_EQ(epmp_state_check(&state_), kErrorOk);
}

TEST_F(EpmpTorTest, Entry1) {
  // Region for entry.
  epmp_region_t region = {
      .start = 0x120,
      .end = 0x140,
  };

  // Configure registers for entry 1, with base address in entry 0.
  PmpCsrs csrs = reset_;
  csrs.pmpaddr[0] = region.start >> 2;
  csrs.pmpcfg[1] = EPMP_CFG_L | EPMP_CFG_A_TOR | EPMP_CFG_R;
  csrs.pmpaddr[1] = region.end >> 2;

  // Configure same entry in state.
  epmp_state_configure_tor(&state_, 1, region, kEpmpPermLockedReadOnly);

  // Check CSRs match.
  ExpectReadState(csrs);
  EXPECT_EQ(epmp_state_check(&state_), kErrorOk);
}

TEST_F(EpmpTorTest, Entry15) {
  // Region for entry.
  epmp_region_t region = {
      .start = 0x120,
      .end = 0x140,
  };

  // Configure registers for entry 15, with base address in entry 14.
  PmpCsrs csrs = reset_;
  csrs.pmpaddr[14] = region.start >> 2;
  csrs.pmpcfg[15] = EPMP_CFG_L | EPMP_CFG_A_TOR | EPMP_CFG_R;
  csrs.pmpaddr[15] = region.end >> 2;

  // Configure same entry in state.
  epmp_state_configure_tor(&state_, 15, region, kEpmpPermLockedReadOnly);

  // Check CSRs match.
  ExpectReadState(csrs);
  EXPECT_EQ(epmp_state_check(&state_), kErrorOk);
}

TEST_F(EpmpTorTest, SharedAddress) {
  // Adjacent regions for entries.
  epmp_region_t region1 = {
      .start = 0x120,
      .end = 0x140,
  };
  epmp_region_t region2 = {
      .start = 0x140,
      .end = 0x180,
  };

  // Configure registers for entries 1 and 2, with base address in entry 0.
  PmpCsrs csrs = reset_;
  csrs.pmpaddr[0] = region1.start >> 2;
  csrs.pmpcfg[1] = EPMP_CFG_L | EPMP_CFG_A_TOR | EPMP_CFG_R | EPMP_CFG_X;
  csrs.pmpaddr[1] = region1.end >> 2;
  csrs.pmpcfg[2] = EPMP_CFG_L | EPMP_CFG_A_TOR | EPMP_CFG_R;
  csrs.pmpaddr[2] = region2.end >> 2;

  // Configure same entry in state.
  epmp_state_configure_tor(&state_, 1, region1, kEpmpPermLockedReadExecute);
  epmp_state_configure_tor(&state_, 2, region2, kEpmpPermLockedReadOnly);

  // Check CSRs match.
  ExpectReadState(csrs);
  EXPECT_EQ(epmp_state_check(&state_), kErrorOk);
}

class EpmpNa4Test : public EpmpTest {};

TEST_F(EpmpNa4Test, Entry0) {
  // Region for entry.
  epmp_region_t region = {
      .start = 0xF4,
      .end = 0xF8,
  };

  // Configure registers for entry 0.
  PmpCsrs csrs = reset_;
  csrs.pmpcfg[0] = EPMP_CFG_L | EPMP_CFG_A_NA4 | EPMP_CFG_R | EPMP_CFG_X;
  csrs.pmpaddr[0] = region.start >> 2;

  // Configure same entry in state.
  epmp_state_configure_na4(&state_, 0, region, kEpmpPermLockedReadExecute);

  // Check CSRs match.
  ExpectReadState(csrs);
  EXPECT_EQ(epmp_state_check(&state_), kErrorOk);
}

TEST_F(EpmpNa4Test, Entry15) {
  // Region for entry.
  epmp_region_t region = {
      .start = 0x0,
      .end = 0x4,
  };

  // Configure registers for entry 15.
  PmpCsrs csrs = reset_;
  csrs.pmpcfg[15] = EPMP_CFG_L | EPMP_CFG_A_NA4 | EPMP_CFG_R | EPMP_CFG_W;
  csrs.pmpaddr[15] = region.start >> 2;

  // Configure same entry in state.
  epmp_state_configure_na4(&state_, 15, region, kEpmpPermLockedReadWrite);

  // Check CSRs match.
  ExpectReadState(csrs);
  EXPECT_EQ(epmp_state_check(&state_), kErrorOk);
}

class EpmpNapotTest : public EpmpTest {};

TEST_F(EpmpNapotTest, Entry0) {
  // Region for entry.
  epmp_region_t region = {
      .start = 0xFF00,
      .end = 0xFF10,
  };

  // Configure registers for entry 0.
  PmpCsrs csrs = reset_;
  csrs.pmpcfg[0] = EPMP_CFG_L | EPMP_CFG_A_NAPOT | EPMP_CFG_R | EPMP_CFG_X;
  csrs.pmpaddr[0] = napot(region);

  // Configure same entry in state.
  epmp_state_configure_napot(&state_, 0, region, kEpmpPermLockedReadExecute);

  // Check CSRs match.
  ExpectReadState(csrs);
  EXPECT_EQ(epmp_state_check(&state_), kErrorOk);
}

TEST_F(EpmpNapotTest, Entry15) {
  // Region for entry.
  epmp_region_t region = {
      .start = 0x0,
      .end = 0x8,
  };

  // Configure registers for entry 15.
  PmpCsrs csrs = reset_;
  csrs.pmpcfg[15] = EPMP_CFG_L | EPMP_CFG_A_NAPOT | EPMP_CFG_R | EPMP_CFG_W;
  csrs.pmpaddr[15] = napot(region);

  // Configure same entry in state.
  epmp_state_configure_napot(&state_, 15, region, kEpmpPermLockedReadWrite);

  // Check CSRs match.
  ExpectReadState(csrs);
  EXPECT_EQ(epmp_state_check(&state_), kErrorOk);
}

}  // namespace epmp_unittest
<commit_msg>[silicon_creator] Fix warnings and clean up epmp_unittest.cc<commit_after>// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0

#include "sw/device/silicon_creator/lib/epmp.h"

#include <array>
#include <cstring>
#include <vector>

#include "gtest/gtest.h"
#include "sw/device/lib/base/bitfield.h"
#include "sw/device/lib/base/csr.h"
#include "sw/device/silicon_creator/lib/base/mock_csr.h"
#include "sw/device/silicon_creator/testing/mask_rom_test.h"

namespace epmp_unittest {
namespace {

/**
 * Representation of the hardware PMP control register state.
 */
struct PmpCsrs {
  /**
   * The unpacked PMP configuration (`pmpNcfg`) registers, one entry for each
   * PMP entry.
   */
  std::array<uint8_t, kEpmpNumRegions> pmpcfg = {};

  /**
   * The PMP address registers, one for each PMP entry.
   */
  std::array<uint32_t, kEpmpNumRegions> pmpaddr = {};

  /**
   * The Machine Security Configuration register.
   */
  uint64_t mseccfg = 0;

  /**
   * Pack the individual (`pmpNcfg`) PMP entry configuration register
   * values into hardware register (`pmpcfgN`) values.
   *
   * @returns An array representing the packed `pmpcfgN` values.
   */
  std::array<uint32_t, kEpmpNumRegions / 4> PackCfg() const {
    // There are four 8-bit `pmpNcfg` values packed into each `pmpcfgN`
    // register.
    std::array<uint32_t, kEpmpNumRegions / 4> packed;
    std::memcpy(&packed, &pmpcfg, sizeof(pmpcfg));
    return packed;
  }

  /**
   * Generate a state that is a snapshot of the current PMP control register
   * values.
   *
   * @returns An `epmp_state_t` value.
   */
  epmp_state_t State() const {
    epmp_state_t state = {
        .mseccfg = static_cast<uint32_t>(mseccfg),
    };
    std::memcpy(&state.pmpcfg, &pmpcfg, sizeof(pmpcfg));
    std::memcpy(&state.pmpaddr, &pmpaddr, sizeof(pmpaddr));
    return state;
  }
};

/**
 * Map from PMP config register index to CSR_REG_PMPCFGN value.
 */
constexpr std::array<uint32_t, 4> kPmpcfgMap = {
    CSR_REG_PMPCFG0,
    CSR_REG_PMPCFG1,
    CSR_REG_PMPCFG2,
    CSR_REG_PMPCFG3,
};

/**
 * Map from PMP address register index to CSR_REG_PMPADDRN value.
 */
constexpr std::array<uint32_t, 16> kPmpaddrMap = {
    CSR_REG_PMPADDR0,  CSR_REG_PMPADDR1,  CSR_REG_PMPADDR2,  CSR_REG_PMPADDR3,
    CSR_REG_PMPADDR4,  CSR_REG_PMPADDR5,  CSR_REG_PMPADDR6,  CSR_REG_PMPADDR7,
    CSR_REG_PMPADDR8,  CSR_REG_PMPADDR9,  CSR_REG_PMPADDR10, CSR_REG_PMPADDR11,
    CSR_REG_PMPADDR12, CSR_REG_PMPADDR13, CSR_REG_PMPADDR14, CSR_REG_PMPADDR15,
};

/**
 * Helper function to call EXPECT_CSR_READ on all pmpcfg registers
 * returning the values provided.
 */
void ExpectPmpcfgRead(const PmpCsrs &expect) {
  auto packed = expect.PackCfg();
  for (size_t i = 0; i < packed.size(); ++i) {
    EXPECT_CSR_READ(kPmpcfgMap.at(i), packed.at(i));
  }
}

/**
 * Helper function to call EXPECT_CSR_READ on all pmpaddr registers
 * returning the values provided.
 */
void ExpectPmpaddrRead(const PmpCsrs &expect) {
  for (size_t i = 0; i < expect.pmpaddr.size(); ++i) {
    EXPECT_CSR_READ(kPmpaddrMap.at(i), expect.pmpaddr.at(i));
  }
}

/**
 * Helper function to call EXPECT_CSR_READ on the mseccfg register
 * returning the value provided.
 */
void ExpectMseccfgRead(const PmpCsrs &expect) {
  EXPECT_CSR_READ(CSR_REG_MSECCFG, static_cast<uint32_t>(expect.mseccfg));
  EXPECT_CSR_READ(CSR_REG_MSECCFGH,
                  static_cast<uint32_t>(expect.mseccfg >> 32));
}

/**
 * Helper function to call EXPECT_CSR_READ as necessary for an
 * internal `read_state` call with the expected values provided.
 */
void ExpectReadState(const PmpCsrs &expect) {
  ExpectPmpaddrRead(expect);
  ExpectPmpcfgRead(expect);
  ExpectMseccfgRead(expect);
}

TEST(EpmpCheckTest, Default) {
  mock_csr::MockCsr csr;

  ExpectReadState({});

  epmp_state_t state = {};
  EXPECT_EQ(epmp_state_check(&state), kErrorOk);
}

TEST(EpmpCheckTest, ErrorPmpaddr) {
  mock_csr::MockCsr csr;

  PmpCsrs bad;
  bad.pmpaddr[15] ^= 1 << 31;

  ExpectReadState(bad);

  epmp_state_t state = {};
  EXPECT_EQ(epmp_state_check(&state), kErrorEpmpBadCheck);
}

TEST(EpmpCheckTest, ErrorPmpcfg) {
  mock_csr::MockCsr csr;

  PmpCsrs bad;
  bad.pmpcfg[0] ^= 1;

  ExpectReadState(bad);

  epmp_state_t state = {};
  EXPECT_EQ(epmp_state_check(&state), kErrorEpmpBadCheck);
}

TEST(EpmpCheckTest, ErrorMseccfg) {
  mock_csr::MockCsr csr;

  PmpCsrs bad;
  bad.mseccfg ^= 1;

  ExpectReadState(bad);

  epmp_state_t state = {};
  EXPECT_EQ(epmp_state_check(&state), kErrorEpmpBadCheck);
}

TEST(EpmpTorTest, Entry0) {
  mock_csr::MockCsr csr;
  epmp_region_t region = {
      .start = 0,
      .end = 0,
  };

  // Configure registers for entry 0 (base address implicitly 0).
  PmpCsrs csrs;
  csrs.pmpcfg[0] = EPMP_CFG_L | EPMP_CFG_A_TOR | EPMP_CFG_R;
  csrs.pmpaddr[0] = region.end >> 2;

  epmp_state_t state = {};
  epmp_state_configure_tor(&state, 0, region, kEpmpPermLockedReadOnly);

  ExpectReadState(csrs);
  EXPECT_EQ(epmp_state_check(&state), kErrorOk);
}

TEST(EpmpTorTest, Entry1) {
  mock_csr::MockCsr csr;
  epmp_region_t region = {
      .start = 0x120,
      .end = 0x140,
  };

  // Configure registers for entry 1, with base address in entry 0.
  PmpCsrs csrs;
  csrs.pmpaddr[0] = region.start >> 2;
  csrs.pmpcfg[1] = EPMP_CFG_L | EPMP_CFG_A_TOR | EPMP_CFG_R;
  csrs.pmpaddr[1] = region.end >> 2;

  epmp_state_t state = {};
  epmp_state_configure_tor(&state, 1, region, kEpmpPermLockedReadOnly);

  ExpectReadState(csrs);
  EXPECT_EQ(epmp_state_check(&state), kErrorOk);
}

TEST(EpmpTorTest, Entry15) {
  mock_csr::MockCsr csr;
  epmp_region_t region = {
      .start = 0x120,
      .end = 0x140,
  };

  // Configure registers for entry 15, with base address in entry 14.
  PmpCsrs csrs;
  csrs.pmpaddr[14] = region.start >> 2;
  csrs.pmpcfg[15] = EPMP_CFG_L | EPMP_CFG_A_TOR | EPMP_CFG_R;
  csrs.pmpaddr[15] = region.end >> 2;

  epmp_state_t state = {};
  epmp_state_configure_tor(&state, 15, region, kEpmpPermLockedReadOnly);

  ExpectReadState(csrs);
  EXPECT_EQ(epmp_state_check(&state), kErrorOk);
}

TEST(EpmpTorTest, SharedAddress) {
  mock_csr::MockCsr csr;

  // Adjacent regions for entries.
  epmp_region_t region1 = {
      .start = 0x120,
      .end = 0x140,
  };
  epmp_region_t region2 = {
      .start = 0x140,
      .end = 0x180,
  };

  // Configure registers for entries 1 and 2, with base address in entry 0.
  PmpCsrs csrs;
  csrs.pmpaddr[0] = region1.start >> 2;
  csrs.pmpcfg[1] = EPMP_CFG_L | EPMP_CFG_A_TOR | EPMP_CFG_R | EPMP_CFG_X;
  csrs.pmpaddr[1] = region1.end >> 2;
  csrs.pmpcfg[2] = EPMP_CFG_L | EPMP_CFG_A_TOR | EPMP_CFG_R;
  csrs.pmpaddr[2] = region2.end >> 2;

  epmp_state_t state = {};
  epmp_state_configure_tor(&state, 1, region1, kEpmpPermLockedReadExecute);
  epmp_state_configure_tor(&state, 2, region2, kEpmpPermLockedReadOnly);

  ExpectReadState(csrs);
  EXPECT_EQ(epmp_state_check(&state), kErrorOk);
}

TEST(EpmpNa4Test, Entry0) {
  mock_csr::MockCsr csr;
  epmp_region_t region = {
      .start = 0xF4,
      .end = 0xF8,
  };

  // Configure registers for entry 0.
  PmpCsrs csrs;
  csrs.pmpcfg[0] = EPMP_CFG_L | EPMP_CFG_A_NA4 | EPMP_CFG_R | EPMP_CFG_X;
  csrs.pmpaddr[0] = region.start >> 2;

  epmp_state_t state = {};
  epmp_state_configure_na4(&state, 0, region, kEpmpPermLockedReadExecute);

  ExpectReadState(csrs);
  EXPECT_EQ(epmp_state_check(&state), kErrorOk);
}

TEST(EpmpNa4Test, Entry15) {
  mock_csr::MockCsr csr;
  epmp_region_t region = {
      .start = 0x0,
      .end = 0x4,
  };

  // Configure registers for entry 15.
  PmpCsrs csrs;
  csrs.pmpcfg[15] = EPMP_CFG_L | EPMP_CFG_A_NA4 | EPMP_CFG_R | EPMP_CFG_W;
  csrs.pmpaddr[15] = region.start >> 2;

  epmp_state_t state = {};
  epmp_state_configure_na4(&state, 15, region, kEpmpPermLockedReadWrite);

  ExpectReadState(csrs);
  EXPECT_EQ(epmp_state_check(&state), kErrorOk);
}

/**
 * Encode a region using the NAPOT address mode. This does not
 * do any validation of the values provided.
 *
 * Note: other address modes just require the address to be right
 * shifted by 2.
 */
uint32_t Napot(epmp_region_t region) {
  return (region.start >> 2) | ((region.end - region.start - 1) >> 3);
}

TEST(EpmpNapotTest, Entry0) {
  mock_csr::MockCsr csr;
  epmp_region_t region = {
      .start = 0xFF00,
      .end = 0xFF10,
  };

  // Configure registers for entry 0.
  PmpCsrs csrs;
  csrs.pmpcfg[0] = EPMP_CFG_L | EPMP_CFG_A_NAPOT | EPMP_CFG_R | EPMP_CFG_X;
  csrs.pmpaddr[0] = Napot(region);

  epmp_state_t state = {};
  epmp_state_configure_napot(&state, 0, region, kEpmpPermLockedReadExecute);

  ExpectReadState(csrs);
  EXPECT_EQ(epmp_state_check(&state), kErrorOk);
}

TEST(EpmpNapotTest, Entry15) {
  mock_csr::MockCsr csr;
  epmp_region_t region = {
      .start = 0x0,
      .end = 0x8,
  };

  // Configure registers for entry 15.
  PmpCsrs csrs;
  csrs.pmpcfg[15] = EPMP_CFG_L | EPMP_CFG_A_NAPOT | EPMP_CFG_R | EPMP_CFG_W;
  csrs.pmpaddr[15] = Napot(region);

  epmp_state_t state = {};
  epmp_state_configure_napot(&state, 15, region, kEpmpPermLockedReadWrite);

  ExpectReadState(csrs);
  EXPECT_EQ(epmp_state_check(&state), kErrorOk);
}

}  // namespace
}  // namespace epmp_unittest
<|endoftext|>


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

#include <folly/io/RecordIO.h>

#include <sys/types.h>

#include <random>

#include <glog/logging.h>

#include <folly/Conv.h>
#include <folly/FBString.h>
#include <folly/Random.h>
#include <folly/experimental/TestUtil.h>
#include <folly/io/IOBufQueue.h>
#include <folly/portability/GFlags.h>
#include <folly/portability/GTest.h>
#include <folly/portability/Unistd.h>

DEFINE_int32(random_seed, folly::randomNumberSeed(), "random seed");

namespace folly {
namespace test {

namespace {
// shortcut
StringPiece sp(ByteRange br) {
  return StringPiece(br);
}

template <class T>
std::unique_ptr<IOBuf> iobufs(std::initializer_list<T> ranges) {
  IOBufQueue queue;
  for (auto& range : ranges) {
    StringPiece r(range);
    queue.append(IOBuf::wrapBuffer(r.data(), r.size()));
  }
  return queue.move();
}

} // namespace

TEST(RecordIOTest, Simple) {
  TemporaryFile file;
  {
    RecordIOWriter writer(File(file.fd()));
    writer.write(iobufs({"hello ", "world"}));
    writer.write(iobufs({"goodbye"}));
  }
  {
    RecordIOReader reader(File(file.fd()));
    auto it = reader.begin();
    ASSERT_FALSE(it == reader.end());
    EXPECT_EQ("hello world", sp((it++)->first));
    ASSERT_FALSE(it == reader.end());
    EXPECT_EQ("goodbye", sp((it++)->first));
    EXPECT_TRUE(it == reader.end());
  }
  {
    RecordIOWriter writer(File(file.fd()));
    writer.write(iobufs({"meow"}));
    writer.write(iobufs({"woof"}));
  }
  {
    RecordIOReader reader(File(file.fd()));
    auto it = reader.begin();
    ASSERT_FALSE(it == reader.end());
    EXPECT_EQ("hello world", sp((it++)->first));
    ASSERT_FALSE(it == reader.end());
    EXPECT_EQ("goodbye", sp((it++)->first));
    ASSERT_FALSE(it == reader.end());
    EXPECT_EQ("meow", sp((it++)->first));
    ASSERT_FALSE(it == reader.end());
    EXPECT_EQ("woof", sp((it++)->first));
    EXPECT_TRUE(it == reader.end());
  }
}

TEST(RecordIOTest, SmallRecords) {
  constexpr size_t kSize = 10;
  char tmp[kSize];
  memset(tmp, 'x', kSize);
  TemporaryFile file;
  {
    RecordIOWriter writer(File(file.fd()));
    for (size_t i = 0; i < kSize; ++i) { // record of size 0 should be ignored
      writer.write(IOBuf::wrapBuffer(tmp, i));
    }
  }
  {
    RecordIOReader reader(File(file.fd()));
    auto it = reader.begin();
    for (size_t i = 1; i < kSize; ++i) {
      ASSERT_FALSE(it == reader.end());
      EXPECT_EQ(StringPiece(tmp, i), sp((it++)->first));
    }
    EXPECT_TRUE(it == reader.end());
  }
}

TEST(RecordIOTest, MultipleFileIds) {
  TemporaryFile file;
  {
    RecordIOWriter writer(File(file.fd()), 1);
    writer.write(iobufs({"hello"}));
  }
  {
    RecordIOWriter writer(File(file.fd()), 2);
    writer.write(iobufs({"world"}));
  }
  {
    RecordIOWriter writer(File(file.fd()), 1);
    writer.write(iobufs({"goodbye"}));
  }
  {
    RecordIOReader reader(File(file.fd()), 0); // return all
    auto it = reader.begin();
    ASSERT_FALSE(it == reader.end());
    EXPECT_EQ("hello", sp((it++)->first));
    ASSERT_FALSE(it == reader.end());
    EXPECT_EQ("world", sp((it++)->first));
    ASSERT_FALSE(it == reader.end());
    EXPECT_EQ("goodbye", sp((it++)->first));
    EXPECT_TRUE(it == reader.end());
  }
  {
    RecordIOReader reader(File(file.fd()), 1);
    auto it = reader.begin();
    ASSERT_FALSE(it == reader.end());
    EXPECT_EQ("hello", sp((it++)->first));
    ASSERT_FALSE(it == reader.end());
    EXPECT_EQ("goodbye", sp((it++)->first));
    EXPECT_TRUE(it == reader.end());
  }
  {
    RecordIOReader reader(File(file.fd()), 2);
    auto it = reader.begin();
    ASSERT_FALSE(it == reader.end());
    EXPECT_EQ("world", sp((it++)->first));
    EXPECT_TRUE(it == reader.end());
  }
  {
    RecordIOReader reader(File(file.fd()), 3);
    auto it = reader.begin();
    EXPECT_TRUE(it == reader.end());
  }
}

TEST(RecordIOTest, ExtraMagic) {
  TemporaryFile file;
  {
    RecordIOWriter writer(File(file.fd()));
    writer.write(iobufs({"hello"}));
  }
  uint8_t buf[recordio_helpers::headerSize() + 5];
  EXPECT_EQ(0, lseek(file.fd(), 0, SEEK_SET));
  EXPECT_EQ(sizeof(buf), read(file.fd(), buf, sizeof(buf)));
  // Append an extra magic
  const uint32_t magic = recordio_helpers::recordio_detail::Header::kMagic;
  EXPECT_EQ(sizeof(magic), write(file.fd(), &magic, sizeof(magic)));
  // and an extra record
  EXPECT_EQ(sizeof(buf), write(file.fd(), buf, sizeof(buf)));
  {
    RecordIOReader reader(File(file.fd()));
    auto it = reader.begin();
    ASSERT_FALSE(it == reader.end());
    EXPECT_EQ("hello", sp((it++)->first));
    ASSERT_FALSE(it == reader.end());
    EXPECT_EQ("hello", sp((it++)->first));
    EXPECT_TRUE(it == reader.end());
  }
}

namespace {
void corrupt(int fd, off_t pos) {
  uint8_t val = 0;
  EXPECT_EQ(1, pread(fd, &val, 1, pos));
  ++val;
  EXPECT_EQ(1, pwrite(fd, &val, 1, pos));
}
} // namespace

TEST(RecordIOTest, Randomized) {
  SCOPED_TRACE(to<std::string>("Random seed is ", FLAGS_random_seed));
  std::mt19937 rnd(FLAGS_random_seed);

  size_t recordCount = std::uniform_int_distribution<uint32_t>(30, 300)(rnd);

  std::uniform_int_distribution<uint32_t> recordSizeDist(1, 3 << 16);
  std::uniform_int_distribution<uint32_t> charDist(0, 255);
  std::uniform_int_distribution<uint32_t> junkDist(0, 1 << 20);
  // corrupt 1/5 of all records
  std::uniform_int_distribution<uint32_t> corruptDist(0, 4);

  std::vector<std::pair<fbstring, off_t>> records;
  std::vector<off_t> corruptPositions;
  records.reserve(recordCount);
  TemporaryFile file;

  fbstring record;
  // Recreate the writer multiple times so we test that we create a
  // continuous stream
  for (size_t i = 0; i < 3; ++i) {
    RecordIOWriter writer(File(file.fd()));
    for (size_t j = 0; j < recordCount; ++j) {
      off_t beginPos = writer.filePos();
      record.clear();
      size_t recordSize = recordSizeDist(rnd);
      record.reserve(recordSize);
      for (size_t k = 0; k < recordSize; ++k) {
        record.push_back(charDist(rnd));
      }
      writer.write(iobufs({record}));

      bool corrupt = (corruptDist(rnd) == 0);
      if (corrupt) {
        // Corrupt one random byte in the record (including header)
        std::uniform_int_distribution<uint32_t> corruptByteDist(
            0, recordSize + recordio_helpers::headerSize() - 1);
        off_t corruptRel = corruptByteDist(rnd);
        VLOG(1) << "n=" << records.size() << " bpos=" << beginPos
                << " rsize=" << record.size() << " corrupt rel=" << corruptRel
                << " abs=" << beginPos + corruptRel;
        corruptPositions.push_back(beginPos + corruptRel);
      } else {
        VLOG(2) << "n=" << records.size() << " bpos=" << beginPos
                << " rsize=" << record.size() << " good";
        records.emplace_back(std::move(record), beginPos);
      }
    }
    VLOG(1) << "n=" << records.size() << " close abs=" << writer.filePos();
  }

  for (auto& pos : corruptPositions) {
    corrupt(file.fd(), pos);
  }

  {
    size_t i = 0;
    RecordIOReader reader(File(file.fd()));
    for (auto& r : reader) {
      SCOPED_TRACE(i);
      ASSERT_LT(i, records.size());
      EXPECT_EQ(records[i].first, sp(r.first));
      EXPECT_EQ(records[i].second, r.second);
      ++i;
    }
    EXPECT_EQ(records.size(), i);
  }
}

TEST(RecordIOTest, validateRecordAPI) {
  uint32_t hdrSize = recordio_helpers::headerSize();
  std::vector<uint32_t> testSizes = {
      0, 1, hdrSize - 1, hdrSize, hdrSize + 1, 2 * hdrSize, 1024};
  constexpr uint32_t kTestFileId = 100;
  std::mt19937 rnd(FLAGS_random_seed);

  for (auto& testSize : testSizes) {
    char testChar = testSize % 26 + 'A';
    // create a IOBuf of size = testSize
    auto buf = folly::IOBuf::create(testSize);
    buf->append(testSize);
    EXPECT_NE(buf, nullptr);
    auto wData = buf->writableData();
    if (testSize > hdrSize) {
      // if the testSize is more than header size, populate databytes with
      // testChar
      buf->trimStart(hdrSize);
      memset(wData, testChar, buf->length());
      recordio_helpers::prependHeader(buf, kTestFileId);
      buf->unshare();
      buf->coalesce();
      wData = buf->writableData();
    }
    // validate header
    auto res = recordio_helpers::validateRecordHeader(
        folly::Range<unsigned char*>(wData, buf->length()), kTestFileId);
    if (testSize > hdrSize) {
      // validation should succeed for buffers larger than header size
      EXPECT_TRUE(res);
      auto range = folly::Range<unsigned char*>(wData, buf->length());
      // make sure data validation also succeeds
      auto dataRes = recordio_helpers::validateRecordData(range);
      EXPECT_NE(dataRes.fileId, 0);

      // do the entire record validation again
      auto recRes = recordio_helpers::validateRecord(range, kTestFileId);
      EXPECT_NE(recRes.fileId, 0);

      std::uniform_int_distribution<uint32_t> dataDist(
          0, testSize - hdrSize - 1);
      size_t idx = dataDist(rnd);
      /* Now corrupt a random byte and expect data validation to fail */
      wData[hdrSize + idx] = testChar + 1;
      auto newDataRes = recordio_helpers::validateRecordData(range);
      EXPECT_EQ(newDataRes.fileId, 0);

      /* header validation should still pass */
      auto newRes = recordio_helpers::validateRecordHeader(range, kTestFileId);
      EXPECT_TRUE(newRes);

      /* corrupt a random header byte */
      std::uniform_int_distribution<uint32_t> hdrDist(0, hdrSize - 1);
      idx = hdrDist(rnd);
      wData[idx] = testChar;

      /* header validation should fail now */
      newRes = recordio_helpers::validateRecordHeader(range, kTestFileId);
      EXPECT_FALSE(newRes);
    } else {
      EXPECT_FALSE(res);
    }
  }
}

} // namespace test
} // namespace folly

int main(int argc, char* argv[]) {
  testing::InitGoogleTest(&argc, argv);
  gflags::ParseCommandLineFlags(&argc, &argv, true);
  return RUN_ALL_TESTS();
}
<commit_msg>fix use-of-uninitialized-value MSAN errors in RecordIOTest.cpp<commit_after>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <folly/io/RecordIO.h>

#include <sys/types.h>

#include <random>

#include <glog/logging.h>

#include <folly/Conv.h>
#include <folly/FBString.h>
#include <folly/Random.h>
#include <folly/experimental/TestUtil.h>
#include <folly/io/IOBufQueue.h>
#include <folly/portability/GFlags.h>
#include <folly/portability/GTest.h>
#include <folly/portability/Unistd.h>

DEFINE_int32(random_seed, folly::randomNumberSeed(), "random seed");

namespace folly {
namespace test {

namespace {
// shortcut
StringPiece sp(ByteRange br) {
  return StringPiece(br);
}

template <class T>
std::unique_ptr<IOBuf> iobufs(std::initializer_list<T> ranges) {
  IOBufQueue queue;
  for (auto& range : ranges) {
    StringPiece r(range);
    queue.append(IOBuf::wrapBuffer(r.data(), r.size()));
  }
  return queue.move();
}

} // namespace

TEST(RecordIOTest, Simple) {
  TemporaryFile file;
  {
    RecordIOWriter writer(File(file.fd()));
    writer.write(iobufs({"hello ", "world"}));
    writer.write(iobufs({"goodbye"}));
  }
  {
    RecordIOReader reader(File(file.fd()));
    auto it = reader.begin();
    ASSERT_FALSE(it == reader.end());
    EXPECT_EQ("hello world", sp((it++)->first));
    ASSERT_FALSE(it == reader.end());
    EXPECT_EQ("goodbye", sp((it++)->first));
    EXPECT_TRUE(it == reader.end());
  }
  {
    RecordIOWriter writer(File(file.fd()));
    writer.write(iobufs({"meow"}));
    writer.write(iobufs({"woof"}));
  }
  {
    RecordIOReader reader(File(file.fd()));
    auto it = reader.begin();
    ASSERT_FALSE(it == reader.end());
    EXPECT_EQ("hello world", sp((it++)->first));
    ASSERT_FALSE(it == reader.end());
    EXPECT_EQ("goodbye", sp((it++)->first));
    ASSERT_FALSE(it == reader.end());
    EXPECT_EQ("meow", sp((it++)->first));
    ASSERT_FALSE(it == reader.end());
    EXPECT_EQ("woof", sp((it++)->first));
    EXPECT_TRUE(it == reader.end());
  }
}

TEST(RecordIOTest, SmallRecords) {
  constexpr size_t kSize = 10;
  char tmp[kSize];
  memset(tmp, 'x', kSize);
  TemporaryFile file;
  {
    RecordIOWriter writer(File(file.fd()));
    for (size_t i = 0; i < kSize; ++i) { // record of size 0 should be ignored
      writer.write(IOBuf::wrapBuffer(tmp, i));
    }
  }
  {
    RecordIOReader reader(File(file.fd()));
    auto it = reader.begin();
    for (size_t i = 1; i < kSize; ++i) {
      ASSERT_FALSE(it == reader.end());
      EXPECT_EQ(StringPiece(tmp, i), sp((it++)->first));
    }
    EXPECT_TRUE(it == reader.end());
  }
}

TEST(RecordIOTest, MultipleFileIds) {
  TemporaryFile file;
  {
    RecordIOWriter writer(File(file.fd()), 1);
    writer.write(iobufs({"hello"}));
  }
  {
    RecordIOWriter writer(File(file.fd()), 2);
    writer.write(iobufs({"world"}));
  }
  {
    RecordIOWriter writer(File(file.fd()), 1);
    writer.write(iobufs({"goodbye"}));
  }
  {
    RecordIOReader reader(File(file.fd()), 0); // return all
    auto it = reader.begin();
    ASSERT_FALSE(it == reader.end());
    EXPECT_EQ("hello", sp((it++)->first));
    ASSERT_FALSE(it == reader.end());
    EXPECT_EQ("world", sp((it++)->first));
    ASSERT_FALSE(it == reader.end());
    EXPECT_EQ("goodbye", sp((it++)->first));
    EXPECT_TRUE(it == reader.end());
  }
  {
    RecordIOReader reader(File(file.fd()), 1);
    auto it = reader.begin();
    ASSERT_FALSE(it == reader.end());
    EXPECT_EQ("hello", sp((it++)->first));
    ASSERT_FALSE(it == reader.end());
    EXPECT_EQ("goodbye", sp((it++)->first));
    EXPECT_TRUE(it == reader.end());
  }
  {
    RecordIOReader reader(File(file.fd()), 2);
    auto it = reader.begin();
    ASSERT_FALSE(it == reader.end());
    EXPECT_EQ("world", sp((it++)->first));
    EXPECT_TRUE(it == reader.end());
  }
  {
    RecordIOReader reader(File(file.fd()), 3);
    auto it = reader.begin();
    EXPECT_TRUE(it == reader.end());
  }
}

TEST(RecordIOTest, ExtraMagic) {
  TemporaryFile file;
  {
    RecordIOWriter writer(File(file.fd()));
    writer.write(iobufs({"hello"}));
  }
  uint8_t buf[recordio_helpers::headerSize() + 5];
  EXPECT_EQ(0, lseek(file.fd(), 0, SEEK_SET));
  EXPECT_EQ(sizeof(buf), read(file.fd(), buf, sizeof(buf)));
  // Append an extra magic
  const uint32_t magic = recordio_helpers::recordio_detail::Header::kMagic;
  EXPECT_EQ(sizeof(magic), write(file.fd(), &magic, sizeof(magic)));
  // and an extra record
  EXPECT_EQ(sizeof(buf), write(file.fd(), buf, sizeof(buf)));
  {
    RecordIOReader reader(File(file.fd()));
    auto it = reader.begin();
    ASSERT_FALSE(it == reader.end());
    EXPECT_EQ("hello", sp((it++)->first));
    ASSERT_FALSE(it == reader.end());
    EXPECT_EQ("hello", sp((it++)->first));
    EXPECT_TRUE(it == reader.end());
  }
}

namespace {
void corrupt(int fd, off_t pos) {
  uint8_t val = 0;
  EXPECT_EQ(1, pread(fd, &val, 1, pos));
  ++val;
  EXPECT_EQ(1, pwrite(fd, &val, 1, pos));
}
} // namespace

TEST(RecordIOTest, Randomized) {
  SCOPED_TRACE(to<std::string>("Random seed is ", FLAGS_random_seed));
  std::mt19937 rnd(FLAGS_random_seed);

  size_t recordCount = std::uniform_int_distribution<uint32_t>(30, 300)(rnd);

  std::uniform_int_distribution<uint32_t> recordSizeDist(1, 3 << 16);
  std::uniform_int_distribution<uint32_t> charDist(0, 255);
  std::uniform_int_distribution<uint32_t> junkDist(0, 1 << 20);
  // corrupt 1/5 of all records
  std::uniform_int_distribution<uint32_t> corruptDist(0, 4);

  std::vector<std::pair<fbstring, off_t>> records;
  std::vector<off_t> corruptPositions;
  records.reserve(recordCount);
  TemporaryFile file;

  fbstring record;
  // Recreate the writer multiple times so we test that we create a
  // continuous stream
  for (size_t i = 0; i < 3; ++i) {
    RecordIOWriter writer(File(file.fd()));
    for (size_t j = 0; j < recordCount; ++j) {
      off_t beginPos = writer.filePos();
      record.clear();
      size_t recordSize = recordSizeDist(rnd);
      record.reserve(recordSize);
      for (size_t k = 0; k < recordSize; ++k) {
        record.push_back(charDist(rnd));
      }
      writer.write(iobufs({record}));

      bool corrupt = (corruptDist(rnd) == 0);
      if (corrupt) {
        // Corrupt one random byte in the record (including header)
        std::uniform_int_distribution<uint32_t> corruptByteDist(
            0, recordSize + recordio_helpers::headerSize() - 1);
        off_t corruptRel = corruptByteDist(rnd);
        VLOG(1) << "n=" << records.size() << " bpos=" << beginPos
                << " rsize=" << record.size() << " corrupt rel=" << corruptRel
                << " abs=" << beginPos + corruptRel;
        corruptPositions.push_back(beginPos + corruptRel);
      } else {
        VLOG(2) << "n=" << records.size() << " bpos=" << beginPos
                << " rsize=" << record.size() << " good";
        records.emplace_back(std::move(record), beginPos);
      }
    }
    VLOG(1) << "n=" << records.size() << " close abs=" << writer.filePos();
  }

  for (auto& pos : corruptPositions) {
    corrupt(file.fd(), pos);
  }

  {
    size_t i = 0;
    RecordIOReader reader(File(file.fd()));
    for (auto& r : reader) {
      SCOPED_TRACE(i);
      ASSERT_LT(i, records.size());
      EXPECT_EQ(records[i].first, sp(r.first));
      EXPECT_EQ(records[i].second, r.second);
      ++i;
    }
    EXPECT_EQ(records.size(), i);
  }
}

TEST(RecordIOTest, validateRecordAPI) {
  uint32_t hdrSize = recordio_helpers::headerSize();
  std::vector<uint32_t> testSizes = {
      0, 1, hdrSize - 1, hdrSize, hdrSize + 1, 2 * hdrSize, 1024};
  constexpr uint32_t kTestFileId = 100;
  std::mt19937 rnd(FLAGS_random_seed);

  for (auto& testSize : testSizes) {
    char testChar = testSize % 26 + 'A';
    // create a IOBuf of size = testSize
    auto buf = folly::IOBuf::create(testSize);
    buf->append(testSize);
    EXPECT_NE(buf, nullptr);
    auto wData = buf->writableData();
    if (testSize >= hdrSize) {
      // if the testSize is greater or equal than header size, populate buffer
      // bytes with testChar
      buf->trimStart(hdrSize);
      memset(wData, testChar, buf->capacity());
      recordio_helpers::prependHeader(buf, kTestFileId);
      buf->unshare();
      buf->coalesce();
      wData = buf->writableData();
    }
    // validate header
    auto res = recordio_helpers::validateRecordHeader(
        folly::Range<unsigned char*>(wData, buf->length()), kTestFileId);
    if (testSize > hdrSize) {
      // validation should succeed for buffers larger than header size
      EXPECT_TRUE(res);
      auto range = folly::Range<unsigned char*>(wData, buf->length());
      // make sure data validation also succeeds
      auto dataRes = recordio_helpers::validateRecordData(range);
      EXPECT_NE(dataRes.fileId, 0);

      // do the entire record validation again
      auto recRes = recordio_helpers::validateRecord(range, kTestFileId);
      EXPECT_NE(recRes.fileId, 0);

      std::uniform_int_distribution<uint32_t> dataDist(
          0, testSize - hdrSize - 1);
      size_t idx = dataDist(rnd);
      /* Now corrupt a random byte and expect data validation to fail */
      wData[hdrSize + idx] = testChar + 1;
      auto newDataRes = recordio_helpers::validateRecordData(range);
      EXPECT_EQ(newDataRes.fileId, 0);

      /* header validation should still pass */
      auto newRes = recordio_helpers::validateRecordHeader(range, kTestFileId);
      EXPECT_TRUE(newRes);

      /* corrupt a random header byte */
      std::uniform_int_distribution<uint32_t> hdrDist(0, hdrSize - 1);
      idx = hdrDist(rnd);
      wData[idx] = testChar;

      /* header validation should fail now */
      newRes = recordio_helpers::validateRecordHeader(range, kTestFileId);
      EXPECT_FALSE(newRes);
    } else {
      EXPECT_FALSE(res);
    }
  }
}

} // namespace test
} // namespace folly

int main(int argc, char* argv[]) {
  testing::InitGoogleTest(&argc, argv);
  gflags::ParseCommandLineFlags(&argc, &argv, true);
  return RUN_ALL_TESTS();
}
<|endoftext|>


<commit_before>/*
 * D-Bus AT-SPI, Qt Adaptor
 *
 * Copyright 2008-2011 Nokia Corporation and/or its subsidiary(-ies).
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "constant_mappings.h"

#include <qobject.h>

// FIXME the assignment of roles is quite arbitrary, at some point go through this list and sort and check that it makes sense
//  "calendar" "check menu item"  "color chooser" "column header"    "dateeditor"  "desktop icon"  "desktop frame"
//  "directory pane"  "drawing area"  "file chooser" "fontchooser"  "frame"  "glass pane"  "html container"  "icon"
//  "internal frame"  "option pane"  "password text" "radio menu item"  "root pane"  "row header"    "scroll pane"
//  "tear off menu item"  "terminal" "toggle button" "tree table"  "unknown"  "viewport" "header"  "footer"  "paragraph"
//  "ruler"    "autocomplete"  "edit bar" "embedded component"  "entry"    "caption"
//  "heading"  "page"  "section"  "redundant object"  "form"  "input method window"  "menu"

QHash <QAccessible::Role, RoleNames> qSpiRoleMapping;

static void initializeRoleMapping ()
{
    qSpiRoleMapping.insert(QAccessible::NoRole, RoleNames(ATSPI_ROLE_INVALID, "invalid", QObject::tr("invalid role")));
    qSpiRoleMapping.insert(QAccessible::TitleBar, RoleNames(ATSPI_ROLE_TEXT, "text", QObject::tr("title bar")));
    qSpiRoleMapping.insert(QAccessible::MenuBar, RoleNames(ATSPI_ROLE_MENU_BAR, "menu bar", QObject::tr("menu bar")));
    qSpiRoleMapping.insert(QAccessible::ScrollBar, RoleNames(ATSPI_ROLE_SCROLL_BAR, "scroll bar", QObject::tr("scroll bar")));
    qSpiRoleMapping.insert(QAccessible::Grip, RoleNames(ATSPI_ROLE_UNKNOWN, "unknown", QObject::tr("grip")));
    qSpiRoleMapping.insert(QAccessible::Sound, RoleNames(ATSPI_ROLE_UNKNOWN, "unknown", QObject::tr("sound")));
    qSpiRoleMapping.insert(QAccessible::Cursor, RoleNames(ATSPI_ROLE_ARROW, "arrow", QObject::tr("cursor")));
    qSpiRoleMapping.insert(QAccessible::Caret, RoleNames(ATSPI_ROLE_UNKNOWN, "unknown", QObject::tr("caret")));
    qSpiRoleMapping.insert(QAccessible::AlertMessage, RoleNames(ATSPI_ROLE_ALERT, "alert", QObject::tr("alert message")));
    qSpiRoleMapping.insert(QAccessible::Window, RoleNames(ATSPI_ROLE_WINDOW, "window", QObject::tr("window")));
    qSpiRoleMapping.insert(QAccessible::Client, RoleNames(ATSPI_ROLE_FILLER, "filler", QObject::tr("filler")));
    qSpiRoleMapping.insert(QAccessible::PopupMenu, RoleNames(ATSPI_ROLE_POPUP_MENU, "popup menu", QObject::tr("popup menu")));
    qSpiRoleMapping.insert(QAccessible::MenuItem, RoleNames(ATSPI_ROLE_MENU_ITEM, "menu item", QObject::tr("menu item")));
    qSpiRoleMapping.insert(QAccessible::ToolTip, RoleNames(ATSPI_ROLE_TOOL_TIP, "tool tip", QObject::tr("tool tip")));
    qSpiRoleMapping.insert(QAccessible::Application, RoleNames(ATSPI_ROLE_APPLICATION, "application", QObject::tr("application")));
    qSpiRoleMapping.insert(QAccessible::Document, RoleNames(ATSPI_ROLE_DOCUMENT_FRAME, "document frame", QObject::tr("document")));
    qSpiRoleMapping.insert(QAccessible::Pane, RoleNames(ATSPI_ROLE_PANEL, "panel", QObject::tr("pane")));
    qSpiRoleMapping.insert(QAccessible::Chart, RoleNames(ATSPI_ROLE_CHART, "chart", QObject::tr("chart")));
    qSpiRoleMapping.insert(QAccessible::Dialog, RoleNames(ATSPI_ROLE_DIALOG, "dialog", QObject::tr("dialog")));
    qSpiRoleMapping.insert(QAccessible::Border, RoleNames(ATSPI_ROLE_FRAME, "frame", QObject::tr("border")));
    qSpiRoleMapping.insert(QAccessible::Grouping, RoleNames(ATSPI_ROLE_PANEL, "panel", QObject::tr("grouping")));
    qSpiRoleMapping.insert(QAccessible::Separator, RoleNames(ATSPI_ROLE_SEPARATOR, "separator", QObject::tr("separator")));
    qSpiRoleMapping.insert(QAccessible::ToolBar, RoleNames(ATSPI_ROLE_TOOL_BAR, "tool bar", QObject::tr("tool bar")));
    qSpiRoleMapping.insert(QAccessible::StatusBar, RoleNames(ATSPI_ROLE_STATUS_BAR, "statusbar", QObject::tr("status bar")));
    qSpiRoleMapping.insert(QAccessible::Table, RoleNames(ATSPI_ROLE_TABLE, "table", QObject::tr("table")));
    qSpiRoleMapping.insert(QAccessible::ColumnHeader, RoleNames(ATSPI_ROLE_TABLE_COLUMN_HEADER, "column header", QObject::tr("column header")));
    qSpiRoleMapping.insert(QAccessible::RowHeader, RoleNames(ATSPI_ROLE_TABLE_ROW_HEADER, "row header", QObject::tr("row header")));
    qSpiRoleMapping.insert(QAccessible::Column, RoleNames(ATSPI_ROLE_TABLE_CELL, "table cell", QObject::tr("column")));
    qSpiRoleMapping.insert(QAccessible::Row, RoleNames(ATSPI_ROLE_TABLE_CELL, "table cell", QObject::tr("row")));
    qSpiRoleMapping.insert(QAccessible::Cell, RoleNames(ATSPI_ROLE_TABLE_CELL, "table cell", QObject::tr("cell")));
    qSpiRoleMapping.insert(QAccessible::Link, RoleNames(ATSPI_ROLE_LINK, "link" , QObject::tr("link")));
    qSpiRoleMapping.insert(QAccessible::HelpBalloon, RoleNames(ATSPI_ROLE_DIALOG, "dialog", QObject::tr("help balloon")));
    qSpiRoleMapping.insert(QAccessible::Assistant, RoleNames(ATSPI_ROLE_DIALOG, "dialog", QObject::tr("assistant")));
    qSpiRoleMapping.insert(QAccessible::List, RoleNames(ATSPI_ROLE_LIST, "list", QObject::tr("list")));
    qSpiRoleMapping.insert(QAccessible::ListItem, RoleNames(ATSPI_ROLE_LIST_ITEM, "list item", QObject::tr("list item")));
    qSpiRoleMapping.insert(QAccessible::Tree, RoleNames(ATSPI_ROLE_TREE, "tree", QObject::tr("tree")));
    qSpiRoleMapping.insert(QAccessible::TreeItem, RoleNames(ATSPI_ROLE_TABLE_CELL, "tree item", QObject::tr("tree item")));
    qSpiRoleMapping.insert(QAccessible::PageTab, RoleNames(ATSPI_ROLE_PAGE_TAB, "page tab", QObject::tr("page tab")));
    qSpiRoleMapping.insert(QAccessible::PropertyPage, RoleNames(ATSPI_ROLE_PAGE_TAB, "page tab", QObject::tr("property page")));
    qSpiRoleMapping.insert(QAccessible::Indicator, RoleNames(ATSPI_ROLE_UNKNOWN, "unknown", QObject::tr("indicator")));
    qSpiRoleMapping.insert(QAccessible::Graphic, RoleNames(ATSPI_ROLE_IMAGE, "image", QObject::tr("graphic")));
    qSpiRoleMapping.insert(QAccessible::StaticText, RoleNames(ATSPI_ROLE_LABEL, "label", QObject::tr("label")));
    qSpiRoleMapping.insert(QAccessible::EditableText, RoleNames(ATSPI_ROLE_TEXT, "text", QObject::tr("text")));
    qSpiRoleMapping.insert(QAccessible::PushButton, RoleNames(ATSPI_ROLE_PUSH_BUTTON, "push button", QObject::tr("push button")));
    qSpiRoleMapping.insert(QAccessible::CheckBox, RoleNames(ATSPI_ROLE_CHECK_BOX, "check box", QObject::tr("check box")));
    qSpiRoleMapping.insert(QAccessible::RadioButton, RoleNames(ATSPI_ROLE_RADIO_BUTTON, "radio button", QObject::tr("radio box")));
    qSpiRoleMapping.insert(QAccessible::ComboBox, RoleNames(ATSPI_ROLE_COMBO_BOX, "combo box", QObject::tr("combo box")));
    qSpiRoleMapping.insert(QAccessible::ProgressBar, RoleNames(ATSPI_ROLE_PROGRESS_BAR, "progress bar", QObject::tr("progress bar")));
    qSpiRoleMapping.insert(QAccessible::Dial, RoleNames(ATSPI_ROLE_DIAL, "accelerator label", QObject::tr("dial")));
    qSpiRoleMapping.insert(QAccessible::HotkeyField, RoleNames(ATSPI_ROLE_TEXT, "text", QObject::tr("hotkey field"))); //FIXME text?
    qSpiRoleMapping.insert(QAccessible::Slider, RoleNames(ATSPI_ROLE_SLIDER, "slider", QObject::tr("slider")));
    qSpiRoleMapping.insert(QAccessible::SpinBox, RoleNames(ATSPI_ROLE_SPIN_BUTTON, "spin button", QObject::tr("spin box")));
    qSpiRoleMapping.insert(QAccessible::Canvas, RoleNames(ATSPI_ROLE_CANVAS, "canvas", QObject::tr("canvas")));
    qSpiRoleMapping.insert(QAccessible::Animation, RoleNames(ATSPI_ROLE_ANIMATION, "animation", QObject::tr("animation")));
    qSpiRoleMapping.insert(QAccessible::Equation, RoleNames(ATSPI_ROLE_TEXT, "text", QObject::tr("equation")));
    qSpiRoleMapping.insert(QAccessible::ButtonDropDown, RoleNames(ATSPI_ROLE_PUSH_BUTTON, "push button", QObject::tr("button drop down")));
    qSpiRoleMapping.insert(QAccessible::ButtonMenu, RoleNames(ATSPI_ROLE_PUSH_BUTTON, "push button", QObject::tr("button menu")));
    qSpiRoleMapping.insert(QAccessible::ButtonDropGrid, RoleNames(ATSPI_ROLE_PUSH_BUTTON, "push button", QObject::tr("button drop grid")));
    qSpiRoleMapping.insert(QAccessible::Whitespace, RoleNames(ATSPI_ROLE_FILLER, "filler", QObject::tr("whitespace")));
    qSpiRoleMapping.insert(QAccessible::PageTabList, RoleNames(ATSPI_ROLE_PAGE_TAB_LIST, "page tab list", QObject::tr("page tab list")));
    qSpiRoleMapping.insert(QAccessible::Clock, RoleNames(ATSPI_ROLE_UNKNOWN, "unknown", QObject::tr("clock")));
    qSpiRoleMapping.insert(QAccessible::Splitter, RoleNames(ATSPI_ROLE_SPLIT_PANE, "split pane", QObject::tr("splitter")));
    qSpiRoleMapping.insert(QAccessible::LayeredPane, RoleNames(ATSPI_ROLE_LAYERED_PANE, "layered pane", QObject::tr("layered pane")));
    qSpiRoleMapping.insert(QAccessible::UserRole, RoleNames(ATSPI_ROLE_UNKNOWN, "unknown", QObject::tr("user role")));
}

quint64 spiStatesFromQState(QAccessible::State state)
{
    quint64 spiState = 0;

    setSpiStateBit(&spiState, ATSPI_STATE_EDITABLE);
    setSpiStateBit(&spiState, ATSPI_STATE_ENABLED);
    setSpiStateBit(&spiState, ATSPI_STATE_SHOWING);
    setSpiStateBit(&spiState, ATSPI_STATE_VISIBLE);
    setSpiStateBit(&spiState, ATSPI_STATE_SENSITIVE);

    for (int mask = 1; mask <= int(QAccessible::Modal); mask <<= 1)
    {
        switch (state & mask)
        {
        case QAccessible::Unavailable:
        {
            unsetSpiStateBit(&spiState, ATSPI_STATE_ENABLED);
            unsetSpiStateBit(&spiState, ATSPI_STATE_SHOWING);
            unsetSpiStateBit(&spiState, ATSPI_STATE_VISIBLE);
            unsetSpiStateBit(&spiState, ATSPI_STATE_SENSITIVE);
            break;
        }
        case QAccessible::Selected:
        {
            setSpiStateBit(&spiState, ATSPI_STATE_SELECTED);
            break;
        }
        case QAccessible::Focused:
        {
            setSpiStateBit(&spiState, ATSPI_STATE_FOCUSED);
            break;
        }
        case QAccessible::Pressed:
        {
            setSpiStateBit(&spiState, ATSPI_STATE_PRESSED);
            break;
        }
        case QAccessible::Checked:
        {
            setSpiStateBit(&spiState, ATSPI_STATE_CHECKED);
            break;
        }
        case QAccessible::Mixed:
        {
            setSpiStateBit(&spiState, ATSPI_STATE_INDETERMINATE);
            break;
        }
        case QAccessible::ReadOnly:
        {
            unsetSpiStateBit(&spiState, ATSPI_STATE_EDITABLE);
            break;
        }
        case QAccessible::HotTracked:
        {
            break;
        }
        case QAccessible::DefaultButton:
        {
            setSpiStateBit(&spiState, ATSPI_STATE_IS_DEFAULT);
            break;
        }
        case QAccessible::Expanded:
        {
            setSpiStateBit(&spiState, ATSPI_STATE_EXPANDED);
            break;
        }
        case QAccessible::Collapsed:
        {
            setSpiStateBit(&spiState, ATSPI_STATE_COLLAPSED);
            break;
        }
        case QAccessible::Busy:
        {
            setSpiStateBit(&spiState, ATSPI_STATE_BUSY);
            break;
        }
        case QAccessible::Marqueed:
        case QAccessible::Animated:
        {
            setSpiStateBit(&spiState, ATSPI_STATE_ANIMATED);
            break;
        }
        case QAccessible::Invisible:
        case QAccessible::Offscreen:
        {
            unsetSpiStateBit(&spiState, ATSPI_STATE_SHOWING);
            break;
        }
        case QAccessible::Sizeable:
        {
            setSpiStateBit(&spiState, ATSPI_STATE_RESIZABLE);
            break;
        }
        case QAccessible::Movable:
        case QAccessible::SelfVoicing:
        {
            break;
        }
        case QAccessible::Focusable:
        {
            setSpiStateBit(&spiState, ATSPI_STATE_FOCUSABLE);
            break;
        }
        case QAccessible::Selectable:
        {
            setSpiStateBit(&spiState, ATSPI_STATE_SELECTABLE);
            break;
        }
        case QAccessible::Linked:
        {
            break;
        }
        case QAccessible::Traversed:
        {
            setSpiStateBit(&spiState, ATSPI_STATE_VISITED);
            break;
        }
        case QAccessible::MultiSelectable:
        {
            setSpiStateBit(&spiState, ATSPI_STATE_MULTISELECTABLE);
            break;
        }
        case QAccessible::ExtSelectable:
        {
            setSpiStateBit(&spiState, ATSPI_STATE_SELECTABLE);
            break;
        }
        case QAccessible::Protected:
        case QAccessible::HasPopup:
        {
            break;
        }
        case QAccessible::Modal:
        {
            setSpiStateBit(&spiState, ATSPI_STATE_MODAL);
            break;
        }
//        case QAccessible::SingleLine:
//        {
//            setSpiStateBit(&spiState, ATSPI_STATE_SINGLE_LINE);
//            break;
//        }
        default:
        {
            break;
        }
        }
    }
    return spiState;
}

QSpiUIntList spiStateSetFromSpiStates(quint64 states)
{
    uint low = states & 0xFFFFFFFF;
    uint high = (states >> 32) & 0xFFFFFFFF;

    QSpiUIntList stateList;
    stateList.append(low);
    stateList.append(high);
    return stateList;
}

void qSpiInitializeConstantMappings()
{
    initializeRoleMapping();
}
<commit_msg>Actually check state bits.<commit_after>/*
 * D-Bus AT-SPI, Qt Adaptor
 *
 * Copyright 2008-2011 Nokia Corporation and/or its subsidiary(-ies).
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "constant_mappings.h"

#include <qobject.h>

// FIXME the assignment of roles is quite arbitrary, at some point go through this list and sort and check that it makes sense
//  "calendar" "check menu item"  "color chooser" "column header"    "dateeditor"  "desktop icon"  "desktop frame"
//  "directory pane"  "drawing area"  "file chooser" "fontchooser"  "frame"  "glass pane"  "html container"  "icon"
//  "internal frame"  "option pane"  "password text" "radio menu item"  "root pane"  "row header"    "scroll pane"
//  "tear off menu item"  "terminal" "toggle button" "tree table"  "unknown"  "viewport" "header"  "footer"  "paragraph"
//  "ruler"    "autocomplete"  "edit bar" "embedded component"  "entry"    "caption"
//  "heading"  "page"  "section"  "redundant object"  "form"  "input method window"  "menu"

QHash <QAccessible::Role, RoleNames> qSpiRoleMapping;

static void initializeRoleMapping ()
{
    qSpiRoleMapping.insert(QAccessible::NoRole, RoleNames(ATSPI_ROLE_INVALID, "invalid", QObject::tr("invalid role")));
    qSpiRoleMapping.insert(QAccessible::TitleBar, RoleNames(ATSPI_ROLE_TEXT, "text", QObject::tr("title bar")));
    qSpiRoleMapping.insert(QAccessible::MenuBar, RoleNames(ATSPI_ROLE_MENU_BAR, "menu bar", QObject::tr("menu bar")));
    qSpiRoleMapping.insert(QAccessible::ScrollBar, RoleNames(ATSPI_ROLE_SCROLL_BAR, "scroll bar", QObject::tr("scroll bar")));
    qSpiRoleMapping.insert(QAccessible::Grip, RoleNames(ATSPI_ROLE_UNKNOWN, "unknown", QObject::tr("grip")));
    qSpiRoleMapping.insert(QAccessible::Sound, RoleNames(ATSPI_ROLE_UNKNOWN, "unknown", QObject::tr("sound")));
    qSpiRoleMapping.insert(QAccessible::Cursor, RoleNames(ATSPI_ROLE_ARROW, "arrow", QObject::tr("cursor")));
    qSpiRoleMapping.insert(QAccessible::Caret, RoleNames(ATSPI_ROLE_UNKNOWN, "unknown", QObject::tr("caret")));
    qSpiRoleMapping.insert(QAccessible::AlertMessage, RoleNames(ATSPI_ROLE_ALERT, "alert", QObject::tr("alert message")));
    qSpiRoleMapping.insert(QAccessible::Window, RoleNames(ATSPI_ROLE_WINDOW, "window", QObject::tr("window")));
    qSpiRoleMapping.insert(QAccessible::Client, RoleNames(ATSPI_ROLE_FILLER, "filler", QObject::tr("filler")));
    qSpiRoleMapping.insert(QAccessible::PopupMenu, RoleNames(ATSPI_ROLE_POPUP_MENU, "popup menu", QObject::tr("popup menu")));
    qSpiRoleMapping.insert(QAccessible::MenuItem, RoleNames(ATSPI_ROLE_MENU_ITEM, "menu item", QObject::tr("menu item")));
    qSpiRoleMapping.insert(QAccessible::ToolTip, RoleNames(ATSPI_ROLE_TOOL_TIP, "tool tip", QObject::tr("tool tip")));
    qSpiRoleMapping.insert(QAccessible::Application, RoleNames(ATSPI_ROLE_APPLICATION, "application", QObject::tr("application")));
    qSpiRoleMapping.insert(QAccessible::Document, RoleNames(ATSPI_ROLE_DOCUMENT_FRAME, "document frame", QObject::tr("document")));
    qSpiRoleMapping.insert(QAccessible::Pane, RoleNames(ATSPI_ROLE_PANEL, "panel", QObject::tr("pane")));
    qSpiRoleMapping.insert(QAccessible::Chart, RoleNames(ATSPI_ROLE_CHART, "chart", QObject::tr("chart")));
    qSpiRoleMapping.insert(QAccessible::Dialog, RoleNames(ATSPI_ROLE_DIALOG, "dialog", QObject::tr("dialog")));
    qSpiRoleMapping.insert(QAccessible::Border, RoleNames(ATSPI_ROLE_FRAME, "frame", QObject::tr("border")));
    qSpiRoleMapping.insert(QAccessible::Grouping, RoleNames(ATSPI_ROLE_PANEL, "panel", QObject::tr("grouping")));
    qSpiRoleMapping.insert(QAccessible::Separator, RoleNames(ATSPI_ROLE_SEPARATOR, "separator", QObject::tr("separator")));
    qSpiRoleMapping.insert(QAccessible::ToolBar, RoleNames(ATSPI_ROLE_TOOL_BAR, "tool bar", QObject::tr("tool bar")));
    qSpiRoleMapping.insert(QAccessible::StatusBar, RoleNames(ATSPI_ROLE_STATUS_BAR, "statusbar", QObject::tr("status bar")));
    qSpiRoleMapping.insert(QAccessible::Table, RoleNames(ATSPI_ROLE_TABLE, "table", QObject::tr("table")));
    qSpiRoleMapping.insert(QAccessible::ColumnHeader, RoleNames(ATSPI_ROLE_TABLE_COLUMN_HEADER, "column header", QObject::tr("column header")));
    qSpiRoleMapping.insert(QAccessible::RowHeader, RoleNames(ATSPI_ROLE_TABLE_ROW_HEADER, "row header", QObject::tr("row header")));
    qSpiRoleMapping.insert(QAccessible::Column, RoleNames(ATSPI_ROLE_TABLE_CELL, "table cell", QObject::tr("column")));
    qSpiRoleMapping.insert(QAccessible::Row, RoleNames(ATSPI_ROLE_TABLE_CELL, "table cell", QObject::tr("row")));
    qSpiRoleMapping.insert(QAccessible::Cell, RoleNames(ATSPI_ROLE_TABLE_CELL, "table cell", QObject::tr("cell")));
    qSpiRoleMapping.insert(QAccessible::Link, RoleNames(ATSPI_ROLE_LINK, "link" , QObject::tr("link")));
    qSpiRoleMapping.insert(QAccessible::HelpBalloon, RoleNames(ATSPI_ROLE_DIALOG, "dialog", QObject::tr("help balloon")));
    qSpiRoleMapping.insert(QAccessible::Assistant, RoleNames(ATSPI_ROLE_DIALOG, "dialog", QObject::tr("assistant")));
    qSpiRoleMapping.insert(QAccessible::List, RoleNames(ATSPI_ROLE_LIST, "list", QObject::tr("list")));
    qSpiRoleMapping.insert(QAccessible::ListItem, RoleNames(ATSPI_ROLE_LIST_ITEM, "list item", QObject::tr("list item")));
    qSpiRoleMapping.insert(QAccessible::Tree, RoleNames(ATSPI_ROLE_TREE, "tree", QObject::tr("tree")));
    qSpiRoleMapping.insert(QAccessible::TreeItem, RoleNames(ATSPI_ROLE_TABLE_CELL, "tree item", QObject::tr("tree item")));
    qSpiRoleMapping.insert(QAccessible::PageTab, RoleNames(ATSPI_ROLE_PAGE_TAB, "page tab", QObject::tr("page tab")));
    qSpiRoleMapping.insert(QAccessible::PropertyPage, RoleNames(ATSPI_ROLE_PAGE_TAB, "page tab", QObject::tr("property page")));
    qSpiRoleMapping.insert(QAccessible::Indicator, RoleNames(ATSPI_ROLE_UNKNOWN, "unknown", QObject::tr("indicator")));
    qSpiRoleMapping.insert(QAccessible::Graphic, RoleNames(ATSPI_ROLE_IMAGE, "image", QObject::tr("graphic")));
    qSpiRoleMapping.insert(QAccessible::StaticText, RoleNames(ATSPI_ROLE_LABEL, "label", QObject::tr("label")));
    qSpiRoleMapping.insert(QAccessible::EditableText, RoleNames(ATSPI_ROLE_TEXT, "text", QObject::tr("text")));
    qSpiRoleMapping.insert(QAccessible::PushButton, RoleNames(ATSPI_ROLE_PUSH_BUTTON, "push button", QObject::tr("push button")));
    qSpiRoleMapping.insert(QAccessible::CheckBox, RoleNames(ATSPI_ROLE_CHECK_BOX, "check box", QObject::tr("check box")));
    qSpiRoleMapping.insert(QAccessible::RadioButton, RoleNames(ATSPI_ROLE_RADIO_BUTTON, "radio button", QObject::tr("radio box")));
    qSpiRoleMapping.insert(QAccessible::ComboBox, RoleNames(ATSPI_ROLE_COMBO_BOX, "combo box", QObject::tr("combo box")));
    qSpiRoleMapping.insert(QAccessible::ProgressBar, RoleNames(ATSPI_ROLE_PROGRESS_BAR, "progress bar", QObject::tr("progress bar")));
    qSpiRoleMapping.insert(QAccessible::Dial, RoleNames(ATSPI_ROLE_DIAL, "accelerator label", QObject::tr("dial")));
    qSpiRoleMapping.insert(QAccessible::HotkeyField, RoleNames(ATSPI_ROLE_TEXT, "text", QObject::tr("hotkey field"))); //FIXME text?
    qSpiRoleMapping.insert(QAccessible::Slider, RoleNames(ATSPI_ROLE_SLIDER, "slider", QObject::tr("slider")));
    qSpiRoleMapping.insert(QAccessible::SpinBox, RoleNames(ATSPI_ROLE_SPIN_BUTTON, "spin button", QObject::tr("spin box")));
    qSpiRoleMapping.insert(QAccessible::Canvas, RoleNames(ATSPI_ROLE_CANVAS, "canvas", QObject::tr("canvas")));
    qSpiRoleMapping.insert(QAccessible::Animation, RoleNames(ATSPI_ROLE_ANIMATION, "animation", QObject::tr("animation")));
    qSpiRoleMapping.insert(QAccessible::Equation, RoleNames(ATSPI_ROLE_TEXT, "text", QObject::tr("equation")));
    qSpiRoleMapping.insert(QAccessible::ButtonDropDown, RoleNames(ATSPI_ROLE_PUSH_BUTTON, "push button", QObject::tr("button drop down")));
    qSpiRoleMapping.insert(QAccessible::ButtonMenu, RoleNames(ATSPI_ROLE_PUSH_BUTTON, "push button", QObject::tr("button menu")));
    qSpiRoleMapping.insert(QAccessible::ButtonDropGrid, RoleNames(ATSPI_ROLE_PUSH_BUTTON, "push button", QObject::tr("button drop grid")));
    qSpiRoleMapping.insert(QAccessible::Whitespace, RoleNames(ATSPI_ROLE_FILLER, "filler", QObject::tr("whitespace")));
    qSpiRoleMapping.insert(QAccessible::PageTabList, RoleNames(ATSPI_ROLE_PAGE_TAB_LIST, "page tab list", QObject::tr("page tab list")));
    qSpiRoleMapping.insert(QAccessible::Clock, RoleNames(ATSPI_ROLE_UNKNOWN, "unknown", QObject::tr("clock")));
    qSpiRoleMapping.insert(QAccessible::Splitter, RoleNames(ATSPI_ROLE_SPLIT_PANE, "split pane", QObject::tr("splitter")));
    qSpiRoleMapping.insert(QAccessible::LayeredPane, RoleNames(ATSPI_ROLE_LAYERED_PANE, "layered pane", QObject::tr("layered pane")));
    qSpiRoleMapping.insert(QAccessible::UserRole, RoleNames(ATSPI_ROLE_UNKNOWN, "unknown", QObject::tr("user role")));
}

quint64 spiStatesFromQState(QAccessible::State state)
{
    quint64 spiState = 0;

    setSpiStateBit(&spiState, ATSPI_STATE_EDITABLE);
    setSpiStateBit(&spiState, ATSPI_STATE_ENABLED);
    setSpiStateBit(&spiState, ATSPI_STATE_SHOWING);
    setSpiStateBit(&spiState, ATSPI_STATE_VISIBLE);
    setSpiStateBit(&spiState, ATSPI_STATE_SENSITIVE);

    for (quint32 mask = 1; mask <= 0x10000000u; mask <<= 1)
    {
        switch (state & mask)
        {
        case QAccessible::Unavailable:
        {
            unsetSpiStateBit(&spiState, ATSPI_STATE_ENABLED);
            unsetSpiStateBit(&spiState, ATSPI_STATE_SHOWING);
            unsetSpiStateBit(&spiState, ATSPI_STATE_VISIBLE);
            unsetSpiStateBit(&spiState, ATSPI_STATE_SENSITIVE);
            break;
        }
        case QAccessible::Selected:
        {
            setSpiStateBit(&spiState, ATSPI_STATE_SELECTED);
            break;
        }
        case QAccessible::Focused:
        {
            setSpiStateBit(&spiState, ATSPI_STATE_FOCUSED);
            break;
        }
        case QAccessible::Pressed:
        {
            setSpiStateBit(&spiState, ATSPI_STATE_PRESSED);
            break;
        }
        case QAccessible::Checked:
        {
            setSpiStateBit(&spiState, ATSPI_STATE_CHECKED);
            break;
        }
        case QAccessible::Mixed:
        {
            setSpiStateBit(&spiState, ATSPI_STATE_INDETERMINATE);
            break;
        }
        case QAccessible::ReadOnly:
        {
            unsetSpiStateBit(&spiState, ATSPI_STATE_EDITABLE);
            break;
        }
        case QAccessible::HotTracked:
        {
            break;
        }
        case QAccessible::DefaultButton:
        {
            setSpiStateBit(&spiState, ATSPI_STATE_IS_DEFAULT);
            break;
        }
        case QAccessible::Expanded:
        {
            setSpiStateBit(&spiState, ATSPI_STATE_EXPANDED);
            break;
        }
        case QAccessible::Collapsed:
        {
            setSpiStateBit(&spiState, ATSPI_STATE_COLLAPSED);
            break;
        }
        case QAccessible::Busy:
        {
            setSpiStateBit(&spiState, ATSPI_STATE_BUSY);
            break;
        }
        case QAccessible::Marqueed:
        case QAccessible::Animated:
        {
            setSpiStateBit(&spiState, ATSPI_STATE_ANIMATED);
            break;
        }
        case QAccessible::Invisible:
        case QAccessible::Offscreen:
        {
            unsetSpiStateBit(&spiState, ATSPI_STATE_SHOWING);
            break;
        }
        case QAccessible::Sizeable:
        {
            setSpiStateBit(&spiState, ATSPI_STATE_RESIZABLE);
            break;
        }
        case QAccessible::Movable:
        case QAccessible::SelfVoicing:
        {
            break;
        }
        case QAccessible::Focusable:
        {
            setSpiStateBit(&spiState, ATSPI_STATE_FOCUSABLE);
            break;
        }
        case QAccessible::Selectable:
        {
            setSpiStateBit(&spiState, ATSPI_STATE_SELECTABLE);
            break;
        }
        case QAccessible::Linked:
        {
            break;
        }
        case QAccessible::Traversed:
        {
            setSpiStateBit(&spiState, ATSPI_STATE_VISITED);
            break;
        }
        case QAccessible::MultiSelectable:
        {
            setSpiStateBit(&spiState, ATSPI_STATE_MULTISELECTABLE);
            break;
        }
        case QAccessible::ExtSelectable:
        {
            setSpiStateBit(&spiState, ATSPI_STATE_SELECTABLE);
            break;
        }
        case QAccessible::Protected:
        case QAccessible::HasPopup:
        {
            break;
        }
        case QAccessible::Modal:
        {
            setSpiStateBit(&spiState, ATSPI_STATE_MODAL);
            break;
        }
//        case QAccessible::SingleLine:
//        {
//            setSpiStateBit(&spiState, ATSPI_STATE_SINGLE_LINE);
//            break;
//        }
        default:
        {
            break;
        }
        }
    }
    return spiState;
}

QSpiUIntList spiStateSetFromSpiStates(quint64 states)
{
    uint low = states & 0xFFFFFFFF;
    uint high = (states >> 32) & 0xFFFFFFFF;

    QSpiUIntList stateList;
    stateList.append(low);
    stateList.append(high);
    return stateList;
}

void qSpiInitializeConstantMappings()
{
    initializeRoleMapping();
}
<|endoftext|>


<commit_before>#include "to_test_cbna_forum.hpp"
#include <fstream>

using JSON = nlohmann::json;

static bool mkdir(const std::string& directory)
{
    std::cout << ("mkdir -p " + directory) << std::endl;
    return std::system(("mkdir -p " + directory).c_str());
}

void to_test_cbna_forum(const RawForum& forum, const std::string& directory)
{
    std::map<std::string,int> category_id_map;

    //-------------
    // Topics
    //-------------
    for(auto& topic_pair : forum.topics)
    {
        auto& topic_id = topic_pair.first;
        auto& topic    = topic_pair.second;

        int& category_id = category_id_map[topic.path[2]];
        bool create_category = false;
        if (!category_id)
        {
            category_id = category_id_map.size();
            create_category = true;
        }

        std::string category_directory =
            directory + "/message_board/contents/categories/" +
            std::to_string(category_id);
        std::string topic_directory = category_directory +
             "/topics/" + std::to_string(topic_id);
        mkdir(topic_directory);

        //---------------------
        // Making new category
        //---------------------
        if (create_category)
        {
            JSON json;
            json["description"] = "";
            json["name"] = topic.path[2];
            std::ofstream(category_directory+"/info") << std::setw(4) << json;
        }

        //-------------
        // Topic info
        //-------------
        std::ofstream file(topic_directory + "/info");
        JSON json;
        json["creation_date"] = "2016-01-01T00:00:00";
        json["draft"] = false;
        json["locked"] = false;
        json["title"] = topic.title;
        file << std::setw(4) << json;

        //-------------
        // Messages
        //-------------
        mkdir(topic_directory+"/messages/");
        int message_id = 0;
        for(auto& message : topic.messages)
        {
            JSON json;
            json["authors"].push_back(0);
            json["date"] = "2016-01-01T00:00:00";
            //json["date"] = message.date.to_string();
            json["text"] = message.content;
            std::ofstream file(topic_directory +
                "/messages/" + std::to_string(message_id++));
            file << std::setw(4) << json;
        }
    }
}
<commit_msg>Transform the CBNA exporter to fit the last update format.<commit_after>#include "to_test_cbna_forum.hpp"
#include <fstream>

using JSON = nlohmann::json;

static bool mkdir(const std::string& directory)
{
    std::cout << ("mkdir -p " + directory) << std::endl;
    return std::system(("mkdir -p " + directory).c_str());
}

void to_test_cbna_forum(const RawForum& forum, const std::string& directory)
{
    //-------------
    // Topics
    //-------------
    for(auto& topic_pair : forum.topics)
    {
        auto& topic_id = topic_pair.first;
        auto& topic    = topic_pair.second;

        std::string topic_directory =
            directory + "/message_board/contents" +
             "/topics/" + std::to_string(topic_id);
        mkdir(topic_directory);

        //-------------
        // Topic info
        //-------------
        std::ofstream file(topic_directory + "/info");
        JSON json;
        json["creation_date"] = "2016-01-01T00:00:00";
        json["draft"] = false;
        json["locked"] = false;
        json["title"] = topic.title;
        file << std::setw(4) << json;

        //-------------
        // Messages
        //-------------
        mkdir(topic_directory+"/messages/");
        int message_id = 0;
        for(auto& message : topic.messages)
        {
            JSON json;
            json["authors"].push_back(0);
            json["date"] = "2016-01-01T00:00:00";
            json["content"] = message.content;
            std::ofstream file(topic_directory +
                "/messages/" + std::to_string(message_id++));
            file << std::setw(4) << json;
        }
    }
}
<|endoftext|>