text:
#ifndef SERVER_SERVER_HPP
#define SERVER_SERVER_HPP

#include <boost/bind.hpp>
#include <boost/asio.hpp>
#include <boost/date_time.hpp>
#include "room.hpp"
#include "session.hpp"

using Connections = std::set<boost::asio::ip::udp::endpoint>;
using EndpointRef = std::reference_wrapper<const boost::asio::ip::udp::endpoint>;

class Server {
public:
    explicit Server(unsigned short port)
            : _socket(_io_context, boost::asio::ip::udp::endpoint(boost::asio::ip::udp::v4(), port)) {
        start_receive();
        _io_context.run();
    }

protected:
    void start_receive() {
        _socket.async_receive_from(
                boost::asio::buffer(_chat_message.get_raw_data(), ChatMessage::MESSAGE_SIZE),
                _remote_endpoint,
                boost::bind(
                        &Server::handle_receive,
                        this,
                        boost::asio::placeholders::error,
                        boost::asio::placeholders::bytes_transferred
                )
        );
    }

    void handle_receive(const boost::system::error_code &error,
                        std::size_t /*bytes_transferred*/) {
        if (!error || error == boost::asio::error::message_size) {
            _chat_message.decode();
            if (_chat_message.get_type() == MessageType::CONNECT) {
                handle_new_connection();
            } else if (_chat_message.get_type() == MessageType::MESSAGE) {
                handle_message();
            } else if (_chat_message.get_type() == MessageType::COMMAND) {
                handle_command();
            }
            start_receive();
        }
    }

    void handle_new_connection() {
        std::string username = _chat_message.get_text();
        username = username.substr(username.find(' ') + 1, username.size() - username.find(' '));
        std::cout << "[SERVER] Client with login \"" << username << "\" tries to establish connection." << std::endl;
        if (_connected_users.find(_remote_endpoint) != std::end(_connected_users)) {
            std::cout << "[SERVER] Error! Client with such login already connected" << std::endl;
            Session tmp(_socket, _remote_endpoint, "");
            tmp.deliver(ChatMessage("User with such login already exists."));
        } else {
            auto pos = _connections.insert(std::move(_remote_endpoint)).first;
            _connected_users[*pos] =
                    std::make_shared<Session>(_socket, *pos, username);
            _connected_users[*pos]->deliver(ChatMessage("Ok"));
            std::cout << "[SERVER] Client \"" << username << "\" connected." << std::endl;
        }
    }

    void handle_message() {
        auto session = _connected_users[_remote_endpoint];
        auto room = session->get_room();
        if (!room)
            return;
        std::string username = session->get_username();
        std::string curr_time = boost::posix_time::to_simple_string(boost::posix_time::second_clock::local_time());
        std::string room_name = room->get_name();
        std::string message = "\033[35m[" + username + "]"
                              + " \033[36m[" + curr_time + "]"
                              + " \033[32m[" + room_name + "]\033[0m "
                              + _chat_message.get_text();
        std::string message_for_users = "\033[35m[" + username + "]"
                                        + " \033[36m[" + curr_time + "]\033[0m "
                                        + _chat_message.get_text();
        std::cout << message << std::endl;
        room->deliver(ChatMessage(message_for_users));
    }

    void handle_command() {
        auto session = _connected_users[_remote_endpoint];
        std::string username = session->get_username();
        std::string command = _chat_message.get_text();
        std::cout << "[SERVER] Client \"" << username << "\" sent command: " << command << std::endl;
        if (command == "/users") {
            session->deliver(ChatMessage(get_users(username)));
        } else if (command == "/rooms") {
            session->deliver(ChatMessage(get_rooms()));
        } else if (command.starts_with("/create_room")) {
            session->deliver(ChatMessage(create_room(username)));
        } else if (command.starts_with("/exit_room")) {
            exit_room();
        } else if (command.starts_with("/join_room")) {
            const auto &[status, message] = join_room(username);
            session->deliver(ChatMessage(message));
            // костыль, чтобы получать последние сообщения при подключении к чату
            if (status) {
                session->get_room()->deliver_first(session);
            }
        } else if (command.starts_with("/invite_messaging")) {
            auto result = invite_messaging(username);
            if (result == "Bad")
                session->deliver(ChatMessage("Bad"));
        } else if (command.starts_with("/accept_messaging")) {
            auto result = accept_messaging(username);
            session->deliver(ChatMessage(result));
        } else if (command.starts_with("/reject_messaging")) {
            auto result = reject_messaging(username);
            session->deliver(ChatMessage(result));
        }
    }

    std::string get_users(const std::string &current_user) {
        std::string users;
        for (const auto &[_, session] : _connected_users)
            if (session->get_username() != current_user)
                users += session->get_username() + "\n";
        return users;
    }

    std::string get_rooms() {
        std::string rooms;
        for (const auto &[room_name, room] : _rooms)
            if (room->is_public())
                rooms += room_name + (!room->is_locked() ? "" : " (locked)") + "\n";
        return rooms;
    }

    std::string create_room(const std::string &current_user) {
        std::istringstream is(_chat_message.get_text());
        std::string command;
        std::string room_name;
        std::string password;
        is >> command >> room_name >> password;
        if (_rooms.find(room_name) != std::end(_rooms))
            return "Such room already exists.";
        _rooms[room_name] = std::make_shared<Room>(room_name, password);
        std::cout << "[SERVER] Client \"" << current_user << "\" created room with name \"" << room_name
                  << "\" and password \"" << password << "\"" << std::endl;
        return "Ok";
    }

    std::pair<bool, std::string> join_room(const std::string &user) {
        std::istringstream is(_chat_message.get_text());
        std::string command;
        std::string room_name;
        std::string password;
        is >> command >> room_name >> password;
        auto room_it = _rooms.find(room_name);
        if (room_it == std::end(_rooms))
            return {false, "No such room."};
        auto room = room_it->second;
        if (!room->validate_password(password) && room->is_locked())
            return {false, "Incorrect password."};
        room->join(_connected_users[_remote_endpoint]);
        _connected_users[_remote_endpoint]->set_room(room);
        std::cout << "[SERVER] Client \"" << user << "\" joins room with name \"" << room_name << "\"" << std::endl;
        return {true, "Ok"};
    }

    void exit_room() {
        auto user = _connected_users[_remote_endpoint];
        auto room = user->get_room();
        if (!room)
            return;
        std::string room_name = room->get_name();
        room->leave(user);
        user->set_room(nullptr);
        std::cout << "[SERVER] Client \"" << user->get_username() << "\" leaves room with name \"" << room_name << "\""
                  << std::endl;
    }

    std::string invite_messaging(const std::string &username) {
        std::istringstream is(_chat_message.get_text());
        std::string command;
        std::string invited_username;
        is >> command >> invited_username;
        bool user_exists = false;
        EndpointRef invited_endpoint = _remote_endpoint;
        for (const auto &[endp, session] : _connected_users)
            if (session->get_username() == invited_username) {
                user_exists = true;
                invited_endpoint = endp;
                break;
            }
        if (!user_exists)
            return "Bad";
        _invites_from_to.emplace(_connected_users.find(_remote_endpoint)->first, invited_endpoint);
        _connected_users[invited_endpoint]->deliver(ChatMessage(
                "\033[32m[INFO] User " + username + " invites you to private chat.\033[0m"));
        std::cout << "[SERVER] Client \"" << username << "\" invites \"" << invited_username << "\" to private chat"
                  << std::endl;
        return "";
    }

    std::string accept_messaging(const std::string &username) {
        std::istringstream is(_chat_message.get_text());
        std::string command;
        std::string invite_from_username;
        is >> command >> invite_from_username;
        bool user_exists = false;
        EndpointRef invited_endpoint = _remote_endpoint;
        std::shared_ptr<Session> invited_user_session;
        for (const auto &[endp, session] : _connected_users)
            if (session->get_username() == invite_from_username) {
                user_exists = true;
                invited_endpoint = endp;
                invited_user_session = session;
                break;
            }
        if (!user_exists)
            return "Bad";
        auto user_invites = _invites_from_to.find(invited_endpoint);
        if (user_invites == std::end(_invites_from_to))
            return "Bad";
        EndpointRef from = user_invites->first;
        EndpointRef to = user_invites->second;
        if (to != _remote_endpoint)
            return "Bad";

        // generate private room
        std::string room_name = "Chat_" + invite_from_username + "_" + username;
        std::string password = "private";
        _rooms[room_name] = std::make_shared<Room>(room_name, password, false);
        _connected_users[from]->set_room(_rooms[room_name]);
        _connected_users[to]->set_room(_rooms[room_name]);
        _rooms[room_name]->join(_connected_users[from]);
        _rooms[room_name]->join(_connected_users[to]);

        _connected_users[from]->deliver(ChatMessage("Ok"));

        _invites_from_to.erase(user_invites);

        return "Ok";
    }

    std::string reject_messaging(const std::string &username) {
        std::istringstream is(_chat_message.get_text());
        std::string command;
        std::string invite_from_username;
        is >> command >> invite_from_username;
        bool user_exists = false;
        EndpointRef invited_endpoint = _remote_endpoint;
        std::shared_ptr<Session> invited_user_session;
        for (const auto &[endp, session] : _connected_users)
            if (session->get_username() == invite_from_username) {
                user_exists = true;
                invited_endpoint = endp;
                invited_user_session = session;
                break;
            }
        if (!user_exists)
            return "Bad";
        auto user_invites = _invites_from_to.find(invited_endpoint);
        if (user_invites == std::end(_invites_from_to))
            return "Bad";
        EndpointRef from = user_invites->first;
        EndpointRef to = user_invites->second;
        if (to != _remote_endpoint)
            return "Bad";
        _connected_users[from]->deliver(ChatMessage("Bad"));
        _invites_from_to.erase(from);
        return "Ok";
    }

private:
    boost::asio::io_context _io_context;
    boost::asio::ip::udp::socket _socket;
    boost::asio::ip::udp::endpoint _remote_endpoint;

    ChatMessage _chat_message;

    Connections _connections;
    std::map<EndpointRef, std::shared_ptr<Session>> _connected_users;
    std::unordered_map<std::string, std::shared_ptr<Room>> _rooms;
    std::map<EndpointRef, EndpointRef> _invites_from_to;
};

#endif //SERVER_SERVER_HPP

max_stars_repo_name: AndrewShkrob/UDPChat

max_issues_repo_name: AndrewShkrob/UDPChat

max_forks_repo_name: AndrewShkrob/UDPChat


text:
/*
(c) 2014 Glen Joseph Fernandes
<glenjofe -at- gmail.com>

Distributed under the Boost Software
License, Version 1.0.
http://boost.org/LICENSE_1_0.txt
*/
#ifndef ALIGNED_VECTOR_HPP
#define ALIGNED_VECTOR_HPP

#include <boost/align/aligned_allocator.hpp>
#include <vector>

template<class T, std::size_t Alignment = 1>
using aligned_vector = std::vector<T,
    boost::alignment::aligned_allocator<T, Alignment> >;

#endif

ext: hpp

max_issues_repo_name: lipper/arangodb

max_forks_repo_name: lipper/arangodb


text:
// Copyright 2020 The Defold Foundation
// Licensed under the Defold License version 1.0 (the "License"); you may not use
// this file except in compliance with the License.
//
// You may obtain a copy of the License, together with FAQs at
// https://www.defold.com/license
//
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.

#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <time.h>

#include <sys/types.h>
#include <sys/stat.h>

