static Plane_3 convert(const CGAL::Plane_3<R>& p){return Converter()(p);}


  void print_point_impl(std::ostream& out, const CGAL::Point_3<R> p, Null_functor)
  {
    out << p.x() << " " << p.y() << " " << p.z() << " " << 1;
  }


void SNC_io_parser<EW>::add_infi_box() {

  for(i=0; i<8; ++i)  Vertex_of.push_back(this->sncp()->new_vertex_only());
  for(i=0; i<24; ++i)  Edge_of.push_back(this->sncp()->new_halfedge_only());
  for(i=0; i<12; ++i)  Halffacet_of.push_back(this->sncp()->new_halffacet_only());
  for(i=0; i<48; ++i) SEdge_of.push_back(this->sncp()->new_shalfedge_only());
  for(i=0; i<16; ++i) SFace_of.push_back(this->sncp()->new_sface_only());

  typename Standard_kernel::RT hx,hy,hz,hw;
  for(int i=0; i<8; ++i) {
    Vertex_handle vh = Vertex_of[vn+i];
    vh->svertices_begin() = Edge_of[en+3*i];
    vh->svertices_last()  = Edge_of[en+3*i+2];
    vh->shalfedges_begin() = SEdge_of[sen+6*i];
    vh->shalfedges_last()  = SEdge_of[sen+6*i+5];
    vh->sfaces_begin() = SFace_of[sfn+2*i];
    vh->sfaces_last()  = SFace_of[sfn+2*i+1];
    vh->shalfloop() = this->shalfloops_end();
    hx = i % 2 ? -1 : 1;
    hy = i % 4 > 1 ? -1 : 1;
    hz = i > 3 ? -1 : 1;
    vh->point() = Infi_box::create_extended_point(hx, hy, hz);
    vh->mark() = 1;
    vh->sncp() = this->sncp();
  }

  int seOff[3] = {0, 1, 3};
  int twinIdx[24] = { 3, 7,14,
                      0,10,17,
                      9, 1,20,
                      6, 4,23,
                      15,19, 2,
                      12,22, 5,
                      21,13, 8,
                      18,16,11};

  for(int i = 0; i < 24; ++i) {
    Halfedge_handle eh = Edge_of[en+i];
    eh->twin() = Edge_of[en+twinIdx[i]];
    eh->center_vertex() = Vertex_of[vn+(i/3)];
    eh->out_sedge() = SEdge_of[sen+(i/3*6)+seOff[i%3]];
    switch(i%3) {
    case 0 :
      hx = i % 6 ? 1 : -1;
      hy = hz = 0;
      break;
    case 1:
      hy = i % 12 >= 6 ? 1 : -1;
      hx = hz = 0;
      break;
    case 2:
      hz = i >= 12 ? 1 : -1;
      hx = hy = 0;
      break;
    }
    eh->point() = Sphere_point(hx,hy,hz);
    eh->mark() = 1;
  }

  int bnd[12] = {19, 18, 43, 42, 35, 34,
                 47, 46, 39, 38, 45, 44};
  for(int i = 0; i < 12; ++i) {
    Halffacet_handle fh = Halffacet_of[fn+i];
    fh->twin() = Halffacet_of[fn+(i/2*2)+((i+1)%2)];
    fh->boundary_entry_objects().push_back(make_object(SEdge_of[sen+bnd[i]]));
    fh->incident_volume() = Volume_of[((i%4) == 1 || (i%4 == 2)) ? 1 : 0];
    if(i<4) {
      hz = i % 2 ? -1 : 1;
      hx = hy = 0;
    }
    else if(i<8) {
      hy = i % 2 ? -1 : 1;
      hx = hz = 0;
    }
    else {
      hx = i % 2 ? -1 : 1;
      hz = hy = 0;
    }
    hw = ((i%4) == 1 || (i%4) == 2) ? 1 : -1;
    fh->plane() = Infi_box::create_extended_plane(hx,hy,hz,hw);
    fh->mark() = 1;
  }

  Volume_of[0]->shell_entry_objects().push_back(make_object(SFace_of[sfn]));
  Volume_of[0]->mark() = 0;
  Volume_of[1]->shell_entry_objects().push_front(make_object(SFace_of[sfn+1]));

  int sprevOff[6] = {4,3,0,5,2,1};
  int snextOff[6] = {2,5,4,1,0,3};
  int prevIdx[48] = {7,12,15,26,29,10,
                     1,18,21,32,35,4,
                     19,0,3,38,41,22,
                     13,6,9,44,47,16,
                     31,36,39,2,5,34,
                     25,42,45,8,11,28,
                     43,24,27,14,17,46,
                     37,30,33,20,23,40};
  int nextIdx[48] = {13,6,27,14,11,28,
                     19,0,33,20,5,34,
                     1,18,39,2,23,40,
                     7,12,45,8,17,46,
                     37,30,3,38,35,4,
                     43,24,9,44,29,10,
                     25,42,15,26,47,16,
                     31,36,21,32,41,22};
  int factIdx[48] = {1,0,9,8,5,4,
                     0,1,11,10,4,5,
                     0,1,8,9,7,6,
                     1,0,10,11,6,7,
                     3,2,8,9,4,5,
                     2,3,10,11,5,4,
                     2,3,9,8,6,7,
                     3,2,11,10,7,6};
  int sgn[24] = {1,1,1,-1,1,-1,
                 -1,-1,1,1,-1,-1,
                 1,-1,-1,-1,-1,1,
                 -1,1,-1,1,1,1};

  for(int i = 0; i < 48; ++i) {
    SHalfedge_handle seh = SEdge_of[sen+i];

    seh->twin() = SEdge_of[sen+(i/2*2)+((i+1)%2)];
    seh->sprev() = SEdge_of[sen+sprevOff[i%6]+(i/6*6)];
    seh->snext() = SEdge_of[sen+snextOff[i%6]+(i/6*6)];
    seh->source() = Edge_of[en+((i+1)%6)/2+(i/6)*3];
    seh->incident_sface() = SFace_of[sfn+(i%2)+(i/6)*2];
    seh->prev() = SEdge_of[sen+prevIdx[i]];
    seh->next() = SEdge_of[sen+nextIdx[i]];
    seh->facet() = Halffacet_of[fn+factIdx[i]];
    if(i%6 < 2) {
      hz = (i%2) ? sgn[i/2] * (-1) : sgn[i/2];
      hx = hy = 0;
    }
    else if(i%6 < 4) {
      hx = (i%2) ? sgn[i/2] * (-1) : sgn[i/2];
      hz = hy = 0;
    }
    else {
      hy = (i%2) ? sgn[i/2] * (-1) : sgn[i/2];
      hx = hz = 0;
    }
    seh->circle() = Sphere_circle(Plane_3(RT(hx),RT(hy),RT(hz),RT(0)));
    seh->mark() = 1;
  }

  int volIdx[8] = {0,1,1,0,1,0,0,1};

  for(int i = 0; i < 16; ++i) {
    SFace_handle sfh = SFace_of[sfn+i];
    sfh->center_vertex() = Vertex_of[vn+(i/2)];
    sfh->boundary_entry_objects().push_back(make_object(SEdge_of[sen+(i/2*6)+(i%2)]));
    this->sncp()->store_sm_boundary_item(SEdge_of[sen+(i/2*6)+(i%2)],
                                          --(sfh->sface_cycles_end()));
    int cIdx = i%2 ? 1-volIdx[i/2] : volIdx[i/2];
    sfh->volume() = Volume_of[cIdx];
    sfh->mark() = cIdx ? Volume_of[1]->mark() : 0;
  }
}


static Vector_3 convert(const CGAL::Vector_3<R>& v){return v.exact();}


void SNC_io_parser<EW>::read()
{
  if ( !check_sep("Selective Nef Complex") )
    CGAL_error_msg("SNC_io_parser::read: no SNC header.");
  std::string kernel_type;
  in >> kernel_type;
  CGAL_assertion(kernel_type == "standard" || kernel_type == "extended");
  if ( !(check_sep("vertices") && (in >> vn)) )
    CGAL_error_msg("SNC_io_parser::read: wrong vertex line.");
  if ( !(check_sep("halfedges") && (in >> en) && (en%2==0)) )
    CGAL_error_msg("SNC_io_parser::read: wrong edge line.");
  if ( !(check_sep("facets") && (in >> fn) && (fn%2==0)) )
    CGAL_error_msg("SNC_io_parser::read: wrong facet line.");
  if ( !(check_sep("volumes") && (in >> cn)) )
    CGAL_error_msg("SNC_io_parser::read: wrong volume line.");
  if ( !(check_sep("shalfedges") && (in >> sen)) )
    CGAL_error_msg("SNC_io_parser::read: wrong sedge line.");
  if ( !(check_sep("shalfloops") && (in >> sln)) )
    CGAL_error_msg("SNC_io_parser::read: wrong sloop line.");
  if ( !(check_sep("sfaces") && (in >> sfn)) )
    CGAL_error_msg("SNC_io_parser::read: wrong sface line.");

  addInfiBox = (kernel_type == "standard" && Infi_box::extended_kernel());

  for(i=0; i<vn; ++i)  Vertex_of.push_back(this->sncp()->new_vertex_only());
  for(i=0; i<en; ++i)  Edge_of.push_back(this->sncp()->new_halfedge_only());
  for(i=0; i<fn; ++i)  Halffacet_of.push_back(this->sncp()->new_halffacet_only());
  for(i=0; i<cn; ++i)  Volume_of.push_back(this->sncp()->new_volume_only());
  for(i=0; i<sen; ++i) SEdge_of.push_back(this->sncp()->new_shalfedge_only());
  for(i=0; i<sln; ++i) SLoop_of.push_back(this->sncp()->new_shalfloop_only());
  for(i=0; i<sfn; ++i) SFace_of.push_back(this->sncp()->new_sface_only());

  if(addInfiBox) {
    Volume_of.push_back(this->sncp()->new_volume_only());
    read_items<Standard_kernel>(1);
    add_infi_box();
  } else
    read_items<Kernel>(0);
}


TEST_P(Security, BuiltinAuthenticationAndAccessAndCryptoPlugin_PermissionsDisableDiscoveryEnableAccessEncrypt_validation_ok_disable_discovery_disable_access_encrypt)
// *INDENT-ON*
{
    PubSubReader<HelloWorldType> reader(TEST_TOPIC_NAME);
    PubSubWriter<HelloWorldType> writer(TEST_TOPIC_NAME);
    std::string governance_file("governance_disable_discovery_enable_access_encrypt.smime");

    BuiltinAuthenticationAndAccessAndCryptoPlugin_Permissions_validation_ok_common(reader, writer, governance_file);
}


    bool disable_positive_acks() const
    {
        return false;
    }


    void persistence_guid(
            const GUID_t& /*guid*/)
    {
    }


bool DataWriterImpl::create_new_change_with_params(
        ChangeKind_t changeKind,
        void* data,
        WriteParams& wparams,
        const fastrtps::rtps::InstanceHandle_t& handle)
{
    if (!check_new_change_preconditions(changeKind, data))
    {
        return false;
    }

    return perform_create_new_change(changeKind, data, wparams, handle);
}


    WriterProxyData(
            size_t max_unicast_locators,
            size_t max_multicast_locators)
        : remote_locators_(max_unicast_locators, max_multicast_locators)
        , topic_kind_(NO_KEY)
        , is_alive_(true)
        , type_id_()
        , type_()
        , type_info_()
        , m_typeMaxSerialized(0)
        , m_userDefinedId(0)
    {
        m_qos.m_userData.set_max_size(0);
        m_qos.m_partition.set_max_size(0);
    }


ReturnCode_t DataReaderImpl::set_qos(
        const DataReaderQos& qos)
{
    bool enabled = reader_ != nullptr;
    const DataReaderQos& qos_to_set = (&qos == &DATAREADER_QOS_DEFAULT) ?
            subscriber_->get_default_datareader_qos() : qos;

    // Default qos is always considered consistent
    if (&qos != &DATAREADER_QOS_DEFAULT)
    {
        if (subscriber_->get_participant()->get_qos().allocation().data_limits.max_user_data != 0 &&
                subscriber_->get_participant()->get_qos().allocation().data_limits.max_user_data <
                qos_to_set.user_data().getValue().size())
        {
            return ReturnCode_t::RETCODE_INCONSISTENT_POLICY;
        }

        ReturnCode_t check_result = check_qos(qos_to_set);
        if (!check_result)
        {
            return check_result;
        }
    }

    if (enabled && !can_qos_be_updated(qos_, qos_to_set))
    {
        return ReturnCode_t::RETCODE_IMMUTABLE_POLICY;
    }

    set_qos(qos_, qos_to_set, !enabled);

    if (enabled)
    {
        //NOTIFY THE BUILTIN PROTOCOLS THAT THE READER HAS CHANGED
        ReaderQos rqos = qos.get_readerqos(get_subscriber()->get_qos());
        subscriber_->rtps_participant()->updateReader(reader_, topic_attributes(), rqos);

        // Deadline
        if (qos_.deadline().period != c_TimeInfinite)
        {
            deadline_duration_us_ = duration<double, std::ratio<1, 1000000>>(qos_.deadline().period.to_ns() * 1e-3);
            deadline_timer_->update_interval_millisec(qos_.deadline().period.to_ns() * 1e-6);
        }
        else
        {
            deadline_timer_->cancel_timer();
        }

        // Lifespan
        if (qos_.lifespan().duration != c_TimeInfinite)
        {
            lifespan_duration_us_ =
                    std::chrono::duration<double, std::ratio<1, 1000000>>(qos_.lifespan().duration.to_ns() * 1e-3);
            lifespan_timer_->update_interval_millisec(qos_.lifespan().duration.to_ns() * 1e-6);
        }
        else
        {
            lifespan_timer_->cancel_timer();
        }
    }

    return ReturnCode_t::RETCODE_OK;
}


void DataWriterImpl::InnerDataWriterListener::on_liveliness_lost(
        fastrtps::rtps::RTPSWriter* /*writer*/,
        const fastrtps::LivelinessLostStatus& status)
{
    DataWriterListener* listener = data_writer_->get_listener_for(StatusMask::liveliness_lost());
    if (listener != nullptr)
    {
        listener->on_liveliness_lost(
            data_writer_->user_datawriter_, status);
    }
}


