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

#include "iceoryx_hoofs/posix_wrapper/named_pipe.hpp"
#include "iceoryx_hoofs/cxx/helplets.hpp"
#include "iceoryx_hoofs/posix_wrapper/posix_call.hpp"

#include <thread>

namespace iox
{
namespace posix
{
constexpr const char NamedPipe::NAMED_PIPE_PREFIX[];
constexpr const char NamedPipe::SEND_SEMAPHORE_PREFIX[];
constexpr const char NamedPipe::RECEIVE_SEMAPHORE_PREFIX[];
constexpr units::Duration NamedPipe::CYCLE_TIME;
constexpr units::Duration NamedPipe::NamedPipeData::WAIT_FOR_INIT_SLEEP_TIME;

NamedPipe::NamedPipe() noexcept
{
    this->m_isInitialized = false;
    this->m_errorValue = IpcChannelError::NOT_INITIALIZED;
}

NamedPipe::NamedPipe(const IpcChannelName_t& name,
                     const IpcChannelSide channelSide,
                     const size_t maxMsgSize,
                     const uint64_t maxMsgNumber) noexcept
{
    // We do not store maxMsgSize or maxMsgNumber, this is just technical debt since every ipc channel
    // requires the same behavior as the message queue. The named pipe would get later two template
    // parameters MAX_MSG_SIZE and MAX_MSG_NUMBER from which the Message_t size and m_messages queue
    // size is obtained. Reducing the max message size / number of messages even further would not gain
    // reduced memory usage or decreased runtime. See issue #832.
    if (name.size() + strlen(NAMED_PIPE_PREFIX) > MAX_MESSAGE_SIZE)
    {
        std::cerr << "The named pipe name: \"" << name
                  << "\" is too long. Maxium name length is: " << MAX_MESSAGE_SIZE - strlen(NAMED_PIPE_PREFIX)
                  << std::endl;
        m_isInitialized = false;
        m_errorValue = IpcChannelError::INVALID_CHANNEL_NAME;
        return;
    }

    // leading slash is allowed even though it is not a valid file name
    bool isValidPipeName = cxx::isValidFileName(name)
                           || (!name.empty() && name.c_str()[0] == '/' && cxx::isValidFileName(*name.substr(1)));
    if (!isValidPipeName)
    {
        std::cerr << "The named pipe name: \"" << name << "\" is not a valid file path name." << std::endl;
        m_isInitialized = false;
        m_errorValue = IpcChannelError::INVALID_CHANNEL_NAME;
        return;
    }

    if (maxMsgSize > MAX_MESSAGE_SIZE)
    {
        std::cerr << "A message size of " << maxMsgSize << " exceeds the maximum message size for named pipes of "
                  << MAX_MESSAGE_SIZE << std::endl;
        m_isInitialized = false;
        m_errorValue = IpcChannelError::MAX_MESSAGE_SIZE_EXCEEDED;
        return;
    }

    if (maxMsgNumber > MAX_NUMBER_OF_MESSAGES)
    {
        std::cerr << "A message amount of " << maxMsgNumber
                  << " exceeds the maximum number of messages for named pipes of " << MAX_NUMBER_OF_MESSAGES
                  << std::endl;
        m_isInitialized = false;
        m_errorValue = IpcChannelError::MAX_MESSAGE_SIZE_EXCEEDED;
        return;
    }

    auto sharedMemory = SharedMemoryObject::create(
        convertName(NAMED_PIPE_PREFIX, name),
        // add alignment since we require later aligned memory to perform the placement new of
        // m_messages. when we add the alignment it is guaranteed that enough memory should be available.
        sizeof(NamedPipeData) + alignof(NamedPipeData),
        AccessMode::READ_WRITE,
        (channelSide == IpcChannelSide::SERVER) ? OpenMode::OPEN_OR_CREATE : OpenMode::OPEN_EXISTING,
        iox::posix::SharedMemoryObject::NO_ADDRESS_HINT);

    if (sharedMemory.has_error())
    {
        std::cerr << "Unable to open shared memory: \"" << convertName(NAMED_PIPE_PREFIX, name)
                  << "\" for named pipe \"" << name << "\"" << std::endl;
        m_isInitialized = false;
        m_errorValue = (channelSide == IpcChannelSide::CLIENT) ? IpcChannelError::NO_SUCH_CHANNEL
                                                               : IpcChannelError::INTERNAL_LOGIC_ERROR;
        return;
    }

    m_sharedMemory.emplace(std::move(*sharedMemory));

    m_data = static_cast<NamedPipeData*>(m_sharedMemory->allocate(sizeof(NamedPipeData), alignof(NamedPipeData)));

    m_isInitialized = true;
    if (m_sharedMemory->hasOwnership())
    {
        new (m_data) NamedPipeData(m_isInitialized, m_errorValue, maxMsgNumber);
    }
    else
    {
        m_isInitialized = m_data->waitForInitialization();
        if (m_isInitialized == false)
        {
            m_errorValue = IpcChannelError::INTERNAL_LOGIC_ERROR;
        }
    }
}

NamedPipe::NamedPipe(NamedPipe&& rhs) noexcept
{
    *this = std::move(rhs);
}

NamedPipe& NamedPipe::operator=(NamedPipe&& rhs) noexcept
{
    if (this != &rhs)
    {
        IOX_DISCARD_RESULT(destroy());
        CreationPattern_t::operator=(std::move(rhs));

        m_sharedMemory = std::move(rhs.m_sharedMemory);
        m_data = std::move(rhs.m_data);
        rhs.m_data = nullptr;
    }

    return *this;
}

NamedPipe::~NamedPipe() noexcept
{
    IOX_DISCARD_RESULT(destroy());
}

template <typename Prefix>
IpcChannelName_t NamedPipe::convertName(const Prefix& p, const IpcChannelName_t& name) noexcept
{
    return IpcChannelName_t(cxx::TruncateToCapacity,
                            cxx::concatenate(p, (name.c_str()[0] == '/') ? *name.substr(1) : name).c_str());
}

cxx::expected<IpcChannelError> NamedPipe::destroy() noexcept
{
    if (m_isInitialized)
    {
        m_isInitialized = false;
        m_errorValue = IpcChannelError::NOT_INITIALIZED;
        m_data->~NamedPipeData();
        m_sharedMemory.reset();
        m_data = nullptr;
    }
    return cxx::success<>();
}

cxx::expected<bool, IpcChannelError> NamedPipe::isOutdated() noexcept
{
    return cxx::success<bool>(false);
}

cxx::expected<bool, IpcChannelError> NamedPipe::unlinkIfExists(const IpcChannelName_t& name) noexcept
{
    constexpr int ERROR_CODE = -1;
    auto unlinkCall = posixCall(iox_shm_unlink)(convertName(NAMED_PIPE_PREFIX, name).c_str())
                          .failureReturnValue(ERROR_CODE)
                          .ignoreErrnos(ENOENT)
                          .evaluate();
    if (!unlinkCall.has_error())
    {
        return cxx::success<bool>(unlinkCall->errnum != ENOENT);
    }

    return cxx::error<IpcChannelError>(IpcChannelError::INTERNAL_LOGIC_ERROR);
}

cxx::expected<IpcChannelError> NamedPipe::trySend(const std::string& message) const noexcept
{
    if (!m_isInitialized)
    {
        return cxx::error<IpcChannelError>(IpcChannelError::NOT_INITIALIZED);
    }

    if (message.size() > MAX_MESSAGE_SIZE)
    {
        return cxx::error<IpcChannelError>(IpcChannelError::MESSAGE_TOO_LONG);
    }

    auto result = m_data->sendSemaphore().tryWait();
    cxx::Expects(!result.has_error());

    if (*result)
    {
        IOX_DISCARD_RESULT(m_data->messages.push(Message_t(cxx::TruncateToCapacity, message)));
        cxx::Expects(!m_data->receiveSemaphore().post().has_error());
        return cxx::success<>();
    }
    return cxx::error<IpcChannelError>(IpcChannelError::TIMEOUT);
}

cxx::expected<IpcChannelError> NamedPipe::send(const std::string& message) const noexcept
{
    if (!m_isInitialized)
    {
        return cxx::error<IpcChannelError>(IpcChannelError::NOT_INITIALIZED);
    }

    if (message.size() > MAX_MESSAGE_SIZE)
    {
        return cxx::error<IpcChannelError>(IpcChannelError::MESSAGE_TOO_LONG);
    }

    cxx::Expects(!m_data->sendSemaphore().wait().has_error());
    IOX_DISCARD_RESULT(m_data->messages.push(Message_t(cxx::TruncateToCapacity, message)));
    cxx::Expects(!m_data->receiveSemaphore().post().has_error());

    return cxx::success<>();
}

cxx::expected<IpcChannelError> NamedPipe::timedSend(const std::string& message,
                                                    const units::Duration& timeout) const noexcept
{
    if (!m_isInitialized)
    {
        return cxx::error<IpcChannelError>(IpcChannelError::NOT_INITIALIZED);
    }

    if (message.size() > MAX_MESSAGE_SIZE)
    {
        return cxx::error<IpcChannelError>(IpcChannelError::MESSAGE_TOO_LONG);
    }

    auto result = m_data->sendSemaphore().timedWait(timeout);
    cxx::Expects(!result.has_error());

    if (*result == SemaphoreWaitState::NO_TIMEOUT)
    {
        IOX_DISCARD_RESULT(m_data->messages.push(Message_t(cxx::TruncateToCapacity, message)));
        cxx::Expects(!m_data->receiveSemaphore().post().has_error());
        return cxx::success<>();
    }
    return cxx::error<IpcChannelError>(IpcChannelError::TIMEOUT);
}

cxx::expected<std::string, IpcChannelError> NamedPipe::receive() const noexcept
{
    if (!m_isInitialized)
    {
        return cxx::error<IpcChannelError>(IpcChannelError::NOT_INITIALIZED);
    }

    cxx::Expects(!m_data->receiveSemaphore().wait().has_error());
    auto message = m_data->messages.pop();
    if (message.has_value())
    {
        cxx::Expects(!m_data->sendSemaphore().post().has_error());
        return cxx::success<std::string>(message->c_str());
    }
    return cxx::error<IpcChannelError>(IpcChannelError::INTERNAL_LOGIC_ERROR);
}

cxx::expected<std::string, IpcChannelError> NamedPipe::tryReceive() const noexcept
{
    if (!m_isInitialized)
    {
        return cxx::error<IpcChannelError>(IpcChannelError::NOT_INITIALIZED);
    }

    auto result = m_data->receiveSemaphore().tryWait();
    cxx::Expects(!result.has_error());

    if (*result)
    {
        auto message = m_data->messages.pop();
        if (message.has_value())
        {
            cxx::Expects(!m_data->sendSemaphore().post().has_error());
            return cxx::success<std::string>(message->c_str());
        }
        return cxx::error<IpcChannelError>(IpcChannelError::INTERNAL_LOGIC_ERROR);
    }

    return cxx::error<IpcChannelError>(IpcChannelError::TIMEOUT);
}

cxx::expected<std::string, IpcChannelError> NamedPipe::timedReceive(const units::Duration& timeout) const noexcept
{
    if (!m_isInitialized)
    {
        return cxx::error<IpcChannelError>(IpcChannelError::NOT_INITIALIZED);
    }

    auto result = m_data->receiveSemaphore().timedWait(timeout);
    cxx::Expects(!result.has_error());

    if (*result == SemaphoreWaitState::NO_TIMEOUT)
    {
        auto message = m_data->messages.pop();
        if (message.has_value())
        {
            cxx::Expects(!m_data->sendSemaphore().post().has_error());
            return cxx::success<std::string>(message->c_str());
        }
        return cxx::error<IpcChannelError>(IpcChannelError::INTERNAL_LOGIC_ERROR);
    }
    return cxx::error<IpcChannelError>(IpcChannelError::TIMEOUT);
}

NamedPipe::NamedPipeData::NamedPipeData(bool& isInitialized,
                                        IpcChannelError& error,
                                        const uint64_t maxMsgNumber) noexcept
{
    auto signalError = [&](const char* name) {
        std::cerr << "Unable to create " << name << " semaphore for named pipe \"" << 'x' << "\"";
        isInitialized = false;
        error = IpcChannelError::INTERNAL_LOGIC_ERROR;
    };

    Semaphore::placementCreate(
        &semaphores[SEND_SEMAPHORE], CreateUnnamedSharedMemorySemaphore, static_cast<unsigned int>(maxMsgNumber))
        .or_else([&](auto) { signalError("send"); });

    if (!isInitialized)
    {
        return;
    }

    Semaphore::placementCreate(&semaphores[RECEIVE_SEMAPHORE], CreateUnnamedSharedMemorySemaphore, 0U)
        .or_else([&](auto) { signalError("receive"); });

    if (!isInitialized)
    {
        return;
    }

    initializationGuard.store(VALID_DATA);
}

NamedPipe::NamedPipeData::~NamedPipeData() noexcept
{
    if (hasValidState())
    {
        sendSemaphore().~Semaphore();
        receiveSemaphore().~Semaphore();
    }
}

Semaphore& NamedPipe::NamedPipeData::sendSemaphore() noexcept
{
    return reinterpret_cast<Semaphore&>(semaphores[SEND_SEMAPHORE]);
}

Semaphore& NamedPipe::NamedPipeData::receiveSemaphore() noexcept
{
    return reinterpret_cast<Semaphore&>(semaphores[RECEIVE_SEMAPHORE]);
}

bool NamedPipe::NamedPipeData::waitForInitialization() const noexcept
{
    if (hasValidState())
    {
        return true;
    }

    units::Duration remainingTime = WAIT_FOR_INIT_TIMEOUT;

    while (remainingTime.toNanoseconds() > 0U)
    {
        std::this_thread::sleep_for(std::chrono::nanoseconds(WAIT_FOR_INIT_SLEEP_TIME.toNanoseconds()));
        remainingTime = remainingTime - WAIT_FOR_INIT_SLEEP_TIME;
        if (hasValidState())
        {
            return true;
        }
    }

    return false;
}

bool NamedPipe::NamedPipeData::hasValidState() const noexcept
{
    return initializationGuard.load() == VALID_DATA;
}

} // namespace posix
} // namespace iox
<commit_msg>iox-#33 Fix clang linker issue with static constexpr member<commit_after>// Copyright (c) 2021 by Apex.AI Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0