#include <dlib/profile.h>
#include <dlib/dstrings.h>
#include <dlib/hash.h>
#include <dlib/hashtable.h>
#include <dlib/log.h>
#include <dlib/uri.h>
#include <dlib/time.h>
#include <dlib/spinlock.h>

#include "block_allocator.h"
#include "resource.h"
#include "resource_private.h"
#include "async/load_queue.h"

namespace dmResource
{
    // The preloader works as follow; a tree is constructed with each resource to be loaded as a node in the tree.
    // The tree is stored in m_Requests and indices are used to point around in the tree.
    //
    // 0) /rootcollection
    //    1) /go1
    //      3) /script1
    //      4) /script2 PENDING
    //    2) /go2
    //      5) /tilemap
    //        6) /textureset
    //        7) /material
    //
    // Nodes store the LoadResult as RESULT_PENDING, which is invalid as any actual result.
    // These are the 'real' states a node can be in.
    //
    // => New (RESULT_PENDING, m_LoadRequest=0, m_Buffer)
    // => Waiting for load through load queue, (RESULT_PENDING, m_LoadRequest=<handle>)
    //    (Once the load completes, the resource preload will have run and populated the node with children)
    // => Preloaded, waiting on children (RESULT_PENDING, m_Buffer=<data>, m_PreloadData=<data>, m_FirstChild != -1)
    // => Created successfully, (RESULT_OK, m_Resource=<resource>, m_FirstChild == -1)
    // => Created with error, (neither RESULT_PENDING nor RESULT_OK)
    //
    // Nodes are scheduled for load in depth first order. Once they are loaded they might add new child items to the
    // tree. Child items to a node will then be loaded and created before the parent node is created.
    //
    // Once a node with children finds none of them are in PENDING state any longer, resource create will happen,
    // child nodes (which are done) are then erased and the tree is traversed upwards to see if the parent can be
    // completed in the same manner. (PreloaderTryPruneParent)
    //
    // New items added in PreloadHint are added to a guarded dmArray and the preloader pops this array after it
    // has detected a completion of an item in the preloader queue. This keeps the syncronized state small and
    // reduces the contention on the syncronized lock.
    //
    // A path cache is also used to allow the path strings to be internalized without adding the full path length
    // to each request item. The path cache is also syncronized with the same spinlock as the new preloader hints array.
    // The path cache is not touched by the UpdatePreloader code, we keep the internalized pointers in the item.

    // If max number of preload items is reached or the path cache is full new items added to the preloader will
    // be thrown away and can potentially cause synced loading of those resources.

    struct PathDescriptor
    {
        const char* m_InternalizedName;
        const char* m_InternalizedCanonicalPath;
        SResourceType* m_ResourceType;
        dmhash_t m_NameHash;
        dmhash_t m_CanonicalPathHash;
    };

    typedef int16_t TRequestIndex;

    struct PreloadRequest
    {
        PathDescriptor m_PathDescriptor;

        TRequestIndex m_Parent;
        TRequestIndex m_FirstChild;
        TRequestIndex m_NextSibling;
        uint16_t m_PendingChildCount;

        // Set once resources have started loading, they have a load request
        dmLoadQueue::HRequest m_LoadRequest;

        // Set for items that are pending and waiting for children to complete
        void* m_Buffer;
        uint32_t m_BufferSize;

        // Set once preload function has run
        void* m_PreloadData;

        // Set once load has completed
        Result m_LoadResult;
        void* m_Resource;
    };

    // Internal data structure for passing parameters to postcreate function callbacks
    struct ResourcePostCreateParamsInternal
    {
        ResourcePostCreateParams m_Params;
        SResourceDescriptor m_ResourceDesc;
        bool m_Destroy;
    };

    // The preloader will function even down to a value of 1 here (the root object)
    // and sets the limit of how large a dependencies tree can be stored. Since nodes
    // are always present with all their children inserted (unless there was not room)
    // the required size is something the sum of all children on each level down along
    // the largest branch.

    typedef dmHashTable<dmhash_t, uint32_t> TPathHashTable;
    typedef dmHashTable<dmhash_t, bool> TPathInProgressTable;

    static const uint32_t MAX_PRELOADER_REQUESTS         = 1024;
    static const uint32_t PATH_IN_PROGRESS_TABLE_SIZE    = MAX_PRELOADER_REQUESTS / 3;
    static const uint32_t PATH_IN_PROGRESS_CAPACITY      = MAX_PRELOADER_REQUESTS;
    static const uint32_t PATH_IN_PROGRESS_HASHDATA_SIZE = (PATH_IN_PROGRESS_TABLE_SIZE * sizeof(uint32_t)) + (PATH_IN_PROGRESS_CAPACITY * sizeof(TPathInProgressTable::Entry));
    static const uint32_t PATH_AVERAGE_LENGTH            = 40;
    static const uint32_t MAX_PRELOADER_PATHS            = 1536;
    static const uint32_t PATH_BUFFER_TABLE_SIZE         = 509;
    static const uint32_t PATH_BUFFER_TABLE_CAPACITY     = MAX_PRELOADER_PATHS;
    static const uint32_t PATH_BUFFER_HASHDATA_SIZE      = (PATH_BUFFER_TABLE_SIZE * sizeof(uint32_t)) + (PATH_BUFFER_TABLE_CAPACITY * sizeof(TPathHashTable::Entry));

    struct PendingHint
    {
        PathDescriptor m_PathDescriptor;
        TRequestIndex m_Parent;
    };

    struct ResourcePreloader
    {
        ResourcePreloader()
            : m_InProgress(&m_PathInProgressData, PATH_IN_PROGRESS_TABLE_SIZE, PATH_IN_PROGRESS_CAPACITY)
        {
        }
        struct SyncedData
        {
            SyncedData()
                : m_PathLookup(m_LookupData, PATH_BUFFER_TABLE_SIZE, PATH_BUFFER_TABLE_CAPACITY)
                , m_PathDataUsed(0)
            {
            }
            dmArray<PendingHint> m_NewHints;
            TPathHashTable m_PathLookup;
            uint8_t m_LookupData[PATH_BUFFER_HASHDATA_SIZE];
            char m_PathData[MAX_PRELOADER_PATHS * PATH_AVERAGE_LENGTH];
            uint32_t m_PathDataUsed;
        } m_SyncedData;

        dmSpinlock::lock_t m_SyncedDataSpinlock;

        PreloadRequest m_Request[MAX_PRELOADER_REQUESTS];

        // list of free nodes
        TRequestIndex m_Freelist[MAX_PRELOADER_REQUESTS];
        uint32_t m_FreelistSize;
        dmLoadQueue::HQueue m_LoadQueue;
        HFactory m_Factory;
        TPathInProgressTable m_InProgress;
        uint8_t m_PathInProgressData[PATH_IN_PROGRESS_HASHDATA_SIZE];

        // used instead of dynamic allocs as far as it lasts.
        dmBlockAllocator::HContext m_BlockAllocator;

        // post create state
        bool m_LoadQueueFull;
        bool m_CreateComplete;
        uint32_t m_PostCreateCallbackIndex;
        dmArray<ResourcePostCreateParamsInternal> m_PostCreateCallbacks;

        // How many of the initial resources where requested - they should not be release until preloader destruction
        TRequestIndex m_PersistResourceCount;

        dmArray<void*> m_PersistedResources;
    };

    const char* InternalizePath(ResourcePreloader::SyncedData* preloader_synced_data, dmhash_t path_hash, const char* path, uint32_t path_len)
    {
        uint32_t* path_lookup = preloader_synced_data->m_PathLookup.Get(path_hash);
        if (path_lookup != 0x0)
        {
            return &preloader_synced_data->m_PathData[*path_lookup];
        }
        if (preloader_synced_data->m_PathLookup.Full())
        {
            return 0x0;
        }
        if (preloader_synced_data->m_PathDataUsed + path_len + 1 > sizeof(preloader_synced_data->m_PathData))
        {
            return 0x0;
        }
        char* result = &preloader_synced_data->m_PathData[preloader_synced_data->m_PathDataUsed];
        dmStrlCpy(result, path, path_len + 1);
        preloader_synced_data->m_PathLookup.Put(path_hash, preloader_synced_data->m_PathDataUsed);
        preloader_synced_data->m_PathDataUsed += path_len + 1;
        return result;
    }

    static SResourceType* GetResourceType(HPreloader preloader, const char* path)
    {
        const char* ext = strrchr(path, '.');
        if (!ext)
        {
            dmLogWarning("Unknown resource type: '%s'. Missing file extension.", path);
            return 0;
        }
        else
        {
            SResourceType* resource_type = FindResourceType(preloader->m_Factory, ext + 1);
            if (resource_type)
            {
                assert(resource_type->m_CreateFunction);
                return resource_type;
            }
            dmLogError("Unknown resource type: '%s'. Unknown resource type: %s", path, ext);
        }
        return 0;
    }

    Result MakePathDescriptor(ResourcePreloader* preloader, const char* name, PathDescriptor& out_path_descriptor)
    {
        if (name == 0x0)
        {
            return RESULT_INVALID_DATA;
        }
        // Ignore malformed paths that would fail anyway.
        Result res = CheckSuppliedResourcePath(name);
        if (res != dmResource::RESULT_OK)
        {
            return res;
        }
        uint32_t name_len = strlen(name);
        if (name_len >= RESOURCE_PATH_MAX)
        {
            dmLogError("Resource path is to long: (%s)", name);
            return RESULT_INVALID_DATA;
        }
        out_path_descriptor.m_NameHash = dmHashBuffer64(name, name_len);
        out_path_descriptor.m_ResourceType = GetResourceType(preloader, name);
        // We do not bail on out_path_descriptor.m_ResourceType being zero here, we want to
        // create a request so we can get a proper error code for the resource in the request

        char canonical_path[RESOURCE_PATH_MAX];
        uint32_t canonical_path_len = GetCanonicalPath(name, canonical_path);
        out_path_descriptor.m_CanonicalPathHash = dmHashBuffer64(canonical_path, canonical_path_len);

        DM_SPINLOCK_SCOPED_LOCK(preloader->m_SyncedDataSpinlock)
        {
            out_path_descriptor.m_InternalizedName = InternalizePath(&preloader->m_SyncedData, out_path_descriptor.m_NameHash, name, name_len);
            if (out_path_descriptor.m_InternalizedName == 0x0)
            {
                dmSpinlock::Unlock(&preloader->m_SyncedDataSpinlock);
                return RESULT_OUT_OF_MEMORY;
            }
            out_path_descriptor.m_InternalizedCanonicalPath = InternalizePath(&preloader->m_SyncedData, out_path_descriptor.m_CanonicalPathHash, canonical_path, canonical_path_len);
            if (out_path_descriptor.m_InternalizedCanonicalPath == 0x0)
            {
                dmSpinlock::Unlock(&preloader->m_SyncedDataSpinlock);
                return RESULT_OUT_OF_MEMORY;
            }
        }

        return RESULT_OK;
    }

    static bool IsPathInProgress(ResourcePreloader* preloader, const PathDescriptor* path_descriptor)
    {
        dmhash_t path_hash = path_descriptor->m_CanonicalPathHash;
        bool* path_lookup  = preloader->m_InProgress.Get(path_hash);
        return path_lookup != 0x0;
    }

    static void MarkPathInProgress(ResourcePreloader* preloader, const PathDescriptor* path_descriptor)
    {
        dmhash_t path_hash = path_descriptor->m_CanonicalPathHash;
        assert(preloader->m_InProgress.Get(path_hash) == 0x0);
        preloader->m_InProgress.Put(path_hash, true);
    }

