<commit_before>/* Copyright (C) 2013 BMW Group
 * Author: Manfred Bathelt (manfred.bathelt@bmw.de)
 * Author: Juergen Gehring (juergen.gehring@bmw.de)
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <dlfcn.h>

#include <algorithm>
#include <iostream>
#include <unordered_map>
#include <stdexcept>

#include "Runtime.h"
#include "Configuration.h"
#include "utils.h"

namespace CommonAPI {

static std::unordered_map<std::string, MiddlewareRuntimeLoadFunction>* registeredRuntimeLoadFunctions_;
static bool isDynamic_ = false;

static const char COMMONAPI_LIB_PREFIX[] = "libCommonAPI-";
static const char MIDDLEWARE_INFO_SYMBOL_NAME[] = "middlewareInfo";

inline bool Runtime::tryLoadLibrary(const std::string& libraryPath,
                                    void** sharedLibraryHandle,
                                    MiddlewareInfo** foundMiddlewareInfo) {

    //In case we find an already loaded library again while looking for another one,
    //there is no need to look at it
    if (dlopen(libraryPath.c_str(), RTLD_NOLOAD)) {
        return false;
    }

    //In order to place symbols of the newly loaded library ahead of already resolved symbols, we need
    //RTLD_DEEPBIND. This is necessary for this case: A library already is linked at compile time, but while
    //trying to resolve another library dynamically we might find the very same library again.
    //dlopen() doesn't know about the compile time linked library and will close it if dlclose() ever is
    //called, thereby causing memory corruptions. Therefore, we must be able to access the middlewareInfo
    //of the newly dlopened library in order to determine whether it already has been linked.
    *sharedLibraryHandle = dlopen(libraryPath.c_str(), RTLD_LAZY | RTLD_LOCAL | RTLD_DEEPBIND);
    if (sharedLibraryHandle == NULL) {
        return false;
    }

    *foundMiddlewareInfo = static_cast<MiddlewareInfo*>(dlsym(*sharedLibraryHandle, MIDDLEWARE_INFO_SYMBOL_NAME));

    //In this context, a resolved value of NULL it is just as invalid as if dlerror() was set additionally.
    if (!*foundMiddlewareInfo) {
        dlclose(*sharedLibraryHandle);
        return false;
    }

    if (!(*foundMiddlewareInfo)->middlewareName_ || !(*foundMiddlewareInfo)->getInstance_) {
        dlclose(sharedLibraryHandle);
        return false;
    }

    return true;
}

bool Runtime::checkAndLoadLibrary(const std::string& libraryPath,
                                  const std::string& requestedBindingIdentifier,
                                  bool keepLibrary) {

    void* sharedLibraryHandle = NULL;
    MiddlewareInfo* foundMiddlewareInfo;
    if (!tryLoadLibrary(libraryPath, &sharedLibraryHandle, &foundMiddlewareInfo)) {
        return false;
    }

    if (foundMiddlewareInfo->middlewareName_ != requestedBindingIdentifier) {
        //If library was linked at compile time (and therefore an appropriate runtime loader is registered),
        //the library must not be closed!
        auto foundRegisteredRuntimeLoader = registeredRuntimeLoadFunctions_->find(foundMiddlewareInfo->middlewareName_);
        if (foundRegisteredRuntimeLoader == registeredRuntimeLoadFunctions_->end()) {
            dlclose(sharedLibraryHandle);
        }
        return false;
    }

    if (!keepLibrary) {
        dlclose(sharedLibraryHandle);
    } else {
        //Extend visibility to make symbols available to all other libraries that are loaded afterwards,
        //e.g. libraries containing generated binding specific code.
        sharedLibraryHandle = dlopen(libraryPath.c_str(), RTLD_NOW | RTLD_GLOBAL);
        if (!sharedLibraryHandle) {
            return false;
        }
        registeredRuntimeLoadFunctions_->insert( {foundMiddlewareInfo->middlewareName_, foundMiddlewareInfo->getInstance_} );
    }

    return true;
}

bool Runtime::checkAndLoadDefaultLibrary(std::string& foundBindingIdentifier, const std::string& libraryPath) {
    void* sharedLibraryHandle = NULL;
    MiddlewareInfo* foundMiddlewareInfo;
    if (!tryLoadLibrary(libraryPath, &sharedLibraryHandle, &foundMiddlewareInfo)) {
        return false;
    }

    //Extend visibility to make symbols available to all other linked libraries,
    //e.g. libraries containing generated binding specific code
    sharedLibraryHandle = dlopen(libraryPath.c_str(), RTLD_NOW | RTLD_GLOBAL);
    if (!sharedLibraryHandle) {
        return false;
    }
    registeredRuntimeLoadFunctions_->insert( {foundMiddlewareInfo->middlewareName_, foundMiddlewareInfo->getInstance_} );
    foundBindingIdentifier = foundMiddlewareInfo->middlewareName_;

    return true;
}

const std::vector<std::string> Runtime::readDirectory(const std::string& path) {
    std::vector<std::string> result;
    struct stat filestat;

    DIR *directory = opendir(path.c_str());

    if (!directory) {
        return std::vector<std::string>();
    }

    struct dirent* entry;

    while ((entry = readdir(directory))) {
        const std::string fqnOfEntry = path + entry->d_name;

        if (stat(fqnOfEntry.c_str(), &filestat)) {
            continue;
        }
        if (S_ISDIR(filestat.st_mode)) {
            continue;
        }

        if (strncmp(COMMONAPI_LIB_PREFIX, entry->d_name, strlen(COMMONAPI_LIB_PREFIX)) != 0) {
            continue;
        }

        const char* fileNamePtr = entry->d_name;
        while ((fileNamePtr = strchr(fileNamePtr + 1, '.'))) {
            if (strncmp(".so", fileNamePtr, 3) == 0) {
                break;
            }
        }

        if (fileNamePtr) {
            result.push_back(fqnOfEntry);
        }
    }

    closedir (directory);

    std::sort( result.begin(), result.end() );

    return result;
}

struct LibraryVersion {
    int32_t major;
    int32_t minor;
    int32_t revision;
};

bool operator<(LibraryVersion const& lhs, LibraryVersion const& rhs) {
    if (lhs.major == rhs.major) {
        if (lhs.minor == rhs.minor) {
            return lhs.revision < rhs.revision;
        }
        return lhs.minor < rhs.minor;
    }
    return lhs.major < rhs.major;
}

std::shared_ptr<Runtime> Runtime::checkDynamicLibraries(const std::string& requestedMiddlewareName, LoadState& loadState) {
    const std::string& middlewareLibraryPath = Configuration::getInstance().getMiddlewareLibraryPath(requestedMiddlewareName);

    if (middlewareLibraryPath != "") {
        if (!checkAndLoadLibrary(middlewareLibraryPath, requestedMiddlewareName, true)) {
            //A path for requestedMiddlewareName was configured, but no corresponding library was found
            loadState = LoadState::CONFIGURATION_ERROR;
            return std::shared_ptr<Runtime>(NULL);
        } else {
            const std::string currentBinaryFQN = getCurrentBinaryFileFQN();
            const uint32_t lastPathSeparatorPosition = currentBinaryFQN.find_last_of("/\\");
            const std::string currentBinaryPath = currentBinaryFQN.substr(0, lastPathSeparatorPosition + 1);
            auto foundRuntimeLoader = registeredRuntimeLoadFunctions_->find(requestedMiddlewareName);
            if (foundRuntimeLoader != registeredRuntimeLoadFunctions_->end()) {
                return (foundRuntimeLoader->second)();
            }
            //One should not get here
            loadState = LoadState::BINDING_ERROR;
            return std::shared_ptr<Runtime>(NULL);
        }
    }

    const std::vector<std::string>& librarySearchPaths = Configuration::getInstance().getLibrarySearchPaths();

    LibraryVersion highestVersionFound = {0, 0, 0};
    std::string fqnOfHighestVersion = "";
    struct stat filestat;

    for (const std::string& singleSearchPath: librarySearchPaths) {
        std::vector<std::string> orderedLibraries = readDirectory(singleSearchPath);

        for (const std::string& fqnOfEntry : orderedLibraries) {
            std::string versionString;
            LibraryVersion currentLibraryVersion = {-1, -1, -1};

            const char* fileNamePtr = fqnOfEntry.c_str();
            while ((fileNamePtr = strchr(fileNamePtr + 1, '.'))) {
                if (strncmp(".so", fileNamePtr, 3) == 0) {
                    break;
                }
            }

            const char* positionOfFirstDot = strchr(fileNamePtr + 1, '.');
            if (positionOfFirstDot) {
                versionString = positionOfFirstDot + 1;
            }

            std::vector<std::string> versionElements = split(versionString, '.');
            if (versionElements.size() >= 1 && containsOnlyDigits(versionElements[0])) {
                currentLibraryVersion.major = strtol(versionElements[0].c_str(), NULL, 0);
            }
            if (versionElements.size() >= 3 && containsOnlyDigits(versionElements[2])) {
                currentLibraryVersion.minor = strtol(versionElements[1].c_str(), NULL, 0);
                currentLibraryVersion.revision = strtol(versionElements[2].c_str(), NULL, 0);
            }

            if (highestVersionFound < currentLibraryVersion) {
                if (!checkAndLoadLibrary(fqnOfEntry, requestedMiddlewareName, false)) {
                    continue;
                }
                highestVersionFound = currentLibraryVersion;
                fqnOfHighestVersion = fqnOfEntry;
            }
        }
    }

    if (fqnOfHighestVersion != "") {
        checkAndLoadLibrary(fqnOfHighestVersion, requestedMiddlewareName, true);

        auto foundRuntimeLoader = registeredRuntimeLoadFunctions_->find(requestedMiddlewareName);
        if (foundRuntimeLoader != registeredRuntimeLoadFunctions_->end()) {
            std::shared_ptr<Runtime> loadedRuntime = foundRuntimeLoader->second();
            if (!loadedRuntime) {
                loadState = LoadState::BINDING_ERROR;
            }
            return loadedRuntime;
        }
    }

    loadState = LoadState::NO_LIBRARY_FOUND;

    return std::shared_ptr<Runtime>();
}

std::shared_ptr<Runtime> Runtime::checkDynamicLibraries(LoadState& loadState) {
    const std::vector<std::string>& librarySearchPaths = Configuration::getInstance().getLibrarySearchPaths();

    for (const std::string& singleSearchPath : librarySearchPaths) {
        std::vector<std::string> orderedLibraries = readDirectory(singleSearchPath);

        for (const std::string& fqnOfEntry: orderedLibraries) {
            std::string foundBindingName;
            if (!checkAndLoadDefaultLibrary(foundBindingName, fqnOfEntry)) {
                continue;
            }

            auto foundRuntimeLoader = registeredRuntimeLoadFunctions_->find(foundBindingName);
            if (foundRuntimeLoader != registeredRuntimeLoadFunctions_->end()) {
                return (foundRuntimeLoader->second)();
            }
        }
    }

    loadState = LoadState::NO_LIBRARY_FOUND;

    return std::shared_ptr<Runtime>();
}

void Runtime::registerRuntimeLoader(const std::string& middlewareName, const MiddlewareRuntimeLoadFunction& middlewareRuntimeLoadFunction) {
    if (!registeredRuntimeLoadFunctions_) {
        registeredRuntimeLoadFunctions_ = new std::unordered_map<std::string, MiddlewareRuntimeLoadFunction>();
    }
    if (!isDynamic_) {
        registeredRuntimeLoadFunctions_->insert( {middlewareName, middlewareRuntimeLoadFunction});
    }
}

std::shared_ptr<Runtime> Runtime::load() {
    LoadState dummyState;
    return load(dummyState);
}

std::shared_ptr<Runtime> Runtime::load(LoadState& loadState) {
    isDynamic_ = true;
    loadState = LoadState::SUCCESS;
    if(!registeredRuntimeLoadFunctions_) {
        registeredRuntimeLoadFunctions_ = new std::unordered_map<std::string, MiddlewareRuntimeLoadFunction>();
    }

    const std::string& defaultBindingIdentifier = Configuration::getInstance().getDefaultMiddlewareIdentifier();
    if (defaultBindingIdentifier != "") {
        const auto defaultRuntimeLoader = registeredRuntimeLoadFunctions_->find(defaultBindingIdentifier);
        if (defaultRuntimeLoader != registeredRuntimeLoadFunctions_->end()) {
            return (defaultRuntimeLoader->second)();
        }
        return checkDynamicLibraries(defaultBindingIdentifier, loadState);

    } else {
        const auto defaultRuntimeLoader = registeredRuntimeLoadFunctions_->begin();
        if (defaultRuntimeLoader != registeredRuntimeLoadFunctions_->end()) {
            return (defaultRuntimeLoader->second)();
        }
        return checkDynamicLibraries(loadState);
    }
}

std::shared_ptr<Runtime> Runtime::load(const std::string& middlewareIdOrAlias) {
    LoadState dummyState;
    return load(middlewareIdOrAlias, dummyState);
}

std::shared_ptr<Runtime> Runtime::load(const std::string& middlewareIdOrAlias, LoadState& loadState) {
    isDynamic_ = true;
    loadState = LoadState::SUCCESS;
    if (!registeredRuntimeLoadFunctions_) {
        registeredRuntimeLoadFunctions_ = new std::unordered_map<std::string, MiddlewareRuntimeLoadFunction>();
    }

    const std::string middlewareName = Configuration::getInstance().getMiddlewareNameForAlias(middlewareIdOrAlias);

    auto foundRuntimeLoader = registeredRuntimeLoadFunctions_->find(middlewareName);
    if (foundRuntimeLoader != registeredRuntimeLoadFunctions_->end()) {
        return (foundRuntimeLoader->second)();
    }

    return checkDynamicLibraries(middlewareName, loadState);
}

std::shared_ptr<MainLoopContext> Runtime::getNewMainLoopContext() const {
    return std::make_shared<MainLoopContext>();
}

std::shared_ptr<Factory> Runtime::createFactory(const std::string factoryName,
                                                const bool nullOnInvalidName) {
    return createFactory(std::shared_ptr<MainLoopContext>(NULL), factoryName, nullOnInvalidName);
}

std::shared_ptr<Factory> Runtime::createFactory(std::shared_ptr<MainLoopContext> mainLoopContext,
                                                const std::string factoryName,
                                                const bool nullOnInvalidName) {
    if(mainLoopContext && !mainLoopContext->isInitialized()) {
        return std::shared_ptr<Factory>(NULL);
    }
    return doCreateFactory(mainLoopContext, factoryName, nullOnInvalidName);
}

} // namespace CommonAPI
<commit_msg>Fix compatibility with MacOS<commit_after>/* Copyright (C) 2013 BMW Group
 * Author: Manfred Bathelt (manfred.bathelt@bmw.de)
 * Author: Juergen Gehring (juergen.gehring@bmw.de)
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <dlfcn.h>

#include <algorithm>
#include <iostream>
#include <unordered_map>
#include <stdexcept>

#include "Runtime.h"
#include "Configuration.h"
#include "utils.h"

namespace CommonAPI {

static std::unordered_map<std::string, MiddlewareRuntimeLoadFunction>* registeredRuntimeLoadFunctions_;
static bool isDynamic_ = false;

static const char COMMONAPI_LIB_PREFIX[] = "libCommonAPI-";
static const char MIDDLEWARE_INFO_SYMBOL_NAME[] = "middlewareInfo";

inline bool Runtime::tryLoadLibrary(const std::string& libraryPath,
                                    void** sharedLibraryHandle,
                                    MiddlewareInfo** foundMiddlewareInfo) {

    //In case we find an already loaded library again while looking for another one,
    //there is no need to look at it
    if (dlopen(libraryPath.c_str(), RTLD_NOLOAD)) {
        return false;
    }

    //In order to place symbols of the newly loaded library ahead of already resolved symbols, we need
    //RTLD_DEEPBIND. This is necessary for this case: A library already is linked at compile time, but while
    //trying to resolve another library dynamically we might find the very same library again.
    //dlopen() doesn't know about the compile time linked library and will close it if dlclose() ever is
    //called, thereby causing memory corruptions. Therefore, we must be able to access the middlewareInfo
    //of the newly dlopened library in order to determine whether it already has been linked.
    auto flags = RTLD_LAZY | RTLD_LOCAL;
#ifdef __linux__
    flags |= RTLD_DEEPBIND;
#endif
    *sharedLibraryHandle = dlopen(libraryPath.c_str(), flags);
    if (sharedLibraryHandle == NULL) {
        return false;
    }

    *foundMiddlewareInfo = static_cast<MiddlewareInfo*>(dlsym(*sharedLibraryHandle, MIDDLEWARE_INFO_SYMBOL_NAME));

    //In this context, a resolved value of NULL it is just as invalid as if dlerror() was set additionally.
    if (!*foundMiddlewareInfo) {
        dlclose(*sharedLibraryHandle);
        return false;
    }

    if (!(*foundMiddlewareInfo)->middlewareName_ || !(*foundMiddlewareInfo)->getInstance_) {
        dlclose(sharedLibraryHandle);
        return false;
    }

    return true;
}

bool Runtime::checkAndLoadLibrary(const std::string& libraryPath,
                                  const std::string& requestedBindingIdentifier,
                                  bool keepLibrary) {

    void* sharedLibraryHandle = NULL;
    MiddlewareInfo* foundMiddlewareInfo;
    if (!tryLoadLibrary(libraryPath, &sharedLibraryHandle, &foundMiddlewareInfo)) {
        return false;
    }

    if (foundMiddlewareInfo->middlewareName_ != requestedBindingIdentifier) {
        //If library was linked at compile time (and therefore an appropriate runtime loader is registered),
        //the library must not be closed!
        auto foundRegisteredRuntimeLoader = registeredRuntimeLoadFunctions_->find(foundMiddlewareInfo->middlewareName_);
        if (foundRegisteredRuntimeLoader == registeredRuntimeLoadFunctions_->end()) {
            dlclose(sharedLibraryHandle);
        }
        return false;
    }

    if (!keepLibrary) {
        dlclose(sharedLibraryHandle);
    } else {
        //Extend visibility to make symbols available to all other libraries that are loaded afterwards,
        //e.g. libraries containing generated binding specific code.
        sharedLibraryHandle = dlopen(libraryPath.c_str(), RTLD_NOW | RTLD_GLOBAL);
        if (!sharedLibraryHandle) {
            return false;
        }
        registeredRuntimeLoadFunctions_->insert( {foundMiddlewareInfo->middlewareName_, foundMiddlewareInfo->getInstance_} );
    }

    return true;
}

bool Runtime::checkAndLoadDefaultLibrary(std::string& foundBindingIdentifier, const std::string& libraryPath) {
    void* sharedLibraryHandle = NULL;
    MiddlewareInfo* foundMiddlewareInfo;
    if (!tryLoadLibrary(libraryPath, &sharedLibraryHandle, &foundMiddlewareInfo)) {
        return false;
    }

    //Extend visibility to make symbols available to all other linked libraries,
    //e.g. libraries containing generated binding specific code
    sharedLibraryHandle = dlopen(libraryPath.c_str(), RTLD_NOW | RTLD_GLOBAL);
    if (!sharedLibraryHandle) {
        return false;
    }
    registeredRuntimeLoadFunctions_->insert( {foundMiddlewareInfo->middlewareName_, foundMiddlewareInfo->getInstance_} );
    foundBindingIdentifier = foundMiddlewareInfo->middlewareName_;

    return true;
}

const std::vector<std::string> Runtime::readDirectory(const std::string& path) {
    std::vector<std::string> result;
    struct stat filestat;

    DIR *directory = opendir(path.c_str());

    if (!directory) {
        return std::vector<std::string>();
    }

    struct dirent* entry;

    while ((entry = readdir(directory))) {
        const std::string fqnOfEntry = path + entry->d_name;

        if (stat(fqnOfEntry.c_str(), &filestat)) {
            continue;
        }
        if (S_ISDIR(filestat.st_mode)) {
            continue;
        }

        if (strncmp(COMMONAPI_LIB_PREFIX, entry->d_name, strlen(COMMONAPI_LIB_PREFIX)) != 0) {
            continue;
        }

        const char* fileNamePtr = entry->d_name;
        while ((fileNamePtr = strchr(fileNamePtr + 1, '.'))) {
            if (strncmp(".so", fileNamePtr, 3) == 0) {
                break;
            }
        }

        if (fileNamePtr) {
            result.push_back(fqnOfEntry);
        }
    }

    closedir (directory);

    std::sort( result.begin(), result.end() );

    return result;
}

struct LibraryVersion {
    int32_t major;
    int32_t minor;
    int32_t revision;
};

bool operator<(LibraryVersion const& lhs, LibraryVersion const& rhs) {
    if (lhs.major == rhs.major) {
        if (lhs.minor == rhs.minor) {
            return lhs.revision < rhs.revision;
        }
        return lhs.minor < rhs.minor;
    }
    return lhs.major < rhs.major;
}

std::shared_ptr<Runtime> Runtime::checkDynamicLibraries(const std::string& requestedMiddlewareName, LoadState& loadState) {
    const std::string& middlewareLibraryPath = Configuration::getInstance().getMiddlewareLibraryPath(requestedMiddlewareName);

    if (middlewareLibraryPath != "") {
        if (!checkAndLoadLibrary(middlewareLibraryPath, requestedMiddlewareName, true)) {
            //A path for requestedMiddlewareName was configured, but no corresponding library was found
            loadState = LoadState::CONFIGURATION_ERROR;
            return std::shared_ptr<Runtime>(NULL);
        } else {
            const std::string currentBinaryFQN = getCurrentBinaryFileFQN();
            const uint32_t lastPathSeparatorPosition = currentBinaryFQN.find_last_of("/\\");
            const std::string currentBinaryPath = currentBinaryFQN.substr(0, lastPathSeparatorPosition + 1);
            auto foundRuntimeLoader = registeredRuntimeLoadFunctions_->find(requestedMiddlewareName);
            if (foundRuntimeLoader != registeredRuntimeLoadFunctions_->end()) {
                return (foundRuntimeLoader->second)();
            }
            //One should not get here
            loadState = LoadState::BINDING_ERROR;
            return std::shared_ptr<Runtime>(NULL);
        }
    }

    const std::vector<std::string>& librarySearchPaths = Configuration::getInstance().getLibrarySearchPaths();

    LibraryVersion highestVersionFound = {0, 0, 0};
    std::string fqnOfHighestVersion = "";
    struct stat filestat;

    for (const std::string& singleSearchPath: librarySearchPaths) {
        std::vector<std::string> orderedLibraries = readDirectory(singleSearchPath);

        for (const std::string& fqnOfEntry : orderedLibraries) {
            std::string versionString;
            LibraryVersion currentLibraryVersion = {-1, -1, -1};

            const char* fileNamePtr = fqnOfEntry.c_str();
            while ((fileNamePtr = strchr(fileNamePtr + 1, '.'))) {
                if (strncmp(".so", fileNamePtr, 3) == 0) {
                    break;
                }
            }

            const char* positionOfFirstDot = strchr(fileNamePtr + 1, '.');
            if (positionOfFirstDot) {
                versionString = positionOfFirstDot + 1;
            }

            std::vector<std::string> versionElements = split(versionString, '.');
            if (versionElements.size() >= 1 && containsOnlyDigits(versionElements[0])) {
                currentLibraryVersion.major = strtol(versionElements[0].c_str(), NULL, 0);
            }
            if (versionElements.size() >= 3 && containsOnlyDigits(versionElements[2])) {
                currentLibraryVersion.minor = strtol(versionElements[1].c_str(), NULL, 0);
                currentLibraryVersion.revision = strtol(versionElements[2].c_str(), NULL, 0);
            }

            if (highestVersionFound < currentLibraryVersion) {
                if (!checkAndLoadLibrary(fqnOfEntry, requestedMiddlewareName, false)) {
                    continue;
                }
                highestVersionFound = currentLibraryVersion;
                fqnOfHighestVersion = fqnOfEntry;
            }
        }
    }

    if (fqnOfHighestVersion != "") {
        checkAndLoadLibrary(fqnOfHighestVersion, requestedMiddlewareName, true);

        auto foundRuntimeLoader = registeredRuntimeLoadFunctions_->find(requestedMiddlewareName);
        if (foundRuntimeLoader != registeredRuntimeLoadFunctions_->end()) {
            std::shared_ptr<Runtime> loadedRuntime = foundRuntimeLoader->second();
            if (!loadedRuntime) {
                loadState = LoadState::BINDING_ERROR;
            }
            return loadedRuntime;
        }
    }

    loadState = LoadState::NO_LIBRARY_FOUND;

    return std::shared_ptr<Runtime>();
}

std::shared_ptr<Runtime> Runtime::checkDynamicLibraries(LoadState& loadState) {
    const std::vector<std::string>& librarySearchPaths = Configuration::getInstance().getLibrarySearchPaths();

    for (const std::string& singleSearchPath : librarySearchPaths) {
        std::vector<std::string> orderedLibraries = readDirectory(singleSearchPath);

        for (const std::string& fqnOfEntry: orderedLibraries) {
            std::string foundBindingName;
            if (!checkAndLoadDefaultLibrary(foundBindingName, fqnOfEntry)) {
                continue;
            }

            auto foundRuntimeLoader = registeredRuntimeLoadFunctions_->find(foundBindingName);
            if (foundRuntimeLoader != registeredRuntimeLoadFunctions_->end()) {
                return (foundRuntimeLoader->second)();
            }
        }
    }

    loadState = LoadState::NO_LIBRARY_FOUND;

    return std::shared_ptr<Runtime>();
}

void Runtime::registerRuntimeLoader(const std::string& middlewareName, const MiddlewareRuntimeLoadFunction& middlewareRuntimeLoadFunction) {
    if (!registeredRuntimeLoadFunctions_) {
        registeredRuntimeLoadFunctions_ = new std::unordered_map<std::string, MiddlewareRuntimeLoadFunction>();
    }
    if (!isDynamic_) {
        registeredRuntimeLoadFunctions_->insert( {middlewareName, middlewareRuntimeLoadFunction});
    }
}

std::shared_ptr<Runtime> Runtime::load() {
    LoadState dummyState;
    return load(dummyState);
}

std::shared_ptr<Runtime> Runtime::load(LoadState& loadState) {
    isDynamic_ = true;
    loadState = LoadState::SUCCESS;
    if(!registeredRuntimeLoadFunctions_) {
        registeredRuntimeLoadFunctions_ = new std::unordered_map<std::string, MiddlewareRuntimeLoadFunction>();
    }

    const std::string& defaultBindingIdentifier = Configuration::getInstance().getDefaultMiddlewareIdentifier();
    if (defaultBindingIdentifier != "") {
        const auto defaultRuntimeLoader = registeredRuntimeLoadFunctions_->find(defaultBindingIdentifier);
        if (defaultRuntimeLoader != registeredRuntimeLoadFunctions_->end()) {
            return (defaultRuntimeLoader->second)();
        }
        return checkDynamicLibraries(defaultBindingIdentifier, loadState);

    } else {
        const auto defaultRuntimeLoader = registeredRuntimeLoadFunctions_->begin();
        if (defaultRuntimeLoader != registeredRuntimeLoadFunctions_->end()) {
            return (defaultRuntimeLoader->second)();
        }
        return checkDynamicLibraries(loadState);
    }
}

std::shared_ptr<Runtime> Runtime::load(const std::string& middlewareIdOrAlias) {
    LoadState dummyState;
    return load(middlewareIdOrAlias, dummyState);
}

std::shared_ptr<Runtime> Runtime::load(const std::string& middlewareIdOrAlias, LoadState& loadState) {
    isDynamic_ = true;
    loadState = LoadState::SUCCESS;
    if (!registeredRuntimeLoadFunctions_) {
        registeredRuntimeLoadFunctions_ = new std::unordered_map<std::string, MiddlewareRuntimeLoadFunction>();
    }

    const std::string middlewareName = Configuration::getInstance().getMiddlewareNameForAlias(middlewareIdOrAlias);

    auto foundRuntimeLoader = registeredRuntimeLoadFunctions_->find(middlewareName);
    if (foundRuntimeLoader != registeredRuntimeLoadFunctions_->end()) {
        return (foundRuntimeLoader->second)();
    }

    return checkDynamicLibraries(middlewareName, loadState);
}

std::shared_ptr<MainLoopContext> Runtime::getNewMainLoopContext() const {
    return std::make_shared<MainLoopContext>();
}

std::shared_ptr<Factory> Runtime::createFactory(const std::string factoryName,
                                                const bool nullOnInvalidName) {
    return createFactory(std::shared_ptr<MainLoopContext>(NULL), factoryName, nullOnInvalidName);
}

std::shared_ptr<Factory> Runtime::createFactory(std::shared_ptr<MainLoopContext> mainLoopContext,
                                                const std::string factoryName,
                                                const bool nullOnInvalidName) {
    if(mainLoopContext && !mainLoopContext->isInitialized()) {
        return std::shared_ptr<Factory>(NULL);
    }
    return doCreateFactory(mainLoopContext, factoryName, nullOnInvalidName);
}

} // namespace CommonAPI
<|endoftext|>


<commit_before>#ifndef YAMAIL_RESOURCE_POOL_ASYNC_DETAIL_POOL_IMPL_HPP
#define YAMAIL_RESOURCE_POOL_ASYNC_DETAIL_POOL_IMPL_HPP

#include <yamail/resource_pool/error.hpp>
#include <yamail/resource_pool/detail/idle.hpp>
#include <yamail/resource_pool/detail/storage.hpp>
#include <yamail/resource_pool/async/detail/queue.hpp>

#include <boost/asio/dispatch.hpp>
#include <boost/asio/post.hpp>
#include <boost/asio/spawn.hpp>

#include <cassert>
#include <type_traits>

namespace yamail {
namespace resource_pool {
namespace async {

struct stats {
    std::size_t size;
    std::size_t available;
    std::size_t used;
    std::size_t queue_size;
};

namespace detail {

namespace asio = boost::asio;

template <class ListIterator, class Handler>
class on_list_iterator_handler {
    static_assert(std::is_invocable_v<Handler, boost::system::error_code, ListIterator>);

    boost::system::error_code error;
    ListIterator list_iterator;
    Handler handler;

public:
    using executor_type = std::decay_t<decltype(asio::get_associated_executor(handler))>;

    on_list_iterator_handler() = default;

    on_list_iterator_handler(const boost::system::error_code& error, ListIterator list_iterator, Handler handler)
        : error(std::move(error)),
          list_iterator(list_iterator),
          handler(std::move(handler)) {}

    template <class ... Args>
    void operator ()() {
        return handler(std::move(error), std::move(list_iterator));
    }

    auto get_executor() const noexcept {
        return asio::get_associated_executor(handler);
    }
};

template <class ListIterator, class Handler>
auto make_on_list_iterator_handler(const boost::system::error_code& error, ListIterator list_iterator, Handler&& handler) {
    using result_type = on_list_iterator_handler<std::decay_t<ListIterator>, std::decay_t<Handler>>;
    return result_type(error, list_iterator, std::forward<Handler>(handler));
}

template <class ListIterator>
struct base_list_iterator_handler_impl {
    virtual void operator ()(boost::system::error_code ec, ListIterator iterator) = 0;
    virtual ~base_list_iterator_handler_impl() = default;
};

template <class ListIterator, class Handler>
class list_iterator_handler_impl final : public base_list_iterator_handler_impl<ListIterator> {
    static_assert(std::is_invocable_v<Handler, boost::system::error_code, ListIterator>);

public:
    template <class HandlerT>
    list_iterator_handler_impl(HandlerT&& handler,
            std::enable_if_t<!std::is_same_v<std::decay_t<HandlerT>, list_iterator_handler_impl>, void*> = nullptr)
            : handler(std::forward<HandlerT>(handler)) {
        static_assert(std::is_same_v<std::decay_t<HandlerT>, Handler>, "HandlerT is not Handler");
    }

    void operator ()(boost::system::error_code ec, ListIterator iterator) final {
        handler(ec, iterator);
    }

private:
    Handler handler;
};

template <class ListIterator>
class list_iterator_handler {
    static_assert(!std::is_same_v<ListIterator, boost::system::error_code>);

public:
    using executor_type = asio::executor;

    list_iterator_handler() = default;

    template <class Handler>
    list_iterator_handler(Handler&& handler,
            std::enable_if_t<!std::is_same_v<std::decay_t<Handler>, list_iterator_handler>, void*> = nullptr)
            : executor(asio::get_associated_executor(handler)),
              impl(std::make_unique<list_iterator_handler_impl<ListIterator, std::decay_t<Handler>>>(std::forward<Handler>(handler))) {
    }

    void operator ()(boost::system::error_code ec, ListIterator iterator) {
        (*impl)(ec, iterator);
    }

    void operator ()(boost::system::error_code ec) {
        (*impl)(ec, ListIterator());
    }

    void operator ()(ListIterator iterator) {
        (*impl)(boost::system::error_code(), iterator);
    }

    auto get_executor() const noexcept {
        return executor;
    }

private:
    asio::executor executor;
    std::unique_ptr<base_list_iterator_handler_impl<ListIterator>> impl;
};

template <class Handler>
class on_error_handler {
    static_assert(std::is_invocable_v<Handler, boost::system::error_code>);

    boost::system::error_code error;
    Handler handler;

public:
    using executor_type = std::decay_t<decltype(asio::get_associated_executor(handler))>;

    template <class HandlerT>
    on_error_handler(boost::system::error_code error, HandlerT&& handler)
            : error(error),
              handler(std::forward<HandlerT>(handler)) {
        static_assert(std::is_same_v<std::decay_t<HandlerT>, Handler>, "HandlerT is not Handler");
    }

    void operator ()() {
        return handler(error);
    }

    auto get_executor() const noexcept {
        return asio::get_associated_executor(handler);
    }
};

template <class Handler>
on_error_handler(boost::system::error_code, Handler&&) -> on_error_handler<std::decay_t<Handler>>;

template <class ListIterator, class Handler>
class on_serve_queued_handler {
    static_assert(std::is_invocable_v<Handler, ListIterator>);

    ListIterator list_iterator;
    Handler handler;

public:
    using executor_type = std::decay_t<decltype(asio::get_associated_executor(handler))>;

    template <class HandlerT>
    on_serve_queued_handler(ListIterator list_iterator, HandlerT&& handler)
            : list_iterator(list_iterator),
              handler(std::forward<HandlerT>(handler)) {
        static_assert(std::is_same_v<std::decay_t<HandlerT>, Handler>, "HandlerT is not Handler");
    }

    void operator ()() {
        return handler(list_iterator);
    }

    auto get_executor() const noexcept {
        return asio::get_associated_executor(handler);
    }
};

template <class ListIterator, class Handler>
on_serve_queued_handler(ListIterator, Handler&&) -> on_serve_queued_handler<ListIterator, std::decay_t<Handler>>;

template <class Value, class Mutex, class IoContext, class Queue>
class pool_impl {
public:
    using value_type = Value;
    using io_context_t = IoContext;
    using idle = resource_pool::detail::idle<value_type>;
    using storage_type = resource_pool::detail::storage<value_type>;
    using list_iterator = typename storage_type::cell_iterator;
    using queue_type = Queue;

    pool_impl(std::size_t capacity,
              std::size_t queue_capacity,
              time_traits::duration idle_timeout)
            : storage_(assert_capacity(capacity), idle_timeout),
              _capacity(capacity),
              _callbacks(std::make_shared<queue_type>(queue_capacity)) {
    }

    template <class Generator>
    pool_impl(Generator&& gen_value,
              std::size_t capacity,
              std::size_t queue_capacity,
              time_traits::duration idle_timeout)
            : storage_(std::forward<Generator>(gen_value), assert_capacity(capacity), idle_timeout),
              _capacity(assert_capacity(capacity)),
              _callbacks(std::make_shared<queue_type>(queue_capacity)) {
    }

    template <class Iter>
    pool_impl(Iter first, Iter last,
              std::size_t queue_capacity,
              time_traits::duration idle_timeout)
            : pool_impl([&]{ return std::move(*first++); },
                    static_cast<std::size_t>(std::distance(first, last)),
                    queue_capacity,
                    idle_timeout) {
    }

    pool_impl(const pool_impl&) = delete;

    pool_impl(pool_impl&&) = delete;

    std::size_t capacity() const noexcept { return _capacity; }
    std::size_t size() const noexcept;
    std::size_t available() const noexcept;
    std::size_t used() const noexcept;
    async::stats stats() const noexcept;

    const queue_type& queue() const noexcept { return *_callbacks; }

    template <class Handler>
    void get(io_context_t& io_context, Handler&& handler, time_traits::duration wait_duration = time_traits::duration(0));
    void recycle(list_iterator res_it);
    void waste(list_iterator res_it);
    void disable();

    static std::size_t assert_capacity(std::size_t value);

private:
    using mutex_t = Mutex;
    using unique_lock = std::unique_lock<mutex_t>;
    using lock_guard = std::lock_guard<mutex_t>;

    mutable mutex_t _mutex;
    storage_type storage_;
    const std::size_t _capacity;
    std::shared_ptr<queue_type> _callbacks;
    bool _disabled = false;
};

template <class V, class M, class I, class Q>
std::size_t pool_impl<V, M, I, Q>::size() const noexcept {
    const auto stats = [&] {
        const lock_guard lock(_mutex);
        return storage_.stats();
    } ();
    return stats.available + stats.used;
}

template <class V, class M, class I, class Q>
std::size_t pool_impl<V, M, I, Q>::available() const noexcept {
    const lock_guard lock(_mutex);
    return storage_.stats().available;
}

template <class V, class M, class I, class Q>
std::size_t pool_impl<V, M, I, Q>::used() const noexcept {
    const lock_guard lock(_mutex);
    return storage_.stats().used;
}

template <class V, class M, class I, class Q>
async::stats pool_impl<V, M, I, Q>::stats() const noexcept {
    const auto stats = [&] {
        const lock_guard lock(_mutex);
        return storage_.stats();
    } ();
    async::stats result;
    result.size = stats.available + stats.used;
    result.available = stats.available;
    result.used = stats.used;
    result.queue_size = _callbacks->size();
    return result;
}

template <class V, class M, class I, class Q>
void pool_impl<V, M, I, Q>::recycle(list_iterator res_it) {
    unique_lock lock(_mutex);
    io_context_t* io_context = nullptr;
    list_iterator_handler<list_iterator> handler;
    if (!_callbacks->pop(io_context, handler)) {
        storage_.recycle(res_it);
        return;
    }
    lock.unlock();
    asio::post(*io_context, on_serve_queued_handler(res_it, std::move(handler)));
}

template <class V, class M, class I, class Q>
void pool_impl<V, M, I, Q>::waste(list_iterator res_it) {
    unique_lock lock(_mutex);
    io_context_t* io_context = nullptr;
    list_iterator_handler<list_iterator> handler;
    if (!_callbacks->pop(io_context, handler)) {
        storage_.waste(res_it);
        return;
    }
    lock.unlock();
    res_it->value.reset();
    asio::post(*io_context, on_serve_queued_handler(res_it, std::move(handler)));
}

template <class V, class M, class I, class Q>
template <class Handler>
void pool_impl<V, M, I, Q>::get(io_context_t& io_context, Handler&& handler, time_traits::duration wait_duration) {
    static_assert(std::is_invocable_v<std::decay_t<Handler>, boost::system::error_code, list_iterator>);

    unique_lock lock(_mutex);
    if (_disabled) {
        lock.unlock();
        asio::dispatch(io_context,
            make_on_list_iterator_handler(
                make_error_code(error::disabled),
                list_iterator(),
                std::forward<Handler>(handler)
            ));
        return;
    }
    if (const auto cell = storage_.lease()) {
        lock.unlock();
        asio::post(io_context,
            make_on_list_iterator_handler(
                boost::system::error_code(),
                *cell,
                std::forward<Handler>(handler)
            ));
        return;
    }
    lock.unlock();
    if (wait_duration.count() == 0) {
        asio::post(io_context,
            make_on_list_iterator_handler(
                make_error_code(error::get_resource_timeout),
                list_iterator(),
                std::forward<Handler>(handler)
            ));
        return;
    }
    list_iterator_handler<list_iterator> wrapped(std::forward<Handler>(handler));
    const bool pushed = _callbacks->push(io_context, wait_duration, std::move(wrapped));
    if (pushed) {
        return;
    }
    asio::post(io_context,
        on_error_handler(
            make_error_code(error::request_queue_overflow),
            std::move(wrapped)
        ));
}

template <class V, class M, class I, class Q>
void pool_impl<V, M, I, Q>::disable() {
    const lock_guard lock(_mutex);
    _disabled = true;
    while (true) {
        io_context_t* io_context;
        list_iterator_handler<list_iterator> handler;
        if (!_callbacks->pop(io_context, handler)) {
            break;
        }
        asio::dispatch(*io_context,
            on_error_handler(
                make_error_code(error::disabled),
                std::move(handler)
            ));
    }
}

template <class V, class M, class I, class Q>
std::size_t pool_impl<V, M, I, Q>::assert_capacity(std::size_t value) {
    if (value == 0) {
        throw error::zero_pool_capacity();
    }
    return value;
}

using boost::asio::async_result;
template<typename CompletionToken, typename Signature>
struct handler_type {
    using type = typename boost::asio::async_result<CompletionToken, Signature>::completion_handler_type;
};

#if BOOST_VERSION < 106600
template <typename CompletionToken, typename Signature>
struct async_completion {
    explicit async_completion(CompletionToken& token)
    : completion_handler(std::move(token)),
      result(completion_handler) {
    }

    using completion_handler_type = typename handler_type<CompletionToken, Signature>::type;

    completion_handler_type completion_handler;
    async_result<completion_handler_type> result;
};
#else
using boost::asio::async_completion;
#endif

template <typename Handler, typename Signature>
using async_return_type = typename ::boost::asio::async_result<Handler, Signature>::return_type;

} // namespace detail
} // namespace async
} // namespace resource_pool
} // namespace yamail

#endif // YAMAIL_RESOURCE_POOL_ASYNC_DETAIL_POOL_IMPL_HPP
<commit_msg>Add deduction guide for on_list_iterator_handler<commit_after>#ifndef YAMAIL_RESOURCE_POOL_ASYNC_DETAIL_POOL_IMPL_HPP
#define YAMAIL_RESOURCE_POOL_ASYNC_DETAIL_POOL_IMPL_HPP

#include <yamail/resource_pool/error.hpp>
#include <yamail/resource_pool/detail/idle.hpp>
#include <yamail/resource_pool/detail/storage.hpp>
#include <yamail/resource_pool/async/detail/queue.hpp>

#include <boost/asio/dispatch.hpp>
#include <boost/asio/post.hpp>
#include <boost/asio/spawn.hpp>

#include <cassert>
#include <type_traits>

namespace yamail {
namespace resource_pool {
namespace async {

struct stats {
    std::size_t size;
    std::size_t available;
    std::size_t used;
    std::size_t queue_size;
};

namespace detail {

namespace asio = boost::asio;

template <class ListIterator, class Handler>
class on_list_iterator_handler {
    static_assert(std::is_invocable_v<Handler, boost::system::error_code, ListIterator>);

    boost::system::error_code error;
    ListIterator list_iterator;
    Handler handler;

public:
    using executor_type = std::decay_t<decltype(asio::get_associated_executor(handler))>;

    on_list_iterator_handler() = default;

    template <class HandlerT>
    on_list_iterator_handler(boost::system::error_code error, ListIterator list_iterator, HandlerT&& handler)
        : error(error),
          list_iterator(list_iterator),
          handler(std::forward<HandlerT>(handler)) {}

    template <class ... Args>
    void operator ()() {
        return handler(error, list_iterator);
    }

    auto get_executor() const noexcept {
        return asio::get_associated_executor(handler);
    }
};

template <class ListIterator, class Handler>
on_list_iterator_handler(boost::system::error_code, ListIterator, Handler&&)
    -> on_list_iterator_handler<ListIterator, std::decay_t<Handler>>;

template <class ListIterator>
struct base_list_iterator_handler_impl {
    virtual void operator ()(boost::system::error_code ec, ListIterator iterator) = 0;
    virtual ~base_list_iterator_handler_impl() = default;
};

template <class ListIterator, class Handler>
class list_iterator_handler_impl final : public base_list_iterator_handler_impl<ListIterator> {
    static_assert(std::is_invocable_v<Handler, boost::system::error_code, ListIterator>);

public:
    template <class HandlerT>
    list_iterator_handler_impl(HandlerT&& handler,
            std::enable_if_t<!std::is_same_v<std::decay_t<HandlerT>, list_iterator_handler_impl>, void*> = nullptr)
            : handler(std::forward<HandlerT>(handler)) {
        static_assert(std::is_same_v<std::decay_t<HandlerT>, Handler>, "HandlerT is not Handler");
    }

    void operator ()(boost::system::error_code ec, ListIterator iterator) final {
        handler(ec, iterator);
    }

private:
    Handler handler;
};

template <class ListIterator>
class list_iterator_handler {
    static_assert(!std::is_same_v<ListIterator, boost::system::error_code>);

public:
    using executor_type = asio::executor;

    list_iterator_handler() = default;

    template <class Handler>
    list_iterator_handler(Handler&& handler,
            std::enable_if_t<!std::is_same_v<std::decay_t<Handler>, list_iterator_handler>, void*> = nullptr)
            : executor(asio::get_associated_executor(handler)),
              impl(std::make_unique<list_iterator_handler_impl<ListIterator, std::decay_t<Handler>>>(std::forward<Handler>(handler))) {
    }

    void operator ()(boost::system::error_code ec, ListIterator iterator) {
        (*impl)(ec, iterator);
    }

    void operator ()(boost::system::error_code ec) {
        (*impl)(ec, ListIterator());
    }

    void operator ()(ListIterator iterator) {
        (*impl)(boost::system::error_code(), iterator);
    }

    auto get_executor() const noexcept {
        return executor;
    }

private:
    asio::executor executor;
    std::unique_ptr<base_list_iterator_handler_impl<ListIterator>> impl;
};

template <class Handler>
class on_error_handler {
    static_assert(std::is_invocable_v<Handler, boost::system::error_code>);

    boost::system::error_code error;
    Handler handler;

public:
    using executor_type = std::decay_t<decltype(asio::get_associated_executor(handler))>;

    template <class HandlerT>
    on_error_handler(boost::system::error_code error, HandlerT&& handler)
            : error(error),
              handler(std::forward<HandlerT>(handler)) {
        static_assert(std::is_same_v<std::decay_t<HandlerT>, Handler>, "HandlerT is not Handler");
    }

    void operator ()() {
        return handler(error);
    }

    auto get_executor() const noexcept {
        return asio::get_associated_executor(handler);
    }
};

template <class Handler>
on_error_handler(boost::system::error_code, Handler&&) -> on_error_handler<std::decay_t<Handler>>;

template <class ListIterator, class Handler>
class on_serve_queued_handler {
    static_assert(std::is_invocable_v<Handler, ListIterator>);

    ListIterator list_iterator;
    Handler handler;

public:
    using executor_type = std::decay_t<decltype(asio::get_associated_executor(handler))>;

    template <class HandlerT>
    on_serve_queued_handler(ListIterator list_iterator, HandlerT&& handler)
            : list_iterator(list_iterator),
              handler(std::forward<HandlerT>(handler)) {
        static_assert(std::is_same_v<std::decay_t<HandlerT>, Handler>, "HandlerT is not Handler");
    }

    void operator ()() {
        return handler(list_iterator);
    }

    auto get_executor() const noexcept {
        return asio::get_associated_executor(handler);
    }
};

template <class ListIterator, class Handler>
on_serve_queued_handler(ListIterator, Handler&&) -> on_serve_queued_handler<ListIterator, std::decay_t<Handler>>;

template <class Value, class Mutex, class IoContext, class Queue>
class pool_impl {
public:
    using value_type = Value;
    using io_context_t = IoContext;
    using idle = resource_pool::detail::idle<value_type>;
    using storage_type = resource_pool::detail::storage<value_type>;
    using list_iterator = typename storage_type::cell_iterator;
    using queue_type = Queue;

    pool_impl(std::size_t capacity,
              std::size_t queue_capacity,
              time_traits::duration idle_timeout)
            : storage_(assert_capacity(capacity), idle_timeout),
              _capacity(capacity),
              _callbacks(std::make_shared<queue_type>(queue_capacity)) {
    }

    template <class Generator>
    pool_impl(Generator&& gen_value,
              std::size_t capacity,
              std::size_t queue_capacity,
              time_traits::duration idle_timeout)
            : storage_(std::forward<Generator>(gen_value), assert_capacity(capacity), idle_timeout),
              _capacity(assert_capacity(capacity)),
              _callbacks(std::make_shared<queue_type>(queue_capacity)) {
    }

    template <class Iter>
    pool_impl(Iter first, Iter last,
              std::size_t queue_capacity,
              time_traits::duration idle_timeout)
            : pool_impl([&]{ return std::move(*first++); },
                    static_cast<std::size_t>(std::distance(first, last)),
                    queue_capacity,
                    idle_timeout) {
    }

    pool_impl(const pool_impl&) = delete;

    pool_impl(pool_impl&&) = delete;

    std::size_t capacity() const noexcept { return _capacity; }
    std::size_t size() const noexcept;
    std::size_t available() const noexcept;
    std::size_t used() const noexcept;
    async::stats stats() const noexcept;

    const queue_type& queue() const noexcept { return *_callbacks; }

    template <class Handler>
    void get(io_context_t& io_context, Handler&& handler, time_traits::duration wait_duration = time_traits::duration(0));
    void recycle(list_iterator res_it);
    void waste(list_iterator res_it);
    void disable();

    static std::size_t assert_capacity(std::size_t value);

private:
    using mutex_t = Mutex;
    using unique_lock = std::unique_lock<mutex_t>;
    using lock_guard = std::lock_guard<mutex_t>;

    mutable mutex_t _mutex;
    storage_type storage_;
    const std::size_t _capacity;
    std::shared_ptr<queue_type> _callbacks;
    bool _disabled = false;
};

template <class V, class M, class I, class Q>
std::size_t pool_impl<V, M, I, Q>::size() const noexcept {
    const auto stats = [&] {
        const lock_guard lock(_mutex);
        return storage_.stats();
    } ();
    return stats.available + stats.used;
}

template <class V, class M, class I, class Q>
std::size_t pool_impl<V, M, I, Q>::available() const noexcept {
    const lock_guard lock(_mutex);
    return storage_.stats().available;
}

template <class V, class M, class I, class Q>
std::size_t pool_impl<V, M, I, Q>::used() const noexcept {
    const lock_guard lock(_mutex);
    return storage_.stats().used;
}

template <class V, class M, class I, class Q>
async::stats pool_impl<V, M, I, Q>::stats() const noexcept {
    const auto stats = [&] {
        const lock_guard lock(_mutex);
        return storage_.stats();
    } ();
    async::stats result;
    result.size = stats.available + stats.used;
    result.available = stats.available;
    result.used = stats.used;
    result.queue_size = _callbacks->size();
    return result;
}

template <class V, class M, class I, class Q>
void pool_impl<V, M, I, Q>::recycle(list_iterator res_it) {
    unique_lock lock(_mutex);
    io_context_t* io_context = nullptr;
    list_iterator_handler<list_iterator> handler;
    if (!_callbacks->pop(io_context, handler)) {
        storage_.recycle(res_it);
        return;
    }
    lock.unlock();
    asio::post(*io_context, on_serve_queued_handler(res_it, std::move(handler)));
}

template <class V, class M, class I, class Q>
void pool_impl<V, M, I, Q>::waste(list_iterator res_it) {
    unique_lock lock(_mutex);
    io_context_t* io_context = nullptr;
    list_iterator_handler<list_iterator> handler;
    if (!_callbacks->pop(io_context, handler)) {
        storage_.waste(res_it);
        return;
    }
    lock.unlock();
    res_it->value.reset();
    asio::post(*io_context, on_serve_queued_handler(res_it, std::move(handler)));
}

template <class V, class M, class I, class Q>
template <class Handler>
void pool_impl<V, M, I, Q>::get(io_context_t& io_context, Handler&& handler, time_traits::duration wait_duration) {
    static_assert(std::is_invocable_v<std::decay_t<Handler>, boost::system::error_code, list_iterator>);

    unique_lock lock(_mutex);
    if (_disabled) {
        lock.unlock();
        asio::dispatch(io_context,
            on_list_iterator_handler(
                make_error_code(error::disabled),
                list_iterator(),
                std::forward<Handler>(handler)
            ));
        return;
    }
    if (const auto cell = storage_.lease()) {
        lock.unlock();
        asio::post(io_context,
            on_list_iterator_handler(
                boost::system::error_code(),
                *cell,
                std::forward<Handler>(handler)
            ));
        return;
    }
    lock.unlock();
    if (wait_duration.count() == 0) {
        asio::post(io_context,
            on_list_iterator_handler(
                make_error_code(error::get_resource_timeout),
                list_iterator(),
                std::forward<Handler>(handler)
            ));
        return;
    }
    list_iterator_handler<list_iterator> wrapped(std::forward<Handler>(handler));
    const bool pushed = _callbacks->push(io_context, wait_duration, std::move(wrapped));
    if (pushed) {
        return;
    }
    asio::post(io_context,
        on_error_handler(
            make_error_code(error::request_queue_overflow),
            std::move(wrapped)
        ));
}

template <class V, class M, class I, class Q>
void pool_impl<V, M, I, Q>::disable() {
    const lock_guard lock(_mutex);
    _disabled = true;
    while (true) {
        io_context_t* io_context;
        list_iterator_handler<list_iterator> handler;
        if (!_callbacks->pop(io_context, handler)) {
            break;
        }
        asio::dispatch(*io_context,
            on_error_handler(
                make_error_code(error::disabled),
                std::move(handler)
            ));
    }
}

template <class V, class M, class I, class Q>
std::size_t pool_impl<V, M, I, Q>::assert_capacity(std::size_t value) {
    if (value == 0) {
        throw error::zero_pool_capacity();
    }
    return value;
}

using boost::asio::async_result;
template<typename CompletionToken, typename Signature>
struct handler_type {
    using type = typename boost::asio::async_result<CompletionToken, Signature>::completion_handler_type;
};

#if BOOST_VERSION < 106600
template <typename CompletionToken, typename Signature>
struct async_completion {
    explicit async_completion(CompletionToken& token)
    : completion_handler(std::move(token)),
      result(completion_handler) {
    }

    using completion_handler_type = typename handler_type<CompletionToken, Signature>::type;

    completion_handler_type completion_handler;
    async_result<completion_handler_type> result;
};
#else
using boost::asio::async_completion;
#endif

template <typename Handler, typename Signature>
using async_return_type = typename ::boost::asio::async_result<Handler, Signature>::return_type;

} // namespace detail
} // namespace async
} // namespace resource_pool
} // namespace yamail

#endif // YAMAIL_RESOURCE_POOL_ASYNC_DETAIL_POOL_IMPL_HPP
<|endoftext|>


<commit_before>#pragma once

#include <tuple>
#include <array>
#include <algorithm>
#include <utility>
#include <type_traits>

namespace gcl::ctc::algorithms::tuple
{
#if not defined(__clang__) and defined(__GNUC__)
    // Works only on GCC
    // Currently not supported by :
    // - MSVC/CL : Request non-variable parameters capture, which is non-legal
    // - Clang   : compiler crash, need to investigate
    //             Shorter crash case sample : https://godbolt.org/z/ovW7b6

    template <typename PredicateType, auto... arguments>
    constexpr auto tuple_erase_if(const PredicateType predicate)
    {
        constexpr auto element_if_predicate = [predicate]<auto argument>() consteval
        {
            if constexpr (predicate(argument))
                return std::tuple<>{};
            else
                return std::tuple{argument};
        };
        return std::tuple_cat(element_if_predicate.template operator()<arguments>()...);
    }
    template <typename TupleType, std::size_t N>
    using tuple_type_at = typename std::tuple_element_t<N, TupleType>;
    template <auto... values>
    constexpr auto tuple_erase_duplicate_values()
    {
        constexpr auto arguments = std::tuple{values...};

        constexpr auto element_if_predicate = [arguments]<auto argument, std::size_t argument_index>() consteval
        {
            if constexpr (argument_index == 0)
                return std::tuple{argument};
            else
            {
                constexpr auto has_previous_position =
                    [&arguments]<std::size_t... indexes>(std::index_sequence<indexes...>) consteval
                {
                    return (((std::get<indexes>(arguments) == argument) || ...));
                }
                (std::make_index_sequence<argument_index>{});
                if constexpr (has_previous_position)
                    return std::tuple{};
                else
                    return std::tuple{argument};
            }
        };

        return [&arguments, element_if_predicate ]<std::size_t... indexes>(std::index_sequence<indexes...>) consteval
        {
            return std::tuple_cat(element_if_predicate.template operator()<std::get<indexes>(arguments), indexes>()...);
        }
        (std::make_index_sequence<std::tuple_size_v<decltype(arguments)>>{});
    }

    template <auto... values>
    constexpr auto tuple_erase_duplicate_types()
    {
        using values_as_tuple_type = std::tuple<decltype(values)...>;
        constexpr auto element_if_predicate = []<auto argument, std::size_t argument_index>() consteval
        {
            constexpr auto has_previous_position = []<std::size_t... indexes>(std::index_sequence<indexes...>) consteval
            {
                return (
                    (std::is_same_v<std::tuple_element_t<indexes, values_as_tuple_type>, decltype(argument)> || ...));
            }
            (std::make_index_sequence<argument_index>{});
            if constexpr (has_previous_position)
                return std::tuple<>{};
            else
                return std::tuple{argument};
        };

        return [element_if_predicate]<std::size_t... indexes>(std::index_sequence<indexes...>) consteval
        {
            return std::tuple_cat(element_if_predicate.template operator()<values, indexes>()...);
        }
        (std::make_index_sequence<sizeof...(values)>{});
    }
    template <auto... values>
    using tuple_erase_duplicate_types_t = std::decay_t<decltype(tuple_erase_duplicate_types<values...>())>;
#endif

    template <typename TupleType>
    constexpr auto tuple_to_std_array(TupleType tuple_value)
    {
        return [&tuple_value]<std::size_t... indexes>(std::index_sequence<indexes...>)
        {
            using element_type = std::common_type_t<decltype(std::get<indexes>(tuple_value))...>;
            return std::array<element_type, sizeof...(indexes)>{std::get<indexes>(tuple_value)...};
        }
        (std::make_index_sequence<std::tuple_size_v<TupleType>>{});
    }

    template <std::size_t N, template <typename...> typename TupleType, typename... TupleElementsTypes>
    constexpr auto tuple_shrink(TupleType<TupleElementsTypes...> tuple_value)
    {
        using tuple_t = TupleType<TupleElementsTypes...>;
        static_assert(std::tuple_size_v<tuple_t> >= N);
        return [&tuple_value]<std::size_t... indexes>(std::index_sequence<indexes...>)
        {
            return TupleType{std::get<indexes>(tuple_value)...};
        }
        (std::make_index_sequence<N>{});
    }
    template <std::size_t N, typename ElementType, auto size>
    constexpr auto tuple_shrink(std::array<ElementType, size> tuple_value)
    {
        using tuple_t = std::array<ElementType, size>;
        static_assert(std::tuple_size_v<tuple_t> >= N);
        return [&tuple_value]<std::size_t... indexes>(std::index_sequence<indexes...>)
        {
            return std::array{std::get<indexes>(tuple_value)...};
        }
        (std::make_index_sequence<N>{});
    }

    template <std::size_t N, typename TupleType>
    constexpr auto tuple_remove_suffix(TupleType tuple_value)
    {
        static_assert((std::tuple_size_v<TupleType> - N) >= 0);
        return [&tuple_value]<std::size_t... indexes>(std::index_sequence<indexes...>)
        {
            return std::tuple{std::get<N + indexes>(tuple_value)...};
        }
        (std::make_index_sequence<std::tuple_size_v<TupleType> - N>{});
    }
    template <std::size_t N, typename ElementType, auto size>
    constexpr auto tuple_remove_suffix(std::array<ElementType, size> tuple_value)
    {
        static_assert((size - N) >= 0);
        return [&tuple_value]<std::size_t... indexes>(std::index_sequence<indexes...>)
        {
            return std::array{std::get<N + indexes>(tuple_value)...};
        }
        (std::make_index_sequence<size - N>{});
    }
}

namespace gcl::ctc::tests::algorithms::tuple
{
    namespace ctc_tuple_algorithms = gcl::ctc::algorithms::tuple;
#if not defined(__clang__) and defined(__GNUC__)
    static_assert(
        ctc_tuple_algorithms::tuple_erase_duplicate_values<'a', 'a', 42, 'b', 42, 'a', 13>() ==
        std::tuple{'a', 42, 'b', 13});
    static_assert(std::is_same_v<
                  std::tuple<char, int>,
                  ctc_tuple_algorithms::tuple_erase_duplicate_types_t<'a', 'a', 'b', 42, 'c', 13>>);
#endif

    static_assert(ctc_tuple_algorithms::tuple_to_std_array(std::tuple{'a', 98, 'c'}) == std::array<int, 3>{'a', 'b', 'c'});

    static_assert(ctc_tuple_algorithms::tuple_shrink<2>(std::tuple{'a', 42, 'b', 43}) == std::tuple{'a', 42});
    static_assert(ctc_tuple_algorithms::tuple_shrink<2>(std::array{'a', 'b', 'c'}) == std::array{'a', 'b'});

    static_assert(ctc_tuple_algorithms::tuple_remove_suffix<2>(std::tuple{'a', 42, 'b', 43}) == std::tuple{'b', 43});
    static_assert(ctc_tuple_algorithms::tuple_remove_suffix<2>(std::array{'a', 'b', 'c'}) == std::array{'c'});
}<commit_msg>[gcl::ctc] add tuple_shrink<size> -> tuple_remove_prefix<index><commit_after>#pragma once

#include <tuple>
#include <array>
#include <algorithm>
#include <utility>
#include <type_traits>

namespace gcl::ctc::algorithms::tuple
{
#if not defined(__clang__) and defined(__GNUC__)
    // Works only on GCC
    // Currently not supported by :
    // - MSVC/CL : Request non-variable parameters capture, which is non-legal
    // - Clang   : compiler crash, need to investigate
    //             Shorter crash case sample : https://godbolt.org/z/ovW7b6

    template <typename PredicateType, auto... arguments>
    constexpr auto tuple_erase_if(const PredicateType predicate)
    {
        constexpr auto element_if_predicate = [predicate]<auto argument>() consteval
        {
            if constexpr (predicate(argument))
                return std::tuple<>{};
            else
                return std::tuple{argument};
        };
        return std::tuple_cat(element_if_predicate.template operator()<arguments>()...);
    }
    template <typename TupleType, std::size_t N>
    using tuple_type_at = typename std::tuple_element_t<N, TupleType>;
    template <auto... values>
    constexpr auto tuple_erase_duplicate_values()
    {
        constexpr auto arguments = std::tuple{values...};

        constexpr auto element_if_predicate = [arguments]<auto argument, std::size_t argument_index>() consteval
        {
            if constexpr (argument_index == 0)
                return std::tuple{argument};
            else
            {
                constexpr auto has_previous_position =
                    [&arguments]<std::size_t... indexes>(std::index_sequence<indexes...>) consteval
                {
                    return (((std::get<indexes>(arguments) == argument) || ...));
                }
                (std::make_index_sequence<argument_index>{});
                if constexpr (has_previous_position)
                    return std::tuple{};
                else
                    return std::tuple{argument};
            }
        };

        return [&arguments, element_if_predicate ]<std::size_t... indexes>(std::index_sequence<indexes...>) consteval
        {
            return std::tuple_cat(element_if_predicate.template operator()<std::get<indexes>(arguments), indexes>()...);
        }
        (std::make_index_sequence<std::tuple_size_v<decltype(arguments)>>{});
    }

    template <auto... values>
    constexpr auto tuple_erase_duplicate_types()
    {
        using values_as_tuple_type = std::tuple<decltype(values)...>;
        constexpr auto element_if_predicate = []<auto argument, std::size_t argument_index>() consteval
        {
            constexpr auto has_previous_position = []<std::size_t... indexes>(std::index_sequence<indexes...>) consteval
            {
                return (
                    (std::is_same_v<std::tuple_element_t<indexes, values_as_tuple_type>, decltype(argument)> || ...));
            }
            (std::make_index_sequence<argument_index>{});
            if constexpr (has_previous_position)
                return std::tuple<>{};
            else
                return std::tuple{argument};
        };

        return [element_if_predicate]<std::size_t... indexes>(std::index_sequence<indexes...>) consteval
        {
            return std::tuple_cat(element_if_predicate.template operator()<values, indexes>()...);
        }
        (std::make_index_sequence<sizeof...(values)>{});
    }
    template <auto... values>
    using tuple_erase_duplicate_types_t = std::decay_t<decltype(tuple_erase_duplicate_types<values...>())>;
#endif

    template <typename TupleType>
    constexpr auto tuple_to_std_array(TupleType tuple_value)
    {
        return [&tuple_value]<std::size_t... indexes>(std::index_sequence<indexes...>)
        {
            using element_type = std::common_type_t<decltype(std::get<indexes>(tuple_value))...>;
            return std::array<element_type, sizeof...(indexes)>{std::get<indexes>(tuple_value)...};
        }
        (std::make_index_sequence<std::tuple_size_v<TupleType>>{});
    }

    template <std::size_t N, template <typename...> typename TupleType, typename... TupleElementsTypes>
    constexpr auto tuple_remove_prefix(TupleType<TupleElementsTypes...> tuple_value)
    {
        using tuple_t = TupleType<TupleElementsTypes...>;
        static_assert(std::tuple_size_v<tuple_t> >= N);
        return [&tuple_value]<std::size_t... indexes>(std::index_sequence<indexes...>)
        {
            return TupleType{std::get<indexes>(tuple_value)...};
        }
        (std::make_index_sequence<N>{});
    }
    template <std::size_t N, typename ElementType, auto size>
    constexpr auto tuple_remove_prefix(std::array<ElementType, size> tuple_value)
    {
        using tuple_t = std::array<ElementType, size>;
        static_assert(std::tuple_size_v<tuple_t> >= N);
        return [&tuple_value]<std::size_t... indexes>(std::index_sequence<indexes...>)
        {
            return std::array{std::get<indexes>(tuple_value)...};
        }
        (std::make_index_sequence<N>{});
    }

    template <std::size_t N, typename TupleType>
    constexpr auto tuple_remove_suffix(TupleType tuple_value)
    {
        static_assert((std::tuple_size_v<TupleType> - N) >= 0);
        return [&tuple_value]<std::size_t... indexes>(std::index_sequence<indexes...>)
        {
            return std::tuple{std::get<N + indexes>(tuple_value)...};
        }
        (std::make_index_sequence<std::tuple_size_v<TupleType> - N>{});
    }
    template <std::size_t N, typename ElementType, auto size>
    constexpr auto tuple_remove_suffix(std::array<ElementType, size> tuple_value)
    {
        static_assert((size - N) >= 0);
        return [&tuple_value]<std::size_t... indexes>(std::index_sequence<indexes...>)
        {
            return std::array{std::get<N + indexes>(tuple_value)...};
        }
        (std::make_index_sequence<size - N>{});
    }
}

namespace gcl::ctc::tests::algorithms::tuple
{
    namespace ctc_tuple_algorithms = gcl::ctc::algorithms::tuple;
#if not defined(__clang__) and defined(__GNUC__)
    static_assert(
        ctc_tuple_algorithms::tuple_erase_duplicate_values<'a', 'a', 42, 'b', 42, 'a', 13>() ==
        std::tuple{'a', 42, 'b', 13});
    static_assert(std::is_same_v<
                  std::tuple<char, int>,
                  ctc_tuple_algorithms::tuple_erase_duplicate_types_t<'a', 'a', 'b', 42, 'c', 13>>);
#endif

    static_assert(ctc_tuple_algorithms::tuple_to_std_array(std::tuple{'a', 98, 'c'}) == std::array<int, 3>{'a', 'b', 'c'});

    static_assert(ctc_tuple_algorithms::tuple_remove_prefix<2>(std::tuple{'a', 42, 'b', 43}) == std::tuple{'a', 42});
    static_assert(ctc_tuple_algorithms::tuple_remove_prefix<2>(std::array{'a', 'b', 'c'}) == std::array{'a', 'b'});

    static_assert(ctc_tuple_algorithms::tuple_remove_suffix<2>(std::tuple{'a', 42, 'b', 43}) == std::tuple{'b', 43});
    static_assert(ctc_tuple_algorithms::tuple_remove_suffix<2>(std::array{'a', 'b', 'c'}) == std::array{'c'});
}<|endoftext|>


<commit_before>/*************************************************************************
 *
 *  $RCSfile: svcompat.cxx,v $
 *
 *  $Revision: 1.2 $
 *
 *  last change: $Author: vg $ $Date: 2004-01-06 13:56:30 $
 *
 *  The Contents of this file are made available subject to the terms of
 *  either of the following licenses
 *
 *         - GNU Lesser General Public License Version 2.1
 *         - Sun Industry Standards Source License Version 1.1
 *
 *  Sun Microsystems Inc., October, 2000
 *
 *  GNU Lesser General Public License Version 2.1
 *  =============================================
 *  Copyright 2000 by Sun Microsystems, Inc.
 *  901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License version 2.1, as published by the Free Software Foundation.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *  MA  02111-1307  USA
 *
 *
 *  Sun Industry Standards Source License Version 1.1
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.1 (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.openoffice.org/license.html.
 *
 *  Software provided under this License is provided on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 *
 *  The Initial Developer of the Original Code is: Sun Microsystems, Inc.
 *
 *  Copyright: 2000 by Sun Microsystems, Inc.
 *
 *  All Rights Reserved.
 *
 *  Contributor(s): _______________________________________
 *
 *
 ************************************************************************/