#include "iceoryx_hoofs/posix_wrapper/named_pipe.hpp"
#include "iceoryx_hoofs/cxx/helplets.hpp"
#include "iceoryx_hoofs/posix_wrapper/posix_call.hpp"

#include <thread>

namespace iox
{
namespace posix
{
constexpr const char NamedPipe::NAMED_PIPE_PREFIX[];
constexpr const char NamedPipe::SEND_SEMAPHORE_PREFIX[];
constexpr const char NamedPipe::RECEIVE_SEMAPHORE_PREFIX[];
constexpr units::Duration NamedPipe::CYCLE_TIME;
constexpr units::Duration NamedPipe::NamedPipeData::WAIT_FOR_INIT_SLEEP_TIME;
constexpr units::Duration NamedPipe::NamedPipeData::WAIT_FOR_INIT_TIMEOUT;

NamedPipe::NamedPipe() noexcept
{
    this->m_isInitialized = false;
    this->m_errorValue = IpcChannelError::NOT_INITIALIZED;
}

NamedPipe::NamedPipe(const IpcChannelName_t& name,
                     const IpcChannelSide channelSide,
                     const size_t maxMsgSize,
                     const uint64_t maxMsgNumber) noexcept
{
    // We do not store maxMsgSize or maxMsgNumber, this is just technical debt since every ipc channel
    // requires the same behavior as the message queue. The named pipe would get later two template
    // parameters MAX_MSG_SIZE and MAX_MSG_NUMBER from which the Message_t size and m_messages queue
    // size is obtained. Reducing the max message size / number of messages even further would not gain
    // reduced memory usage or decreased runtime. See issue #832.
    if (name.size() + strlen(NAMED_PIPE_PREFIX) > MAX_MESSAGE_SIZE)
    {
        std::cerr << "The named pipe name: \"" << name
                  << "\" is too long. Maxium name length is: " << MAX_MESSAGE_SIZE - strlen(NAMED_PIPE_PREFIX)
                  << std::endl;
        m_isInitialized = false;
        m_errorValue = IpcChannelError::INVALID_CHANNEL_NAME;
        return;
    }

    // leading slash is allowed even though it is not a valid file name
    bool isValidPipeName = cxx::isValidFileName(name)
                           || (!name.empty() && name.c_str()[0] == '/' && cxx::isValidFileName(*name.substr(1)));
    if (!isValidPipeName)
    {
        std::cerr << "The named pipe name: \"" << name << "\" is not a valid file path name." << std::endl;
        m_isInitialized = false;
        m_errorValue = IpcChannelError::INVALID_CHANNEL_NAME;
        return;
    }

    if (maxMsgSize > MAX_MESSAGE_SIZE)
    {
        std::cerr << "A message size of " << maxMsgSize << " exceeds the maximum message size for named pipes of "
                  << MAX_MESSAGE_SIZE << std::endl;
        m_isInitialized = false;
        m_errorValue = IpcChannelError::MAX_MESSAGE_SIZE_EXCEEDED;
        return;
    }

    if (maxMsgNumber > MAX_NUMBER_OF_MESSAGES)
    {
        std::cerr << "A message amount of " << maxMsgNumber
                  << " exceeds the maximum number of messages for named pipes of " << MAX_NUMBER_OF_MESSAGES
                  << std::endl;
        m_isInitialized = false;
        m_errorValue = IpcChannelError::MAX_MESSAGE_SIZE_EXCEEDED;
        return;
    }

    auto sharedMemory = SharedMemoryObject::create(
        convertName(NAMED_PIPE_PREFIX, name),
        // add alignment since we require later aligned memory to perform the placement new of
        // m_messages. when we add the alignment it is guaranteed that enough memory should be available.
        sizeof(NamedPipeData) + alignof(NamedPipeData),
        AccessMode::READ_WRITE,
        (channelSide == IpcChannelSide::SERVER) ? OpenMode::OPEN_OR_CREATE : OpenMode::OPEN_EXISTING,
        iox::posix::SharedMemoryObject::NO_ADDRESS_HINT);

    if (sharedMemory.has_error())
    {
        std::cerr << "Unable to open shared memory: \"" << convertName(NAMED_PIPE_PREFIX, name)
                  << "\" for named pipe \"" << name << "\"" << std::endl;
        m_isInitialized = false;
        m_errorValue = (channelSide == IpcChannelSide::CLIENT) ? IpcChannelError::NO_SUCH_CHANNEL
                                                               : IpcChannelError::INTERNAL_LOGIC_ERROR;
        return;
    }

    m_sharedMemory.emplace(std::move(*sharedMemory));

    m_data = static_cast<NamedPipeData*>(m_sharedMemory->allocate(sizeof(NamedPipeData), alignof(NamedPipeData)));

    m_isInitialized = true;
    if (m_sharedMemory->hasOwnership())
    {
        new (m_data) NamedPipeData(m_isInitialized, m_errorValue, maxMsgNumber);
    }
    else
    {
        m_isInitialized = m_data->waitForInitialization();
        if (m_isInitialized == false)
        {
            m_errorValue = IpcChannelError::INTERNAL_LOGIC_ERROR;
        }
    }
}

NamedPipe::NamedPipe(NamedPipe&& rhs) noexcept
{
    *this = std::move(rhs);
}

NamedPipe& NamedPipe::operator=(NamedPipe&& rhs) noexcept
{
    if (this != &rhs)
    {
        IOX_DISCARD_RESULT(destroy());
        CreationPattern_t::operator=(std::move(rhs));

        m_sharedMemory = std::move(rhs.m_sharedMemory);
        m_data = std::move(rhs.m_data);
        rhs.m_data = nullptr;
    }

    return *this;
}

NamedPipe::~NamedPipe() noexcept
{
    IOX_DISCARD_RESULT(destroy());
}

template <typename Prefix>
IpcChannelName_t NamedPipe::convertName(const Prefix& p, const IpcChannelName_t& name) noexcept
{
    return IpcChannelName_t(cxx::TruncateToCapacity,
                            cxx::concatenate(p, (name.c_str()[0] == '/') ? *name.substr(1) : name).c_str());
}

cxx::expected<IpcChannelError> NamedPipe::destroy() noexcept
{
    if (m_isInitialized)
    {
        m_isInitialized = false;
        m_errorValue = IpcChannelError::NOT_INITIALIZED;
        m_data->~NamedPipeData();
        m_sharedMemory.reset();
        m_data = nullptr;
    }
    return cxx::success<>();
}

cxx::expected<bool, IpcChannelError> NamedPipe::isOutdated() noexcept
{
    return cxx::success<bool>(false);
}

cxx::expected<bool, IpcChannelError> NamedPipe::unlinkIfExists(const IpcChannelName_t& name) noexcept
{
    constexpr int ERROR_CODE = -1;
    auto unlinkCall = posixCall(iox_shm_unlink)(convertName(NAMED_PIPE_PREFIX, name).c_str())
                          .failureReturnValue(ERROR_CODE)
                          .ignoreErrnos(ENOENT)
                          .evaluate();
    if (!unlinkCall.has_error())
    {
        return cxx::success<bool>(unlinkCall->errnum != ENOENT);
    }

    return cxx::error<IpcChannelError>(IpcChannelError::INTERNAL_LOGIC_ERROR);
}

cxx::expected<IpcChannelError> NamedPipe::trySend(const std::string& message) const noexcept
{
    if (!m_isInitialized)
    {
        return cxx::error<IpcChannelError>(IpcChannelError::NOT_INITIALIZED);
    }

    if (message.size() > MAX_MESSAGE_SIZE)
    {
        return cxx::error<IpcChannelError>(IpcChannelError::MESSAGE_TOO_LONG);
    }

    auto result = m_data->sendSemaphore().tryWait();
    cxx::Expects(!result.has_error());

    if (*result)
    {
        IOX_DISCARD_RESULT(m_data->messages.push(Message_t(cxx::TruncateToCapacity, message)));
        cxx::Expects(!m_data->receiveSemaphore().post().has_error());
        return cxx::success<>();
    }
    return cxx::error<IpcChannelError>(IpcChannelError::TIMEOUT);
}

cxx::expected<IpcChannelError> NamedPipe::send(const std::string& message) const noexcept
{
    if (!m_isInitialized)
    {
        return cxx::error<IpcChannelError>(IpcChannelError::NOT_INITIALIZED);
    }

    if (message.size() > MAX_MESSAGE_SIZE)
    {
        return cxx::error<IpcChannelError>(IpcChannelError::MESSAGE_TOO_LONG);
    }

    cxx::Expects(!m_data->sendSemaphore().wait().has_error());
    IOX_DISCARD_RESULT(m_data->messages.push(Message_t(cxx::TruncateToCapacity, message)));
    cxx::Expects(!m_data->receiveSemaphore().post().has_error());

    return cxx::success<>();
}

cxx::expected<IpcChannelError> NamedPipe::timedSend(const std::string& message,
                                                    const units::Duration& timeout) const noexcept
{
    if (!m_isInitialized)
    {
        return cxx::error<IpcChannelError>(IpcChannelError::NOT_INITIALIZED);
    }

    if (message.size() > MAX_MESSAGE_SIZE)
    {
        return cxx::error<IpcChannelError>(IpcChannelError::MESSAGE_TOO_LONG);
    }

    auto result = m_data->sendSemaphore().timedWait(timeout);
    cxx::Expects(!result.has_error());

    if (*result == SemaphoreWaitState::NO_TIMEOUT)
    {
        IOX_DISCARD_RESULT(m_data->messages.push(Message_t(cxx::TruncateToCapacity, message)));
        cxx::Expects(!m_data->receiveSemaphore().post().has_error());
        return cxx::success<>();
    }
    return cxx::error<IpcChannelError>(IpcChannelError::TIMEOUT);
}

cxx::expected<std::string, IpcChannelError> NamedPipe::receive() const noexcept
{
    if (!m_isInitialized)
    {
        return cxx::error<IpcChannelError>(IpcChannelError::NOT_INITIALIZED);
    }

    cxx::Expects(!m_data->receiveSemaphore().wait().has_error());
    auto message = m_data->messages.pop();
    if (message.has_value())
    {
        cxx::Expects(!m_data->sendSemaphore().post().has_error());
        return cxx::success<std::string>(message->c_str());
    }
    return cxx::error<IpcChannelError>(IpcChannelError::INTERNAL_LOGIC_ERROR);
}

cxx::expected<std::string, IpcChannelError> NamedPipe::tryReceive() const noexcept
{
    if (!m_isInitialized)
    {
        return cxx::error<IpcChannelError>(IpcChannelError::NOT_INITIALIZED);
    }

    auto result = m_data->receiveSemaphore().tryWait();
    cxx::Expects(!result.has_error());

    if (*result)
    {
        auto message = m_data->messages.pop();
        if (message.has_value())
        {
            cxx::Expects(!m_data->sendSemaphore().post().has_error());
            return cxx::success<std::string>(message->c_str());
        }
        return cxx::error<IpcChannelError>(IpcChannelError::INTERNAL_LOGIC_ERROR);
    }

    return cxx::error<IpcChannelError>(IpcChannelError::TIMEOUT);
}

cxx::expected<std::string, IpcChannelError> NamedPipe::timedReceive(const units::Duration& timeout) const noexcept
{
    if (!m_isInitialized)
    {
        return cxx::error<IpcChannelError>(IpcChannelError::NOT_INITIALIZED);
    }

    auto result = m_data->receiveSemaphore().timedWait(timeout);
    cxx::Expects(!result.has_error());

    if (*result == SemaphoreWaitState::NO_TIMEOUT)
    {
        auto message = m_data->messages.pop();
        if (message.has_value())
        {
            cxx::Expects(!m_data->sendSemaphore().post().has_error());
            return cxx::success<std::string>(message->c_str());
        }
        return cxx::error<IpcChannelError>(IpcChannelError::INTERNAL_LOGIC_ERROR);
    }
    return cxx::error<IpcChannelError>(IpcChannelError::TIMEOUT);
}

NamedPipe::NamedPipeData::NamedPipeData(bool& isInitialized,
                                        IpcChannelError& error,
                                        const uint64_t maxMsgNumber) noexcept
{
    auto signalError = [&](const char* name) {
        std::cerr << "Unable to create " << name << " semaphore for named pipe \"" << 'x' << "\"";
        isInitialized = false;
        error = IpcChannelError::INTERNAL_LOGIC_ERROR;
    };

    Semaphore::placementCreate(
        &semaphores[SEND_SEMAPHORE], CreateUnnamedSharedMemorySemaphore, static_cast<unsigned int>(maxMsgNumber))
        .or_else([&](auto) { signalError("send"); });

    if (!isInitialized)
    {
        return;
    }

    Semaphore::placementCreate(&semaphores[RECEIVE_SEMAPHORE], CreateUnnamedSharedMemorySemaphore, 0U)
        .or_else([&](auto) { signalError("receive"); });

    if (!isInitialized)
    {
        return;
    }

    initializationGuard.store(VALID_DATA);
}

NamedPipe::NamedPipeData::~NamedPipeData() noexcept
{
    if (hasValidState())
    {
        sendSemaphore().~Semaphore();
        receiveSemaphore().~Semaphore();
    }
}

Semaphore& NamedPipe::NamedPipeData::sendSemaphore() noexcept
{
    return reinterpret_cast<Semaphore&>(semaphores[SEND_SEMAPHORE]);
}

Semaphore& NamedPipe::NamedPipeData::receiveSemaphore() noexcept
{
    return reinterpret_cast<Semaphore&>(semaphores[RECEIVE_SEMAPHORE]);
}

bool NamedPipe::NamedPipeData::waitForInitialization() const noexcept
{
    if (hasValidState())
    {
        return true;
    }

    units::Duration remainingTime = WAIT_FOR_INIT_TIMEOUT;

    while (remainingTime.toNanoseconds() > 0U)
    {
        std::this_thread::sleep_for(std::chrono::nanoseconds(WAIT_FOR_INIT_SLEEP_TIME.toNanoseconds()));
        remainingTime = remainingTime - WAIT_FOR_INIT_SLEEP_TIME;
        if (hasValidState())
        {
            return true;
        }
    }

    return false;
}

bool NamedPipe::NamedPipeData::hasValidState() const noexcept
{
    return initializationGuard.load() == VALID_DATA;
}

} // namespace posix
} // namespace iox
<|endoftext|>