    static void UnmarkPathInProgress(ResourcePreloader* preloader, const PathDescriptor* path_descriptor)
    {
        dmhash_t path_hash = path_descriptor->m_CanonicalPathHash;
        assert(preloader->m_InProgress.Get(path_hash) != 0x0);
        preloader->m_InProgress.Erase(path_hash);
    }

    static void PreloaderTreeInsert(ResourcePreloader* preloader, TRequestIndex index, TRequestIndex parent)
    {
        PreloadRequest* reqs      = &preloader->m_Request[0];
        reqs[index].m_NextSibling = reqs[parent].m_FirstChild;
        reqs[index].m_Parent      = parent;
        reqs[parent].m_FirstChild = index;
        reqs[parent].m_PendingChildCount += 1;
    }

    static void RemoveFromParentPendingCount(ResourcePreloader* preloader, PreloadRequest* req)
    {
        if (req->m_Parent != -1)
        {
            assert(preloader->m_Request[req->m_Parent].m_PendingChildCount > 0);
            preloader->m_Request[req->m_Parent].m_PendingChildCount -= 1;
        }
    }

    static Result PreloadPathDescriptor(HPreloader preloader, TRequestIndex parent, const PathDescriptor& path_descriptor)
    {
        // Quick deduplication, check if the child is already listed under the current parent
        TRequestIndex child = preloader->m_Request[parent].m_FirstChild;
        while (child != -1)
        {
            if (preloader->m_Request[child].m_PathDescriptor.m_NameHash == path_descriptor.m_NameHash)
            {
                return RESULT_ALREADY_REGISTERED;
            }
            child = preloader->m_Request[child].m_NextSibling;
        }

        if (!preloader->m_FreelistSize)
        {
            // Preload queue is exhausted; this is not fatal, it just means the resource will be loaded
            // inside the main thread which may cause stuttering
            return RESULT_OUT_OF_MEMORY;
        }

        TRequestIndex new_req = preloader->m_Freelist[--preloader->m_FreelistSize];
        PreloadRequest* req   = &preloader->m_Request[new_req];
        memset(req, 0, sizeof(PreloadRequest));
        req->m_PathDescriptor    = path_descriptor;
        req->m_FirstChild        = -1;
        req->m_LoadResult        = RESULT_PENDING;

        PreloaderTreeInsert(preloader, new_req, parent);

        // Check for recursive resources, if it is, mark with loop error, the recursive load result will
        // be checked for in the PreloaderUpdateOneItem and the result will be propagated to the resource
        // preloaded creator.
        TRequestIndex go_up = parent;
        while (go_up != -1)
        {
            if (preloader->m_Request[go_up].m_PathDescriptor.m_CanonicalPathHash == path_descriptor.m_CanonicalPathHash)
            {
                req->m_LoadResult = RESULT_RESOURCE_LOOP_ERROR;
                assert(parent != -1);
                assert(preloader->m_Request[parent].m_PendingChildCount > 0);
                preloader->m_Request[parent].m_PendingChildCount -= 1;
                break;
            }
            go_up = preloader->m_Request[go_up].m_Parent;
        }
        return RESULT_OK;
    }

    static bool PopHints(HPreloader preloader)
    {
        dmArray<PendingHint> new_hints;
        {
            DM_SPINLOCK_SCOPED_LOCK(preloader->m_SyncedDataSpinlock)
            new_hints.Swap(preloader->m_SyncedData.m_NewHints);
        }

        uint32_t new_hint_count = 0;

        const uint32_t hint_count = new_hints.Size();
        const PendingHint* hints = new_hints.Begin();
        for (uint32_t i = 0; i < hint_count; ++i)
        {
            const PendingHint* hint = &hints[i];
            if (PreloadPathDescriptor(preloader, hint->m_Parent, hint->m_PathDescriptor) == RESULT_OK)
            {
                ++new_hint_count;
            }
        }
        return new_hint_count != 0;
    }

    static Result PreloadHintInternal(HPreloader preloader, TRequestIndex parent, const char* name)
    {
        PathDescriptor path_descriptor;
        Result res = MakePathDescriptor(preloader, name, path_descriptor);
        if (res != RESULT_OK)
        {
            return res;
        }
        res = PreloadPathDescriptor(preloader, parent, path_descriptor);
        return res;
    }

    // Only supports removing the first child, which is all the preloader uses anyway.
    static void PreloaderRemoveLeaf(ResourcePreloader* preloader, TRequestIndex index)
    {
        assert(preloader->m_FreelistSize < MAX_PRELOADER_REQUESTS);

        PreloadRequest* me = &preloader->m_Request[index];
        assert(me->m_FirstChild == -1);
        assert(me->m_PendingChildCount == 0);
        PreloadRequest* parent = &preloader->m_Request[me->m_Parent];
        assert(parent->m_FirstChild == index);

        if (me->m_Resource)
        {
            if (index < preloader->m_PersistResourceCount)
            {
                preloader->m_PersistedResources.Push(me->m_Resource);
            }
            else
            {
                Release(preloader->m_Factory, me->m_Resource);
            }
        }

        parent->m_FirstChild = me->m_NextSibling;

        if (me->m_LoadResult == RESULT_PENDING)
        {
            RemoveFromParentPendingCount(preloader, me);
        }

        preloader->m_Freelist[preloader->m_FreelistSize++] = index;
    }

    static void RemoveChildren(ResourcePreloader* preloader, PreloadRequest* req)
    {
        while (req->m_FirstChild != -1)
        {
            PreloaderRemoveLeaf(preloader, req->m_FirstChild);
        }
        assert(req->m_PendingChildCount == 0);
    }

    HPreloader NewPreloader(HFactory factory, const dmArray<const char*>& names)
    {
        ResourcePreloader* preloader = new ResourcePreloader();
        // root is always allocated so we don't add index zero in the free list
        for (uint32_t i = 0; i < MAX_PRELOADER_REQUESTS - 1; i++)
        {
            preloader->m_Freelist[i] = MAX_PRELOADER_REQUESTS - i - 1;
        }

        preloader->m_FreelistSize = MAX_PRELOADER_REQUESTS - 1;

        preloader->m_Factory         = factory;
        preloader->m_LoadQueue       = dmLoadQueue::CreateQueue(factory);
        dmSpinlock::Init(&preloader->m_SyncedDataSpinlock);

        preloader->m_PersistResourceCount = 0;
        preloader->m_PersistedResources.SetCapacity(names.Size());

        // Insert root.
        PreloadRequest* root = &preloader->m_Request[0];
        memset(root, 0x00, sizeof(PreloadRequest));

        root->m_LoadResult        = MakePathDescriptor(preloader, names[0], root->m_PathDescriptor);
        root->m_Parent            = -1;
        root->m_FirstChild        = -1;
        root->m_NextSibling       = -1;
        preloader->m_PersistResourceCount++;

        // Post create setup
        preloader->m_PostCreateCallbacks.SetCapacity(MAX_PRELOADER_REQUESTS / 8);
        preloader->m_LoadQueueFull           = false;
        preloader->m_CreateComplete          = false;
        preloader->m_PostCreateCallbackIndex = 0;

        preloader->m_BlockAllocator = dmBlockAllocator::CreateContext();

        if (root->m_LoadResult == RESULT_OK)
        {
            root->m_LoadResult = RESULT_PENDING;
        }

        // Add remaining items as children of root (first item).
        // This enables optimised loading in so that resources are not loaded multiple times
        // and are released when they can be (internally pruning and sharing the request tree).
        for (uint32_t i = 1; i < names.Size(); ++i)
        {
            Result res = PreloadHintInternal(preloader, 0, names[i]);
            if (res == RESULT_OK)
            {
                preloader->m_PersistResourceCount++;
            }
        }

        return preloader;
    }

    HPreloader NewPreloader(HFactory factory, const char* name)
    {
        const char* name_array[1] = { name };
        dmArray<const char*> names(name_array, 1, 1);
        return NewPreloader(factory, names);
    }

    // CreateResource operation ends either with
    //   1) Having created the resource and free:d all buffers => RESULT_OK + m_Resource
    //   2) Having failed, (or created and destroyed), leaving => RESULT_SOME_ERROR + everything free:d
    //
    // If buffer is null it means to use the items internal buffer
    static void CreateResource(HPreloader preloader, PreloadRequest* req, void* buffer, uint32_t buffer_size)
    {
        assert(req->m_LoadResult == RESULT_PENDING);
        assert(req->m_PendingChildCount == 0);

        assert(req->m_PathDescriptor.m_ResourceType);

        SResourceDescriptor tmp_resource;
        memset(&tmp_resource, 0, sizeof(tmp_resource));

        SResourceType* resource_type = req->m_PathDescriptor.m_ResourceType;

        // We must call CreateFunction if Preload function has been called, so always do this even when an error has occured
        tmp_resource.m_NameHash       = req->m_PathDescriptor.m_CanonicalPathHash;
        tmp_resource.m_ReferenceCount = 1;
        tmp_resource.m_ResourceType   = (void*)resource_type;

        ResourceCreateParams params;
        params.m_Factory     = preloader->m_Factory;
        params.m_Context     = resource_type->m_Context;
        params.m_PreloadData = req->m_PreloadData;
        params.m_Resource    = &tmp_resource;
        params.m_Filename    = req->m_PathDescriptor.m_InternalizedName;

        if (!buffer)
        {
            assert(req->m_Buffer);
            tmp_resource.m_ResourceSizeOnDisc = req->m_BufferSize;
            params.m_Buffer                   = req->m_Buffer;
            params.m_BufferSize               = req->m_BufferSize;
            req->m_LoadResult                 = resource_type->m_CreateFunction(params);

            dmBlockAllocator::Free(preloader->m_BlockAllocator, req->m_Buffer, req->m_BufferSize);

            req->m_Buffer = 0;
        }
        else
        {
            tmp_resource.m_ResourceSizeOnDisc = buffer_size;
            params.m_Buffer                   = buffer;
            params.m_BufferSize               = buffer_size;
            req->m_LoadResult                 = resource_type->m_CreateFunction(params);
        }

        if (req->m_LoadResult == RESULT_OK)
        {
            if (resource_type->m_PostCreateFunction)
            {
                if (preloader->m_PostCreateCallbacks.Full())
                {
                    preloader->m_PostCreateCallbacks.OffsetCapacity(MAX_PRELOADER_REQUESTS / 8);
                }
                preloader->m_PostCreateCallbacks.SetSize(preloader->m_PostCreateCallbacks.Size() + 1);
                ResourcePostCreateParamsInternal& ip = preloader->m_PostCreateCallbacks.Back();
                ip.m_Destroy                         = false;
                ip.m_Params.m_Factory                = preloader->m_Factory;
                ip.m_Params.m_Context                = resource_type->m_Context;
                ip.m_Params.m_PreloadData            = req->m_PreloadData;
                ip.m_Params.m_Resource               = 0;
                memcpy(&ip.m_ResourceDesc, &tmp_resource, sizeof(SResourceDescriptor));
            }
        }

        assert(req->m_Buffer == 0);
        req->m_PreloadData = 0;

        RemoveFromParentPendingCount(preloader, req);

        // Children can now be removed (and their resources released) as they are not
        // needed any longer as the parent resource have its own references to the
        // child resources.
        RemoveChildren(preloader, req);

        if (req->m_LoadResult != RESULT_OK)
        {
            // Continue up to parent. All the stuff below is only for resources that were created
            return;
        }

        assert(tmp_resource.m_Resource);

        // Only two options from now on is to either destroy the resource or have it inserted
        bool destroy = false;

        // If someone else has loaded the resource already, use that one and mark our loaded resource for destruction
        SResourceDescriptor* rd = FindByHash(preloader->m_Factory, req->m_PathDescriptor.m_CanonicalPathHash);
        if (rd)
        {
            // Use already loaded resource
            rd->m_ReferenceCount++;
            req->m_Resource = rd->m_Resource;
            destroy         = true;
        }
        else
        {
            // Insert the loaded and created resource, if insertion fails, mark the resource for detruction
            req->m_LoadResult = InsertResource(preloader->m_Factory, req->m_PathDescriptor.m_InternalizedName, req->m_PathDescriptor.m_CanonicalPathHash, &tmp_resource);
            if (req->m_LoadResult == RESULT_OK)
            {
                req->m_Resource = tmp_resource.m_Resource;
            }
            else
            {
                destroy = true;
            }
        }

        if (destroy)
        {
            assert(tmp_resource.m_Resource != 0);
            assert(resource_type != 0);

            bool found = false;

            // If the resource has a post create function, mark it as "to be destroyed"
            if (resource_type->m_PostCreateFunction)
            {
                for (uint32_t i = preloader->m_PostCreateCallbackIndex; i < preloader->m_PostCreateCallbacks.Size(); ++i)
                {
                    ResourcePostCreateParamsInternal& ip = preloader->m_PostCreateCallbacks[i];
                    if (ip.m_ResourceDesc.m_Resource == tmp_resource.m_Resource)
                    {
                        // Mark resource that it should be destroyed once postcreate has been run.
                        ip.m_Destroy = true;
                        found        = true;

                        // NOTE: Loading multiple collection proxies with shared resources at the same
                        // time can result in these resources being loading more than once.
                        // We should fix so that this cannot happen since this is wasteful of both performance
                        // and memory usage. There is an issue filed for this: DEF-3088
                        // Once that has been implemented, the delayed destroy (m_Destroy) can be removed,
                        // it would no longer be needed since each resource would only be created/loaded once.
                        // Measurements from the amount of double-loading happening due to this in real games
                        // show that the triggered overhead is very low and it does not happen often so not
                        // a priority to fix.

                        break;
                    }
                }
            }

            // Is the resource wasn't found in the PostCreate callbacks, we can destroy it right away.
            if (!found)
            {
                ResourceDestroyParams params;
                params.m_Factory  = preloader->m_Factory;
                params.m_Context  = resource_type->m_Context;
                params.m_Resource = &tmp_resource;
                resource_type->m_DestroyFunction(params);
            }
        }
    }

