<commit_before>/* Copyright (c) 2011-2013 Stanford University
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR(S) DISCLAIM ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL AUTHORS BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include <unordered_set>

#include "Common.h"
#include "ServerList.h"
#include "ServerTracker.h"
#include "ShortMacros.h"
#include "TransportManager.h"

namespace RAMCloud {

/**
 * Constructor for ServerList.

 * \param context
 *      Overall information about the RAMCloud server.  The constructor
 *      will modify context so that its serverList member refers to this
 *      object.
 */
ServerList::ServerList(Context* context)
    : AbstractServerList(context)
    , serverList()
{
}

/**
 * Destructor for ServerList.
 */
ServerList::~ServerList()
{
}

//////////////////////////////////////////////////////////////////////
// ServerList - Protected Methods (inherited from AbstractServerList)
//////////////////////////////////////////////////////////////////////
ServerDetails*
ServerList::iget(ServerId id)
{
    uint32_t index = id.indexNumber();
    if ((index < serverList.size()) && serverList[index]) {
        ServerDetails* details = serverList[index].get();
        if (details->serverId == id)
            return details;
    }
    return NULL;
}

ServerDetails*
ServerList::iget(uint32_t index)
{
    return (serverList[index]) ? serverList[index].get() : NULL;
}

/**
 * Return the number of valid indexes in this list w/o lock. Valid does not mean
 * that they're occupied, only that they are within the bounds of the array.
 */
size_t
ServerList::isize() const
{
    return serverList.size();
}

//////////////////////////////////////////////////////////////////////
// ServerList Public Methods
//////////////////////////////////////////////////////////////////////
/**
 * Return the ServerId associated with a given index. If there is none,
 * an invalid ServerId is returned (i.e. the isValid() method will return
 * false.
 */
ServerId
ServerList::operator[](uint32_t index)
{
    Lock lock(mutex);
    if (index >= serverList.size() || !serverList[index])
        return ServerId(/* invalid id */);
    return serverList[index]->serverId;
}

/**
 * Apply a server list from the coordinator to the local server list so they
 * are consistent. In addition, all registered trackers will receive
 * notification of all events related to servers they are aware of along with
 * notifications for any new servers in the updated list.
 *
 * Outdated/repeated updates are ignored.
 *
 * The safety of this method relies heavily on invariants placed on the
 * ordering of updates from the coordinator as well as the ordering of the
 * entries in the updates themselves.
 *
 * The coordinator must (and should already) ensure:
 * 1) Updates should be sent to this server in order. Any out-of-order
 *    updates will be discarded.
 * 2) For an enlisting server that "replaces" an old server in the cluster
 *    (that is, a server re-enlisting with backup data written by a former
 *    cluster member process) the order the CRASHED/REMOVE event for the
 *    replaced server versus the UP event can affect correctness. Without
 *    care restarting backups may inadvertently discard important segment
 *    replicas. The ordering is upheld by the current CoordinatorServerList
 *    implementation in two ways:
 *    a) Update lists are only single-entry only and dispatched in the order
 *       they were generated. The coordinator code is ordered carefully to
 *       ensure the REMOVE/CRASHED for the old server precedes the UP of the
 *       enlisting server.
 *    b) Full lists are the only multi-entry lists; the entries are in the
 *       order they appear in the coordinator server list structure. Because
 *       "slots" in that structure are reused it is possible that an
 *       UP for the enlisting server will appear in the full list before the
 *       CRASHED/REMOVE for the backup it replaces. *However*, this is still
 *       safe. The newly enlisting server receiving the full list cannot
 *       have ever created a replica on the replaced server. Proof: the
 *       first population happened from this full list (which is the only
 *       full list it will ever receive) and the replaced server was
 *       either CRASHED or REMOVE: statuses which aren't eligible for backup
 *       use. This means no restarted process part of that full list can ever
 *       find a replica generated by this master. Because the only threat
 *       to safety comes when backup generate isReplicaNeeded rpcs to masters
 *       that generated replicas they found on storage after restart the
 *       natural full list ordering is safe.
 * 3) That all servers are updated to CRASHED state before they are updated
 *    to REMOVE. This ensures that trackers see UP -> CRASHED -> REMOVE for
 *    each server and simplifies the use of trackers. (Trackers may see
 *    CRASHED -> REMOVE only for servers which were initially CRASHED in
 *    the full list and they may see multiple UPs; users of trackers must take
 *    that into account.)
 *
 * \param list
 *      A snapshot of the coordinator's server list.
 *
 * \return
 *      The current version number for the server list at the end of
 *      this update (it may not have changed if the incoming information
 *      was out of date and had to be ignored).
 */
uint64_t
ServerList::applyServerList(const ProtoBuf::ServerList& list)
{
    if (list.type() == ProtoBuf::ServerList::FULL_LIST) {
        // Ignore a full list unless it is the very first update we have
        // received (i.e. version == 0).
        if (version != 0) {
            LOG(NOTICE, "Ignoring full server list with version %lu "
                    "(server list already populated, version %lu)",
                    list.version_number(), version);
            return version;
        }
    } else {
        // Ignore an update unless its version number exactly follows
        // the local version number.
        if (list.version_number() != version + 1) {
            LOG(NOTICE, "Ignoring out-of order server list update with "
                    "version %lu (local server list is at version %lu)",
                    list.version_number(), version);
            return version;
        }
    }

    LOG(DEBUG, "Server List from coordinator:\n%s",
               list.DebugString().c_str());

    foreach (const auto& server, list.server()) {
        ServerStatus status = static_cast<ServerStatus>(server.status());
        uint32_t index = ServerId{server.server_id()}.indexNumber();
        if (index >= serverList.size())
            serverList.resize(index + 1);
        auto& entry = serverList[index];
        if (status == ServerStatus::UP) {
            LOG(NOTICE, "Server %s is up (server list version %lu)",
                    ServerId{server.server_id()}.toString().c_str(),
                    list.version_number());
            entry.construct(ServerDetails(server));
            foreach (ServerTrackerInterface* tracker, trackers)
                tracker->enqueueChange(*entry, ServerChangeEvent::SERVER_ADDED);
        } else if (status == ServerStatus::CRASHED) {
            LOG(NOTICE, "Server %s is crashed (server list version %lu)",
                    ServerId{server.server_id()}.toString().c_str(),
                    list.version_number());
            entry.construct(ServerDetails(server));
            foreach (ServerTrackerInterface* tracker, trackers) {
                tracker->enqueueChange(*entry,
                                       ServerChangeEvent::SERVER_CRASHED);
            }
        } else if (status == ServerStatus::REMOVE) {
            LOG(NOTICE, "Server %s is removed (server list version %lu)",
                    ServerId{server.server_id()}.toString().c_str(),
                    list.version_number());
            // If we don't already have an entry for this server, no
            // need to make one.
            if (entry) {
                entry->status = ServerStatus::REMOVE;
                foreach (ServerTrackerInterface* tracker, trackers) {
                    tracker->enqueueChange(*entry,
                                           ServerChangeEvent::SERVER_REMOVED);
                }
                entry.destroy();
            }
        } else {
            DIE("unknown ServerStatus %d", server.status());
        }
    }

    version = list.version_number();
    foreach (ServerTrackerInterface* tracker, trackers)
        tracker->fireCallback();
    return version;
}

// - private -

/**
 * Add a server to the server list and enqueue the ADDED event to all
 * registered trackers, but don't fire callbacks. Take care that this
 * will blindly replace any entry for a server with an id that
 * has the same index number.
 * Used only for unit testing. Never call this in real code; all
 * server list entry changes should come from the coordinator via
 * applyServerList().
 */
void
ServerList::testingAdd(const ServerDetails server)
{
    uint32_t index = server.serverId.indexNumber();
    if (index >= serverList.size())
        serverList.resize(index + 1);
    auto& entry = serverList.at(index);
    entry.construct(server);
    foreach (ServerTrackerInterface* tracker, trackers)
        tracker->enqueueChange(*entry, ServerChangeEvent::SERVER_ADDED);
}

/**
 * Crash a server from the server list and enqueue the CRASHED event to all
 * registered trackers, but don't fire callbacks. Take care that this blindly
 * crashes the server in the the slot that corresponds to the index number of
 * the server id. It's your problem to make sure that it is populated and that
 * that makes sense.
 * Used only for unit testing. Never call this in real code; all
 * server list entry changes should come from the coordinator via
 * applyServerList().
 */
void
ServerList::testingCrashed(ServerId serverId)
{
    auto& entry = serverList.at(serverId.indexNumber());
    entry->status = ServerStatus::CRASHED;
    foreach (ServerTrackerInterface* tracker, trackers)
        tracker->enqueueChange(*entry, ServerChangeEvent::SERVER_CRASHED);
}

/**
 * Remove a server from the server list and enqueue the REMOVED event to all
 * registered trackers, but don't fire callbacks. Take care that this blindly
 * removes the server in the the slot that corresponds to the index number of
 * the server id. It's your problem to make sure that it is populated and that
 * that makes sense.
 * Used only for unit testing. Never call this in real code; all
 * server list entries should come from the coordinator via
 * applyServerList().
 */
void
ServerList::testingRemove(ServerId serverId)
{
    auto& entry = serverList.at(serverId.indexNumber());
    entry->status = ServerStatus::REMOVE;
    foreach (ServerTrackerInterface* tracker, trackers)
        tracker->enqueueChange(*entry, ServerChangeEvent::SERVER_REMOVED);
    entry.destroy();
}

} // namespace RAMCloud
<commit_msg>Fixed bug in ServerList::applyServerList: was not acquiring the monitor lock, which was causing segmentation faults from reader/writer conflicts.<commit_after>/* Copyright (c) 2011-2013 Stanford University
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR(S) DISCLAIM ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL AUTHORS BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include <unordered_set>

#include "Common.h"
#include "ServerList.h"
#include "ServerTracker.h"
#include "ShortMacros.h"
#include "TransportManager.h"

namespace RAMCloud {

/**
 * Constructor for ServerList.

 * \param context
 *      Overall information about the RAMCloud server.  The constructor
 *      will modify context so that its serverList member refers to this
 *      object.
 */
ServerList::ServerList(Context* context)
    : AbstractServerList(context)
    , serverList()
{
}

/**
 * Destructor for ServerList.
 */
ServerList::~ServerList()
{
}

//////////////////////////////////////////////////////////////////////
// ServerList - Protected Methods (inherited from AbstractServerList)
//////////////////////////////////////////////////////////////////////
ServerDetails*
ServerList::iget(ServerId id)
{
    uint32_t index = id.indexNumber();
    if ((index < serverList.size()) && serverList[index]) {
        ServerDetails* details = serverList[index].get();
        if (details->serverId == id)
            return details;
    }
    return NULL;
}

ServerDetails*
ServerList::iget(uint32_t index)
{
    return (serverList[index]) ? serverList[index].get() : NULL;
}

/**
 * Return the number of valid indexes in this list w/o lock. Valid does not mean
 * that they're occupied, only that they are within the bounds of the array.
 */
size_t
ServerList::isize() const
{
    return serverList.size();
}

//////////////////////////////////////////////////////////////////////
// ServerList Public Methods
//////////////////////////////////////////////////////////////////////
/**
 * Return the ServerId associated with a given index. If there is none,
 * an invalid ServerId is returned (i.e. the isValid() method will return
 * false.
 */
