<commit_before>/*
 * Copyright 2007-2018 Content Management AG
 * All rights reserved.
 *
 * author: Max Kellermann <mk@cm4all.com>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * - Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the
 * distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
 * FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "Stock.hxx"
#include "Factory.hxx"
#include "FilteredSocket.hxx"
#include "AllocatorPtr.hxx"
#include "pool/DisposablePointer.hxx"
#include "stock/Stock.hxx"
#include "stock/Class.hxx"
#include "stock/LoggerDomain.hxx"
#include "address_list.hxx"
#include "net/PConnectSocket.hxx"
#include "net/SocketAddress.hxx"
#include "net/AllocatedSocketAddress.hxx"
#include "net/ToString.hxx"
#include "io/Logger.hxx"
#include "util/Cancellable.hxx"
#include "util/RuntimeError.hxx"
#include "util/Exception.hxx"
#include "stopwatch.hxx"

#include <assert.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <sys/un.h>
#include <sys/socket.h>

struct FilteredSocketStockRequest {
    const AllocatorPtr alloc;

    StopwatchPtr stopwatch;

    const bool ip_transparent;

    const SocketAddress bind_address, address;

    const Event::Duration timeout;

    SocketFilterFactory *const filter_factory;

    FilteredSocketStockRequest(AllocatorPtr _alloc,
                               StopwatchPtr &&_stopwatch,
                               bool _ip_transparent,
                               SocketAddress _bind_address,
                               SocketAddress _address,
                               Event::Duration _timeout,
                               SocketFilterFactory *_filter_factory) noexcept
        :alloc(_alloc),
         stopwatch(std::move(_stopwatch)),
         ip_transparent(_ip_transparent),
         bind_address(_bind_address), address(_address),
         timeout(_timeout),
         filter_factory(_filter_factory) {}
};

class FilteredSocketStockConnection final
    : StockItem, ConnectSocketHandler, BufferedSocketHandler, Cancellable {

    BasicLogger<StockLoggerDomain> logger;

    const FdType type;

    const AllocatedSocketAddress address;

    SocketFilterFactory *const filter_factory;

    /**
     * To cancel the ClientSocket.
     */
    CancellablePointer cancel_ptr;

    FilteredSocket socket;

public:
    FilteredSocketStockConnection(CreateStockItem c, FdType _type,
                                  SocketAddress _address,
                                  SocketFilterFactory *_filter_factory,
                                  CancellablePointer &_cancel_ptr) noexcept
        :StockItem(c),
         logger(c.stock),
         type(_type),
         address(_address),
         filter_factory(_filter_factory),
         socket(c.stock.GetEventLoop()) {
        _cancel_ptr = *this;

        cancel_ptr = nullptr;
    }

    ~FilteredSocketStockConnection() override {
        if (cancel_ptr)
            cancel_ptr.Cancel();
        else if (socket.IsValid() && socket.IsConnected()) {
            socket.Close();
            socket.Destroy();
        }
    }

    void Start(FilteredSocketStockRequest &&request, int domain) noexcept {
        client_socket_new(stock.GetEventLoop(), request.alloc,
                          std::move(request.stopwatch),
                          domain, SOCK_STREAM, 0,
                          request.ip_transparent,
                          request.bind_address,
                          request.address,
                          request.timeout,
                          *this, cancel_ptr);
    }

    SocketAddress GetAddress() const noexcept {
        return address;
    }

    auto &GetSocket() noexcept {
        return socket;
    }

    /* virtual methods from class Cancellable */
    void Cancel() noexcept override {
        assert(cancel_ptr);

        cancel_ptr.CancelAndClear();
        InvokeCreateAborted();
    }

    /* virtual methods from class ConnectSocketHandler */
    void OnSocketConnectSuccess(UniqueSocketDescriptor &&fd) noexcept override;
    void OnSocketConnectError(std::exception_ptr ep) noexcept override;

    /* virtual methods from class BufferedSocketHandler */
    BufferedResult OnBufferedData() override;
    bool OnBufferedClosed() noexcept override;

    gcc_noreturn
    bool OnBufferedWrite() override {
        /* should never be reached because we never schedule
           writing */
        gcc_unreachable();
    }

    void OnBufferedError(std::exception_ptr e) noexcept override;

    /* virtual methods from class StockItem */
    bool Borrow() noexcept override {
        return true;
    }

    bool Release() noexcept override;
};

/*
 * BufferedSocketHandler
 *
 */

BufferedResult
FilteredSocketStockConnection::OnBufferedData()
{
    logger(2, "unexpected data in idle TCP connection");
    InvokeIdleDisconnect();
    return BufferedResult::CLOSED;
}

bool
FilteredSocketStockConnection::OnBufferedClosed() noexcept
{
    InvokeIdleDisconnect();
    return false;
}

void
FilteredSocketStockConnection::OnBufferedError(std::exception_ptr e) noexcept
{
    logger(2, "error on idle connection: ", e);
    InvokeIdleDisconnect();
}

/*
 * client_socket callback
 *
 */

void
FilteredSocketStockConnection::OnSocketConnectSuccess(UniqueSocketDescriptor &&fd) noexcept
{
    cancel_ptr = nullptr;

    try {
        socket.Init(fd.Release(), type,
                    Event::Duration(-1), Event::Duration(-1),
                    filter_factory != nullptr
                    ? filter_factory->CreateFilter()
                    : nullptr,
                    *this);
    } catch (...) {
        InvokeCreateError(std::current_exception());
        return;
    }

    InvokeCreateSuccess();
}

void
FilteredSocketStockConnection::OnSocketConnectError(std::exception_ptr ep) noexcept
{
    cancel_ptr = nullptr;

    ep = NestException(ep,
                       FormatRuntimeError("Failed to connect to '%s'",
                                          GetStockName()));
    InvokeCreateError(ep);
}

/*
 * stock class
 *
 */

void
FilteredSocketStock::Create(CreateStockItem c, StockRequest _request,
                            CancellablePointer &cancel_ptr)
{
    auto &request = *(FilteredSocketStockRequest *)_request.get();

    const int address_family = request.address.GetFamily();
    const FdType type = address_family == AF_LOCAL
        ? FD_SOCKET
        : FD_TCP;

    auto *connection = new FilteredSocketStockConnection(c, type,
                                                         request.address,
                                                         request.filter_factory,
                                                         cancel_ptr);
    connection->Start(std::move(request), address_family);
}

bool
FilteredSocketStockConnection::Release() noexcept
{
    if (!socket.IsConnected())
        return false;

    if (!socket.IsEmpty()) {
        logger(2, "unexpected data in idle connection");
        return false;
    }

    socket.Reinit(Event::Duration(-1), Event::Duration(-1), *this);
    socket.UnscheduleWrite();

    socket.ScheduleReadTimeout(false, std::chrono::minutes(1));

    return true;
}

/*
 * interface
 *
 */

void
FilteredSocketStock::Get(AllocatorPtr alloc,
                         StopwatchPtr stopwatch,
                         const char *name,
                         bool ip_transparent,
                         SocketAddress bind_address,
                         SocketAddress address,
                         Event::Duration timeout,
                         SocketFilterFactory *filter_factory,
                         StockGetHandler &handler,
                         CancellablePointer &cancel_ptr) noexcept
{
    assert(!address.IsNull());

    auto request =
        NewDisposablePointer<FilteredSocketStockRequest>(alloc, alloc,
                                                         std::move(stopwatch),
                                                         ip_transparent,
                                                         bind_address, address,
                                                         timeout,
                                                         filter_factory);

    if (name == nullptr) {
        char buffer[1024];
        if (!ToString(buffer, sizeof(buffer), address))
            buffer[0] = 0;

        if (!bind_address.IsNull()) {
            char bind_buffer[1024];
            if (!ToString(bind_buffer, sizeof(bind_buffer), bind_address))
                bind_buffer[0] = 0;
            name = alloc.Concat(bind_buffer, '>', buffer);
        } else
            name = alloc.Dup(buffer);
    }

    if (filter_factory != nullptr)
        name = alloc.Concat(name, '|', filter_factory->GetFilterId());

    stock.Get(name, std::move(request), handler, cancel_ptr);
}

FilteredSocket &
fs_stock_item_get(StockItem &item)
{
    auto &connection = (FilteredSocketStockConnection &)item;

    return connection.GetSocket();
}

SocketAddress
fs_stock_item_get_address(const StockItem &item)
{
    const auto &connection = (const FilteredSocketStockConnection &)item;

    return connection.GetAddress();
}
<commit_msg>fs/Stock: make more methods private<commit_after>/*
 * Copyright 2007-2018 Content Management AG
 * All rights reserved.
 *
 * author: Max Kellermann <mk@cm4all.com>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * - Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the
 * distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
 * FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "Stock.hxx"
#include "Factory.hxx"
#include "FilteredSocket.hxx"
#include "AllocatorPtr.hxx"
#include "pool/DisposablePointer.hxx"
#include "stock/Stock.hxx"
#include "stock/Class.hxx"
#include "stock/LoggerDomain.hxx"
#include "address_list.hxx"
#include "net/PConnectSocket.hxx"
#include "net/SocketAddress.hxx"
#include "net/AllocatedSocketAddress.hxx"
#include "net/ToString.hxx"
#include "io/Logger.hxx"
#include "util/Cancellable.hxx"
#include "util/RuntimeError.hxx"
#include "util/Exception.hxx"
#include "stopwatch.hxx"

#include <assert.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <sys/un.h>
#include <sys/socket.h>

struct FilteredSocketStockRequest {
    const AllocatorPtr alloc;

    StopwatchPtr stopwatch;

    const bool ip_transparent;

    const SocketAddress bind_address, address;

    const Event::Duration timeout;

    SocketFilterFactory *const filter_factory;

    FilteredSocketStockRequest(AllocatorPtr _alloc,
                               StopwatchPtr &&_stopwatch,
                               bool _ip_transparent,
                               SocketAddress _bind_address,
                               SocketAddress _address,
                               Event::Duration _timeout,
                               SocketFilterFactory *_filter_factory) noexcept
        :alloc(_alloc),
         stopwatch(std::move(_stopwatch)),
         ip_transparent(_ip_transparent),
         bind_address(_bind_address), address(_address),
         timeout(_timeout),
         filter_factory(_filter_factory) {}
};

class FilteredSocketStockConnection final
    : StockItem, ConnectSocketHandler, BufferedSocketHandler, Cancellable {

    BasicLogger<StockLoggerDomain> logger;

    const FdType type;

    const AllocatedSocketAddress address;

    SocketFilterFactory *const filter_factory;

    /**
     * To cancel the ClientSocket.
     */
    CancellablePointer cancel_ptr;

    FilteredSocket socket;

public:
    FilteredSocketStockConnection(CreateStockItem c, FdType _type,
                                  SocketAddress _address,
                                  SocketFilterFactory *_filter_factory,
                                  CancellablePointer &_cancel_ptr) noexcept
        :StockItem(c),
         logger(c.stock),
         type(_type),
         address(_address),
         filter_factory(_filter_factory),
         socket(c.stock.GetEventLoop()) {
        _cancel_ptr = *this;

        cancel_ptr = nullptr;
    }

    ~FilteredSocketStockConnection() override {
        if (cancel_ptr)
            cancel_ptr.Cancel();
        else if (socket.IsValid() && socket.IsConnected()) {
            socket.Close();
            socket.Destroy();
        }
    }

    void Start(FilteredSocketStockRequest &&request, int domain) noexcept {
        client_socket_new(stock.GetEventLoop(), request.alloc,
                          std::move(request.stopwatch),
                          domain, SOCK_STREAM, 0,
                          request.ip_transparent,
                          request.bind_address,
                          request.address,
                          request.timeout,
                          *this, cancel_ptr);
    }

    SocketAddress GetAddress() const noexcept {
        return address;
    }

    auto &GetSocket() noexcept {
        return socket;
    }

