<commit_before>//
//  MCMailProvider.cpp
//  mailcore2
//
//  Created by Robert Widmann on 4/28/13.
//  Copyright (c) 2013 MailCore. All rights reserved.
//

#include "MCMailProvider.h"
#include "MCNetService.h"
#include "MCIterator.h"
#include "MCJSON.h"

#ifdef _MSC_VER
#include <unicode/uregex.h>
#include <unicode/utext.h>
#include <unicode/utypes.h>
#include <unicode/localpointer.h>
#include <unicode/parseerr.h>
#else
#include <regex.h>
#endif

using namespace mailcore;

void MailProvider::init()
{
    mIdentifier = NULL;
    mImapServices = new Array();
    mSmtpServices = new Array();
    mPopServices = new Array();
    mDomainMatch = new Array();
    mDomainExclude = new Array();
    mMxMatch = new Array();
    mMailboxPaths = NULL;
}

MailProvider::MailProvider()
{
    init();
}

MailProvider::MailProvider(MailProvider * other)
{
    init();
    MC_SAFE_REPLACE_COPY(String, mIdentifier, other->mIdentifier);
    MC_SAFE_REPLACE_COPY(Array, mImapServices, other->mImapServices);
    MC_SAFE_REPLACE_COPY(Array, mSmtpServices, other->mSmtpServices);
    MC_SAFE_REPLACE_COPY(Array, mPopServices, other->mPopServices);
    MC_SAFE_REPLACE_COPY(Array, mDomainMatch, other->mDomainMatch);
    MC_SAFE_REPLACE_COPY(Array, mDomainExclude, other->mDomainExclude);
    MC_SAFE_REPLACE_COPY(Array, mMxMatch, other->mMxMatch);
    MC_SAFE_REPLACE_COPY(HashMap, mMailboxPaths, other->mMailboxPaths);
}

MailProvider::~MailProvider()
{
    MC_SAFE_RELEASE(mImapServices);
    MC_SAFE_RELEASE(mSmtpServices);
    MC_SAFE_RELEASE(mPopServices);
    MC_SAFE_RELEASE(mMxMatch);
    MC_SAFE_RELEASE(mDomainMatch);
    MC_SAFE_RELEASE(mDomainExclude);
    MC_SAFE_RELEASE(mMailboxPaths);
    MC_SAFE_RELEASE(mIdentifier);
}

MailProvider * MailProvider::providerWithInfo(HashMap * info)
{
    MailProvider * provider = new MailProvider();
    provider->fillWithInfo(info);
    provider->autorelease();
    return provider;
}

void MailProvider::fillWithInfo(HashMap * info)
{
    Array * imapInfos;
    Array * smtpInfos;
    Array * popInfos;
    HashMap * serverInfo;

    MC_SAFE_RELEASE(mDomainMatch);
    if (info->objectForKey(MCSTR("domain-match")) != NULL) {
        mDomainMatch = (Array *) info->objectForKey(MCSTR("domain-match"))->retain();
    }
    MC_SAFE_RELEASE(mDomainExclude);
    if (info->objectForKey(MCSTR("domain-exclude")) != NULL) {
        mDomainExclude = (Array *) info->objectForKey(MCSTR("domain-exclude"))->retain();
    }
    MC_SAFE_RELEASE(mMailboxPaths);
    if (info->objectForKey(MCSTR("mailboxes")) != NULL) {
        mMailboxPaths = (HashMap *) info->objectForKey(MCSTR("mailboxes"))->retain();
    }
    MC_SAFE_RELEASE(mMxMatch);
    if (info->objectForKey(MCSTR("mx-match")) != NULL) {
        mMxMatch = (Array *) info->objectForKey(MCSTR("mx-match"))->retain();
    }

    serverInfo = (HashMap *) info->objectForKey(MCSTR("servers"));
    if (serverInfo == NULL) {
        MCLog("servers key missing from provider %s", MCUTF8DESC(info));
    }
    MCAssert(serverInfo != NULL);
    imapInfos = (Array *) serverInfo->objectForKey(MCSTR("imap"));
    smtpInfos = (Array *) serverInfo->objectForKey(MCSTR("smtp"));
    popInfos = (Array *) serverInfo->objectForKey(MCSTR("pop"));

    mImapServices->removeAllObjects();
    mc_foreacharray(HashMap, imapInfo, imapInfos) {
        NetService * service = NetService::serviceWithInfo(imapInfo);
        mImapServices->addObject(service);
    }

    mSmtpServices->removeAllObjects();
    mc_foreacharray(HashMap, smtpInfo, smtpInfos) {
        NetService * service = NetService::serviceWithInfo(smtpInfo);
        mSmtpServices->addObject(service);
    }

    mPopServices->removeAllObjects();
    mc_foreacharray(HashMap, popInfo, popInfos) {
        NetService * service = NetService::serviceWithInfo(popInfo);
        mPopServices->addObject(service);
    }
}

void MailProvider::setIdentifier(String * identifier)
{
    MC_SAFE_REPLACE_COPY(String, mIdentifier, identifier);
}

String * MailProvider::identifier()
{
    return mIdentifier;
}

Array * MailProvider::imapServices()
{
    return mImapServices;
}

Array * MailProvider::smtpServices()
{
    return mSmtpServices;
}

Array * MailProvider::popServices()
{
    return mPopServices;
}

bool MailProvider::matchEmail(String * email)
{
    Array * components;
    String * domain;

    components = email->componentsSeparatedByString(MCSTR("@"));
    if (components->count() < 2)
        return false;

    domain = (String *) components->lastObject();

    bool matchExcludeDomain = false;
    mc_foreacharray(String, exclude, mDomainExclude) {
        if (matchDomain(exclude, domain)){
            matchExcludeDomain = true;;
            break;
        }
    }
    if (matchExcludeDomain) {
        return false;
    }

    bool matchValidDomain = false;
    mc_foreacharray(String, match, mDomainMatch) {
        if (matchDomain(match, domain)){
            matchValidDomain = true;
            break;
        }
    }
    if (matchValidDomain) {
        return true;
    }

    return false;
}

bool MailProvider::matchMX(String * hostname)
{
    bool result = false;
    mc_foreacharray(String, match, mMxMatch) {
        if (matchDomain(match, hostname)){
            result = true;
            break;
        }
    }

    return result;
}

bool MailProvider::matchDomain(String * match, String * domain)
{
#ifdef _MSC_VER
    UParseError error;
    UErrorCode code = U_ZERO_ERROR;
    URegularExpression * r = uregex_open(match->unicodeCharacters(), match->length(), UREGEX_CASE_INSENSITIVE, &error, &code);
    if (code != U_ZERO_ERROR) {
        uregex_close(r);
        return false;
    }
    uregex_setText(r, domain->unicodeCharacters(), domain->length(), &code);
    if (code != U_ZERO_ERROR) {
        uregex_close(r);
        return false;
    }

	bool matched = uregex_matches(r, 0, &code);
    if (code != U_ZERO_ERROR) {
        uregex_close(r);
        return false;
    }

    uregex_close(r);

    return matched;
#else
    const char * cDomain;
    regex_t r;
    bool matched;

    cDomain = domain->UTF8Characters();
    match = String::stringWithUTF8Format("^%s$", match->UTF8Characters());
    matched = false;

    if (regcomp(&r, match->UTF8Characters(), REG_EXTENDED | REG_ICASE | REG_NOSUB) != 0){
        return matched;
    }

    if (regexec(&r, cDomain, 0, NULL, 0) == 0) {
        matched = true;
    }

    regfree(&r);

    return matched;
#endif
}

String * MailProvider::sentMailFolderPath()
{
    return (String *) mMailboxPaths->objectForKey(MCSTR("sentmail"));
}

String * MailProvider::starredFolderPath()
{
    return (String *) mMailboxPaths->objectForKey(MCSTR("starred"));
}

String * MailProvider::allMailFolderPath()
{
    return (String *) mMailboxPaths->objectForKey(MCSTR("allmail"));
}

String * MailProvider::trashFolderPath()
{
    return (String *) mMailboxPaths->objectForKey(MCSTR("trash"));
}

String * MailProvider::draftsFolderPath()
{
    return (String *) mMailboxPaths->objectForKey(MCSTR("drafts"));
}

String * MailProvider::spamFolderPath()
{
    return (String *) mMailboxPaths->objectForKey(MCSTR("spam"));
}

String * MailProvider::importantFolderPath()
{
    return (String *) mMailboxPaths->objectForKey(MCSTR("important"));
}

bool MailProvider::isMainFolder(String * folderPath, String * prefix)
{
    bool result = false;
    mc_foreachhashmapValue(String, path, mMailboxPaths) {
        String * fullPath;

        if (prefix != NULL) {
            fullPath = prefix->stringByAppendingString((String *) path);
        }
        else {
            fullPath = path;
        }

        if (fullPath->isEqual(folderPath)) {
            result = true;
            break;
        }
    }

    return result;
}

String * MailProvider::description()
{
    return String::stringWithUTF8Format("<%s:%p, %s>", className()->UTF8Characters(), this, MCUTF8(mIdentifier));
}

Object * MailProvider::copy()
{
    return new MailProvider(this);
}