<commit_before>//
//  Set.h
//  Set
//
//  Created by Antonio Seprano on 10/08/14.
//  Copyright (c) 2014 Antonio Seprano. All rights reserved.
//

#include <vector>
#include <functional>
#include <utility>
#include <algorithm>

#ifndef _SET_HPP
#define _SET_HPP

template<class _T, class _Allocator = std::allocator<_T> >
class Set {
public:
	typedef _T				  value_type;
	typedef _Allocator	allocator_type;

private:
	typedef Set<value_type,allocator_type> _SET;

public:
	typedef typename std::vector<value_type,allocator_type>::iterator				iterator;
	typedef typename std::vector<value_type,allocator_type>::const_iterator	const_iterator;

	// Empty set
	Set() {};

	// Set with initializer list
	Set(std::initializer_list<value_type> l) {
		_set.insert(_set.begin(), l);
	}

	// Copy constructor
	Set(const _SET& s) {
		_set = s._set;
	}

	// Move constructor
	Set(_SET&& s) {
		_set = std::move(s._set);
	}

	// Copy assignment
	_SET& operator=(const _SET& s) noexcept {
		_set = s._set;
		return *this;
	}

	// Move assignment
	_SET& operator=(_SET&& s) noexcept {
		_set = std::move(s._set);
		return *this;
	}

	bool operator==(const _SET& s) const noexcept {
		return this->contains(s, false) && s.contains(*this, false);
	}

	bool operator!=(const _SET& s) const noexcept {
		return !(*this == s);
	};

	bool operator<(const _SET& s) const noexcept {
		if (size() != s.size()) {
			return size() < s.size();
		}

		auto it1=begin(), it2=s.begin();

		while (it1 != end() && it2 != s.end()) {
			if (*it1 < *it2) {
				return true;
			}
			else if (*it1 > *it2) {
				return false;
			}

			it1++;
			it2++;
		};

		return false;
	}

	_SET operator+(const value_type& value) const noexcept {
		return _SET{*this}+_SET{value};
	}

	_SET operator+(value_type&& value) const noexcept {
		return _SET{*this}+_SET{std::move(value)};
	}

	_SET operator+(const _SET& s) const noexcept {
		_SET ret{*this};

		for (const auto& value : s) {
			ret.insert(value);
		}

		return ret;
	}

	_SET operator+(_SET&& s) const noexcept {
		_SET ret{*this};

		for (auto& value : s) {
			ret.insert(std::move(value));
		}

		return ret;
	}

	_SET& operator+=(const value_type& value) noexcept {
		insert(value);
		return *this;
	}

	_SET& operator+=(value_type&& value) noexcept {
		insert(std::move(value));
		return *this;
	}

	_SET& operator+=(const _SET& s) noexcept {
		for (const auto& value : s) {
			insert(value);
		}

		return *this;
	}

	_SET& operator+=(_SET&& s) noexcept {
		for (auto& value : s) {
			insert(std::move(value));
		}

		return *this;
	}

	_SET operator-(const value_type& value) const noexcept {
		_SET ret{*this};
		ret.erase(value);
		return ret;
	}

	_SET operator-(const _SET& s) const noexcept {
		_SET ret{*this};
		for (const auto& v : s) {
			ret.erase(v);
		}
		return ret;
	}

	_SET& operator-=(const value_type& value) noexcept {
		_set.erase(value);
		return *this;
	}

	_SET& operator-=(const _SET& s) noexcept {
		for (const auto& value : s) {
			_set.erase(value);
		}
		return *this;
	}

	Set<_SET> operator*(const _SET& other) noexcept {
		Set<_SET> ret;

		if (!this->empty() && !other.empty()) {
			for (const auto& v1 : _set) {
				for (const auto& v2 : other) {
					ret.insert(_SET{v1,v2});
				}
			}
		}

		return ret;
	}

	_SET intersection(const _SET& s) noexcept {
		_SET s1, s2;

		if (s.size() <= size()) {
			s1 = s;
			s2 = *this;
		}
		else {
			s1 = *this;
			s2 = s;
		}

		for (const auto &v : s1) {
			if (!s2.contains(v)) s1.erase(v);
		}

		return s1;
	}

	iterator begin() noexcept {
		return _set.begin();
	}

	const_iterator begin() const noexcept {
		return _set.begin();
	}

	iterator end() noexcept {
		return _set.end();
	};

	const_iterator end() const noexcept {
		return _set.end();
	}

	size_t size() const noexcept {
		return _set.size();
	}

	bool empty() const noexcept {
		return _set.empty();
	}

	void clear() noexcept {
		_set.clear();
	}

	bool contains(const value_type& value) const noexcept {
		return contains(_SET{value});
	}

	bool contains(const _SET& s, bool strict = false) const noexcept {
		for (const auto& value : s) {
			bool found{false};

			for (const auto& v : _set) {
				if (v == value) {
					found = true;
					break;
				}
			}

			if (!found) {
				return false;
			}
		}

		return strict ? (s.size() != size()) : true;
	}

	void insert(const value_type& value) noexcept {
		_set.push_back(value);
	}

	void insert(value_type&& value) noexcept {
		_set.push_back(std::move(value));
	}

	template<class InputIterator>
	void insert(InputIterator first, InputIterator last) {
		for (auto it = first; it != last; it++) {
			_set.insert(*it);
		}
	};

	iterator erase(const_iterator position) {
		return _set.erase(position);
	}

	size_t erase(const value_type& value) {
		size_t ret{};

		for (auto it = _set.begin(); it != _set.end(); it++) {
			if (*it == value) {
				_set.erase(it);
				++ret;
			}
		}

		return ret;
	}

	iterator erase(const_iterator first, const_iterator last) {
		return _set.erase(first,last);
	}

	size_t erase(const _SET& s) noexcept {
		size_t ret{};

		for (const auto& value : s) {
			ret += erase(value);
		}

		return ret;
	}

	iterator find(const value_type& value) noexcept {
		for (auto it = _set.begin(); it != _set.end(); it++) {
			if (*it == value) return it;
		}

		return _set.end();
	}

	const_iterator find(const value_type& value) const noexcept {
		return (const_iterator)find(value);
	}

	size_t count(const value_type& value) const noexcept {
		size_t ret{};

		for (const auto& v : _set) {
			if (v == value) {
				ret++;
			}
		}

		return ret;
	}

private:
	std::vector<value_type,allocator_type> _set;
};