bool Permissions::return_permissions_handle(
        PermissionsHandle* permissions_handle,
        SecurityException&)
{
    AccessPermissionsHandle* handle = &AccessPermissionsHandle::narrow(*permissions_handle);

    if (!handle->nil())
    {
        delete handle;
        return true;
    }

    return false;
}


TEST_P(Security, BuiltinAuthenticationAndAccessAndCryptoPlugin_PermissionsDisableDiscoveryDisableAccessEncrypt_validation_ok_enable_discovery_disable_access_encrypt)
// *INDENT-ON*
{
    PubSubReader<HelloWorldType> reader(TEST_TOPIC_NAME);
    PubSubWriter<HelloWorldType> writer(TEST_TOPIC_NAME);
    std::string governance_file("governance_disable_discovery_disable_access_encrypt.smime");

    BuiltinAuthenticationAndAccessAndCryptoPlugin_Permissions_validation_ok_common(reader, writer, governance_file);
}


    const string_255& topicName() const
    {
        return topic_name_;
    }


bool StringMatching::matchString(
        const char* str1,
        const char* str2)
{
    if (PathMatchSpec(str1, str2))
    {
        return true;
    }
    if (PathMatchSpec(str2, str1))
    {
        return true;
    }
    return false;
}


static void process_protection_kind(
        const ProtectionKind kind,
        bool& protected_flag,
        bool& encrypted_flag,
        bool& orig_auth_flag)
{
    protected_flag = kind != ProtectionKind::NONE;
    encrypted_flag = (kind == ProtectionKind::ENCRYPT) || (kind == ProtectionKind::ENCRYPT_WITH_ORIGIN_AUTHENTICATION);
    orig_auth_flag = (kind == ProtectionKind::ENCRYPT_WITH_ORIGIN_AUTHENTICATION) ||
            (kind == ProtectionKind::SIGN_WITH_ORIGIN_AUTHENTICATION);
}


void DataReaderImpl::disable()
{
    set_listener(nullptr);
    if (reader_ != nullptr)
    {
        reader_->setListener(nullptr);
    }
}


bool Permissions::check_remote_datawriter(
        const PermissionsHandle& remote_handle,
        const uint32_t domain_id,
        const WriterProxyData& publication_data,
        SecurityException& exception)
{
    bool returned_value = false;
    const AccessPermissionsHandle& rah = AccessPermissionsHandle::narrow(remote_handle);
    const char* topic_name = publication_data.topicName().c_str();

    if (rah.nil())
    {
        exception = _SecurityException_("Bad precondition");
        EMERGENCY_SECURITY_LOGGING("Permissions", exception.what());
        return false;
    }

    const EndpointSecurityAttributes* attributes = nullptr;

    if ((attributes = is_topic_in_sec_attributes(topic_name, rah->governance_topic_rules_))
            != nullptr)
    {
        if (!attributes->is_write_protected)
        {
            return true;
        }
    }
    else
    {
        exception = _SecurityException_(
            "Not found topic access rule for topic " + publication_data.topicName().to_string());
        EMERGENCY_SECURITY_LOGGING("Permissions", exception.what());
        return false;
    }

    for (auto rule : rah->grant.rules)
    {
        if (is_domain_in_set(domain_id, rule.domains))
        {
            if (is_topic_in_criterias(topic_name, rule.publishes))
            {
                returned_value = check_rule(topic_name, rule, publication_data.m_qos.m_partition.getNames(),
                                rule.publishes, exception);
                break;
            }
        }
    }

    if (!returned_value)
    {
        if (strlen(exception.what()) == 0)
        {
            exception = _SecurityException_(topic_name + std::string(" topic not found in allow rule."));
        }
        EMERGENCY_SECURITY_LOGGING("Permissions", exception.what());
    }

    return returned_value;
}


TEST_P(Security, BuiltinAuthenticationAndCryptoPlugin_besteffort_payload_data300kb)
{
    PubSubReader<Data1mbType> reader(TEST_TOPIC_NAME);
    PubSubWriter<Data1mbType> writer(TEST_TOPIC_NAME);

    PropertyPolicy pub_part_property_policy, sub_part_property_policy,
            pub_property_policy, sub_property_policy;

    sub_part_property_policy.properties().emplace_back(Property("dds.sec.auth.plugin",
            "builtin.PKI-DH"));
    sub_part_property_policy.properties().emplace_back(Property("dds.sec.auth.builtin.PKI-DH.identity_ca",
            "file://" + std::string(certs_path) + "/maincacert.pem"));
    sub_part_property_policy.properties().emplace_back(Property("dds.sec.auth.builtin.PKI-DH.identity_certificate",
            "file://" + std::string(certs_path) + "/mainsubcert.pem"));
    sub_part_property_policy.properties().emplace_back(Property("dds.sec.auth.builtin.PKI-DH.private_key",
            "file://" + std::string(certs_path) + "/mainsubkey.pem"));
    sub_part_property_policy.properties().emplace_back(Property("dds.sec.crypto.plugin",
            "builtin.AES-GCM-GMAC"));
    sub_property_policy.properties().emplace_back("rtps.endpoint.payload_protection_kind", "ENCRYPT");

    reader.history_depth(5).
            property_policy(sub_part_property_policy).
            entity_property_policy(sub_property_policy).init();

    ASSERT_TRUE(reader.isInitialized());

    pub_part_property_policy.properties().emplace_back(Property("dds.sec.auth.plugin",
            "builtin.PKI-DH"));
    pub_part_property_policy.properties().emplace_back(Property("dds.sec.auth.builtin.PKI-DH.identity_ca",
            "file://" + std::string(certs_path) + "/maincacert.pem"));
    pub_part_property_policy.properties().emplace_back(Property("dds.sec.auth.builtin.PKI-DH.identity_certificate",
            "file://" + std::string(certs_path) + "/mainpubcert.pem"));
    pub_part_property_policy.properties().emplace_back(Property("dds.sec.auth.builtin.PKI-DH.private_key",
            "file://" + std::string(certs_path) + "/mainpubkey.pem"));
    pub_part_property_policy.properties().emplace_back(Property("dds.sec.crypto.plugin",
            "builtin.AES-GCM-GMAC"));
    pub_property_policy.properties().emplace_back("rtps.endpoint.payload_protection_kind", "ENCRYPT");

    // When doing fragmentation, it is necessary to have some degree of
    // flow control not to overrun the receive buffer.
    uint32_t bytesPerPeriod = 65536;
    uint32_t periodInMs = 500;

    writer.history_depth(5).
            reliability(eprosima::fastrtps::BEST_EFFORT_RELIABILITY_QOS).
            asynchronously(eprosima::fastrtps::ASYNCHRONOUS_PUBLISH_MODE).
            add_throughput_controller_descriptor_to_pparams(bytesPerPeriod, periodInMs).
            property_policy(pub_part_property_policy).
            entity_property_policy(pub_property_policy).init();

    ASSERT_TRUE(writer.isInitialized());

    // Wait for authorization
    reader.waitAuthorized();
    writer.waitAuthorized();

    // Wait for discovery.
    writer.wait_discovery();
    reader.wait_discovery();

    auto data = default_data300kb_data_generator(5);

    reader.startReception(data);

    // Send data
    writer.send(data);
    // In this test all data should be sent.
    ASSERT_TRUE(data.empty());
    // Block reader until reception finished or timeout.
    reader.block_for_at_least(2);
}


TEST_P(Security, BuiltinAuthenticationAndCryptoPlugin_reliable_all_ok)
{
    PubSubReader<HelloWorldType> reader(TEST_TOPIC_NAME);
    PubSubWriter<HelloWorldType> writer(TEST_TOPIC_NAME);

    PropertyPolicy pub_part_property_policy, sub_part_property_policy,
            pub_property_policy, sub_property_policy;

    sub_part_property_policy.properties().emplace_back(Property("dds.sec.auth.plugin",
            "builtin.PKI-DH"));
    sub_part_property_policy.properties().emplace_back(Property("dds.sec.auth.builtin.PKI-DH.identity_ca",
            "file://" + std::string(certs_path) + "/maincacert.pem"));
    sub_part_property_policy.properties().emplace_back(Property("dds.sec.auth.builtin.PKI-DH.identity_certificate",
            "file://" + std::string(certs_path) + "/mainsubcert.pem"));
    sub_part_property_policy.properties().emplace_back(Property("dds.sec.auth.builtin.PKI-DH.private_key",
            "file://" + std::string(certs_path) + "/mainsubkey.pem"));
    sub_part_property_policy.properties().emplace_back(Property("dds.sec.crypto.plugin",
            "builtin.AES-GCM-GMAC"));
    sub_part_property_policy.properties().emplace_back("rtps.participant.rtps_protection_kind", "ENCRYPT");
    sub_property_policy.properties().emplace_back("rtps.endpoint.submessage_protection_kind", "ENCRYPT");
    sub_property_policy.properties().emplace_back("rtps.endpoint.payload_protection_kind", "ENCRYPT");

    reader.history_depth(10).
            reliability(eprosima::fastrtps::RELIABLE_RELIABILITY_QOS).
            property_policy(sub_part_property_policy).
            entity_property_policy(sub_property_policy).init();

    ASSERT_TRUE(reader.isInitialized());

    pub_part_property_policy.properties().emplace_back(Property("dds.sec.auth.plugin",
            "builtin.PKI-DH"));
    pub_part_property_policy.properties().emplace_back(Property("dds.sec.auth.builtin.PKI-DH.identity_ca",
            "file://" + std::string(certs_path) + "/maincacert.pem"));
    pub_part_property_policy.properties().emplace_back(Property("dds.sec.auth.builtin.PKI-DH.identity_certificate",
            "file://" + std::string(certs_path) + "/mainpubcert.pem"));
    pub_part_property_policy.properties().emplace_back(Property("dds.sec.auth.builtin.PKI-DH.private_key",
            "file://" + std::string(certs_path) + "/mainpubkey.pem"));
    pub_part_property_policy.properties().emplace_back(Property("dds.sec.crypto.plugin",
            "builtin.AES-GCM-GMAC"));
    pub_part_property_policy.properties().emplace_back("rtps.participant.rtps_protection_kind", "ENCRYPT");
    pub_property_policy.properties().emplace_back("rtps.endpoint.submessage_protection_kind", "ENCRYPT");
    pub_property_policy.properties().emplace_back("rtps.endpoint.payload_protection_kind", "ENCRYPT");

    writer.history_depth(10).
            property_policy(pub_part_property_policy).
            entity_property_policy(pub_property_policy).init();

    ASSERT_TRUE(writer.isInitialized());

    // Wait for authorization
    reader.waitAuthorized();
    writer.waitAuthorized();

    // Wait for discovery.
    writer.wait_discovery();
    reader.wait_discovery();

    auto data = default_helloworld_data_generator();

    reader.startReception(data);

    // Send data
    writer.send(data);
    // In this test all data should be sent.
    ASSERT_TRUE(data.empty());
    // Block reader until reception finished or timeout.
    reader.block_for_all();
}


static bool is_domain_in_set(
        const uint32_t domain_id,
        const Domains& domains)
{
    bool returned_value = false;

    for (auto range : domains.ranges)
    {
        if (range.second == 0)
        {
            if (domain_id == range.first)
            {
                returned_value = true;
                break;
            }
        }
        else
        {
            if (domain_id >= range.first &&
                    domain_id <= range.second)
            {
                returned_value = true;
                break;
            }
        }
    }

    return returned_value;
}


bool DataReaderImpl::deadline_timer_reschedule()
{
    assert(qos_.deadline().period != c_TimeInfinite);

    std::unique_lock<RecursiveTimedMutex> lock(reader_->getMutex());

    steady_clock::time_point next_deadline_us;
    if (!history_.get_next_deadline(timer_owner_, next_deadline_us))
    {
        logError(SUBSCRIBER, "Could not get the next deadline from the history");
        return false;
    }
    auto interval_ms = duration_cast<milliseconds>(next_deadline_us - steady_clock::now());

    deadline_timer_->update_interval_millisec(static_cast<double>(interval_ms.count()));
    return true;
}


OfferedIncompatibleQosStatus& DataWriterImpl::update_offered_incompatible_qos(
        PolicyMask incompatible_policies)
{
    ++offered_incompatible_qos_status_.total_count;
    ++offered_incompatible_qos_status_.total_count_change;
    for (uint32_t id = 1; id < NEXT_QOS_POLICY_ID; ++id)
    {
        if (incompatible_policies.test(id))
        {
            ++offered_incompatible_qos_status_.policies[static_cast<QosPolicyId_t>(id)].count;
            offered_incompatible_qos_status_.last_policy_id = static_cast<QosPolicyId_t>(id);
        }
    }
    return offered_incompatible_qos_status_;
}


void ParticipantImpl::assert_liveliness()
{
    if (mp_rtpsParticipant->wlp() != nullptr)
    {
        mp_rtpsParticipant->wlp()->assert_liveliness_manual_by_participant();
    }
    else
    {
        logError(PARTICIPANT, "Invalid WLP, cannot assert liveliness of participant");
    }
}


    uint16_t& userDefinedId()
    {
        return m_userDefinedId;
    }