private:
    /* virtual methods from class Cancellable */
    void Cancel() noexcept override {
        assert(cancel_ptr);

        cancel_ptr.CancelAndClear();
        InvokeCreateAborted();
    }

    /* virtual methods from class ConnectSocketHandler */
    void OnSocketConnectSuccess(UniqueSocketDescriptor &&fd) noexcept override;
    void OnSocketConnectError(std::exception_ptr ep) noexcept override;

    /* virtual methods from class BufferedSocketHandler */
    BufferedResult OnBufferedData() override;
    bool OnBufferedClosed() noexcept override;

    gcc_noreturn
    bool OnBufferedWrite() override {
        /* should never be reached because we never schedule
           writing */
        gcc_unreachable();
    }

    void OnBufferedError(std::exception_ptr e) noexcept override;

    /* virtual methods from class StockItem */
    bool Borrow() noexcept override {
        return true;
    }

    bool Release() noexcept override;
};

/*
 * BufferedSocketHandler
 *
 */

BufferedResult
FilteredSocketStockConnection::OnBufferedData()
{
    logger(2, "unexpected data in idle TCP connection");
    InvokeIdleDisconnect();
    return BufferedResult::CLOSED;
}

bool
FilteredSocketStockConnection::OnBufferedClosed() noexcept
{
    InvokeIdleDisconnect();
    return false;
}

void
FilteredSocketStockConnection::OnBufferedError(std::exception_ptr e) noexcept
{
    logger(2, "error on idle connection: ", e);
    InvokeIdleDisconnect();
}

/*
 * client_socket callback
 *
 */

void
FilteredSocketStockConnection::OnSocketConnectSuccess(UniqueSocketDescriptor &&fd) noexcept
{
    cancel_ptr = nullptr;

    try {
        socket.Init(fd.Release(), type,
                    Event::Duration(-1), Event::Duration(-1),
                    filter_factory != nullptr
                    ? filter_factory->CreateFilter()
                    : nullptr,
                    *this);
    } catch (...) {
        InvokeCreateError(std::current_exception());
        return;
    }

    InvokeCreateSuccess();
}

void
FilteredSocketStockConnection::OnSocketConnectError(std::exception_ptr ep) noexcept
{
    cancel_ptr = nullptr;

    ep = NestException(ep,
                       FormatRuntimeError("Failed to connect to '%s'",
                                          GetStockName()));
    InvokeCreateError(ep);
}

/*
 * stock class
 *
 */

void
FilteredSocketStock::Create(CreateStockItem c, StockRequest _request,
                            CancellablePointer &cancel_ptr)
{
    auto &request = *(FilteredSocketStockRequest *)_request.get();

    const int address_family = request.address.GetFamily();
    const FdType type = address_family == AF_LOCAL
        ? FD_SOCKET
        : FD_TCP;

    auto *connection = new FilteredSocketStockConnection(c, type,
                                                         request.address,
                                                         request.filter_factory,
                                                         cancel_ptr);
    connection->Start(std::move(request), address_family);
}

bool
FilteredSocketStockConnection::Release() noexcept
{
    if (!socket.IsConnected())
        return false;

    if (!socket.IsEmpty()) {
        logger(2, "unexpected data in idle connection");
        return false;
    }

    socket.Reinit(Event::Duration(-1), Event::Duration(-1), *this);
    socket.UnscheduleWrite();

    socket.ScheduleReadTimeout(false, std::chrono::minutes(1));

    return true;
}

/*
 * interface
 *
 */

void
FilteredSocketStock::Get(AllocatorPtr alloc,
                         StopwatchPtr stopwatch,
                         const char *name,
                         bool ip_transparent,
                         SocketAddress bind_address,
                         SocketAddress address,
                         Event::Duration timeout,
                         SocketFilterFactory *filter_factory,
                         StockGetHandler &handler,
                         CancellablePointer &cancel_ptr) noexcept
{
    assert(!address.IsNull());

    auto request =
        NewDisposablePointer<FilteredSocketStockRequest>(alloc, alloc,
                                                         std::move(stopwatch),
                                                         ip_transparent,
                                                         bind_address, address,
                                                         timeout,
                                                         filter_factory);

    if (name == nullptr) {
        char buffer[1024];
        if (!ToString(buffer, sizeof(buffer), address))
            buffer[0] = 0;

        if (!bind_address.IsNull()) {
            char bind_buffer[1024];
            if (!ToString(bind_buffer, sizeof(bind_buffer), bind_address))
                bind_buffer[0] = 0;
            name = alloc.Concat(bind_buffer, '>', buffer);
        } else
            name = alloc.Dup(buffer);
    }

    if (filter_factory != nullptr)
        name = alloc.Concat(name, '|', filter_factory->GetFilterId());

    stock.Get(name, std::move(request), handler, cancel_ptr);
}

FilteredSocket &
fs_stock_item_get(StockItem &item)
{
    auto &connection = (FilteredSocketStockConnection &)item;

    return connection.GetSocket();
}

SocketAddress
fs_stock_item_get_address(const StockItem &item)
{
    const auto &connection = (const FilteredSocketStockConnection &)item;

    return connection.GetAddress();
}
<|endoftext|>


<commit_before>/*****************************************************************************
 * Media Library
 *****************************************************************************
 * Copyright (C) 2015 Hugo Beauzée-Luyssen, Videolabs
 *
 * Authors: Hugo Beauzée-Luyssen<hugo@beauzee.fr>
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/

#include "VLCInstance.h"
#include "logging/Logger.h"
#include "vlcpp/vlc.hpp"

namespace
{
// Define this in the .cpp file to avoid including libvlcpp from the header.
struct Init
{
    Init()
    {
        const char* args[] = {
            "-vv",
        };
        instance = VLC::Instance( sizeof(args) / sizeof(args[0]), args );
        instance.logSet([this](int lvl, const libvlc_log_t*, std::string msg) {
            if ( Log::logLevel() != medialibrary::LogLevel::Verbose )
                return;
            if ( lvl == LIBVLC_ERROR )
                Log::Error( msg );
            else if ( lvl == LIBVLC_WARNING )
                Log::Warning( msg );
            else
                Log::Info( msg );
        });
    }

    VLC::Instance instance;
};
}

VLC::Instance& VLCInstance::get()
{
    // Instanciate the wrapper only once, and run initialization in its constructor.
    static Init wrapper;
    return wrapper.instance;
}
<commit_msg>VLCInstance: Add a comment regarding potential string copy<commit_after>/*****************************************************************************
 * Media Library
 *****************************************************************************
 * Copyright (C) 2015 Hugo Beauzée-Luyssen, Videolabs
 *
 * Authors: Hugo Beauzée-Luyssen<hugo@beauzee.fr>
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/

#include "VLCInstance.h"
#include "logging/Logger.h"
#include "vlcpp/vlc.hpp"

namespace
{
// Define this in the .cpp file to avoid including libvlcpp from the header.
struct Init
{
    Init()
    {
        const char* args[] = {
            "-vv",
        };
        instance = VLC::Instance( sizeof(args) / sizeof(args[0]), args );
        // Do not take the string by reference. libvlcpp is constructing the std::string
        // as it calls the log callback, so the string we receive will be move constructed
        instance.logSet([this](int lvl, const libvlc_log_t*, std::string msg) {
            if ( Log::logLevel() != medialibrary::LogLevel::Verbose )
                return;
            if ( lvl == LIBVLC_ERROR )
                Log::Error( msg );
            else if ( lvl == LIBVLC_WARNING )
                Log::Warning( msg );
            else
                Log::Info( msg );
        });
    }

    VLC::Instance instance;
};
}

VLC::Instance& VLCInstance::get()
{
    // Instanciate the wrapper only once, and run initialization in its constructor.
    static Init wrapper;
    return wrapper.instance;
}
<|endoftext|>


<commit_before>#include "lua.h"

extern "C"
#if defined(_MSC_VER) || defined(__BORLANDC__) || defined(__CODEGEARC__)
__declspec(dllimport)
#endif
int luaopen_luapbintf(lua_State* L);

int main() {
    void * p = static_cast<void*>(luaopen_luapbintf);
    return 0;
}
<commit_msg>Fix error: invalid static_cast from type ‘int(lua_State*)’ to type ‘void*’<commit_after>#include "lua.h"

extern "C"
#if defined(_MSC_VER) || defined(__BORLANDC__) || defined(__CODEGEARC__)
__declspec(dllimport)
#endif
int luaopen_luapbintf(lua_State* L);

int main() {
    int (*pFun)(lua_State*);
    pFun = luaopen_luapbintf;
    return 0;
}
<|endoftext|>


<commit_before>// Begin CVS Header
//   $Source: /Volumes/Home/Users/shoops/cvs/copasi_dev/copasi/plotUI/Attic/CHistogram.cpp,v $
//   $Revision: 1.8 $
//   $Name:  $
//   $Author: shoops $
//   $Date: 2009/01/07 19:03:24 $
// End CVS Header

// Copyright (C) 2008 by Pedro Mendes, Virginia Tech Intellectual
// Properties, Inc., EML Research, gGmbH, University of Heidelberg,
// and The University of Manchester.
// All rights reserved.

// Copyright (C) 2001 - 2007 by Pedro Mendes, Virginia Tech Intellectual
// Properties, Inc. and EML Research, gGmbH.
// All rights reserved.

#include <float.h>
#include <math.h>
#include "CHistogram.h"

//  public:

CHistogram::CHistogram():
    mIncrement(0.1),
    mUptodate(false),
    mXArray(NULL),
    mYArray(NULL),
    mArraySize(0),
    mCount(0)
{
  mInvIncrement = 1 / mIncrement;
}

CHistogram::CHistogram(C_FLOAT64 incr):
    mIncrement(incr),
    mUptodate(false),
    mXArray(NULL),
    mYArray(NULL),
    mArraySize(0),
    mCount(0)
{
  mInvIncrement = 1 / mIncrement;
}

CHistogram::~CHistogram()
{
  if (mXArray)
    delete[] mXArray;
  if (mYArray)
    delete[] mYArray;
}

void CHistogram::addValue(const C_FLOAT64 & val)
{
  //just ignore breaks. Later we perhaps want to start a new histogram...
  if (isnan(val)) //NaN
    return;

  if (val >= DBL_MAX || val <= -DBL_MAX)
    return;

  mUptodate = false;
  //std::cout << val<< "  " << (C_INT32)(val/mIncrement)<< std::endl;
  mMap[(C_INT32)floor(val * mInvIncrement)]++;
  mMap[(((C_INT32)floor(val * mInvIncrement))) + 1];
  ++mCount;
}

const double* CHistogram::getXArray()
{
  if (!mUptodate)
    updateArray();
  return mXArray;
}

const double* CHistogram::getYArray()
{
  if (!mUptodate)
    updateArray();
  return mYArray;
}

int CHistogram::size()
{
  if (!mUptodate)
    updateArray();
  return mArraySize;
}

//private
void CHistogram::updateArray()
{
  if (mUptodate)
    return; //do nothing

  //delete Arrays
  if (mXArray)
    delete[] mXArray;
  if (mYArray)
    delete[] mYArray;

  //construct arrays
  mXArray = new double[mMap.size() + 1];
  mYArray = new double[mMap.size() + 1];
  mArraySize = mMap.size() + 1;

  C_FLOAT64 tmpFactor = 1 / (mCount * mIncrement);

  //add one bin to the left
  mXArray[0] = (mMap.begin()->first - 1) * mIncrement;
  mYArray[0] = 0.0;

  C_INT32 i;
  std::map<C_INT32, C_INT32>::const_iterator it, itEnd = mMap.end();
  for (it = mMap.begin(), i = 1; it != itEnd; ++it, ++i)
    {//TODO use pointer increments instead of [...]
      mXArray[i] = it->first * mIncrement;
      mYArray[i] = (double)it->second * tmpFactor;
      //std::cout <<it->first <<" " <<it->second << " : " << mXArray[i] << " " << mYArray[i] << std::endl;
    }
  //std::cout << std::endl;

  mUptodate = true;
}
<commit_msg>Fixed crash due to accessing an invalid iterator mMap.begin().<commit_after>// Begin CVS Header
//   $Source: /Volumes/Home/Users/shoops/cvs/copasi_dev/copasi/plotUI/Attic/CHistogram.cpp,v $
//   $Revision: 1.9 $
//   $Name:  $
//   $Author: shoops $
//   $Date: 2009/07/01 18:53:52 $
// End CVS Header

// Copyright (C) 2008 by Pedro Mendes, Virginia Tech Intellectual
// Properties, Inc., EML Research, gGmbH, University of Heidelberg,
// and The University of Manchester.
// All rights reserved.

// Copyright (C) 2001 - 2007 by Pedro Mendes, Virginia Tech Intellectual
// Properties, Inc. and EML Research, gGmbH.
// All rights reserved.

#include <float.h>
#include <math.h>
#include "CHistogram.h"

//  public:

CHistogram::CHistogram():
    mIncrement(0.1),
    mUptodate(true),
    mXArray(NULL),
    mYArray(NULL),
    mArraySize(0),
    mMap(),
    mCount(0)
{
  mInvIncrement = 1 / mIncrement;
}

CHistogram::CHistogram(C_FLOAT64 incr):
    mIncrement(incr),
    mUptodate(true),
    mXArray(NULL),
    mYArray(NULL),
    mArraySize(0),
    mMap(),
    mCount(0)
{
  mInvIncrement = 1 / mIncrement;
}

CHistogram::~CHistogram()
{
  if (mXArray)
    delete[] mXArray;

  if (mYArray)
    delete[] mYArray;
}

void CHistogram::addValue(const C_FLOAT64 & val)
{
  //just ignore breaks. Later we perhaps want to start a new histogram...
  if (isnan(val)) //NaN
    return;

  if (val >= DBL_MAX || val <= -DBL_MAX)
    return;

  mUptodate = false;
  //std::cout << val<< "  " << (C_INT32)(val/mIncrement)<< std::endl;
  mMap[(C_INT32)floor(val * mInvIncrement)]++;
  mMap[(((C_INT32)floor(val * mInvIncrement))) + 1];
  ++mCount;
}

const double* CHistogram::getXArray()
{
  if (!mUptodate)
    updateArray();

  return mXArray;
}

const double* CHistogram::getYArray()
{
  if (!mUptodate)
    updateArray();

  return mYArray;
}

int CHistogram::size()
{
  if (!mUptodate)
    updateArray();

  return mArraySize;
}

//private
void CHistogram::updateArray()
{
  if (mUptodate)
    return; //do nothing

  //delete Arrays
  if (mXArray)
    delete[] mXArray;

  if (mYArray)
    delete[] mYArray;

  //construct arrays
  mXArray = new double[mMap.size() + 1];
  mYArray = new double[mMap.size() + 1];
  mArraySize = mMap.size() + 1;

  C_FLOAT64 tmpFactor = 1 / (mCount * mIncrement);

  //add one bin to the left
  if (mMap.size() > 0)
    {
      mXArray[0] = (mMap.begin()->first - 1) * mIncrement;
    }
  else
    {
      mXArray[0];
    }

  mYArray[0] = 0.0;

  C_INT32 i;
  std::map<C_INT32, C_INT32>::const_iterator it, itEnd = mMap.end();

  for (it = mMap.begin(), i = 1; it != itEnd; ++it, ++i)
    {//TODO use pointer increments instead of [...]
      mXArray[i] = it->first * mIncrement;
      mYArray[i] = (double)it->second * tmpFactor;
      //std::cout <<it->first <<" " <<it->second << " : " << mXArray[i] << " " << mYArray[i] << std::endl;
    }

  //std::cout << std::endl;

  mUptodate = true;
}
<|endoftext|>


<commit_before>/*
 * Copyright 2012 Bram van der Kroef
 *
 * This file is part of LESS CSS Compiler.
 *
 * LESS CSS Compiler is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * LESS CSS Compiler is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with LESS CSS Compiler.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Author: Bram van der Kroef <bram@vanderkroef.net>
 */