ServerId
ServerList::operator[](uint32_t index)
{
    Lock lock(mutex);
    if (index >= serverList.size() || !serverList[index])
        return ServerId(/* invalid id */);
    return serverList[index]->serverId;
}

/**
 * Apply a server list from the coordinator to the local server list so they
 * are consistent. In addition, all registered trackers will receive
 * notification of all events related to servers they are aware of along with
 * notifications for any new servers in the updated list.
 *
 * Outdated/repeated updates are ignored.
 *
 * The safety of this method relies heavily on invariants placed on the
 * ordering of updates from the coordinator as well as the ordering of the
 * entries in the updates themselves.
 *
 * The coordinator must (and should already) ensure:
 * 1) Updates should be sent to this server in order. Any out-of-order
 *    updates will be discarded.
 * 2) For an enlisting server that "replaces" an old server in the cluster
 *    (that is, a server re-enlisting with backup data written by a former
 *    cluster member process) the order the CRASHED/REMOVE event for the
 *    replaced server versus the UP event can affect correctness. Without
 *    care restarting backups may inadvertently discard important segment
 *    replicas. The ordering is upheld by the current CoordinatorServerList
 *    implementation in two ways:
 *    a) Update lists are only single-entry only and dispatched in the order
 *       they were generated. The coordinator code is ordered carefully to
 *       ensure the REMOVE/CRASHED for the old server precedes the UP of the
 *       enlisting server.
 *    b) Full lists are the only multi-entry lists; the entries are in the
 *       order they appear in the coordinator server list structure. Because
 *       "slots" in that structure are reused it is possible that an
 *       UP for the enlisting server will appear in the full list before the
 *       CRASHED/REMOVE for the backup it replaces. *However*, this is still
 *       safe. The newly enlisting server receiving the full list cannot
 *       have ever created a replica on the replaced server. Proof: the
 *       first population happened from this full list (which is the only
 *       full list it will ever receive) and the replaced server was
 *       either CRASHED or REMOVE: statuses which aren't eligible for backup
 *       use. This means no restarted process part of that full list can ever
 *       find a replica generated by this master. Because the only threat
 *       to safety comes when backup generate isReplicaNeeded rpcs to masters
 *       that generated replicas they found on storage after restart the
 *       natural full list ordering is safe.
 * 3) That all servers are updated to CRASHED state before they are updated
 *    to REMOVE. This ensures that trackers see UP -> CRASHED -> REMOVE for
 *    each server and simplifies the use of trackers. (Trackers may see
 *    CRASHED -> REMOVE only for servers which were initially CRASHED in
 *    the full list and they may see multiple UPs; users of trackers must take
 *    that into account.)
 *
 * \param list
 *      A snapshot of the coordinator's server list.
 *
 * \return
 *      The current version number for the server list at the end of
 *      this update (it may not have changed if the incoming information
 *      was out of date and had to be ignored).
 */
uint64_t
ServerList::applyServerList(const ProtoBuf::ServerList& list)
{
    Lock lock(mutex);
    if (list.type() == ProtoBuf::ServerList::FULL_LIST) {
        // Ignore a full list unless it is the very first update we have
        // received (i.e. version == 0).
        if (version != 0) {
            LOG(NOTICE, "Ignoring full server list with version %lu "
                    "(server list already populated, version %lu)",
                    list.version_number(), version);
            return version;
        }
    } else {
        // Ignore an update unless its version number exactly follows
        // the local version number.
        if (list.version_number() != version + 1) {
            LOG(NOTICE, "Ignoring out-of order server list update with "
                    "version %lu (local server list is at version %lu)",
                    list.version_number(), version);
            return version;
        }
    }

    LOG(DEBUG, "Server List from coordinator:\n%s",
               list.DebugString().c_str());

    foreach (const auto& server, list.server()) {
        ServerStatus status = static_cast<ServerStatus>(server.status());
        uint32_t index = ServerId{server.server_id()}.indexNumber();
        if (index >= serverList.size())
            serverList.resize(index + 1);
        auto& entry = serverList[index];
        if (status == ServerStatus::UP) {
            LOG(NOTICE, "Server %s is up (server list version %lu)",
                    ServerId{server.server_id()}.toString().c_str(),
                    list.version_number());
            entry.construct(ServerDetails(server));
            foreach (ServerTrackerInterface* tracker, trackers)
                tracker->enqueueChange(*entry, ServerChangeEvent::SERVER_ADDED);
        } else if (status == ServerStatus::CRASHED) {
            LOG(NOTICE, "Server %s is crashed (server list version %lu)",
                    ServerId{server.server_id()}.toString().c_str(),
                    list.version_number());
            entry.construct(ServerDetails(server));
            foreach (ServerTrackerInterface* tracker, trackers) {
                tracker->enqueueChange(*entry,
                                       ServerChangeEvent::SERVER_CRASHED);
            }
        } else if (status == ServerStatus::REMOVE) {
            LOG(NOTICE, "Server %s is removed (server list version %lu)",
                    ServerId{server.server_id()}.toString().c_str(),
                    list.version_number());
            // If we don't already have an entry for this server, no
            // need to make one.
            if (entry) {
                entry->status = ServerStatus::REMOVE;
                foreach (ServerTrackerInterface* tracker, trackers) {
                    tracker->enqueueChange(*entry,
                                           ServerChangeEvent::SERVER_REMOVED);
                }
                entry.destroy();
            }
        } else {
            DIE("unknown ServerStatus %d", server.status());
        }
    }

    version = list.version_number();
    foreach (ServerTrackerInterface* tracker, trackers)
        tracker->fireCallback();
    return version;
}

// - private -

/**
 * Add a server to the server list and enqueue the ADDED event to all
 * registered trackers, but don't fire callbacks. Take care that this
 * will blindly replace any entry for a server with an id that
 * has the same index number.
 * Used only for unit testing. Never call this in real code; all
 * server list entry changes should come from the coordinator via
 * applyServerList().
 */
void
ServerList::testingAdd(const ServerDetails server)
{
    uint32_t index = server.serverId.indexNumber();
    if (index >= serverList.size())
        serverList.resize(index + 1);
    auto& entry = serverList.at(index);
    entry.construct(server);
    foreach (ServerTrackerInterface* tracker, trackers)
        tracker->enqueueChange(*entry, ServerChangeEvent::SERVER_ADDED);
}

/**
 * Crash a server from the server list and enqueue the CRASHED event to all
 * registered trackers, but don't fire callbacks. Take care that this blindly
 * crashes the server in the the slot that corresponds to the index number of
 * the server id. It's your problem to make sure that it is populated and that
 * that makes sense.
 * Used only for unit testing. Never call this in real code; all
 * server list entry changes should come from the coordinator via
 * applyServerList().
 */
void
ServerList::testingCrashed(ServerId serverId)
{
    auto& entry = serverList.at(serverId.indexNumber());
    entry->status = ServerStatus::CRASHED;
    foreach (ServerTrackerInterface* tracker, trackers)
        tracker->enqueueChange(*entry, ServerChangeEvent::SERVER_CRASHED);
}

/**
 * Remove a server from the server list and enqueue the REMOVED event to all
 * registered trackers, but don't fire callbacks. Take care that this blindly
 * removes the server in the the slot that corresponds to the index number of
 * the server id. It's your problem to make sure that it is populated and that
 * that makes sense.
 * Used only for unit testing. Never call this in real code; all
 * server list entries should come from the coordinator via
 * applyServerList().
 */
void
ServerList::testingRemove(ServerId serverId)
{
    auto& entry = serverList.at(serverId.indexNumber());
    entry->status = ServerStatus::REMOVE;
    foreach (ServerTrackerInterface* tracker, trackers)
        tracker->enqueueChange(*entry, ServerChangeEvent::SERVER_REMOVED);
    entry.destroy();
}

} // namespace RAMCloud
<|endoftext|>


<commit_before>/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#include "SkirtBrim.h"
#include "support.h"

namespace cura
{

void generateSkirtBrim(SliceDataStorage& storage, int distance, int count, int minLength)
{
    if (count == 0) return;

    bool externalOnly = (distance > 0); // whether to include holes or not

    const int primary_extruder = storage.getSettingAsIndex("adhesion_extruder_nr");
    const int primary_extruder_skirt_brim_line_width = storage.meshgroup->getExtruderTrain(primary_extruder)->getSettingInMicrons("skirt_line_width");

    Polygons& skirt_primary_extruder = storage.skirt_brim[primary_extruder];

    bool get_convex_hull = count == 1 && distance > 0;

    Polygons first_layer_outline = storage.getLayerOutlines(0, true, externalOnly);

    std::vector<Polygons> skirts;
    for(int skirtNr=0; skirtNr<count;skirtNr++)
    {
        const int offsetDistance = distance + primary_extruder_skirt_brim_line_width * skirtNr + primary_extruder_skirt_brim_line_width / 2;

        skirts.emplace_back(first_layer_outline.offset(offsetDistance, ClipperLib::jtRound));
        Polygons& skirt_polygons = skirts.back();

        //Remove small inner skirt holes. Holes have a negative area, remove anything smaller then 100x extrusion "area"
        for(unsigned int n=0; n<skirt_polygons.size(); n++)
        {
            double area = skirt_polygons[n].area();
            if (area < 0 && area > -primary_extruder_skirt_brim_line_width * primary_extruder_skirt_brim_line_width * 100)
            {
                skirt_polygons.remove(n--);
            }
        }

        if (get_convex_hull)
        {
            skirt_polygons = skirt_polygons.approxConvexHull();
        }

        skirt_primary_extruder.add(skirt_polygons);

        int length = skirt_primary_extruder.polygonLength();
        if (skirtNr + 1 >= count && length > 0 && length < minLength) // make brim have more lines when total length is too small
            count++;
    }

    { // process other extruders' brim/skirt (as one brim line around the old brim)
        int offset_distance = 0;
        int last_width = primary_extruder_skirt_brim_line_width;
        for (int extruder = 0; extruder < storage.meshgroup->getExtruderCount(); extruder++)
        {
            if (extruder == primary_extruder) { continue; }
            int width = storage.meshgroup->getExtruderTrain(extruder)->getSettingInMicrons("skirt_line_width");
            offset_distance += last_width / 2 + width/2;
            last_width = width;
            while (storage.skirt_brim[extruder].polygonLength() < minLength)
            {
                storage.skirt_brim[extruder].add(skirts.back().offset(offset_distance, ClipperLib::jtRound));
                offset_distance += width;
            }
        }
    }
}

}//namespace cura
<commit_msg>Rename skirt_polygons to skirt_brim_polygons in generateSkirtBrim<commit_after>/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#include "SkirtBrim.h"
#include "support.h"