static X509_STORE* load_permissions_ca(
        const std::string& permissions_ca,
        bool& there_are_crls,
        std::string& ca_sn,
        std::string& ca_algo,
        SecurityException& exception)
{
    X509_STORE* store = X509_STORE_new();

    if (store != nullptr)
    {
        if (permissions_ca.size() >= 7 && permissions_ca.compare(0, 7, "file://") == 0)
        {
            BIO* in = BIO_new(BIO_s_file());

            if (in != nullptr)
            {
                if (BIO_read_filename(in, permissions_ca.substr(7).c_str()) > 0)
                {
                    STACK_OF(X509_INFO) * inf = PEM_X509_INFO_read_bio(in, NULL, NULL, NULL);

                    if (inf != nullptr)
                    {
                        int i, count = 0;
                        there_are_crls = false;

                        for (i = 0; i < sk_X509_INFO_num(inf); i++)
                        {
                            X509_INFO* itmp = sk_X509_INFO_value(inf, i);

                            if (itmp->x509)
                            {
                                // Retrieve subject name for future use.
                                if (ca_sn.empty())
                                {
                                    X509_NAME* ca_subject_name = X509_get_subject_name(itmp->x509);
                                    assert(ca_subject_name != nullptr);
                                    char* ca_subject_name_str = X509_NAME_oneline(ca_subject_name, 0, 0);
                                    assert(ca_subject_name_str != nullptr);
                                    ca_sn = ca_subject_name_str;
                                    OPENSSL_free(ca_subject_name_str);
                                }

                                // Retrieve signature algorithm
                                if (ca_algo.empty())
                                {
                                    if (get_signature_algorithm(itmp->x509, ca_algo, exception))
                                    {
                                        X509_STORE_add_cert(store, itmp->x509);
                                        count++;
                                    }
                                }
                                else
                                {
                                    X509_STORE_add_cert(store, itmp->x509);
                                    count++;
                                }
                            }
                            if (itmp->crl)
                            {
                                X509_STORE_add_crl(store, itmp->crl);
                                there_are_crls = true;
                            }
                        }

                        sk_X509_INFO_pop_free(inf, X509_INFO_free);

                        if (count > 0)
                        {
                            BIO_free(in);

                            return store;
                        }
                    }
                    else
                    {
                        exception = _SecurityException_(std::string(
                                            "OpenSSL library cannot read X509 info in file ") +
                                        permissions_ca.substr(7));
                    }
                }
                else
                {
                    exception = _SecurityException_(std::string(
                                        "OpenSSL library cannot read file ") + permissions_ca.substr(7));
                }

                BIO_free(in);
            }
            else
            {
                exception = _SecurityException_("OpenSSL library cannot allocate file");
            }
        }
        else
        {
            exception = _SecurityException_("Unsupported permissions_ca format");
        }

        X509_STORE_free(store);
    }
    else
    {
        exception = _SecurityException_("Creation of X509 storage");
    }

    return nullptr;
}


    bool readFromCDRMessage(
            CDRMessage_t* /*msg*/,
            const NetworkFactory& /*network*/,
            bool /*is_shm_transport_possible*/)
    {
        return true;
    }


    void key(
            InstanceHandle_t&& key)
    {
        m_key = std::move(key);
    }


ReturnCode_t DataReaderImpl::take_next_sample(
        void* data,
        SampleInfo* info)
{
    if (reader_ == nullptr)
    {
        return ReturnCode_t::RETCODE_NOT_ENABLED;
    }

    if (history_.getHistorySize() == 0)
    {
        return ReturnCode_t::RETCODE_NO_DATA;
    }

    auto max_blocking_time = std::chrono::steady_clock::now() +
#if HAVE_STRICT_REALTIME
            std::chrono::microseconds(::TimeConv::Time_t2MicroSecondsInt64(qos_.reliability().max_blocking_time));
#else
            std::chrono::hours(24);
#endif // if HAVE_STRICT_REALTIME

    SampleInfo_t rtps_info;
    if (history_.takeNextData(data, &rtps_info, max_blocking_time))
    {
        sample_info_to_dds(rtps_info, info);
        return ReturnCode_t::RETCODE_OK;
    }
    return ReturnCode_t::RETCODE_ERROR;
}


bool Permissions::get_permissions_token(
        PermissionsToken** permissions_token,
        const PermissionsHandle& handle,
        SecurityException& exception)
{
    const AccessPermissionsHandle& phandle = AccessPermissionsHandle::narrow(handle);

    if (!phandle.nil())
    {
        *permissions_token = new PermissionsToken(phandle->permissions_token_);
        return true;
    }
    else
    {
        exception = _SecurityException_("Invalid permissions handle");
        EMERGENCY_SECURITY_LOGGING("Permissions", exception.what());
    }

    return false;
}


TEST_P(Security, BuiltinAuthenticationAndCryptoPlugin_besteffort_submessage_data300kb)
{
    PubSubReader<Data1mbType> reader(TEST_TOPIC_NAME);
    PubSubWriter<Data1mbType> writer(TEST_TOPIC_NAME);

    PropertyPolicy pub_part_property_policy, sub_part_property_policy,
            pub_property_policy, sub_property_policy;

    sub_part_property_policy.properties().emplace_back(Property("dds.sec.auth.plugin",
            "builtin.PKI-DH"));
    sub_part_property_policy.properties().emplace_back(Property("dds.sec.auth.builtin.PKI-DH.identity_ca",
            "file://" + std::string(certs_path) + "/maincacert.pem"));
    sub_part_property_policy.properties().emplace_back(Property("dds.sec.auth.builtin.PKI-DH.identity_certificate",
            "file://" + std::string(certs_path) + "/mainsubcert.pem"));
    sub_part_property_policy.properties().emplace_back(Property("dds.sec.auth.builtin.PKI-DH.private_key",
            "file://" + std::string(certs_path) + "/mainsubkey.pem"));
    sub_part_property_policy.properties().emplace_back(Property("dds.sec.crypto.plugin",
            "builtin.AES-GCM-GMAC"));
    sub_property_policy.properties().emplace_back("rtps.endpoint.submessage_protection_kind", "ENCRYPT");

    reader.history_depth(5).
            property_policy(sub_part_property_policy).
            entity_property_policy(sub_property_policy).init();

    ASSERT_TRUE(reader.isInitialized());

    pub_part_property_policy.properties().emplace_back(Property("dds.sec.auth.plugin",
            "builtin.PKI-DH"));
    pub_part_property_policy.properties().emplace_back(Property("dds.sec.auth.builtin.PKI-DH.identity_ca",
            "file://" + std::string(certs_path) + "/maincacert.pem"));
    pub_part_property_policy.properties().emplace_back(Property("dds.sec.auth.builtin.PKI-DH.identity_certificate",
            "file://" + std::string(certs_path) + "/mainpubcert.pem"));
    pub_part_property_policy.properties().emplace_back(Property("dds.sec.auth.builtin.PKI-DH.private_key",
            "file://" + std::string(certs_path) + "/mainpubkey.pem"));
    pub_part_property_policy.properties().emplace_back(Property("dds.sec.crypto.plugin",
            "builtin.AES-GCM-GMAC"));
    pub_property_policy.properties().emplace_back("rtps.endpoint.submessage_protection_kind", "ENCRYPT");

    // When doing fragmentation, it is necessary to have some degree of
    // flow control not to overrun the receive buffer.
    uint32_t bytesPerPeriod = 65536;
    uint32_t periodInMs = 500;

    writer.history_depth(5).
            reliability(eprosima::fastrtps::BEST_EFFORT_RELIABILITY_QOS).
            asynchronously(eprosima::fastrtps::ASYNCHRONOUS_PUBLISH_MODE).
            add_throughput_controller_descriptor_to_pparams(bytesPerPeriod, periodInMs).
            property_policy(pub_part_property_policy).
            entity_property_policy(pub_property_policy).init();

    ASSERT_TRUE(writer.isInitialized());

    // Wait for authorization
    reader.waitAuthorized();
    writer.waitAuthorized();

    // Wait for discovery.
    writer.wait_discovery();
    reader.wait_discovery();

    auto data = default_data300kb_data_generator(5);

    reader.startReception(data);

    // Send data
    writer.send(data);
    // In this test all data should be sent.
    ASSERT_TRUE(data.empty());
    // Block reader until reception finished or timeout.
    reader.block_for_at_least(2);
}


bool DataWriterImpl::write(
        void* data)
{
    if (writer_ == nullptr)
    {
        return false;
    }

    logInfo(DATA_WRITER, "Writing new data");
    return create_new_change(ALIVE, data);
}


void blackbox_security_init()
{
    certs_path = std::getenv("CERTS_PATH");

    if (certs_path == nullptr)
    {
        std::cout << "Cannot get enviroment variable CERTS_PATH" << std::endl;
        exit(-1);
    }
}


TEST_P(Security, BuiltinAuthenticationAndAccessAndCryptoPlugin_PermissionsEnableDiscoveryEnableAccessNone_validation_ok_disable_discovery_disable_access_none)
// *INDENT-ON*
{
    PubSubReader<HelloWorldType> reader(TEST_TOPIC_NAME);
    PubSubWriter<HelloWorldType> writer(TEST_TOPIC_NAME);
    std::string governance_file("governance_enable_discovery_enable_access_none.smime");

    BuiltinAuthenticationAndAccessAndCryptoPlugin_Permissions_validation_ok_common(reader, writer, governance_file);
}


bool DataWriterImpl::remove_min_seq_change()
{
    return history_.removeMinChange();
}


TEST_P(Security, BuiltinAuthenticationAndAccessAndCryptoPlugin_Permissions_validation_fail_on_topic_wildcards)
{
    {
        // Wildcards are only considered on PERMISSIONS, Topic values should be treated as plain strings
        PubSubReader<HelloWorldType> reader("*");
        PubSubWriter<HelloWorldType> writer("*");
        std::string governance_file("governance_helloworld_all_enable.smime");

        PropertyPolicy pub_property_policy, sub_property_policy;

        sub_property_policy.properties().emplace_back(Property("dds.sec.auth.plugin",
                "builtin.PKI-DH"));
        sub_property_policy.properties().emplace_back(Property("dds.sec.auth.builtin.PKI-DH.identity_ca",
                "file://" + std::string(certs_path) + "/maincacert.pem"));
        sub_property_policy.properties().emplace_back(Property("dds.sec.auth.builtin.PKI-DH.identity_certificate",
                "file://" + std::string(certs_path) + "/mainsubcert.pem"));
        sub_property_policy.properties().emplace_back(Property("dds.sec.auth.builtin.PKI-DH.private_key",
                "file://" + std::string(certs_path) + "/mainsubkey.pem"));
        sub_property_policy.properties().emplace_back(Property("dds.sec.crypto.plugin",
                "builtin.AES-GCM-GMAC"));
        sub_property_policy.properties().emplace_back(Property("dds.sec.access.plugin",
                "builtin.Access-Permissions"));
        sub_property_policy.properties().emplace_back(Property(
                    "dds.sec.access.builtin.Access-Permissions.permissions_ca",
                    "file://" + std::string(certs_path) + "/maincacert.pem"));
        sub_property_policy.properties().emplace_back(Property("dds.sec.access.builtin.Access-Permissions.governance",
                "file://" + std::string(certs_path) + "/" + governance_file));
        sub_property_policy.properties().emplace_back(Property("dds.sec.access.builtin.Access-Permissions.permissions",
                "file://" + std::string(certs_path) + "/permissions_helloworld.smime"));

        reader.setManualTopicName("*").
                property_policy(sub_property_policy).init();
        ASSERT_FALSE(reader.isInitialized());

        pub_property_policy.properties().emplace_back(Property("dds.sec.auth.plugin",
                "builtin.PKI-DH"));
        pub_property_policy.properties().emplace_back(Property("dds.sec.auth.builtin.PKI-DH.identity_ca",
                "file://" + std::string(certs_path) + "/maincacert.pem"));
        pub_property_policy.properties().emplace_back(Property("dds.sec.auth.builtin.PKI-DH.identity_certificate",
                "file://" + std::string(certs_path) + "/mainpubcert.pem"));
        pub_property_policy.properties().emplace_back(Property("dds.sec.auth.builtin.PKI-DH.private_key",
                "file://" + std::string(certs_path) + "/mainpubkey.pem"));
        pub_property_policy.properties().emplace_back(Property("dds.sec.crypto.plugin",
                "builtin.AES-GCM-GMAC"));
        pub_property_policy.properties().emplace_back(Property("dds.sec.access.plugin",
                "builtin.Access-Permissions"));
        pub_property_policy.properties().emplace_back(Property(
                    "dds.sec.access.builtin.Access-Permissions.permissions_ca",
                    "file://" + std::string(certs_path) + "/maincacert.pem"));
        pub_property_policy.properties().emplace_back(Property("dds.sec.access.builtin.Access-Permissions.governance",
                "file://" + std::string(certs_path) + "/" + governance_file));
        pub_property_policy.properties().emplace_back(Property("dds.sec.access.builtin.Access-Permissions.permissions",
                "file://" + std::string(certs_path) + "/permissions_helloworld.smime"));

        writer.setManualTopicName("*").
                property_policy(pub_property_policy).init();

        ASSERT_FALSE(writer.isInitialized());
    }

    {
        // Wildcards are only considered on PERMISSIONS, Topic values should be treated as plain strings
        PubSubReader<HelloWorldType> reader("HelloWorldTopic");
        PubSubWriter<HelloWorldType> writer("HelloWorldTopic");
        std::string governance_file("governance_helloworld_all_enable.smime");

        PropertyPolicy pub_property_policy, sub_property_policy;

        sub_property_policy.properties().emplace_back(Property("dds.sec.auth.plugin",
                "builtin.PKI-DH"));
        sub_property_policy.properties().emplace_back(Property("dds.sec.auth.builtin.PKI-DH.identity_ca",
                "file://" + std::string(certs_path) + "/maincacert.pem"));
        sub_property_policy.properties().emplace_back(Property("dds.sec.auth.builtin.PKI-DH.identity_certificate",
                "file://" + std::string(certs_path) + "/mainsubcert.pem"));
        sub_property_policy.properties().emplace_back(Property("dds.sec.auth.builtin.PKI-DH.private_key",
                "file://" + std::string(certs_path) + "/mainsubkey.pem"));
        sub_property_policy.properties().emplace_back(Property("dds.sec.crypto.plugin",
                "builtin.AES-GCM-GMAC"));
        sub_property_policy.properties().emplace_back(Property("dds.sec.access.plugin",
                "builtin.Access-Permissions"));
        sub_property_policy.properties().emplace_back(Property(
                    "dds.sec.access.builtin.Access-Permissions.permissions_ca",
                    "file://" + std::string(certs_path) + "/maincacert.pem"));
        sub_property_policy.properties().emplace_back(Property("dds.sec.access.builtin.Access-Permissions.governance",
                "file://" + std::string(certs_path) + "/" + governance_file));
        sub_property_policy.properties().emplace_back(Property("dds.sec.access.builtin.Access-Permissions.permissions",
                "file://" + std::string(certs_path) + "/permissions_helloworld.smime"));

        reader.history_depth(10).
                reliability(eprosima::fastrtps::RELIABLE_RELIABILITY_QOS).
                property_policy(sub_property_policy).init();

        ASSERT_TRUE(reader.isInitialized());

        pub_property_policy.properties().emplace_back(Property("dds.sec.auth.plugin",
                "builtin.PKI-DH"));
        pub_property_policy.properties().emplace_back(Property("dds.sec.auth.builtin.PKI-DH.identity_ca",
                "file://" + std::string(certs_path) + "/maincacert.pem"));
        pub_property_policy.properties().emplace_back(Property("dds.sec.auth.builtin.PKI-DH.identity_certificate",
                "file://" + std::string(certs_path) + "/mainpubcert.pem"));
        pub_property_policy.properties().emplace_back(Property("dds.sec.auth.builtin.PKI-DH.private_key",
                "file://" + std::string(certs_path) + "/mainpubkey.pem"));
        pub_property_policy.properties().emplace_back(Property("dds.sec.crypto.plugin",
                "builtin.AES-GCM-GMAC"));
        pub_property_policy.properties().emplace_back(Property("dds.sec.access.plugin",
                "builtin.Access-Permissions"));
        pub_property_policy.properties().emplace_back(Property(
                    "dds.sec.access.builtin.Access-Permissions.permissions_ca",
                    "file://" + std::string(certs_path) + "/maincacert.pem"));
        pub_property_policy.properties().emplace_back(Property("dds.sec.access.builtin.Access-Permissions.governance",
                "file://" + std::string(certs_path) + "/" + governance_file));
        pub_property_policy.properties().emplace_back(Property("dds.sec.access.builtin.Access-Permissions.permissions",
                "file://" + std::string(certs_path) + "/permissions_helloworld.smime"));

        writer.history_depth(10).
                property_policy(pub_property_policy).init();

        ASSERT_TRUE(writer.isInitialized());

        // Wait for authorization
        reader.waitAuthorized();
        writer.waitAuthorized();

        // Wait for discovery.
        writer.wait_discovery();
        reader.wait_discovery();

        auto data = default_helloworld_data_generator();

        reader.startReception(data);

        // Send data
        writer.send(data);
        // In this test all data should be sent.
        ASSERT_TRUE(data.empty());
        // Block reader until reception finished or timeout.
        reader.block_for_all();
    }
}