#endif /* defined(_SET_HPP) */
<commit_msg>[ref] Small refactor and added unique() method<commit_after>//
//  Set.h
//  Set
//
//  Created by Antonio Seprano on 10/08/14.
//  Copyright (c) 2014 Antonio Seprano. All rights reserved.
//

#include <vector>
#include <functional>
#include <utility>
#include <algorithm>

#ifndef _SET_HPP
#define _SET_HPP

template<class _T, class _Allocator = std::allocator<_T> >
class Set {
public:
	typedef _T				  value_type;
	typedef _Allocator	allocator_type;

private:
	typedef Set<value_type,allocator_type> _SET;

public:
	typedef typename std::vector<value_type,allocator_type>::iterator				iterator;
	typedef typename std::vector<value_type,allocator_type>::const_iterator	const_iterator;

	// Empty set
	Set() {};

	// Set with initializer list
	Set(std::initializer_list<value_type> l) {
		_set.insert(_set.begin(), l);
	}

	// Copy constructor
	Set(const _SET& s) {
		_set = s._set;
	}

	// Move constructor
	Set(_SET&& s) {
		_set = std::move(s._set);
	}

	// Copy assignment
	_SET& operator=(const _SET& s) noexcept {
		_set = s._set;
		return *this;
	}

	// Move assignment
	_SET& operator=(_SET&& s) noexcept {
		_set = std::move(s._set);
		return *this;
	}

	iterator begin() noexcept {
		return _set.begin();
	}

	const_iterator begin() const noexcept {
		return _set.begin();
	}

	iterator end() noexcept {
		return _set.end();
	};

	const_iterator end() const noexcept {
		return _set.end();
	}

	size_t size() const noexcept {
		return _set.size();
	}

	bool empty() const noexcept {
		return _set.empty();
	}

	void clear() noexcept {
		_set.clear();
	}

	iterator find(const value_type& value) noexcept {
		for (auto it = _set.begin(); it != _set.end(); it++) {
			if (*it == value) return it;
		}

		return _set.end();
	}

	const_iterator find(const value_type& value) const noexcept {
		return (const_iterator)find(value);
	}

	size_t count(const value_type& value) const noexcept {
		size_t ret{};

		for (const auto& v : _set) {
			if (v == value) {
				ret++;
			}
		}

		return ret;
	}

	bool operator==(const _SET& s) const noexcept {
		return this->contains(s, false) && s.contains(*this, false);
	}

	bool operator!=(const _SET& s) const noexcept {
		return !(*this == s);
	};

	/*
	bool operator<(const _SET& s) const noexcept {
		if (size() != s.size()) {
			return size() < s.size();
		}

		auto it1=begin(), it2=s.begin();

		while (it1 != end() && it2 != s.end()) {
			if (*it1 < *it2) {
				return true;
			}
			else if (*it1 > *it2) {
				return false;
			}

			it1++;
			it2++;
		};

		return false;
	}
	*/

	// Insert new value
	// unique = true => values must be unique (no duplicate values allowed)
	void insert(const value_type& value, bool unique = false) noexcept {
		if (!unique || find(value) == _set.end()) {
			_set.push_back(value);
		}
	}

	void insert(value_type&& value, bool unique = false) noexcept {
		if (!unique || find(value) == _set.end()) {
			_set.push_back(std::move(value));
		}
	}

	_SET operator+(const value_type& value) const noexcept {
		return _SET{*this}+_SET{value};
	}

	_SET operator+(value_type&& value) const noexcept {
		return _SET{*this}+_SET{std::move(value)};
	}

	_SET operator+(const _SET& s) const noexcept {
		_SET ret{*this};

		for (const auto& value : s) {
			ret.insert(value);
		}

		return ret;
	}

	_SET operator+(_SET&& s) const noexcept {
		_SET ret{*this};

		for (auto& value : s) {
			ret.insert(std::move(value));
		}

		return ret;
	}

	_SET& operator+=(const value_type& value) noexcept {
		insert(value);
		return *this;
	}

	_SET& operator+=(value_type&& value) noexcept {
		insert(std::move(value));
		return *this;
	}

	_SET& operator+=(const _SET& s) noexcept {
		for (const auto& value : s) {
			insert(value);
		}

		return *this;
	}

	_SET& operator+=(_SET&& s) noexcept {
		for (auto& value : s) {
			insert(std::move(value));
		}

		return *this;
	}

	_SET operator-(const value_type& value) const noexcept {
		_SET ret{*this};
		ret.erase(value);
		return ret;
	}

	_SET operator-(const _SET& s) const noexcept {
		_SET ret{*this};
		for (const auto& v : s) {
			ret.erase(v);
		}
		return ret;
	}

	_SET& operator-=(const value_type& value) noexcept {
		_set.erase(value);
		return *this;
	}

	_SET& operator-=(const _SET& s) noexcept {
		for (const auto& value : s) {
			_set.erase(value);
		}
		return *this;
	}

	Set<_SET> operator*(const _SET& other) noexcept {
		Set<_SET> ret;

		if (!this->empty() && !other.empty()) {
			for (const auto& v1 : _set) {
				for (const auto& v2 : other) {
					ret.insert(_SET{v1,v2});
				}
			}
		}

		return ret;
	}

	_SET intersection(const _SET& s) noexcept {
		_SET s1, s2;

		if (s.size() <= size()) {
			s1 = s;
			s2 = *this;
		}
		else {
			s1 = *this;
			s2 = s;
		}

		for (const auto &v : s1) {
			if (!s2.contains(v)) s1.erase(v);
		}

		return s1;
	}

	bool contains(const value_type& value) const noexcept {
		return contains(_SET{value});
	}

	bool contains(const _SET& s, bool strict = false) const noexcept {
		for (const auto& value : s) {
			bool found{false};

			for (const auto& v : _set) {
				if (v == value) {
					found = true;
					break;
				}
			}

			if (!found) {
				return false;
			}
		}

		return strict ? (s.size() != size()) : true;
	}

	template<class InputIterator>
	void insert(InputIterator first, InputIterator last) {
		for (auto it = first; it != last; it++) {
			_set.insert(*it);
		}
	};

	iterator erase(const_iterator position) {
		return _set.erase(position);
	}

	iterator erase(const_iterator first, const_iterator last) {
		return _set.erase(first,last);
	}

	size_t erase(const value_type& value, bool all = true) {
		size_t ret{};

		if (!all) {
			auto it = find(value);

			if (it != end()) {
				erase(it);
				ret++;
			}

			return ret;
		}

		for (auto it = _set.begin(); it != _set.end(); it++) {
			if (*it == value) {
				_set.erase(it);
				++ret;
			}
		}

		return ret;
	}

	_SET& unique() noexcept {
		for (auto i = begin(); i != end(); i++) {
			auto j = i+1;

			while (j != end()) {
				if (*i == *j) {
					_set.erase(j);
				}
				else {
					j++;
				}
			}
		}

		return *this;
	}

private:
	std::vector<value_type,allocator_type> _set;
};

#endif /* defined(_SET_HPP) */
<|endoftext|>


<commit_before>#ifndef _INCLUDED_ESACOMP_COLLIDER_HPP_
#define _INCLUDED_ESACOMP_COLLIDER_HPP_

#include <vector>
#include <sdsl/suffix_arrays.hpp>
#include <tudocomp/lzss/LZSSFactor.hpp>

namespace tudocomp {
namespace lzss {

/// Implements the "Collider" selection strategy for ESAComp.
///
/// TODO: Describe
class ESACompCollider {

private:
    struct Interval {
        size_t p, q, l;

        size_t other;
        bool deleted;

        Interval(size_t _p, size_t _q, size_t _l, size_t _other)
            : p(_p), q(_q), l(_l), other(_other), deleted(false)
        {
        }
    };

public:
    inline ESACompCollider() {
    }

    void factorize(const sdsl::csa_bitcompressed<>& sa,
                   const sdsl::int_vector<>& isa,
                   sdsl::int_vector<>& lcp,
                   size_t fact_min,
                   std::vector<LZSSFactor>& out_factors) {

        //induce intervals
        std::vector<Interval> intervals;
        for(size_t i = 1; i < sa.size(); i++) {
            if(lcp[i] >= fact_min) {
                size_t k = intervals.size();
                intervals.push_back(Interval(sa[i], sa[i-1], lcp[i], k+1));
                intervals.push_back(Interval(sa[i-1], sa[i], lcp[i], k));
            }
        }

        //process
        size_t count = intervals.size();
        while(count) {
            //debug output
            DLOG(INFO) << "Intervals (" << count << "):";
            {
                for(size_t i = 0; i < intervals.size(); i++) {
                    const Interval& it = intervals[i];
                    if(!it.deleted) {
                        DLOG(INFO) << "(" << i << "): [" << (it.p+1) << ", " << (it.q+1) << ", " << it.l << "] / (" << it.other << ")";
                    }
                }
            }

            //save best pick
            struct {
                size_t index, length;
                size_t extent;
                std::list<size_t> collisions;
            } best;

            best.index = SIZE_MAX;
            best.extent = SIZE_MAX;

            //find interval with smallest collision size
            for(size_t i = 0; i < intervals.size(); i++) {
                const Interval& it = intervals[i];
                if(it.deleted) continue;

                size_t lb = it.p;
                size_t rb = it.p + it.l;
                std::list<size_t> collisions;

                for(size_t k = 0; k < intervals.size(); k++) {
                    if(k == i) continue;
                    const Interval& kt = intervals[k];
                    if(kt.deleted) continue;

                    //test possible collision cases
                    if(it.p >= kt.p && it.p <= kt.p + kt.l) {
                        //start of it is included in kt
                        collisions.push_back(k);
                        lb = std::min(lb, kt.p);
                    } else if(kt.p >= it.p && kt.p <= it.p + it.l) {
                        //start of kt is included in it
                        collisions.push_back(k);
                        rb = std::max(rb, kt.p + kt.l);
                    } else if(kt.p <= it.p && kt.p + kt.l >= it.p + it.l) {
                        //it is fully included in kt
                        collisions.push_back(k);
                        lb = std::min(lb, kt.p);
                        rb = std::max(rb, kt.p + kt.l);
                    } else if(it.p <= kt.p && it.p + it.l >= kt.p + kt.l) {
                        //kt is fully included in it
                        collisions.push_back(k);
                    }
                }

                if(collisions.empty()) {
                    best.extent = 0;
                    best.index = i;
                    best.length = it.l;
                    best.collisions.clear();
                    break;
                } else {
                    size_t extent = rb - lb;
                    DLOG(INFO) << "collision extent of (" << i << ") is: " << extent;

                    if(extent < best.extent || (extent == best.extent && it.l > best.length)) {
                        best.extent = extent;
                        best.index = i;
                        best.length = it.l;
                        best.collisions = collisions;
                    }
                }
            }

            //factorize best
            if(best.index != SIZE_MAX) {
                Interval& it = intervals[best.index];
                DLOG(INFO) << "Pick: (" << best.index << ")";

                //factorize
                LZSSFactor fact(it.p, it.q, it.l);
                out_factors.push_back(fact);

                //remove self
                it.deleted = true;
                assert(count > 0);
                --count;

                //remove other and subintervals of other
                {
                    size_t j = it.other;
                    size_t jl;
                    do {
                        Interval& jt = intervals[j];

                        if(!jt.deleted) {
                            DLOG(INFO) << "remove other (" << j << ")";
                            jt.deleted = true;
                            assert(count > 0);
                            --count;
                        }

                        jl = jt.l;
                        j += 2;
                    } while(j < intervals.size() && intervals[j].l < jl);
                }

                //remove collisions
                for(auto x = best.collisions.begin(); x != best.collisions.end(); ++x) {
                    Interval& xt = intervals[*x];
                    if(!xt.deleted) {
                        DLOG(INFO) << "remove collision (" << (*x) << ")";
                        xt.deleted = true;
                        assert(count > 0);
                        --count;
                    }
                }
            }
        }
    }
};

}}