#include "NumberValue.h"

NumberValue::NumberValue(Token* token) {
  tokens.push(token);

  switch(token->type) {
  case Token::NUMBER:
    type = NUMBER;
    break;
  case Token::PERCENTAGE:
    type = PERCENTAGE;
    break;
  case Token::DIMENSION:
    type = DIMENSION;
    break;
  default:
    throw new ValueException("Token used for Value that isn't a number, percentage or dimension");
  }
}

NumberValue::~NumberValue() {
}

void NumberValue::verifyUnits(NumberValue* v) {
  if (type == Value::DIMENSION &&
      v->type == Value::DIMENSION &&
      getUnit().compare(v->getUnit()) != 0) {

    if (v->convert(getUnit())) {
      return;
    } else
      throw new ValueException("Can't do math on dimensions with different units.");
  }
}

bool NumberValue::convert(string unit) {
  UnitValue::UnitGroup group = UnitValue::getUnitGroup(unit);
  double value = getValue();

  if (UnitValue::getUnitGroup(getUnit()) == group) {

    switch(group) {
    case UnitValue::LENGTH:
      value = UnitValue::lengthToPx(value, getUnit());
      setValue(UnitValue::pxToLength(value, unit));
      setUnit(unit);
      return true;

    case UnitValue::TIME:
      value = UnitValue::timeToMs(value, getUnit());
      setValue(UnitValue::msToTime(value, unit));
      setUnit(unit);
      return true;

    case UnitValue::ANGLE:
      value = UnitValue::angleToRad(value, getUnit());
      setValue(UnitValue::radToAngle(value, unit));
      setUnit(unit);
      return true;

    default:
      break;
    }
  }
  return false;
}

Value* NumberValue::add(Value* v) {
  Token* t;
  StringValue* s;

  if (isNumber(v)) {
    if (type == NUMBER)
      setType(v);
    else
      verifyUnits((NumberValue*)v);

    setValue(getValue() + ((NumberValue*)v)->getValue());
    return this;

  } else if (v->type == COLOR) {
    return v->add(this);

  } else if (v->type == STRING) {
    t = this->tokens.front()->clone();
    t->type = Token::STRING;
    s = new StringValue(t, ((StringValue*)v)->getQuotes());
    s->add(v);
    return s;
  } else {
    throw new ValueException("Unsupported type.");
  }
}
Value* NumberValue::substract(Value* v) {
  if (isNumber(v)) {
    if (type == NUMBER)
      setType(v);
    else
      verifyUnits((NumberValue*)v);

    setValue(getValue() - ((NumberValue*)v)->getValue());
    return this;
  } else
    throw new ValueException("You can only substract a *number* from a number.");
}
Value* NumberValue::multiply(Value* v) {
  if (isNumber(v)) {
    if (type == NUMBER)
      setType(v);
    else
      verifyUnits((NumberValue*)v);
    setValue(getValue() * ((NumberValue*)v)->getValue());
    return this;
  } else if (v->type == COLOR ||
             v->type == STRING) {
    return v->multiply(this);
  } else {
    throw new ValueException("Unsupported type.");
  }
}

Value* NumberValue::divide(Value* v) {
  if (isNumber(v)) {
    if (type == NUMBER)
      setType(v);
    else
      verifyUnits((NumberValue*)v);
    setValue(getValue() / ((NumberValue*)v)->getValue());
    return this;
  } else
    throw new ValueException("You can only divide a number by a *number*.");
}
int NumberValue::compare(Value* v) {
  if (isNumber(v)) {
    verifyUnits((NumberValue*)v);
    return getValue() - ((NumberValue*)v)->getValue();
  } else {
    throw new ValueException("You can only compare a number with a *number*.");
  }
}

void NumberValue::setType(Value* v) {
  NumberValue* n;

  if (isNumber(v)) {

    n = (NumberValue*)v;

    type = n->type;
    if (n->type == DIMENSION)
      setUnit(n->getUnit());
    else if (n->type == PERCENTAGE)
      tokens.front()->type = Token::PERCENTAGE;
    else if (n->type == NUMBER) {
      setUnit("");
    }
  }
}

double NumberValue::getValue() {
  string number;
  istringstream stm;
  double ret;
  char c;

  for (unsigned int i = 0; i < tokens.front()->str.size(); i++) {
    c = tokens.front()->str[i];
    if (!isdigit(c) && c != '.' && c != '-') {
      number = tokens.front()->str.substr(0, i);
      break;
    }
  }
  if (number == "")
    number = tokens.front()->str;
  stm.str(number);
  stm >>ret;
  return ret;
}
string NumberValue::getUnit () {
  char c;
  unsigned int i;

  for (i = 0; i < tokens.front()->str.size(); i++) {
    c = tokens.front()->str[i];
    if (!isdigit(c) && c != '.' && c != '-')
      return tokens.front()->str.substr(i);
  }
  return string("");
}

void NumberValue::setUnit(string unit) {
  ostringstream stm;
  stm << getValue();
  stm << unit;
  tokens.front()->str = stm.str();

  if (unit.length() == 0) {
    type = NUMBER;
    tokens.front()->type = Token::NUMBER;
  } else {
    type = DIMENSION;
    tokens.front()->type = Token::DIMENSION;
  }
}

void NumberValue::setValue(double d) {
  ostringstream stm;
  stm << d;
  if (type == DIMENSION)
    stm << getUnit();
  else if (type == PERCENTAGE)
    stm << "%";
  tokens.front()->str = stm.str();
}

bool NumberValue::isNumber(Value* val) {
  return (val->type == Value::NUMBER ||
          val->type == Value::DIMENSION ||
          val->type == Value::PERCENTAGE);
}

void NumberValue::loadFunctions(FunctionLibrary* lib) {
  lib->push("unit", ".U?", &NumberValue::unit);
  lib->push("ceil", ".", &NumberValue::ceil);
  lib->push("floor", ".", &NumberValue::floor);
  lib->push("percentage", "N", &NumberValue::percentage);
  lib->push("round", ".", &NumberValue::round);
  lib->push("sqrt", ".", &NumberValue::sqrt);
  lib->push("abs", ".", &NumberValue::abs);
  lib->push("sin", ".", &NumberValue::sin);
  lib->push("asin", "N", &NumberValue::asin);
  lib->push("cos", ".", &NumberValue::cos);
  lib->push("acos", "N", &NumberValue::acos);
  lib->push("tan", ".", &NumberValue::tan);
  lib->push("atan", "N", &NumberValue::atan);
  lib->push("pi", "", &NumberValue::pi);
  lib->push("pow", ".N", &NumberValue::pow);
  lib->push("mod", "..", &NumberValue::mod);
  lib->push("convert", "..", &NumberValue::convert);
}

// DIMENSION unit(DIMENSION, UNIT)
Value* NumberValue::unit(vector<Value*> arguments) {
  if (arguments[0]->type == Value::NUMBER ||
      arguments[0]->type == Value::DIMENSION) {
    if (arguments.size() > 1) {
      ((NumberValue*)arguments[0])
        ->setUnit(((UnitValue*)arguments[1])->getUnit());
    } else
      ((NumberValue*)arguments[0])->setUnit("");
    return arguments[0];
  } else
    throw new ValueException("argument 1 has to be a number or dimension");
}
Value* NumberValue::ceil(vector<Value*> args) {
  if (!NumberValue::isNumber(args[0]))
    throw new ValueException("ceil() only works on numeric values");

  double val = ((NumberValue*)args[0])->getValue();
  ((NumberValue*)args[0])->setValue(std::ceil(val));
  return args[0];
}
Value* NumberValue::floor(vector<Value*> args) {
  if (!NumberValue::isNumber(args[0]))
    throw new ValueException("floor() only works on numeric values");

  double val = ((NumberValue*)args[0])->getValue();
  ((NumberValue*)args[0])->setValue(std::floor(val));
  return args[0];
}
Value* NumberValue::percentage(vector<Value*> args) {
  double val = ((NumberValue*)args[0])->getValue();
  ((NumberValue*)args[0])->setValue(val * 100);
  ((NumberValue*)args[0])->type = Value::PERCENTAGE;
  return args[0];
}
Value* NumberValue::round(vector<Value*> args) {
  if (!NumberValue::isNumber(args[0]))
    throw new ValueException("round() only works on numeric values");

  double val = ((NumberValue*)args[0])->getValue();
  double decimalplaces = 0;

  if (args.size() > 1)
    decimalplaces = ((NumberValue*)args[0])->getValue();

  val = val * std::pow(10, decimalplaces);
  val = std::floor(val + 0.5);
  val = val / std::pow(10, decimalplaces);
  ((NumberValue*)args[0])->setValue(val);
  return args[0];
}
Value* NumberValue::sqrt(vector<Value*> args) {
  if (!NumberValue::isNumber(args[0]))
    throw new ValueException("sqrt() only works on numeric values");

  double val = ((NumberValue*)args[0])->getValue();
  ((NumberValue*)args[0])->setValue(std::sqrt(val));
  return args[0];
}
Value* NumberValue::abs(vector<Value*> args) {
  if (!NumberValue::isNumber(args[0]))
    throw new ValueException("abs() only works on numeric values");

  double val = ((NumberValue*)args[0])->getValue();
  ((NumberValue*)args[0])->setValue(fabs(val));
  return args[0];
}