    // Try to create the resource of the parent if all the child requests has been
    // resolved. We continue up the parent chain until we find a parent where all
    // children are not resolved and we break
    // Returns true if at least one parent in the chain is created
    static bool PreloaderTryPruneParent(HPreloader preloader, PreloadRequest* req)
    {
        TRequestIndex parent = req->m_Parent;
        if (parent == -1)
        {
            return false;
        }
        PreloadRequest* parent_req = &preloader->m_Request[parent];
        if (parent_req->m_PendingChildCount > 0)
        {
            return false;
        }
        CreateResource(preloader, parent_req, 0, 0);
        UnmarkPathInProgress(preloader, &parent_req->m_PathDescriptor);
        PreloaderTryPruneParent(preloader, parent_req);
        return true;
    }

    // Ends the Load part of the resource and handles the result of the load
    // It will create the resource if it has no children, otherwise it will
    // copy the loaded buffer for later use when all the children has been created.
    //
    // Returns true if the resource was created
    static bool FinishLoad(HPreloader preloader, PreloadRequest* req, dmLoadQueue::LoadResult& load_result, void* buffer, uint32_t buffer_size)
    {
        // Pop any hints the load/preload of the item that may have been generated
        PopHints(preloader);

        // Propagate errors
        if (load_result.m_LoadResult != RESULT_OK)
        {
            req->m_LoadResult = load_result.m_LoadResult;
        }
        else if (load_result.m_PreloadResult != RESULT_OK)
        {
            req->m_LoadResult = load_result.m_PreloadResult;
        }

        // On error remove all children
        if (req->m_LoadResult != RESULT_PENDING)
        {
            RemoveChildren(preloader, req);
            RemoveFromParentPendingCount(preloader, req);
        }

        req->m_PreloadData = load_result.m_PreloadData;

        bool created_resource = false;

        // If no children, do the create step immediately with the buffer in place
        if (req->m_FirstChild == -1)
        {
            if (req->m_LoadResult == RESULT_PENDING)
            {
                // Create the resource using the loading buffer directly.
                CreateResource(preloader, req, buffer, buffer_size);
                created_resource = true;
            }
            UnmarkPathInProgress(preloader, &req->m_PathDescriptor);
            dmLoadQueue::FreeLoad(preloader->m_LoadQueue, req->m_LoadRequest);
            req->m_LoadRequest = 0;

            PreloaderTryPruneParent(preloader, req);
        }
        else
        {
            // Keep the loaded bytes until we have loaded all children
            req->m_Buffer = dmBlockAllocator::Allocate(preloader->m_BlockAllocator, buffer_size);
            memcpy(req->m_Buffer, buffer, buffer_size);
            req->m_BufferSize = buffer_size;
            dmLoadQueue::FreeLoad(preloader->m_LoadQueue, req->m_LoadRequest);
            req->m_LoadRequest = 0;
        }
        return created_resource;
    }

    static bool DoPreloaderUpdateOneReq(HPreloader preloader, TRequestIndex index, PreloadRequest* req);

    // Find the first request that has is RESULT_PENDING and try to load it
    // Continue until all requests are checked or we have created a one resource
    static bool PreloaderUpdateOneItem(HPreloader preloader, TRequestIndex index)
    {
        DM_PROFILE(Resource, "PreloaderUpdateOneItem");
        while (index >= 0)
        {
            PreloadRequest* req = &preloader->m_Request[index];
            switch (req->m_LoadResult)
            {
                case RESULT_PENDING:
                    if (DoPreloaderUpdateOneReq(preloader, index, req))
                    {
                        return true;
                    }
                    break;
                case RESULT_RESOURCE_LOOP_ERROR:
                    if (PreloaderTryPruneParent(preloader, req))
                    {
                        return true;
                    }
                    break;
                default:
                    break;
            }
            index = req->m_NextSibling;
        }
        return false;
    }

    // Returns true if the item or any of its children is found that has completed its loading from the load queue
    static bool DoPreloaderUpdateOneReq(HPreloader preloader, TRequestIndex index, PreloadRequest* req)
    {
        DM_PROFILE(Resource, "DoPreloaderUpdateOneReq");

        assert(!req->m_Resource);

        if (req->m_PathDescriptor.m_ResourceType == 0)
        {
            req->m_LoadResult = RESULT_UNKNOWN_RESOURCE_TYPE;
            RemoveFromParentPendingCount(preloader, req);
            if (PreloaderTryPruneParent(preloader, req))
            {
                return true;
            }
            return false;
        }

        // If loading it must finish first before trying to go down to children
        if (req->m_LoadRequest)
        {
            void* buffer;
            uint32_t buffer_size;

            // Can hold the buffer till we FreeLoad it
            dmLoadQueue::LoadResult res;
            dmLoadQueue::Result e = dmLoadQueue::EndLoad(preloader->m_LoadQueue, req->m_LoadRequest, &buffer, &buffer_size, &res);
            if (e == dmLoadQueue::RESULT_PENDING)
            {
                return false;
            }
            preloader->m_LoadQueueFull = false;

            if (FinishLoad(preloader, req, res, buffer, buffer_size))
            {
                return true;
            }
            return false;
        }

        // It has a buffer if is waiting for children to complete first
        if (req->m_Buffer)
        {
            // traverse depth first
            if (PreloaderUpdateOneItem(preloader, req->m_FirstChild))
            {
                return true;
            }
            return false;
        }

        // It might have been loaded by unhinted resource Gets or loaded by a different preloader, just grab & bump refcount
        SResourceDescriptor* rd = FindByHash(preloader->m_Factory, req->m_PathDescriptor.m_CanonicalPathHash);
        if (rd)
        {
            rd->m_ReferenceCount++;
            req->m_Resource   = rd->m_Resource;
            req->m_LoadResult = RESULT_OK;
            RemoveChildren(preloader, req);
            RemoveFromParentPendingCount(preloader, req);
            if (PreloaderTryPruneParent(preloader, req))
            {
                return true;
            }
            return false;
        }

        if (preloader->m_LoadQueueFull)
        {
            return false;
        }

        if (IsPathInProgress(preloader, &req->m_PathDescriptor))
        {
            // A different item in the resource tree is already loading the same resource, just wait
            return false;
        }

        dmLoadQueue::PreloadInfo info;
        info.m_HintInfo.m_Preloader = preloader;
        info.m_HintInfo.m_Parent    = index;
        info.m_Function             = req->m_PathDescriptor.m_ResourceType->m_PreloadFunction;
        info.m_Context              = req->m_PathDescriptor.m_ResourceType->m_Context;

        // If we can't add the request to the load queue it is because the queue is full
        // We will try again once we completed loading of an item via dmLoadQueue::EndLoad
        if ((req->m_LoadRequest = dmLoadQueue::BeginLoad(preloader->m_LoadQueue, req->m_PathDescriptor.m_InternalizedName, req->m_PathDescriptor.m_InternalizedCanonicalPath, &info)))
        {
            MarkPathInProgress(preloader, &req->m_PathDescriptor);
            return true;
        }

        preloader->m_LoadQueueFull = true;
        return false;
    }

    // Calls the PostCreate function of any pending resources
    // If the resource load is duplicate (indicated by ResourcePostCreateParamsInternal::m_Destroy)
    // the resource will be deleted but we still need to call the PostCreate function
    static Result PostCreateUpdateOneItem(HPreloader preloader)
    {
        ResourcePostCreateParamsInternal& ip = preloader->m_PostCreateCallbacks[preloader->m_PostCreateCallbackIndex];
        ResourcePostCreateParams& params     = ip.m_Params;
        params.m_Resource                    = &ip.m_ResourceDesc;
        SResourceType* resource_type         = (SResourceType*)params.m_Resource->m_ResourceType;
        Result ret                           = resource_type->m_PostCreateFunction(params);

        if (ret == RESULT_PENDING)
        {
            return ret;
        }
        ++preloader->m_PostCreateCallbackIndex;

        // Resource has been marked for delayed destroy after PostCreate function has been run.
        if (ip.m_Destroy)
        {
            ResourceDestroyParams params;
            params.m_Factory  = preloader->m_Factory;
            params.m_Context  = resource_type->m_Context;
            params.m_Resource = &ip.m_ResourceDesc;
            resource_type->m_DestroyFunction(params);
            ip.m_Destroy = false;
        }

        if (preloader->m_PostCreateCallbackIndex == preloader->m_PostCreateCallbacks.Size())
        {
            preloader->m_PostCreateCallbacks.SetSize(0);
            preloader->m_PostCreateCallbackIndex = 0;
        }

        return ret;
    }