static int cmd_uname(void *data, const char *input) {
	const char* help_msg[] = {
		"Usage:", "u", "uname or undo write/seek",
		"u", "", "show system uname",
		"uw", "", "alias for wc (requires: e io.cache=true)",
		"us", "", "alias for s- (seek history)",
		NULL};
	switch (input[0]) {
	case '?':
		r_core_cmd_help (data, help_msg);
		return 1;
	case 's':
		r_core_cmdf (data, "s-%s", input + 1);
		return 1;
	case 'w':
		r_core_cmdf (data, "wc%s", input + 1);
		return 1;
	}
#if __UNIX__
	struct utsname un;
	uname (&un);
	r_cons_printf ("%s %s %s %s\n", un.sysname,
		un.nodename, un.release, un.machine);
#elif __WINDOWS__
	r_cons_printf ("windows\n");
#else
	r_cons_printf ("unknown\n");
#endif
	return 0;
}


static int cmd_env(void *data, const char *input) {
	RCore *core = (RCore*)data;
	int ret = true;
	switch (*input) {
	case '?':
		{
		const char* help_msg[] = {
			"Usage:", "%[name[=value]]", "Set each NAME to VALUE in the environment",
			"%", "", "list all environment variables",
			"%", "SHELL", "prints SHELL value",
			"%", "TMPDIR=/tmp", "sets TMPDIR value to \"/tmp\"",
			NULL};
		r_core_cmd_help (core, help_msg);
		}
		break;
	default:
		ret = r_core_cmdf (core, "env %s", input);
	}
	return ret;
}


static int demangle(RCore *core, const char *s) {
	char *p, *q;
	const char *ss = strchr (s, ' ');
	if (!*s) {
		return 0;
	}
	if (!ss) {
		const char *lang = r_config_get (core->config, "bin.lang");
		demangle_internal (core, lang, s);
		return 1;
	}
	p = strdup (s);
	q = p + (ss - s);
	*q = 0;
	demangle_internal (core, p, q + 1);
	free (p);
	return 1;
}


static int cmd_yank(void *data, const char *input) {
	ut64 n;
	RCore *core = (RCore *)data;
	switch (input[0]) {
	case ' ':
		r_core_yank (core, core->offset, r_num_math (core->num, input + 1));
		break;
	case 'l':
		core->num->value = core->yank_buf->length;
		break;
	case 'y':
		while (input[1] == ' ') {
			input++;
		}
		n = input[1]? r_num_math (core->num, input + 1): core->offset;
		r_core_yank_paste (core, n, 0);
		break;
	case 'x':
		r_core_yank_hexdump (core, r_num_math (core->num, input + 1));
		break;
	case 'z':
		r_core_yank_string (core, core->offset, r_num_math (core->num, input + 1));
		break;
	case 'w':
		switch (input[1]) {
		case ' ':
			r_core_yank_set (core, 0, (const ut8*)input + 2, strlen (input + 2));
			break;
		case 'x':
			if (input[2] == ' ') {
				char *out = strdup (input + 3);
				int len = r_hex_str2bin (input + 3, (ut8*)out);
				if (len> 0) {
					r_core_yank_set (core, 0LL, (const ut8*)out, len);
				} else {
					eprintf ("Invalid length\n");
				}
				free (out);
			} else {
				eprintf ("Usage: ywx [hexpairs]\n");
			}
			// r_core_yank_write_hex (core, input + 2);
			break;
		}
		break;
	case 'p':
		r_core_yank_cat (core, r_num_math (core->num, input + 1));
		break;
	case 's':
		r_core_yank_cat_string (core, r_num_math (core->num, input + 1));
		break;
	case 't': // "wt"
		if (input[1] == 'f') { // "wtf"
			const char *file = r_str_chop_ro (input + 2);
			if (!r_file_dump (file, core->yank_buf->buf, core->yank_buf->length, false)) {
				eprintf ("Cannot dump to '%s'\n", file);
			}
		} else {
			r_core_yank_to (core, input + 1);
		}
		break;
	case 'f':
		switch (input[1]) {
		case ' ': // "wf"
			r_core_yank_file_ex (core, input + 1);
			break;
		case 'a': // "wfa"
			r_core_yank_file_all (core, input + 2);
			break;
		}
		break;
	case '\0':
		r_core_yank_dump (core, r_num_math (core->num, ""));
		break;
	default:{
		const char* help_msg[] = {
		"Usage:", "y[ptxy] [len] [[@]addr]", " # See wd? for memcpy, same as 'yf'.",
		"y", "", "show yank buffer information (srcoff len bytes)",
		"y", " 16", "copy 16 bytes into clipboard",
		"y", " 16 0x200", "copy 16 bytes into clipboard from 0x200",
		"y", " 16 @ 0x200", "copy 16 bytes into clipboard from 0x200",
		"yz", "", "copy up to blocksize zero terminated string bytes into clipboard",
		"yz", " 16", "copy up to 16 zero terminated string bytes into clipboard",
		"yz", " @ 0x200", "copy up to blocksize zero terminated string bytes into clipboard from 0x200",
		"yz", " 16 @ 0x200", "copy up to 16 zero terminated string bytes into clipboard from 0x200",
		"yp", "", "print contents of clipboard",
		"yx", "", "print contents of clipboard in hexadecimal",
		"ys", "", "print contents of clipboard as string",
		"yt", " 64 0x200", "copy 64 bytes from current seek to 0x200",
		"ytf", " file", "dump the clipboard to given file",
		"yf", " 64 0x200", "file copy 64 bytes from 0x200 from file (opens w/ io), use -1 for all bytes",
		"yfa", " file copy", "copy all bytes from file (opens w/ io)",
		"yy", " 0x3344", "paste clipboard",
		NULL};
		r_core_cmd_help (core, help_msg);
		}
		break;
	}
	return true;
}


static int cmd_system(void *data, const char *input) {
	RCore *core = (RCore*)data;
	ut64 n;
	int ret = 0;
	switch (*input) {
	case '=':
		if (input[1] == '?') {
			r_cons_printf ("Usage: !=[!]  - enable/disable remote commands\n");
		} else {
			if (!r_sandbox_enable (0)) {
				core->cmdremote = input[1]? 1: 0;
				r_cons_println (r_str_bool (core->cmdremote));
			}
		}
		break;
	case '!':
		if (r_sandbox_enable (0)) {
			eprintf ("This command is disabled in sandbox mode\n");
			return 0;
		}
		if (input[1]) {
			int olen;
			char *out = NULL;
			char *cmd = r_core_sysenv_begin (core, input);
			if (cmd) {
				ret = r_sys_cmd_str_full (cmd + 1, NULL, &out, &olen, NULL);
				r_core_sysenv_end (core, input);
				r_cons_memcat (out, olen);
				free (out);
				free (cmd);
			} //else eprintf ("Error setting up system environment\n");
		} else {
			eprintf ("History saved to "R2_HOMEDIR"/history\n");
			r_line_hist_save (R2_HOMEDIR"/history");
		}
		break;
	case '\0':
		r_line_hist_list ();
		break;
	case '?':
		r_core_sysenv_help (core);
		break;
	default:
		n = atoi (input);
		if (*input == '0' || n > 0) {
			const char *cmd = r_line_hist_get (n);
			if (cmd) r_core_cmd0 (core, cmd);
			//else eprintf ("Error setting up system environment\n");
		} else {
			char *cmd = r_core_sysenv_begin (core, input);
			if (cmd) {
				ret = r_sys_cmd (cmd);
				r_core_sysenv_end (core, input);
				free (cmd);
			} else eprintf ("Error setting up system environment\n");
		}
		break;
	}
	return ret;
}


R_API RAsmOp *r_core_disassemble (RCore *core, ut64 addr) {
	int delta;
	ut8 buf[128];
	static RBuffer *b = NULL; // XXX: never freed and non-thread safe. move to RCore
	RAsmOp *op;
	if (!b) {
		b = r_buf_new ();
		if (!b || !r_core_read_at (core, addr, buf, sizeof (buf))) {
			return NULL;
		}
		b->base = addr;
		r_buf_set_bytes (b, buf, sizeof (buf));
	} else {
		if ((addr < b->base) || addr > (b->base + b->length - 32)) {
			if (!r_core_read_at (core, addr, buf, sizeof (buf))) {
				return NULL;
			}
			b->base = addr;
			r_buf_set_bytes (b, buf, sizeof (buf));
		}
	}
	delta = addr - b->base;
	op = R_NEW0 (RAsmOp);
	r_asm_set_pc (core->assembler, addr);
	if (r_asm_disassemble (core->assembler, op, b->buf + delta, b->length) < 1) {
		free (op);
		return NULL;
	}
	return op;
}


struct vfsmount *collect_mounts(struct path *path)
{
	struct mount *tree;
	down_write(&namespace_sem);
	tree = copy_tree(real_mount(path->mnt), path->dentry,
			 CL_COPY_ALL | CL_PRIVATE);
	up_write(&namespace_sem);
	if (IS_ERR(tree))
		return NULL;
	return &tree->mnt;
}


static int do_add_mount(struct mount *newmnt, struct path *path, int mnt_flags)
{
	int err;

	mnt_flags &= ~(MNT_SHARED | MNT_WRITE_HOLD | MNT_INTERNAL);

	err = lock_mount(path);
	if (err)
		return err;

	err = -EINVAL;
	if (unlikely(!check_mnt(real_mount(path->mnt)))) {
		/* that's acceptable only for automounts done in private ns */
		if (!(mnt_flags & MNT_SHRINKABLE))
			goto unlock;
		/* ... and for those we'd better have mountpoint still alive */
		if (!real_mount(path->mnt)->mnt_ns)
			goto unlock;
	}

	/* Refuse the same filesystem on the same mount point */
	err = -EBUSY;
	if (path->mnt->mnt_sb == newmnt->mnt.mnt_sb &&
	    path->mnt->mnt_root == path->dentry)
		goto unlock;

	err = -EINVAL;
	if (S_ISLNK(newmnt->mnt.mnt_root->d_inode->i_mode))
		goto unlock;

	newmnt->mnt.mnt_flags = mnt_flags;
	err = graft_tree(newmnt, path);

unlock:
	unlock_mount(path);
	return err;
}


static int mnt_make_readonly(struct mount *mnt)
{
	int ret = 0;

	br_write_lock(&vfsmount_lock);
	mnt->mnt.mnt_flags |= MNT_WRITE_HOLD;
	/*
	 * After storing MNT_WRITE_HOLD, we'll read the counters. This store
	 * should be visible before we do.
	 */
	smp_mb();

	/*
	 * With writers on hold, if this value is zero, then there are
	 * definitely no active writers (although held writers may subsequently
	 * increment the count, they'll have to wait, and decrement it after
	 * seeing MNT_READONLY).
	 *
	 * It is OK to have counter incremented on one CPU and decremented on
	 * another: the sum will add up correctly. The danger would be when we
	 * sum up each counter, if we read a counter before it is incremented,
	 * but then read another CPU's count which it has been subsequently
	 * decremented from -- we would see more decrements than we should.
	 * MNT_WRITE_HOLD protects against this scenario, because
	 * mnt_want_write first increments count, then smp_mb, then spins on
	 * MNT_WRITE_HOLD, so it can't be decremented by another CPU while
	 * we're counting up here.
	 */
	if (mnt_get_writers(mnt) > 0)
		ret = -EBUSY;
	else
		mnt->mnt.mnt_flags |= MNT_READONLY;
	/*
	 * MNT_READONLY must become visible before ~MNT_WRITE_HOLD, so writers
	 * that become unheld will see MNT_READONLY.
	 */
	smp_wmb();
	mnt->mnt.mnt_flags &= ~MNT_WRITE_HOLD;
	br_write_unlock(&vfsmount_lock);
	return ret;
}