Value* NumberValue::sin(vector<Value*> args) {
  if (args[0]->type != Value::NUMBER &&
      args[0]->type != Value::DIMENSION) {
    throw new ValueException("sin() only works on numbers or dimensions");
  }

  NumberValue* arg = ((NumberValue*)args[0]);
  double val = arg->getValue();

  if (arg->type == Value::DIMENSION) {
    if(arg->getUnit().compare("rad") != 0 &&
       arg->getUnit().compare("deg") != 0 &&
       arg->getUnit().compare("grad") != 0 &&
       arg->getUnit().compare("turn") != 0) {
      throw new ValueException("sin() requires rad, deg, grad or turn units.");
    }
    val = UnitValue::angleToRad(val, arg->getUnit());
  }

  arg->setValue(std::sin(val));
  arg->type = Value::NUMBER;
  arg->setUnit("");
  return arg;
}
Value* NumberValue::asin(vector<Value*> args) {
  NumberValue* arg = ((NumberValue*)args[0]);

  arg->setValue(std::asin(arg->getValue()));
  arg->setUnit("rad");
  arg->type = Value::DIMENSION;
  return arg;
}
Value* NumberValue::cos(vector<Value*> args) {
  if (args[0]->type != Value::NUMBER &&
      args[0]->type != Value::DIMENSION) {
    throw new ValueException("cos() only works on numbers or dimensions");
  }
  NumberValue* arg = ((NumberValue*)args[0]);
  double val = arg->getValue();

  if (arg->type == Value::DIMENSION) {
    if(arg->getUnit().compare("rad") != 0 &&
       arg->getUnit().compare("deg") != 0 &&
       arg->getUnit().compare("grad") != 0 &&
       arg->getUnit().compare("turn") != 0) {
      throw new ValueException("cos() requires rad, deg, grad or turn units.");
    }
    val = UnitValue::angleToRad(val, arg->getUnit());
  }

  arg->setValue(std::cos(val));
  arg->type = Value::NUMBER;
  arg->setUnit("");
  return arg;
}
Value* NumberValue::acos(vector<Value*> args) {
  NumberValue* arg = ((NumberValue*)args[0]);

  arg->setValue(std::acos(arg->getValue()));
  arg->setUnit("rad");
  arg->type = Value::DIMENSION;
  return arg;
}
Value* NumberValue::tan(vector<Value*> args) {
  if (args[0]->type != Value::NUMBER &&
      args[0]->type != Value::DIMENSION) {
    throw new ValueException("tan() only works on numbers or dimensions");
  }
  NumberValue* arg = ((NumberValue*)args[0]);
  double val = arg->getValue();

  if (arg->type == Value::DIMENSION) {
    if(arg->getUnit().compare("rad") != 0 &&
       arg->getUnit().compare("deg") != 0 &&
       arg->getUnit().compare("grad") != 0 &&
       arg->getUnit().compare("turn") != 0) {
      throw new ValueException("tan() requires rad, deg, grad or turn units.");
    }
    val = UnitValue::angleToRad(val, arg->getUnit());
  }

  arg->setValue(std::tan(val));
  arg->type = Value::NUMBER;
  arg->setUnit("");
  return arg;
}
Value* NumberValue::atan(vector<Value*> args) {
  NumberValue* arg = ((NumberValue*)args[0]);

  arg->setValue(std::atan(arg->getValue()));
  arg->setUnit("rad");
  arg->type = Value::DIMENSION;
  return arg;
}
Value* NumberValue::pi(vector<Value*> args) {
  (void)args;
  return new NumberValue(new Token("3.141592653589793", Token::NUMBER));
}
Value* NumberValue::pow(vector<Value*> args) {
  if (!NumberValue::isNumber(args[0]))
    throw new ValueException("pow() only works on numeric values");

  double val = ((NumberValue*)args[0])->getValue();
  double exp = ((NumberValue*)args[1])->getValue();
  ((NumberValue*)args[0])->setValue(std::pow(val, exp));
  return args[0];
}
Value* NumberValue::mod(vector<Value*> args) {
  if (!NumberValue::isNumber(args[0]) ||
      !NumberValue::isNumber(args[1]))
    throw new ValueException("mod() only works on numeric values");

  double val1 = ((NumberValue*)args[0])->getValue();
  double val2 = ((NumberValue*)args[1])->getValue();
  ((NumberValue*)args[0])->setValue(std::fmod(val1, val2));
  return args[0];
}
Value* NumberValue::convert(vector<Value*> args) {
  if (!NumberValue::isNumber(args[0]))
    throw new ValueException("convert() only works on numeric values");
  if (args[1]->type != Value::STRING &&
      args[1]->type != Value::UNIT) {
    throw new ValueException("convert() requires a unit \
(or unit as a string)");
  }

  NumberValue* val = (NumberValue*)args[0];
  string unit;

  if (args[1]->type == Value::STRING)
    unit = ((StringValue*)args[1])->getString();
  else
    unit.append(((UnitValue*)args[1])->getUnit());

  val->convert(unit);
  return val;
}
<commit_msg>Fixed percentage() LESS function.<commit_after>/*
 * Copyright 2012 Bram van der Kroef
 *
 * This file is part of LESS CSS Compiler.
 *
 * LESS CSS Compiler is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * LESS CSS Compiler is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with LESS CSS Compiler.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Author: Bram van der Kroef <bram@vanderkroef.net>
 */

#include "NumberValue.h"

NumberValue::NumberValue(Token* token) {
  tokens.push(token);

  switch(token->type) {
  case Token::NUMBER:
    type = NUMBER;
    break;
  case Token::PERCENTAGE:
    type = PERCENTAGE;
    break;
  case Token::DIMENSION:
    type = DIMENSION;
    break;
  default:
    throw new ValueException("Token used for Value that isn't a number, percentage or dimension");
  }
}

NumberValue::~NumberValue() {
}

void NumberValue::verifyUnits(NumberValue* v) {
  if (type == Value::DIMENSION &&
      v->type == Value::DIMENSION &&
      getUnit().compare(v->getUnit()) != 0) {

    if (v->convert(getUnit())) {
      return;
    } else
      throw new ValueException("Can't do math on dimensions with different units.");
  }
}

bool NumberValue::convert(string unit) {
  UnitValue::UnitGroup group = UnitValue::getUnitGroup(unit);
  double value = getValue();

  if (UnitValue::getUnitGroup(getUnit()) == group) {

    switch(group) {
    case UnitValue::LENGTH:
      value = UnitValue::lengthToPx(value, getUnit());
      setValue(UnitValue::pxToLength(value, unit));
      setUnit(unit);
      return true;

    case UnitValue::TIME:
      value = UnitValue::timeToMs(value, getUnit());
      setValue(UnitValue::msToTime(value, unit));
      setUnit(unit);
      return true;

    case UnitValue::ANGLE:
      value = UnitValue::angleToRad(value, getUnit());
      setValue(UnitValue::radToAngle(value, unit));
      setUnit(unit);
      return true;

    default:
      break;
    }
  }
  return false;
}

Value* NumberValue::add(Value* v) {
  Token* t;
  StringValue* s;

  if (isNumber(v)) {
    if (type == NUMBER)
      setType(v);
    else
      verifyUnits((NumberValue*)v);

    setValue(getValue() + ((NumberValue*)v)->getValue());
    return this;

  } else if (v->type == COLOR) {
    return v->add(this);

  } else if (v->type == STRING) {
    t = this->tokens.front()->clone();
    t->type = Token::STRING;
    s = new StringValue(t, ((StringValue*)v)->getQuotes());
    s->add(v);
    return s;
  } else {
    throw new ValueException("Unsupported type.");
  }
}
Value* NumberValue::substract(Value* v) {
  if (isNumber(v)) {
    if (type == NUMBER)
      setType(v);
    else
      verifyUnits((NumberValue*)v);

    setValue(getValue() - ((NumberValue*)v)->getValue());
    return this;
  } else
    throw new ValueException("You can only substract a *number* from a number.");
}
Value* NumberValue::multiply(Value* v) {
  if (isNumber(v)) {
    if (type == NUMBER)
      setType(v);
    else
      verifyUnits((NumberValue*)v);
    setValue(getValue() * ((NumberValue*)v)->getValue());
    return this;
  } else if (v->type == COLOR ||
             v->type == STRING) {
    return v->multiply(this);
  } else {
    throw new ValueException("Unsupported type.");
  }
}

Value* NumberValue::divide(Value* v) {
  if (isNumber(v)) {
    if (type == NUMBER)
      setType(v);
    else
      verifyUnits((NumberValue*)v);
    setValue(getValue() / ((NumberValue*)v)->getValue());
    return this;
  } else
    throw new ValueException("You can only divide a number by a *number*.");
}
int NumberValue::compare(Value* v) {
  if (isNumber(v)) {
    verifyUnits((NumberValue*)v);
    return getValue() - ((NumberValue*)v)->getValue();
  } else {
    throw new ValueException("You can only compare a number with a *number*.");
  }
}

void NumberValue::setType(Value* v) {
  NumberValue* n;

  if (isNumber(v)) {

    n = (NumberValue*)v;

    type = n->type;
    if (n->type == DIMENSION)
      setUnit(n->getUnit());
    else if (n->type == PERCENTAGE)
      tokens.front()->type = Token::PERCENTAGE;
    else if (n->type == NUMBER) {
      setUnit("");
    }
  }
}

double NumberValue::getValue() {
  string number;
  istringstream stm;
  double ret;
  char c;

  for (unsigned int i = 0; i < tokens.front()->str.size(); i++) {
    c = tokens.front()->str[i];
    if (!isdigit(c) && c != '.' && c != '-') {
      number = tokens.front()->str.substr(0, i);
      break;
    }
  }
  if (number == "")
    number = tokens.front()->str;
  stm.str(number);
  stm >>ret;
  return ret;
}
string NumberValue::getUnit () {
  char c;
  unsigned int i;

  for (i = 0; i < tokens.front()->str.size(); i++) {
    c = tokens.front()->str[i];
    if (!isdigit(c) && c != '.' && c != '-')
      return tokens.front()->str.substr(i);
  }
  return string("");
}

void NumberValue::setUnit(string unit) {
  ostringstream stm;
  stm << getValue();
  stm << unit;
  tokens.front()->str = stm.str();

  if (unit.length() == 0) {
    type = NUMBER;
    tokens.front()->type = Token::NUMBER;
  } else {
    type = DIMENSION;
    tokens.front()->type = Token::DIMENSION;
  }
}

void NumberValue::setValue(double d) {
  ostringstream stm;
  stm << d;
  if (type == DIMENSION)
    stm << getUnit();
  else if (type == PERCENTAGE)
    stm << "%";
  tokens.front()->str = stm.str();
}

bool NumberValue::isNumber(Value* val) {
  return (val->type == Value::NUMBER ||
          val->type == Value::DIMENSION ||
          val->type == Value::PERCENTAGE);
}