    Result UpdatePreloader(HPreloader preloader, FPreloaderCompleteCallback complete_callback, PreloaderCompleteCallbackParams* complete_callback_params, uint32_t soft_time_limit)
    {
        DM_PROFILE(Resource, "UpdatePreloader");

        uint64_t start           = dmTime::GetTime();
        uint32_t empty_runs      = 0;
        bool close_to_time_limit = soft_time_limit < 1000;

        do
        {
            Result root_result        = preloader->m_Request[0].m_LoadResult;
            Result post_create_result = RESULT_OK;
            if (preloader->m_PostCreateCallbackIndex < preloader->m_PostCreateCallbacks.Size())
            {
                post_create_result = PostCreateUpdateOneItem(preloader);
                if (post_create_result != RESULT_PENDING)
                {
                    empty_runs = 0;
                    if (root_result == RESULT_OK)
                    {
                        // Just waiting for the post-create functions to complete
                        // If main result is RESULT_OK pick up any errors from
                        // post create function
                        preloader->m_Request[0].m_LoadResult = post_create_result;
                    }
                    continue;
                }
            }

            if (root_result == RESULT_PENDING)
            {
                if (PreloaderUpdateOneItem(preloader, 0))
                {
                    empty_runs = 0;
                    continue;
                }
            }
            else
            {
                if (!preloader->m_CreateComplete)
                {
                    // Root is resolved - all items has been created, we should now
                    // call the post-create function (if given) and then post-create
                    // of all created items
                    preloader->m_CreateComplete = true;
                    if (root_result == RESULT_OK && complete_callback)
                    {
                        if (!complete_callback(complete_callback_params))
                        {
                            preloader->m_Request[0].m_LoadResult = RESULT_NOT_LOADED;
                        }
                        empty_runs = 0;
                        // We need to continue to do all post create functions
                        continue;
                    }
                }

                if (post_create_result != RESULT_PENDING)
                {
                    // All done!
                    return root_result;
                }
            }

            if (PopHints(preloader))
            {
                empty_runs = 0;
                continue;
            }

            if (close_to_time_limit)
            {
                ++empty_runs;
                if (empty_runs > 3)
                {
                    break;
                }
            }
            else
            {
                close_to_time_limit = (dmTime::GetTime() + 1000 - start) > soft_time_limit;
                if (close_to_time_limit)
                {
                    // Sleep a very short time, on windows it this small number just means "give up time slice"
                    dmTime::Sleep(1);
                    continue;
                }

                // In case of non-threaded loading, we never get any empty runs really.
                // In case of threaded loading and loading small files, use up a little
                // more of our time waiting for files to complete loading.
                dmTime::Sleep(1000);
            }
        } while (dmTime::GetTime() - start <= soft_time_limit);

        return RESULT_PENDING;
    }

    void DeletePreloader(HPreloader preloader)
    {
        // Since Preload calls need their Create calls done and PostCreate calls must always follow Create calls.
        // To fix this:
        // * Make Get calls insta-fail on RESULT_ABORTED or something
        // * Make Queue only return RESULT_ABORTED always.
        // This is not a super-important use-case, the only way to trigger this is to start a load and
        // then do unload before it completes or if you destroy the collection while loading.
        // The normal operation is to issue a load and progress once complete.
        while (UpdatePreloader(preloader, 0, 0, 1000000) == RESULT_PENDING)
        {
            dmLogWarning("Waiting for preloader to complete.");
        }

        // Release root and persisted resources
        preloader->m_PersistedResources.Push(preloader->m_Request[0].m_Resource);
        for (uint32_t i = 0; i < preloader->m_PersistedResources.Size(); ++i)
        {
            void* resource = preloader->m_PersistedResources[i];
            if (!resource)
                continue;
            Release(preloader->m_Factory, resource);
        }

        assert(preloader->m_FreelistSize == (MAX_PRELOADER_REQUESTS - 1));
        dmLoadQueue::DeleteQueue(preloader->m_LoadQueue);

        dmBlockAllocator::DeleteContext(preloader->m_BlockAllocator);

        delete preloader;
    }

    bool PreloadHint(HPreloadHintInfo info, const char* name)
    {
        if (!info || !name)
            return false;

        HPreloader preloader = info->m_Preloader;

        PathDescriptor path_descriptor;
        Result res = MakePathDescriptor(info->m_Preloader, name, path_descriptor);
        if (res != RESULT_OK)
        {
            return false;
        }

        DM_SPINLOCK_SCOPED_LOCK(preloader->m_SyncedDataSpinlock)
        uint32_t new_hints_size = preloader->m_SyncedData.m_NewHints.Size();
        if (preloader->m_SyncedData.m_NewHints.Capacity() == new_hints_size)
        {
            preloader->m_SyncedData.m_NewHints.OffsetCapacity(32);
        }
        preloader->m_SyncedData.m_NewHints.SetSize(new_hints_size + 1);
        PendingHint& hint     = preloader->m_SyncedData.m_NewHints.Back();
        hint.m_PathDescriptor = path_descriptor;
        hint.m_Parent         = info->m_Parent;

        return true;
    }
} // namespace dmResource

ext: cpp

max_stars_repo_name: Krzlfx/defold

max_issues_repo_name: Krzlfx/defold

max_forks_repo_name: Krzlfx/defold


text:
//  Copyright (c) 2007-2017 Hartmut Kaiser
//
//  Distributed under the Boost Software License, Version 1.0. (See accompanying
//  file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

#if !defined(HPX_SHENEOS_PARTITION3D_AUG_08_2011_1220PM)
#define HPX_SHENEOS_PARTITION3D_AUG_08_2011_1220PM

#include <hpx/hpx.hpp>

#include <cstddef>
#include <cstdint>
#include <string>
#include <vector>

#include "../dimension.hpp"

#include <boost/scoped_array.hpp>

namespace sheneos
{
    ///////////////////////////////////////////////////////////////////////////
    struct sheneos_coord
    {
        sheneos_coord(double ye = 0.0, double temp = 0.0, double rho = 0.0)
          : ye_(ye), temp_(temp), rho_(rho)
        {}

        double ye_;
        double temp_;
        double rho_;
    };

    ///////////////////////////////////////////////////////////////////////////
    // one mutex per application instance
    typedef hpx::lcos::local::spinlock mutex_type;
    extern mutex_type mtx_;
}

namespace sheneos { namespace server
{
    ///////////////////////////////////////////////////////////////////////////
    class HPX_COMPONENT_EXPORT partition3d
      : public hpx::components::component_base<partition3d>
    {
    private:
        inline void init_dimension(std::string const&, int, dimension const&,
            char const*, boost::scoped_array<double>&);

        inline void init_data(std::string const& datafilename,
            char const* name, boost::scoped_array<double>& values,
            std::size_t array_size);

        /// Get index of a given value in the one-dimensional array-slice.
        inline std::size_t get_index(dimension::type d, double value) const;

        /// Tri-linear interpolation routine.
        inline double tl_interpolate(double* values,
            std::size_t idx_x, std::size_t idx_y, std::size_t idx_z,
            double delta_ye, double delta_logtemp, double delta_logrho) const;

    public:
        enum eos_values {
            logpress = 0x00000001,  ///< pressure
            logenergy = 0x00000002, ///< specific internal energy
            entropy = 0x00000004,   ///< specific entropy
            munu = 0x00000008,      ///< mu_e - mun + mu_p
            cs2 = 0x00000010,       ///< speed of sound squared
            // Derivatives.
            dedt = 0x00000020,      ///< C_v
            dpdrhoe = 0x00000040,   ///< dp/deps|rho
            dpderho = 0x00000080,   ///< dp/drho|eps
            small_api_values = 0x000000FF,
#if SHENEOS_SUPPORT_FULL_API
            // Chemical potentials.
            muhat = 0x00000100,     ///< mu_n - mu_p
            mu_e = 0x00000200,      ///< electron chemical potential
                                    ///< including electron rest mass
            mu_p = 0x00000400,      ///< proton chemical potential
            mu_n = 0x00000800,      ///< neutron chemical potential
            // Compositions.
            xa = 0x00001000,        ///< alpha particle number fraction
            xh = 0x00002000,        ///< heavy nucleus number fraction
            xn = 0x00004000,        ///< neutron number fraction
            xp = 0x00008000,        ///< proton number fraction
            abar = 0x00010000,      ///< average heavy nucleus A
            zbar = 0x00020000,      ///< average heavy nucleus Z
            gamma = 0x00040000,     ///< Gamma_1
            full_api_values = 0x0007FFFF,
#endif
        };

        ///////////////////////////////////////////////////////////////////////
        partition3d();

        ///////////////////////////////////////////////////////////////////////
        // Exposed functionality.

        /// Initialize this partition.
        void init(std::string const& datafilename, dimension const& dimx,
            dimension const& dimy, dimension const& dimz);

        /// Perform an interpolation on this partition.
        ///
        /// \param ye        [in] Electron fraction.
        /// \param temp      [in] Temperature.
        /// \param rho       [in] Rest mass density of the plasma.
        /// \param eosvalues [in] The EOS values to interpolate. Must be
        ///                  in the range of this partition.
        std::vector<double> interpolate(double ye, double temp, double rho,
            std::uint32_t eosvalues) const;

        /// Perform an interpolation of one given field on this partition.
        ///
        /// \param ye        [in] Electron fraction.
        /// \param temp      [in] Temperature.
        /// \param rho       [in] Rest mass density of the plasma.
        /// \param eosvalues [in] The EOS value to interpolate. Must be
        ///                  in the range of this partition.
        double interpolate_one(double ye, double temp, double rho,
            std::uint32_t eosvalue) const;

        /// Perform several interpolations of all given fields on this partition.
        ///
        /// \param cords     [in] triples of electron fractions, temperatures,
        ///                  and rest mass densities of the plasma.
        /// \param eosvalues [in] The EOS values to interpolate. Must be
        ///                  in the range of this partition.
        std::vector<std::vector<double> >
        interpolate_bulk(std::vector<sheneos_coord> const& coords,
            std::uint32_t eosvalues) const;

        /// Perform several interpolations of one given field on this partition.
        ///
        /// \param cords     [in] triples of electron fractions, temperatures,
        ///                  and rest mass densities of the plasma.
        /// \param eosvalue  [in] The EOS value to interpolate. Must be
        ///                  in the range of this partition.
        std::vector<double>
        interpolate_one_bulk(std::vector<sheneos_coord> const& coords,
            std::uint32_t eosvalue) const;

        ///////////////////////////////////////////////////////////////////////
        // Each of the exposed functions needs to be encapsulated into an
        // action type, generating all required boilerplate code for threads,
        // serialization, etc.
        HPX_DEFINE_COMPONENT_ACTION(partition3d, init);
        HPX_DEFINE_COMPONENT_ACTION(partition3d, interpolate);
        HPX_DEFINE_COMPONENT_ACTION(partition3d, interpolate_one);
        HPX_DEFINE_COMPONENT_ACTION(partition3d, interpolate_bulk);
        HPX_DEFINE_COMPONENT_ACTION(partition3d, interpolate_one_bulk);

    protected:
        double interpolate_one(sheneos_coord const& c,
            std::uint32_t eosvalue) const;
        std::vector<double> interpolate(sheneos_coord const& c,
            std::uint32_t eosvalues) const;

    private:
        dimension dim_[dimension::dim];

        double min_value_[dimension::dim];
        double max_value_[dimension::dim];
        double delta_[dimension::dim];

        // Values of independent variables.
        boost::scoped_array<double> ye_values_;
        boost::scoped_array<double> logtemp_values_;
        boost::scoped_array<double> logrho_values_;

        double energy_shift_;

        // Dependent variables.
        boost::scoped_array<double> logpress_values_;
        boost::scoped_array<double> logenergy_values_;
        boost::scoped_array<double> entropy_values_;
        boost::scoped_array<double> munu_values_;
        boost::scoped_array<double> cs2_values_;
        boost::scoped_array<double> dedt_values_;
        boost::scoped_array<double> dpdrhoe_values_;
        boost::scoped_array<double> dpderho_values_;
#if SHENEOS_SUPPORT_FULL_API
        boost::scoped_array<double> muhat_values_;
        boost::scoped_array<double> mu_e_values_;
        boost::scoped_array<double> mu_p_values_;
        boost::scoped_array<double> mu_n_values_;
        boost::scoped_array<double> xa_values_;
        boost::scoped_array<double> xh_values_;
        boost::scoped_array<double> xn_values_;
        boost::scoped_array<double> xp_values_;
        boost::scoped_array<double> abar_values_;
        boost::scoped_array<double> zbar_values_;
        boost::scoped_array<double> gamma_values_;
#endif
    };
}}