int mempool_getinfo(MemoryPoolHandle handle, struct pool_info *poolinfo) {
  struct mempool *pool = (struct mempool *)handle;

  if ((pool == NULL) || (poolinfo == NULL)) {
    return S3_MEMPOOL_INVALID_ARG;
  }

  if (pool->flags & ENABLE_LOCKING) {
    pthread_mutex_lock(&pool->lock);
  }

  poolinfo->mempool_item_size = pool->mempool_item_size;
  poolinfo->free_bufs_in_pool = pool->free_bufs_in_pool;
  poolinfo->number_of_bufs_shared = pool->number_of_bufs_shared;
  poolinfo->expandable_size = pool->expandable_size;
  poolinfo->total_bufs_allocated_by_pool = pool->total_bufs_allocated_by_pool;
  poolinfo->flags = pool->flags;

  if ((pool->flags & ENABLE_LOCKING) != 0) {
    pthread_mutex_unlock(&pool->lock);
  }
  return 0;
}


int mempool_create_with_shared_mem(
    size_t pool_item_size, size_t pool_initial_size, size_t pool_expansion_size,
    func_mem_available_callback_type mem_get_free_space_func,
    func_mark_mem_used_callback_type mem_mark_used_space_func,
    func_mark_mem_free_callback_type mem_mark_free_space_func,
    func_log_callback_type log_callback_func, int flags,
    MemoryPoolHandle *p_handle) {
  int rc = 0;
  struct mempool *pool = NULL;
  if (mem_get_free_space_func == NULL || mem_mark_used_space_func == NULL ||
      mem_mark_free_space_func == NULL || p_handle == NULL) {
    return S3_MEMPOOL_INVALID_ARG;
  }

  if (pool_initial_size > mem_get_free_space_func()) {
    return S3_MEMPOOL_THRESHOLD_EXCEEDED;
  }

  rc = mempool_create(pool_item_size, pool_initial_size, pool_expansion_size, 0,
                      log_callback_func, flags, p_handle);
  if (rc != 0) {
    return rc;
  }

  pool = (struct mempool *)*p_handle;

  pool->mem_get_free_space_func = mem_get_free_space_func;
  pool->mem_mark_used_space_func = mem_mark_used_space_func;
  pool->mem_mark_free_space_func = mem_mark_free_space_func;

  /* Explicitly mark used space, since mempool_create -> freelist_allocate
     dont have the function callbacks set. */
  pool->mem_mark_used_space_func(pool->total_bufs_allocated_by_pool *
                                 pool->mempool_item_size);

  return 0;
}


int mempool_available_space(MemoryPoolHandle p_pool, size_t *p_avail_bytes) {
  if (!p_pool || !p_avail_bytes) {
    return S3_MEMPOOL_INVALID_ARG;
  }
  if (p_pool->flags & ENABLE_LOCKING) {
    pthread_mutex_lock(&p_pool->lock);
  }
  const size_t used_space =
      p_pool->number_of_bufs_shared * p_pool->mempool_item_size;
  const size_t max_memory_threshold = p_pool->max_memory_threshold;

  *p_avail_bytes =
      max_memory_threshold > used_space ? max_memory_threshold - used_space : 0;

  if (p_pool->flags & ENABLE_LOCKING) {
    pthread_mutex_unlock(&p_pool->lock);
  }
  return 0;
}


int _yr_emit_inst_arg_uint16(
    RE_EMIT_CONTEXT* emit_context,
    uint8_t opcode,
    uint16_t argument,
    uint8_t** instruction_addr,
    uint16_t** argument_addr,
    size_t* code_size)
{
  FAIL_ON_ERROR(yr_arena_write_data(
      emit_context->arena,
      &opcode,
      sizeof(uint8_t),
      (void**) instruction_addr));

  FAIL_ON_ERROR(yr_arena_write_data(
      emit_context->arena,
      &argument,
      sizeof(uint16_t),
      (void**) argument_addr));

  *code_size = sizeof(uint8_t) + sizeof(uint16_t);

  return ERROR_SUCCESS;
}


int _yr_emit_inst_arg_struct(
    RE_EMIT_CONTEXT* emit_context,
    uint8_t opcode,
    void* structure,
    size_t structure_size,
    uint8_t** instruction_addr,
    void** argument_addr,
    size_t* code_size)
{
  FAIL_ON_ERROR(yr_arena_write_data(
      emit_context->arena,
      &opcode,
      sizeof(uint8_t),
      (void**) instruction_addr));

  FAIL_ON_ERROR(yr_arena_write_data(
      emit_context->arena,
      structure,
      structure_size,
      (void**) argument_addr));

  *code_size = sizeof(uint8_t) + structure_size;

  return ERROR_SUCCESS;
}


SYSCALL_DEFINE1(shmdt, char __user *, shmaddr)
{
	struct mm_struct *mm = current->mm;
	struct vm_area_struct *vma;
	unsigned long addr = (unsigned long)shmaddr;
	int retval = -EINVAL;
#ifdef CONFIG_MMU
	loff_t size = 0;
	struct file *file;
	struct vm_area_struct *next;
#endif

	if (addr & ~PAGE_MASK)
		return retval;

	down_write(&mm->mmap_sem);

	/*
	 * This function tries to be smart and unmap shm segments that
	 * were modified by partial mlock or munmap calls:
	 * - It first determines the size of the shm segment that should be
	 *   unmapped: It searches for a vma that is backed by shm and that
	 *   started at address shmaddr. It records it's size and then unmaps
	 *   it.
	 * - Then it unmaps all shm vmas that started at shmaddr and that
	 *   are within the initially determined size and that are from the
	 *   same shm segment from which we determined the size.
	 * Errors from do_munmap are ignored: the function only fails if
	 * it's called with invalid parameters or if it's called to unmap
	 * a part of a vma. Both calls in this function are for full vmas,
	 * the parameters are directly copied from the vma itself and always
	 * valid - therefore do_munmap cannot fail. (famous last words?)
	 */
	/*
	 * If it had been mremap()'d, the starting address would not
	 * match the usual checks anyway. So assume all vma's are
	 * above the starting address given.
	 */
	vma = find_vma(mm, addr);

#ifdef CONFIG_MMU
	while (vma) {
		next = vma->vm_next;

		/*
		 * Check if the starting address would match, i.e. it's
		 * a fragment created by mprotect() and/or munmap(), or it
		 * otherwise it starts at this address with no hassles.
		 */
		if ((vma->vm_ops == &shm_vm_ops) &&
			(vma->vm_start - addr)/PAGE_SIZE == vma->vm_pgoff) {

			/*
			 * Record the file of the shm segment being
			 * unmapped.  With mremap(), someone could place
			 * page from another segment but with equal offsets
			 * in the range we are unmapping.
			 */
			file = vma->vm_file;
			size = i_size_read(file_inode(vma->vm_file));
			do_munmap(mm, vma->vm_start, vma->vm_end - vma->vm_start);
			/*
			 * We discovered the size of the shm segment, so
			 * break out of here and fall through to the next
			 * loop that uses the size information to stop
			 * searching for matching vma's.
			 */
			retval = 0;
			vma = next;
			break;
		}
		vma = next;
	}

	/*
	 * We need look no further than the maximum address a fragment
	 * could possibly have landed at. Also cast things to loff_t to
	 * prevent overflows and make comparisons vs. equal-width types.
	 */
	size = PAGE_ALIGN(size);
	while (vma && (loff_t)(vma->vm_end - addr) <= size) {
		next = vma->vm_next;

		/* finding a matching vma now does not alter retval */
		if ((vma->vm_ops == &shm_vm_ops) &&
		    ((vma->vm_start - addr)/PAGE_SIZE == vma->vm_pgoff) &&
		    (vma->vm_file == file))
			do_munmap(mm, vma->vm_start, vma->vm_end - vma->vm_start);
		vma = next;
	}

#else /* CONFIG_MMU */
	/* under NOMMU conditions, the exact address to be destroyed must be
	 * given */
	if (vma && vma->vm_start == addr && vma->vm_ops == &shm_vm_ops) {
		do_munmap(mm, vma->vm_start, vma->vm_end - vma->vm_start);
		retval = 0;
	}

#endif

	up_write(&mm->mmap_sem);
	return retval;
}


static int sysvipc_msg_proc_show(struct seq_file *s, void *it)
{
	struct user_namespace *user_ns = seq_user_ns(s);
	struct msg_queue *msq = it;

	seq_printf(s,
		   "%10d %10d  %4o  %10lu %10lu %5u %5u %5u %5u %5u %5u %10lu %10lu %10lu\n",
		   msq->q_perm.key,
		   msq->q_perm.id,
		   msq->q_perm.mode,
		   msq->q_cbytes,
		   msq->q_qnum,
		   msq->q_lspid,
		   msq->q_lrpid,
		   from_kuid_munged(user_ns, msq->q_perm.uid),
		   from_kgid_munged(user_ns, msq->q_perm.gid),
		   from_kuid_munged(user_ns, msq->q_perm.cuid),
		   from_kgid_munged(user_ns, msq->q_perm.cgid),
		   msq->q_stime,
		   msq->q_rtime,
		   msq->q_ctime);

	return 0;
}


void *ipc_alloc(int size)
{
	void *out;
	if (size > PAGE_SIZE)
		out = vmalloc(size);
	else
		out = kmalloc(size, GFP_KERNEL);
	return out;
}


static void freeque(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp)
{
	struct msg_msg *msg, *t;
	struct msg_queue *msq = container_of(ipcp, struct msg_queue, q_perm);

	expunge_all(msq, -EIDRM);
	ss_wakeup(&msq->q_senders, 1);
	msg_rmid(ns, msq);
	ipc_unlock_object(&msq->q_perm);
	rcu_read_unlock();

	list_for_each_entry_safe(msg, t, &msq->q_messages, m_list) {
		atomic_dec(&ns->msg_hdrs);
		free_msg(msg);
	}
	atomic_sub(msq->q_cbytes, &ns->msg_bytes);
	ipc_rcu_putref(msq, msg_rcu_free);
}


static void shm_open(struct vm_area_struct *vma)
{
	struct file *file = vma->vm_file;
	struct shm_file_data *sfd = shm_file_data(file);
	struct shmid_kernel *shp;

	shp = shm_lock(sfd->ns, sfd->id);
	shp->shm_atim = get_seconds();
	shp->shm_lprid = task_tgid_vnr(current);
	shp->shm_nattch++;
	shm_unlock(shp);
}


static int newque(struct ipc_namespace *ns, struct ipc_params *params)
{
	struct msg_queue *msq;
	int id, retval;
	key_t key = params->key;
	int msgflg = params->flg;

	msq = ipc_rcu_alloc(sizeof(*msq));
	if (!msq)
		return -ENOMEM;

	msq->q_perm.mode = msgflg & S_IRWXUGO;
	msq->q_perm.key = key;

	msq->q_perm.security = NULL;
	retval = security_msg_queue_alloc(msq);
	if (retval) {
		ipc_rcu_putref(msq, ipc_rcu_free);
		return retval;
	}

	msq->q_stime = msq->q_rtime = 0;
	msq->q_ctime = get_seconds();
	msq->q_cbytes = msq->q_qnum = 0;
	msq->q_qbytes = ns->msg_ctlmnb;
	msq->q_lspid = msq->q_lrpid = 0;
	INIT_LIST_HEAD(&msq->q_messages);
	INIT_LIST_HEAD(&msq->q_receivers);
	INIT_LIST_HEAD(&msq->q_senders);

	/* ipc_addid() locks msq upon success. */
	id = ipc_addid(&msg_ids(ns), &msq->q_perm, ns->msg_ctlmni);
	if (id < 0) {
		ipc_rcu_putref(msq, msg_rcu_free);
		return id;
	}

	ipc_unlock_object(&msq->q_perm);
	rcu_read_unlock();

	return msq->q_perm.id;
}


void syscall_log(const void *buf __maybe_unused, size_t len __maybe_unused)
{
#ifdef CFG_TEE_CORE_TA_TRACE
	char *kbuf;

	if (len == 0)
		return;

	kbuf = malloc(len + 1);
	if (kbuf == NULL)
		return;

	if (tee_svc_copy_from_user(kbuf, buf, len) == TEE_SUCCESS) {
		kbuf[len] = '\0';
		trace_ext_puts(kbuf);
	}

	free(kbuf);
#endif
}


static TEE_Result get_prop_tee_dev_id(struct tee_ta_session *sess __unused,
				      void *buf, size_t *blen)
{
	TEE_Result res;
	TEE_UUID uuid;
	const size_t nslen = 5;
	uint8_t data[5 + FVR_DIE_ID_NUM_REGS * sizeof(uint32_t)] = {
	    'O', 'P', 'T', 'E', 'E' };

	if (*blen < sizeof(uuid)) {
		*blen = sizeof(uuid);
		return TEE_ERROR_SHORT_BUFFER;
	}
	*blen = sizeof(uuid);

	if (tee_otp_get_die_id(data + nslen, sizeof(data) - nslen))
		return TEE_ERROR_BAD_STATE;

	res = tee_hash_createdigest(TEE_ALG_SHA256, data, sizeof(data),
				    (uint8_t *)&uuid, sizeof(uuid));
	if (res != TEE_SUCCESS)
		return TEE_ERROR_BAD_STATE;

	/*
	 * Changes the random value into and UUID as specifiec
	 * in RFC 4122. The magic values are from the example
	 * code in the RFC.
	 *
	 * TEE_UUID is defined slightly different from the RFC,
	 * but close enough for our purpose.
	 */

	uuid.timeHiAndVersion &= 0x0fff;
	uuid.timeHiAndVersion |= 5 << 12;

	/* uuid.clock_seq_hi_and_reserved in the RFC */
	uuid.clockSeqAndNode[0] &= 0x3f;
	uuid.clockSeqAndNode[0] |= 0x80;

	return tee_svc_copy_to_user(buf, &uuid, sizeof(TEE_UUID));
}


TEE_Result syscall_close_ta_session(unsigned long ta_sess)
{
	TEE_Result res;
	struct tee_ta_session *sess;
	TEE_Identity clnt_id;
	struct tee_ta_session *s = tee_svc_uref_to_kaddr(ta_sess);
	struct user_ta_ctx *utc;

	res = tee_ta_get_current_session(&sess);
	if (res != TEE_SUCCESS)
		return res;
	utc = to_user_ta_ctx(sess->ctx);

	clnt_id.login = TEE_LOGIN_TRUSTED_APP;
	memcpy(&clnt_id.uuid, &sess->ctx->uuid, sizeof(TEE_UUID));

	return tee_ta_close_session(s, &utc->open_sessions, &clnt_id);
}