namespace cura
{

void generateSkirtBrim(SliceDataStorage& storage, int distance, int count, int minLength)
{
    if (count == 0) return;

    bool externalOnly = (distance > 0); // whether to include holes or not

    const int primary_extruder = storage.getSettingAsIndex("adhesion_extruder_nr");
    const int primary_extruder_skirt_brim_line_width = storage.meshgroup->getExtruderTrain(primary_extruder)->getSettingInMicrons("skirt_line_width");

    Polygons& skirt_primary_extruder = storage.skirt_brim[primary_extruder];

    bool get_convex_hull = count == 1 && distance > 0;

    Polygons first_layer_outline = storage.getLayerOutlines(0, true, externalOnly);

    std::vector<Polygons> skirts;
    for(int skirtNr=0; skirtNr<count;skirtNr++)
    {
        const int offsetDistance = distance + primary_extruder_skirt_brim_line_width * skirtNr + primary_extruder_skirt_brim_line_width / 2;

        skirts.emplace_back(first_layer_outline.offset(offsetDistance, ClipperLib::jtRound));
        Polygons& skirt_brim_polygons = skirts.back();

        //Remove small inner skirt and brim holes. Holes have a negative area, remove anything smaller then 100x extrusion "area"
        for(unsigned int n=0; n<skirt_brim_polygons.size(); n++)
        {
            double area = skirt_brim_polygons[n].area();
            if (area < 0 && area > -primary_extruder_skirt_brim_line_width * primary_extruder_skirt_brim_line_width * 100)
            {
                skirt_brim_polygons.remove(n--);
            }
        }

        if (get_convex_hull)
        {
            skirt_polygons = skirt_polygons.approxConvexHull();
        }

        skirt_primary_extruder.add(skirt_polygons);

        int length = skirt_primary_extruder.polygonLength();
        if (skirtNr + 1 >= count && length > 0 && length < minLength) // make brim have more lines when total length is too small
            count++;
    }

    { // process other extruders' brim/skirt (as one brim line around the old brim)
        int offset_distance = 0;
        int last_width = primary_extruder_skirt_brim_line_width;
        for (int extruder = 0; extruder < storage.meshgroup->getExtruderCount(); extruder++)
        {
            if (extruder == primary_extruder) { continue; }
            int width = storage.meshgroup->getExtruderTrain(extruder)->getSettingInMicrons("skirt_line_width");
            offset_distance += last_width / 2 + width/2;
            last_width = width;
            while (storage.skirt_brim[extruder].polygonLength() < minLength)
            {
                storage.skirt_brim[extruder].add(skirts.back().offset(offset_distance, ClipperLib::jtRound));
                offset_distance += width;
            }
        }
    }
}

}//namespace cura
<|endoftext|>


<commit_before>//Copyright (C) 2018 Ultimaker B.V.
//CuraEngine is released under the terms of the AGPLv3 or higher.

#include "SkirtBrim.h"
#include "support.h"
#include "Application.h"

namespace cura
{

void SkirtBrim::getFirstLayerOutline(SliceDataStorage& storage, const size_t primary_line_count, const bool is_skirt, Polygons& first_layer_outline)
{
    const ExtruderTrain& train = Application::getInstance().current_slice->scene.current_mesh_group->settings.get<ExtruderTrain&>("adhesion_extruder_nr");
    const bool external_only = is_skirt || train.settings.get<bool>("brim_outside_only"); //Whether to include holes or not. Skirt doesn't have any holes.
    const LayerIndex layer_nr = 0;
    if (is_skirt)
    {
        const bool include_helper_parts = true;
        first_layer_outline = storage.getLayerOutlines(layer_nr, include_helper_parts, external_only);
        first_layer_outline = first_layer_outline.approxConvexHull();
    }
    else
    { // add brim underneath support by removing support where there's brim around the model
        constexpr bool include_helper_parts = false; // include manually below
        constexpr bool external_outlines_only = false; //Remove manually below.
        first_layer_outline = storage.getLayerOutlines(layer_nr, include_helper_parts, external_outlines_only);
        first_layer_outline = first_layer_outline.unionPolygons(); //To guard against overlapping outlines, which would produce holes according to the even-odd rule.
        Polygons first_layer_empty_holes;
        if (external_only)
        {
            first_layer_empty_holes = first_layer_outline.getEmptyHoles();
            first_layer_outline = first_layer_outline.removeEmptyHoles();
        }
        if (storage.support.generated && primary_line_count > 0)
        { // remove model-brim from support
            SupportLayer& support_layer = storage.support.supportLayers[0];
            if (train.settings.get<bool>("brim_replaces_support"))
            {
            // avoid gap in the middle
            //    V
            //  +---+     +----+
            //  |+-+|     |+--+|
            //  || ||     ||[]|| > expand to fit an extra brim line
            //  |+-+|     |+--+|
            //  +---+     +----+
            const coord_t primary_extruder_skirt_brim_line_width = train.settings.get<coord_t>("skirt_brim_line_width") * train.settings.get<Ratio>("initial_layer_line_width_factor");
            Polygons model_brim_covered_area = first_layer_outline.offset(primary_extruder_skirt_brim_line_width * (primary_line_count + primary_line_count % 2), ClipperLib::jtRound); // always leave a gap of an even number of brim lines, so that it fits if it's generating brim from both sides
            if (external_only)
            { // don't remove support within empty holes where no brim is generated.
                model_brim_covered_area.add(first_layer_empty_holes);
            }
            AABB model_brim_covered_area_boundary_box(model_brim_covered_area);
            support_layer.excludeAreasFromSupportInfillAreas(model_brim_covered_area, model_brim_covered_area_boundary_box);
            }
            for (const SupportInfillPart& support_infill_part : support_layer.support_infill_parts)
            {
                first_layer_outline.add(support_infill_part.outline);
            }
            first_layer_outline.add(support_layer.support_bottom);
            first_layer_outline.add(support_layer.support_roof);
        }
        if (storage.primeTower.enabled)
        {
            first_layer_outline.add(storage.primeTower.outer_poly); // don't remove parts of the prime tower, but make a brim for it
        }
    }
    constexpr coord_t join_distance = 20;
    first_layer_outline = first_layer_outline.offset(join_distance).offset(-join_distance); // merge adjacent models into single polygon
    constexpr coord_t smallest_line_length = 200;
    constexpr coord_t largest_error_of_removed_point = 50;
    first_layer_outline.simplify(smallest_line_length, largest_error_of_removed_point); // simplify for faster processing of the brim lines
    if (first_layer_outline.size() == 0)
    {
        logError("Couldn't generate skirt / brim! No polygons on first layer.");
    }
}

int SkirtBrim::generatePrimarySkirtBrimLines(const coord_t start_distance, size_t primary_line_count, const coord_t primary_extruder_minimal_length, const Polygons& first_layer_outline, Polygons& skirt_brim_primary_extruder)
{
    const Settings& adhesion_settings = Application::getInstance().current_slice->scene.current_mesh_group->settings.get<ExtruderTrain&>("adhesion_extruder_nr").settings;
    const coord_t primary_extruder_skirt_brim_line_width = adhesion_settings.get<coord_t>("skirt_brim_line_width") * adhesion_settings.get<Ratio>("initial_layer_line_width_factor");
    coord_t offset_distance = start_distance - primary_extruder_skirt_brim_line_width / 2;
    for (unsigned int skirt_brim_number = 0; skirt_brim_number < primary_line_count; skirt_brim_number++)
    {
        offset_distance += primary_extruder_skirt_brim_line_width;

        Polygons outer_skirt_brim_line = first_layer_outline.offset(offset_distance, ClipperLib::jtRound);

        //Remove small inner skirt and brim holes. Holes have a negative area, remove anything smaller then 100x extrusion "area"
        for (unsigned int n = 0; n < outer_skirt_brim_line.size(); n++)
        {
            double area = outer_skirt_brim_line[n].area();
            if (area < 0 && area > -primary_extruder_skirt_brim_line_width * primary_extruder_skirt_brim_line_width * 100)
            {
                outer_skirt_brim_line.remove(n--);
            }
        }

        skirt_brim_primary_extruder.add(outer_skirt_brim_line);

        int length = skirt_brim_primary_extruder.polygonLength();
        if (skirt_brim_number + 1 >= primary_line_count && length > 0 && length < primary_extruder_minimal_length) //Make brim or skirt have more lines when total length is too small.
        {
            primary_line_count++;
        }
    }
    return offset_distance;
}

void SkirtBrim::generate(SliceDataStorage& storage, int start_distance, unsigned int primary_line_count)
{
    const bool is_skirt = start_distance > 0;

    const size_t adhesion_extruder_nr = Application::getInstance().current_slice->scene.current_mesh_group->settings.get<ExtruderTrain&>("adhesion_extruder_nr").extruder_nr;
    const Settings& adhesion_settings = Application::getInstance().current_slice->scene.extruders[adhesion_extruder_nr].settings;
    const coord_t primary_extruder_skirt_brim_line_width = adhesion_settings.get<coord_t>("skirt_brim_line_width") * adhesion_settings.get<Ratio>("initial_layer_line_width_factor");
    const coord_t primary_extruder_minimal_length = adhesion_settings.get<coord_t>("skirt_brim_minimal_length");

    Polygons& skirt_brim_primary_extruder = storage.skirt_brim[adhesion_extruder_nr];

    Polygons first_layer_outline;
    getFirstLayerOutline(storage, primary_line_count, is_skirt, first_layer_outline);

    const bool has_ooze_shield = storage.oozeShield.size() > 0 && storage.oozeShield[0].size() > 0;
    const bool has_draft_shield = storage.draft_protection_shield.size() > 0;

    if (is_skirt && (has_ooze_shield || has_draft_shield))
    { // make sure we don't generate skirt through draft / ooze shield
        first_layer_outline = first_layer_outline.offset(start_distance - primary_extruder_skirt_brim_line_width / 2, ClipperLib::jtRound).unionPolygons(storage.draft_protection_shield);
        if (has_ooze_shield)
        {
            first_layer_outline = first_layer_outline.unionPolygons(storage.oozeShield[0]);
        }
        first_layer_outline = first_layer_outline.approxConvexHull();
        start_distance = primary_extruder_skirt_brim_line_width / 2;
    }

    int offset_distance = generatePrimarySkirtBrimLines(start_distance, primary_line_count, primary_extruder_minimal_length, first_layer_outline, skirt_brim_primary_extruder);

    // generate brim for ooze shield and draft shield
    if (!is_skirt && (has_ooze_shield || has_draft_shield))
    {
        // generate areas where to make extra brim for the shields
        // avoid gap in the middle
        //    V
        //  +---+     +----+
        //  |+-+|     |+--+|
        //  || ||     ||[]|| > expand to fit an extra brim line
        //  |+-+|     |+--+|
        //  +---+     +----+
        const int64_t primary_skirt_brim_width = (primary_line_count + primary_line_count % 2) * primary_extruder_skirt_brim_line_width; // always use an even number, because we will fil the area from both sides

        Polygons shield_brim;
        if (has_ooze_shield)
        {
            shield_brim = storage.oozeShield[0].difference(storage.oozeShield[0].offset(-primary_skirt_brim_width - primary_extruder_skirt_brim_line_width));
        }
        if (has_draft_shield)
        {
            shield_brim = shield_brim.unionPolygons(storage.draft_protection_shield.difference(storage.draft_protection_shield.offset(-primary_skirt_brim_width - primary_extruder_skirt_brim_line_width)));
        }
        const Polygons outer_primary_brim = first_layer_outline.offset(offset_distance, ClipperLib::jtRound);
        shield_brim = shield_brim.difference(outer_primary_brim.offset(primary_extruder_skirt_brim_line_width));

        // generate brim within shield_brim
        skirt_brim_primary_extruder.add(shield_brim);
        while (shield_brim.size() > 0)
        {
            shield_brim = shield_brim.offset(-primary_extruder_skirt_brim_line_width);
            skirt_brim_primary_extruder.add(shield_brim);
        }

        // update parameters to generate secondary skirt around
        first_layer_outline = outer_primary_brim;
        if (has_draft_shield)
        {
            first_layer_outline = first_layer_outline.unionPolygons(storage.draft_protection_shield);
        }
        if (has_ooze_shield)
        {
            first_layer_outline = first_layer_outline.unionPolygons(storage.oozeShield[0]);
        }

        offset_distance = 0;
    }

    { // process other extruders' brim/skirt (as one brim line around the old brim)
        int last_width = primary_extruder_skirt_brim_line_width;
        std::vector<bool> extruder_is_used = storage.getExtrudersUsed();
        for (size_t extruder_nr = 0; extruder_nr < Application::getInstance().current_slice->scene.extruders.size(); extruder_nr++)
        {
            if (extruder_nr == adhesion_extruder_nr || !extruder_is_used[extruder_nr])
            {
                continue;
            }
            const ExtruderTrain& train = Application::getInstance().current_slice->scene.extruders[extruder_nr];
            const coord_t width = train.settings.get<coord_t>("skirt_brim_line_width") * train.settings.get<Ratio>("initial_layer_line_width_factor");
            const coord_t minimal_length = train.settings.get<coord_t>("skirt_brim_minimal_length");
            offset_distance += last_width / 2 + width/2;
            last_width = width;
            while (storage.skirt_brim[extruder_nr].polygonLength() < minimal_length)
            {
                storage.skirt_brim[extruder_nr].add(first_layer_outline.offset(offset_distance, ClipperLib::jtRound));
                offset_distance += width;
            }
        }
    }
}

}//namespace cura
<commit_msg>indent only<commit_after>//Copyright (C) 2018 Ultimaker B.V.
//CuraEngine is released under the terms of the AGPLv3 or higher.