void NumberValue::loadFunctions(FunctionLibrary* lib) {
  lib->push("unit", ".U?", &NumberValue::unit);
  lib->push("ceil", ".", &NumberValue::ceil);
  lib->push("floor", ".", &NumberValue::floor);
  lib->push("percentage", "N", &NumberValue::percentage);
  lib->push("round", ".", &NumberValue::round);
  lib->push("sqrt", ".", &NumberValue::sqrt);
  lib->push("abs", ".", &NumberValue::abs);
  lib->push("sin", ".", &NumberValue::sin);
  lib->push("asin", "N", &NumberValue::asin);
  lib->push("cos", ".", &NumberValue::cos);
  lib->push("acos", "N", &NumberValue::acos);
  lib->push("tan", ".", &NumberValue::tan);
  lib->push("atan", "N", &NumberValue::atan);
  lib->push("pi", "", &NumberValue::pi);
  lib->push("pow", ".N", &NumberValue::pow);
  lib->push("mod", "..", &NumberValue::mod);
  lib->push("convert", "..", &NumberValue::convert);
}

// DIMENSION unit(DIMENSION, UNIT)
Value* NumberValue::unit(vector<Value*> arguments) {
  if (arguments[0]->type == Value::NUMBER ||
      arguments[0]->type == Value::DIMENSION) {
    if (arguments.size() > 1) {
      ((NumberValue*)arguments[0])
        ->setUnit(((UnitValue*)arguments[1])->getUnit());
    } else
      ((NumberValue*)arguments[0])->setUnit("");
    return arguments[0];
  } else
    throw new ValueException("argument 1 has to be a number or dimension");
}
Value* NumberValue::ceil(vector<Value*> args) {
  if (!NumberValue::isNumber(args[0]))
    throw new ValueException("ceil() only works on numeric values");

  double val = ((NumberValue*)args[0])->getValue();
  ((NumberValue*)args[0])->setValue(std::ceil(val));
  return args[0];
}
Value* NumberValue::floor(vector<Value*> args) {
  if (!NumberValue::isNumber(args[0]))
    throw new ValueException("floor() only works on numeric values");

  double val = ((NumberValue*)args[0])->getValue();
  ((NumberValue*)args[0])->setValue(std::floor(val));
  return args[0];
}
Value* NumberValue::percentage(vector<Value*> args) {
  double val = ((NumberValue*)args[0])->getValue();
  ((NumberValue*)args[0])->type = Value::PERCENTAGE;
  ((NumberValue*)args[0])->setValue(val * 100);
  return args[0];
}
Value* NumberValue::round(vector<Value*> args) {
  if (!NumberValue::isNumber(args[0]))
    throw new ValueException("round() only works on numeric values");

  double val = ((NumberValue*)args[0])->getValue();
  double decimalplaces = 0;

  if (args.size() > 1)
    decimalplaces = ((NumberValue*)args[0])->getValue();

  val = val * std::pow(10, decimalplaces);
  val = std::floor(val + 0.5);
  val = val / std::pow(10, decimalplaces);
  ((NumberValue*)args[0])->setValue(val);
  return args[0];
}
Value* NumberValue::sqrt(vector<Value*> args) {
  if (!NumberValue::isNumber(args[0]))
    throw new ValueException("sqrt() only works on numeric values");

  double val = ((NumberValue*)args[0])->getValue();
  ((NumberValue*)args[0])->setValue(std::sqrt(val));
  return args[0];
}
Value* NumberValue::abs(vector<Value*> args) {
  if (!NumberValue::isNumber(args[0]))
    throw new ValueException("abs() only works on numeric values");

  double val = ((NumberValue*)args[0])->getValue();
  ((NumberValue*)args[0])->setValue(fabs(val));
  return args[0];
}

Value* NumberValue::sin(vector<Value*> args) {
  if (args[0]->type != Value::NUMBER &&
      args[0]->type != Value::DIMENSION) {
    throw new ValueException("sin() only works on numbers or dimensions");
  }

  NumberValue* arg = ((NumberValue*)args[0]);
  double val = arg->getValue();

  if (arg->type == Value::DIMENSION) {
    if(arg->getUnit().compare("rad") != 0 &&
       arg->getUnit().compare("deg") != 0 &&
       arg->getUnit().compare("grad") != 0 &&
       arg->getUnit().compare("turn") != 0) {
      throw new ValueException("sin() requires rad, deg, grad or turn units.");
    }
    val = UnitValue::angleToRad(val, arg->getUnit());
  }

  arg->setValue(std::sin(val));
  arg->type = Value::NUMBER;
  arg->setUnit("");
  return arg;
}
Value* NumberValue::asin(vector<Value*> args) {
  NumberValue* arg = ((NumberValue*)args[0]);

  arg->setValue(std::asin(arg->getValue()));
  arg->setUnit("rad");
  arg->type = Value::DIMENSION;
  return arg;
}
Value* NumberValue::cos(vector<Value*> args) {
  if (args[0]->type != Value::NUMBER &&
      args[0]->type != Value::DIMENSION) {
    throw new ValueException("cos() only works on numbers or dimensions");
  }
  NumberValue* arg = ((NumberValue*)args[0]);
  double val = arg->getValue();

  if (arg->type == Value::DIMENSION) {
    if(arg->getUnit().compare("rad") != 0 &&
       arg->getUnit().compare("deg") != 0 &&
       arg->getUnit().compare("grad") != 0 &&
       arg->getUnit().compare("turn") != 0) {
      throw new ValueException("cos() requires rad, deg, grad or turn units.");
    }
    val = UnitValue::angleToRad(val, arg->getUnit());
  }

  arg->setValue(std::cos(val));
  arg->type = Value::NUMBER;
  arg->setUnit("");
  return arg;
}
Value* NumberValue::acos(vector<Value*> args) {
  NumberValue* arg = ((NumberValue*)args[0]);

  arg->setValue(std::acos(arg->getValue()));
  arg->setUnit("rad");
  arg->type = Value::DIMENSION;
  return arg;
}
Value* NumberValue::tan(vector<Value*> args) {
  if (args[0]->type != Value::NUMBER &&
      args[0]->type != Value::DIMENSION) {
    throw new ValueException("tan() only works on numbers or dimensions");
  }
  NumberValue* arg = ((NumberValue*)args[0]);
  double val = arg->getValue();

  if (arg->type == Value::DIMENSION) {
    if(arg->getUnit().compare("rad") != 0 &&
       arg->getUnit().compare("deg") != 0 &&
       arg->getUnit().compare("grad") != 0 &&
       arg->getUnit().compare("turn") != 0) {
      throw new ValueException("tan() requires rad, deg, grad or turn units.");
    }
    val = UnitValue::angleToRad(val, arg->getUnit());
  }

  arg->setValue(std::tan(val));
  arg->type = Value::NUMBER;
  arg->setUnit("");
  return arg;
}
Value* NumberValue::atan(vector<Value*> args) {
  NumberValue* arg = ((NumberValue*)args[0]);

  arg->setValue(std::atan(arg->getValue()));
  arg->setUnit("rad");
  arg->type = Value::DIMENSION;
  return arg;
}
Value* NumberValue::pi(vector<Value*> args) {
  (void)args;
  return new NumberValue(new Token("3.141592653589793", Token::NUMBER));
}
Value* NumberValue::pow(vector<Value*> args) {
  if (!NumberValue::isNumber(args[0]))
    throw new ValueException("pow() only works on numeric values");

  double val = ((NumberValue*)args[0])->getValue();
  double exp = ((NumberValue*)args[1])->getValue();
  ((NumberValue*)args[0])->setValue(std::pow(val, exp));
  return args[0];
}
Value* NumberValue::mod(vector<Value*> args) {
  if (!NumberValue::isNumber(args[0]) ||
      !NumberValue::isNumber(args[1]))
    throw new ValueException("mod() only works on numeric values");

  double val1 = ((NumberValue*)args[0])->getValue();
  double val2 = ((NumberValue*)args[1])->getValue();
  ((NumberValue*)args[0])->setValue(std::fmod(val1, val2));
  return args[0];
}
Value* NumberValue::convert(vector<Value*> args) {
  if (!NumberValue::isNumber(args[0]))
    throw new ValueException("convert() only works on numeric values");
  if (args[1]->type != Value::STRING &&
      args[1]->type != Value::UNIT) {
    throw new ValueException("convert() requires a unit \
(or unit as a string)");
  }

  NumberValue* val = (NumberValue*)args[0];
  string unit;

  if (args[1]->type == Value::STRING)
    unit = ((StringValue*)args[1])->getString();
  else
    unit.append(((UnitValue*)args[1])->getUnit());

  val->convert(unit);
  return val;
}
<|endoftext|>


<commit_before>#if _WIN32
#define PLATFORM_WINDOWS
#endif

#include "view/RallyNetView.h"

#if PLATFORM_WINDOWS
#include <WinSock2.h>
//#include <fcntl.h>
#pragma comment(lib, "wsock32.lib")
#else
#include <sys/socket.h>
#include <netinet/in.h>
#include <fcntl.h>
// Not needed here
//#include <sys/types.h>
#include <arpa/inet.h>
//#include <netdb.h>
// close() is in here for newer gcc
#include <unistd.h>
#include <errno.h>
#endif

#include <stdexcept>
#include <map>
#include <climits>

namespace Rally { namespace View {

    namespace {
        const unsigned int MAX_PACKET_SIZE = 255;
        const unsigned int CLIENT_TIMEOUT_DELAY = 40; // seconds

        // Some ugly code to fill in a vector3 to a packet. Should endian-convert from
        // host to network byte order if necessary. (Usually it isn't necessary for float.)
        void writeVector3toPacket(char* packet, const Rally::Vector3 & vector) {
            memcpy(packet + 0*4, &vector.x, 4);
            memcpy(packet + 1*4, &vector.y, 4);
            memcpy(packet + 2*4, &vector.z, 4);
        }

        // Takes a packet with offset pre-added and returns a Vector3. This should
        // convert from network to host byte order if necessary (usually not for float).
        Rally::Vector3 packetToVector3(char* packet) {
            float* a = reinterpret_cast<float*>(packet + 0*4);
            float* b = reinterpret_cast<float*>(packet + 1*4);
            float* c = reinterpret_cast<float*>(packet + 2*4);

            return Rally::Vector3(*a, *b, *c);
        }

        // Windows does it another way. Actually a better way too...
        int getErrno() {
#ifdef PLATFORM_WINDOWS
            return ::WSAGetLastError();
#else
            return errno;
#endif
        }
    }

    RallyNetView::RallyNetView(RallyNetView_NetworkCarListener & listener)
        : socket(0),
        lastSentPacketId(0),
        listener(listener) {
    }

    RallyNetView::~RallyNetView() {
    #ifdef PLATFORM_WINDOWS
        if(socket) ::closesocket(socket);
        ::WSACleanup();
    #else
        if(socket) ::close(socket);
    #endif
    }

    void RallyNetView::initialize(const std::string & serverAddress, unsigned short serverPort, Model::Car & playerCar) {
        this->playerCar = playerCar;

    #ifdef PLATFORM_WINDOWS
        WSADATA WsaData;
        if(::WSAStartup(MAKEWORD(2, 2), &WsaData) != NO_ERROR) {
            throw std::runtime_error("WSAStartup failed!");
        }
    #endif

        socket = ::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
        if(socket <= 0) {
            throw std::runtime_error("Could not connect!");
        }

        sockaddr_in address;
        memset(&address, 0, sizeof(address));
        address.sin_family = AF_INET;
        // TODO: use getaddrinfo() if we need more flexibility in the future
        address.sin_addr.s_addr = inet_addr(serverAddress.c_str());
        address.sin_port = htons(serverPort);

        // Note that connect assigns the local server:port choosen by the OS to this socket!
        if(::connect(socket, (const sockaddr*) & address, sizeof(sockaddr_in)) < 0) {
            // Throwing here is ok, since we're using UDP so no connection will actually happen,
            // meaning it won't fail because of network problems.
            throw std::runtime_error("Could not connect to server!");
        }

        bool nonBlockSucceded = false;
#ifdef PLATFORM_WINDOWS
        DWORD nonBlocking = 1;
        nonBlockSucceded = (ioctlsocket(socket, FIONBIO, &nonBlocking) == 0);
#else
        int flags = fcntl(socket, F_GETFL, 0);
        flags &= ~O_NONBLOCK;
        nonBlockSucceded = (fcntl(socket, F_SETFL, flags) == 0);
#endif

        if(!nonBlockSucceded) {
            throw std::runtime_error("Could not make socket non-blocking!");
        }
    }