<commit_msg>Do not segfault on MailProvider::*FolderPath in case of absence information about folders in providers.json (#1410)<commit_after>//
//  MCMailProvider.cpp
//  mailcore2
//
//  Created by Robert Widmann on 4/28/13.
//  Copyright (c) 2013 MailCore. All rights reserved.
//

#include "MCMailProvider.h"
#include "MCNetService.h"
#include "MCIterator.h"
#include "MCJSON.h"

#ifdef _MSC_VER
#include <unicode/uregex.h>
#include <unicode/utext.h>
#include <unicode/utypes.h>
#include <unicode/localpointer.h>
#include <unicode/parseerr.h>
#else
#include <regex.h>
#endif

using namespace mailcore;

void MailProvider::init()
{
    mIdentifier = NULL;
    mImapServices = new Array();
    mSmtpServices = new Array();
    mPopServices = new Array();
    mDomainMatch = new Array();
    mDomainExclude = new Array();
    mMxMatch = new Array();
    mMailboxPaths = NULL;
}

MailProvider::MailProvider()
{
    init();
}

MailProvider::MailProvider(MailProvider * other)
{
    init();
    MC_SAFE_REPLACE_COPY(String, mIdentifier, other->mIdentifier);
    MC_SAFE_REPLACE_COPY(Array, mImapServices, other->mImapServices);
    MC_SAFE_REPLACE_COPY(Array, mSmtpServices, other->mSmtpServices);
    MC_SAFE_REPLACE_COPY(Array, mPopServices, other->mPopServices);
    MC_SAFE_REPLACE_COPY(Array, mDomainMatch, other->mDomainMatch);
    MC_SAFE_REPLACE_COPY(Array, mDomainExclude, other->mDomainExclude);
    MC_SAFE_REPLACE_COPY(Array, mMxMatch, other->mMxMatch);
    MC_SAFE_REPLACE_COPY(HashMap, mMailboxPaths, other->mMailboxPaths);
}

MailProvider::~MailProvider()
{
    MC_SAFE_RELEASE(mImapServices);
    MC_SAFE_RELEASE(mSmtpServices);
    MC_SAFE_RELEASE(mPopServices);
    MC_SAFE_RELEASE(mMxMatch);
    MC_SAFE_RELEASE(mDomainMatch);
    MC_SAFE_RELEASE(mDomainExclude);
    MC_SAFE_RELEASE(mMailboxPaths);
    MC_SAFE_RELEASE(mIdentifier);
}

MailProvider * MailProvider::providerWithInfo(HashMap * info)
{
    MailProvider * provider = new MailProvider();
    provider->fillWithInfo(info);
    provider->autorelease();
    return provider;
}

void MailProvider::fillWithInfo(HashMap * info)
{
    Array * imapInfos;
    Array * smtpInfos;
    Array * popInfos;
    HashMap * serverInfo;

    MC_SAFE_RELEASE(mDomainMatch);
    if (info->objectForKey(MCSTR("domain-match")) != NULL) {
        mDomainMatch = (Array *) info->objectForKey(MCSTR("domain-match"))->retain();
    }
    MC_SAFE_RELEASE(mDomainExclude);
    if (info->objectForKey(MCSTR("domain-exclude")) != NULL) {
        mDomainExclude = (Array *) info->objectForKey(MCSTR("domain-exclude"))->retain();
    }
    MC_SAFE_RELEASE(mMailboxPaths);
    if (info->objectForKey(MCSTR("mailboxes")) != NULL) {
        mMailboxPaths = (HashMap *) info->objectForKey(MCSTR("mailboxes"))->retain();
    }
    MC_SAFE_RELEASE(mMxMatch);
    if (info->objectForKey(MCSTR("mx-match")) != NULL) {
        mMxMatch = (Array *) info->objectForKey(MCSTR("mx-match"))->retain();
    }

    serverInfo = (HashMap *) info->objectForKey(MCSTR("servers"));
    if (serverInfo == NULL) {
        MCLog("servers key missing from provider %s", MCUTF8DESC(info));
    }
    MCAssert(serverInfo != NULL);
    imapInfos = (Array *) serverInfo->objectForKey(MCSTR("imap"));
    smtpInfos = (Array *) serverInfo->objectForKey(MCSTR("smtp"));
    popInfos = (Array *) serverInfo->objectForKey(MCSTR("pop"));

    mImapServices->removeAllObjects();
    mc_foreacharray(HashMap, imapInfo, imapInfos) {
        NetService * service = NetService::serviceWithInfo(imapInfo);
        mImapServices->addObject(service);
    }

    mSmtpServices->removeAllObjects();
    mc_foreacharray(HashMap, smtpInfo, smtpInfos) {
        NetService * service = NetService::serviceWithInfo(smtpInfo);
        mSmtpServices->addObject(service);
    }

    mPopServices->removeAllObjects();
    mc_foreacharray(HashMap, popInfo, popInfos) {
        NetService * service = NetService::serviceWithInfo(popInfo);
        mPopServices->addObject(service);
    }
}

void MailProvider::setIdentifier(String * identifier)
{
    MC_SAFE_REPLACE_COPY(String, mIdentifier, identifier);
}

String * MailProvider::identifier()
{
    return mIdentifier;
}

Array * MailProvider::imapServices()
{
    return mImapServices;
}

Array * MailProvider::smtpServices()
{
    return mSmtpServices;
}

Array * MailProvider::popServices()
{
    return mPopServices;
}

bool MailProvider::matchEmail(String * email)
{
    Array * components;
    String * domain;

    components = email->componentsSeparatedByString(MCSTR("@"));
    if (components->count() < 2)
        return false;

    domain = (String *) components->lastObject();

    bool matchExcludeDomain = false;
    mc_foreacharray(String, exclude, mDomainExclude) {
        if (matchDomain(exclude, domain)){
            matchExcludeDomain = true;;
            break;
        }
    }
    if (matchExcludeDomain) {
        return false;
    }

    bool matchValidDomain = false;
    mc_foreacharray(String, match, mDomainMatch) {
        if (matchDomain(match, domain)){
            matchValidDomain = true;
            break;
        }
    }
    if (matchValidDomain) {
        return true;
    }

    return false;
}

bool MailProvider::matchMX(String * hostname)
{
    bool result = false;
    mc_foreacharray(String, match, mMxMatch) {
        if (matchDomain(match, hostname)){
            result = true;
            break;
        }
    }

    return result;
}

bool MailProvider::matchDomain(String * match, String * domain)
{
#ifdef _MSC_VER
    UParseError error;
    UErrorCode code = U_ZERO_ERROR;
    URegularExpression * r = uregex_open(match->unicodeCharacters(), match->length(), UREGEX_CASE_INSENSITIVE, &error, &code);
    if (code != U_ZERO_ERROR) {
        uregex_close(r);
        return false;
    }
    uregex_setText(r, domain->unicodeCharacters(), domain->length(), &code);
    if (code != U_ZERO_ERROR) {
        uregex_close(r);
        return false;
    }

	bool matched = uregex_matches(r, 0, &code);
    if (code != U_ZERO_ERROR) {
        uregex_close(r);
        return false;
    }

    uregex_close(r);

    return matched;
#else
    const char * cDomain;
    regex_t r;
    bool matched;

    cDomain = domain->UTF8Characters();
    match = String::stringWithUTF8Format("^%s$", match->UTF8Characters());
    matched = false;

    if (regcomp(&r, match->UTF8Characters(), REG_EXTENDED | REG_ICASE | REG_NOSUB) != 0){
        return matched;
    }

    if (regexec(&r, cDomain, 0, NULL, 0) == 0) {
        matched = true;
    }

    regfree(&r);

    return matched;
#endif
}

String * MailProvider::sentMailFolderPath()
{
    if (mMailboxPaths == NULL)
        return NULL;
    return (String *) mMailboxPaths->objectForKey(MCSTR("sentmail"));
}

String * MailProvider::starredFolderPath()
{
    if (mMailboxPaths == NULL)
        return NULL;
    return (String *) mMailboxPaths->objectForKey(MCSTR("starred"));
}

String * MailProvider::allMailFolderPath()
{
    if (mMailboxPaths == NULL)
        return NULL;
    return (String *) mMailboxPaths->objectForKey(MCSTR("allmail"));
}

String * MailProvider::trashFolderPath()
{
    if (mMailboxPaths == NULL)
        return NULL;
    return (String *) mMailboxPaths->objectForKey(MCSTR("trash"));
}

String * MailProvider::draftsFolderPath()
{
    if (mMailboxPaths == NULL)
        return NULL;
    return (String *) mMailboxPaths->objectForKey(MCSTR("drafts"));
}

String * MailProvider::spamFolderPath()
{
    if (mMailboxPaths == NULL)
        return NULL;
    return (String *) mMailboxPaths->objectForKey(MCSTR("spam"));
}

String * MailProvider::importantFolderPath()
{
    if (mMailboxPaths == NULL)
        return NULL;
    return (String *) mMailboxPaths->objectForKey(MCSTR("important"));
}

bool MailProvider::isMainFolder(String * folderPath, String * prefix)
{
    bool result = false;
    mc_foreachhashmapValue(String, path, mMailboxPaths) {
        String * fullPath;

        if (prefix != NULL) {
            fullPath = prefix->stringByAppendingString((String *) path);
        }
        else {
            fullPath = path;
        }

        if (fullPath->isEqual(folderPath)) {
            result = true;
            break;
        }
    }

    return result;
}

String * MailProvider::description()
{
    return String::stringWithUTF8Format("<%s:%p, %s>", className()->UTF8Characters(), this, MCUTF8(mIdentifier));
}

Object * MailProvider::copy()
{
    return new MailProvider(this);
}

<|endoftext|>


<commit_before>/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2015 Microsoft Corporation
 *
 * -=- Robust Distributed System Nucleus (rDSN) -=-
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

# if defined(__APPLE__) || defined(__FreeBSD__)

# include "io_looper.h"

# define IO_LOOPER_USER_NOTIFICATION_FD (-10)

namespace dsn
{
    namespace tools
    {
        io_looper::io_looper()
        {
            _io_queue = -1;
            _local_notification_fd = IO_LOOPER_USER_NOTIFICATION_FD;
            _filters.insert(EVFILT_READ);
            _filters.insert(EVFILT_WRITE);
            //_filters.insert(EVFILT_AIO);
            _filters.insert(EVFILT_READ_WRITE);
            _filters.insert(EVFILT_USER);
        }

        io_looper::~io_looper(void)
        {
            stop();
        }

        error_code io_looper::bind_io_handle(
            dsn_handle_t handle,
            io_loop_callback* cb,
            unsigned int events,
            ref_counter* ctx
            )
        {
            int fd;
            short filters[2];
            int nr_filters;
            struct kevent e;

            if (cb == nullptr)
            {
                derror("cb == nullptr");
                return ERR_INVALID_PARAMETERS;
            }

            fd = (int)(intptr_t)(handle);
            if (fd < 0)
            {
                if (fd != IO_LOOPER_USER_NOTIFICATION_FD)
                {
                    derror("The fd %d is less than 0.", fd);
                    return ERR_INVALID_PARAMETERS;
                }
            }

            if (_filters.find((short)events) == _filters.end())
            {
                derror("The filter %u is unsupported.", events);
                return ERR_INVALID_PARAMETERS;
            }

            if (fd > 0)
            {
                int flags = fcntl(fd, F_GETFL, 0);
                dassert (flags != -1, "fcntl failed, err = %s, fd = %d", strerror(errno), fd);

                if (!(flags & O_NONBLOCK))
                {
                    flags |= O_NONBLOCK;
                    flags = fcntl(fd, F_SETFL, flags);
                    dassert(flags != -1, "fcntl failed, err = %s, fd = %d", strerror(errno), fd);
                }
            }

            uintptr_t cb0 = (uintptr_t)cb;
            dassert((cb0 & 0x1) == 0, "the least one bit must be zero for the callback address");

            if (ctx)
            {
                cb0 |= 0x1; // has ref_counter

                utils::auto_lock<utils::ex_lock_nr_spin> l(_io_sessions_lock);
                auto pr = _io_sessions.insert(io_sessions::value_type(cb, ctx));
                dassert(pr.second, "the callback must not be registered before");
            }

            if ((short)events == EVFILT_READ_WRITE)
            {
                filters[0] = EVFILT_READ;
                filters[1] = EVFILT_WRITE;
                nr_filters = 2;
            }
            else
            {
                filters[0] = (short)events;
                nr_filters = 1;
            }

            for (int i = 0; i < nr_filters; i++)
            {
                EV_SET(&e, fd, filters[i], (EV_ADD | EV_ENABLE | EV_CLEAR), 0, 0, (void*)cb0);

                if (kevent(_io_queue, &e, 1, nullptr, 0, nullptr) == -1)
                {
                    derror("bind io handler to kqueue failed, err = %s, fd = %d", strerror(errno), fd);

                    if (ctx)
                    {
                        utils::auto_lock<utils::ex_lock_nr_spin> l(_io_sessions_lock);
                        auto r = _io_sessions.erase(cb);
                        dassert(r > 0, "the callback must be present");
                    }

                    for (int j = 0; j < i; j++)
                    {
                        EV_SET(&e, fd, filters[j], EV_DELETE, 0, 0, nullptr);
                        if (kevent(_io_queue, &e, 1, nullptr, 0, nullptr) == -1)
                        {
                            derror("Unregister kqueue failed, filter = %d, err = %s, fd = %d", filters[j], strerror(errno), fd);
                        }
                    }

                    return ERR_BIND_IOCP_FAILED;
                }
            }

            return ERR_OK;
        }

        error_code io_looper::unbind_io_handle(dsn_handle_t handle, io_loop_callback* cb)
        {
            int fd = (int)(intptr_t)handle;
            struct kevent e;

            if (cb)
            {
                utils::auto_lock<utils::ex_lock_nr_spin> l(_io_sessions_lock);
                auto r = _io_sessions.erase(cb);
                dassert(r > 0, "the callback must be present");
            }

            for (auto filter : _filters)
            {
                EV_SET(&e, fd, filter, EV_DELETE, 0, 0, nullptr);
                if (kevent(_io_queue, &e, 1, nullptr, 0, nullptr) == -1)
                {
                    if (errno != ENOENT)
                    {
                        derror("unbind io handler to kqueue failed, err = %s, fd = %d", strerror(errno), fd);
                        return ERR_BIND_IOCP_FAILED;
                    }
                }
            }

            return ERR_OK;
        }

        void io_looper::notify_local_execution()
        {
            struct kevent e;
            EV_SET(&e, _local_notification_fd, EVFILT_USER, 0, (NOTE_FFCOPY | NOTE_TRIGGER), 0, &_local_notification_callback);

            if (kevent(_io_queue, &e, 1, nullptr, 0, nullptr) == -1)
            {
                dassert(false, "post local notification via eventfd failed, err = %s", strerror(errno));
            }
            dinfo("notify local");
        }

        void io_looper::create_completion_queue()
        {
            _io_queue = ::kqueue();
            dassert(_io_queue != -1, "Fail to create kqueue");

            _local_notification_callback = [this](
                int native_error,
                uint32_t io_size,
                uintptr_t lolp_or_events
                )
            {
                this->handle_local_queues();
            };

            bind_io_handle((dsn_handle_t)(intptr_t)_local_notification_fd, &_local_notification_callback,
                EVFILT_USER);
        }

        void io_looper::close_completion_queue()
        {
            if (_io_queue != -1)
            {
		unbind_io_handle((dsn_handle_t)(intptr_t)_local_notification_fd, &_local_notification_callback);
                ::close(_io_queue);
                _io_queue = -1;
            }
        }

        void io_looper::start(service_node* node, int worker_count)
        {
            create_completion_queue();

            for (int i = 0; i < worker_count; i++)
            {
                std::thread* thr = new std::thread([this, node, i]()
                {
                    task::set_tls_dsn_context(node, nullptr, nullptr);

                    const char* name = node ? ::dsn::tools::get_service_node_name(node) : "glb";
                    char buffer[128];
                    sprintf(buffer, "%s.io-loop.%d", name, i);
                    task_worker::set_name(buffer);

                    this->loop_worker();
                });
                _workers.push_back(thr);
            }
        }

        void io_looper::stop()
        {
            close_completion_queue();

            if (_workers.size() > 0)
            {
                for (auto thr : _workers)
                {
                    thr->join();
                    delete thr;
                }
                _workers.clear();
            }
        }

        void io_looper::loop_worker()
        {
            struct timespec ts = { 0, 1000000 };

            while (true)
            {
                int nfds = kevent(_io_queue, nullptr, 0, _events, IO_LOOPER_MAX_EVENT_COUNT, &ts); // 1ms for timers
                if (nfds == 0) // timeout
                {
                    handle_local_queues();
                    continue;
                }
                else if (-1 == nfds)
                {
                    if (errno == EINTR)
                    {
                        continue;
                    }
                    else
                    {
                        derror("epoll_wait loop exits, err = %s", strerror(errno));
                        break;
                    }
                }

                for (int i = 0; i < nfds; i++)
                {
                    auto cb = (io_loop_callback*)_events[i].udata;
                    dinfo("kevent get events %x, cb = %p", _events[i].filter, cb);

                    uintptr_t cb0 = (uintptr_t)cb;

                    // for those with ref_counter register entries
                    if (cb0 & 0x1)
                    {
                        cb = (io_loop_callback*)(cb0 - 1);

                        ref_counter* robj;
                        {

                            utils::auto_lock<utils::ex_lock_nr_spin> l(_io_sessions_lock);
                            auto it = _io_sessions.find(cb);
                            if (it != _io_sessions.end())
                            {
                                robj = it->second;
                                // make sure callback is protected by ref counting
                                robj->add_ref();
                            }
                            else
                            {
                                robj = nullptr;
                            }
                        }

                        if (robj)
                        {
                            (*cb)(0, 0, (uintptr_t)&_events[i]);
                            robj->release_ref();
                        }
                        else
                        {
                            // context is gone (unregistered), let's skip
                            dwarn("kevent event %x skipped as session is gone, cb = %p",
                                _events[i].filter,
                                cb
                                );
                        }
                    }
                    else
                    {
                        (*cb)(0, 0, (uintptr_t)&_events[i]);
                    }
                }
            }
        }
    }
}

# endif
<commit_msg>More error checks.<commit_after>/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2015 Microsoft Corporation
 *
 * -=- Robust Distributed System Nucleus (rDSN) -=-
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

# if defined(__APPLE__) || defined(__FreeBSD__)

# include "io_looper.h"

# define IO_LOOPER_USER_NOTIFICATION_FD (-10)

namespace dsn
{
    namespace tools
    {
        io_looper::io_looper()
        {
            _io_queue = -1;
            _local_notification_fd = IO_LOOPER_USER_NOTIFICATION_FD;
            _filters.insert(EVFILT_READ);
            _filters.insert(EVFILT_WRITE);
            //_filters.insert(EVFILT_AIO);
            _filters.insert(EVFILT_READ_WRITE);
            _filters.insert(EVFILT_USER);
        }

        io_looper::~io_looper(void)
        {
            stop();
        }

        error_code io_looper::bind_io_handle(
            dsn_handle_t handle,
            io_loop_callback* cb,
            unsigned int events,
            ref_counter* ctx
            )
        {
            int fd;
            short filters[2];
            int nr_filters;
            struct kevent e;

            if (cb == nullptr)
            {
                derror("cb == nullptr");
                return ERR_INVALID_PARAMETERS;
            }

            fd = (int)(intptr_t)(handle);
            if (fd < 0)
            {
                if (fd != IO_LOOPER_USER_NOTIFICATION_FD)
                {
                    derror("The fd %d is less than 0.", fd);
                    return ERR_INVALID_PARAMETERS;
                }
            }

            if (_filters.find((short)events) == _filters.end())
            {
                derror("The filter %u is unsupported.", events);
                return ERR_INVALID_PARAMETERS;
            }

            if (fd > 0)
            {
                int flags = fcntl(fd, F_GETFL, 0);
                dassert (flags != -1, "fcntl failed, err = %s, fd = %d", strerror(errno), fd);

                if (!(flags & O_NONBLOCK))
                {
                    flags |= O_NONBLOCK;
                    flags = fcntl(fd, F_SETFL, flags);
                    dassert(flags != -1, "fcntl failed, err = %s, fd = %d", strerror(errno), fd);
                }
            }

            uintptr_t cb0 = (uintptr_t)cb;
            dassert((cb0 & 0x1) == 0, "the least one bit must be zero for the callback address");

            if (ctx)
            {
                cb0 |= 0x1; // has ref_counter

                utils::auto_lock<utils::ex_lock_nr_spin> l(_io_sessions_lock);
                auto pr = _io_sessions.insert(io_sessions::value_type(cb, ctx));
                dassert(pr.second, "the callback must not be registered before");
            }

            if ((short)events == EVFILT_READ_WRITE)
            {
                filters[0] = EVFILT_READ;
                filters[1] = EVFILT_WRITE;
                nr_filters = 2;
            }
            else
            {
                filters[0] = (short)events;
                nr_filters = 1;
            }

            for (int i = 0; i < nr_filters; i++)
            {
                EV_SET(&e, fd, filters[i], (EV_ADD | EV_ENABLE | EV_CLEAR), 0, 0, (void*)cb0);

                if (kevent(_io_queue, &e, 1, nullptr, 0, nullptr) == -1)
                {
                    derror("bind io handler to kqueue failed, err = %s, fd = %d", strerror(errno), fd);

                    if (ctx)
                    {
                        utils::auto_lock<utils::ex_lock_nr_spin> l(_io_sessions_lock);
                        auto r = _io_sessions.erase(cb);
                        dassert(r > 0, "the callback must be present");
                    }

                    for (int j = 0; j < i; j++)
                    {
                        EV_SET(&e, fd, filters[j], EV_DELETE, 0, 0, nullptr);
                        if (kevent(_io_queue, &e, 1, nullptr, 0, nullptr) == -1)
                        {
                            derror("Unregister kqueue failed, filter = %d, err = %s, fd = %d", filters[j], strerror(errno), fd);
                        }
                    }

                    return ERR_BIND_IOCP_FAILED;
                }
            }

            return ERR_OK;
        }

        error_code io_looper::unbind_io_handle(dsn_handle_t handle, io_loop_callback* cb)
        {
            int fd = (int)(intptr_t)handle;
            struct kevent e;
            int cnt = 0;

            for (auto filter : _filters)
            {
                EV_SET(&e, fd, filter, EV_DELETE, 0, 0, nullptr);
                if (kevent(_io_queue, &e, 1, nullptr, 0, nullptr) == -1)
                {
                    if (errno != ENOENT)
                    {
                        derror("unbind io handler to kqueue failed, err = %s, fd = %d", strerror(errno), fd);
                        return ERR_BIND_IOCP_FAILED;
                    }
                }
                else
                {
                    cnt++;
                }
            }

            if (cnt == 0)
            {
                derror("fd = %d has not been binded yet.", fd);
                return ERR_BIND_IOCP_FAILED;
            }

            if (cb)
            {
                utils::auto_lock<utils::ex_lock_nr_spin> l(_io_sessions_lock);
                auto r = _io_sessions.erase(cb);
                dassert(r > 0, "the callback must be present");
            }

            return ERR_OK;
        }

        void io_looper::notify_local_execution()
        {
            struct kevent e;
            EV_SET(&e, _local_notification_fd, EVFILT_USER, 0, (NOTE_FFCOPY | NOTE_TRIGGER), 0, &_local_notification_callback);

            if (kevent(_io_queue, &e, 1, nullptr, 0, nullptr) == -1)
            {
                dassert(false, "post local notification via eventfd failed, err = %s", strerror(errno));
            }
            dinfo("notify local");
        }

        void io_looper::create_completion_queue()
        {
            _io_queue = ::kqueue();
            dassert(_io_queue != -1, "Fail to create kqueue");

            _local_notification_callback = [this](
                int native_error,
                uint32_t io_size,
                uintptr_t lolp_or_events
                )
            {
                this->handle_local_queues();
            };

            bind_io_handle((dsn_handle_t)(intptr_t)_local_notification_fd, &_local_notification_callback,
                EVFILT_USER);
        }

        void io_looper::close_completion_queue()
        {
            if (_io_queue != -1)
            {
		unbind_io_handle((dsn_handle_t)(intptr_t)_local_notification_fd, &_local_notification_callback);
                ::close(_io_queue);
                _io_queue = -1;
            }
        }

        void io_looper::start(service_node* node, int worker_count)
        {
            create_completion_queue();

            for (int i = 0; i < worker_count; i++)
            {
                std::thread* thr = new std::thread([this, node, i]()
                {
                    task::set_tls_dsn_context(node, nullptr, nullptr);

                    const char* name = node ? ::dsn::tools::get_service_node_name(node) : "glb";
                    char buffer[128];
                    sprintf(buffer, "%s.io-loop.%d", name, i);
                    task_worker::set_name(buffer);

                    this->loop_worker();
                });
                _workers.push_back(thr);
            }
        }

        void io_looper::stop()
        {
            close_completion_queue();

            if (_workers.size() > 0)
            {
                for (auto thr : _workers)
                {
                    thr->join();
                    delete thr;
                }
                _workers.clear();
            }
        }

        void io_looper::loop_worker()
        {
            struct timespec ts = { 0, 1000000 };

            while (true)
            {
                int nfds = kevent(_io_queue, nullptr, 0, _events, IO_LOOPER_MAX_EVENT_COUNT, &ts); // 1ms for timers
                if (nfds == 0) // timeout
                {
                    handle_local_queues();
                    continue;
                }
                else if (-1 == nfds)
                {
                    if (errno == EINTR)
                    {
                        continue;
                    }
                    else
                    {
                        derror("epoll_wait loop exits, err = %s", strerror(errno));
                        break;
                    }
                }

                for (int i = 0; i < nfds; i++)
                {
                    auto cb = (io_loop_callback*)_events[i].udata;
                    dinfo("kevent get events %x, cb = %p", _events[i].filter, cb);

                    uintptr_t cb0 = (uintptr_t)cb;

                    // for those with ref_counter register entries
                    if (cb0 & 0x1)
                    {
                        cb = (io_loop_callback*)(cb0 - 1);

                        ref_counter* robj;
                        {

                            utils::auto_lock<utils::ex_lock_nr_spin> l(_io_sessions_lock);
                            auto it = _io_sessions.find(cb);
                            if (it != _io_sessions.end())
                            {
                                robj = it->second;
                                // make sure callback is protected by ref counting
                                robj->add_ref();
                            }
                            else
                            {
                                robj = nullptr;
                            }
                        }

                        if (robj)
                        {
                            (*cb)(0, 0, (uintptr_t)&_events[i]);
                            robj->release_ref();
                        }
                        else
                        {
                            // context is gone (unregistered), let's skip
                            dwarn("kevent event %x skipped as session is gone, cb = %p",
                                _events[i].filter,
                                cb
                                );
                        }
                    }
                    else
                    {
                        (*cb)(0, 0, (uintptr_t)&_events[i]);
                    }
                }
            }
        }
    }
}

# endif
<|endoftext|>


<commit_before>#include <iostream>
#include <vector>>
using namespace std;
<commit_msg>Delete progr_main.cpp<commit_after><|endoftext|>


<commit_before>#include "MultiPoint.hpp"
#include "BoundingBox.hpp"

namespace Slic3r {

MultiPoint::operator Points() const
{
    return this->points;
}

void MultiPoint::scale(double factor)
{
    for (Point &pt : points)
        pt *= factor;
}

void MultiPoint::scale(double factor_x, double factor_y)
{
    for (Point &pt : points)
    {
		pt(0) = coord_t(pt(0) * factor_x);
		pt(1) = coord_t(pt(1) * factor_y);
    }
}

void MultiPoint::translate(double x, double y)
{
    Vector v(x, y);
    for (Point &pt : points)
        pt += v;
}

void MultiPoint::translate(const Point &v)
{
    for (Point &pt : points)
        pt += v;
}

void MultiPoint::rotate(double cos_angle, double sin_angle)
{
    for (Point &pt : this->points) {
        double cur_x = double(pt(0));
        double cur_y = double(pt(1));
        pt(0) = coord_t(round(cos_angle * cur_x - sin_angle * cur_y));
        pt(1) = coord_t(round(cos_angle * cur_y + sin_angle * cur_x));
    }
}

void MultiPoint::rotate(double angle, const Point &center)
{
    double s = sin(angle);
    double c = cos(angle);
    for (Point &pt : points) {
        Vec2crd v(pt - center);
        pt(0) = (coord_t)round(double(center(0)) + c * v[0] - s * v[1]);
        pt(1) = (coord_t)round(double(center(1)) + c * v[1] + s * v[0]);
    }
}

void MultiPoint::reverse()
{
    std::reverse(this->points.begin(), this->points.end());
}

Point MultiPoint::first_point() const
{
    return this->points.front();
}

double
MultiPoint::length() const
{
    Lines lines = this->lines();
    double len = 0;
    for (Lines::iterator it = lines.begin(); it != lines.end(); ++it) {
        len += it->length();
    }
    return len;
}

int
MultiPoint::find_point(const Point &point) const
{
    for (const Point &pt : this->points)
        if (pt == point)
            return int(&pt - &this->points.front());
    return -1;  // not found
}

bool
MultiPoint::has_boundary_point(const Point &point) const
{
    double dist = (point.projection_onto(*this) - point).cast<double>().norm();
    return dist < SCALED_EPSILON;
}

BoundingBox
MultiPoint::bounding_box() const
{
    return BoundingBox(this->points);
}

bool
MultiPoint::has_duplicate_points() const
{
    for (size_t i = 1; i < points.size(); ++i)
        if (points[i-1] == points[i])
            return true;
    return false;
}

bool
MultiPoint::remove_duplicate_points()
{
    size_t j = 0;
    for (size_t i = 1; i < points.size(); ++i) {
        if (points[j] == points[i]) {
            // Just increase index i.
        } else {
            ++ j;
            if (j < i)
                points[j] = points[i];
        }
    }
    if (++ j < points.size()) {
        points.erase(points.begin() + j, points.end());
        return true;
    }
    return false;
}

bool
MultiPoint::intersection(const Line& line, Point* intersection) const
{
    Lines lines = this->lines();
    for (Lines::const_iterator it = lines.begin(); it != lines.end(); ++it) {
        if (it->intersection(line, intersection)) return true;
    }
    return false;
}

bool MultiPoint::first_intersection(const Line& line, Point* intersection) const
{
    bool   found = false;
    double dmin  = 0.;
    for (const Line &l : this->lines()) {
        Point ip;
        if (l.intersection(line, &ip)) {
            if (! found) {
                found = true;
                dmin = (line.a - ip).cast<double>().norm();
                *intersection = ip;
            } else {
                double d = (line.a - ip).cast<double>().norm();
                if (d < dmin) {
                    dmin = d;
                    *intersection = ip;
                }
            }
        }
    }
    return found;
}

std::vector<Point> MultiPoint::_douglas_peucker(const std::vector<Point>& pts, const double tolerance)
{
    std::vector<Point> result_pts;
	double tolerance_sq = tolerance * tolerance;
    if (! pts.empty()) {
        const Point  *anchor      = &pts.front();
        size_t        anchor_idx  = 0;
        const Point  *floater     = &pts.back();
        size_t        floater_idx = pts.size() - 1;
        result_pts.reserve(pts.size());
        result_pts.emplace_back(*anchor);
        if (anchor_idx != floater_idx) {
            assert(pts.size() > 1);
            std::vector<size_t> dpStack;
            dpStack.reserve(pts.size());
            dpStack.emplace_back(floater_idx);
            for (;;) {
                double max_dist_sq  = 0.0;
                size_t furthest_idx = anchor_idx;
                // find point furthest from line seg created by (anchor, floater) and note it
                for (size_t i = anchor_idx + 1; i < floater_idx; ++ i) {
                    double dist_sq = Line::distance_to_squared(pts[i], *anchor, *floater);
                    if (dist_sq > max_dist_sq) {
                        max_dist_sq  = dist_sq;
                        furthest_idx = i;
                    }
                }
                // remove point if less than tolerance
                if (max_dist_sq <= tolerance_sq) {
                    result_pts.emplace_back(*floater);
                    anchor_idx = floater_idx;
                    anchor     = floater;
                    assert(dpStack.back() == floater_idx);
                    dpStack.pop_back();
                    if (dpStack.empty())
                        break;
                    floater_idx = dpStack.back();
                } else {
                    floater_idx = furthest_idx;
                    dpStack.emplace_back(floater_idx);
                }
                floater = &pts[floater_idx];
            }
        }
    }
    return result_pts;
}

// Visivalingam simplification algorithm https://github.com/slic3r/Slic3r/pull/3825
// thanks to @fuchstraumer
/*
     struct - vis_node
     Used with the visivalignam simplification algorithm, which needs to be able to find a points
    successors and predecessors to operate succesfully. Since this struct is only used in one
    location, it could probably be dropped into a namespace to avoid polluting the slic3r namespace.
     Source: https://github.com/shortsleeves/visvalingam_simplify
     ^ Provided original algorithm implementation. I've only changed things a bit to "clean" them up
    (i.e be more like my personal style), and managed to do this without requiring a binheap implementation
 */
struct vis_node{
    vis_node(const size_t& idx, const size_t& _prev_idx, const size_t& _next_idx, const double& _area) : pt_idx(idx), prev_idx(_prev_idx), next_idx(_next_idx), area(_area) {}
    // Indices into a Points container, from which this object was constructed
    size_t pt_idx, prev_idx, next_idx;
    // Effective area of this "node"
    double area;
    // Overloaded operator used to sort the binheap
    // Greater area = "more important" node. So, this node is less than the
    // other node if it's area is less than the other node's area
    bool operator<(const vis_node& other) { return (this->area < other.area); }
};
Points MultiPoint::visivalingam(const Points& pts, const double& tolerance)
{
    // Make sure there's enough points in "pts" to bother with simplification.
    assert(pts.size() >= 2);
     // Result object
    Points results;
     // Lambda to calculate effective area spanned by a point and its immediate
    // successor + predecessor.
    auto effective_area = [pts](const size_t& curr_pt_idx, const size_t& prev_pt_idx, const size_t& next_pt_idx)->coordf_t {
        const Point& curr = pts[curr_pt_idx];
        const Point& prev = pts[prev_pt_idx];
        const Point& next = pts[next_pt_idx];
        // Use point objects as vector-distances
		const Vec2d curr_to_next = (next - curr).cast<double>();
		const Vec2d prev_to_next = (prev - curr).cast<double>();
        // Take cross product of these two vector distances
		return 0.50 * abs(cross2(curr_to_next, prev_to_next));
    };
     // We store the effective areas for each node
    std::vector<coordf_t> areas;
    areas.reserve(pts.size());
     // Construct the initial set of nodes. We will make a heap out of the "heap" vector using
    // std::make_heap. node_list is used later.
    std::vector<vis_node*> node_list;
    node_list.resize(pts.size());
    std::vector<vis_node*> heap;
    heap.reserve(pts.size());
    for (size_t i = 1; i < pts.size() - 1; ++ i) {
        // Get effective area of current node.
        coordf_t area = effective_area(i, i - 1, i + 1);
        // If area is greater than some arbitrarily small value, use it.
        node_list[i] = new vis_node(i, i - 1, i + 1, area);
        heap.push_back(node_list[i]);
    }
     // Call std::make_heap, which uses the < operator by default to make "heap" into
    // a binheap, sorted by the < operator we defind in the vis_node struct
    std::make_heap(heap.begin(), heap.end());
     // Start comparing areas. Set min_area to an outrageous value initially.
    double min_area = -std::numeric_limits<double>::max();
    while (!heap.empty()) {
         // Get current node.
        vis_node* curr = heap.front();
         // Pop node we just retrieved off the heap. pop_heap moves front element in vector
        // to the back, so we can call pop_back()
        std::pop_heap(heap.begin(), heap.end());
        heap.pop_back();
         // Sanity assert check
        assert(curr == node_list[curr->pt_idx]);
         // If the current pt'ss area is less than that of the previous pt's area
        // use the last pt's area instead. This ensures we don't elimate the current
        // point without eliminating the previous
        min_area = std::max(min_area, curr->area);
         // Update prev
        vis_node* prev = node_list[curr->prev_idx];
        if(prev != nullptr){
            prev->next_idx = curr->next_idx;
            prev->area = effective_area(prev->pt_idx, prev->prev_idx, prev->next_idx);
            // For some reason, std::make_heap() is the fastest way to resort the heap. Probably needs testing.
            std::make_heap(heap.begin(), heap.end());
        }
         // Update next
        vis_node* next = node_list[curr->next_idx];
        if(next != nullptr){
            next->prev_idx = curr->prev_idx;
            next->area = effective_area(next->pt_idx, next->prev_idx, next->next_idx);
            std::make_heap(heap.begin(), heap.end());
        }
         areas[curr->pt_idx] = min_area;
        node_list[curr->pt_idx] = nullptr;
        delete curr;
    }
    // Clear node list and shrink_to_fit() (to free actual memory). Not necessary. Could be removed.
    node_list.clear();
    node_list.shrink_to_fit();
    // This lambda is how we test whether or not to keep a point.
    auto use_point = [areas, tolerance](const size_t& idx)->bool {
        assert(idx < areas.size());
        // Return true at front/back of path/areas
        if(idx == 0 || idx == areas.size() - 1){
            return true;
        }
        // Return true if area at idx is greater than minimum area to consider "valid"
        else{
            return areas[idx] > tolerance;
        }
    };
    // Use previously defined lambda to build results.
    for (size_t i = 0; i < pts.size(); ++i) {
        if (use_point(i)){
            results.push_back(pts[i]);
        }
    }
     // Check that results has at least two points
    assert(results.size() >= 2);
     // Return simplified vector of points
    return results;
}

void MultiPoint3::translate(double x, double y)
{
    for (Vec3crd &p : points) {
        p(0) += coord_t(x);
        p(1) += coord_t(y);
    }
}

void MultiPoint3::translate(const Point& vector)
{
    this->translate(vector(0), vector(1));
}

double MultiPoint3::length() const
{
    double len = 0.0;
    for (const Line3& line : this->lines())
        len += line.length();
    return len;
}

BoundingBox3 MultiPoint3::bounding_box() const
{
    return BoundingBox3(points);
}

bool MultiPoint3::remove_duplicate_points()
{
    size_t j = 0;
    for (size_t i = 1; i < points.size(); ++i) {
        if (points[j] == points[i]) {
            // Just increase index i.
        } else {
            ++ j;
            if (j < i)
                points[j] = points[i];
        }
    }

    if (++j < points.size())
    {
        points.erase(points.begin() + j, points.end());
        return true;
    }

    return false;
}

BoundingBox get_extents(const MultiPoint &mp)
{
    return BoundingBox(mp.points);
}

BoundingBox get_extents_rotated(const Points &points, double angle)
{
    BoundingBox bbox;
    if (! points.empty()) {
        double s = sin(angle);
        double c = cos(angle);
        Points::const_iterator it = points.begin();
        double cur_x = (double)(*it)(0);
        double cur_y = (double)(*it)(1);
        bbox.min(0) = bbox.max(0) = (coord_t)round(c * cur_x - s * cur_y);
        bbox.min(1) = bbox.max(1) = (coord_t)round(c * cur_y + s * cur_x);
        for (++it; it != points.end(); ++it) {
            double cur_x = (double)(*it)(0);
            double cur_y = (double)(*it)(1);
            coord_t x = (coord_t)round(c * cur_x - s * cur_y);
            coord_t y = (coord_t)round(c * cur_y + s * cur_x);
            bbox.min(0) = std::min(x, bbox.min(0));
            bbox.min(1) = std::min(y, bbox.min(1));
            bbox.max(0) = std::max(x, bbox.max(0));
            bbox.max(1) = std::max(y, bbox.max(1));
        }
        bbox.defined = true;
    }
    return bbox;
}

BoundingBox get_extents_rotated(const MultiPoint &mp, double angle)
{
    return get_extents_rotated(mp.points, angle);
}

}
<commit_msg>Added debugging code for the Douglas-Peucker contour simplification code.<commit_after>#include "MultiPoint.hpp"
#include "BoundingBox.hpp"

namespace Slic3r {

MultiPoint::operator Points() const
{
    return this->points;
}

void MultiPoint::scale(double factor)
{
    for (Point &pt : points)
        pt *= factor;
}

void MultiPoint::scale(double factor_x, double factor_y)
{
    for (Point &pt : points)
    {
		pt(0) = coord_t(pt(0) * factor_x);
		pt(1) = coord_t(pt(1) * factor_y);
    }
}

void MultiPoint::translate(double x, double y)
{
    Vector v(x, y);
    for (Point &pt : points)
        pt += v;
}

void MultiPoint::translate(const Point &v)
{
    for (Point &pt : points)
        pt += v;
}

void MultiPoint::rotate(double cos_angle, double sin_angle)
{
    for (Point &pt : this->points) {
        double cur_x = double(pt(0));
        double cur_y = double(pt(1));
        pt(0) = coord_t(round(cos_angle * cur_x - sin_angle * cur_y));
        pt(1) = coord_t(round(cos_angle * cur_y + sin_angle * cur_x));
    }
}

void MultiPoint::rotate(double angle, const Point &center)
{
    double s = sin(angle);
    double c = cos(angle);
    for (Point &pt : points) {
        Vec2crd v(pt - center);
        pt(0) = (coord_t)round(double(center(0)) + c * v[0] - s * v[1]);
        pt(1) = (coord_t)round(double(center(1)) + c * v[1] + s * v[0]);
    }
}

void MultiPoint::reverse()
{
    std::reverse(this->points.begin(), this->points.end());
}

Point MultiPoint::first_point() const
{
    return this->points.front();
}

double
MultiPoint::length() const
{
    Lines lines = this->lines();
    double len = 0;
    for (Lines::iterator it = lines.begin(); it != lines.end(); ++it) {
        len += it->length();
    }
    return len;
}

int
MultiPoint::find_point(const Point &point) const
{
    for (const Point &pt : this->points)
        if (pt == point)
            return int(&pt - &this->points.front());
    return -1;  // not found
}

bool
MultiPoint::has_boundary_point(const Point &point) const
{
    double dist = (point.projection_onto(*this) - point).cast<double>().norm();
    return dist < SCALED_EPSILON;
}

BoundingBox
MultiPoint::bounding_box() const
{
    return BoundingBox(this->points);
}

bool
MultiPoint::has_duplicate_points() const
{
    for (size_t i = 1; i < points.size(); ++i)
        if (points[i-1] == points[i])
            return true;
    return false;
}

bool
MultiPoint::remove_duplicate_points()
{
    size_t j = 0;
    for (size_t i = 1; i < points.size(); ++i) {
        if (points[j] == points[i]) {
            // Just increase index i.
        } else {
            ++ j;
            if (j < i)
                points[j] = points[i];
        }
    }
    if (++ j < points.size()) {
        points.erase(points.begin() + j, points.end());
        return true;
    }
    return false;
}

bool
MultiPoint::intersection(const Line& line, Point* intersection) const
{
    Lines lines = this->lines();
    for (Lines::const_iterator it = lines.begin(); it != lines.end(); ++it) {
        if (it->intersection(line, intersection)) return true;
    }
    return false;
}

bool MultiPoint::first_intersection(const Line& line, Point* intersection) const
{
    bool   found = false;
    double dmin  = 0.;
    for (const Line &l : this->lines()) {
        Point ip;
        if (l.intersection(line, &ip)) {
            if (! found) {
                found = true;
                dmin = (line.a - ip).cast<double>().norm();
                *intersection = ip;
            } else {
                double d = (line.a - ip).cast<double>().norm();
                if (d < dmin) {
                    dmin = d;
                    *intersection = ip;
                }
            }
        }
    }
    return found;
}

std::vector<Point> MultiPoint::_douglas_peucker(const std::vector<Point>& pts, const double tolerance)
{
    std::vector<Point> result_pts;
	double tolerance_sq = tolerance * tolerance;
    if (! pts.empty()) {
        const Point  *anchor      = &pts.front();
        size_t        anchor_idx  = 0;
        const Point  *floater     = &pts.back();
        size_t        floater_idx = pts.size() - 1;
        result_pts.reserve(pts.size());
        result_pts.emplace_back(*anchor);
        if (anchor_idx != floater_idx) {
            assert(pts.size() > 1);
            std::vector<size_t> dpStack;
            dpStack.reserve(pts.size());
            dpStack.emplace_back(floater_idx);
            for (;;) {
                double max_dist_sq  = 0.0;
                size_t furthest_idx = anchor_idx;
                // find point furthest from line seg created by (anchor, floater) and note it
                for (size_t i = anchor_idx + 1; i < floater_idx; ++ i) {
                    double dist_sq = Line::distance_to_squared(pts[i], *anchor, *floater);
                    if (dist_sq > max_dist_sq) {
                        max_dist_sq  = dist_sq;
                        furthest_idx = i;
                    }
                }
                // remove point if less than tolerance
                if (max_dist_sq <= tolerance_sq) {
                    result_pts.emplace_back(*floater);
                    anchor_idx = floater_idx;
                    anchor     = floater;
                    assert(dpStack.back() == floater_idx);
                    dpStack.pop_back();
                    if (dpStack.empty())
                        break;
                    floater_idx = dpStack.back();
                } else {
                    floater_idx = furthest_idx;
                    dpStack.emplace_back(floater_idx);
                }
                floater = &pts[floater_idx];
            }
        }
        assert(result_pts.front() == pts.front());
        assert(result_pts.back()  == pts.back());

#if 0
        {
            static int iRun = 0;
			BoundingBox bbox(pts);
			BoundingBox bbox2(result_pts);
			bbox.merge(bbox2);
            SVG svg(debug_out_path("douglas_peucker_%d.svg", iRun ++).c_str(), bbox);
            if (pts.front() == pts.back())
                svg.draw(Polygon(pts), "black");
            else
                svg.draw(Polyline(pts), "black");
            if (result_pts.front() == result_pts.back())
                svg.draw(Polygon(result_pts), "green", scale_(0.1));
            else
                svg.draw(Polyline(result_pts), "green", scale_(0.1));
        }
#endif
    }
    return result_pts;
}

// Visivalingam simplification algorithm https://github.com/slic3r/Slic3r/pull/3825
// thanks to @fuchstraumer
/*
     struct - vis_node
     Used with the visivalignam simplification algorithm, which needs to be able to find a points
    successors and predecessors to operate succesfully. Since this struct is only used in one
    location, it could probably be dropped into a namespace to avoid polluting the slic3r namespace.
     Source: https://github.com/shortsleeves/visvalingam_simplify
     ^ Provided original algorithm implementation. I've only changed things a bit to "clean" them up
    (i.e be more like my personal style), and managed to do this without requiring a binheap implementation
 */
struct vis_node{
    vis_node(const size_t& idx, const size_t& _prev_idx, const size_t& _next_idx, const double& _area) : pt_idx(idx), prev_idx(_prev_idx), next_idx(_next_idx), area(_area) {}
    // Indices into a Points container, from which this object was constructed
    size_t pt_idx, prev_idx, next_idx;
    // Effective area of this "node"
    double area;
    // Overloaded operator used to sort the binheap
    // Greater area = "more important" node. So, this node is less than the
    // other node if it's area is less than the other node's area
    bool operator<(const vis_node& other) { return (this->area < other.area); }
};
Points MultiPoint::visivalingam(const Points& pts, const double& tolerance)
{
    // Make sure there's enough points in "pts" to bother with simplification.
    assert(pts.size() >= 2);
     // Result object
    Points results;
     // Lambda to calculate effective area spanned by a point and its immediate
    // successor + predecessor.
    auto effective_area = [pts](const size_t& curr_pt_idx, const size_t& prev_pt_idx, const size_t& next_pt_idx)->coordf_t {
        const Point& curr = pts[curr_pt_idx];
        const Point& prev = pts[prev_pt_idx];
        const Point& next = pts[next_pt_idx];
        // Use point objects as vector-distances
		const Vec2d curr_to_next = (next - curr).cast<double>();
		const Vec2d prev_to_next = (prev - curr).cast<double>();
        // Take cross product of these two vector distances
		return 0.50 * abs(cross2(curr_to_next, prev_to_next));
    };
     // We store the effective areas for each node
    std::vector<coordf_t> areas;
    areas.reserve(pts.size());
     // Construct the initial set of nodes. We will make a heap out of the "heap" vector using
    // std::make_heap. node_list is used later.
    std::vector<vis_node*> node_list;
    node_list.resize(pts.size());
    std::vector<vis_node*> heap;
    heap.reserve(pts.size());
    for (size_t i = 1; i < pts.size() - 1; ++ i) {
        // Get effective area of current node.
        coordf_t area = effective_area(i, i - 1, i + 1);
        // If area is greater than some arbitrarily small value, use it.
        node_list[i] = new vis_node(i, i - 1, i + 1, area);
        heap.push_back(node_list[i]);
    }
     // Call std::make_heap, which uses the < operator by default to make "heap" into
    // a binheap, sorted by the < operator we defind in the vis_node struct
    std::make_heap(heap.begin(), heap.end());
     // Start comparing areas. Set min_area to an outrageous value initially.
    double min_area = -std::numeric_limits<double>::max();
    while (!heap.empty()) {
         // Get current node.
        vis_node* curr = heap.front();
         // Pop node we just retrieved off the heap. pop_heap moves front element in vector
        // to the back, so we can call pop_back()
        std::pop_heap(heap.begin(), heap.end());
        heap.pop_back();
         // Sanity assert check
        assert(curr == node_list[curr->pt_idx]);
         // If the current pt'ss area is less than that of the previous pt's area
        // use the last pt's area instead. This ensures we don't elimate the current
        // point without eliminating the previous
        min_area = std::max(min_area, curr->area);
         // Update prev
        vis_node* prev = node_list[curr->prev_idx];
        if(prev != nullptr){
            prev->next_idx = curr->next_idx;
            prev->area = effective_area(prev->pt_idx, prev->prev_idx, prev->next_idx);
            // For some reason, std::make_heap() is the fastest way to resort the heap. Probably needs testing.
            std::make_heap(heap.begin(), heap.end());
        }
         // Update next
        vis_node* next = node_list[curr->next_idx];
        if(next != nullptr){
            next->prev_idx = curr->prev_idx;
            next->area = effective_area(next->pt_idx, next->prev_idx, next->next_idx);
            std::make_heap(heap.begin(), heap.end());
        }
         areas[curr->pt_idx] = min_area;
        node_list[curr->pt_idx] = nullptr;
        delete curr;
    }
    // Clear node list and shrink_to_fit() (to free actual memory). Not necessary. Could be removed.
    node_list.clear();
    node_list.shrink_to_fit();
    // This lambda is how we test whether or not to keep a point.
    auto use_point = [areas, tolerance](const size_t& idx)->bool {
        assert(idx < areas.size());
        // Return true at front/back of path/areas
        if(idx == 0 || idx == areas.size() - 1){
            return true;
        }
        // Return true if area at idx is greater than minimum area to consider "valid"
        else{
            return areas[idx] > tolerance;
        }
    };
    // Use previously defined lambda to build results.
    for (size_t i = 0; i < pts.size(); ++i) {
        if (use_point(i)){
            results.push_back(pts[i]);
        }
    }
     // Check that results has at least two points
    assert(results.size() >= 2);
     // Return simplified vector of points
    return results;
}

void MultiPoint3::translate(double x, double y)
{
    for (Vec3crd &p : points) {
        p(0) += coord_t(x);
        p(1) += coord_t(y);
    }
}

void MultiPoint3::translate(const Point& vector)
{
    this->translate(vector(0), vector(1));
}

double MultiPoint3::length() const
{
    double len = 0.0;
    for (const Line3& line : this->lines())
        len += line.length();
    return len;
}

BoundingBox3 MultiPoint3::bounding_box() const
{
    return BoundingBox3(points);
}

bool MultiPoint3::remove_duplicate_points()
{
    size_t j = 0;
    for (size_t i = 1; i < points.size(); ++i) {
        if (points[j] == points[i]) {
            // Just increase index i.
        } else {
            ++ j;
            if (j < i)
                points[j] = points[i];
        }
    }

    if (++j < points.size())
    {
        points.erase(points.begin() + j, points.end());
        return true;
    }

    return false;
}

BoundingBox get_extents(const MultiPoint &mp)
{
    return BoundingBox(mp.points);
}

BoundingBox get_extents_rotated(const Points &points, double angle)
{
    BoundingBox bbox;
    if (! points.empty()) {
        double s = sin(angle);
        double c = cos(angle);
        Points::const_iterator it = points.begin();
        double cur_x = (double)(*it)(0);
        double cur_y = (double)(*it)(1);
        bbox.min(0) = bbox.max(0) = (coord_t)round(c * cur_x - s * cur_y);
        bbox.min(1) = bbox.max(1) = (coord_t)round(c * cur_y + s * cur_x);
        for (++it; it != points.end(); ++it) {
            double cur_x = (double)(*it)(0);
            double cur_y = (double)(*it)(1);
            coord_t x = (coord_t)round(c * cur_x - s * cur_y);
            coord_t y = (coord_t)round(c * cur_y + s * cur_x);
            bbox.min(0) = std::min(x, bbox.min(0));
            bbox.min(1) = std::min(y, bbox.min(1));
            bbox.max(0) = std::max(x, bbox.max(0));
            bbox.max(1) = std::max(y, bbox.max(1));
        }
        bbox.defined = true;
    }
    return bbox;
}

BoundingBox get_extents_rotated(const MultiPoint &mp, double angle)
{
    return get_extents_rotated(mp.points, angle);
}

}
<|endoftext|>


<commit_before>#include "stdafx.h"
#include "luanode.h"
#include "luanode_net.h"
#include "luanode_net_acceptor.h"
#include "blogger.h"

#include <boost/asio/placeholders.hpp>
#include <boost/make_shared.hpp>

#include <boost/bind.hpp>

using namespace LuaNode::Net;

static unsigned long s_nextAcceptorId = 0;
static unsigned long s_acceptorCount = 0;

//////////////////////////////////////////////////////////////////////////
///
void LuaNode::Net::Acceptor::RegisterFunctions(lua_State* L) {}

const char* Acceptor::className = "Acceptor";
const Acceptor::RegType Acceptor::methods[] = {
	{"open", &Acceptor::Open},
	{"close", &Acceptor::Close},
	{"setoption", &Acceptor::SetOption},
	{"getsockname", &Acceptor::GetLocalAddress},
	{"bind", &Acceptor::Bind},
	{"listen", &Acceptor::Listen},
	{"accept", &Acceptor::Accept},
	{0}
};

const Acceptor::RegType Acceptor::setters[] = {
	{0}
};

const Acceptor::RegType Acceptor::getters[] = {
	{0}
};

Acceptor::Acceptor(lua_State* L) :
	m_L( LuaNode::GetLuaVM() ),
	m_acceptorId(++s_nextAcceptorId),
	m_acceptor( GetIoService() )
{
	s_acceptorCount++;
	LogDebug("Constructing Acceptor (%p) (id:%u). Current acceptor count = %lu", this, m_acceptorId, s_acceptorCount);
}

Acceptor::~Acceptor(void)
{
	s_acceptorCount--;
	LogDebug("Destructing Acceptor (%p) (id:%u). Current acceptor count = %lu", this, m_acceptorId, s_acceptorCount);

	// Close the acceptor if it was still open
	if(m_acceptor.is_open()) {
		boost::system::error_code ec;
		m_acceptor.close(ec);
		if(ec) {
			LogError("Error closing acceptor (%p) (id:%u) - %s", this, m_acceptorId, ec.message().c_str());
		}
	}
}

//////////////////////////////////////////////////////////////////////////
///
/*static*/ int Acceptor::tostring_T(lua_State* L) {
	userdataType* ud = static_cast<userdataType*>(lua_touserdata(L, 1));
	Acceptor* obj = ud->pT;
	lua_pushfstring(L, "%s (%p) (id:%d)", className, obj, obj->m_acceptorId);
	return 1;
}

//////////////////////////////////////////////////////////////////////////
/// Open the acceptor using the specified protocol
int Acceptor::Open(lua_State* L) {
	const char* kind = luaL_checkstring(L, 2);
	LogDebug("Acceptor::Open (%p) (id:%u) - %s", this, m_acceptorId, kind);

	boost::system::error_code ec;
	if(strcmp(kind, "tcp4") == 0) {
		m_acceptor.open( boost::asio::ip::tcp::v4(), ec );
	}
	else if(strcmp(kind, "tcp6") == 0) {
		m_acceptor.open( boost::asio::ip::tcp::v6(), ec );
	}
	else {
		lua_pushnil(L);
		lua_pushfstring(L, "unknown protocol %s", kind);
#if defined(WSAEPROTONOSUPPORT)
		lua_pushinteger(L, WSAEPROTONOSUPPORT);
#else
		lua_pushinteger(L, EPROTONOSUPPORT);
#endif
		return 3;
	}
	return BoostErrorCodeToLua(L, ec);
}

//////////////////////////////////////////////////////////////////////////
///
int Acceptor::Close(lua_State* L) {
	LogDebug("Acceptor::Close (%p) (id:%u)", this, m_acceptorId);
	boost::system::error_code ec;
	m_acceptor.close(ec);
	return BoostErrorCodeToLua(L, ec);
}

//////////////////////////////////////////////////////////////////////////
///
int Acceptor::SetOption(lua_State* L) {
	const char* option = luaL_checkstring(L, 2);
	LogDebug("Acceptor::SetOption (%p) (id:%u) - %s", this, m_acceptorId, option);

	boost::system::error_code ec;

	if(strcmp(option, "reuseaddr") == 0) {
		bool value = lua_toboolean(L, 3) != 0;
		m_acceptor.set_option( boost::asio::socket_base::reuse_address(value), ec );
	}
	else {
		ec = boost::asio::error::invalid_argument;
	}
	return BoostErrorCodeToLua(L, ec);
}

//////////////////////////////////////////////////////////////////////////
///
int Acceptor::GetLocalAddress(lua_State* L) {
	boost::system::error_code ec;
	const boost::asio::ip::tcp::endpoint& endpoint = m_acceptor.local_endpoint(ec);

	if(!ec) {
		lua_pushstring(L, endpoint.address().to_string().c_str());
		lua_pushinteger(L, endpoint.port());
		return 2;
	}
	return luaL_error(L, "Acceptor::GetLocalAddress - %s:%d", ec.message().c_str(), ec.value());
}

//////////////////////////////////////////////////////////////////////////
///
int Acceptor::Bind(lua_State* L) {
	const char* ip = luaL_checkstring(L, 2);
	unsigned short port = luaL_checkinteger(L, 3);

	LogDebug("Acceptor::Bind (%p) (id:%u) - (%s,%hu)", this, m_acceptorId, ip, port);

	boost::system::error_code ec;
	boost::asio::ip::address address = boost::asio::ip::address::from_string(ip, ec);
	if(ec) {
		return BoostErrorCodeToLua(L, ec);
	}

	boost::asio::ip::tcp::endpoint endpoint( address, port );
	m_acceptor.bind(endpoint, ec);
	return BoostErrorCodeToLua(L, ec);
}

//////////////////////////////////////////////////////////////////////////
///
int Acceptor::Listen(lua_State* L) {
	int backlog = luaL_optinteger(L, 2, boost::asio::socket_base::max_connections);
	LogDebug("Acceptor::Listen (%p) (id:%u) - backlog = %d", this, m_acceptorId, backlog);

	boost::system::error_code ec;
	m_acceptor.listen(backlog, ec);
	return BoostErrorCodeToLua(L, ec);
}

//////////////////////////////////////////////////////////////////////////
///
int Acceptor::Accept(lua_State* L) {
	LogDebug("Acceptor::Accept (%p) (id:%u)", this, m_acceptorId);

	boost::shared_ptr<boost::asio::ip::tcp::socket> socket = boost::make_shared<boost::asio::ip::tcp::socket>( boost::ref(GetIoService()) );

	// store a reference in the registry
	lua_pushvalue(L, 1);
	int reference = luaL_ref(L, LUA_REGISTRYINDEX);

	m_acceptor.async_accept(
		*socket,
		boost::bind(&Acceptor::HandleAccept, this, reference, socket, boost::asio::placeholders::error)
	);
	return 0;
}

//////////////////////////////////////////////////////////////////////////
///
void Acceptor::HandleAccept(int reference, boost::shared_ptr<boost::asio::ip::tcp::socket> socket, const boost::system::error_code& error) {
	LogDebug("Acceptor::HandleAccept (%p) (id:%u) (new socket %p)", this, m_acceptorId, socket.get());
	lua_State* L = LuaNode::GetLuaVM();
	lua_rawgeti(L, LUA_REGISTRYINDEX, reference);
	luaL_unref(L, LUA_REGISTRYINDEX, reference);

	lua_getfield(L, 1, "callback");
	assert(lua_type(L, -1) == LUA_TFUNCTION); //An acceptor must have a callback"

	if(!error) {
		boost::asio::ip::address address = socket->remote_endpoint().address();

		lua_pushnil(L);
		lua_newtable(L);
		int peer = lua_gettop(L);

		lua_pushstring(L, "socket");
		Socket* luasocket = new Socket(L, socket);
		Socket::push(L, luasocket, true);	// now Socket is the owner
		lua_rawset(L, peer);

		const std::string& sAddress = address.to_string();
		lua_pushstring(L, "address");
		lua_pushlstring(L, sAddress.c_str(), sAddress.length());
		lua_rawset(L, peer);

		lua_pushstring(L, "port");
		lua_pushnumber(L, socket->remote_endpoint().port());
		lua_rawset(L, peer);

		LuaNode::GetLuaVM().call(2, LUA_MULTRET);
	}
	else {
		if(error != boost::asio::error::operation_aborted) {
			LogError("Acceptor::HandleAccept (%p) (id:%u) (new socket %p) - %s", this, m_acceptorId, socket.get(), error.message().c_str());
		}

		int ret = BoostErrorToCallback(L, error);
		LuaNode::GetLuaVM().call(ret, LUA_MULTRET);
	}
	lua_settop(L, 0);
}
<commit_msg>Deal with errors in socket.remote_endpoint<commit_after>#include "stdafx.h"
#include "luanode.h"
#include "luanode_net.h"
#include "luanode_net_acceptor.h"
#include "blogger.h"

#include <boost/asio/placeholders.hpp>
#include <boost/make_shared.hpp>

#include <boost/bind.hpp>

using namespace LuaNode::Net;

static unsigned long s_nextAcceptorId = 0;
static unsigned long s_acceptorCount = 0;

//////////////////////////////////////////////////////////////////////////
///
void LuaNode::Net::Acceptor::RegisterFunctions(lua_State* L) {}

const char* Acceptor::className = "Acceptor";
const Acceptor::RegType Acceptor::methods[] = {
	{"open", &Acceptor::Open},
	{"close", &Acceptor::Close},
	{"setoption", &Acceptor::SetOption},
	{"getsockname", &Acceptor::GetLocalAddress},
	{"bind", &Acceptor::Bind},
	{"listen", &Acceptor::Listen},
	{"accept", &Acceptor::Accept},
	{0}
};

const Acceptor::RegType Acceptor::setters[] = {
	{0}
};

const Acceptor::RegType Acceptor::getters[] = {
	{0}
};

Acceptor::Acceptor(lua_State* L) :
	m_L( LuaNode::GetLuaVM() ),
	m_acceptorId(++s_nextAcceptorId),
	m_acceptor( GetIoService() )
{
	s_acceptorCount++;
	LogDebug("Constructing Acceptor (%p) (id:%u). Current acceptor count = %lu", this, m_acceptorId, s_acceptorCount);
}

Acceptor::~Acceptor(void)
{
	s_acceptorCount--;
	LogDebug("Destructing Acceptor (%p) (id:%u). Current acceptor count = %lu", this, m_acceptorId, s_acceptorCount);

	// Close the acceptor if it was still open
	if(m_acceptor.is_open()) {
		boost::system::error_code ec;
		m_acceptor.close(ec);
		if(ec) {
			LogError("Error closing acceptor (%p) (id:%u) - %s", this, m_acceptorId, ec.message().c_str());
		}
	}
}

//////////////////////////////////////////////////////////////////////////
///
/*static*/ int Acceptor::tostring_T(lua_State* L) {
	userdataType* ud = static_cast<userdataType*>(lua_touserdata(L, 1));
	Acceptor* obj = ud->pT;
	lua_pushfstring(L, "%s (%p) (id:%d)", className, obj, obj->m_acceptorId);
	return 1;
}

//////////////////////////////////////////////////////////////////////////
/// Open the acceptor using the specified protocol
int Acceptor::Open(lua_State* L) {
	const char* kind = luaL_checkstring(L, 2);
	LogDebug("Acceptor::Open (%p) (id:%u) - %s", this, m_acceptorId, kind);

	boost::system::error_code ec;
	if(strcmp(kind, "tcp4") == 0) {
		m_acceptor.open( boost::asio::ip::tcp::v4(), ec );
	}
	else if(strcmp(kind, "tcp6") == 0) {
		m_acceptor.open( boost::asio::ip::tcp::v6(), ec );
	}
	else {
		lua_pushnil(L);
		lua_pushfstring(L, "unknown protocol %s", kind);
#if defined(WSAEPROTONOSUPPORT)
		lua_pushinteger(L, WSAEPROTONOSUPPORT);
#else
		lua_pushinteger(L, EPROTONOSUPPORT);
#endif
		return 3;
	}
	return BoostErrorCodeToLua(L, ec);
}

//////////////////////////////////////////////////////////////////////////
///
int Acceptor::Close(lua_State* L) {
	LogDebug("Acceptor::Close (%p) (id:%u)", this, m_acceptorId);
	boost::system::error_code ec;
	m_acceptor.close(ec);
	return BoostErrorCodeToLua(L, ec);
}

//////////////////////////////////////////////////////////////////////////
///
int Acceptor::SetOption(lua_State* L) {
	const char* option = luaL_checkstring(L, 2);
	LogDebug("Acceptor::SetOption (%p) (id:%u) - %s", this, m_acceptorId, option);

	boost::system::error_code ec;

	if(strcmp(option, "reuseaddr") == 0) {
		bool value = lua_toboolean(L, 3) != 0;
		m_acceptor.set_option( boost::asio::socket_base::reuse_address(value), ec );
	}
	else {
		ec = boost::asio::error::invalid_argument;
	}
	return BoostErrorCodeToLua(L, ec);
}

//////////////////////////////////////////////////////////////////////////
///
int Acceptor::GetLocalAddress(lua_State* L) {
	boost::system::error_code ec;
	const boost::asio::ip::tcp::endpoint& endpoint = m_acceptor.local_endpoint(ec);

	if(!ec) {
		lua_pushstring(L, endpoint.address().to_string().c_str());
		lua_pushinteger(L, endpoint.port());
		return 2;
	}
	return luaL_error(L, "Acceptor::GetLocalAddress - %s:%d", ec.message().c_str(), ec.value());
}

//////////////////////////////////////////////////////////////////////////
///
int Acceptor::Bind(lua_State* L) {
	const char* ip = luaL_checkstring(L, 2);
	unsigned short port = luaL_checkinteger(L, 3);

	LogDebug("Acceptor::Bind (%p) (id:%u) - (%s,%hu)", this, m_acceptorId, ip, port);

	boost::system::error_code ec;
	boost::asio::ip::address address = boost::asio::ip::address::from_string(ip, ec);
	if(ec) {
		return BoostErrorCodeToLua(L, ec);
	}

	boost::asio::ip::tcp::endpoint endpoint( address, port );
	m_acceptor.bind(endpoint, ec);
	return BoostErrorCodeToLua(L, ec);
}

//////////////////////////////////////////////////////////////////////////
///
int Acceptor::Listen(lua_State* L) {
	int backlog = luaL_optinteger(L, 2, boost::asio::socket_base::max_connections);
	LogDebug("Acceptor::Listen (%p) (id:%u) - backlog = %d", this, m_acceptorId, backlog);

	boost::system::error_code ec;
	m_acceptor.listen(backlog, ec);
	return BoostErrorCodeToLua(L, ec);
}

//////////////////////////////////////////////////////////////////////////
///
int Acceptor::Accept(lua_State* L) {
	LogDebug("Acceptor::Accept (%p) (id:%u)", this, m_acceptorId);

	boost::shared_ptr<boost::asio::ip::tcp::socket> socket = boost::make_shared<boost::asio::ip::tcp::socket>( boost::ref(GetIoService()) );

	// store a reference in the registry
	lua_pushvalue(L, 1);
	int reference = luaL_ref(L, LUA_REGISTRYINDEX);

	m_acceptor.async_accept(
		*socket,
		boost::bind(&Acceptor::HandleAccept, this, reference, socket, boost::asio::placeholders::error)
	);
	return 0;
}

//////////////////////////////////////////////////////////////////////////
///
void Acceptor::HandleAccept(int reference, boost::shared_ptr<boost::asio::ip::tcp::socket> socket, const boost::system::error_code& error) {
	LogDebug("Acceptor::HandleAccept (%p) (id:%u) (new socket %p)", this, m_acceptorId, socket.get());
	lua_State* L = LuaNode::GetLuaVM();
	lua_rawgeti(L, LUA_REGISTRYINDEX, reference);
	luaL_unref(L, LUA_REGISTRYINDEX, reference);

	lua_getfield(L, 1, "callback");
	assert(lua_type(L, -1) == LUA_TFUNCTION); //An acceptor must have a callback"

	if(!error) {
		// Get the remote endpoint, dealing with the case the connection is already closed when we get here.
		boost::system::error_code ec;
		const boost::asio::ip::tcp::endpoint& endpoint = socket->remote_endpoint(ec);
		if(ec) {
			if(ec == boost::asio::error::not_connected) {
				LogWarning("Acceptor::HandleAccept (%p) (id:%u) (new socket %p) - Socket was already closed when accepted. %s",
					this, m_acceptorId, socket.get(), ec.message().c_str());
			}
			else {
				LogWarning("Acceptor::HandleAccept (%p) (id:%u) (new socket %p) - Error retrieving remote endpoint. %s",
					this, m_acceptorId, socket.get(), ec.message().c_str());
			}
			// TODO: Should I call the callback, even when the connection is already closed and there's nothing to be done?
			return;
		}

		boost::asio::ip::address address = endpoint.address();

		lua_pushnil(L);
		lua_newtable(L);
		int peer = lua_gettop(L);

		lua_pushstring(L, "socket");
		Socket* luasocket = new Socket(L, socket);
		Socket::push(L, luasocket, true);	// now Socket is the owner
		lua_rawset(L, peer);

		const std::string& sAddress = address.to_string();
		lua_pushstring(L, "address");
		lua_pushlstring(L, sAddress.c_str(), sAddress.length());
		lua_rawset(L, peer);

		lua_pushstring(L, "port");
		lua_pushnumber(L, endpoint.port());
		lua_rawset(L, peer);

		LuaNode::GetLuaVM().call(2, LUA_MULTRET);
	}
	else {
		if(error != boost::asio::error::operation_aborted) {
			LogError("Acceptor::HandleAccept (%p) (id:%u) (new socket %p) - %s", this, m_acceptorId, socket.get(), error.message().c_str());
		}

		int ret = BoostErrorToCallback(L, error);
		LuaNode::GetLuaVM().call(ret, LUA_MULTRET);
	}
	lua_settop(L, 0);
}
<|endoftext|>


<commit_before>#ifndef LUNAR_SLAB_ALLOCATOR
#define LUNAR_SLAB_ALLOCATOR

#include <new>
#include <limits>

#include <stdlib.h>

#include "slab.hpp"

namespace lunar {

template <typename T>
class slab_allocator {
public:
    typedef T         value_type;
    typedef size_t    size_type;
    typedef ptrdiff_t difference_type;
    typedef T*        pointer;
    typedef const T*  const_pointer;
    typedef T&        reference;
    typedef const T&  const_reference;

    template <class U> struct rebind { typedef slab_allocator<U> other; };
    slab_allocator() throw() { slab_init(&m_slab, sizeof(T)); }
    slab_allocator(const slab_allocator&) throw() { slab_init(&m_slab, sizeof(T)); }

    template <class U> slab_allocator(const slab_allocator<U>&) throw(){ slab_init(&m_slab, sizeof(T)); }

    ~slab_allocator() throw() { slab_destroy(&m_slab); }

    pointer address(reference x) const { return &x; }
    const_pointer address(const_reference x) const { return &x; }

    pointer allocate(size_type s, void const * = 0) {
        if (s == 1) {
            return (pointer)slab_alloc(&m_slab);
        } else if (s >= 1) {
            pointer temp = (pointer)malloc(sizeof(void*) + s * sizeof(T));
            if (temp == nullptr)
                return nullptr;

            void **vp = (void**)temp;
            *vp = (void*)~(uint64_t)0;

            return (pointer)((char*)temp + sizeof(void*));
        } else {
            return nullptr;
        }
    }

    void deallocate(pointer p, size_type) {
        void **vp = (void**)((char*)p - sizeof(void*));

        if (*vp == (void*)~(uint64_t)0)
            free(vp);
        else
            slab_free(&m_slab, p);
    }

    size_type max_size() const throw() {
        return std::numeric_limits<size_t>::max() / sizeof(T);
    }

    void construct(pointer p, const T& val) {
        new((void *)p) T(val);
    }

    void destroy(pointer p) {
        p->~T();
    }

private:
    slab_chain m_slab;
};

}

#endif // LUNAR_SLAB_ALLOCATOR<commit_msg>use thread local storage<commit_after>#ifndef LUNAR_SLAB_ALLOCATOR
#define LUNAR_SLAB_ALLOCATOR

#include <new>
#include <limits>

#include <stdlib.h>

#include "slab.hpp"

namespace lunar {

template <typename T>
class slab_allocator {
public:
    typedef T         value_type;
    typedef size_t    size_type;
    typedef ptrdiff_t difference_type;
    typedef T*        pointer;
    typedef const T*  const_pointer;
    typedef T&        reference;
    typedef const T&  const_reference;

    template <class U> struct rebind { typedef slab_allocator<U> other; };
    slab_allocator() throw()
    {
        if (! slab_allocator<T>::m_is_init) {
            slab_init(&m_slab, sizeof(T));
            slab_allocator<T>::m_is_init = true;
        }
    }
    slab_allocator(const slab_allocator&) throw()
    {
        if (! slab_allocator<T>::m_is_init) {
            slab_init(&m_slab, sizeof(T));
            slab_allocator<T>::m_is_init = true;
        }
    }

    template <class U> slab_allocator(const slab_allocator<U>&) throw()
    {
        if (! slab_allocator<U>::m_is_init) {
            slab_init(&slab_allocator<U>::m_slab, sizeof(U));
            slab_allocator<U>::m_is_init = true;
        }
    }

    ~slab_allocator() throw() { slab_destroy(&m_slab); }

    pointer address(reference x) const { return &x; }
    const_pointer address(const_reference x) const { return &x; }

    pointer allocate(size_type s, void const * = 0) {
        if (s == 1) {
            return (pointer)slab_alloc(&m_slab);
        } else if (s >= 1) {
            pointer temp = (pointer)malloc(sizeof(void*) + s * sizeof(T));
            if (temp == nullptr)
                return nullptr;

            void **vp = (void**)temp;
            *vp = (void*)~(uint64_t)0;

            return (pointer)((char*)temp + sizeof(void*));
        } else {
            return nullptr;
        }
    }

    void deallocate(pointer p, size_type) {
        void **vp = (void**)((char*)p - sizeof(void*));

        if (*vp == (void*)~(uint64_t)0)
            free(vp);
        else
            slab_free(&m_slab, p);
    }

    size_type max_size() const throw() {
        return std::numeric_limits<size_t>::max() / sizeof(T);
    }

    void construct(pointer p, const T& val) {
        new((void *)p) T(val);
    }

    void destroy(pointer p) {
        p->~T();
    }

private:
    __thread static bool m_is_init;
    __thread static slab_chain m_slab;
};

template <typename T> __thread bool slab_allocator<T>::m_is_init = false;
template <typename T> __thread slab_chain slab_allocator<T>::m_slab;

}

#endif // LUNAR_SLAB_ALLOCATOR<|endoftext|>


<commit_before>#pragma once
#include "BOSSAssert.h"
#include "Common.h"
#include <algorithm>

namespace BOSS
{
template <class T,size_t max_capacity>
class Vec
{
    size_t	_size;
    size_t  _capacity;
    T		_arr[max_capacity];

public:

    Vec<T,max_capacity>()
        : _size(0)
        ,_capacity(max_capacity)
    {
    }

    Vec<T,max_capacity>(const size_t & size)
        : _size(size)
        ,_capacity(max_capacity)
    {
        BOSS_ASSERT(size <= max_capacity,"Vec initializing with size > capacity, Size = %d, Capacity = %d",size,_capacity);
    }

    Vec<T,max_capacity>(const size_t & size,const T & val)
        : _size(size)
        ,_capacity(max_capacity)
    {
        BOSS_ASSERT(size <= max_capacity,"Vec initializing with size > capacity, Size = %d, Capacity = %d",size,_capacity);
        fill(val);
    }

    void resize(const size_t & size)
    {
        BOSS_ASSERT(size <= max_capacity,"Vec resizing with size > capacity, Size = %d, Cpacity = %d",size,_capacity);
        _size = size;
    }

    T & get(const size_t & index)
    {
        BOSS_ASSERT(index < size(),"Vec out of bounds exception, Size = %d, Index = %d",size(),index);
        return _arr[index];
    }

    const T & get(const size_t & index) const
    {
        BOSS_ASSERT(index < size(),"Vec out of bounds exception, Size = %d, Index = %d",size(),index);
        return _arr[index];
    }

    T & operator [] (const size_t & index)
    {
        return get(index);
    }

    const T & operator [] (const size_t & index) const
    {
        return get(index);
    }

    const bool contains(const T & e) const
    {
        for (size_t i(0); i<size(); ++i)
        {
            if (get(i) == e)
            {
                return true;
            }
        }

        return false;
    }

    void addSorted(const T & e)
    {
        size_t index(0);
        while (_arr[index] < e)
        {
            ++index;
        }

        addAtIndex(e, index);
    }

    void addAtIndex(const T & e, const size_t & index)
    {
        BOSS_ASSERT(index <= size(), "Can't add at index: Index = %d, Size = %d", index, size());
        BOSS_ASSERT(_size < capacity(),"Array over capacity: Size = %d",capacity());

        copyShiftRight(index);
        _arr[index] = e;
    }

    void copyShiftRight(const size_t & index)
    {
        BOSS_ASSERT(_size < capacity(),"Array over capacity: Size = %d",capacity());
        for (size_t i(_size + 1); i > index; --i)
        {
            _arr[i] = _arr[i-1];
        }

        _size++;
    }

    void copyShiftLeft(const size_t & index)
    {
        BOSS_ASSERT(size() > 0, "Can't shift left when empty");
        for (size_t i(index); i < size()-1; ++i)
        {
            _arr[i] = _arr[i+1];
        }

        _size--;
    }

    const size_t & capacity() const
    {
        return _capacity;
    }

    void push_back(const T & e)
    {
        BOSS_ASSERT(_size < capacity(),"Array over capacity: Size = %d",capacity());
        _arr[_size] = e;
        ++_size;
    }

    const T & back() const
    {
        BOSS_ASSERT(!empty(),"Vector back() with empty array");

        return get(_size);
    }

    void remove(const size_t & index)
    {
        remove_by_swap(index);
    }

    void removeByShift(const size_t & index)
    {
        copyShiftLeft(index);
    }

    void remove_by_swap(const size_t & index)
    {
        BOSS_ASSERT(index < size(),"Vector out of bounds exception, Size = %d, Index = %d",size(),index);

        std::swap(_arr[index],_arr[size()-1]);
        pop_back();
    }

    void pop_back()
    {
        BOSS_ASSERT(!empty(),"Vector pop_back() with empty array");

        _size--;
    }

    void sort()
    {
        std::sort(_arr, _arr + _size);
    }

    const bool empty() const
    {
        return size() == 0;
    }

    void clear()
    {
        _size = 0;
    }

    const size_t & size() const
    {
        return _size;
    }

    inline void fill(const T & val)
    {
        std::fill(_arr,_arr + _size,val);
    }

    class iterator
    {
        T * _ptr;
    public:
        iterator(T * ptr) : _ptr(ptr) { }
        iterator    operator ++ ()                      { iterator i = *this; _ptr++; return i; }
        iterator    operator ++ (int junk)              { _ptr++; return *this; }
        T &         operator *  ()                      { return *_ptr; }
        T *         operator -> ()                      { return _ptr; }
        bool        operator == (const iterator & rhs)  { return _ptr == rhs._ptr; }
        bool        operator != (const iterator & rhs)  { return _ptr != rhs._ptr; }
    };

    class const_iterator
    {
        T * _ptr;
    public:
        const_iterator(T * ptr) : _ptr(ptr) { }
        const_iterator  operator ++ ()                              { const_iterator i = *this; _ptr++; return i; }
        const_iterator  operator ++ (int junk)                      { _ptr++; return *this; }
        const T &       operator *  ()                              { return *_ptr; }
        const T *       operator -> ()                              { return _ptr; }
        bool            operator == (const const_iterator & rhs)    { return _ptr == rhs._ptr; }
        bool            operator != (const const_iterator & rhs)    { return _ptr != rhs._ptr; }
    };

    iterator begin()
    {
        return iterator(_arr);
    }

    iterator end()
    {
        return iterator(_arr + size());
    }

};

}<commit_msg>Extra log to track down crash against XIMP<commit_after>#pragma once
#include "BOSSAssert.h"
#include "Common.h"
#include <algorithm>

namespace BOSS
{
template <class T,size_t max_capacity>
class Vec
{
    size_t	_size;
    size_t  _capacity;
    T		_arr[max_capacity];

public:

    Vec<T,max_capacity>()
        : _size(0)
        ,_capacity(max_capacity)
    {
		BOSS_ASSERT(max_capacity>0, "Vec initializing with capacity = 0");
    }

    Vec<T,max_capacity>(const size_t & size)
        : _size(size)
        ,_capacity(max_capacity)
    {
        BOSS_ASSERT(size <= max_capacity,"Vec initializing with size > capacity, Size = %d, Capacity = %d",size,_capacity);
    }

    Vec<T,max_capacity>(const size_t & size,const T & val)
        : _size(size)
        ,_capacity(max_capacity)
    {
        BOSS_ASSERT(size <= max_capacity,"Vec initializing with size > capacity, Size = %d, Capacity = %d",size,_capacity);
        fill(val);
    }

    void resize(const size_t & size)
    {
        BOSS_ASSERT(size <= max_capacity,"Vec resizing with size > capacity, Size = %d, Cpacity = %d",size,_capacity);
        _size = size;
    }

    T & get(const size_t & index)
    {
        BOSS_ASSERT(index < size(),"Vec out of bounds exception, Size = %d, Index = %d",size(),index);
        return _arr[index];
    }

    const T & get(const size_t & index) const
    {
        BOSS_ASSERT(index < size(),"Vec out of bounds exception, Size = %d, Index = %d",size(),index);
        return _arr[index];
    }

    T & operator [] (const size_t & index)
    {
        return get(index);
    }

    const T & operator [] (const size_t & index) const
    {
        return get(index);
    }

    const bool contains(const T & e) const
    {
        for (size_t i(0); i<size(); ++i)
        {
            if (get(i) == e)
            {
                return true;
            }
        }

        return false;
    }

    void addSorted(const T & e)
    {
        size_t index(0);
        while (_arr[index] < e)
        {
            ++index;
        }

        addAtIndex(e, index);
    }

    void addAtIndex(const T & e, const size_t & index)
    {
        BOSS_ASSERT(index <= size(), "Can't add at index: Index = %d, Size = %d", index, size());
        BOSS_ASSERT(_size < capacity(),"Array over capacity: Size = %d",capacity());

        copyShiftRight(index);
        _arr[index] = e;
    }

    void copyShiftRight(const size_t & index)
    {
        BOSS_ASSERT(_size < capacity(),"Array over capacity: Size = %d",capacity());
        for (size_t i(_size + 1); i > index; --i)
        {
            _arr[i] = _arr[i-1];
        }

        _size++;
    }

    void copyShiftLeft(const size_t & index)
    {
        BOSS_ASSERT(size() > 0, "Can't shift left when empty");
        for (size_t i(index); i < size()-1; ++i)
        {
            _arr[i] = _arr[i+1];
        }

        _size--;
    }

    const size_t & capacity() const
    {
        return _capacity;
    }

    void push_back(const T & e)
    {
        BOSS_ASSERT(_size < capacity(),"Array over capacity: Capacity = %d, Size = %d",capacity(), _size);
        _arr[_size] = e;
        ++_size;
    }

    const T & back() const
    {
        BOSS_ASSERT(!empty(),"Vector back() with empty array");

        return get(_size);
    }

    void remove(const size_t & index)
    {
        remove_by_swap(index);
    }

    void removeByShift(const size_t & index)
    {
        copyShiftLeft(index);
    }

    void remove_by_swap(const size_t & index)
    {
        BOSS_ASSERT(index < size(),"Vector out of bounds exception, Size = %d, Index = %d",size(),index);

        std::swap(_arr[index],_arr[size()-1]);
        pop_back();
    }

    void pop_back()
    {
        BOSS_ASSERT(!empty(),"Vector pop_back() with empty array");

        _size--;
    }

    void sort()
    {
        std::sort(_arr, _arr + _size);
    }

    const bool empty() const
    {
        return size() == 0;
    }

    void clear()
    {
        _size = 0;
    }

    const size_t & size() const
    {
        return _size;
    }

    inline void fill(const T & val)
    {
        std::fill(_arr,_arr + _size,val);
    }

    class iterator
    {
        T * _ptr;
    public:
        iterator(T * ptr) : _ptr(ptr) { }
        iterator    operator ++ ()                      { iterator i = *this; _ptr++; return i; }
        iterator    operator ++ (int junk)              { _ptr++; return *this; }
        T &         operator *  ()                      { return *_ptr; }
        T *         operator -> ()                      { return _ptr; }
        bool        operator == (const iterator & rhs)  { return _ptr == rhs._ptr; }
        bool        operator != (const iterator & rhs)  { return _ptr != rhs._ptr; }
    };

    class const_iterator
    {
        T * _ptr;
    public:
        const_iterator(T * ptr) : _ptr(ptr) { }
        const_iterator  operator ++ ()                              { const_iterator i = *this; _ptr++; return i; }
        const_iterator  operator ++ (int junk)                      { _ptr++; return *this; }
        const T &       operator *  ()                              { return *_ptr; }
        const T *       operator -> ()                              { return _ptr; }
        bool            operator == (const const_iterator & rhs)    { return _ptr == rhs._ptr; }
        bool            operator != (const const_iterator & rhs)    { return _ptr != rhs._ptr; }
    };

    iterator begin()
    {
        return iterator(_arr);
    }

    iterator end()
    {
        return iterator(_arr + size());
    }

};

}<|endoftext|>