#include "SkirtBrim.h"
#include "support.h"
#include "Application.h"

namespace cura
{

void SkirtBrim::getFirstLayerOutline(SliceDataStorage& storage, const size_t primary_line_count, const bool is_skirt, Polygons& first_layer_outline)
{
    const ExtruderTrain& train = Application::getInstance().current_slice->scene.current_mesh_group->settings.get<ExtruderTrain&>("adhesion_extruder_nr");
    const bool external_only = is_skirt || train.settings.get<bool>("brim_outside_only"); //Whether to include holes or not. Skirt doesn't have any holes.
    const LayerIndex layer_nr = 0;
    if (is_skirt)
    {
        const bool include_helper_parts = true;
        first_layer_outline = storage.getLayerOutlines(layer_nr, include_helper_parts, external_only);
        first_layer_outline = first_layer_outline.approxConvexHull();
    }
    else
    { // add brim underneath support by removing support where there's brim around the model
        constexpr bool include_helper_parts = false; // include manually below
        constexpr bool external_outlines_only = false; //Remove manually below.
        first_layer_outline = storage.getLayerOutlines(layer_nr, include_helper_parts, external_outlines_only);
        first_layer_outline = first_layer_outline.unionPolygons(); //To guard against overlapping outlines, which would produce holes according to the even-odd rule.
        Polygons first_layer_empty_holes;
        if (external_only)
        {
            first_layer_empty_holes = first_layer_outline.getEmptyHoles();
            first_layer_outline = first_layer_outline.removeEmptyHoles();
        }
        if (storage.support.generated && primary_line_count > 0)
        { // remove model-brim from support
            SupportLayer& support_layer = storage.support.supportLayers[0];
            if (train.settings.get<bool>("brim_replaces_support"))
            {
                // avoid gap in the middle
                //    V
                //  +---+     +----+
                //  |+-+|     |+--+|
                //  || ||     ||[]|| > expand to fit an extra brim line
                //  |+-+|     |+--+|
                //  +---+     +----+
                const coord_t primary_extruder_skirt_brim_line_width = train.settings.get<coord_t>("skirt_brim_line_width") * train.settings.get<Ratio>("initial_layer_line_width_factor");
                Polygons model_brim_covered_area = first_layer_outline.offset(primary_extruder_skirt_brim_line_width * (primary_line_count + primary_line_count % 2), ClipperLib::jtRound); // always leave a gap of an even number of brim lines, so that it fits if it's generating brim from both sides
                if (external_only)
                { // don't remove support within empty holes where no brim is generated.
                    model_brim_covered_area.add(first_layer_empty_holes);
                }
                AABB model_brim_covered_area_boundary_box(model_brim_covered_area);
                support_layer.excludeAreasFromSupportInfillAreas(model_brim_covered_area, model_brim_covered_area_boundary_box);
            }
            for (const SupportInfillPart& support_infill_part : support_layer.support_infill_parts)
            {
                first_layer_outline.add(support_infill_part.outline);
            }
            first_layer_outline.add(support_layer.support_bottom);
            first_layer_outline.add(support_layer.support_roof);
        }
        if (storage.primeTower.enabled)
        {
            first_layer_outline.add(storage.primeTower.outer_poly); // don't remove parts of the prime tower, but make a brim for it
        }
    }
    constexpr coord_t join_distance = 20;
    first_layer_outline = first_layer_outline.offset(join_distance).offset(-join_distance); // merge adjacent models into single polygon
    constexpr coord_t smallest_line_length = 200;
    constexpr coord_t largest_error_of_removed_point = 50;
    first_layer_outline.simplify(smallest_line_length, largest_error_of_removed_point); // simplify for faster processing of the brim lines
    if (first_layer_outline.size() == 0)
    {
        logError("Couldn't generate skirt / brim! No polygons on first layer.");
    }
}

int SkirtBrim::generatePrimarySkirtBrimLines(const coord_t start_distance, size_t primary_line_count, const coord_t primary_extruder_minimal_length, const Polygons& first_layer_outline, Polygons& skirt_brim_primary_extruder)
{
    const Settings& adhesion_settings = Application::getInstance().current_slice->scene.current_mesh_group->settings.get<ExtruderTrain&>("adhesion_extruder_nr").settings;
    const coord_t primary_extruder_skirt_brim_line_width = adhesion_settings.get<coord_t>("skirt_brim_line_width") * adhesion_settings.get<Ratio>("initial_layer_line_width_factor");
    coord_t offset_distance = start_distance - primary_extruder_skirt_brim_line_width / 2;
    for (unsigned int skirt_brim_number = 0; skirt_brim_number < primary_line_count; skirt_brim_number++)
    {
        offset_distance += primary_extruder_skirt_brim_line_width;

        Polygons outer_skirt_brim_line = first_layer_outline.offset(offset_distance, ClipperLib::jtRound);

        //Remove small inner skirt and brim holes. Holes have a negative area, remove anything smaller then 100x extrusion "area"
        for (unsigned int n = 0; n < outer_skirt_brim_line.size(); n++)
        {
            double area = outer_skirt_brim_line[n].area();
            if (area < 0 && area > -primary_extruder_skirt_brim_line_width * primary_extruder_skirt_brim_line_width * 100)
            {
                outer_skirt_brim_line.remove(n--);
            }
        }

        skirt_brim_primary_extruder.add(outer_skirt_brim_line);

        int length = skirt_brim_primary_extruder.polygonLength();
        if (skirt_brim_number + 1 >= primary_line_count && length > 0 && length < primary_extruder_minimal_length) //Make brim or skirt have more lines when total length is too small.
        {
            primary_line_count++;
        }
    }
    return offset_distance;
}

void SkirtBrim::generate(SliceDataStorage& storage, int start_distance, unsigned int primary_line_count)
{
    const bool is_skirt = start_distance > 0;

    const size_t adhesion_extruder_nr = Application::getInstance().current_slice->scene.current_mesh_group->settings.get<ExtruderTrain&>("adhesion_extruder_nr").extruder_nr;
    const Settings& adhesion_settings = Application::getInstance().current_slice->scene.extruders[adhesion_extruder_nr].settings;
    const coord_t primary_extruder_skirt_brim_line_width = adhesion_settings.get<coord_t>("skirt_brim_line_width") * adhesion_settings.get<Ratio>("initial_layer_line_width_factor");
    const coord_t primary_extruder_minimal_length = adhesion_settings.get<coord_t>("skirt_brim_minimal_length");

    Polygons& skirt_brim_primary_extruder = storage.skirt_brim[adhesion_extruder_nr];

    Polygons first_layer_outline;
    getFirstLayerOutline(storage, primary_line_count, is_skirt, first_layer_outline);

    const bool has_ooze_shield = storage.oozeShield.size() > 0 && storage.oozeShield[0].size() > 0;
    const bool has_draft_shield = storage.draft_protection_shield.size() > 0;

    if (is_skirt && (has_ooze_shield || has_draft_shield))
    { // make sure we don't generate skirt through draft / ooze shield
        first_layer_outline = first_layer_outline.offset(start_distance - primary_extruder_skirt_brim_line_width / 2, ClipperLib::jtRound).unionPolygons(storage.draft_protection_shield);
        if (has_ooze_shield)
        {
            first_layer_outline = first_layer_outline.unionPolygons(storage.oozeShield[0]);
        }
        first_layer_outline = first_layer_outline.approxConvexHull();
        start_distance = primary_extruder_skirt_brim_line_width / 2;
    }

    int offset_distance = generatePrimarySkirtBrimLines(start_distance, primary_line_count, primary_extruder_minimal_length, first_layer_outline, skirt_brim_primary_extruder);

    // generate brim for ooze shield and draft shield
    if (!is_skirt && (has_ooze_shield || has_draft_shield))
    {
        // generate areas where to make extra brim for the shields
        // avoid gap in the middle
        //    V
        //  +---+     +----+
        //  |+-+|     |+--+|
        //  || ||     ||[]|| > expand to fit an extra brim line
        //  |+-+|     |+--+|
        //  +---+     +----+
        const int64_t primary_skirt_brim_width = (primary_line_count + primary_line_count % 2) * primary_extruder_skirt_brim_line_width; // always use an even number, because we will fil the area from both sides

        Polygons shield_brim;
        if (has_ooze_shield)
        {
            shield_brim = storage.oozeShield[0].difference(storage.oozeShield[0].offset(-primary_skirt_brim_width - primary_extruder_skirt_brim_line_width));
        }
        if (has_draft_shield)
        {
            shield_brim = shield_brim.unionPolygons(storage.draft_protection_shield.difference(storage.draft_protection_shield.offset(-primary_skirt_brim_width - primary_extruder_skirt_brim_line_width)));
        }
        const Polygons outer_primary_brim = first_layer_outline.offset(offset_distance, ClipperLib::jtRound);
        shield_brim = shield_brim.difference(outer_primary_brim.offset(primary_extruder_skirt_brim_line_width));

        // generate brim within shield_brim
        skirt_brim_primary_extruder.add(shield_brim);
        while (shield_brim.size() > 0)
        {
            shield_brim = shield_brim.offset(-primary_extruder_skirt_brim_line_width);
            skirt_brim_primary_extruder.add(shield_brim);
        }

        // update parameters to generate secondary skirt around
        first_layer_outline = outer_primary_brim;
        if (has_draft_shield)
        {
            first_layer_outline = first_layer_outline.unionPolygons(storage.draft_protection_shield);
        }
        if (has_ooze_shield)
        {
            first_layer_outline = first_layer_outline.unionPolygons(storage.oozeShield[0]);
        }

        offset_distance = 0;
    }

    { // process other extruders' brim/skirt (as one brim line around the old brim)
        int last_width = primary_extruder_skirt_brim_line_width;
        std::vector<bool> extruder_is_used = storage.getExtrudersUsed();
        for (size_t extruder_nr = 0; extruder_nr < Application::getInstance().current_slice->scene.extruders.size(); extruder_nr++)
        {
            if (extruder_nr == adhesion_extruder_nr || !extruder_is_used[extruder_nr])
            {
                continue;
            }
            const ExtruderTrain& train = Application::getInstance().current_slice->scene.extruders[extruder_nr];
            const coord_t width = train.settings.get<coord_t>("skirt_brim_line_width") * train.settings.get<Ratio>("initial_layer_line_width_factor");
            const coord_t minimal_length = train.settings.get<coord_t>("skirt_brim_minimal_length");
            offset_distance += last_width / 2 + width/2;
            last_width = width;
            while (storage.skirt_brim[extruder_nr].polygonLength() < minimal_length)
            {
                storage.skirt_brim[extruder_nr].add(first_layer_outline.offset(offset_distance, ClipperLib::jtRound));
                offset_distance += width;
            }
        }
    }
}

}//namespace cura
<|endoftext|>