    void RallyNetView::update() {
        // TODO: Rate limit!
        updatePutCar();
        updateGetCars();
    }

    void RallyNetView::updatePutCar() {
        char packet[40];

        packet[0] = 1; // Type = 1

        // sequence id
        unsigned short packetId = htons(++lastSentPacketId);
        memcpy(packet + 1, &packetId, 2);

        packet[3]  = 0; // Car type = 0

        writeVector3toPacket(packet + 4 + 0*3*4, playerCar.getPosition());
        writeVector3toPacket(packet + 4 + 1*3*4, playerCar.getOrientation());
        writeVector3toPacket(packet + 4 + 2*3*4, playerCar.getVelocity());

        int status = ::send(socket, packet, sizeof(packet), 0x00000000);
        int failedBecauseNonBlocking = (getErrno() == EWOULDBLOCK || getErrno() == EAGAIN);
        if(status < 0 && !failedBecauseNonBlocking) {
            // Note that we don't care if we couldn't send all bytes above...
            throw std::runtime_error("Socket error when sending position update to server.");
        }
    }

    void RallyNetView::updateGetCars() {
        char packet[MAX_PACKET_SIZE];
        while(true) {
            int receivedBytes = ::recv(socket, packet, MAX_PACKET_SIZE, 0x00000000);
            if(receivedBytes == 42 && packet[0] == 1) { // complete packetType == 1
                unsigned short sequenceId;
                memcpy(&sequenceId, packet+1, 2);
                sequenceId = ntohs(sequenceId);

                unsigned short playerId;
                memcpy(&playerId, packet + 4, 2);
                playerId = ntohs(playerId);

                std::map<unsigned short, RallyNetView_InternalClient>::iterator sendingClientIterator = clients.find(playerId);

                // If client was found as previous sender, make sure this packet is relevant
                if(sendingClientIterator != clients.end()) {
                     if(sendingClientIterator->second.lastPositionSequenceId > sequenceId) {
                        // Possible wrap-around?
                        if(sendingClientIterator->second.lastPositionSequenceId > USHRT_MAX/2 && sequenceId < USHRT_MAX/2) {
                            // If we use local 32-bit seq.id like the server in the future: lastPositionPacketId += USHRT_MAX + 1;
                        } else {
                            // Drop packet!
                            continue;
                        }
                    }
                }

                // Lazily create the internal client if not already existant.
                RallyNetView_InternalClient& internalClient = (sendingClientIterator == clients.end()) ? clients[playerId] : sendingClientIterator->second;

                internalClient.lastPositionSequenceId = sequenceId;
                internalClient.lastPacketArrival = time(0);

                char carType = packet[3];

                listener.carUpdated(
                    playerId,
                    packetToVector3(packet + 6 + 0*4*3),
                    packetToVector3(packet + 6 + 1*4*3),
                    packetToVector3(packet + 6 + 2*4*3));
            }
        }
    }
} }
<commit_msg>Fixed if to ifdef WIN32 check<commit_after>#if _WIN32
#define PLATFORM_WINDOWS
#endif

#include "view/RallyNetView.h"

#ifdef PLATFORM_WINDOWS
#include <WinSock2.h>
//#include <fcntl.h>
#pragma comment(lib, "wsock32.lib")
#else
#include <sys/socket.h>
#include <netinet/in.h>
#include <fcntl.h>
// Not needed here
//#include <sys/types.h>
#include <arpa/inet.h>
//#include <netdb.h>
// close() is in here for newer gcc
#include <unistd.h>
#include <errno.h>
#endif

#include <stdexcept>
#include <map>
#include <climits>

namespace Rally { namespace View {

    namespace {
        const unsigned int MAX_PACKET_SIZE = 255;
        const unsigned int CLIENT_TIMEOUT_DELAY = 40; // seconds

        // Some ugly code to fill in a vector3 to a packet. Should endian-convert from
        // host to network byte order if necessary. (Usually it isn't necessary for float.)
        void writeVector3toPacket(char* packet, const Rally::Vector3 & vector) {
            memcpy(packet + 0*4, &vector.x, 4);
            memcpy(packet + 1*4, &vector.y, 4);
            memcpy(packet + 2*4, &vector.z, 4);
        }

        // Takes a packet with offset pre-added and returns a Vector3. This should
        // convert from network to host byte order if necessary (usually not for float).
        Rally::Vector3 packetToVector3(char* packet) {
            float* a = reinterpret_cast<float*>(packet + 0*4);
            float* b = reinterpret_cast<float*>(packet + 1*4);
            float* c = reinterpret_cast<float*>(packet + 2*4);

            return Rally::Vector3(*a, *b, *c);
        }

        // Windows does it another way. Actually a better way too...
        int getErrno() {
#ifdef PLATFORM_WINDOWS
            return ::WSAGetLastError();
#else
            return errno;
#endif
        }
    }

    RallyNetView::RallyNetView(RallyNetView_NetworkCarListener & listener)
        : socket(0),
        lastSentPacketId(0),
        listener(listener) {
    }

    RallyNetView::~RallyNetView() {
    #ifdef PLATFORM_WINDOWS
        if(socket) ::closesocket(socket);
        ::WSACleanup();
    #else
        if(socket) ::close(socket);
    #endif
    }

    void RallyNetView::initialize(const std::string & serverAddress, unsigned short serverPort, Model::Car & playerCar) {
        this->playerCar = playerCar;

    #ifdef PLATFORM_WINDOWS
        WSADATA WsaData;
        if(::WSAStartup(MAKEWORD(2, 2), &WsaData) != NO_ERROR) {
            throw std::runtime_error("WSAStartup failed!");
        }
    #endif

        socket = ::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
        if(socket <= 0) {
            throw std::runtime_error("Could not connect!");
        }

        sockaddr_in address;
        memset(&address, 0, sizeof(address));
        address.sin_family = AF_INET;
        // TODO: use getaddrinfo() if we need more flexibility in the future
        address.sin_addr.s_addr = inet_addr(serverAddress.c_str());
        address.sin_port = htons(serverPort);

        // Note that connect assigns the local server:port choosen by the OS to this socket!
        if(::connect(socket, (const sockaddr*) & address, sizeof(sockaddr_in)) < 0) {
            // Throwing here is ok, since we're using UDP so no connection will actually happen,
            // meaning it won't fail because of network problems.
            throw std::runtime_error("Could not connect to server!");
        }

        bool nonBlockSucceded = false;
#ifdef PLATFORM_WINDOWS
        DWORD nonBlocking = 1;
        nonBlockSucceded = (ioctlsocket(socket, FIONBIO, &nonBlocking) == 0);
#else
        int flags = fcntl(socket, F_GETFL, 0);
        flags &= ~O_NONBLOCK;
        nonBlockSucceded = (fcntl(socket, F_SETFL, flags) == 0);
#endif

        if(!nonBlockSucceded) {
            throw std::runtime_error("Could not make socket non-blocking!");
        }
    }

    void RallyNetView::update() {
        // TODO: Rate limit!
        updatePutCar();
        updateGetCars();
    }

    void RallyNetView::updatePutCar() {
        char packet[40];

        packet[0] = 1; // Type = 1

        // sequence id
        unsigned short packetId = htons(++lastSentPacketId);
        memcpy(packet + 1, &packetId, 2);

        packet[3]  = 0; // Car type = 0

        writeVector3toPacket(packet + 4 + 0*3*4, playerCar.getPosition());
        writeVector3toPacket(packet + 4 + 1*3*4, playerCar.getOrientation());
        writeVector3toPacket(packet + 4 + 2*3*4, playerCar.getVelocity());

        int status = ::send(socket, packet, sizeof(packet), 0x00000000);
        int failedBecauseNonBlocking = (getErrno() == EWOULDBLOCK || getErrno() == EAGAIN);
        if(status < 0 && !failedBecauseNonBlocking) {
            // Note that we don't care if we couldn't send all bytes above...
            throw std::runtime_error("Socket error when sending position update to server.");
        }
    }

    void RallyNetView::updateGetCars() {
        char packet[MAX_PACKET_SIZE];
        while(true) {
            int receivedBytes = ::recv(socket, packet, MAX_PACKET_SIZE, 0x00000000);
            if(receivedBytes == 42 && packet[0] == 1) { // complete packetType == 1
                unsigned short sequenceId;
                memcpy(&sequenceId, packet+1, 2);
                sequenceId = ntohs(sequenceId);

                unsigned short playerId;
                memcpy(&playerId, packet + 4, 2);
                playerId = ntohs(playerId);

                std::map<unsigned short, RallyNetView_InternalClient>::iterator sendingClientIterator = clients.find(playerId);

                // If client was found as previous sender, make sure this packet is relevant
                if(sendingClientIterator != clients.end()) {
                     if(sendingClientIterator->second.lastPositionSequenceId > sequenceId) {
                        // Possible wrap-around?
                        if(sendingClientIterator->second.lastPositionSequenceId > USHRT_MAX/2 && sequenceId < USHRT_MAX/2) {
                            // If we use local 32-bit seq.id like the server in the future: lastPositionPacketId += USHRT_MAX + 1;
                        } else {
                            // Drop packet!
                            continue;
                        }
                    }
                }

                // Lazily create the internal client if not already existant.
                RallyNetView_InternalClient& internalClient = (sendingClientIterator == clients.end()) ? clients[playerId] : sendingClientIterator->second;

                internalClient.lastPositionSequenceId = sequenceId;
                internalClient.lastPacketArrival = time(0);

                char carType = packet[3];

                listener.carUpdated(
                    playerId,
                    packetToVector3(packet + 6 + 0*4*3),
                    packetToVector3(packet + 6 + 1*4*3),
                    packetToVector3(packet + 6 + 2*4*3));
            }
        }
    }
} }
<|endoftext|>


<commit_before>#include <node.h>
#include "gn_player.h"
#include "gn_playlist.h"
#include "gn_playlist_item.h"

using namespace v8;

GNPlayer::GNPlayer() {};
GNPlayer::~GNPlayer() {
    groove_player_destroy(player);
};

Persistent<Function> GNPlayer::constructor;

template <typename target_t, typename func_t>
static void AddGetter(target_t tpl, const char* name, func_t fn) {
    tpl->PrototypeTemplate()->SetAccessor(String::NewSymbol(name), fn);
}

template <typename target_t, typename func_t>
static void AddMethod(target_t tpl, const char* name, func_t fn) {
    tpl->PrototypeTemplate()->Set(String::NewSymbol(name),
            FunctionTemplate::New(fn)->GetFunction());
}

void GNPlayer::Init() {
    // Prepare constructor template
    Local<FunctionTemplate> tpl = FunctionTemplate::New(New);
    tpl->SetClassName(String::NewSymbol("GroovePlayer"));
    tpl->InstanceTemplate()->SetInternalFieldCount(2);
    // Fields
    AddGetter(tpl, "id", GetId);
    AddGetter(tpl, "playlist", GetPlaylist);
    // Methods
    AddMethod(tpl, "attach", Attach);
    AddMethod(tpl, "detach", Detach);
    AddMethod(tpl, "position", Position);

    constructor = Persistent<Function>::New(tpl->GetFunction());
}

Handle<Value> GNPlayer::New(const Arguments& args) {
    HandleScope scope;

    GNPlayer *obj = new GNPlayer();
    obj->Wrap(args.This());

    return scope.Close(args.This());
}

Handle<Value> GNPlayer::NewInstance(GroovePlayer *player) {
    HandleScope scope;

    Local<Object> instance = constructor->NewInstance();

    GNPlayer *gn_player = node::ObjectWrap::Unwrap<GNPlayer>(instance);
    gn_player->player = player;

    return scope.Close(instance);
}

Handle<Value> GNPlayer::GetId(Local<String> property, const AccessorInfo &info) {
    HandleScope scope;
    GNPlayer *gn_player = node::ObjectWrap::Unwrap<GNPlayer>(info.This());
    char buf[64];
    snprintf(buf, sizeof(buf), "%p", gn_player->player);
    return scope.Close(String::New(buf));
}