#ifndef _STREAM_HXX
#include <tools/stream.hxx>
#endif

#ifndef _SV_SVCOMPAT_HXX
#include <svcompat.hxx>
#endif

// --------------
// - ImplCompat -
// --------------

ImplCompat::ImplCompat( SvStream& rStm, USHORT nStreamMode, USHORT nVersion ) :
            mpRWStm     ( &rStm ),
            mnStmMode   ( nStreamMode ),
            mnVersion   ( nVersion )
{
    if( !mpRWStm->GetError() )
    {
        if( STREAM_WRITE == mnStmMode )
        {
            *mpRWStm << mnVersion;
            mnTotalSize = ( mnCompatPos = mpRWStm->Tell() ) + 4UL;
            mpRWStm->SeekRel( 4L );
        }
        else
        {
            *mpRWStm >> mnVersion;
            *mpRWStm >> mnTotalSize;
            mnCompatPos = mpRWStm->Tell();
        }
    }
}

// ------------------------------------------------------------------------

ImplCompat::~ImplCompat()
{
    if( STREAM_WRITE == mnStmMode )
    {
        const UINT32 nEndPos = mpRWStm->Tell();

        mpRWStm->Seek( mnCompatPos );
        *mpRWStm << ( nEndPos - mnTotalSize );
        mpRWStm->Seek( nEndPos );
    }
    else
    {
        const UINT32 nReadSize = mpRWStm->Tell() - mnCompatPos;

        if( mnTotalSize > nReadSize )
            mpRWStm->SeekRel( mnTotalSize - nReadSize );
    }
}
<commit_msg>INTEGRATION: CWS ooo19126 (1.2.546); FILE MERGED 2005/09/05 14:45:00 rt 1.2.546.1: #i54170# Change license header: remove SISSL<commit_after>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: svcompat.cxx,v $
 *
 *  $Revision: 1.3 $
 *
 *  last change: $Author: rt $ $Date: 2005-09-09 12:12:16 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