<commit_before>/*
 * The "slice" memory allocator.  It is an allocator for large numbers
 * of small fixed-size objects.
 *
 * author: Max Kellermann <mk@cm4all.com>
 */

#include "SlicePool.hxx"
#include "system/mmap.h"
#include "AllocatorStats.hxx"

#include <boost/intrusive/list.hpp>

#include <new>

#include <assert.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>

static constexpr unsigned ALLOCATED = -1;
static constexpr unsigned END_OF_LIST = -2;

#ifndef NDEBUG
static constexpr unsigned MARK = -3;
#endif

struct slice_slot {
    unsigned next;

    constexpr bool IsAllocated() const {
        return next == ALLOCATED;
    }
};

struct SliceArea {
    static constexpr auto link_mode = boost::intrusive::normal_link;
    typedef boost::intrusive::link_mode<link_mode> LinkMode;
    typedef boost::intrusive::list_member_hook<LinkMode> SiblingsHook;
    SiblingsHook siblings;

    unsigned allocated_count;

    unsigned free_head;

    struct slice_slot slices[1];

private:
    SliceArea(SlicePool &pool);

    ~SliceArea() {
        assert(allocated_count == 0);
    }

public:
    static SliceArea *New(SlicePool &pool);
    void Delete(SlicePool &pool);

    void ForkCow(const SlicePool &pool, bool inherit);

    bool IsEmpty() const {
        return allocated_count == 0;
    }

    bool IsFull(const SlicePool &pool) const;

    size_t GetNettoSize(size_t slice_size) const {
        return allocated_count * slice_size;
    }

    gcc_pure
    void *GetPage(const SlicePool &pool, unsigned page);

    gcc_pure
    void *GetSlice(const SlicePool &pool, unsigned slice);

    /**
     * Calculates the allocation slot index from an allocated pointer.
     * This is used to locate the #slice_slot for a pointer passed to a
     * public function.
     */
    gcc_pure
    unsigned IndexOf(const SlicePool &pool, const void *_p);

    /**
     * Find the first free slot index, starting at the specified position.
     */
    gcc_pure
    unsigned FindFree(const SlicePool &pool, unsigned start) const;

    /**
     * Find the first allocated slot index, starting at the specified
     * position.
     */
    gcc_pure
    unsigned FindAllocated(const SlicePool &pool,
                           unsigned start) const;

    /**
     * Punch a hole in the memory map in the specified slot index range.
     * This means notifying the kernel that we will no longer need the
     * contents, which allows the kernel to drop the allocated pages and
     * reuse it for other processes.
     */
    void PunchSliceRange(SlicePool &pool,
                         unsigned start, gcc_unused unsigned end);

    void Compress(SlicePool &pool);

    void *Alloc(SlicePool &pool);
    void Free(SlicePool &pool, void *p);

    struct Disposer {
        SlicePool &pool;

        void operator()(SliceArea *area) {
            area->Delete(pool);
        }
    };
};

constexpr
static inline size_t
align_size(size_t size)
{
    return ((size - 1) | 0x1f) + 1;
}

gcc_const
static inline size_t
align_page_size(size_t size)
{
    return ((size - 1) | (mmap_page_size() - 1)) + 1;
}

static constexpr unsigned
divide_round_up(unsigned a, unsigned b)
{
    return (a + b - 1) / b;
}

struct SlicePool {
    size_t slice_size;

    /**
     * Number of slices that fit on one MMU page (4 kB).
     */
    unsigned slices_per_page;

    unsigned pages_per_slice;

    unsigned pages_per_area;

    unsigned slices_per_area;

    /**
     * Number of pages for the area header.
     */
    unsigned header_pages;

    size_t area_size;

    boost::intrusive::list<SliceArea,
                           boost::intrusive::member_hook<SliceArea,
                                                         SliceArea::SiblingsHook,
                                                         &SliceArea::siblings>,
                           boost::intrusive::constant_time_size<false>> areas;

    bool fork_cow = true;

    SlicePool(size_t _slice_size, unsigned _slices_per_area);
    ~SlicePool();

    void ForkCow(bool inherit);

    gcc_pure
    AllocatorStats GetStats() const;

    void Compress();

    gcc_pure
    SliceArea *FindNonFullArea();

    SliceAllocation Alloc();
};

/*
 * SliceArea methods
 *
 */

SliceArea::SliceArea(SlicePool &pool)
    :allocated_count(0), free_head(0)
{
    /* build the "free" list */
    for (unsigned i = 0; i < pool.slices_per_area - 1; ++i)
        slices[i].next = i + 1;

    slices[pool.slices_per_area - 1].next = END_OF_LIST;
}

SliceArea *
SliceArea::New(SlicePool &pool)
{
    void *p = mmap_alloc_anonymous(pool.area_size);
    if (p == (void *)-1) {
        fputs("Out of adress space\n", stderr);
        abort();
    }

    return ::new(p) SliceArea(pool);
}

inline bool
SliceArea::IsFull(gcc_unused const SlicePool &pool) const
{
    assert(free_head < pool.slices_per_area ||
           free_head == END_OF_LIST);

    return free_head == END_OF_LIST;
}

void
SliceArea::Delete(SlicePool &pool)
{
    assert(allocated_count == 0);

#ifndef NDEBUG
    for (unsigned i = 0; i < pool.slices_per_area; ++i)
        assert(slices[i].next < pool.slices_per_area ||
               slices[i].next == END_OF_LIST);

    unsigned i = free_head;
    while (i != END_OF_LIST) {
        assert(i < pool.slices_per_area);

        unsigned next = slices[i].next;
        slices[i].next = MARK;
        i = next;
    }
#endif

    this->~SliceArea();
    mmap_free(this, pool.area_size);
}

inline void *
SliceArea::GetPage(const SlicePool &pool, unsigned page)
{
    assert(page <= pool.pages_per_area);

    return (uint8_t *)this + (pool.header_pages + page) * mmap_page_size();
}

inline void *
SliceArea::GetSlice(const SlicePool &pool, unsigned slice)
{
    assert(slice < pool.slices_per_area);
    assert(slices[slice].IsAllocated());

    unsigned page = (slice / pool.slices_per_page) * pool.pages_per_slice;
    slice %= pool.slices_per_page;

    return (uint8_t *)GetPage(pool, page) + slice * pool.slice_size;
}

inline unsigned
SliceArea::IndexOf(const SlicePool &pool, const void *_p)
{
    const uint8_t *p = (const uint8_t *)_p;
    assert(p >= (uint8_t *)GetPage(pool, 0));
    assert(p < (uint8_t *)GetPage(pool, pool.pages_per_area));

    size_t offset = p - (const uint8_t *)this;
    const unsigned page = offset / mmap_page_size() - pool.header_pages;
    offset %= mmap_page_size();
    assert(offset % pool.slice_size == 0);

    return page * pool.slices_per_page / pool.pages_per_slice
        + offset / pool.slice_size;
}

unsigned
SliceArea::FindFree(const SlicePool &pool, unsigned start) const
{
    assert(start <= pool.slices_per_page);

    const unsigned end = pool.slices_per_page;

    unsigned i;
    for (i = start; i != end; ++i)
        if (!slices[i].IsAllocated())
            break;

    return i;
}

/**
 * Find the first allocated slot index, starting at the specified
 * position.
 */
gcc_pure
unsigned
SliceArea::FindAllocated(const SlicePool &pool, unsigned start) const
{
    assert(start <= pool.slices_per_page);

    const unsigned end = pool.slices_per_page;

    unsigned i;
    for (i = start; i != end; ++i)
        if (slices[i].IsAllocated())
            break;

    return i;
}

void
SliceArea::PunchSliceRange(SlicePool &pool,
                            unsigned start, gcc_unused unsigned end)
{
    assert(start <= end);

    unsigned start_page = divide_round_up(start, pool.slices_per_page)
        * pool.pages_per_slice;
    unsigned end_page = (start / pool.slices_per_page )
        * pool.pages_per_slice;
    assert(start_page <= end_page + 1);
    if (start_page >= end_page)
        return;

    uint8_t *start_pointer = (uint8_t *)GetPage(pool, start_page);
    uint8_t *end_pointer = (uint8_t *)GetPage(pool, end_page);

    mmap_discard_pages(start_pointer, end_pointer - start_pointer);
}

void
SliceArea::Compress(SlicePool &pool)
{
    unsigned position = 0;

    while (true) {
        unsigned first_free = FindFree(pool, position);
        if (first_free == pool.slices_per_page)
            break;

        unsigned first_allocated = FindAllocated(pool, first_free + 1);
        PunchSliceRange(pool, first_free, first_allocated);

        position = first_allocated;
    }
}

/*
 * SlicePool methods
 *
 */

inline
SlicePool::SlicePool(size_t _slice_size, unsigned _slices_per_area)
{
    assert(_slice_size > 0);
    assert(_slices_per_area > 0);

    if (_slice_size <= mmap_page_size() / 2) {
        slice_size = align_size(_slice_size);

        slices_per_page = mmap_page_size() / slice_size;
        pages_per_slice = 1;

        pages_per_area = divide_round_up(_slices_per_area,
                                         slices_per_page);
    } else {
        slice_size = align_page_size(_slice_size);

        slices_per_page = 1;
        pages_per_slice = slice_size / mmap_page_size();

        pages_per_area = _slices_per_area * pages_per_slice;
    }

    slices_per_area = (pages_per_area / pages_per_slice) * slices_per_page;

    const SliceArea *area = nullptr;
    const size_t header_size = sizeof(*area)
        + sizeof(area->slices[0]) * (slices_per_area - 1);
    header_pages = divide_round_up(header_size, mmap_page_size());

    area_size = mmap_page_size() * (header_pages + pages_per_area);
}

inline
SlicePool::~SlicePool()
{
    areas.clear_and_dispose(SliceArea::Disposer{*this});
}

SlicePool *
slice_pool_new(size_t slice_size, unsigned slices_per_area)
{
    return new SlicePool(slice_size, slices_per_area);
}