Handle<Value> GNPlayer::GetPlaylist(Local<String> property,
        const AccessorInfo &info)
{
    HandleScope scope;
    GNPlayer *gn_player = node::ObjectWrap::Unwrap<GNPlayer>(info.This());
    GroovePlaylist *playlist = gn_player->player->playlist;
    if (playlist) {
        return scope.Close(GNPlaylist::NewInstance(playlist));
    } else {
        return scope.Close(Null());
    }
}

Handle<Value> GNPlayer::Position(const Arguments& args) {
    HandleScope scope;
    GNPlayer *gn_player = node::ObjectWrap::Unwrap<GNPlayer>(args.This());
    GroovePlaylistItem *item;
    double pos;
    groove_player_position(gn_player->player, &item, &pos);
    Local<Object> obj = Object::New();
    obj->Set(String::NewSymbol("pos"), Number::New(pos));
    if (item) {
        obj->Set(String::NewSymbol("item"), GNPlaylistItem::NewInstance(item));
    } else {
        obj->Set(String::NewSymbol("item"), Null());
    }
    return scope.Close(obj);
}

struct AttachReq {
    uv_work_t req;
    Persistent<Function> callback;
    GroovePlayer *player;
    GroovePlaylist *playlist;
    int errcode;
    Persistent<Object> instance;
    String::Utf8Value *device_name;
    GNPlayer::EventContext *event_context;
};

static void EventAsyncCb(uv_async_t *handle, int status) {
    HandleScope scope;

    GNPlayer::EventContext *context = reinterpret_cast<GNPlayer::EventContext *>(handle->data);

    // flush events
    GroovePlayerEvent event;

    const unsigned argc = 1;
    Handle<Value> argv[argc];
    while (groove_player_event_get(context->player, &event, 0) > 0) {
        argv[0] = Number::New(event.type);

        TryCatch try_catch;
        context->event_cb->Call(Context::GetCurrent()->Global(), argc, argv);

        if (try_catch.HasCaught()) {
            node::FatalException(try_catch);
        }
    }

    uv_cond_signal(&context->cond);
}

static void EventThreadEntry(void *arg) {
    GNPlayer::EventContext *context = reinterpret_cast<GNPlayer::EventContext *>(arg);
    while (groove_player_event_peek(context->player, 1) > 0) {
        uv_async_send(&context->event_async);
        uv_mutex_lock(&context->mutex);
        uv_cond_wait(&context->cond, &context->mutex);
        uv_mutex_unlock(&context->mutex);
    }
}

static void AttachAsync(uv_work_t *req) {
    AttachReq *r = reinterpret_cast<AttachReq *>(req->data);

    if (r->device_name) {
        r->player->device_name = **r->device_name;
    } else {
        r->player->device_name = NULL;
    }
    r->errcode = groove_player_attach(r->player, r->playlist);
    if (r->device_name) {
        delete r->device_name;
        r->device_name = NULL;
    }

    GNPlayer::EventContext *context = r->event_context;

    uv_cond_init(&context->cond);
    uv_mutex_init(&context->mutex);

    uv_async_init(uv_default_loop(), &context->event_async, EventAsyncCb);
    context->event_async.data = context;

    uv_thread_create(&context->event_thread, EventThreadEntry, context);
}

static void AttachAfter(uv_work_t *req) {
    HandleScope scope;
    AttachReq *r = reinterpret_cast<AttachReq *>(req->data);

    const unsigned argc = 1;
    Handle<Value> argv[argc];
    if (r->errcode < 0) {
        argv[0] = Exception::Error(String::New("player attach failed"));
    } else {
        argv[0] = Null();

        Local<Object> actualAudioFormat = Object::New();
        actualAudioFormat->Set(String::NewSymbol("sampleRate"),
                Number::New(r->player->actual_audio_format.sample_rate));
        actualAudioFormat->Set(String::NewSymbol("channelLayout"),
                Number::New(r->player->actual_audio_format.channel_layout));
        actualAudioFormat->Set(String::NewSymbol("sampleFormat"),
                Number::New(r->player->actual_audio_format.sample_fmt));

        r->instance->Set(String::NewSymbol("actualAudioFormat"), actualAudioFormat);
    }

    TryCatch try_catch;
    r->callback->Call(Context::GetCurrent()->Global(), argc, argv);

    delete r;

    if (try_catch.HasCaught()) {
        node::FatalException(try_catch);
    }
}

Handle<Value> GNPlayer::Create(const Arguments& args) {
    HandleScope scope;

    if (args.Length() < 1 || !args[0]->IsFunction()) {
        ThrowException(Exception::TypeError(String::New("Expected function arg[0]")));
        return scope.Close(Undefined());
    }

    GroovePlayer *player = groove_player_create();
    Handle<Object> instance = NewInstance(player)->ToObject();
    GNPlayer *gn_player = node::ObjectWrap::Unwrap<GNPlayer>(instance);
    EventContext *context = new EventContext;
    gn_player->event_context = context;
    context->event_cb = Persistent<Function>::New(Local<Function>::Cast(args[0]));
    context->player = player;

    // set properties on the instance with default values from
    // GroovePlayer struct
    Local<Object> targetAudioFormat = Object::New();
    targetAudioFormat->Set(String::NewSymbol("sampleRate"),
            Number::New(player->target_audio_format.sample_rate));
    targetAudioFormat->Set(String::NewSymbol("channelLayout"),
            Number::New(player->target_audio_format.channel_layout));
    targetAudioFormat->Set(String::NewSymbol("sampleFormat"),
            Number::New(player->target_audio_format.sample_fmt));

    instance->Set(String::NewSymbol("deviceName"), Null());
    instance->Set(String::NewSymbol("actualAudioFormat"), Null());
    instance->Set(String::NewSymbol("targetAudioFormat"), targetAudioFormat);
    instance->Set(String::NewSymbol("deviceBufferSize"),
            Number::New(player->device_buffer_size));
    instance->Set(String::NewSymbol("sinkBufferSize"),
            Number::New(player->sink_buffer_size));

    return scope.Close(instance);
}

Handle<Value> GNPlayer::Attach(const Arguments& args) {
    HandleScope scope;

    GNPlayer *gn_player = node::ObjectWrap::Unwrap<GNPlayer>(args.This());

    if (args.Length() < 1 || !args[0]->IsObject()) {
        ThrowException(Exception::TypeError(String::New("Expected object arg[0]")));
        return scope.Close(Undefined());
    }
    if (args.Length() < 2 || !args[1]->IsFunction()) {
        ThrowException(Exception::TypeError(String::New("Expected function arg[1]")));
        return scope.Close(Undefined());
    }

    Local<Object> instance = args.This();
    Local<Value> targetAudioFormatValue = instance->Get(String::NewSymbol("targetAudioFormat"));
    if (!targetAudioFormatValue->IsObject()) {
        ThrowException(Exception::TypeError(String::New("Expected targetAudioFormat to be an object")));
        return scope.Close(Undefined());
    }

    GNPlaylist *gn_playlist = node::ObjectWrap::Unwrap<GNPlaylist>(args[0]->ToObject());

    AttachReq *request = new AttachReq;

    request->req.data = request;
    request->callback = Persistent<Function>::New(Local<Function>::Cast(args[1]));
    request->instance = Persistent<Object>::New(args.This());
    request->playlist = gn_playlist->playlist;
    GroovePlayer *player = gn_player->player;
    request->player = player;
    request->event_context = gn_player->event_context;

    // copy the properties from our instance to the player
    Local<Value> deviceName = instance->Get(String::NewSymbol("deviceName"));

    if (deviceName->IsNull() || deviceName->IsUndefined()) {
        request->device_name = NULL;
    } else {
        request->device_name = new String::Utf8Value(deviceName->ToString());
    }
    Local<Object> targetAudioFormat = targetAudioFormatValue->ToObject();
    Local<Value> sampleRate = targetAudioFormat->Get(String::NewSymbol("sampleRate"));
    double sample_rate = sampleRate->NumberValue();
    double channel_layout = targetAudioFormat->Get(String::NewSymbol("channelLayout"))->NumberValue();
    double sample_fmt = targetAudioFormat->Get(String::NewSymbol("sampleFormat"))->NumberValue();
    player->target_audio_format.sample_rate = (int)sample_rate;
    player->target_audio_format.channel_layout = (int)channel_layout;
    player->target_audio_format.sample_fmt = (enum GrooveSampleFormat)(int)sample_fmt;

    double device_buffer_size = instance->Get(String::NewSymbol("deviceBufferSize"))->NumberValue();
    player->device_buffer_size = (int)device_buffer_size;

    double sink_buffer_size = instance->Get(String::NewSymbol("sinkBufferSize"))->NumberValue();
    player->sink_buffer_size = (int)sink_buffer_size;

    uv_queue_work(uv_default_loop(), &request->req, AttachAsync,
            (uv_after_work_cb)AttachAfter);

    return scope.Close(Undefined());
}

struct DetachReq {
    uv_work_t req;
    GroovePlayer *player;
    Persistent<Function> callback;
    int errcode;
    GNPlayer::EventContext *event_context;
};

static void DetachAsyncFree(uv_handle_t *handle) {
    GNPlayer::EventContext *context = reinterpret_cast<GNPlayer::EventContext *>(handle->data);
    delete context;
}

static void DetachAsync(uv_work_t *req) {
    DetachReq *r = reinterpret_cast<DetachReq *>(req->data);
    r->errcode = groove_player_detach(r->player);
    uv_cond_signal(&r->event_context->cond);
    uv_thread_join(&r->event_context->event_thread);
    uv_cond_destroy(&r->event_context->cond);
    uv_mutex_destroy(&r->event_context->mutex);
    uv_close(reinterpret_cast<uv_handle_t*>(&r->event_context->event_async), DetachAsyncFree);
}

static void DetachAfter(uv_work_t *req) {
    HandleScope scope;
    DetachReq *r = reinterpret_cast<DetachReq *>(req->data);

    const unsigned argc = 1;
    Handle<Value> argv[argc];
    if (r->errcode < 0) {
        argv[0] = Exception::Error(String::New("player detach failed"));
    } else {
        argv[0] = Null();
    }
    TryCatch try_catch;
    r->callback->Call(Context::GetCurrent()->Global(), argc, argv);

    delete r;

    if (try_catch.HasCaught()) {
        node::FatalException(try_catch);
    }
}

Handle<Value> GNPlayer::Detach(const Arguments& args) {
    HandleScope scope;
    GNPlayer *gn_player = node::ObjectWrap::Unwrap<GNPlayer>(args.This());

    if (args.Length() < 1 || !args[0]->IsFunction()) {
        ThrowException(Exception::TypeError(String::New("Expected function arg[0]")));
        return scope.Close(Undefined());
    }

    DetachReq *request = new DetachReq;

    request->req.data = request;
    request->callback = Persistent<Function>::New(Local<Function>::Cast(args[0]));
    request->player = gn_player->player;
    request->event_context = gn_player->event_context;

    uv_queue_work(uv_default_loop(), &request->req, DetachAsync,
            (uv_after_work_cb)DetachAfter);

    return scope.Close(Undefined());
}
<commit_msg>fix segfault when detaching and re-attaching a player<commit_after>#include <node.h>
#include "gn_player.h"
#include "gn_playlist.h"
#include "gn_playlist_item.h"

using namespace v8;

GNPlayer::GNPlayer() {};
GNPlayer::~GNPlayer() {
    groove_player_destroy(player);
    delete event_context;
};

Persistent<Function> GNPlayer::constructor;

template <typename target_t, typename func_t>
static void AddGetter(target_t tpl, const char* name, func_t fn) {
    tpl->PrototypeTemplate()->SetAccessor(String::NewSymbol(name), fn);
}

template <typename target_t, typename func_t>
static void AddMethod(target_t tpl, const char* name, func_t fn) {
    tpl->PrototypeTemplate()->Set(String::NewSymbol(name),
            FunctionTemplate::New(fn)->GetFunction());
}

void GNPlayer::Init() {
    // Prepare constructor template
    Local<FunctionTemplate> tpl = FunctionTemplate::New(New);
    tpl->SetClassName(String::NewSymbol("GroovePlayer"));
    tpl->InstanceTemplate()->SetInternalFieldCount(2);
    // Fields
    AddGetter(tpl, "id", GetId);
    AddGetter(tpl, "playlist", GetPlaylist);
    // Methods
    AddMethod(tpl, "attach", Attach);
    AddMethod(tpl, "detach", Detach);
    AddMethod(tpl, "position", Position);

    constructor = Persistent<Function>::New(tpl->GetFunction());
}