#endif

<commit_msg>Fix + improve collider strategy<commit_after>#ifndef _INCLUDED_ESACOMP_COLLIDER_HPP_
#define _INCLUDED_ESACOMP_COLLIDER_HPP_

#include <list>
#include <vector>
#include <sdsl/suffix_arrays.hpp>
#include <tudocomp/lzss/LZSSFactor.hpp>

namespace tudocomp {
namespace lzss {

/// Implements the "Collider" selection strategy for ESAComp.
///
/// TODO: Describe
class ESACompCollider {

private:
    struct Interval {
        size_t p, q, l;

        size_t other;
        bool deleted;

        std::list<size_t> collisions;
        size_t extent;

        Interval(size_t _p, size_t _q, size_t _l, size_t _other)
            : p(_p), q(_q), l(_l), other(_other), deleted(false), extent(0)
        {
        }

        bool set_deleted() {
            if(deleted) {
                return false;
            } else {
                deleted = true;
                return true;
            }
        }
    };

public:
    inline ESACompCollider() {
    }

    void factorize(const sdsl::csa_bitcompressed<>& sa,
                   const sdsl::int_vector<>& isa,
                   sdsl::int_vector<>& lcp,
                   size_t fact_min,
                   std::vector<LZSSFactor>& out_factors) {

        //induce intervals
        std::vector<Interval> intervals;
        for(size_t i = 1; i < sa.size(); i++) {
            if(lcp[i] >= fact_min) {
                size_t k = intervals.size();
                intervals.push_back(Interval(sa[i], sa[i-1], lcp[i], k+1));
                intervals.push_back(Interval(sa[i-1], sa[i], lcp[i], k));
            }
        }

        //process
        size_t count = intervals.size();
        while(count) {
            //debug output
            DLOG(INFO) << "Intervals (" << count << "):";
            {
                for(size_t i = 0; i < intervals.size(); i++) {
                    const Interval& it = intervals[i];
                    if(!it.deleted) {
                        DLOG(INFO) << "(" << i << "): [" << (it.p+1) << ", " << (it.q+1) << ", " << it.l << "] / (" << it.other << ")";
                    }
                }
            }

            //calculate collision extents
            for(size_t i = 0; i < intervals.size(); i++) {
                Interval& it = intervals[i];
                it.collisions.clear();
                it.extent = SIZE_MAX;

                if(it.deleted) continue;

                size_t lb = it.p;
                size_t rb = it.p + it.l;

                for(size_t k = 0; k < intervals.size(); k++) {
                    if(k == i) continue;
                    const Interval& kt = intervals[k];
                    if(kt.deleted) continue;

                    //test possible collision cases
                    if(it.p >= kt.p && it.p <= kt.p + kt.l) {
                        //start of it is included in kt
                        it.collisions.push_back(k);
                        lb = std::min(lb, kt.p);
                    } else if(kt.p >= it.p && kt.p <= it.p + it.l) {
                        //start of kt is included in it
                        it.collisions.push_back(k);
                        rb = std::max(rb, kt.p + kt.l);
                    } else if(kt.p <= it.p && kt.p + kt.l >= it.p + it.l) {
                        //it is fully included in kt
                        it.collisions.push_back(k);
                        lb = std::min(lb, kt.p);
                        rb = std::max(rb, kt.p + kt.l);
                    } else if(it.p <= kt.p && it.p + it.l >= kt.p + kt.l) {
                        //kt is fully included in it
                        it.collisions.push_back(k);
                    }
                }

                if(it.collisions.empty()) {
                    it.extent = 0;
                } else {
                    it.extent = rb - lb;
                }
            }

            //find best
            size_t best = SIZE_MAX;
            size_t best_extent = SIZE_MAX;

            for(size_t i = 0; i < intervals.size(); i++) {
                Interval& it = intervals[i];
                if(!it.deleted) {
                    size_t extent = it.extent;

                    Interval& ot = intervals[it.other];
                    if(!ot.deleted) {
                        extent += ot.extent;
                    }

                    if(extent < best_extent || (extent == best_extent && it.l > intervals[best].l)) {
                        best = i;
                        best_extent = extent;
                    }
                }
            }

            //factorize
            #define DECREASE_INTERVAL_COUNT assert(count > 0); --count;

            if(best != SIZE_MAX) {
                Interval& it = intervals[best];
                DLOG(INFO) << "Pick: (" << best << ")";

                //factorize
                LZSSFactor fact(it.p, it.q, it.l);
                out_factors.push_back(fact);

                //remove self
                it.set_deleted();
                DLOG(INFO) << "remove self (" << best << ")";
                DECREASE_INTERVAL_COUNT;

                //remove collisions
                for(auto x = it.collisions.begin(); x != it.collisions.end(); ++x) {
                    Interval& xt = intervals[*x];
                    if(xt.set_deleted()) {
                        DLOG(INFO) << "remove collision (" << (*x) << ")";
                        DECREASE_INTERVAL_COUNT;
                    }
                }

                //remove other
                {
                    size_t j = it.other;
                    Interval& jt = intervals[j];
                    if(jt.set_deleted()) {
                        DLOG(INFO) << "remove other (" << j << ")";
                        DECREASE_INTERVAL_COUNT;
                    }

                    //remove other full collisions
                    for(auto x = jt.collisions.begin(); x != jt.collisions.end(); ++x) {
                        Interval& xt = intervals[*x];
                        if(
                            (xt.p >= jt.p && xt.p + xt.l <= jt.p + jt.l) ||
                            (jt.p >= xt.p && jt.p + jt.l <= xt.p + xt.l)
                        ) {
                            if(xt.set_deleted()) {
                                DLOG(INFO) << "remove other full collision (" << (*x) << ")";
                                DECREASE_INTERVAL_COUNT;
                            }
                        }
                    }
                }
            } else {
                DLOG(INFO) << "didn't find anything good!?";
                //break;
            }

            #undef DECREASE_INTERVAL_COUNT
        } //while
    }
};

}}

#endif

<|endoftext|>


<commit_before>#define EXTERN_DLL_EXPORT

#include <windows.h>
#include <Psapi.h>
#include <tlhelp32.h>
#include <vector>
#include <algorithm>

#include <beaengine/BeaEngine.h>

const int PATH_MAXIMUM_LENGTH = 260;

enum class RequestFunction
{
	IsProcessValid,
	OpenRemoteProcess,
	CloseRemoteProcess,
	ReadRemoteMemory,
	WriteRemoteMemory,
	EnumerateProcesses,
	EnumerateRemoteSectionsAndModules,
	DisassembleRemoteCode,
	ControlRemoteProcess
};

typedef LPVOID(__stdcall *RequestFunctionPtrCallback)(RequestFunction request);
RequestFunctionPtrCallback requestFunction;

EXTERN_DLL_EXPORT VOID __stdcall Initialize(RequestFunctionPtrCallback requestCallback)
{
	requestFunction = requestCallback;
}

DWORD lastError = 0;
EXTERN_DLL_EXPORT DWORD __stdcall GetLastErrorCode()
{
	return lastError;
}

EXTERN_DLL_EXPORT BOOL __stdcall IsProcessValid(HANDLE process)
{
	if (!process)
	{
		return FALSE;
	}

	auto retn = WaitForSingleObject(process, 0);
	if (retn == WAIT_FAILED)
	{
		return FALSE;
	}
	return retn == WAIT_TIMEOUT;
}

EXTERN_DLL_EXPORT LPVOID __stdcall OpenRemoteProcess(DWORD pid, DWORD desiredAccess)
{
	return OpenProcess(desiredAccess, FALSE, pid);
}

EXTERN_DLL_EXPORT VOID __stdcall CloseRemoteProcess(HANDLE process)
{
	CloseHandle(process);
}

EXTERN_DLL_EXPORT BOOL __stdcall ReadRemoteMemory(HANDLE process, LPCVOID address, LPVOID buffer, SIZE_T size)
{
	if (ReadProcessMemory(process, address, buffer, size, nullptr))
	{
		lastError = 0;

		return TRUE;
	}

	lastError = GetLastError();

	return FALSE;
}

EXTERN_DLL_EXPORT BOOL __stdcall WriteRemoteMemory(HANDLE process, LPVOID address, LPCVOID buffer, SIZE_T size)
{
	DWORD oldProtect;
	if (VirtualProtectEx(process, address, size, PAGE_EXECUTE_READWRITE, &oldProtect))
	{
		if (WriteProcessMemory(process, address, buffer, size, nullptr))
		{
			VirtualProtectEx(process, address, size, oldProtect, nullptr);

			lastError = 0;

			return TRUE;
		}
	}

	lastError = GetLastError();

	return FALSE;
}

enum class Platform
{
	Unknown,
	X86,
	X64
};

Platform GetProcessPlatform(HANDLE process)
{
	auto GetProcessorArchitecture = []()
	{
		static USHORT processorArchitecture = PROCESSOR_ARCHITECTURE_UNKNOWN;
		if (processorArchitecture == PROCESSOR_ARCHITECTURE_UNKNOWN)
		{
			SYSTEM_INFO info = {};
			GetNativeSystemInfo(&info);

			processorArchitecture = info.wProcessorArchitecture;
		}
		return processorArchitecture;
	};

	switch (GetProcessorArchitecture())
	{
		case PROCESSOR_ARCHITECTURE_INTEL:
			return Platform::X86;
		case PROCESSOR_ARCHITECTURE_AMD64:
			BOOL isWow64 = FALSE;
			if (IsWow64Process(process, &isWow64))
			{
				return isWow64 ? Platform::X86 : Platform::X64;
			}

#ifdef _WIN64
			return Platform::X64;
#else
			return Platform::X86;
#endif
	}
	return Platform::Unknown;
}

typedef VOID(__stdcall EnumerateProcessCallback)(DWORD pid, WCHAR modulePath[PATH_MAXIMUM_LENGTH]);

EXTERN_DLL_EXPORT VOID __stdcall EnumerateProcesses(EnumerateProcessCallback callbackProcess)
{
	if (callbackProcess == nullptr)
	{
		return;
	}

	auto handle = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
	if (handle != INVALID_HANDLE_VALUE)
	{
		PROCESSENTRY32W pe32 = {};
		pe32.dwSize = sizeof(PROCESSENTRY32W);
		if (Process32FirstW(handle, &pe32))
		{
			auto openRemoteProcess = reinterpret_cast<decltype(OpenRemoteProcess)*>(requestFunction(RequestFunction::OpenRemoteProcess));
			auto closeRemoteProcess = reinterpret_cast<decltype(CloseRemoteProcess)*>(requestFunction(RequestFunction::CloseRemoteProcess));

			do
			{
				auto process = openRemoteProcess(pe32.th32ProcessID, PROCESS_QUERY_INFORMATION | PROCESS_VM_READ);
				if (process != nullptr && process != INVALID_HANDLE_VALUE)
				{
					auto platform = GetProcessPlatform(process);

#ifdef _WIN64
					if (platform == Platform::X64)
#else
					if (platform == Platform::X86)
#endif
					{
						WCHAR process_path[MAX_PATH] = { };
						GetModuleFileNameExW(process, NULL, process_path, MAX_PATH);

						callbackProcess(pe32.th32ProcessID, process_path);
					}

					closeRemoteProcess(process);
				}
			} while (Process32NextW(handle, &pe32));
		}

		CloseHandle(handle);

		lastError = 0;

		return;
	}

	lastError = GetLastError();
}