void
slice_pool_free(SlicePool *pool)
{
    delete pool;
}

inline void
SliceArea::ForkCow(const SlicePool &pool, bool inherit)
{
    mmap_enable_fork(this, pool.area_size, inherit);
}

inline void
SlicePool::ForkCow(bool inherit)
{
    if (inherit == fork_cow)
        return;

    fork_cow = inherit;
    for (auto &area : areas)
        area.ForkCow(*this, fork_cow);
}

void
slice_pool_fork_cow(SlicePool &pool, bool inherit)
{
    pool.ForkCow(inherit);
}

size_t
slice_pool_get_slice_size(const SlicePool *pool)
{
    return pool->slice_size;
}

inline void
SlicePool::Compress()
{
    for (auto i = areas.begin(), end = areas.end(); i != end;) {
        if (i->IsEmpty()) {
            i = areas.erase_and_dispose(i, SliceArea::Disposer{*this});
        } else {
            i->Compress(*this);
            ++i;
        }
    }
}

void
slice_pool_compress(SlicePool *pool)
{
    pool->Compress();
}

gcc_pure
inline SliceArea *
SlicePool::FindNonFullArea()
{
    for (SliceArea &area : areas)
        if (!area.IsFull(*this))
            return &area;

    return nullptr;
}

inline void *
SliceArea::Alloc(SlicePool &pool)
{
    assert(!IsFull(pool));

    const unsigned i = free_head;
    struct slice_slot *const slot = &slices[i];

    ++allocated_count;
    free_head = slot->next;
    slot->next = ALLOCATED;

    return GetSlice(pool, i);
}

inline SliceAllocation
SlicePool::Alloc()
{
    SliceArea *area = FindNonFullArea();
    if (area == nullptr) {
        area = SliceArea::New(*this);
        area->ForkCow(*this, fork_cow);
        areas.push_front(*area);
    }

    return { area, area->Alloc(*this), slice_size };
}

SliceAllocation
slice_alloc(SlicePool *pool)
{
    return pool->Alloc();
}

inline void
SliceArea::Free(SlicePool &pool, void *p)
{
    unsigned i = IndexOf(pool, p);
    assert(slices[i].IsAllocated());

    slices[i].next = free_head;
    free_head = i;

    assert(allocated_count > 0);
    --allocated_count;
}

void
slice_free(SlicePool *pool, SliceArea *area, void *p)
{
    area->Free(*pool, p);
}

inline AllocatorStats
SlicePool::GetStats() const
{
    AllocatorStats stats;
    stats.brutto_size = stats.netto_size = 0;

    for (const auto &area : areas) {
        stats.brutto_size += area_size;
        stats.netto_size += area.GetNettoSize(slice_size);
    }

    return stats;
}

AllocatorStats
slice_pool_get_stats(const SlicePool &pool)
{
    return pool.GetStats();
}
<commit_msg>SlicePool: rename struct slice_slot<commit_after>/*
 * The "slice" memory allocator.  It is an allocator for large numbers
 * of small fixed-size objects.
 *
 * author: Max Kellermann <mk@cm4all.com>
 */

#include "SlicePool.hxx"
#include "system/mmap.h"
#include "AllocatorStats.hxx"

#include <boost/intrusive/list.hpp>

#include <new>

#include <assert.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>

static constexpr unsigned ALLOCATED = -1;
static constexpr unsigned END_OF_LIST = -2;

#ifndef NDEBUG
static constexpr unsigned MARK = -3;
#endif

struct SliceSlot {
    unsigned next;

    constexpr bool IsAllocated() const {
        return next == ALLOCATED;
    }
};

struct SliceArea {
    static constexpr auto link_mode = boost::intrusive::normal_link;
    typedef boost::intrusive::link_mode<link_mode> LinkMode;
    typedef boost::intrusive::list_member_hook<LinkMode> SiblingsHook;
    SiblingsHook siblings;

    unsigned allocated_count;

    unsigned free_head;

    SliceSlot slices[1];

private:
    SliceArea(SlicePool &pool);

    ~SliceArea() {
        assert(allocated_count == 0);
    }

public:
    static SliceArea *New(SlicePool &pool);
    void Delete(SlicePool &pool);

    void ForkCow(const SlicePool &pool, bool inherit);

    bool IsEmpty() const {
        return allocated_count == 0;
    }

    bool IsFull(const SlicePool &pool) const;

    size_t GetNettoSize(size_t slice_size) const {
        return allocated_count * slice_size;
    }

    gcc_pure
    void *GetPage(const SlicePool &pool, unsigned page);

    gcc_pure
    void *GetSlice(const SlicePool &pool, unsigned slice);

    /**
     * Calculates the allocation slot index from an allocated pointer.
     * This is used to locate the #SliceSlot for a pointer passed to a
     * public function.
     */
    gcc_pure
    unsigned IndexOf(const SlicePool &pool, const void *_p);

    /**
     * Find the first free slot index, starting at the specified position.
     */
    gcc_pure
    unsigned FindFree(const SlicePool &pool, unsigned start) const;

    /**
     * Find the first allocated slot index, starting at the specified
     * position.
     */
    gcc_pure
    unsigned FindAllocated(const SlicePool &pool,
                           unsigned start) const;

    /**
     * Punch a hole in the memory map in the specified slot index range.
     * This means notifying the kernel that we will no longer need the
     * contents, which allows the kernel to drop the allocated pages and
     * reuse it for other processes.
     */
    void PunchSliceRange(SlicePool &pool,
                         unsigned start, gcc_unused unsigned end);

    void Compress(SlicePool &pool);

    void *Alloc(SlicePool &pool);
    void Free(SlicePool &pool, void *p);

    struct Disposer {
        SlicePool &pool;

        void operator()(SliceArea *area) {
            area->Delete(pool);
        }
    };
};

constexpr
static inline size_t
align_size(size_t size)
{
    return ((size - 1) | 0x1f) + 1;
}

gcc_const
static inline size_t
align_page_size(size_t size)
{
    return ((size - 1) | (mmap_page_size() - 1)) + 1;
}

static constexpr unsigned
divide_round_up(unsigned a, unsigned b)
{
    return (a + b - 1) / b;
}

struct SlicePool {
    size_t slice_size;

    /**
     * Number of slices that fit on one MMU page (4 kB).
     */
    unsigned slices_per_page;

    unsigned pages_per_slice;

    unsigned pages_per_area;

    unsigned slices_per_area;

    /**
     * Number of pages for the area header.
     */
    unsigned header_pages;

    size_t area_size;

    boost::intrusive::list<SliceArea,
                           boost::intrusive::member_hook<SliceArea,
                                                         SliceArea::SiblingsHook,
                                                         &SliceArea::siblings>,
                           boost::intrusive::constant_time_size<false>> areas;

    bool fork_cow = true;

    SlicePool(size_t _slice_size, unsigned _slices_per_area);
    ~SlicePool();

    void ForkCow(bool inherit);

    gcc_pure
    AllocatorStats GetStats() const;

    void Compress();

    gcc_pure
    SliceArea *FindNonFullArea();

    SliceAllocation Alloc();
};

/*
 * SliceArea methods
 *
 */

SliceArea::SliceArea(SlicePool &pool)
    :allocated_count(0), free_head(0)
{
    /* build the "free" list */
    for (unsigned i = 0; i < pool.slices_per_area - 1; ++i)
        slices[i].next = i + 1;

    slices[pool.slices_per_area - 1].next = END_OF_LIST;
}

SliceArea *
SliceArea::New(SlicePool &pool)
{
    void *p = mmap_alloc_anonymous(pool.area_size);
    if (p == (void *)-1) {
        fputs("Out of adress space\n", stderr);
        abort();
    }

    return ::new(p) SliceArea(pool);
}

inline bool
SliceArea::IsFull(gcc_unused const SlicePool &pool) const
{
    assert(free_head < pool.slices_per_area ||
           free_head == END_OF_LIST);

    return free_head == END_OF_LIST;
}

void
SliceArea::Delete(SlicePool &pool)
{
    assert(allocated_count == 0);

#ifndef NDEBUG
    for (unsigned i = 0; i < pool.slices_per_area; ++i)
        assert(slices[i].next < pool.slices_per_area ||
               slices[i].next == END_OF_LIST);

    unsigned i = free_head;
    while (i != END_OF_LIST) {
        assert(i < pool.slices_per_area);

        unsigned next = slices[i].next;
        slices[i].next = MARK;
        i = next;
    }
#endif

    this->~SliceArea();
    mmap_free(this, pool.area_size);
}

inline void *
SliceArea::GetPage(const SlicePool &pool, unsigned page)
{
    assert(page <= pool.pages_per_area);

    return (uint8_t *)this + (pool.header_pages + page) * mmap_page_size();
}

inline void *
SliceArea::GetSlice(const SlicePool &pool, unsigned slice)
{
    assert(slice < pool.slices_per_area);
    assert(slices[slice].IsAllocated());

    unsigned page = (slice / pool.slices_per_page) * pool.pages_per_slice;
    slice %= pool.slices_per_page;

    return (uint8_t *)GetPage(pool, page) + slice * pool.slice_size;
}

inline unsigned
SliceArea::IndexOf(const SlicePool &pool, const void *_p)
{
    const uint8_t *p = (const uint8_t *)_p;
    assert(p >= (uint8_t *)GetPage(pool, 0));
    assert(p < (uint8_t *)GetPage(pool, pool.pages_per_area));

    size_t offset = p - (const uint8_t *)this;
    const unsigned page = offset / mmap_page_size() - pool.header_pages;
    offset %= mmap_page_size();
    assert(offset % pool.slice_size == 0);

    return page * pool.slices_per_page / pool.pages_per_slice
        + offset / pool.slice_size;
}

unsigned
SliceArea::FindFree(const SlicePool &pool, unsigned start) const
{
    assert(start <= pool.slices_per_page);

    const unsigned end = pool.slices_per_page;

    unsigned i;
    for (i = start; i != end; ++i)
        if (!slices[i].IsAllocated())
            break;

    return i;
}

/**
 * Find the first allocated slot index, starting at the specified
 * position.
 */
gcc_pure
unsigned
SliceArea::FindAllocated(const SlicePool &pool, unsigned start) const
{
    assert(start <= pool.slices_per_page);

    const unsigned end = pool.slices_per_page;

    unsigned i;
    for (i = start; i != end; ++i)
        if (slices[i].IsAllocated())
            break;

    return i;
}

void
SliceArea::PunchSliceRange(SlicePool &pool,
                            unsigned start, gcc_unused unsigned end)
{
    assert(start <= end);

    unsigned start_page = divide_round_up(start, pool.slices_per_page)
        * pool.pages_per_slice;
    unsigned end_page = (start / pool.slices_per_page )
        * pool.pages_per_slice;
    assert(start_page <= end_page + 1);
    if (start_page >= end_page)
        return;

    uint8_t *start_pointer = (uint8_t *)GetPage(pool, start_page);
    uint8_t *end_pointer = (uint8_t *)GetPage(pool, end_page);

    mmap_discard_pages(start_pointer, end_pointer - start_pointer);
}