///////////////////////////////////////////////////////////////////////////////
// Non-intrusive serialization.
namespace hpx { namespace serialization
{
    HPX_COMPONENT_EXPORT void
    serialize(input_archive& ar,
        sheneos::sheneos_coord& coord, unsigned int const);

    HPX_COMPONENT_EXPORT void
    serialize(output_archive& ar,
        sheneos::sheneos_coord& coord, unsigned int const);
}}

///////////////////////////////////////////////////////////////////////////////
HPX_REGISTER_ACTION_DECLARATION(
    sheneos::server::partition3d::init_action,
    sheneos_partition3d_init_action);
HPX_ACTION_USES_LARGE_STACK(sheneos::server::partition3d::init_action);

HPX_REGISTER_ACTION_DECLARATION(
    sheneos::server::partition3d::interpolate_action,
    sheneos_partition3d_interpolate_action);
HPX_REGISTER_ACTION_DECLARATION(
    sheneos::server::partition3d::interpolate_one_action,
    sheneos_partition3d_interpolate_one_action);

HPX_REGISTER_ACTION_DECLARATION(
    sheneos::server::partition3d::interpolate_bulk_action,
    sheneos_partition3d_interpolate_bulk_action);
HPX_REGISTER_ACTION_DECLARATION(
    hpx::lcos::base_lco_with_value<std::vector<std::vector<double> > >::set_value_action,
    set_value_action_vector_vector_double);

HPX_REGISTER_ACTION_DECLARATION(
    sheneos::server::partition3d::interpolate_one_bulk_action,
    sheneos_partition3d_interpolate_one_bulk_action);
HPX_REGISTER_ACTION_DECLARATION(
    hpx::lcos::base_lco_with_value<std::vector<double> >::set_value_action,
    set_value_action_vector_double);

#endif

max_stars_repo_name: PixelOfDeath/hpx

max_issues_repo_name: atrantan/hpx

max_forks_repo_name: atrantan/hpx


text:
#include <puppet/compiler/evaluation/operators/binary/modulo.hpp>
#include <puppet/compiler/evaluation/operators/binary/call_context.hpp>
#include <puppet/compiler/exceptions.hpp>
#include <boost/format.hpp>

using namespace std;
using namespace puppet::runtime;

namespace puppet { namespace compiler { namespace evaluation { namespace operators { namespace binary {

    descriptor modulo::create_descriptor()
    {
        binary::descriptor descriptor{ ast::binary_operator::modulo };

        descriptor.add("Integer", "Integer", [](call_context& context) {
            auto left = context.left().require<int64_t>();
            auto right = context.right().require<int64_t>();
            if (right == 0) {
                throw evaluation_exception("cannot divide by zero.", context.right_context(), context.context().backtrace());
            }
            return left % right;
        });
        return descriptor;
    }

}}}}}  // namespace puppet::compiler::evaluation::operators::binary

ext: cc

max_stars_repo_name: puppetlabs/puppetcpp

max_issues_repo_name: puppetlabs/puppetcpp

max_forks_repo_name: puppetlabs/puppetcpp


text:
// Copyright (C) 2011-2020 Internet Systems Consortium, Inc. ("ISC")
//
// 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 <config.h>

#include <stdio.h>
#include <unistd.h>

#include <fstream>
#include <iostream>
#include <string>

#include <gtest/gtest.h>

#include <boost/scoped_array.hpp>
#include <boost/lexical_cast.hpp>

#include <exceptions/exceptions.h>

#include <log/macros.h>
#include <log/log_messages.h>
#include <log/logger.h>
#include <log/logger_level.h>
#include <log/logger_manager.h>
#include <log/logger_specification.h>
#include <log/message_initializer.h>
#include <log/output_option.h>

#include <log/tests/tempdir.h>

#include <sys/types.h>
#include <regex.h>

using namespace isc;
using namespace isc::log;
using namespace std;

/// \brief LoggerManager Test
class LoggerManagerTest : public ::testing::Test {
public:
    LoggerManagerTest() {
        // Initialization of logging is done in main()
    }

    ~LoggerManagerTest() {
        LoggerManager::reset();
    }
};

// Convenience class to create the specification for the logger "filelogger",
// which, as the name suggests, logs to a file.  It remembers the file name and
// deletes the file when instance of the class is destroyed.
class SpecificationForFileLogger {
public:

    // Constructor - allocate file and create the specification object
    SpecificationForFileLogger() : spec_(), name_(createTempFilename()),
                                   logname_("filelogger") {

        // Set the output to a temporary file.
        OutputOption option;
        option.destination = OutputOption::DEST_FILE;
        option.filename = name_;

        // Set target output to the file logger.  The defaults indicate
        // INFO severity.
        spec_.setName(logname_);
        spec_.addOutputOption(option);
    }

    // Destructor, remove the file.  This is only a test, so ignore failures
    ~SpecificationForFileLogger() {
        if (! name_.empty()) {
            static_cast<void>(remove(name_.c_str()));

            // Depending on the log4cplus version, a lock file may also be
            // created.
            static_cast<void>(remove((name_ + ".lock").c_str()));
        }
    }

    // Return reference to the logging specification for this loggger
    LoggerSpecification& getSpecification() {
        return spec_;
    }

    // Return name of the logger
    string getLoggerName() const {
        return logname_;
    }

    // Return name of the file
    string getFileName() const {
        return name_;
    }

    // Create temporary filename
    //
    // The compiler warns against tmpnam() and suggests mkstemp instead.
    // Unfortunately, this creates the filename and opens it.  So we need to
    // close and delete the file before returning the name.  Also, the name
    // is based on the template supplied and the name of the temporary
    // directory may vary between systems.  So translate TMPDIR and if that
    // does not exist, use /tmp.
    //
    // \return Temporary file name
    static std::string createTempFilename() {
        string filename = TEMP_DIR + "/kea_logger_manager_test_XXXXXX";

        // Copy into writable storage for the call to mkstemp
        boost::scoped_array<char> tname(new char[filename.size() + 1]);
        strcpy(tname.get(), filename.c_str());

        // Create file, close and delete it, and store the name for later.
        // There is still a race condition here, albeit a small one.
        int filenum = mkstemp(tname.get());
        if (filenum == -1) {
            isc_throw(Exception, "Unable to obtain unique filename");
        }
        close(filenum);

        return (string(tname.get()));
    }

private:
    LoggerSpecification     spec_;      // Specification for this file logger
    string                  name_;      // Name of the output file
    string                  logname_;   // Name of this logger
};

// Convenience function to read an output log file and check that each line
// contains the expected message ID
//
// \param filename Name of the file to check
// \param start Iterator pointing to first expected message ID
// \param finish Iterator pointing to last expected message ID
template <typename T>
void checkFileContents(const std::string& filename, T start, T finish) {

    // Access the file for input
    ifstream infile(filename.c_str());
    if (! infile.good()) {
        FAIL() << "Unable to open the logging file " << filename;
    }

    // Iterate round the expected message IDs and check that they appear in
    // the string.
    string line;    // Line read from the file

    T i = start;    // Iterator
    getline(infile, line);
    int lineno = 1;

    while ((i != finish) && (infile.good())) {

        // Check that the message ID appears in the line.
        EXPECT_TRUE(line.find(string(*i)) != string::npos)
            << "Expected to find " << string(*i) << " on line " << lineno
            << " of logging file " << filename;

        // Go for the next line
        ++i;
        getline(infile, line);
        ++lineno;
    }

    // Why did the loop end?
    EXPECT_TRUE(i == finish) << "Did not reach the end of the message ID list";
    EXPECT_TRUE(infile.eof()) << "Did not reach the end of the logging file";

    // File will close when the instream is deleted at the end of this
    // function.
}

// Check that the logger correctly creates something logging to a file.
TEST_F(LoggerManagerTest, FileLogger) {

    // Create a specification for the file logger and use the manager to
    // connect the "filelogger" logger to it.
    SpecificationForFileLogger file_spec;

    // For the first test, we want to check that the file is created
    // if it does not already exist.  So delete the temporary file before
    // logging the first message.
    static_cast<void>(remove(file_spec.getFileName().c_str()));

    // Set up the file appenders.
    LoggerManager manager;
    manager.process(file_spec.getSpecification());

    // Try logging to the file.  Local scope is set to ensure that the logger
    // is destroyed before we reset the global logging.  We record what we
    // put in the file for a later comparison.
    vector<MessageID> ids;
    {

        // Scope-limit the logger to ensure it is destroyed after the brief
        // check.  This adds weight to the idea that the logger will not
        // keep the file open.
        Logger logger(file_spec.getLoggerName().c_str());

        LOG_FATAL(logger, LOG_DUPLICATE_MESSAGE_ID).arg("test");
        ids.push_back(LOG_DUPLICATE_MESSAGE_ID);

        LOG_FATAL(logger, LOG_DUPLICATE_NAMESPACE).arg("test");
        ids.push_back(LOG_DUPLICATE_NAMESPACE);
    }
    LoggerManager::reset();

    // At this point, the output file should contain two lines with messages
    // LOG_DUPLICATE_MESSAGE_ID and LOG_DUPLICATE_NAMESPACE messages - test this.
    checkFileContents(file_spec.getFileName(), ids.begin(), ids.end());

    // Re-open the file (we have to assume that it was closed when we
    // reset the logger - there is no easy way to check) and check that
    // new messages are appended to it.  We use the alternative
    // invocation of process() here to check it works.
    vector<LoggerSpecification> spec(1, file_spec.getSpecification());
    manager.process(spec.begin(), spec.end());

    // Create a new instance of the logger and log three more messages.
    Logger logger(file_spec.getLoggerName().c_str());

    LOG_FATAL(logger, LOG_NO_SUCH_MESSAGE).arg("test");
    ids.push_back(LOG_NO_SUCH_MESSAGE);

    LOG_FATAL(logger, LOG_INVALID_MESSAGE_ID).arg("test").arg("test2");
    ids.push_back(LOG_INVALID_MESSAGE_ID);

    LOG_FATAL(logger, LOG_NO_MESSAGE_ID).arg("42");
    ids.push_back(LOG_NO_MESSAGE_ID);

    // Close the file and check again
    LoggerManager::reset();
    checkFileContents(file_spec.getFileName(), ids.begin(), ids.end());
}