typedef VOID(__stdcall EnumerateRemoteSectionsCallback)(LPVOID baseAddress, SIZE_T regionSize, BYTE name[IMAGE_SIZEOF_SHORT_NAME + 2], DWORD state, DWORD protection, DWORD type, WCHAR modulePath[PATH_MAXIMUM_LENGTH]);
typedef VOID(__stdcall EnumerateRemoteModulesCallback)(LPVOID baseAddress, SIZE_T regionSize, WCHAR modulePath[PATH_MAXIMUM_LENGTH]);

EXTERN_DLL_EXPORT VOID __stdcall EnumerateRemoteSectionsAndModules(HANDLE process, EnumerateRemoteSectionsCallback callbackSection, EnumerateRemoteModulesCallback callbackModule)
{
	if (callbackSection == nullptr && callbackModule == nullptr)
	{
		return;
	}

	struct SectionInfo
	{
		LPVOID BaseAddress;
		SIZE_T RegionSize;
		BYTE Name[IMAGE_SIZEOF_SHORT_NAME + 2];
		DWORD State;
		DWORD Protection;
		DWORD Type;
		WCHAR ModulePath[PATH_MAXIMUM_LENGTH];
	};
	std::vector<SectionInfo> sections;

	SYSTEM_INFO sysInfo;
	GetSystemInfo(&sysInfo);

	MEMORY_BASIC_INFORMATION memInfo;
	size_t address = (size_t)sysInfo.lpMinimumApplicationAddress;
	while (address < (size_t)sysInfo.lpMaximumApplicationAddress)
	{
		if (VirtualQueryEx(process, (LPCVOID)address, &memInfo, sizeof(MEMORY_BASIC_INFORMATION)) != 0)
		{
			if (memInfo.State == MEM_COMMIT /*&& memInfo.Type == MEM_PRIVATE*/)
			{
				SectionInfo section = {};
				section.BaseAddress = memInfo.BaseAddress;
				section.RegionSize = memInfo.RegionSize;
				section.State = memInfo.State;
				section.Protection = memInfo.Protect;
				section.Type = memInfo.Type;

				sections.push_back(std::move(section));
			}
			address = (ULONG_PTR)memInfo.BaseAddress + memInfo.RegionSize;
		}
		else
		{
			address += 1024;
		}
	}

	auto handle = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, GetProcessId(process));
	if (handle != INVALID_HANDLE_VALUE)
	{
		MODULEENTRY32W me32 = {};
		me32.dwSize = sizeof(MODULEENTRY32W);
		if (Module32FirstW(handle, &me32))
		{
			auto readRemoteMemory = reinterpret_cast<decltype(ReadRemoteMemory)*>(requestFunction(RequestFunction::ReadRemoteMemory));

			do
			{
				if (callbackModule != nullptr)
				{
					callbackModule(me32.modBaseAddr, me32.modBaseSize, me32.szExePath);
				}

				if (callbackSection != nullptr)
				{
					auto it = std::lower_bound(std::begin(sections), std::end(sections), (LPVOID)me32.modBaseAddr, [&sections](const SectionInfo& lhs, const LPVOID& rhs)
					{
						return lhs.BaseAddress < rhs;
					});

					IMAGE_DOS_HEADER DosHdr = {};
					IMAGE_NT_HEADERS NtHdr = {};

					readRemoteMemory(process, me32.modBaseAddr, &DosHdr, sizeof(IMAGE_DOS_HEADER));
					readRemoteMemory(process, me32.modBaseAddr + DosHdr.e_lfanew, &NtHdr, sizeof(IMAGE_NT_HEADERS));

					std::vector<IMAGE_SECTION_HEADER> sectionHeaders(NtHdr.FileHeader.NumberOfSections);
					readRemoteMemory(process, me32.modBaseAddr + DosHdr.e_lfanew + sizeof(IMAGE_NT_HEADERS), sectionHeaders.data(), NtHdr.FileHeader.NumberOfSections * sizeof(IMAGE_SECTION_HEADER));
					for (int i = 0; i < NtHdr.FileHeader.NumberOfSections; ++i)
					{
						auto&& section = sectionHeaders[i];

						auto sectionAddress = (size_t)me32.modBaseAddr + section.VirtualAddress;
						for (auto j = it; j != std::end(sections); ++j)
						{
							if (sectionAddress >= (size_t)j->BaseAddress && sectionAddress < (size_t)j->BaseAddress + (size_t)j->RegionSize)
							{
								std::memcpy(j->Name, section.Name, IMAGE_SIZEOF_SHORT_NAME);
								std::memcpy(j->ModulePath, me32.szExePath, sizeof(SectionInfo::ModulePath));
								break;
							}
						}

					}
				}
			} while (Module32NextW(handle, &me32));
		}

		CloseHandle(handle);

		if (callbackSection != nullptr)
		{
			for (auto&& section : sections)
			{
				callbackSection(section.BaseAddress, section.RegionSize, section.Name, section.State, section.Protection, section.Type, section.ModulePath);
			}
		}

		lastError = 0;

		return;
	}

	lastError = GetLastError();
}

typedef VOID(__stdcall DisassembleRemoteCodeCallback)(LPVOID address, DWORD length, CHAR instruction[64]);

EXTERN_DLL_EXPORT VOID __stdcall DisassembleRemoteCode(HANDLE process, LPVOID address, int length, DisassembleRemoteCodeCallback callbackDisassembledCode)
{
	if (callbackDisassembledCode == nullptr)
	{
		return;
	}

	UIntPtr start = (UIntPtr)address;
	UIntPtr end = start + length;
	if (end <= start)
	{
		return;
	}

	DISASM disasm;
	std::memset(&disasm, 0, sizeof(DISASM));
	disasm.Options = NasmSyntax + PrefixedNumeral;
#ifdef _WIN64
	disasm.Archi = 64;
#endif

	auto readRemoteMemory = reinterpret_cast<decltype(ReadRemoteMemory)*>(requestFunction(RequestFunction::ReadRemoteMemory));

	std::vector<uint8_t> buffer(length);
	readRemoteMemory(process, address, buffer.data(), buffer.size());

	disasm.EIP = (UIntPtr)buffer.data();
	disasm.VirtualAddr = start;

	while (true)
	{
		disasm.SecurityBlock = ((UIntPtr)buffer.data() + buffer.size()) - disasm.EIP;

		auto disamLength = Disasm(&disasm);
		if (disamLength == OUT_OF_BLOCK || disamLength == UNKNOWN_OPCODE)
		{
			break;
		}

		callbackDisassembledCode((LPVOID)disasm.VirtualAddr, disamLength, disasm.CompleteInstr);

		disasm.EIP += disamLength;
		if (disasm.EIP >= end)
		{
			break;
		}
		disasm.VirtualAddr += disamLength;
	}
}

enum class ControlRemoteProcessAction
{
	Suspend,
	Resume,
	Terminate
};

EXTERN_DLL_EXPORT VOID __stdcall ControlRemoteProcess(HANDLE process, ControlRemoteProcessAction action)
{
	if (action == ControlRemoteProcessAction::Suspend || action == ControlRemoteProcessAction::Resume)
	{
		auto processId = GetProcessId(process);

		auto handle = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
		if (handle != INVALID_HANDLE_VALUE)
		{
			auto fn = action == ControlRemoteProcessAction::Suspend ? SuspendThread : ResumeThread;

			THREADENTRY32 te32 = {};
			te32.dwSize = sizeof(THREADENTRY32);
			if (Thread32First(handle, &te32))
			{
				do
				{
					if (te32.th32OwnerProcessID == processId)
					{
						auto threadHandle = OpenThread(THREAD_SUSPEND_RESUME, FALSE, te32.th32ThreadID);
						if (threadHandle)
						{
							fn(threadHandle);

							CloseHandle(threadHandle);
						}
					}
				} while (Thread32Next(handle, &te32));
			}

			CloseHandle(handle);
		}
	}
	else if (action == ControlRemoteProcessAction::Terminate)
	{
		TerminateProcess(process, 0);
	}
}
<commit_msg>Fixed disassembler range bug.<commit_after>#define EXTERN_DLL_EXPORT

#include <windows.h>
#include <Psapi.h>
#include <tlhelp32.h>
#include <vector>
#include <algorithm>

#include <beaengine/BeaEngine.h>

const int PATH_MAXIMUM_LENGTH = 260;

enum class RequestFunction
{
	IsProcessValid,
	OpenRemoteProcess,
	CloseRemoteProcess,
	ReadRemoteMemory,
	WriteRemoteMemory,
	EnumerateProcesses,
	EnumerateRemoteSectionsAndModules,
	DisassembleRemoteCode,
	ControlRemoteProcess
};

typedef LPVOID(__stdcall *RequestFunctionPtrCallback)(RequestFunction request);
RequestFunctionPtrCallback requestFunction;

EXTERN_DLL_EXPORT VOID __stdcall Initialize(RequestFunctionPtrCallback requestCallback)
{
	requestFunction = requestCallback;
}

DWORD lastError = 0;
EXTERN_DLL_EXPORT DWORD __stdcall GetLastErrorCode()
{
	return lastError;
}

EXTERN_DLL_EXPORT BOOL __stdcall IsProcessValid(HANDLE process)
{
	if (!process)
	{
		return FALSE;
	}

	auto retn = WaitForSingleObject(process, 0);
	if (retn == WAIT_FAILED)
	{
		return FALSE;
	}
	return retn == WAIT_TIMEOUT;
}

EXTERN_DLL_EXPORT LPVOID __stdcall OpenRemoteProcess(DWORD pid, DWORD desiredAccess)
{
	return OpenProcess(desiredAccess, FALSE, pid);
}

EXTERN_DLL_EXPORT VOID __stdcall CloseRemoteProcess(HANDLE process)
{
	CloseHandle(process);
}

EXTERN_DLL_EXPORT BOOL __stdcall ReadRemoteMemory(HANDLE process, LPCVOID address, LPVOID buffer, SIZE_T size)
{
	if (ReadProcessMemory(process, address, buffer, size, nullptr))
	{
		lastError = 0;

		return TRUE;
	}

	lastError = GetLastError();

	return FALSE;
}

EXTERN_DLL_EXPORT BOOL __stdcall WriteRemoteMemory(HANDLE process, LPVOID address, LPCVOID buffer, SIZE_T size)
{
	DWORD oldProtect;
	if (VirtualProtectEx(process, address, size, PAGE_EXECUTE_READWRITE, &oldProtect))
	{
		if (WriteProcessMemory(process, address, buffer, size, nullptr))
		{
			VirtualProtectEx(process, address, size, oldProtect, nullptr);

			lastError = 0;

			return TRUE;
		}
	}

	lastError = GetLastError();

	return FALSE;
}

enum class Platform
{
	Unknown,
	X86,
	X64
};

Platform GetProcessPlatform(HANDLE process)
{
	auto GetProcessorArchitecture = []()
	{
		static USHORT processorArchitecture = PROCESSOR_ARCHITECTURE_UNKNOWN;
		if (processorArchitecture == PROCESSOR_ARCHITECTURE_UNKNOWN)
		{
			SYSTEM_INFO info = {};
			GetNativeSystemInfo(&info);

			processorArchitecture = info.wProcessorArchitecture;
		}
		return processorArchitecture;
	};

	switch (GetProcessorArchitecture())
	{
		case PROCESSOR_ARCHITECTURE_INTEL:
			return Platform::X86;
		case PROCESSOR_ARCHITECTURE_AMD64:
			BOOL isWow64 = FALSE;
			if (IsWow64Process(process, &isWow64))
			{
				return isWow64 ? Platform::X86 : Platform::X64;
			}

#ifdef _WIN64
			return Platform::X64;
#else
			return Platform::X86;
#endif
	}
	return Platform::Unknown;
}