void
SliceArea::Compress(SlicePool &pool)
{
    unsigned position = 0;

    while (true) {
        unsigned first_free = FindFree(pool, position);
        if (first_free == pool.slices_per_page)
            break;

        unsigned first_allocated = FindAllocated(pool, first_free + 1);
        PunchSliceRange(pool, first_free, first_allocated);

        position = first_allocated;
    }
}

/*
 * SlicePool methods
 *
 */

inline
SlicePool::SlicePool(size_t _slice_size, unsigned _slices_per_area)
{
    assert(_slice_size > 0);
    assert(_slices_per_area > 0);

    if (_slice_size <= mmap_page_size() / 2) {
        slice_size = align_size(_slice_size);

        slices_per_page = mmap_page_size() / slice_size;
        pages_per_slice = 1;

        pages_per_area = divide_round_up(_slices_per_area,
                                         slices_per_page);
    } else {
        slice_size = align_page_size(_slice_size);

        slices_per_page = 1;
        pages_per_slice = slice_size / mmap_page_size();

        pages_per_area = _slices_per_area * pages_per_slice;
    }

    slices_per_area = (pages_per_area / pages_per_slice) * slices_per_page;

    const SliceArea *area = nullptr;
    const size_t header_size = sizeof(*area)
        + sizeof(area->slices[0]) * (slices_per_area - 1);
    header_pages = divide_round_up(header_size, mmap_page_size());

    area_size = mmap_page_size() * (header_pages + pages_per_area);
}

inline
SlicePool::~SlicePool()
{
    areas.clear_and_dispose(SliceArea::Disposer{*this});
}

SlicePool *
slice_pool_new(size_t slice_size, unsigned slices_per_area)
{
    return new SlicePool(slice_size, slices_per_area);
}

void
slice_pool_free(SlicePool *pool)
{
    delete pool;
}

inline void
SliceArea::ForkCow(const SlicePool &pool, bool inherit)
{
    mmap_enable_fork(this, pool.area_size, inherit);
}

inline void
SlicePool::ForkCow(bool inherit)
{
    if (inherit == fork_cow)
        return;

    fork_cow = inherit;
    for (auto &area : areas)
        area.ForkCow(*this, fork_cow);
}

void
slice_pool_fork_cow(SlicePool &pool, bool inherit)
{
    pool.ForkCow(inherit);
}

size_t
slice_pool_get_slice_size(const SlicePool *pool)
{
    return pool->slice_size;
}

inline void
SlicePool::Compress()
{
    for (auto i = areas.begin(), end = areas.end(); i != end;) {
        if (i->IsEmpty()) {
            i = areas.erase_and_dispose(i, SliceArea::Disposer{*this});
        } else {
            i->Compress(*this);
            ++i;
        }
    }
}

void
slice_pool_compress(SlicePool *pool)
{
    pool->Compress();
}

gcc_pure
inline SliceArea *
SlicePool::FindNonFullArea()
{
    for (SliceArea &area : areas)
        if (!area.IsFull(*this))
            return &area;

    return nullptr;
}

inline void *
SliceArea::Alloc(SlicePool &pool)
{
    assert(!IsFull(pool));

    const unsigned i = free_head;
    SliceSlot *const slot = &slices[i];

    ++allocated_count;
    free_head = slot->next;
    slot->next = ALLOCATED;

    return GetSlice(pool, i);
}

inline SliceAllocation
SlicePool::Alloc()
{
    SliceArea *area = FindNonFullArea();
    if (area == nullptr) {
        area = SliceArea::New(*this);
        area->ForkCow(*this, fork_cow);
        areas.push_front(*area);
    }

    return { area, area->Alloc(*this), slice_size };
}

SliceAllocation
slice_alloc(SlicePool *pool)
{
    return pool->Alloc();
}

inline void
SliceArea::Free(SlicePool &pool, void *p)
{
    unsigned i = IndexOf(pool, p);
    assert(slices[i].IsAllocated());

    slices[i].next = free_head;
    free_head = i;

    assert(allocated_count > 0);
    --allocated_count;
}

void
slice_free(SlicePool *pool, SliceArea *area, void *p)
{
    area->Free(*pool, p);
}

inline AllocatorStats
SlicePool::GetStats() const
{
    AllocatorStats stats;
    stats.brutto_size = stats.netto_size = 0;

    for (const auto &area : areas) {
        stats.brutto_size += area_size;
        stats.netto_size += area.GetNettoSize(slice_size);
    }

    return stats;
}

AllocatorStats
slice_pool_get_stats(const SlicePool &pool)
{
    return pool.GetStats();
}
<|endoftext|>


<commit_before>//
// TcpSocket.cpp for TcpSocket in
//
// Made by mognetworkhrabi Alexandre
// Login   <alexmog@epitech.net>
//
// Started on  Thu Jun  5 20:09:34 2014 mognetworkhrabi Alexandre
// Last update Mon Mar  9 18:28:24 2015 Moghrabi Alexandre
//

#include "mognetwork/OS.hh"

#include <sys/types.h>
#ifndef OS_WINDOWS
#include <sys/socket.h>
#include <netdb.h>
#else
#include <winsock2.h>
#include <Ws2tcpip.h>
#endif // !OS_WINDOWS
#include <algorithm>
#include <cstring>
#include <iostream>
#include "mognetwork/TcpSocket.hh"
#include "mognetwork/OsSocket.hh"
#include "mognetwork/Packet.hh"

namespace
{
#ifdef OS_LINUX
  const int flags = MSG_NOSIGNAL;
#else
  const int flags = 0;
#endif
}

// ! Every packets sends a header (who contains the size of the packet) BEFORE its content.
namespace mognetwork
{
  bool deleteAll(TcpSocket::Data* elem) {delete elem; return true;}

  TcpSocket::TcpSocket() :
    Socket(Tcp), m_userData(NULL)
  {
    // Create the socket, and init it
    create();
  }

  TcpSocket::TcpSocket(SocketFD fd) :
    Socket(Tcp), m_userData(NULL)
  {
    create(fd);
  }

  Socket::Status TcpSocket::connect(const IpAddress& rAddress, unsigned short port)
  {
    // Create the sockaddr by the OS
    sockaddr_in address = OsSocket::createAddress(rAddress.getInt(), port);

    // connect the socket to the address
    return (::connect(getSocketFD(), reinterpret_cast<sockaddr*>(&address), sizeof(address))  == -1 ? OsSocket::getErrorStatus() : Ok);
  }

  void TcpSocket::disconnect()
  {
    close();
    m_pendingDatas.remove_if(deleteAll);
    m_pendingRDatas = ReadedDatas();
  }

  Socket::Status TcpSocket::receive(char* data, std::size_t size, std::size_t& received, int _flags)
  {
    received = 0;
    if (!data)
      {
	std::cerr << "Cannot receive datas if the buffer is null." << std::endl;
	return (Error);
      }
    int readed = recv(getSocketFD(), data, static_cast<int>(size), flags | _flags);
    if (readed > 0)
      {
	received = static_cast<std::size_t>(readed);
	return (Ok);
      }
    else if (readed == 0)
      return (Disconnected);
    else
      return (OsSocket::getErrorStatus());
  }

  Socket::Status TcpSocket::receiveAll(Data& data)
  {
    std::size_t readed = 0;
    char buffer[1024]; // Buffer to get the datas
    std::size_t numReaded = 0;

    ReadedDatas _data = ReadedDatas();
    data.clear();
    // Read the size of the pending packet
    while (readed < sizeof(std::size_t))
      {
	char* d = reinterpret_cast<char*>(&_data.totalSize) + numReaded;
	Socket::Status status = receive(d, sizeof(std::size_t) - _data.readed, readed, 0);
	numReaded += readed;
	if (status != Ok)
	  return (status);
      }
    // Size is set, let's read the content!
    if (numReaded >= sizeof(std::size_t))
      {
	while (_data.readed < _data.totalSize)
	  {
	    std::size_t toGet = std::min(static_cast<std::size_t>(_data.totalSize - _data.readed), sizeof(buffer));
	    Socket::Status status = receive(buffer, toGet, readed, 0);
	    if (status != Ok)
	      return (status);
	    _data.readed += readed;
	    if (readed > 0)
	      {
		data.resize(data.size() + readed);
		char* begin = &data[0] + data.size() - readed;
		std::memcpy(begin, buffer, readed);
	      }
	    if (_data.readed >= _data.totalSize)
	      return (Ok);
	  }
      }
    return (Error);
  }

  Socket::Status TcpSocket::asyncSend(const char* data, std::size_t size)
  {
    if (!data || (size == 0))
      {
	std::cerr << "Cannot send null data" << std::endl;
	return (Error);
      }
    Data* _data = new Data;
    _data->resize(size + sizeof(std::size_t));
    std::memcpy(&_data->front(), &size, sizeof(std::size_t));
    std::memcpy(&_data->front() + sizeof(std::size_t), data, size);
    m_mutex.lock();
    m_pendingDatas.push_back(_data);
    m_mutex.unlock();
    return (Ok);
  }

  bool TcpSocket::havingPendingDatas()
  {
    m_mutex.lock();
    bool status = m_pendingDatas.empty();
    m_mutex.unlock();
    return (status);
  }

  Socket::Status TcpSocket::sendPendingDatas()
  {
    if (!m_pendingDatas.empty())
      {
	int sended;
	DataList::iterator it = m_pendingDatas.begin();
	sended = ::send(getSocketFD(), &(*it)->front(), (*it)->size(), flags | MSG_DONTWAIT);
	if (sended < 0)
	  return (OsSocket::getErrorStatus());
	if (sended == 0)
	  return (Disconnected);
	if ((std::size_t)sended == (*it)->size())
	  {
	    delete *it;
	    m_pendingDatas.pop_front();
	    return (sendPendingDatas());
	  }
	Data temp;
	temp.resize((*it)->size());
	std::memcpy(&temp[0], &(*it)->front(), (*it)->size());
	std::size_t waiting = (*it)->size() - sended;
	(*it)->resize(waiting);
	std::memcpy(&(*it)->front(), &temp[0], waiting);
	return (Ok);
      }
    return (Nok);
  }

  // Normal send method, waits until the server has received everything.
  Socket::Status TcpSocket::send(const char* data, std::size_t size)
  {
    int sent;

    if (!data || (size == 0))
      {
	std::cerr << "Cannot send null data" << std::endl;
	return (Error);
      }
    Data dat;
    dat.resize(size + sizeof(std::size_t));
    std::memcpy(&dat[0], &size, sizeof(std::size_t));
    std::memcpy(&dat[0] + sizeof(std::size_t), data, size);
    sent = ::send(getSocketFD(), &dat[0], dat.size(), 0);
    if (sent < 0)
      return (OsSocket::getErrorStatus());
    if (sent == 0)
      return (Disconnected);
    return (Ok);
  }

  void TcpSocket::setUserData(void* userData)
  {
    m_mutex.lock();
    m_userData = userData;
    m_mutex.unlock();
  }

  void* TcpSocket::getUserData() const
  {
    return (m_userData);
  }