// Check if the file rolls over when it gets above a certain size.
TEST_F(LoggerManagerTest, FileSizeRollover) {
    // Set to a suitable minimum that log4cplus can copy with
    static const size_t SIZE_LIMIT = 204800;

    // Set up the name of the file.
    SpecificationForFileLogger file_spec;
    LoggerSpecification& spec = file_spec.getSpecification();

    // Expand the option to ensure that a maximum version size is set.
    LoggerSpecification::iterator opt = spec.begin();
    EXPECT_TRUE(opt != spec.end());
    opt->maxsize = SIZE_LIMIT;    // Bytes
    opt->maxver = 2;

    // The current current output file does not exist (the creation of file_spec
    // ensures that.  Check that previous versions don't either.
    vector<string> prev_name;
    for (int i = 0; i < 3; ++i) {
        prev_name.push_back(file_spec.getFileName() + "." +
                            boost::lexical_cast<string>(i + 1));
        (void) remove(prev_name[i].c_str());
    }

    // Generate an argument for a message that ensures that the message when
    // logged will be over that size.
    string big_arg(SIZE_LIMIT, 'x');

    // Set up the file logger
    LoggerManager manager;
    manager.process(spec);

    // Log the message twice using different message IDs.  This should generate
    // three files as for the log4cplus implementation, the files appear to
    // be rolled after the message is logged.
    {
        Logger logger(file_spec.getLoggerName().c_str());
        LOG_FATAL(logger, LOG_NO_SUCH_MESSAGE).arg(big_arg);
        LOG_FATAL(logger, LOG_DUPLICATE_NAMESPACE).arg(big_arg);
    }

    // Check them.
    LoggerManager::reset();     // Ensure files are closed

    vector<MessageID> ids;
    ids.push_back(LOG_NO_SUCH_MESSAGE);
    checkFileContents(prev_name[1], ids.begin(), ids.end());

    ids.clear();
    ids.push_back(LOG_DUPLICATE_NAMESPACE);
    checkFileContents(prev_name[0], ids.begin(), ids.end());

    // Log another message and check that the files have rotated and that
    // a .3 version does not exist.
    manager.process(spec);
    {
        Logger logger(file_spec.getLoggerName().c_str());
        LOG_FATAL(logger, LOG_NO_MESSAGE_TEXT).arg("42").arg(big_arg);
    }

    LoggerManager::reset();     // Ensure files are closed

    // Check that the files have moved.
    ids.clear();
    ids.push_back(LOG_DUPLICATE_NAMESPACE);
    checkFileContents(prev_name[1], ids.begin(), ids.end());

    ids.clear();
    ids.push_back(LOG_NO_MESSAGE_TEXT);
    checkFileContents(prev_name[0], ids.begin(), ids.end());

    // ... and check that the .3 version does not exist.
    ifstream file3(prev_name[2].c_str(), ifstream::in);
    EXPECT_FALSE(file3.good());

    // Tidy up
    for (vector<string>::size_type i = 0; i < prev_name.size(); ++i) {
       (void) remove(prev_name[i].c_str());
    }
}

namespace { // begin unnamed namespace

// When we begin to use C++11, we could replace use of POSIX API with
// <regex>.

class RegexHolder {
public:
    RegexHolder(const char* expr, const int flags = REG_EXTENDED) {
        const int rc = regcomp(&regex_, expr, flags);
        if (rc) {
            regfree(&regex_);
            throw;
        }
    }

    ~RegexHolder() {
        regfree(&regex_);
    }

    regex_t* operator*() {
        return (&regex_);
    }

private:
    regex_t regex_;
};

} // end of unnamed namespace

// Check that the logger correctly outputs the full formatted layout
// pattern.
TEST_F(LoggerManagerTest, checkLayoutPattern) {
    // Create a specification for the file logger and use the manager to
    // connect the "filelogger" logger to it.
    SpecificationForFileLogger file_spec;

    // For the first test, we want to check that the file is created
    // if it does not already exist.  So delete the temporary file before
    // logging the first message.
    static_cast<void>(remove(file_spec.getFileName().c_str()));

    // Set up the file appenders.
    LoggerManager manager;
    manager.process(file_spec.getSpecification());

    // Try logging to the file.  Local scope is set to ensure that the logger
    // is destroyed before we reset the global logging.
    {
        Logger logger(file_spec.getLoggerName().c_str());
        LOG_FATAL(logger, LOG_DUPLICATE_MESSAGE_ID).arg("test");
    }

    LoggerManager::reset();

    // Access the file for input
    const std::string& filename = file_spec.getFileName();
    ifstream infile(filename.c_str());
    if (! infile.good()) {
        FAIL() << "Unable to open the logging file " << filename;
    }

    std::string line;
    std::getline(infile, line);

    RegexHolder regex(// %D{%Y-%m-%d %H:%M:%S.%q}
                      "^[[:digit:]]{4}-[[:digit:]]{2}-[[:digit:]]{2}[[:space:]]"
                      "[[:digit:]]{2}:[[:digit:]]{2}:[[:digit:]]{2}\\.[[:digit:]]+[[:space:]]"
                      // %-5p
                      "[[:alpha:]]{1,5}[[:space:]]"
                      // [%c/%i/%t]
                      "\\[[[:alnum:]\\-\\.]+/[[:digit:]]+\\.(0x)?[[:xdigit:]]+\\][[:space:]]"
                      );

    const int re = regexec(*regex, line.c_str(), 0, NULL, 0);
    ASSERT_EQ(0, re)
        << "Logged message does not match expected layout pattern";
}

// Check that after calling the logDuplicatedMessages, the duplicated
// messages are removed.
TEST_F(LoggerManagerTest, logDuplicatedMessages) {
    // Original set should not have duplicates.
    ASSERT_EQ(0, MessageInitializer::getDuplicates().size());

    // This just defines 1, but we'll add it a number of times.
    const char* dupe[] = {
        "DUPE", "dupe",
        NULL
    };
    const MessageInitializer init_message_initializer_1(dupe);
    const MessageInitializer init_message_initializer_2(dupe);

    MessageInitializer::loadDictionary();
    // Should have a duplicate now.
    ASSERT_EQ(1, MessageInitializer::getDuplicates().size());

    // The logDuplicatedMessages, besides logging, should also remove the
    // duplicates.
    LoggerManager::logDuplicatedMessages();
    ASSERT_EQ(0, MessageInitializer::getDuplicates().size());
}

max_stars_repo_name: svenauhagen/kea

max_issues_repo_name: svenauhagen/kea

max_forks_repo_name: svenauhagen/kea


text:
//  indexed_property: expose getter/setter functions with multiple
//                    arguments as proxy objects with __getitem__/__setitem__.
//
//  (C) 2012 Jakob van Santen <vansanten@wisc.edu>
//           and the IceCube Collaboration

#ifndef ICETRAY_PYTHON_INDEXED_PROPERTY_HPP_INCLUDED
#define ICETRAY_PYTHON_INDEXED_PROPERTY_HPP_INCLUDED

#include <boost/python/def_visitor.hpp>
#include <boost/python/scope.hpp>
#include <boost/python/signature.hpp>

#include <boost/mpl/at.hpp>
#include <boost/mpl/size.hpp>
#include <boost/mpl/pop_front.hpp>
#include <boost/mpl/less.hpp>
#include <boost/mpl/greater.hpp>
#include <boost/mpl/begin_end.hpp>
#include <boost/mpl/advance.hpp>
#include <boost/mpl/push_back.hpp>
#include <boost/mpl/vector.hpp>
#include <boost/mpl/for_each.hpp>
#include <boost/mpl/assert.hpp>
#include <boost/type_traits/is_same.hpp>

namespace boost { namespace python {

template <typename G, typename GS, typename S, typename SS>
class indexed_property_impl {

private:
	typedef G Getter;
	typedef S Setter;

	// Getter must have at least one argument, else we wouldn't need to jump through this
	// many hoops to get a nice property interface. The current implementation supports up
	// to 3 key parameters.
	BOOST_MPL_ASSERT(( mpl::greater<typename mpl::size<GS>::type, typename mpl::long_<2l>::type > ));
	BOOST_MPL_ASSERT(( mpl::less<typename mpl::size<GS>::type, typename mpl::long_<6l>::type > ));

	typedef typename mpl::at<GS, typename mpl::long_<0>::type>::type ValueType;
	typedef typename mpl::at<GS, typename mpl::long_<1>::type>::type Target;
	typedef typename mpl::pop_front<typename mpl::pop_front<GS>::type>::type GetArgs;

	// Test if we actually have a setter
	typedef typename mpl::not_<typename mpl::bool_<is_same<void (*)(), S>::value > > have_setter;
	typedef typename mpl::if_<have_setter,
		typename mpl::push_back<GetArgs, ValueType>::type, void*>::type DeducedSetArgs;
	typedef typename mpl::if_<have_setter,
		typename mpl::pop_front<typename mpl::pop_front<SS>::type>::type, void*>::type SetArgs;
	typedef typename mpl::if_<have_setter,
		typename mpl::at<SS, typename mpl::long_<0>::type>::type, void>::type SetReturnType;

	// Ensure that the Setter is of the form void (Target::*)(..., ValueType)
	// FIXME: how do I compare synthesized MPL sequence types?
	// BOOST_MPL_ASSERT(( is_same<DeducedSetArgs, SetArgs> ));
	BOOST_MPL_ASSERT(( is_same<SetReturnType, void> ));

	Getter getter_;
	Setter setter_;

	// Convert reference types to value types
	template <typename T> struct value_type { typedef T type; };
	template <typename T> struct value_type<T&> { typedef T type; };
	typedef shared_ptr<typename value_type<Target>::type> TargetPtr;

	// Make getters to return references if needed
	typedef typename mpl::and_<
		is_reference<ValueType>,
		is_class<typename value_type<ValueType>::type>
		>::type valuetype_is_reference;

	typedef typename mpl::if_<valuetype_is_reference,
	    return_internal_reference<1>,
		default_call_policies
	    >::type getter_policy;

	struct name_collector {
		name_collector(std::string &n) : names(n) {};
		std::string &names;
		template <typename T>
		void operator()(T t)
		{
			if (names.size() > 0)
				names += std::string(", ") + type_id<T>().name();
			else
				names += type_id<T>().name();
		}
	};

	template <typename Args>
	static std::string
	stringify_signature()
	{
		std::string names;
		name_collector coll(names);
		mpl::for_each<Args>(coll);
		return names;
	}

	template <typename Args, unsigned N>
	struct invoke_getter;

	template <typename Args>
	struct invoke_getter<Args,2> {
		typedef typename mpl::at<Args, mpl::long_<0l> >::type A0;
		typedef typename mpl::at<Args, mpl::long_<1l> >::type A1;

		static ValueType apply(TargetPtr target, G g, python::tuple args)
		{
			return (*target.*g)(
			    python::extract<A0>(args[0]),
			    python::extract<A1>(args[1])
				);
		}
	};

	template <typename Args>
	struct invoke_getter<Args,3> {
		typedef typename mpl::at<Args, mpl::long_<0l> >::type A0;
		typedef typename mpl::at<Args, mpl::long_<1l> >::type A1;
		typedef typename mpl::at<Args, mpl::long_<2l> >::type A2;

		static ValueType apply(TargetPtr target, G g, python::tuple args)
		{

			A0 a0 = python::extract<A0>(args[0]);
			A1 a1 = python::extract<A1>(args[1]);
			A2 a2 = python::extract<A1>(args[2]);

			return (*target.*g)(a0, a1, a2);
		}
	};

	template <typename Args, unsigned N>
	struct invoke_setter;