Handle<Value> GNPlayer::New(const Arguments& args) {
    HandleScope scope;

    GNPlayer *obj = new GNPlayer();
    obj->Wrap(args.This());

    return scope.Close(args.This());
}

Handle<Value> GNPlayer::NewInstance(GroovePlayer *player) {
    HandleScope scope;

    Local<Object> instance = constructor->NewInstance();

    GNPlayer *gn_player = node::ObjectWrap::Unwrap<GNPlayer>(instance);
    gn_player->player = player;

    return scope.Close(instance);
}

Handle<Value> GNPlayer::GetId(Local<String> property, const AccessorInfo &info) {
    HandleScope scope;
    GNPlayer *gn_player = node::ObjectWrap::Unwrap<GNPlayer>(info.This());
    char buf[64];
    snprintf(buf, sizeof(buf), "%p", gn_player->player);
    return scope.Close(String::New(buf));
}

Handle<Value> GNPlayer::GetPlaylist(Local<String> property,
        const AccessorInfo &info)
{
    HandleScope scope;
    GNPlayer *gn_player = node::ObjectWrap::Unwrap<GNPlayer>(info.This());
    GroovePlaylist *playlist = gn_player->player->playlist;
    if (playlist) {
        return scope.Close(GNPlaylist::NewInstance(playlist));
    } else {
        return scope.Close(Null());
    }
}

Handle<Value> GNPlayer::Position(const Arguments& args) {
    HandleScope scope;
    GNPlayer *gn_player = node::ObjectWrap::Unwrap<GNPlayer>(args.This());
    GroovePlaylistItem *item;
    double pos;
    groove_player_position(gn_player->player, &item, &pos);
    Local<Object> obj = Object::New();
    obj->Set(String::NewSymbol("pos"), Number::New(pos));
    if (item) {
        obj->Set(String::NewSymbol("item"), GNPlaylistItem::NewInstance(item));
    } else {
        obj->Set(String::NewSymbol("item"), Null());
    }
    return scope.Close(obj);
}

struct AttachReq {
    uv_work_t req;
    Persistent<Function> callback;
    GroovePlayer *player;
    GroovePlaylist *playlist;
    int errcode;
    Persistent<Object> instance;
    String::Utf8Value *device_name;
    GNPlayer::EventContext *event_context;
};

static void EventAsyncCb(uv_async_t *handle, int status) {
    HandleScope scope;

    GNPlayer::EventContext *context = reinterpret_cast<GNPlayer::EventContext *>(handle->data);

    // flush events
    GroovePlayerEvent event;

    const unsigned argc = 1;
    Handle<Value> argv[argc];
    while (groove_player_event_get(context->player, &event, 0) > 0) {
        argv[0] = Number::New(event.type);

        TryCatch try_catch;
        context->event_cb->Call(Context::GetCurrent()->Global(), argc, argv);

        if (try_catch.HasCaught()) {
            node::FatalException(try_catch);
        }
    }

    uv_cond_signal(&context->cond);
}

static void EventThreadEntry(void *arg) {
    GNPlayer::EventContext *context = reinterpret_cast<GNPlayer::EventContext *>(arg);
    while (groove_player_event_peek(context->player, 1) > 0) {
        uv_async_send(&context->event_async);
        uv_mutex_lock(&context->mutex);
        uv_cond_wait(&context->cond, &context->mutex);
        uv_mutex_unlock(&context->mutex);
    }
}

static void AttachAsync(uv_work_t *req) {
    AttachReq *r = reinterpret_cast<AttachReq *>(req->data);

    if (r->device_name) {
        r->player->device_name = **r->device_name;
    } else {
        r->player->device_name = NULL;
    }
    r->errcode = groove_player_attach(r->player, r->playlist);
    if (r->device_name) {
        delete r->device_name;
        r->device_name = NULL;
    }

    GNPlayer::EventContext *context = r->event_context;

    uv_cond_init(&context->cond);
    uv_mutex_init(&context->mutex);

    uv_async_init(uv_default_loop(), &context->event_async, EventAsyncCb);
    context->event_async.data = context;

    uv_thread_create(&context->event_thread, EventThreadEntry, context);
}

static void AttachAfter(uv_work_t *req) {
    HandleScope scope;
    AttachReq *r = reinterpret_cast<AttachReq *>(req->data);

    const unsigned argc = 1;
    Handle<Value> argv[argc];
    if (r->errcode < 0) {
        argv[0] = Exception::Error(String::New("player attach failed"));
    } else {
        argv[0] = Null();

        Local<Object> actualAudioFormat = Object::New();
        actualAudioFormat->Set(String::NewSymbol("sampleRate"),
                Number::New(r->player->actual_audio_format.sample_rate));
        actualAudioFormat->Set(String::NewSymbol("channelLayout"),
                Number::New(r->player->actual_audio_format.channel_layout));
        actualAudioFormat->Set(String::NewSymbol("sampleFormat"),
                Number::New(r->player->actual_audio_format.sample_fmt));

        r->instance->Set(String::NewSymbol("actualAudioFormat"), actualAudioFormat);
    }

    TryCatch try_catch;
    r->callback->Call(Context::GetCurrent()->Global(), argc, argv);

    delete r;

    if (try_catch.HasCaught()) {
        node::FatalException(try_catch);
    }
}

Handle<Value> GNPlayer::Create(const Arguments& args) {
    HandleScope scope;

    if (args.Length() < 1 || !args[0]->IsFunction()) {
        ThrowException(Exception::TypeError(String::New("Expected function arg[0]")));
        return scope.Close(Undefined());
    }

    GroovePlayer *player = groove_player_create();
    Handle<Object> instance = NewInstance(player)->ToObject();
    GNPlayer *gn_player = node::ObjectWrap::Unwrap<GNPlayer>(instance);
    EventContext *context = new EventContext;
    gn_player->event_context = context;
    context->event_cb = Persistent<Function>::New(Local<Function>::Cast(args[0]));
    context->player = player;

    // set properties on the instance with default values from
    // GroovePlayer struct
    Local<Object> targetAudioFormat = Object::New();
    targetAudioFormat->Set(String::NewSymbol("sampleRate"),
            Number::New(player->target_audio_format.sample_rate));
    targetAudioFormat->Set(String::NewSymbol("channelLayout"),
            Number::New(player->target_audio_format.channel_layout));
    targetAudioFormat->Set(String::NewSymbol("sampleFormat"),
            Number::New(player->target_audio_format.sample_fmt));

    instance->Set(String::NewSymbol("deviceName"), Null());
    instance->Set(String::NewSymbol("actualAudioFormat"), Null());
    instance->Set(String::NewSymbol("targetAudioFormat"), targetAudioFormat);
    instance->Set(String::NewSymbol("deviceBufferSize"),
            Number::New(player->device_buffer_size));
    instance->Set(String::NewSymbol("sinkBufferSize"),
            Number::New(player->sink_buffer_size));

    return scope.Close(instance);
}

Handle<Value> GNPlayer::Attach(const Arguments& args) {
    HandleScope scope;

    GNPlayer *gn_player = node::ObjectWrap::Unwrap<GNPlayer>(args.This());

    if (args.Length() < 1 || !args[0]->IsObject()) {
        ThrowException(Exception::TypeError(String::New("Expected object arg[0]")));
        return scope.Close(Undefined());
    }
    if (args.Length() < 2 || !args[1]->IsFunction()) {
        ThrowException(Exception::TypeError(String::New("Expected function arg[1]")));
        return scope.Close(Undefined());
    }

    Local<Object> instance = args.This();
    Local<Value> targetAudioFormatValue = instance->Get(String::NewSymbol("targetAudioFormat"));
    if (!targetAudioFormatValue->IsObject()) {
        ThrowException(Exception::TypeError(String::New("Expected targetAudioFormat to be an object")));
        return scope.Close(Undefined());
    }

    GNPlaylist *gn_playlist = node::ObjectWrap::Unwrap<GNPlaylist>(args[0]->ToObject());

    AttachReq *request = new AttachReq;

    request->req.data = request;
    request->callback = Persistent<Function>::New(Local<Function>::Cast(args[1]));
    request->instance = Persistent<Object>::New(args.This());
    request->playlist = gn_playlist->playlist;
    GroovePlayer *player = gn_player->player;
    request->player = player;
    request->event_context = gn_player->event_context;

    // copy the properties from our instance to the player
    Local<Value> deviceName = instance->Get(String::NewSymbol("deviceName"));

    if (deviceName->IsNull() || deviceName->IsUndefined()) {
        request->device_name = NULL;
    } else {
        request->device_name = new String::Utf8Value(deviceName->ToString());
    }
    Local<Object> targetAudioFormat = targetAudioFormatValue->ToObject();
    Local<Value> sampleRate = targetAudioFormat->Get(String::NewSymbol("sampleRate"));
    double sample_rate = sampleRate->NumberValue();
    double channel_layout = targetAudioFormat->Get(String::NewSymbol("channelLayout"))->NumberValue();
    double sample_fmt = targetAudioFormat->Get(String::NewSymbol("sampleFormat"))->NumberValue();
    player->target_audio_format.sample_rate = (int)sample_rate;
    player->target_audio_format.channel_layout = (int)channel_layout;
    player->target_audio_format.sample_fmt = (enum GrooveSampleFormat)(int)sample_fmt;

    double device_buffer_size = instance->Get(String::NewSymbol("deviceBufferSize"))->NumberValue();
    player->device_buffer_size = (int)device_buffer_size;

    double sink_buffer_size = instance->Get(String::NewSymbol("sinkBufferSize"))->NumberValue();
    player->sink_buffer_size = (int)sink_buffer_size;

    uv_queue_work(uv_default_loop(), &request->req, AttachAsync,
            (uv_after_work_cb)AttachAfter);

    return scope.Close(Undefined());
}

struct DetachReq {
    uv_work_t req;
    GroovePlayer *player;
    Persistent<Function> callback;
    int errcode;
    GNPlayer::EventContext *event_context;
};

static void DetachAsyncFree(uv_handle_t *handle) {
}

static void DetachAsync(uv_work_t *req) {
    DetachReq *r = reinterpret_cast<DetachReq *>(req->data);
    r->errcode = groove_player_detach(r->player);
    uv_cond_signal(&r->event_context->cond);
    uv_thread_join(&r->event_context->event_thread);
    uv_cond_destroy(&r->event_context->cond);
    uv_mutex_destroy(&r->event_context->mutex);
    uv_close(reinterpret_cast<uv_handle_t*>(&r->event_context->event_async), DetachAsyncFree);
}

static void DetachAfter(uv_work_t *req) {
    HandleScope scope;
    DetachReq *r = reinterpret_cast<DetachReq *>(req->data);

    const unsigned argc = 1;
    Handle<Value> argv[argc];
    if (r->errcode < 0) {
        argv[0] = Exception::Error(String::New("player detach failed"));
    } else {
        argv[0] = Null();
    }
    TryCatch try_catch;
    r->callback->Call(Context::GetCurrent()->Global(), argc, argv);

    delete r;

    if (try_catch.HasCaught()) {
        node::FatalException(try_catch);
    }
}

Handle<Value> GNPlayer::Detach(const Arguments& args) {
    HandleScope scope;
    GNPlayer *gn_player = node::ObjectWrap::Unwrap<GNPlayer>(args.This());

    if (args.Length() < 1 || !args[0]->IsFunction()) {
        ThrowException(Exception::TypeError(String::New("Expected function arg[0]")));
        return scope.Close(Undefined());
    }

    DetachReq *request = new DetachReq;

    request->req.data = request;
    request->callback = Persistent<Function>::New(Local<Function>::Cast(args[0]));
    request->player = gn_player->player;
    request->event_context = gn_player->event_context;

    uv_queue_work(uv_default_loop(), &request->req, DetachAsync,
            (uv_after_work_cb)DetachAfter);

    return scope.Close(Undefined());
}
<|endoftext|>