  Socket::Status TcpSocket::readPendingDatas()
  {
    std::size_t readed;
    char buffer[1024]; // Buffer to get the datas

    // Read the size of the pending packet
    if (m_pendingRDatas.readed < sizeof(std::size_t))
      {
	char* data = reinterpret_cast<char*>(&m_pendingRDatas.totalSize) + m_pendingRDatas.readed;
	Socket::Status status = receive(data, sizeof(std::size_t) - m_pendingRDatas.readed, readed, MSG_DONTWAIT);
	m_pendingRDatas.readed += readed;
	if (status != Ok)
	  return (status);
      }
    // Size is set, let's read the content!
    if (m_pendingRDatas.readed >= sizeof(std::size_t))
      {
	std::size_t toGet = std::min(static_cast<std::size_t>(m_pendingRDatas.totalSize - m_pendingRDatas.datas.size()), sizeof(buffer));

	Socket::Status status = receive(buffer, toGet, readed, MSG_DONTWAIT);
	if (status != Ok)
	  return (status);
	m_pendingRDatas.readed += readed;
	if (readed > 0)
	  {
	    m_pendingRDatas.datas.resize(m_pendingRDatas.datas.size() + readed);
	    char* begin = &m_pendingRDatas.datas[0] + m_pendingRDatas.datas.size() - readed;
	    std::memcpy(begin, buffer, readed);
	  }
	if (m_pendingRDatas.readed >= m_pendingRDatas.totalSize)
	  {
	    m_allDataReaded = new ReadedDatas();
	    m_allDataReaded->datas = m_pendingRDatas.datas;
	    m_allDataReaded->readed = m_pendingRDatas.readed;
	    m_allDataReaded->totalSize = m_pendingRDatas.totalSize;
	    m_pendingRDatas = ReadedDatas();
	    return (Ok);
	  }
      }
    return (Waiting);
  }

  TcpSocket::ReadedDatas* TcpSocket::getDatasReaded() const
  {
    return (m_allDataReaded);
  }

  void TcpSocket::setServer(TcpASIOServer* server)
  {
    m_mutex.lock();
    m_server = server;
    m_mutex.unlock();
  }

  TcpASIOServer* TcpSocket::getServer() const
  {
    return m_server;
  }

  Packet* TcpSocket::getPacketReaded()
  {
    return new Packet(m_allDataReaded);
  }

  TcpSocket::ReadedDatas::ReadedDatas() :
    readed(0),
    totalSize(0),
    datas()
  {}
} // namespace mognetwork
<commit_msg>Added mutex<commit_after>//
// TcpSocket.cpp for TcpSocket in
//
// Made by mognetworkhrabi Alexandre
// Login   <alexmog@epitech.net>
//
// Started on  Thu Jun  5 20:09:34 2014 mognetworkhrabi Alexandre
// Last update Mon Mar  9 18:50:19 2015 Moghrabi Alexandre
//

#include "mognetwork/OS.hh"

#include <sys/types.h>
#ifndef OS_WINDOWS
#include <sys/socket.h>
#include <netdb.h>
#else
#include <winsock2.h>
#include <Ws2tcpip.h>
#endif // !OS_WINDOWS
#include <algorithm>
#include <cstring>
#include <iostream>
#include "mognetwork/TcpSocket.hh"
#include "mognetwork/OsSocket.hh"
#include "mognetwork/Packet.hh"

namespace
{
#ifdef OS_LINUX
  const int flags = MSG_NOSIGNAL;
#else
  const int flags = 0;
#endif
}

// ! Every packets sends a header (who contains the size of the packet) BEFORE its content.
namespace mognetwork
{
  bool deleteAll(TcpSocket::Data* elem) {delete elem; return true;}

  TcpSocket::TcpSocket() :
    Socket(Tcp), m_userData(NULL)
  {
    // Create the socket, and init it
    create();
  }

  TcpSocket::TcpSocket(SocketFD fd) :
    Socket(Tcp), m_userData(NULL)
  {
    create(fd);
  }

  Socket::Status TcpSocket::connect(const IpAddress& rAddress, unsigned short port)
  {
    // Create the sockaddr by the OS
    sockaddr_in address = OsSocket::createAddress(rAddress.getInt(), port);

    // connect the socket to the address
    return (::connect(getSocketFD(), reinterpret_cast<sockaddr*>(&address), sizeof(address))  == -1 ? OsSocket::getErrorStatus() : Ok);
  }

  void TcpSocket::disconnect()
  {
    close();
    m_pendingDatas.remove_if(deleteAll);
    m_pendingRDatas = ReadedDatas();
  }

  Socket::Status TcpSocket::receive(char* data, std::size_t size, std::size_t& received, int _flags)
  {
    received = 0;
    if (!data)
      {
	std::cerr << "Cannot receive datas if the buffer is null." << std::endl;
	return (Error);
      }
    int readed = recv(getSocketFD(), data, static_cast<int>(size), flags | _flags);
    if (readed > 0)
      {
	received = static_cast<std::size_t>(readed);
	return (Ok);
      }
    else if (readed == 0)
      return (Disconnected);
    else
      return (OsSocket::getErrorStatus());
  }

  Socket::Status TcpSocket::receiveAll(Data& data)
  {
    std::size_t readed = 0;
    char buffer[1024]; // Buffer to get the datas
    std::size_t numReaded = 0;

    ReadedDatas _data = ReadedDatas();
    data.clear();
    // Read the size of the pending packet
    while (readed < sizeof(std::size_t))
      {
	char* d = reinterpret_cast<char*>(&_data.totalSize) + numReaded;
	Socket::Status status = receive(d, sizeof(std::size_t) - _data.readed, readed, 0);
	numReaded += readed;
	if (status != Ok)
	  return (status);
      }
    // Size is set, let's read the content!
    if (numReaded >= sizeof(std::size_t))
      {
	while (_data.readed < _data.totalSize)
	  {
	    std::size_t toGet = std::min(static_cast<std::size_t>(_data.totalSize - _data.readed), sizeof(buffer));
	    Socket::Status status = receive(buffer, toGet, readed, 0);
	    if (status != Ok)
	      return (status);
	    _data.readed += readed;
	    if (readed > 0)
	      {
		data.resize(data.size() + readed);
		char* begin = &data[0] + data.size() - readed;
		std::memcpy(begin, buffer, readed);
	      }
	    if (_data.readed >= _data.totalSize)
	      return (Ok);
	  }
      }
    return (Error);
  }

  Socket::Status TcpSocket::asyncSend(const char* data, std::size_t size)
  {
    if (!data || (size == 0))
      {
	std::cerr << "Cannot send null data" << std::endl;
	return (Error);
      }
    Data* _data = new Data;
    _data->resize(size + sizeof(std::size_t));
    std::memcpy(&_data->front(), &size, sizeof(std::size_t));
    std::memcpy(&_data->front() + sizeof(std::size_t), data, size);
    m_mutex.lock();
    m_pendingDatas.push_back(_data);
    m_mutex.unlock();
    return (Ok);
  }

  bool TcpSocket::havingPendingDatas()
  {
    m_mutex.lock();
    bool status = m_pendingDatas.empty();
    m_mutex.unlock();
    return (status);
  }

  Socket::Status TcpSocket::sendPendingDatas()
  {
    m_mutex.lock();
    if (!m_pendingDatas.empty())
      {
	int sended;
	DataList::iterator it = m_pendingDatas.begin();
	sended = ::send(getSocketFD(), &(*it)->front(), (*it)->size(), flags | MSG_DONTWAIT);
	if (sended < 0) {
	  m_mutex.unlock();
	  return (OsSocket::getErrorStatus());
	}
	if (sended == 0) {
	  m_mutex.unlock();
	  return (Disconnected);
	}
	if ((std::size_t)sended == (*it)->size())
	  {
	    delete *it;
	    m_pendingDatas.pop_front();
	    m_mutex.unlock();
	    return (sendPendingDatas());
	  }
	Data temp;
	temp.resize((*it)->size());
	std::memcpy(&temp[0], &(*it)->front(), (*it)->size());
	std::size_t waiting = (*it)->size() - sended;
	(*it)->resize(waiting);
	std::memcpy(&(*it)->front(), &temp[0], waiting);
	m_mutex.unlock();
	return (Ok);
      }
    m_mutex.unlock();
    return (Nok);
  }

  // Normal send method, waits until the server has received everything.
  Socket::Status TcpSocket::send(const char* data, std::size_t size)
  {
    int sent;

    if (!data || (size == 0))
      {
	std::cerr << "Cannot send null data" << std::endl;
	return (Error);
      }
    Data dat;
    dat.resize(size + sizeof(std::size_t));
    std::memcpy(&dat[0], &size, sizeof(std::size_t));
    std::memcpy(&dat[0] + sizeof(std::size_t), data, size);
    sent = ::send(getSocketFD(), &dat[0], dat.size(), 0);
    if (sent < 0)
      return (OsSocket::getErrorStatus());
    if (sent == 0)
      return (Disconnected);
    return (Ok);
  }

  void TcpSocket::setUserData(void* userData)
  {
    m_mutex.lock();
    m_userData = userData;
    m_mutex.unlock();
  }

  void* TcpSocket::getUserData() const
  {
    return (m_userData);
  }

  Socket::Status TcpSocket::readPendingDatas()
  {
    std::size_t readed;
    char buffer[1024]; // Buffer to get the datas

    // Read the size of the pending packet
    if (m_pendingRDatas.readed < sizeof(std::size_t))
      {
	char* data = reinterpret_cast<char*>(&m_pendingRDatas.totalSize) + m_pendingRDatas.readed;
	Socket::Status status = receive(data, sizeof(std::size_t) - m_pendingRDatas.readed, readed, MSG_DONTWAIT);
	m_pendingRDatas.readed += readed;
	if (status != Ok)
	  return (status);
      }
    // Size is set, let's read the content!
    if (m_pendingRDatas.readed >= sizeof(std::size_t))
      {
	std::size_t toGet = std::min(static_cast<std::size_t>(m_pendingRDatas.totalSize - m_pendingRDatas.datas.size()), sizeof(buffer));

	Socket::Status status = receive(buffer, toGet, readed, MSG_DONTWAIT);
	if (status != Ok)
	  return (status);
	m_pendingRDatas.readed += readed;
	if (readed > 0)
	  {
	    m_pendingRDatas.datas.resize(m_pendingRDatas.datas.size() + readed);
	    char* begin = &m_pendingRDatas.datas[0] + m_pendingRDatas.datas.size() - readed;
	    std::memcpy(begin, buffer, readed);
	  }
	if (m_pendingRDatas.readed >= m_pendingRDatas.totalSize)
	  {
	    m_allDataReaded = new ReadedDatas();
	    m_allDataReaded->datas = m_pendingRDatas.datas;
	    m_allDataReaded->readed = m_pendingRDatas.readed;
	    m_allDataReaded->totalSize = m_pendingRDatas.totalSize;
	    m_pendingRDatas = ReadedDatas();
	    return (Ok);
	  }
      }
    return (Waiting);
  }

  TcpSocket::ReadedDatas* TcpSocket::getDatasReaded() const
  {
    return (m_allDataReaded);
  }

  void TcpSocket::setServer(TcpASIOServer* server)
  {
    m_mutex.lock();
    m_server = server;
    m_mutex.unlock();
  }

  TcpASIOServer* TcpSocket::getServer() const
  {
    return m_server;
  }

  Packet* TcpSocket::getPacketReaded()
  {
    return new Packet(m_allDataReaded);
  }

  TcpSocket::ReadedDatas::ReadedDatas() :
    readed(0),
    totalSize(0),
    datas()
  {}
} // namespace mognetwork
<|endoftext|>