	template <typename Args>
	struct invoke_setter<Args,3> {
		typedef typename mpl::at<Args, mpl::long_<0l> >::type A0;
		typedef typename mpl::at<Args, mpl::long_<1l> >::type A1;

		static void apply(TargetPtr target, S s, python::tuple args, ValueType v)
		{
			return (*target.*s)(
			    python::extract<A0>(args[0]),
			    python::extract<A1>(args[1]),
				v
				);
		}
	};

	template <typename Args>
	struct invoke_setter<Args,4> {
		typedef typename mpl::at<Args, mpl::long_<0l> >::type A0;
		typedef typename mpl::at<Args, mpl::long_<1l> >::type A1;
		typedef typename mpl::at<Args, mpl::long_<2l> >::type A2;

		static void apply(TargetPtr target, S s, python::tuple args, ValueType v)
		{
			return (*target.*s)(
			    python::extract<A0>(args[0]),
			    python::extract<A1>(args[1]),
				python::extract<A2>(args[2]),
				v
				);
		}
	};

	struct Proxy {
		TargetPtr held_;
		Getter getter_;
		Setter setter_;

		Proxy(TargetPtr target, Getter g) : held_(target), getter_(g) {}
		Proxy(TargetPtr target, Getter g, Setter s) : held_(target), getter_(g), setter_(s) {}

		static Proxy
		create(TargetPtr target, Getter g)
		{
			return Proxy(target, g);
		}

		static Proxy
		create(TargetPtr target, Getter g, Setter s)
		{
			return Proxy(target, g, s);
		}

		static object
		make_function(Getter g)
		{
			return python::make_function(bind(&Proxy::create, _1, g), default_call_policies(),
			    mpl::vector<Proxy, TargetPtr>());
		}

		static object
		make_function(Getter g, Setter s)
		{
			return python::make_function(bind(&Proxy::create, _1, g, s), default_call_policies(),
			    mpl::vector<Proxy, TargetPtr>());
		}

		// Specializations for arity
		template <typename Args>
		typename enable_if<
		    mpl::equal_to<typename mpl::size<Args>::type,
			          typename mpl::long_<1l>::type>,
		    ValueType>::type
		getitem(typename mpl::at<Args, mpl::long_<0l> >::type a1)
		{
			return (*held_.*getter_)(a1);
		}

		template <typename Args>
		typename enable_if<
		    mpl::greater<typename mpl::size<Args>::type,
			          typename mpl::long_<1l>::type>,
		    ValueType>::type
		getitem(python::tuple args)
		{
			return invoke_getter<Args, mpl::size<Args>::type::value>::apply(held_, getter_, args);
		}

		template <typename Args>
		typename enable_if<
		    mpl::equal_to<typename mpl::size<Args>::type,
			          typename mpl::long_<2l>::type>,
		    void>::type
  		setitem(typename mpl::at<Args, mpl::long_<0l> >::type a1,
  		        ValueType v)
		{
			return (*held_.*setter_)(a1, v);
		}

		template <typename Args>
		typename enable_if<
		    mpl::greater<typename mpl::size<Args>::type,
			          typename mpl::long_<2l>::type>,
		    void>::type
  		setitem(python::tuple args, ValueType v)
		{
			return invoke_setter<Args, mpl::size<Args>::type::value>::apply(held_, setter_, args, v);
		}

	};

	struct ReadOnly {};
	struct ReadWrite {};

	template <class Kind, class Class>
	void
	bind_property(Class &cl, class_<Proxy> &proxy_class, const char* propname, Kind k) const;

	template <class Class>
	void
	bind_property(Class &cl, class_<Proxy> &proxy_class, const char* propname, ReadOnly k) const
	{
		cl.add_property(propname, Proxy::make_function(getter_));
	}

	template <class Class>
	void
	bind_property(Class &cl, class_<Proxy> &proxy_class, const char* propname, ReadWrite k) const
	{
		cl.add_property(propname, Proxy::make_function(getter_, setter_));
		std::string usage = std::string("Usage: ") + std::string(propname) + "["
		    + stringify_signature<SetArgs>() + "] = " + type_id<ValueType>().name();
		proxy_class.def("__setitem__", &Proxy::template setitem<SetArgs>, usage.c_str());
	}

public:
	indexed_property_impl<G,GS,S,SS>(G g, S s) : getter_(g), setter_(s) {};

	template <class Class, class Options>
	void visit(Class& cl, const char *name, Options &options) const
	{

		scope inner = cl;

		std::string proxy_name = "_" + std::string(name) + "_proxy";

		class_<Proxy> proxy_class(proxy_name.c_str(), no_init);
		proxy_class.def_readonly("parent", &Proxy::held_);

		std::string usage = std::string("Usage: ") + name + "[" + stringify_signature<GetArgs>() + "]";

		proxy_class.def("__getitem__", &Proxy::template getitem<GetArgs>, usage.c_str(), getter_policy());

		typedef is_same<void (*)(), Setter> no_setter;
		typedef typename mpl::if_c<no_setter::value, ReadOnly, ReadWrite>::type property_type;
		bind_property(cl, proxy_class, name, property_type());

	}

};

template <class Class, class Options, typename G, typename GS, typename S, typename SS>
void dispatch_visit(Class& cl, const char *name, Options &options, G g, GS gs, S s, SS ss)
{
	indexed_property_impl<G,GS,S,SS>(g,s).visit(cl, name, options);
}

template <typename G, typename S>
class indexed_property : public def_visitor<indexed_property<G,S> > {

public:

	indexed_property(G g, S s) : g_(g), s_(s) {};

	template <class Class, class Options>
	void visit(Class& cl, const char *name, Options &options) const
	{
		// Kick down to an implementation that knows the function signature
		dispatch_visit(cl, name, options, g_, detail::get_signature(g_), s_, detail::get_signature(s_));
	}

private:
	G g_;
	S s_;

};

// Dispatcher for read-only properties
template <typename G>
indexed_property<G, void (*)()>
make_indexed_property(G g)
{
	typedef void (*nullary)();
	nullary nil;
	return indexed_property<G, nullary>(g, nil);
}

// Dispatcher for read-write properties
template <typename G, typename S>
indexed_property<G,S>
make_indexed_property(G g, S s)
{
	return indexed_property<G,S>(g, s);
}

}} // namespace boost::python

#endif


text:
// --------------------------------------------------------------------------
//                   OpenMS -- Open-Source Mass Spectrometry
// --------------------------------------------------------------------------
// Copyright The OpenMS Team -- Eberhard Karls University Tuebingen,
// ETH Zurich, and Freie Universitaet Berlin 2002-2020.
//
// This software is released under a three-clause BSD license:
//  * Redistributions of source code must retain the above copyright
//    notice, this list of conditions and the following disclaimer.
//  * Redistributions in binary form must reproduce the above copyright
//    notice, this list of conditions and the following disclaimer in the
//    documentation and/or other materials provided with the distribution.
//  * Neither the name of any author or any participating institution
//    may be used to endorse or promote products derived from this software
//    without specific prior written permission.
// For a full list of authors, refer to the file AUTHORS.
// --------------------------------------------------------------------------
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL ANY OF THE AUTHORS OR THE CONTRIBUTING
// INSTITUTIONS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// --------------------------------------------------------------------------
// $Maintainer: Chris Bielow, Xiao Liang $
// $Authors: Marc Sturm, Chris Bielow $
// --------------------------------------------------------------------------

#include <OpenMS/CHEMISTRY/ProteaseDigestion.h>
#include <OpenMS/CHEMISTRY/ProteaseDB.h>
#include <OpenMS/SYSTEM/File.h>
#include <OpenMS/CONCEPT/LogStream.h>
#include <boost/regex.hpp>

#include <iostream>
#include <limits>

using namespace std;

namespace OpenMS
{
  void ProteaseDigestion::setEnzyme(const String& enzyme_name)
  {
    enzyme_ = ProteaseDB::getInstance()->getEnzyme(enzyme_name);
    re_ = boost::regex(enzyme_->getRegEx());
  }

  bool ProteaseDigestion::isValidProduct(const String& protein,
                                          int pos,
                                          int length,
                                          bool ignore_missed_cleavages,
                                          bool allow_nterm_protein_cleavage,
                                          bool allow_random_asp_pro_cleavage) const
  {
    return isValidProduct_(protein, pos, length, ignore_missed_cleavages, allow_nterm_protein_cleavage, allow_random_asp_pro_cleavage);
  }

  bool ProteaseDigestion::isValidProduct(const AASequence& protein,
                                         int pep_pos,
                                         int pep_length,
                                         bool ignore_missed_cleavages,
                                         bool allow_nterm_protein_cleavage,
                                         bool allow_random_asp_pro_cleavage) const
  {
    String seq = protein.toUnmodifiedString();
    return isValidProduct_(seq, pep_pos, pep_length, ignore_missed_cleavages, allow_nterm_protein_cleavage, allow_random_asp_pro_cleavage);
  }

  Size ProteaseDigestion::peptideCount(const AASequence& protein)
  {
    // For unspecific cleavage every cutting position may be skipped. Thus, we get (n + 1) \choose 2 products.
    if (enzyme_->getName() == UnspecificCleavage)
    {
      return (protein.size() + 1) * protein.size() / 2;
    };

    std::vector<int> pep_positions = tokenize_(protein.toUnmodifiedString());
    Size count = pep_positions.size();
    // missed cleavages
    Size sum = count;
    for (Size i = 1; i < count; ++i)
    {
      if (i > missed_cleavages_)
      {
        break;
      }
      sum += count - i;
    }
    return sum;
  }

  Size ProteaseDigestion::digest(const AASequence& protein, vector<AASequence>& output, Size min_length, Size max_length) const
  {
    // initialization
    output.clear();

    // disable max length filter by setting to maximum length
    if (max_length == 0 || max_length > protein.size())
    {
      max_length = protein.size();
    }

    Size mc = (enzyme_->getName() == UnspecificCleavage) ? std::numeric_limits<Size>::max() : missed_cleavages_;
    Size wrong_size(0);

    // naive cleavage sites
    std::vector<int> pep_positions = tokenize_(protein.toUnmodifiedString());
    pep_positions.push_back(protein.size()); // positions now contains 0, x1, ... xn, end
    Size count = pep_positions.size();
    Size begin = pep_positions[0];
    for (Size i = 1; i < count; ++i)
    {
      Size l = pep_positions[i] - begin;
      if (l >= min_length && l <= max_length)
      {
        output.push_back(protein.getSubsequence(begin, l));
      }
      else
      {
        ++wrong_size;
      }
      begin = pep_positions[i];
    }

    // missed cleavages
    if (pep_positions.size() > 1 && mc != 0) // there is at least one cleavage site (in addition to last position)!
    {
      // generate fragments with missed cleavages
      for (Size mcs = 1; ((mcs <= mc) && (mcs < count - 1)); ++mcs)
      {
        begin = pep_positions[0];
        for (Size j = 1; j < count - mcs; ++j)
        {
          Size l = pep_positions[j + mcs] - begin;
          if (l >= min_length && l <= max_length)
          {
            output.push_back(protein.getSubsequence(begin, l));
          }
          else
          {
            ++wrong_size;
          }
          begin = pep_positions[j];
        }
      }
    }
    return wrong_size;
  }

} //namespace OpenMS

max_stars_repo_name: ahmedskhalil/OpenMS

max_issues_repo_name: ahmedskhalil/OpenMS

max_forks_repo_name: timosachsenberg/OpenMS