#ifndef _STREAM_HXX
#include <tools/stream.hxx>
#endif

#ifndef _SV_SVCOMPAT_HXX
#include <svcompat.hxx>
#endif

// --------------
// - ImplCompat -
// --------------

ImplCompat::ImplCompat( SvStream& rStm, USHORT nStreamMode, USHORT nVersion ) :
            mpRWStm     ( &rStm ),
            mnStmMode   ( nStreamMode ),
            mnVersion   ( nVersion )
{
    if( !mpRWStm->GetError() )
    {
        if( STREAM_WRITE == mnStmMode )
        {
            *mpRWStm << mnVersion;
            mnTotalSize = ( mnCompatPos = mpRWStm->Tell() ) + 4UL;
            mpRWStm->SeekRel( 4L );
        }
        else
        {
            *mpRWStm >> mnVersion;
            *mpRWStm >> mnTotalSize;
            mnCompatPos = mpRWStm->Tell();
        }
    }
}

// ------------------------------------------------------------------------

ImplCompat::~ImplCompat()
{
    if( STREAM_WRITE == mnStmMode )
    {
        const UINT32 nEndPos = mpRWStm->Tell();

        mpRWStm->Seek( mnCompatPos );
        *mpRWStm << ( nEndPos - mnTotalSize );
        mpRWStm->Seek( nEndPos );
    }
    else
    {
        const UINT32 nReadSize = mpRWStm->Tell() - mnCompatPos;

        if( mnTotalSize > nReadSize )
            mpRWStm->SeekRel( mnTotalSize - nReadSize );
    }
}
<|endoftext|>