typedef VOID(__stdcall EnumerateProcessCallback)(DWORD pid, WCHAR modulePath[PATH_MAXIMUM_LENGTH]);

EXTERN_DLL_EXPORT VOID __stdcall EnumerateProcesses(EnumerateProcessCallback callbackProcess)
{
	if (callbackProcess == nullptr)
	{
		return;
	}

	auto handle = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
	if (handle != INVALID_HANDLE_VALUE)
	{
		PROCESSENTRY32W pe32 = {};
		pe32.dwSize = sizeof(PROCESSENTRY32W);
		if (Process32FirstW(handle, &pe32))
		{
			auto openRemoteProcess = reinterpret_cast<decltype(OpenRemoteProcess)*>(requestFunction(RequestFunction::OpenRemoteProcess));
			auto closeRemoteProcess = reinterpret_cast<decltype(CloseRemoteProcess)*>(requestFunction(RequestFunction::CloseRemoteProcess));

			do
			{
				auto process = openRemoteProcess(pe32.th32ProcessID, PROCESS_QUERY_INFORMATION | PROCESS_VM_READ);
				if (process != nullptr && process != INVALID_HANDLE_VALUE)
				{
					auto platform = GetProcessPlatform(process);

#ifdef _WIN64
					if (platform == Platform::X64)
#else
					if (platform == Platform::X86)
#endif
					{
						WCHAR process_path[MAX_PATH] = { };
						GetModuleFileNameExW(process, NULL, process_path, MAX_PATH);

						callbackProcess(pe32.th32ProcessID, process_path);
					}

					closeRemoteProcess(process);
				}
			} while (Process32NextW(handle, &pe32));
		}

		CloseHandle(handle);

		lastError = 0;

		return;
	}

	lastError = GetLastError();
}

typedef VOID(__stdcall EnumerateRemoteSectionsCallback)(LPVOID baseAddress, SIZE_T regionSize, BYTE name[IMAGE_SIZEOF_SHORT_NAME + 2], DWORD state, DWORD protection, DWORD type, WCHAR modulePath[PATH_MAXIMUM_LENGTH]);
typedef VOID(__stdcall EnumerateRemoteModulesCallback)(LPVOID baseAddress, SIZE_T regionSize, WCHAR modulePath[PATH_MAXIMUM_LENGTH]);

EXTERN_DLL_EXPORT VOID __stdcall EnumerateRemoteSectionsAndModules(HANDLE process, EnumerateRemoteSectionsCallback callbackSection, EnumerateRemoteModulesCallback callbackModule)
{
	if (callbackSection == nullptr && callbackModule == nullptr)
	{
		return;
	}

	struct SectionInfo
	{
		LPVOID BaseAddress;
		SIZE_T RegionSize;
		BYTE Name[IMAGE_SIZEOF_SHORT_NAME + 2];
		DWORD State;
		DWORD Protection;
		DWORD Type;
		WCHAR ModulePath[PATH_MAXIMUM_LENGTH];
	};
	std::vector<SectionInfo> sections;

	SYSTEM_INFO sysInfo;
	GetSystemInfo(&sysInfo);

	MEMORY_BASIC_INFORMATION memInfo;
	size_t address = (size_t)sysInfo.lpMinimumApplicationAddress;
	while (address < (size_t)sysInfo.lpMaximumApplicationAddress)
	{
		if (VirtualQueryEx(process, (LPCVOID)address, &memInfo, sizeof(MEMORY_BASIC_INFORMATION)) != 0)
		{
			if (memInfo.State == MEM_COMMIT /*&& memInfo.Type == MEM_PRIVATE*/)
			{
				SectionInfo section = {};
				section.BaseAddress = memInfo.BaseAddress;
				section.RegionSize = memInfo.RegionSize;
				section.State = memInfo.State;
				section.Protection = memInfo.Protect;
				section.Type = memInfo.Type;

				sections.push_back(std::move(section));
			}
			address = (ULONG_PTR)memInfo.BaseAddress + memInfo.RegionSize;
		}
		else
		{
			address += 1024;
		}
	}

	auto handle = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, GetProcessId(process));
	if (handle != INVALID_HANDLE_VALUE)
	{
		MODULEENTRY32W me32 = {};
		me32.dwSize = sizeof(MODULEENTRY32W);
		if (Module32FirstW(handle, &me32))
		{
			auto readRemoteMemory = reinterpret_cast<decltype(ReadRemoteMemory)*>(requestFunction(RequestFunction::ReadRemoteMemory));

			do
			{
				if (callbackModule != nullptr)
				{
					callbackModule(me32.modBaseAddr, me32.modBaseSize, me32.szExePath);
				}

				if (callbackSection != nullptr)
				{
					auto it = std::lower_bound(std::begin(sections), std::end(sections), (LPVOID)me32.modBaseAddr, [&sections](const SectionInfo& lhs, const LPVOID& rhs)
					{
						return lhs.BaseAddress < rhs;
					});

					IMAGE_DOS_HEADER DosHdr = {};
					IMAGE_NT_HEADERS NtHdr = {};

					readRemoteMemory(process, me32.modBaseAddr, &DosHdr, sizeof(IMAGE_DOS_HEADER));
					readRemoteMemory(process, me32.modBaseAddr + DosHdr.e_lfanew, &NtHdr, sizeof(IMAGE_NT_HEADERS));

					std::vector<IMAGE_SECTION_HEADER> sectionHeaders(NtHdr.FileHeader.NumberOfSections);
					readRemoteMemory(process, me32.modBaseAddr + DosHdr.e_lfanew + sizeof(IMAGE_NT_HEADERS), sectionHeaders.data(), NtHdr.FileHeader.NumberOfSections * sizeof(IMAGE_SECTION_HEADER));
					for (int i = 0; i < NtHdr.FileHeader.NumberOfSections; ++i)
					{
						auto&& section = sectionHeaders[i];

						auto sectionAddress = (size_t)me32.modBaseAddr + section.VirtualAddress;
						for (auto j = it; j != std::end(sections); ++j)
						{
							if (sectionAddress >= (size_t)j->BaseAddress && sectionAddress < (size_t)j->BaseAddress + (size_t)j->RegionSize)
							{
								std::memcpy(j->Name, section.Name, IMAGE_SIZEOF_SHORT_NAME);
								std::memcpy(j->ModulePath, me32.szExePath, sizeof(SectionInfo::ModulePath));
								break;
							}
						}

					}
				}
			} while (Module32NextW(handle, &me32));
		}

		CloseHandle(handle);

		if (callbackSection != nullptr)
		{
			for (auto&& section : sections)
			{
				callbackSection(section.BaseAddress, section.RegionSize, section.Name, section.State, section.Protection, section.Type, section.ModulePath);
			}
		}

		lastError = 0;

		return;
	}

	lastError = GetLastError();
}

typedef VOID(__stdcall DisassembleRemoteCodeCallback)(LPVOID address, DWORD length, CHAR instruction[64]);

EXTERN_DLL_EXPORT VOID __stdcall DisassembleRemoteCode(HANDLE process, LPVOID address, int length, DisassembleRemoteCodeCallback callbackDisassembledCode)
{
	if (callbackDisassembledCode == nullptr)
	{
		return;
	}

	UIntPtr start = (UIntPtr)address;

	DISASM disasm = { };
	disasm.Options = NasmSyntax + PrefixedNumeral;
#ifdef _WIN64
	disasm.Archi = 64;
#endif

	auto readRemoteMemory = reinterpret_cast<decltype(ReadRemoteMemory)*>(requestFunction(RequestFunction::ReadRemoteMemory));

	std::vector<uint8_t> buffer(length);
	readRemoteMemory(process, address, buffer.data(), buffer.size());

	UIntPtr end = (UIntPtr)buffer.data() + length;

	disasm.EIP = (UIntPtr)buffer.data();
	disasm.VirtualAddr = start;

	while (true)
	{
		disasm.SecurityBlock = end - disasm.EIP;

		auto disamLength = Disasm(&disasm);
		if (disamLength == OUT_OF_BLOCK || disamLength == UNKNOWN_OPCODE)
		{
			break;
		}

		callbackDisassembledCode((LPVOID)disasm.VirtualAddr, disamLength, disasm.CompleteInstr);

		disasm.EIP += disamLength;
		if (disasm.EIP >= end || buffer[disasm.EIP - (UIntPtr)buffer.data()] == 0xCC)
		{
			break;
		}
		disasm.VirtualAddr += disamLength;
	}
}

enum class ControlRemoteProcessAction
{
	Suspend,
	Resume,
	Terminate
};

EXTERN_DLL_EXPORT VOID __stdcall ControlRemoteProcess(HANDLE process, ControlRemoteProcessAction action)
{
	if (action == ControlRemoteProcessAction::Suspend || action == ControlRemoteProcessAction::Resume)
	{
		auto processId = GetProcessId(process);

		auto handle = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
		if (handle != INVALID_HANDLE_VALUE)
		{
			auto fn = action == ControlRemoteProcessAction::Suspend ? SuspendThread : ResumeThread;

			THREADENTRY32 te32 = {};
			te32.dwSize = sizeof(THREADENTRY32);
			if (Thread32First(handle, &te32))
			{
				do
				{
					if (te32.th32OwnerProcessID == processId)
					{
						auto threadHandle = OpenThread(THREAD_SUSPEND_RESUME, FALSE, te32.th32ThreadID);
						if (threadHandle)
						{
							fn(threadHandle);

							CloseHandle(threadHandle);
						}
					}
				} while (Thread32Next(handle, &te32));
			}

			CloseHandle(handle);
		}
	}
	else if (action == ControlRemoteProcessAction::Terminate)
	{
		TerminateProcess(process, 0);
	}
}
<|endoftext|>