TEE_Result tee_svc_copy_kaddr_to_uref(uint32_t *uref, void *kaddr)
{
	uint32_t ref = tee_svc_kaddr_to_uref(kaddr);

	return tee_svc_copy_to_user(uref, &ref, sizeof(ref));
}


TEE_Result syscall_get_time(unsigned long cat, TEE_Time *mytime)
{
	TEE_Result res, res2;
	struct tee_ta_session *s = NULL;
	TEE_Time t;

	res = tee_ta_get_current_session(&s);
	if (res != TEE_SUCCESS)
		return res;

	switch (cat) {
	case UTEE_TIME_CAT_SYSTEM:
		res = tee_time_get_sys_time(&t);
		break;
	case UTEE_TIME_CAT_TA_PERSISTENT:
		res = tee_time_get_ta_time((const void *)&s->ctx->uuid, &t);
		break;
	case UTEE_TIME_CAT_REE:
		res = tee_time_get_ree_time(&t);
		break;
	default:
		res = TEE_ERROR_BAD_PARAMETERS;
		break;
	}

	if (res == TEE_SUCCESS || res == TEE_ERROR_OVERFLOW) {
		res2 = tee_svc_copy_to_user(mytime, &t, sizeof(t));
		if (res2 != TEE_SUCCESS)
			res = res2;
	}

	return res;
}


read_R2004_section_map (Bit_Chain *restrict dat, Dwg_Data *restrict dwg)
{
  BITCODE_RC *decomp, *ptr;
  int i, error = 0, found_section_map_id = 0;
  uint64_t section_address;
  int64_t bytes_remaining;
  const uint32_t comp_data_size = dwg->r2004_header.comp_data_size;
  const uint32_t decomp_data_size = dwg->r2004_header.decomp_data_size;
  const int32_t section_array_size = (int32_t)dwg->r2004_header.section_array_size;
  const uint64_t section_map_address = dwg->r2004_header.section_map_address + 0x100;
  const BITCODE_RL section_map_id = dwg->r2004_header.section_map_id;
  int max_id = 0;

  dwg->header.num_sections = 0;
  dwg->header.section = 0;

  // decompressed data
  decomp = (BITCODE_RC *)calloc (decomp_data_size + 1024, sizeof (BITCODE_RC));
  if (!decomp)
    {
      LOG_ERROR ("Out of memory");
      return DWG_ERR_OUTOFMEM;
    }

  error = decompress_R2004_section (dat, decomp, decomp_data_size + 1024,
                                    comp_data_size);
  if (error > DWG_ERR_CRITICAL || error == DWG_ERR_VALUEOUTOFBOUNDS)
    {
      free (decomp);
      return error;
    }
  LOG_TRACE ("\n#### Read 2004 Section Page Map ####\n")

  section_address = 0x100; // starting address
  i = 0;
  bytes_remaining = (int64_t)decomp_data_size;
  ptr = decomp;
  dwg->header.num_sections = 0;

  while (bytes_remaining >= 8)
    {
      error |= add_section (dwg);
      if (error > DWG_ERR_CRITICAL)
        return error;

      /* endian specific code: */
      bfr_read (&dwg->header.section[i], &ptr, 8);
      bytes_remaining -= 8;
      LOG_TRACE ("Section[%2d]=%2d,", i, (int)dwg->header.section[i].number)
      LOG_TRACE (" size: %5u,", dwg->header.section[i].size)
      dwg->header.section[i].address = section_address;
      if (dwg->header.section[i].number <= section_array_size) // GH #144
        {
          section_address += dwg->header.section[i].size;
          LOG_TRACE (" address: 0x%04lx\n",
                     (unsigned long)dwg->header.section[i].address)
        }
      else
        LOG_TRACE (" (ignored > %d section_array_size)\n",
                   (int)section_array_size);
      if (dwg->header.section[i].number > max_id)
        max_id = dwg->header.section[i].number;

      // repair section_map_id.address from section_map_address
      if ((BITCODE_RL)dwg->header.section[i].number == section_map_id)
        {
          found_section_map_id++;
          if (dwg->header.section[i].address != section_map_address)
            {
              LOG_WARN ("Repair invalid section_map_address: %" PRIx64 " != %" PRIx64,
                        dwg->header.section[i].address, section_map_address);
              error |= DWG_ERR_VALUEOUTOFBOUNDS;
              dwg->header.section[i].address = section_map_address;
            }
        }
      if (i >= (int)section_array_size)
        {
          error |= DWG_ERR_VALUEOUTOFBOUNDS;
          LOG_WARN ("Overflow section_array_size: %d >= %d",
                    i, (int)section_array_size);
          if (i > 1000)
            return error;
        }

      if (bytes_remaining >= 16
          && dwg->header.section[i].number < 0) // negative: gap/unused data
        //|| dwg->header.section[i].number > section_array_size))
        {
          /* endian specific code: */
          bfr_read (&dwg->header.section[i].parent, &ptr, 16);
          bytes_remaining -= 16;
          LOG_TRACE ("  Parent: %d, ", dwg->header.section[i].parent)
          LOG_TRACE ("Left:   %d, ", dwg->header.section[i].left)
          LOG_TRACE ("Right:  %d, ", dwg->header.section[i].right)
          LOG_TRACE ("0x00:   %d\n", dwg->header.section[i].x00)
        }

      i++;
    }
  i--;
  free (decomp);

  if (max_id != (int32_t)section_array_size)
    {
      LOG_WARN ("Invalid section_array_size: [%d].%d != %d",
                i, max_id, (int)section_array_size);
    }
  if (section_address
      != (uint64_t)dwg->r2004_header.last_section_address + 0x100)
    {
      error |= DWG_ERR_VALUEOUTOFBOUNDS;
      LOG_WARN ("Invalid last_section_address: %" PRIx64 " != %" PRIx64,
                section_address, dwg->r2004_header.last_section_address);
    }
  if (dwg->header.num_sections
      != dwg->r2004_header.num_gaps + dwg->r2004_header.num_sections)
    {
      error |= DWG_ERR_VALUEOUTOFBOUNDS;
      LOG_WARN ("Invalid num_sections: %d != gaps: " FORMAT_RL
                " + sects: " FORMAT_RL,
                dwg->header.num_sections, dwg->r2004_header.num_gaps,
                dwg->r2004_header.num_sections);
    }
  if (!found_section_map_id)
    {
      BITCODE_RLx section_type;
      Dwg_Section *info;
      LOG_WARN ("section_map_id " FORMAT_RL " not found", section_map_id);
      info = find_section (dwg, section_map_id);
      if (!info)
        {
          i = dwg->header.num_sections;
          add_section (dwg);
          LOG_WARN ("Add section_map_id [%d] %d => address 0x%" PRIx64, i,
                    section_map_id, section_map_address);
          error |= DWG_ERR_VALUEOUTOFBOUNDS;
          dwg->header.section[i].number = section_map_id;
          dwg->header.section[i].address = section_map_address;
          if (i > 0)
            dwg->header.section[i].size
              = section_map_address - dwg->header.section[i - 1].address;
        }
      info = find_section (dwg, dwg->r2004_header.section_info_id);
      if (!info)
        goto repair_info_id;
      dat->bit = 0;
      dat->byte = info->address;
      section_type = bit_read_RL (dat);
      if (section_type != 0x4163003b)
        {
        repair_info_id:
          LOG_WARN ("Repair invalid section_info_id [%d]: => %u",
                    i-1, dwg->r2004_header.section_info_id);
          error |= DWG_ERR_VALUEOUTOFBOUNDS;
          for (i = 0; i < (int)dwg->header.num_sections; ++i)
            {
              dat->bit = 0;
              dat->byte = dwg->header.section[i].address;
              if (dat->byte > dat->size)
                break;
              section_type = bit_read_RL (dat);
              if (section_type == 0x4163003b)
                {
                  LOG_WARN ("Fixed section_info_id [%d]: => %u @" FORMAT_RLL,
                            i, dwg->r2004_header.section_info_id,
                            dwg->header.section[i].address);
                  if (!info)
                    info = &dwg->header.section[i];
                  info->address = dwg->header.section[i].address;
                  info->size = dwg->header.section[i].size;
                  info->number = dwg->r2004_header.section_info_id;
                }
            }
          if (!info || info->number != (int)dwg->r2004_header.section_info_id)
            {
              i = dwg->header.num_sections;
              add_section (dwg);
              error |= DWG_ERR_VALUEOUTOFBOUNDS;
              dwg->header.section[i].number = dwg->r2004_header.section_info_id;
              dwg->header.section[i].address = dwg->header.section[i-1].address +
                dwg->header.section[i-1].size;
              LOG_WARN ("Add section_info_id [%d] %u => address 0x%" PRIx64, i,
                        dwg->r2004_header.section_info_id,
                        dwg->header.section[i].address);
            }
        }
    }
  if (found_section_map_id > 1)
    {
      error |= DWG_ERR_VALUEOUTOFBOUNDS;
      LOG_WARN ("Illegal 2004 Section Page Map. Found %d section_map_id sections",
                found_section_map_id)
    }

  return error;
}