<commit_before>/*
 *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

#include "webrtc/modules/video_coding/utility/ivf_file_writer.h"

#include "webrtc/base/checks.h"
#include "webrtc/base/logging.h"
#include "webrtc/modules/rtp_rtcp/source/byte_io.h"

namespace webrtc {

IvfFileWriter::IvfFileWriter(const std::string& file_name,
                             std::unique_ptr<FileWrapper> file,
                             VideoCodecType codec_type)
    : codec_type_(codec_type),
      num_frames_(0),
      width_(0),
      height_(0),
      last_timestamp_(-1),
      using_capture_timestamps_(false),
      file_name_(file_name),
      file_(std::move(file)) {}

IvfFileWriter::~IvfFileWriter() {
  Close();
}

const size_t kIvfHeaderSize = 32;

std::unique_ptr<IvfFileWriter> IvfFileWriter::Open(const std::string& file_name,
                                                   VideoCodecType codec_type) {
  std::unique_ptr<IvfFileWriter> file_writer;
  std::unique_ptr<FileWrapper> file(FileWrapper::Create());
  if (file->OpenFile(file_name.c_str(), false) != 0)
    return file_writer;

  file_writer.reset(new IvfFileWriter(
      file_name, std::unique_ptr<FileWrapper>(std::move(file)), codec_type));
  if (!file_writer->WriteHeader())
    file_writer.reset();

  return file_writer;
}

bool IvfFileWriter::WriteHeader() {
  if (file_->Rewind() != 0) {
    LOG(LS_WARNING) << "Unable to rewind output file " << file_name_;
    return false;
  }

  uint8_t ivf_header[kIvfHeaderSize] = {0};
  ivf_header[0] = 'D';
  ivf_header[1] = 'K';
  ivf_header[2] = 'I';
  ivf_header[3] = 'F';
  ByteWriter<uint16_t>::WriteLittleEndian(&ivf_header[4], 0);   // Version.
  ByteWriter<uint16_t>::WriteLittleEndian(&ivf_header[6], 32);  // Header size.

  switch (codec_type_) {
    case kVideoCodecVP8:
      ivf_header[8] = 'V';
      ivf_header[9] = 'P';
      ivf_header[10] = '8';
      ivf_header[11] = '0';
      break;
    case kVideoCodecVP9:
      ivf_header[8] = 'V';
      ivf_header[9] = 'P';
      ivf_header[10] = '9';
      ivf_header[11] = '0';
      break;
    case kVideoCodecH264:
      ivf_header[8] = 'H';
      ivf_header[9] = '2';
      ivf_header[10] = '6';
      ivf_header[11] = '4';
      break;
    default:
      LOG(LS_ERROR) << "Unknown CODEC type: " << codec_type_;
      return false;
  }

  ByteWriter<uint16_t>::WriteLittleEndian(&ivf_header[12], width_);
  ByteWriter<uint16_t>::WriteLittleEndian(&ivf_header[14], height_);
  // Render timestamps are in ms (1/1000 scale), while RTP timestamps use a
  // 90kHz clock.
  ByteWriter<uint32_t>::WriteLittleEndian(
      &ivf_header[16], using_capture_timestamps_ ? 1000 : 90000);
  ByteWriter<uint32_t>::WriteLittleEndian(&ivf_header[20], 1);
  ByteWriter<uint32_t>::WriteLittleEndian(&ivf_header[24],
                                          static_cast<uint32_t>(num_frames_));
  ByteWriter<uint32_t>::WriteLittleEndian(&ivf_header[28], 0);  // Reserved.

  if (!file_->Write(ivf_header, kIvfHeaderSize)) {
    LOG(LS_ERROR) << "Unable to write IVF header for file " << file_name_;
    return false;
  }

  return true;
}

bool IvfFileWriter::InitFromFirstFrame(const EncodedImage& encoded_image) {
  width_ = encoded_image._encodedWidth;
  height_ = encoded_image._encodedHeight;
  RTC_CHECK_GT(width_, 0);
  RTC_CHECK_GT(height_, 0);
  using_capture_timestamps_ = encoded_image._timeStamp == 0;

  if (!WriteHeader())
    return false;

  std::string codec_name;
  switch (codec_type_) {
    case kRtpVideoVp8:
      codec_name = "VP8";
      break;
    case kRtpVideoVp9:
      codec_name = "VP9";
      break;
    case kRtpVideoH264:
      codec_name = "H264";
      break;
    default:
      codec_name = "Unkown";
  }
  LOG(LS_WARNING) << "Created IVF file " << file_name_
                  << " for codec data of type " << codec_name
                  << " at resolution " << width_ << " x " << height_
                  << ", using " << (using_capture_timestamps_ ? "1" : "90")
                  << "kHz clock resolution.";
  return true;
}

bool IvfFileWriter::WriteFrame(const EncodedImage& encoded_image) {
  RTC_DCHECK(file_->Open());

  if (num_frames_ == 0 && !InitFromFirstFrame(encoded_image))
    return false;

  if ((encoded_image._encodedWidth > 0 || encoded_image._encodedHeight > 0) &&
      (encoded_image._encodedHeight != height_ ||
       encoded_image._encodedWidth != width_)) {
    LOG(LS_WARNING)
        << "Incomig frame has diffferent resolution then previous: (" << width_
        << "x" << height_ << ") -> (" << encoded_image._encodedWidth << "x"
        << encoded_image._encodedHeight << ")";
  }

  int64_t timestamp = using_capture_timestamps_
                          ? encoded_image.capture_time_ms_
                          : wrap_handler_.Unwrap(encoded_image._timeStamp);
  if (last_timestamp_ != -1 && timestamp <= last_timestamp_) {
    LOG(LS_WARNING) << "Timestamp no increasing: " << last_timestamp_ << " -> "
                    << timestamp;
  }
  last_timestamp_ = timestamp;

  const size_t kFrameHeaderSize = 12;
  uint8_t frame_header[kFrameHeaderSize] = {};
  ByteWriter<uint32_t>::WriteLittleEndian(
      &frame_header[0], static_cast<uint32_t>(encoded_image._length));
  ByteWriter<uint64_t>::WriteLittleEndian(&frame_header[4], timestamp);
  if (!file_->Write(frame_header, kFrameHeaderSize) ||
      !file_->Write(encoded_image._buffer, encoded_image._length)) {
    LOG(LS_ERROR) << "Unable to write frame to file " << file_name_;
    return false;
  }

  ++num_frames_;
  return true;
}

bool IvfFileWriter::Close() {
  if (!file_->Open())
    return false;

  if (num_frames_ == 0) {
    // No frame written to file, close and remove it entirely if possible.
    file_->CloseFile();
    if (remove(file_name_.c_str()) != 0)
      LOG(LS_WARNING) << "Failed to remove empty IVF file " << file_name_;

    return true;
  }

  return WriteHeader() && (file_->CloseFile() == 0);
}

}  // namespace webrtc
<commit_msg>Fix codec name logging in ivf_file_writer.cc<commit_after>/*
 *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

#include "webrtc/modules/video_coding/utility/ivf_file_writer.h"

#include "webrtc/base/checks.h"
#include "webrtc/base/logging.h"
#include "webrtc/modules/rtp_rtcp/source/byte_io.h"

namespace webrtc {

IvfFileWriter::IvfFileWriter(const std::string& file_name,
                             std::unique_ptr<FileWrapper> file,
                             VideoCodecType codec_type)
    : codec_type_(codec_type),
      num_frames_(0),
      width_(0),
      height_(0),
      last_timestamp_(-1),
      using_capture_timestamps_(false),
      file_name_(file_name),
      file_(std::move(file)) {}

IvfFileWriter::~IvfFileWriter() {
  Close();
}

const size_t kIvfHeaderSize = 32;

std::unique_ptr<IvfFileWriter> IvfFileWriter::Open(const std::string& file_name,
                                                   VideoCodecType codec_type) {
  std::unique_ptr<IvfFileWriter> file_writer;
  std::unique_ptr<FileWrapper> file(FileWrapper::Create());
  if (file->OpenFile(file_name.c_str(), false) != 0)
    return file_writer;

  file_writer.reset(new IvfFileWriter(
      file_name, std::unique_ptr<FileWrapper>(std::move(file)), codec_type));
  if (!file_writer->WriteHeader())
    file_writer.reset();

  return file_writer;
}

bool IvfFileWriter::WriteHeader() {
  if (file_->Rewind() != 0) {
    LOG(LS_WARNING) << "Unable to rewind output file " << file_name_;
    return false;
  }

  uint8_t ivf_header[kIvfHeaderSize] = {0};
  ivf_header[0] = 'D';
  ivf_header[1] = 'K';
  ivf_header[2] = 'I';
  ivf_header[3] = 'F';
  ByteWriter<uint16_t>::WriteLittleEndian(&ivf_header[4], 0);   // Version.
  ByteWriter<uint16_t>::WriteLittleEndian(&ivf_header[6], 32);  // Header size.

  switch (codec_type_) {
    case kVideoCodecVP8:
      ivf_header[8] = 'V';
      ivf_header[9] = 'P';
      ivf_header[10] = '8';
      ivf_header[11] = '0';
      break;
    case kVideoCodecVP9:
      ivf_header[8] = 'V';
      ivf_header[9] = 'P';
      ivf_header[10] = '9';
      ivf_header[11] = '0';
      break;
    case kVideoCodecH264:
      ivf_header[8] = 'H';
      ivf_header[9] = '2';
      ivf_header[10] = '6';
      ivf_header[11] = '4';
      break;
    default:
      LOG(LS_ERROR) << "Unknown CODEC type: " << codec_type_;
      return false;
  }

  ByteWriter<uint16_t>::WriteLittleEndian(&ivf_header[12], width_);
  ByteWriter<uint16_t>::WriteLittleEndian(&ivf_header[14], height_);
  // Render timestamps are in ms (1/1000 scale), while RTP timestamps use a
  // 90kHz clock.
  ByteWriter<uint32_t>::WriteLittleEndian(
      &ivf_header[16], using_capture_timestamps_ ? 1000 : 90000);
  ByteWriter<uint32_t>::WriteLittleEndian(&ivf_header[20], 1);
  ByteWriter<uint32_t>::WriteLittleEndian(&ivf_header[24],
                                          static_cast<uint32_t>(num_frames_));
  ByteWriter<uint32_t>::WriteLittleEndian(&ivf_header[28], 0);  // Reserved.

  if (!file_->Write(ivf_header, kIvfHeaderSize)) {
    LOG(LS_ERROR) << "Unable to write IVF header for file " << file_name_;
    return false;
  }

  return true;
}

bool IvfFileWriter::InitFromFirstFrame(const EncodedImage& encoded_image) {
  width_ = encoded_image._encodedWidth;
  height_ = encoded_image._encodedHeight;
  RTC_CHECK_GT(width_, 0);
  RTC_CHECK_GT(height_, 0);
  using_capture_timestamps_ = encoded_image._timeStamp == 0;

  if (!WriteHeader())
    return false;

  std::string codec_name;
  switch (codec_type_) {
    case kVideoCodecVP8:
      codec_name = "VP8";
      break;
    case kVideoCodecVP9:
      codec_name = "VP9";
      break;
    case kVideoCodecH264:
      codec_name = "H264";
      break;
    default:
      codec_name = "Unknown";
  }
  LOG(LS_WARNING) << "Created IVF file " << file_name_
                  << " for codec data of type " << codec_name
                  << " at resolution " << width_ << " x " << height_
                  << ", using " << (using_capture_timestamps_ ? "1" : "90")
                  << "kHz clock resolution.";
  return true;
}

bool IvfFileWriter::WriteFrame(const EncodedImage& encoded_image) {
  RTC_DCHECK(file_->Open());

  if (num_frames_ == 0 && !InitFromFirstFrame(encoded_image))
    return false;

  if ((encoded_image._encodedWidth > 0 || encoded_image._encodedHeight > 0) &&
      (encoded_image._encodedHeight != height_ ||
       encoded_image._encodedWidth != width_)) {
    LOG(LS_WARNING)
        << "Incomig frame has diffferent resolution then previous: (" << width_
        << "x" << height_ << ") -> (" << encoded_image._encodedWidth << "x"
        << encoded_image._encodedHeight << ")";
  }

  int64_t timestamp = using_capture_timestamps_
                          ? encoded_image.capture_time_ms_
                          : wrap_handler_.Unwrap(encoded_image._timeStamp);
  if (last_timestamp_ != -1 && timestamp <= last_timestamp_) {
    LOG(LS_WARNING) << "Timestamp no increasing: " << last_timestamp_ << " -> "
                    << timestamp;
  }
  last_timestamp_ = timestamp;

  const size_t kFrameHeaderSize = 12;
  uint8_t frame_header[kFrameHeaderSize] = {};
  ByteWriter<uint32_t>::WriteLittleEndian(
      &frame_header[0], static_cast<uint32_t>(encoded_image._length));
  ByteWriter<uint64_t>::WriteLittleEndian(&frame_header[4], timestamp);
  if (!file_->Write(frame_header, kFrameHeaderSize) ||
      !file_->Write(encoded_image._buffer, encoded_image._length)) {
    LOG(LS_ERROR) << "Unable to write frame to file " << file_name_;
    return false;
  }

  ++num_frames_;
  return true;
}

bool IvfFileWriter::Close() {
  if (!file_->Open())
    return false;

  if (num_frames_ == 0) {
    // No frame written to file, close and remove it entirely if possible.
    file_->CloseFile();
    if (remove(file_name_.c_str()) != 0)
      LOG(LS_WARNING) << "Failed to remove empty IVF file " << file_name_;

    return true;
  }

  return WriteHeader() && (file_->CloseFile() == 0);
}

}  // namespace webrtc
<|endoftext|>