dwg_encode_add_object (Dwg_Object *restrict obj, Bit_Chain *restrict dat,
                       unsigned long address)
{
  int error = 0;
  unsigned long oldpos;
  unsigned long end_address = address + obj->size;

  oldpos = bit_position (dat);
  assert (address);
  dat->byte = address;
  dat->bit = 0;

  LOG_INFO ("Object number: %lu", (unsigned long)obj->index);
  if (obj->size > 0x100000)
    {
      LOG_ERROR ("Object size %u overflow", obj->size);
      return DWG_ERR_VALUEOUTOFBOUNDS;
    }
  while (dat->byte + obj->size >= dat->size)
    bit_chain_alloc (dat);

  // TODO: calculate size from the fields. either <0x7fff or more
  // patch it afterwards and check old<>new size if enough space allocated.
  bit_write_MS (dat, obj->size);
  obj->address = dat->byte;
  PRE (R_2010) {
    bit_write_BS (dat, obj->type);
    LOG_INFO (", Size: %d [MS], Type: %d [BS]\n", obj->size, obj->type)
  }
  LATER_VERSIONS
  {
    if (!obj->handlestream_size && obj->bitsize)
      obj->handlestream_size = obj->size * 8 - obj->bitsize;
    bit_write_UMC (dat, obj->handlestream_size);
    obj->address = dat->byte;
    bit_write_BOT (dat, obj->type);
    LOG_INFO (", Size: %d [MS], Hdlsize: %lu [UMC], Type: %d [BOT]\n",
              obj->size, (unsigned long)obj->handlestream_size, obj->type)
  }

  /* Write the specific type to dat */
  switch (obj->type)
    {
    case DWG_TYPE_TEXT:
      error = dwg_encode_TEXT (dat, obj);
      break;
    case DWG_TYPE_ATTRIB:
      error = dwg_encode_ATTRIB (dat, obj);
      break;
    case DWG_TYPE_ATTDEF:
      error = dwg_encode_ATTDEF (dat, obj);
      break;
    case DWG_TYPE_BLOCK:
      error = dwg_encode_BLOCK (dat, obj);
      break;
    case DWG_TYPE_ENDBLK:
      error = dwg_encode_ENDBLK (dat, obj);
      break;
    case DWG_TYPE_SEQEND:
      error = dwg_encode_SEQEND (dat, obj);
      break;
    case DWG_TYPE_INSERT:
      error = dwg_encode_INSERT (dat, obj);
      break;
    case DWG_TYPE_MINSERT:
      error = dwg_encode_MINSERT (dat, obj);
      break;
    case DWG_TYPE_VERTEX_2D:
      error = dwg_encode_VERTEX_2D (dat, obj);
      break;
    case DWG_TYPE_VERTEX_3D:
      error = dwg_encode_VERTEX_3D (dat, obj);
      break;
    case DWG_TYPE_VERTEX_MESH:
      error = dwg_encode_VERTEX_MESH (dat, obj);
      break;
    case DWG_TYPE_VERTEX_PFACE:
      error = dwg_encode_VERTEX_PFACE (dat, obj);
      break;
    case DWG_TYPE_VERTEX_PFACE_FACE:
      error = dwg_encode_VERTEX_PFACE_FACE (dat, obj);
      break;
    case DWG_TYPE_POLYLINE_2D:
      error = dwg_encode_POLYLINE_2D (dat, obj);
      break;
    case DWG_TYPE_POLYLINE_3D:
      error = dwg_encode_POLYLINE_3D (dat, obj);
      break;
    case DWG_TYPE_ARC:
      error = dwg_encode_ARC (dat, obj);
      break;
    case DWG_TYPE_CIRCLE:
      error = dwg_encode_CIRCLE (dat, obj);
      break;
    case DWG_TYPE_LINE:
      error = dwg_encode_LINE (dat, obj);
      break;
    case DWG_TYPE_DIMENSION_ORDINATE:
      error = dwg_encode_DIMENSION_ORDINATE (dat, obj);
      break;
    case DWG_TYPE_DIMENSION_LINEAR:
      error = dwg_encode_DIMENSION_LINEAR (dat, obj);
      break;
    case DWG_TYPE_DIMENSION_ALIGNED:
      error = dwg_encode_DIMENSION_ALIGNED (dat, obj);
      break;
    case DWG_TYPE_DIMENSION_ANG3PT:
      error = dwg_encode_DIMENSION_ANG3PT (dat, obj);
      break;
    case DWG_TYPE_DIMENSION_ANG2LN:
      error = dwg_encode_DIMENSION_ANG2LN (dat, obj);
      break;
    case DWG_TYPE_DIMENSION_RADIUS:
      error = dwg_encode_DIMENSION_RADIUS (dat, obj);
      break;
    case DWG_TYPE_DIMENSION_DIAMETER:
      error = dwg_encode_DIMENSION_DIAMETER (dat, obj);
      break;
    case DWG_TYPE_POINT:
      error = dwg_encode_POINT (dat, obj);
      break;
    case DWG_TYPE__3DFACE:
      error = dwg_encode__3DFACE (dat, obj);
      break;
    case DWG_TYPE_POLYLINE_PFACE:
      error = dwg_encode_POLYLINE_PFACE (dat, obj);
      break;
    case DWG_TYPE_POLYLINE_MESH:
      error = dwg_encode_POLYLINE_MESH (dat, obj);
      break;
    case DWG_TYPE_SOLID:
      error = dwg_encode_SOLID (dat, obj);
      break;
    case DWG_TYPE_TRACE:
      error = dwg_encode_TRACE (dat, obj);
      break;
    case DWG_TYPE_SHAPE:
      error = dwg_encode_SHAPE (dat, obj);
      break;
    case DWG_TYPE_VIEWPORT:
      error = dwg_encode_VIEWPORT (dat, obj);
      break;
    case DWG_TYPE_ELLIPSE:
      error = dwg_encode_ELLIPSE (dat, obj);
      break;
    case DWG_TYPE_SPLINE:
      error = dwg_encode_SPLINE (dat, obj);
      break;
    case DWG_TYPE_REGION:
      error = dwg_encode_REGION (dat, obj);
      break;
    case DWG_TYPE__3DSOLID:
      error = dwg_encode__3DSOLID (dat, obj);
      break;
    case DWG_TYPE_BODY:
      error = dwg_encode_BODY (dat, obj);
      break;
    case DWG_TYPE_RAY:
      error = dwg_encode_RAY (dat, obj);
      break;
    case DWG_TYPE_XLINE:
      error = dwg_encode_XLINE (dat, obj);
      break;
    case DWG_TYPE_DICTIONARY:
      error = dwg_encode_DICTIONARY (dat, obj);
      break;
    case DWG_TYPE_MTEXT:
      error = dwg_encode_MTEXT (dat, obj);
      break;
    case DWG_TYPE_LEADER:
      error = dwg_encode_LEADER (dat, obj);
      break;
    case DWG_TYPE_TOLERANCE:
      error = dwg_encode_TOLERANCE (dat, obj);
      break;
    case DWG_TYPE_MLINE:
      error = dwg_encode_MLINE (dat, obj);
      break;
    case DWG_TYPE_BLOCK_CONTROL:
      error = dwg_encode_BLOCK_CONTROL (dat, obj);
      break;
    case DWG_TYPE_BLOCK_HEADER:
      error = dwg_encode_BLOCK_HEADER (dat, obj);
      break;
    case DWG_TYPE_LAYER_CONTROL:
      error = dwg_encode_LAYER_CONTROL (dat, obj);
      break;
    case DWG_TYPE_LAYER:
      error = dwg_encode_LAYER (dat, obj);
      break;
    case DWG_TYPE_STYLE_CONTROL:
      error = dwg_encode_STYLE_CONTROL (dat, obj);
      break;
    case DWG_TYPE_STYLE:
      error = dwg_encode_STYLE (dat, obj);
      break;
    case DWG_TYPE_LTYPE_CONTROL:
      error = dwg_encode_LTYPE_CONTROL (dat, obj);
      break;
    case DWG_TYPE_LTYPE:
      error = dwg_encode_LTYPE (dat, obj);
      break;
    case DWG_TYPE_VIEW_CONTROL:
      error = dwg_encode_VIEW_CONTROL (dat, obj);
      break;
    case DWG_TYPE_VIEW:
      error = dwg_encode_VIEW (dat, obj);
      break;
    case DWG_TYPE_UCS_CONTROL:
      error = dwg_encode_UCS_CONTROL (dat, obj);
      break;
    case DWG_TYPE_UCS:
      error = dwg_encode_UCS (dat, obj);
      break;
    case DWG_TYPE_VPORT_CONTROL:
      error = dwg_encode_VPORT_CONTROL (dat, obj);
      break;
    case DWG_TYPE_VPORT:
      error = dwg_encode_VPORT (dat, obj);
      break;
    case DWG_TYPE_APPID_CONTROL:
      error = dwg_encode_APPID_CONTROL (dat, obj);
      break;
    case DWG_TYPE_APPID:
      error = dwg_encode_APPID (dat, obj);
      break;
    case DWG_TYPE_DIMSTYLE_CONTROL:
      error = dwg_encode_DIMSTYLE_CONTROL (dat, obj);
      break;
    case DWG_TYPE_DIMSTYLE:
      error = dwg_encode_DIMSTYLE (dat, obj);
      break;
    case DWG_TYPE_VPORT_ENTITY_CONTROL:
      error = dwg_encode_VPORT_ENTITY_CONTROL (dat, obj);
      break;
    case DWG_TYPE_VPORT_ENTITY_HEADER:
      error = dwg_encode_VPORT_ENTITY_HEADER (dat, obj);
      break;
    case DWG_TYPE_GROUP:
      error = dwg_encode_GROUP (dat, obj);
      break;
    case DWG_TYPE_MLINESTYLE:
      error = dwg_encode_MLINESTYLE (dat, obj);
      (void)dwg_encode_get_class (obj->parent, obj);
      break;
    case DWG_TYPE_OLE2FRAME:
      error = dwg_encode_OLE2FRAME (dat, obj);
      (void)dwg_encode_get_class (obj->parent, obj);
      break;
    case DWG_TYPE_DUMMY:
      error = dwg_encode_DUMMY (dat, obj);
      break;
    case DWG_TYPE_LONG_TRANSACTION:
      error = dwg_encode_LONG_TRANSACTION (dat, obj);
      break;
    case DWG_TYPE_LWPOLYLINE:
      error = dwg_encode_LWPOLYLINE (dat, obj);
      (void)dwg_encode_get_class (obj->parent, obj);
      break;
    case DWG_TYPE_HATCH:
      error = dwg_encode_HATCH (dat, obj);
      (void)dwg_encode_get_class (obj->parent, obj);
      break;
    case DWG_TYPE_XRECORD:
      error = dwg_encode_XRECORD (dat, obj);
      (void)dwg_encode_get_class (obj->parent, obj);
      break;
    case DWG_TYPE_PLACEHOLDER:
      error = dwg_encode_PLACEHOLDER (dat, obj);
      (void)dwg_encode_get_class (obj->parent, obj);
      break;
    case DWG_TYPE_OLEFRAME:
      error = dwg_encode_OLEFRAME (dat, obj);
      (void)dwg_encode_get_class (obj->parent, obj);
      break;
    case DWG_TYPE_VBA_PROJECT:
      LOG_ERROR ("Unhandled Object VBA_PROJECT. Has its own section");
      // dwg_encode_VBA_PROJECT(dat, obj);
      break;
    case DWG_TYPE_LAYOUT:
      error |= dwg_encode_LAYOUT (dat, obj);
      (void)dwg_encode_get_class (obj->parent, obj);
      break;
    case DWG_TYPE_PROXY_ENTITY:
      error = dwg_encode_PROXY_ENTITY (dat, obj);
      break;
    case DWG_TYPE_PROXY_OBJECT:
      error = dwg_encode_PROXY_OBJECT (dat, obj);
      break;
    default:
      if (obj->type == obj->parent->layout_type
          && obj->fixedtype == DWG_TYPE_LAYOUT)
        {
          error = dwg_encode_LAYOUT (dat, obj);
          (void)dwg_encode_get_class (obj->parent, obj);
        }
      else if ((error = dwg_encode_variable_type (obj->parent, dat, obj))
               & DWG_ERR_UNHANDLEDCLASS)
        {
          Dwg_Data *dwg = obj->parent;
          int is_entity;
          int i = obj->type - 500;
          Dwg_Class *klass = dwg_encode_get_class (dwg, obj);

          assert (address);
          dat->byte = address; // restart and write into the UNKNOWN_OBJ object
          dat->bit = 0;
          bit_write_MS (dat, obj->size); // unknown blobs have a known size
          if (dat->version >= R_2010)
            {
              bit_write_UMC (dat, obj->handlestream_size);
              bit_write_BOT (dat, obj->type);
            }
          else
            bit_write_BS (dat, obj->type);

          if (klass && obj->supertype == DWG_SUPERTYPE_UNKNOWN)
            is_entity = dwg_class_is_entity (klass);
          else
            is_entity = obj->supertype == DWG_SUPERTYPE_ENTITY;
          // properly dwg_decode_object/_entity for eed, reactors, xdic
          if (is_entity)
            error = dwg_encode_UNKNOWN_ENT (dat, obj);
          else
            error = dwg_encode_UNKNOWN_OBJ (dat, obj);
        }
    }

  /* DXF: patchup size and bitsize */
  if (!obj->size)
    {
      BITCODE_BL pos = bit_position (dat);
      assert (address);
      obj->size = dat->byte - address - 2; // excludes the CRC
      if (dat->bit)
        obj->size++;
      //assert (obj->bitsize); // on errors
      if (!obj->bitsize)
        {
          LOG_TRACE ("-bitsize calc from address (no handle) @%lu.%u\n",
                     dat->byte, dat->bit);
          obj->bitsize = pos - (obj->address * 8);
        }
      bit_set_position (dat, address * 8);
      if (obj->size > 0x7fff)
        // TODO: with overlarge sizes >0x7fff memmove dat
        LOG_ERROR ("Unhandled size %u > 0x7fff", (unsigned)obj->size);
      bit_write_MS (dat, obj->size);
      LOG_TRACE ("-size: %u [MS] @%lu\n", obj->size, address);
      SINCE (R_2013)
      {
        if (!obj->handlestream_size && obj->bitsize)
          obj->handlestream_size = obj->size * 8 - obj->bitsize;
        bit_write_UMC (dat, obj->handlestream_size);
        LOG_TRACE ("-handlestream_size: %lu [UMC]\n", obj->handlestream_size);
      }
      SINCE (R_2000)
      {
        if (obj->bitsize_pos && obj->bitsize)
          {
            bit_set_position (dat, obj->bitsize_pos);
            bit_write_RL (dat, obj->bitsize);
            LOG_TRACE ("-bitsize: %u [RL] @%lu.%lu\n", obj->bitsize,
                       obj->bitsize_pos / 8, obj->bitsize_pos % 8);
          }
      }
      bit_set_position (dat, pos);
    }

  /*
   if (obj->supertype != DWG_SUPERTYPE_UNKNOWN)
   {
     fprintf (stderr, "Begin address:\t%10lu\n", address);
     fprintf (stderr, "Last address:\t%10lu\tSize: %10lu\n", dat->byte,
   obj->size); fprintf (stderr, "End address:\t%10lu (calculated)\n", address +
   2 + obj->size);
   }
   */

  /* Now 1 padding bits until next byte, and then a RS CRC */
  if (dat->bit)
    LOG_TRACE ("padding: +%d [*B]\n", 8 - dat->bit)
  while (dat->bit)
    bit_write_B (dat, 1);
  end_address = obj->address + obj->size;
  if (end_address != dat->byte)
    {
      if (obj->size)
        LOG_WARN ("Wrong object size: %lu + %u = %lu != %lu: %ld off", obj->address,
                  obj->size, end_address, dat->byte, (long)(end_address - dat->byte));
      dat->byte = end_address;
    }
  assert (!dat->bit);
  bit_write_CRC (dat, address, 0xC0C1);
  return error;
}


find_section (Dwg_Data *dwg, unsigned long int idx)
{
  BITCODE_BL i;
  if (dwg->header.section == 0 || idx == 0)
    return 0;
  for (i = 0; i < dwg->header.num_sections; ++i)
    {
      if ((unsigned long int)dwg->header.section[i].number == idx)
        return &dwg->header.section[i];
    }
  return NULL;
}


dwg_new_ref (Dwg_Data *restrict dwg)
{
  Dwg_Object_Ref *ref = (Dwg_Object_Ref *)calloc (1, sizeof (Dwg_Object_Ref));
  if (!ref)
    {
      LOG_ERROR ("Out of memory");
      return NULL;
    }
  if (dwg_decode_add_object_ref (dwg, ref))
    {
      free (ref);
      return NULL;
    }
  return ref;
}


decode_preR13_section_ptr (const char *restrict name, Dwg_Section_Type_r11 id,
                           Bit_Chain *restrict dat, Dwg_Data *restrict dwg)
{
  Dwg_Section *tbl = &dwg->header.section[id];
  tbl->size = bit_read_RS (dat);
  tbl->number = bit_read_RL (dat);
  tbl->address = bit_read_RL (dat);
  strncpy (tbl->name, name, 63);
  LOG_TRACE ("ptr table %-8s [%2d]: size:%-4u nr:%-2ld (0x%lx-0x%lx)\n",
             tbl->name, id, tbl->size, (long)tbl->number, (unsigned long)tbl->address,
             (unsigned long)(tbl->address + (unsigned long)(tbl->number * tbl->size)))
}


dwg_section_page_checksum (const uint32_t seed, Bit_Chain *restrict dat, uint32_t size)
{
  uint32_t sum1 = seed & 0xffff;
  uint32_t sum2 = seed >> 0x10;
  unsigned char *data = &(dat->chain[dat->byte]);

  while (size)
    {
      uint32_t i;
      uint32_t chunksize = size < 0x15b0 ? size : 0x15b0;
      size -= chunksize;
      for (i = 0; i < chunksize; i++)
        {
          sum1 += *data++;
          sum2 += sum1;
        }
      sum1 %= 0xFFF1;
      sum2 %= 0xFFF1;
    }
  return (sum2 << 0x10) | (sum1 & 0xffff);
}


read_2004_section_preview (Bit_Chain *restrict dat, Dwg_Data *restrict dwg)
{
  Bit_Chain sec_dat = { 0 };
  int error = 0;
  BITCODE_RL size;
  unsigned char *sentinel;

  // not compressed, num_sections: 1
  error = read_2004_compressed_section (dat, dwg, &sec_dat, SECTION_PREVIEW);
  if (error >= DWG_ERR_CRITICAL)
    {
      LOG_ERROR ("Failed to read uncompressed %s section", "Preview");
      if (sec_dat.chain)
        free (sec_dat.chain);
      return error;
    }

  if (dwg->header.thumbnail_address != (BITCODE_RL)dat->byte)
    LOG_WARN ("thumbnail_address mismatch: " FORMAT_RL " != %lu",
              dwg->header.thumbnail_address, dat->byte);
  LOG_TRACE ("Preview\n-------------------\n");
  if (!sec_dat.chain || sec_dat.size < 16)
    {
      LOG_WARN ("Empty thumbnail");
      if (sec_dat.chain)
        free (sec_dat.chain);
      return error;
    }
  sentinel = dwg_sentinel (DWG_SENTINEL_THUMBNAIL_BEGIN);
  if (memcmp (sentinel, sec_dat.chain, 16))
    {
      LOG_WARN ("thumbnail sentinel mismatch");
      free (sec_dat.chain);
      return error;
    }

  dwg->thumbnail.size = sec_dat.size - 32; // 2x sentinel
  dwg->thumbnail.chain = sec_dat.chain;
  dwg->thumbnail.byte = 16; // sentinel

  dwg_bmp (dwg, &size);
  if (abs ((int)((long)size - (long)dwg->thumbnail.size))
      > 200) // various headers
    LOG_WARN ("thumbnail.size mismatch: %lu != " FORMAT_RL,
              dwg->thumbnail.size, size);

  dat->byte += dwg->thumbnail.size;

  return error;
}


eed_need_size (BITCODE_BS need, BITCODE_BS have)
{
  if (need > have)
    {
      LOG_ERROR ("Invalid EED size %d > %d", (int)need, (int)have);
      return 1;
    }
  return 0;
}


dwg_encode_common_entity_handle_data (Bit_Chain *dat, Bit_Chain *hdl_dat,
                                      Dwg_Object *restrict obj)
{
  Dwg_Object_Entity *ent;
  // Dwg_Data *dwg = obj->parent;
  Dwg_Object_Entity *_obj;
  BITCODE_BL vcount;
  int error = 0;
  ent = obj->tio.entity;
  _obj = ent;

  // clang-format off
  #include "common_entity_handle_data.spec"
  // clang-format on

  return error;
}


dxf_encode_alias (char *restrict name)
{
  if (strEQc (name, "DICTIONARYWDFLT"))
    return "ACDBDICTIONARYWDFLT";
  else if (strEQc (name, "SECTIONVIEWSTYLE"))
    return "ACDBSECTIONVIEWSTYLE";
  else if (strEQc (name, "PLACEHOLDER"))
    return "ACDBPLACEHOLDER";
  else if (strEQc (name, "DETAILVIEWSTYLE"))
    return "ACDBDETAILVIEWSTYLE";
  else if (strEQc (name, "ASSOCPERSSUBENTMANAGER"))
    return "ACDBASSOCPERSSUBENTMANAGER";
  else if (strEQc (name, "EVALUATION_GRAPH"))
    return "ACAD_EVALUATION_GRAPH";
  else if (strEQc (name, "ASSOCACTION"))
    return "ACDBASSOCACTION";
  else if (strEQc (name, "ASSOCALIGNEDDIMACTIONBODY"))
    return "ACDBASSOCALIGNEDDIMACTIONBODY";
  else if (strEQc (name, "ASSOCOSNAPPOINTREFACTIONPARAM"))
    return "ACDBASSOCOSNAPPOINTREFACTIONPARAM";
  else if (strEQc (name, "ASSOCVERTEXACTIONPARAM"))
    return "ACDBASSOCVERTEXACTIONPARAM";
  else if (strEQc (name, "ASSOCGEOMDEPENDENCY"))
    return "ACDBASSOCGEOMDEPENDENCY";
  else if (strEQc (name, "ASSOCDEPENDENCY"))
    return "ACDBASSOCDEPENDENCY";
  else if (strEQc (name, "TABLE"))
    return "ACAD_TABLE";
  else
    return NULL;
}


dwg_free_xdata (Dwg_Object_XRECORD *obj, int size)
{
  dwg_free_xdata_resbuf (obj->xdata);
  obj->xdata = NULL;
}


static int handle_invvpid(struct kvm_vcpu *vcpu)
{
	struct vcpu_vmx *vmx = to_vmx(vcpu);
	u32 vmx_instruction_info;
	unsigned long type, types;
	gva_t gva;
	struct x86_exception e;
	int vpid;

	if (!(vmx->nested.nested_vmx_secondary_ctls_high &
	      SECONDARY_EXEC_ENABLE_VPID) ||
			!(vmx->nested.nested_vmx_vpid_caps & VMX_VPID_INVVPID_BIT)) {
		kvm_queue_exception(vcpu, UD_VECTOR);
		return 1;
	}

	if (!nested_vmx_check_permission(vcpu))
		return 1;

	vmx_instruction_info = vmcs_read32(VMX_INSTRUCTION_INFO);
	type = kvm_register_readl(vcpu, (vmx_instruction_info >> 28) & 0xf);

	types = (vmx->nested.nested_vmx_vpid_caps >> 8) & 0x7;

	if (!(types & (1UL << type))) {
		nested_vmx_failValid(vcpu,
			VMXERR_INVALID_OPERAND_TO_INVEPT_INVVPID);
		skip_emulated_instruction(vcpu);
		return 1;
	}

	/* according to the intel vmx instruction reference, the memory
	 * operand is read even if it isn't needed (e.g., for type==global)
	 */
	if (get_vmx_mem_address(vcpu, vmcs_readl(EXIT_QUALIFICATION),
			vmx_instruction_info, false, &gva))
		return 1;
	if (kvm_read_guest_virt(&vcpu->arch.emulate_ctxt, gva, &vpid,
				sizeof(u32), &e)) {
		kvm_inject_page_fault(vcpu, &e);
		return 1;
	}

	switch (type) {
	case VMX_VPID_EXTENT_SINGLE_CONTEXT:
		/*
		 * Old versions of KVM use the single-context version so we
		 * have to support it; just treat it the same as all-context.
		 */
	case VMX_VPID_EXTENT_ALL_CONTEXT:
		__vmx_flush_tlb(vcpu, to_vmx(vcpu)->nested.vpid02);
		nested_vmx_succeed(vcpu);
		break;
	default:
		/* Trap individual address invalidation invvpid calls */
		BUG_ON(1);
		break;
	}

	skip_emulated_instruction(vcpu);
	return 1;
}


static int nested_vmx_check_msr_switch(struct kvm_vcpu *vcpu,
				       unsigned long count_field,
				       unsigned long addr_field)
{
	int maxphyaddr;
	u64 count, addr;

	if (vmcs12_read_any(vcpu, count_field, &count) ||
	    vmcs12_read_any(vcpu, addr_field, &addr)) {
		WARN_ON(1);
		return -EINVAL;
	}
	if (count == 0)
		return 0;
	maxphyaddr = cpuid_maxphyaddr(vcpu);
	if (!IS_ALIGNED(addr, 16) || addr >> maxphyaddr ||
	    (addr + count * sizeof(struct vmx_msr_entry) - 1) >> maxphyaddr) {
		pr_warn_ratelimited(
			"nVMX: invalid MSR switch (0x%lx, %d, %llu, 0x%08llx)",
			addr_field, maxphyaddr, count, addr);
		return -EINVAL;
	}
	return 0;
}


static void vmx_set_virtual_x2apic_mode(struct kvm_vcpu *vcpu, bool set)
{
	u32 sec_exec_control;

	/*
	 * There is not point to enable virtualize x2apic without enable
	 * apicv
	 */
	if (!cpu_has_vmx_virtualize_x2apic_mode() ||
				!kvm_vcpu_apicv_active(vcpu))
		return;

	if (!cpu_need_tpr_shadow(vcpu))
		return;

	sec_exec_control = vmcs_read32(SECONDARY_VM_EXEC_CONTROL);

	if (set) {
		sec_exec_control &= ~SECONDARY_EXEC_VIRTUALIZE_APIC_ACCESSES;
		sec_exec_control |= SECONDARY_EXEC_VIRTUALIZE_X2APIC_MODE;
	} else {
		sec_exec_control &= ~SECONDARY_EXEC_VIRTUALIZE_X2APIC_MODE;
		sec_exec_control |= SECONDARY_EXEC_VIRTUALIZE_APIC_ACCESSES;
	}
	vmcs_write32(SECONDARY_VM_EXEC_CONTROL, sec_exec_control);

	vmx_set_msr_bitmap(vcpu);
}


static int vmx_get_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
{
	struct shared_msr_entry *msr;

	switch (msr_info->index) {
#ifdef CONFIG_X86_64
	case MSR_FS_BASE:
		msr_info->data = vmcs_readl(GUEST_FS_BASE);
		break;
	case MSR_GS_BASE:
		msr_info->data = vmcs_readl(GUEST_GS_BASE);
		break;
	case MSR_KERNEL_GS_BASE:
		vmx_load_host_state(to_vmx(vcpu));
		msr_info->data = to_vmx(vcpu)->msr_guest_kernel_gs_base;
		break;
#endif
	case MSR_EFER:
		return kvm_get_msr_common(vcpu, msr_info);
	case MSR_IA32_TSC:
		msr_info->data = guest_read_tsc(vcpu);
		break;
	case MSR_IA32_SYSENTER_CS:
		msr_info->data = vmcs_read32(GUEST_SYSENTER_CS);
		break;
	case MSR_IA32_SYSENTER_EIP:
		msr_info->data = vmcs_readl(GUEST_SYSENTER_EIP);
		break;
	case MSR_IA32_SYSENTER_ESP:
		msr_info->data = vmcs_readl(GUEST_SYSENTER_ESP);
		break;
	case MSR_IA32_BNDCFGS:
		if (!kvm_mpx_supported())
			return 1;
		msr_info->data = vmcs_read64(GUEST_BNDCFGS);
		break;
	case MSR_IA32_FEATURE_CONTROL:
		if (!nested_vmx_allowed(vcpu))
			return 1;
		msr_info->data = to_vmx(vcpu)->nested.msr_ia32_feature_control;
		break;
	case MSR_IA32_VMX_BASIC ... MSR_IA32_VMX_VMFUNC:
		if (!nested_vmx_allowed(vcpu))
			return 1;
		return vmx_get_vmx_msr(vcpu, msr_info->index, &msr_info->data);
	case MSR_IA32_XSS:
		if (!vmx_xsaves_supported())
			return 1;
		msr_info->data = vcpu->arch.ia32_xss;
		break;
	case MSR_TSC_AUX:
		if (!guest_cpuid_has_rdtscp(vcpu) && !msr_info->host_initiated)
			return 1;
		/* Otherwise falls through */
	default:
		msr = find_msr_entry(to_vmx(vcpu), msr_info->index);
		if (msr) {
			msr_info->data = msr->data;
			break;
		}
		return kvm_get_msr_common(vcpu, msr_info);
	}

	return 0;
}


static int handle_set_cr0(struct kvm_vcpu *vcpu, unsigned long val)
{
	if (is_guest_mode(vcpu)) {
		struct vmcs12 *vmcs12 = get_vmcs12(vcpu);
		unsigned long orig_val = val;

		/*
		 * We get here when L2 changed cr0 in a way that did not change
		 * any of L1's shadowed bits (see nested_vmx_exit_handled_cr),
		 * but did change L0 shadowed bits. So we first calculate the
		 * effective cr0 value that L1 would like to write into the
		 * hardware. It consists of the L2-owned bits from the new
		 * value combined with the L1-owned bits from L1's guest_cr0.
		 */
		val = (val & ~vmcs12->cr0_guest_host_mask) |
			(vmcs12->guest_cr0 & vmcs12->cr0_guest_host_mask);

		if (!nested_cr0_valid(vcpu, val))
			return 1;

		if (kvm_set_cr0(vcpu, val))
			return 1;
		vmcs_writel(CR0_READ_SHADOW, orig_val);
		return 0;
	} else {
		if (to_vmx(vcpu)->nested.vmxon &&
		    ((val & VMXON_CR0_ALWAYSON) != VMXON_CR0_ALWAYSON))
			return 1;
		return kvm_set_cr0(vcpu, val);
	}
}


static inline bool nested_vmx_merge_msr_bitmap(struct kvm_vcpu *vcpu,
					       struct vmcs12 *vmcs12)
{
	int msr;
	struct page *page;
	unsigned long *msr_bitmap;

	if (!nested_cpu_has_virt_x2apic_mode(vmcs12))
		return false;

	page = nested_get_page(vcpu, vmcs12->msr_bitmap);
	if (!page) {
		WARN_ON(1);
		return false;
	}
	msr_bitmap = (unsigned long *)kmap(page);
	if (!msr_bitmap) {
		nested_release_page_clean(page);
		WARN_ON(1);
		return false;
	}

	if (nested_cpu_has_virt_x2apic_mode(vmcs12)) {
		if (nested_cpu_has_apic_reg_virt(vmcs12))
			for (msr = 0x800; msr <= 0x8ff; msr++)
				nested_vmx_disable_intercept_for_msr(
					msr_bitmap,
					vmx_msr_bitmap_nested,
					msr, MSR_TYPE_R);
		/* TPR is allowed */
		nested_vmx_disable_intercept_for_msr(msr_bitmap,
				vmx_msr_bitmap_nested,
				APIC_BASE_MSR + (APIC_TASKPRI >> 4),
				MSR_TYPE_R | MSR_TYPE_W);
		if (nested_cpu_has_vid(vmcs12)) {
			/* EOI and self-IPI are allowed */
			nested_vmx_disable_intercept_for_msr(
				msr_bitmap,
				vmx_msr_bitmap_nested,
				APIC_BASE_MSR + (APIC_EOI >> 4),
				MSR_TYPE_W);
			nested_vmx_disable_intercept_for_msr(
				msr_bitmap,
				vmx_msr_bitmap_nested,
				APIC_BASE_MSR + (APIC_SELF_IPI >> 4),
				MSR_TYPE_W);
		}
	} else {
		/*
		 * Enable reading intercept of all the x2apic
		 * MSRs. We should not rely on vmcs12 to do any
		 * optimizations here, it may have been modified
		 * by L1.
		 */
		for (msr = 0x800; msr <= 0x8ff; msr++)
			__vmx_enable_intercept_for_msr(
				vmx_msr_bitmap_nested,
				msr,
				MSR_TYPE_R);

		__vmx_enable_intercept_for_msr(
				vmx_msr_bitmap_nested,
				APIC_BASE_MSR + (APIC_TASKPRI >> 4),
				MSR_TYPE_W);
		__vmx_enable_intercept_for_msr(
				vmx_msr_bitmap_nested,
				APIC_BASE_MSR + (APIC_EOI >> 4),
				MSR_TYPE_W);
		__vmx_enable_intercept_for_msr(
				vmx_msr_bitmap_nested,
				APIC_BASE_MSR + (APIC_SELF_IPI >> 4),
				MSR_TYPE_W);
	}
	kunmap(page);
	nested_release_page_clean(page);

	return true;
}