<commit_before>// Copyright 2010 Google Inc.  All Rights Reserved
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

#include <boost/thread/mutex.hpp>
#include <algorithm>

#include "util/debug-util.h"
#include "util/memory/arena.h"

using std::copy;
using std::max;
using std::min;
using std::reverse;
using std::sort;
using std::swap;

DEFINE_int64(arena_warn_threshold_bytes, 256*1024*1024,
             "Number of bytes beyond which to emit a warning for a large arena");

namespace kudu {

template <bool THREADSAFE>
ArenaBase<THREADSAFE>::ArenaBase(
  BufferAllocator* const buffer_allocator,
  size_t initial_buffer_size,
  size_t max_buffer_size)
    : buffer_allocator_(buffer_allocator),
      max_buffer_size_(max_buffer_size),
      arena_footprint_(0),
      warned_(false) {
  AddComponent(CHECK_NOTNULL(NewComponent(initial_buffer_size, 0)));
}

template <bool THREADSAFE>
ArenaBase<THREADSAFE>::ArenaBase(size_t initial_buffer_size, size_t max_buffer_size)
    : buffer_allocator_(HeapBufferAllocator::Get()),
      max_buffer_size_(max_buffer_size),
      arena_footprint_(0) {
  AddComponent(CHECK_NOTNULL(NewComponent(initial_buffer_size, 0)));
}

template <bool THREADSAFE>
void *ArenaBase<THREADSAFE>::AllocateBytesFallback(const size_t size, const size_t align) {
  boost::lock_guard<mutex_type> lock(component_lock_);

  // It's possible another thread raced with us and already allocated
  // a new component, in which case we should try the "fast path" again
  void * result = current_->AllocateBytesAligned(size, align);
  if (PREDICT_FALSE(result != NULL)) return result;

  // Really need to allocate more space.
  size_t next_component_size = min(2 * current_->size(), max_buffer_size_);
  // But, allocate enough, even if the request is large. In this case,
  // might violate the max_element_size bound.
  if (next_component_size < size) {
    next_component_size = size;
  }
  // If soft quota is exhausted we will only get the "minimal" amount of memory
  // we ask for. In this case if we always use "size" as minimal, we may degrade
  // to allocating a lot of tiny components, one for each string added to the
  // arena. This would be very inefficient, so let's first try something between
  // "size" and "next_component_size". If it fails due to hard quota being
  // exhausted, we'll fall back to using "size" as minimal.
  size_t minimal = (size + next_component_size) / 2;
  CHECK_LE(size, minimal);
  CHECK_LE(minimal, next_component_size);
  // Now, just make sure we can actually get the memory.
  Component* component = NewComponent(next_component_size, minimal);
  if (component == NULL) {
    component = NewComponent(next_component_size, size);
  }
  if (!component) return NULL;

  // Now, must succeed. The component has at least 'size' bytes.
  result = component->AllocateBytesAligned(size, align);
  CHECK(result != NULL);

  // Now add it to the arena.
  AddComponent(component);

  return result;
}

template <bool THREADSAFE>
typename ArenaBase<THREADSAFE>::Component* ArenaBase<THREADSAFE>::NewComponent(
  size_t requested_size,
  size_t minimum_size) {
  Buffer* buffer = buffer_allocator_->BestEffortAllocate(requested_size,
                                                         minimum_size);
  if (buffer == NULL) return NULL;

  CHECK_EQ(reinterpret_cast<uintptr_t>(buffer->data()) & (16 - 1), 0)
    << "Components should be 16-byte aligned: " << buffer->data();

  return new Component(buffer);
}

// LOCKING: component_lock_ must be held by the current thread.
template <bool THREADSAFE>
void ArenaBase<THREADSAFE>::AddComponent(ArenaBase::Component *component) {
  current_ = component;
  arena_.push_back(shared_ptr<Component>(current_));
  arena_footprint_ += current_->size();
  if (PREDICT_FALSE(arena_footprint_ > FLAGS_arena_warn_threshold_bytes) && !warned_) {
    LOG(WARNING) << "Arena " << reinterpret_cast<const void *>(this)
                 << " exceeded warning threshold"
                 << GetStackTrace();
    warned_ = true;
  }
}

template <bool THREADSAFE>
void ArenaBase<THREADSAFE>::Reset() {
  boost::lock_guard<mutex_type> lock(component_lock_);

  if (PREDICT_FALSE(arena_.size() > 1)) {
    shared_ptr<Component> last = arena_.back();
    arena_.clear();
    arena_.push_back(last);
    current_ = last.get();
  }
  arena_.back()->Reset();
  arena_footprint_ = arena_.back()->size();
  warned_ = false;

#ifndef NDEBUG
  // In debug mode release the last component too for (hopefully) better
  // detection of memory-related bugs (invalid shallow copies, etc.).
  size_t last_size = arena_.back()->size();
  arena_.clear();
  AddComponent(CHECK_NOTNULL(NewComponent(last_size, 0)));
  arena_footprint_ = 0;
#endif
}

// Explicit instantiation.
template class ArenaBase<true>;
template class ArenaBase<false>;

}  // namespace kudu
<commit_msg>arena: Fix initialization of "warned_" member<commit_after>// Copyright 2010 Google Inc.  All Rights Reserved
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

#include <boost/thread/mutex.hpp>
#include <algorithm>

#include "util/debug-util.h"
#include "util/memory/arena.h"

using std::copy;
using std::max;
using std::min;
using std::reverse;
using std::sort;
using std::swap;

DEFINE_int64(arena_warn_threshold_bytes, 256*1024*1024,
             "Number of bytes beyond which to emit a warning for a large arena");

namespace kudu {

template <bool THREADSAFE>
ArenaBase<THREADSAFE>::ArenaBase(
  BufferAllocator* const buffer_allocator,
  size_t initial_buffer_size,
  size_t max_buffer_size)
    : buffer_allocator_(buffer_allocator),
      max_buffer_size_(max_buffer_size),
      arena_footprint_(0),
      warned_(false) {
  AddComponent(CHECK_NOTNULL(NewComponent(initial_buffer_size, 0)));
}

template <bool THREADSAFE>
ArenaBase<THREADSAFE>::ArenaBase(size_t initial_buffer_size, size_t max_buffer_size)
    : buffer_allocator_(HeapBufferAllocator::Get()),
      max_buffer_size_(max_buffer_size),
      arena_footprint_(0),
      warned_(false) {
  AddComponent(CHECK_NOTNULL(NewComponent(initial_buffer_size, 0)));
}

template <bool THREADSAFE>
void *ArenaBase<THREADSAFE>::AllocateBytesFallback(const size_t size, const size_t align) {
  boost::lock_guard<mutex_type> lock(component_lock_);

  // It's possible another thread raced with us and already allocated
  // a new component, in which case we should try the "fast path" again
  void * result = current_->AllocateBytesAligned(size, align);
  if (PREDICT_FALSE(result != NULL)) return result;

  // Really need to allocate more space.
  size_t next_component_size = min(2 * current_->size(), max_buffer_size_);
  // But, allocate enough, even if the request is large. In this case,
  // might violate the max_element_size bound.
  if (next_component_size < size) {
    next_component_size = size;
  }
  // If soft quota is exhausted we will only get the "minimal" amount of memory
  // we ask for. In this case if we always use "size" as minimal, we may degrade
  // to allocating a lot of tiny components, one for each string added to the
  // arena. This would be very inefficient, so let's first try something between
  // "size" and "next_component_size". If it fails due to hard quota being
  // exhausted, we'll fall back to using "size" as minimal.
  size_t minimal = (size + next_component_size) / 2;
  CHECK_LE(size, minimal);
  CHECK_LE(minimal, next_component_size);
  // Now, just make sure we can actually get the memory.
  Component* component = NewComponent(next_component_size, minimal);
  if (component == NULL) {
    component = NewComponent(next_component_size, size);
  }
  if (!component) return NULL;

  // Now, must succeed. The component has at least 'size' bytes.
  result = component->AllocateBytesAligned(size, align);
  CHECK(result != NULL);

  // Now add it to the arena.
  AddComponent(component);

  return result;
}

template <bool THREADSAFE>
typename ArenaBase<THREADSAFE>::Component* ArenaBase<THREADSAFE>::NewComponent(
  size_t requested_size,
  size_t minimum_size) {
  Buffer* buffer = buffer_allocator_->BestEffortAllocate(requested_size,
                                                         minimum_size);
  if (buffer == NULL) return NULL;

  CHECK_EQ(reinterpret_cast<uintptr_t>(buffer->data()) & (16 - 1), 0)
    << "Components should be 16-byte aligned: " << buffer->data();

  return new Component(buffer);
}

// LOCKING: component_lock_ must be held by the current thread.
template <bool THREADSAFE>
void ArenaBase<THREADSAFE>::AddComponent(ArenaBase::Component *component) {
  current_ = component;
  arena_.push_back(shared_ptr<Component>(current_));
  arena_footprint_ += current_->size();
  if (PREDICT_FALSE(arena_footprint_ > FLAGS_arena_warn_threshold_bytes) && !warned_) {
    LOG(WARNING) << "Arena " << reinterpret_cast<const void *>(this)
                 << " exceeded warning threshold"
                 << GetStackTrace();
    warned_ = true;
  }
}

template <bool THREADSAFE>
void ArenaBase<THREADSAFE>::Reset() {
  boost::lock_guard<mutex_type> lock(component_lock_);

  if (PREDICT_FALSE(arena_.size() > 1)) {
    shared_ptr<Component> last = arena_.back();
    arena_.clear();
    arena_.push_back(last);
    current_ = last.get();
  }
  arena_.back()->Reset();
  arena_footprint_ = arena_.back()->size();
  warned_ = false;

#ifndef NDEBUG
  // In debug mode release the last component too for (hopefully) better
  // detection of memory-related bugs (invalid shallow copies, etc.).
  size_t last_size = arena_.back()->size();
  arena_.clear();
  AddComponent(CHECK_NOTNULL(NewComponent(last_size, 0)));
  arena_footprint_ = 0;
#endif
}

// Explicit instantiation.
template class ArenaBase<true>;
template class ArenaBase<false>;

}  // namespace kudu
<|endoftext|>


<commit_before>/**
 * \file Library.cpp
 * \author Matt Hammerly
 */

#include <string>
#include <iostream>
#include "Library.h"

/**
 * \brief Default constructor
 *
 * Todo: sanitize the db credentials I guess lol
 */
CLibrary::CLibrary()
{
    char connectionString[512];
    snprintf(connectionString, 512, "dbname=%s host=%s user=%s password=%s",
             DBNAME, DBHOST, DBUSER, DBPW);

    mConnection = PQconnectdb(connectionString);

    if (PQstatus(mConnection) == CONNECTION_BAD)
    {
        std::cout << "Failed to connect to the database" << std::endl;
        exit(0);
    }
}

/**
 * \brief Destructor
 *
 * Will close the database connection before exiting
 */
CLibrary::~CLibrary()
{
    PQfinish(mConnection);
}

/**
 * \brief Create the database tables for this application
 *
 * \returns -1 if something goes wrong
 *
 * Todo: Write database schema in this comment
 *       `create trigger IF NOT EXISTS` or similar
 */
int CLibrary::PrepareDatabase()
{
    PGresult *res;
    res = PQexec(mConnection,
            "CREATE TABLE IF NOT EXISTS tracks (\
                id SERIAL NOT NULL PRIMARY KEY,\
                filepath TEXT NOT NULL,\
                date_added TIMESTAMPTZ NOT NULL DEFAULT NOW()\
          )");
    PQclear(res);

    res = PQexec(mConnection,
            "CREATE TABLE IF NOT EXISTS playlists (\
                id SERIAL NOT NULL PRIMARY KEY,\
                title TEXT NOT NULL,\
                length INTEGER NOT NULL DEFAULT 0\
          )");
    PQclear(res);

    res = PQexec(mConnection,
            "CREATE TABLE IF NOT EXISTS tracks_playlists (\
                id SERIAL NOT NULL PRIMARY KEY,\
                track_id INTEGER NOT NULL,\
                playlist_id INTEGER NOT NULL,\
                position FLOAT NOT NULL\
          )");
    PQclear(res);

    // Create a function to adjust the length of a playlist
    res = PQexec(mConnection,
            "CREATE OR REPLACE FUNCTION tracks_playlists_insert_func() RETURNS TRIGGER\
            LANGUAGE plpgsql\
            AS $tracks_playlists_insert_func$\
            DECLARE\
                pid INTEGER;\
            BEGIN\
                pid=0;\
                IF (TG_OP = 'INSERT') THEN pid = NEW.playlist_id;\
                ELSIF (TG_OP = 'DELETE') THEN pid = OLD.playlist_id;\
                END IF;\
                WITH Sub AS (SELECT COUNT(id) AS c FROM tracks_playlists WHERE playlist_id = pid)\
                    UPDATE playlists AS Main SET length = Sub.c FROM Sub WHERE Main.id = pid AND 1=1;\
                RETURN NULL;\
            END;\
            $tracks_playlists_insert_func$;");
    PQclear(res);

    // Create a trigger to adjust the length of a playlist on each insert or delete
    res = PQexec(mConnection,
            "CREATE TRIGGER tracks_playlists_insert_trg\
            AFTER INSERT OR DELETE ON tracks_playlists\
            FOR EACH ROW EXECUTE PROCEDURE tracks_playlists_insert_func();");
    PQclear(res);

    // Create a default playlist for all songs to be added to
    res = PQexec(mConnection, "INSERT INTO playlists (title) VALUES ('library')");
    PQclear(res);

    return 0;
}

/**
 * \brief Destroy all database objects used in this application
 *
 * \returns -1 if something goes wrong? I don't know what or why, frankly.
 */
int CLibrary::DestroyDatabase()
{
    PGresult *res;
    res = PQexec(mConnection, "DROP TRIGGER IF EXISTS tracks_playlists_insert_trg ON tracks_playlists;");
    PQclear(res);

    res = PQexec(mConnection, "DROP FUNCTION IF EXISTS tracks_playlists_insert_func() CASCADE;");
    PQclear(res);

    res = PQexec(mConnection, "DROP TABLE IF EXISTS tracks;");
    PQclear(res);

    res = PQexec(mConnection, "DROP TABLE IF EXISTS playlists;");
    PQclear(res);

    res = PQexec(mConnection, "DROP TABLE IF EXISTS tracks_playlists;");
    PQclear(res);

    return 0;
}

/**
 * \brief Exposes the database connection status
 * \returns PostgreSQL connection status object
 *
 * Just in case.
 */
ConnStatusType CLibrary::GetStatus()
{
    return PQstatus(mConnection);
}

/**
 * \brief Returns a pointer to the connection object
 *
 * It makes tests easier. Might make this protected and
 * require a testing subclass to use this later, whatever.
 */
PGconn* CLibrary::GetConnection()
{
    return mConnection;
}

/**
 * \brief Add a track to the database
 * \param filepath The filepath of the file to be added
 * \returns The ID of the new track (as a string)
 *
 * This method also adds the track to the all-library playlist created on database setup
 */
std::string CLibrary::AddTrack(std::string filepath)
{
    std::string query = "INSERT INTO tracks (filepath) VALUES (";

    // Safety first
    char *escaped_filepath = PQescapeLiteral(mConnection, filepath.c_str(), filepath.length());
    query.append(escaped_filepath);
    PQfreemem(escaped_filepath);

    query.append(") RETURNING id");

    PGresult *res = PQexec(mConnection, query.c_str());
    char *id = PQgetvalue(res, 0, 0);

    // Create an std::string to return so we can appropriately free the PGresult
    std::string std_id(id);
    PQclear(res);

    // Add this track to the all-library playlist created on database setup
    std::string query2 = "INSERT INTO tracks_playlists (track_id, playlist_id, position) SELECT " + std_id + ", 1, COALESCE(MAX(position), 0) + 1 FROM tracks_playlists RETURNING id";
    res = PQexec(mConnection, query2.c_str());
    PQclear(res);

    return std_id;
}

/**
 * \brief Add a playlist to the database
 * \param title The title of the playlist to be added
 * \returns The ID of the new playlist (as a string)
 */
std::string CLibrary::AddPlaylist(std::string title)
{
    std::string query = "INSERT INTO playlists (title) VALUES (";

    // Safety first
    char *escaped_title = PQescapeLiteral(mConnection, title.c_str(), title.length());
    query.append(escaped_title);
    PQfreemem(escaped_title);

    query.append(") RETURNING id");

    PGresult *res = PQexec(mConnection, query.c_str());
    char *id = PQgetvalue(res, 0, 0);

    // Create an std::string to return so we can appropriately free the PGresult
    std::string std_id(id);
    PQclear(res);

    return std_id;
}

/**
 * \brief Removes a track from the database, and all records of the track's playlist membership
 * \param id ID of the track to be deleted
 * \returns ID of the deleted track
 */
std::string CLibrary::RemoveTrack(std::string id)
{
    std::string query = "DELETE FROM tracks WHERE id=";

    char escaped_id[30];
    PQescapeStringConn(mConnection, escaped_id, id.c_str(), 30, 0);
    query.append(escaped_id);
    query.append("; DELETE FROM tracks_playlists WHERE track_id=");
    query.append(escaped_id);

    PGresult *res = PQexec(mConnection, query.c_str());
    PQclear(res);

    // We need to now normalize every playlist
    // Ideally we'll only normalize those playlists the track was actually in
    // but I'll do that some other day
    res = PQexec(mConnection, "SELECT id FROM playlists");
    int n = PQntuples(res);
    for (int i = 0; i < n; ++i)
    {
        char *id = PQgetvalue(res, i, 0);

        std::string normalize_query = "WITH Sub AS (SELECT id, row_number() OVER (ORDER BY position) FROM tracks_playlists WHERE playlist_id=";
        normalize_query.append(id);
        normalize_query.append(") UPDATE tracks_playlists AS Main SET position = Sub.row_number FROM Sub WHERE Main.id = Sub.id");

        PQclear(PQexec(mConnection, normalize_query.c_str()));
    }

    return id;
}

/**
 * \brief Removes a playlist from the database, and all records of membership in the playlist
 * \param id ID of the playlist to be deleted
 * \returns ID of the deleted playlist
 */
std::string CLibrary::RemovePlaylist(std::string id)
{
    std::string query = "DELETE FROM playlists WHERE id=";

    char escaped_id[30];
    PQescapeStringConn(mConnection, escaped_id, id.c_str(), 30, 0);
    query.append(escaped_id);
    query.append("; DELETE FROM tracks_playlists WHERE playlist_id=");
    query.append(escaped_id);

    PGresult *res = PQexec(mConnection, query.c_str());
    PQclear(res);

    return id;
}
<commit_msg>fixed memory leaks, oops<commit_after>/**
 * \file Library.cpp
 * \author Matt Hammerly
 */

#include <string>
#include <iostream>
#include "Library.h"

/**
 * \brief Default constructor
 *
 * Todo: sanitize the db credentials I guess lol
 */
CLibrary::CLibrary()
{
    char connectionString[512];
    snprintf(connectionString, 512, "dbname=%s host=%s user=%s password=%s",
             DBNAME, DBHOST, DBUSER, DBPW);

    mConnection = PQconnectdb(connectionString);

    if (PQstatus(mConnection) == CONNECTION_BAD)
    {
        std::cout << "Failed to connect to the database" << std::endl;
        exit(0);
    }
}

/**
 * \brief Destructor
 *
 * Will close the database connection before exiting
 */
CLibrary::~CLibrary()
{
    PQfinish(mConnection);
}

/**
 * \brief Create the database tables for this application
 *
 * \returns -1 if something goes wrong
 *
 * Todo: Write database schema in this comment
 *       `create trigger IF NOT EXISTS` or similar
 */
int CLibrary::PrepareDatabase()
{
    PGresult *res;
    res = PQexec(mConnection,
            "CREATE TABLE IF NOT EXISTS tracks (\
                id SERIAL NOT NULL PRIMARY KEY,\
                filepath TEXT NOT NULL,\
                date_added TIMESTAMPTZ NOT NULL DEFAULT NOW()\
          )");
    PQclear(res);

    res = PQexec(mConnection,
            "CREATE TABLE IF NOT EXISTS playlists (\
                id SERIAL NOT NULL PRIMARY KEY,\
                title TEXT NOT NULL,\
                length INTEGER NOT NULL DEFAULT 0\
          )");
    PQclear(res);

    res = PQexec(mConnection,
            "CREATE TABLE IF NOT EXISTS tracks_playlists (\
                id SERIAL NOT NULL PRIMARY KEY,\
                track_id INTEGER NOT NULL,\
                playlist_id INTEGER NOT NULL,\
                position FLOAT NOT NULL\
          )");
    PQclear(res);

    // Create a function to adjust the length of a playlist
    res = PQexec(mConnection,
            "CREATE OR REPLACE FUNCTION tracks_playlists_insert_func() RETURNS TRIGGER\
            LANGUAGE plpgsql\
            AS $tracks_playlists_insert_func$\
            DECLARE\
                pid INTEGER;\
            BEGIN\
                pid=0;\
                IF (TG_OP = 'INSERT') THEN pid = NEW.playlist_id;\
                ELSIF (TG_OP = 'DELETE') THEN pid = OLD.playlist_id;\
                END IF;\
                WITH Sub AS (SELECT COUNT(id) AS c FROM tracks_playlists WHERE playlist_id = pid)\
                    UPDATE playlists AS Main SET length = Sub.c FROM Sub WHERE Main.id = pid AND 1=1;\
                RETURN NULL;\
            END;\
            $tracks_playlists_insert_func$;");
    PQclear(res);

    // Create a trigger to adjust the length of a playlist on each insert or delete
    res = PQexec(mConnection,
            "CREATE TRIGGER tracks_playlists_insert_trg\
            AFTER INSERT OR DELETE ON tracks_playlists\
            FOR EACH ROW EXECUTE PROCEDURE tracks_playlists_insert_func();");
    PQclear(res);

    // Create a default playlist for all songs to be added to
    res = PQexec(mConnection, "INSERT INTO playlists (title) VALUES ('library')");
    PQclear(res);

    return 0;
}

/**
 * \brief Destroy all database objects used in this application
 *
 * \returns -1 if something goes wrong? I don't know what or why, frankly.
 */
int CLibrary::DestroyDatabase()
{
    PGresult *res;
    res = PQexec(mConnection, "DROP TRIGGER IF EXISTS tracks_playlists_insert_trg ON tracks_playlists;");
    PQclear(res);

    res = PQexec(mConnection, "DROP FUNCTION IF EXISTS tracks_playlists_insert_func() CASCADE;");
    PQclear(res);

    res = PQexec(mConnection, "DROP TABLE IF EXISTS tracks;");
    PQclear(res);

    res = PQexec(mConnection, "DROP TABLE IF EXISTS playlists;");
    PQclear(res);

    res = PQexec(mConnection, "DROP TABLE IF EXISTS tracks_playlists;");
    PQclear(res);

    return 0;
}

/**
 * \brief Exposes the database connection status
 * \returns PostgreSQL connection status object
 *
 * Just in case.
 */
ConnStatusType CLibrary::GetStatus()
{
    return PQstatus(mConnection);
}

/**
 * \brief Returns a pointer to the connection object
 *
 * It makes tests easier. Might make this protected and
 * require a testing subclass to use this later, whatever.
 */
PGconn* CLibrary::GetConnection()
{
    return mConnection;
}

/**
 * \brief Add a track to the database
 * \param filepath The filepath of the file to be added
 * \returns The ID of the new track (as a string)
 *
 * This method also adds the track to the all-library playlist created on database setup
 */
std::string CLibrary::AddTrack(std::string filepath)
{
    std::string query = "INSERT INTO tracks (filepath) VALUES (";

    // Safety first
    char *escaped_filepath = PQescapeLiteral(mConnection, filepath.c_str(), filepath.length());
    query.append(escaped_filepath);
    PQfreemem(escaped_filepath);

    query.append(") RETURNING id");

    PGresult *res = PQexec(mConnection, query.c_str());
    char *id = PQgetvalue(res, 0, 0);

    // Create an std::string to return so we can appropriately free the PGresult
    std::string std_id(id);
    PQclear(res);

    // Add this track to the all-library playlist created on database setup
    std::string query2 = "INSERT INTO tracks_playlists (track_id, playlist_id, position) SELECT " + std_id + ", 1, COALESCE(MAX(position), 0) + 1 FROM tracks_playlists RETURNING id";
    res = PQexec(mConnection, query2.c_str());
    PQclear(res);

    return std_id;
}

/**
 * \brief Add a playlist to the database
 * \param title The title of the playlist to be added
 * \returns The ID of the new playlist (as a string)
 */
std::string CLibrary::AddPlaylist(std::string title)
{
    std::string query = "INSERT INTO playlists (title) VALUES (";

    // Safety first
    char *escaped_title = PQescapeLiteral(mConnection, title.c_str(), title.length());
    query.append(escaped_title);
    PQfreemem(escaped_title);

    query.append(") RETURNING id");

    PGresult *res = PQexec(mConnection, query.c_str());
    char *id = PQgetvalue(res, 0, 0);

    // Create an std::string to return so we can appropriately free the PGresult
    std::string std_id(id);
    PQclear(res);

    return std_id;
}

/**
 * \brief Removes a track from the database, and all records of the track's playlist membership
 * \param id ID of the track to be deleted
 * \returns ID of the deleted track
 */
std::string CLibrary::RemoveTrack(std::string id)
{
    std::string query = "DELETE FROM tracks WHERE id=";

    char escaped_id[30];
    PQescapeStringConn(mConnection, escaped_id, id.c_str(), 30, 0);
    query.append(escaped_id);
    query.append("; DELETE FROM tracks_playlists WHERE track_id=");
    query.append(escaped_id);

    PGresult *res = PQexec(mConnection, query.c_str());
    PQclear(res);

    // We need to now normalize every playlist
    // Ideally we'll only normalize those playlists the track was actually in
    // but I'll do that some other day
    res = PQexec(mConnection, "SELECT id FROM playlists");
    int n = PQntuples(res);
    for (int i = 0; i < n; ++i)
    {
        char *id = PQgetvalue(res, i, 0);

        std::string normalize_query = "WITH Sub AS (SELECT id, row_number() OVER (ORDER BY position) FROM tracks_playlists WHERE playlist_id=";
        normalize_query.append(id);
        normalize_query.append(") UPDATE tracks_playlists AS Main SET position = Sub.row_number FROM Sub WHERE Main.id = Sub.id");

        PQclear(PQexec(mConnection, normalize_query.c_str()));
    }
    PQclear(res);

    return id;
}

/**
 * \brief Removes a playlist from the database, and all records of membership in the playlist
 * \param id ID of the playlist to be deleted
 * \returns ID of the deleted playlist
 */
std::string CLibrary::RemovePlaylist(std::string id)
{
    std::string query = "DELETE FROM playlists WHERE id=";

    char escaped_id[30];
    PQescapeStringConn(mConnection, escaped_id, id.c_str(), 30, 0);
    query.append(escaped_id);
    query.append("; DELETE FROM tracks_playlists WHERE playlist_id=");
    query.append(escaped_id);

    PGresult *res = PQexec(mConnection, query.c_str());
    PQclear(res);

    return id;
}
<|endoftext|>


<commit_before><commit_msg>make fem algorithm respect parameters<commit_after><|endoftext|>


<commit_before>/*
 * plugin_factory.cpp
 *
 *  Created on: Nov 20, 2012
 *      Author: partio
 */

#include "plugin_factory.h"
#include "logger_factory.h"
#include <boost/filesystem.hpp>
#include <boost/regex.hpp>
#include <cstdlib>
#include <dlfcn.h>
#include "util.h"

using namespace himan;
using namespace himan::plugin;

plugin_factory* plugin_factory::itsInstance = NULL;

plugin_factory* plugin_factory::Instance()
{
    if (!itsInstance)
    {
        itsInstance = new plugin_factory();
    }

    return itsInstance;
}

plugin_factory::plugin_factory() : itsPluginSearchPath()
{

    itsLogger = std::unique_ptr<logger> (logger_factory::Instance()->GetLog("plugin_factory"));

    char* path;

    path = std::getenv("HIMAN_LIBRARY_PATH");

    if (path != NULL)
    {
        std::vector<std::string> paths = util::Split(std::string(path), ":", false);

        itsPluginSearchPath.insert(itsPluginSearchPath.end(), paths.begin(), paths.end());

    }
    else
    {
        itsLogger->Trace("Environment variable HIMAN_LIBRARY_PATH not set -- search plugins only from pre-defined locations");
    }

    itsPluginSearchPath.push_back(".");
    itsPluginSearchPath.push_back("/usr/lib64/himan-plugins"); // Default RPM location

    ReadPlugins();
}

// Hide constructor
plugin_factory::~plugin_factory() {}

std::vector<std::shared_ptr<himan_plugin> > plugin_factory::Plugins(HPPluginClass pluginClass)
{

    std::vector<std::shared_ptr<himan_plugin>> thePlugins;

    for (size_t i = 0; i < itsPluginFactory.size(); i++)
    {
        if (pluginClass == itsPluginFactory[i]->Plugin()->PluginClass())
        {
            thePlugins.push_back(itsPluginFactory[i]->Plugin());
        }

        else if (pluginClass == kUnknownPlugin)
        {
            thePlugins.push_back(itsPluginFactory[i]->Plugin());
        }
    }

    return thePlugins;
}

std::vector<std::shared_ptr<himan_plugin> > plugin_factory::CompiledPlugins()
{
    return Plugins(kCompiled);
}
std::vector<std::shared_ptr<himan_plugin> > plugin_factory::AuxiliaryPlugins()
{
    return Plugins(kAuxiliary);
}
std::vector<std::shared_ptr<himan_plugin> > plugin_factory::InterpretedPlugins()
{
    return Plugins(kInterpreted);
}

/*
 * Plugin()
 *
 * Return instance of the requested plugin if found. Caller must cast
 * the plugin to the derived class. If second argument is true, a new
 * instance is created and returned. Otherwise function behaves like
 * a regular factory pattern and return one known instance to each
 * caller (this is suitable only in non-threaded functions).
 */

std::shared_ptr<himan_plugin> plugin_factory::Plugin(const std::string& theClassName, bool theNewInstance)
{

    for (size_t i = 0; i < itsPluginFactory.size(); i++)
    {

        if ((itsPluginFactory[i]->Plugin()->ClassName() == theClassName) ||
                (itsPluginFactory[i]->Plugin()->ClassName() == "himan::plugin::" + theClassName))
        {
            if (theNewInstance)
            {
                return itsPluginFactory[i]->Clone();
            }
            else
            {
                return itsPluginFactory[i]->Plugin();
            }
        }
    }

    throw std::runtime_error("plugin_factory: Unknown plugin clone operation requested: " + theClassName);
}

/*
 * ReadPlugins()
 *
 * Read plugins from defined paths. Will try to load all files in given directories
 * that end with .so. Will not ascend to child directories (equals to "--max-depth 1").
 */

void plugin_factory::ReadPlugins()
{

    using namespace boost::filesystem;

    directory_iterator end_iter;

    for (size_t i = 0; i < itsPluginSearchPath.size(); i++)
    {
    	itsLogger->Trace("Search plugins from " + itsPluginSearchPath[i]);

        path p (itsPluginSearchPath[i]);

        try
        {
            if (exists(p) && is_directory(p))      // is p a directory?
            {

                for ( directory_iterator dir_iter(p) ; dir_iter != end_iter ; ++dir_iter)
                {
                    if (dir_iter->path().filename().extension().string() == ".so")
                    {
                        Load(dir_iter->path().string());
                    }
                }
            }
        }

        catch (const filesystem_error& ex)
        {
            itsLogger->Error(ex.what());
        }
    }

}

bool plugin_factory::Load(const std::string& thePluginFileName)
{

    /*
     * Open libraries with
     *
     *   RTLD_LAZY
     * We don't specify the ordering of the plugin load -- usually it is alphabetical
     * but not necessarily so. With RTLD_LAZY the symbols aren't checked during load
     * which means that the first loaded plugin can refer to functions defined in the
     * last loaded plugin without compiler issuing warnings like
     *  <file>.so undefined symbol: ...
     *
     *   RTLD_GLOBAL
     * We need this because core library (himan-lib) need to access aux plugins
     * and the plugin symbol information needs to be propagated throughout the
     * plugins system (or using aux plugins in core lib will fail).
     */

    void* theLibraryHandle = dlopen(thePluginFileName.c_str(), RTLD_LAZY | RTLD_GLOBAL);

    if (!theLibraryHandle)
    {
        itsLogger->Error("Unable to load plugin: " + std::string(dlerror()));
        return false;
    }

    dlerror(); // clear error handle

    //create_t* create_plugin = (create_t*) dlsym(theLibraryHandle, "create");
    create_t* create_plugin =  reinterpret_cast<create_t*> (dlsym(theLibraryHandle, "create"));

    if (!create_plugin)
    {
        itsLogger->Error("Unable to load symbol: " + std::string(dlerror()));
        return false;
    }

    std::shared_ptr<plugin_container> mc = std::shared_ptr<plugin_container> (new plugin_container(theLibraryHandle, create_plugin()));

    for (size_t i = 0; i < itsPluginFactory.size(); i++)
    {
        if (mc->Plugin()->ClassName() == itsPluginFactory[i]->Plugin()->ClassName())
        {
            itsLogger->Trace("Plugin '" + mc->Plugin()->ClassName() + "' found more than once, skipping one found from '" + thePluginFileName + "'");
            return true;
        }
    }

    itsPluginFactory.push_back(mc);

    itsLogger->Debug("Load " + thePluginFileName);

    return true;
}
<commit_msg>Check plugin name before creating plugin_container to avoid memory allocation errors<commit_after>/*
 * plugin_factory.cpp
 *
 *  Created on: Nov 20, 2012
 *      Author: partio
 */

#include "plugin_factory.h"
#include "logger_factory.h"
#include <boost/filesystem.hpp>
#include <boost/regex.hpp>
#include <cstdlib>
#include <dlfcn.h>
#include "util.h"

using namespace himan;
using namespace himan::plugin;

plugin_factory* plugin_factory::itsInstance = NULL;

plugin_factory* plugin_factory::Instance()
{
    if (!itsInstance)
    {
        itsInstance = new plugin_factory();
    }

    return itsInstance;
}

plugin_factory::plugin_factory() : itsPluginSearchPath()
{

    itsLogger = std::unique_ptr<logger> (logger_factory::Instance()->GetLog("plugin_factory"));

    char* path;

    path = std::getenv("HIMAN_LIBRARY_PATH");

    if (path != NULL)
    {
        std::vector<std::string> paths = util::Split(std::string(path), ":", false);

        itsPluginSearchPath.insert(itsPluginSearchPath.end(), paths.begin(), paths.end());

    }
    else
    {
        itsLogger->Trace("Environment variable HIMAN_LIBRARY_PATH not set -- search plugins only from pre-defined locations");
    }

    itsPluginSearchPath.push_back(".");
    itsPluginSearchPath.push_back("/usr/lib64/himan-plugins"); // Default RPM location

    ReadPlugins();
}

// Hide constructor
plugin_factory::~plugin_factory() {}

std::vector<std::shared_ptr<himan_plugin> > plugin_factory::Plugins(HPPluginClass pluginClass)
{

    std::vector<std::shared_ptr<himan_plugin>> thePlugins;

    for (size_t i = 0; i < itsPluginFactory.size(); i++)
    {
        if (pluginClass == itsPluginFactory[i]->Plugin()->PluginClass())
        {
            thePlugins.push_back(itsPluginFactory[i]->Plugin());
        }

        else if (pluginClass == kUnknownPlugin)
        {
            thePlugins.push_back(itsPluginFactory[i]->Plugin());
        }
    }

    return thePlugins;
}

std::vector<std::shared_ptr<himan_plugin> > plugin_factory::CompiledPlugins()
{
    return Plugins(kCompiled);
}
std::vector<std::shared_ptr<himan_plugin> > plugin_factory::AuxiliaryPlugins()
{
    return Plugins(kAuxiliary);
}
std::vector<std::shared_ptr<himan_plugin> > plugin_factory::InterpretedPlugins()
{
    return Plugins(kInterpreted);
}

/*
 * Plugin()
 *
 * Return instance of the requested plugin if found. Caller must cast
 * the plugin to the derived class. If second argument is true, a new
 * instance is created and returned. Otherwise function behaves like
 * a regular factory pattern and return one known instance to each
 * caller (this is suitable only in non-threaded functions).
 */

std::shared_ptr<himan_plugin> plugin_factory::Plugin(const std::string& theClassName, bool theNewInstance)
{

    for (size_t i = 0; i < itsPluginFactory.size(); i++)
    {

        if ((itsPluginFactory[i]->Plugin()->ClassName() == theClassName) ||
                (itsPluginFactory[i]->Plugin()->ClassName() == "himan::plugin::" + theClassName))
        {
            if (theNewInstance)
            {
                return itsPluginFactory[i]->Clone();
            }
            else
            {
                return itsPluginFactory[i]->Plugin();
            }
        }
    }

    throw std::runtime_error("plugin_factory: Unknown plugin clone operation requested: " + theClassName);
}

/*
 * ReadPlugins()
 *
 * Read plugins from defined paths. Will try to load all files in given directories
 * that end with .so. Will not ascend to child directories (equals to "--max-depth 1").
 */

void plugin_factory::ReadPlugins()
{

    using namespace boost::filesystem;

    directory_iterator end_iter;

    for (size_t i = 0; i < itsPluginSearchPath.size(); i++)
    {
    	itsLogger->Trace("Search plugins from " + itsPluginSearchPath[i]);

        path p (itsPluginSearchPath[i]);

        try
        {
            if (exists(p) && is_directory(p))      // is p a directory?
            {

                for ( directory_iterator dir_iter(p) ; dir_iter != end_iter ; ++dir_iter)
                {
                    if (dir_iter->path().filename().extension().string() == ".so")
                    {
                        Load(dir_iter->path().string());
                    }
                }
            }
        }

        catch (const filesystem_error& ex)
        {
            itsLogger->Error(ex.what());
        }
    }

}

bool plugin_factory::Load(const std::string& thePluginFileName)
{

    /*
     * Open libraries with
     *
     *   RTLD_LAZY
     * We don't specify the ordering of the plugin load -- usually it is alphabetical
     * but not necessarily so. With RTLD_LAZY the symbols aren't checked during load
     * which means that the first loaded plugin can refer to functions defined in the
     * last loaded plugin without compiler issuing warnings like
     *  <file>.so undefined symbol: ...
     *
     *   RTLD_GLOBAL
     * We need this because core library (himan-lib) need to access aux plugins
     * and the plugin symbol information needs to be propagated throughout the
     * plugins system (or using aux plugins in core lib will fail).
     */

    void* theLibraryHandle = dlopen(thePluginFileName.c_str(), RTLD_LAZY | RTLD_GLOBAL);

    if (!theLibraryHandle)
    {
        itsLogger->Error("Unable to load plugin: " + std::string(dlerror()));
        return false;
    }

    dlerror(); // clear error handle

    //create_t* create_plugin = (create_t*) dlsym(theLibraryHandle, "create");
    create_t* create_plugin =  reinterpret_cast<create_t*> (dlsym(theLibraryHandle, "create"));

    if (!create_plugin)
    {
        itsLogger->Error("Unable to load symbol: " + std::string(dlerror()));
        return false;
    }

    std::shared_ptr<himan_plugin> p (create_plugin());

    for (size_t i = 0; i < itsPluginFactory.size(); i++)
	{
		if (p->ClassName() == itsPluginFactory[i]->Plugin()->ClassName())
		{
			itsLogger->Trace("Plugin '" + p->ClassName() + "' found more than once, skipping one found from '" + thePluginFileName + "'");
			return true;
		}
	}

    std::shared_ptr<plugin_container> mc = std::shared_ptr<plugin_container> (new plugin_container(theLibraryHandle, p));

    itsPluginFactory.push_back(mc);

    itsLogger->Debug("Load " + thePluginFileName);

    return true;
}
<|endoftext|>


<commit_before>#include "MyModel.h"
#include <cmath>

namespace Perceptron
{

MyModel::MyModel()
:MyModel{10}
{

}

MyModel::MyModel(const std::initializer_list<unsigned int>& num_hidden)
:mean(num_hidden)
{

}

void MyModel::from_prior(DNest4::RNG& rng)
{
    mean.from_prior(rng);
}

double MyModel::perturb(DNest4::RNG& rng)
{
    double logH = 0.0;

    logH += mean.perturb(rng);

    return logH;
}

double MyModel::log_likelihood() const
{
    double logL = 0.0;

    const auto& inputs = Data::get_instance().get_inputs();
    const auto& outputs = Data::get_instance().get_outputs();

    double C = 0.5*log(2.0*M_PI);
    for(size_t i=0; i<inputs.size(); ++i)
    {
        // Predict the output
        Vector result = mean.calculate_output(inputs[i]);

        for(int j=0; j<result.size(); ++j)
        {
            logL += -C - 0.5*pow(outputs[i][j] - result[j], 2);
        }
    }

    if(std::isnan(logL) || std::isinf(logL))
        logL = -1E300;

    return logL;
}

void MyModel::print(std::ostream& out) const
{
    mean.print(out);
}
std::string MyModel::description() const
{
    return std::string("");
}

} // namespace Perceptron

<commit_msg>One more layer :)<commit_after>#include "MyModel.h"
#include <cmath>

namespace Perceptron
{

MyModel::MyModel()
:MyModel{20, 10}
{

}

MyModel::MyModel(const std::initializer_list<unsigned int>& num_hidden)
:mean(num_hidden)
{

}

void MyModel::from_prior(DNest4::RNG& rng)
{
    mean.from_prior(rng);
}

double MyModel::perturb(DNest4::RNG& rng)
{
    double logH = 0.0;

    logH += mean.perturb(rng);

    return logH;
}

double MyModel::log_likelihood() const
{
    double logL = 0.0;

    const auto& inputs = Data::get_instance().get_inputs();
    const auto& outputs = Data::get_instance().get_outputs();

    double C = 0.5*log(2.0*M_PI);
    for(size_t i=0; i<inputs.size(); ++i)
    {
        // Predict the output
        Vector result = mean.calculate_output(inputs[i]);

        for(int j=0; j<result.size(); ++j)
        {
            logL += -C - 0.5*pow(outputs[i][j] - result[j], 2);
        }
    }

    if(std::isnan(logL) || std::isinf(logL))
        logL = -1E300;

    return logL;
}

void MyModel::print(std::ostream& out) const
{
    mean.print(out);
}
std::string MyModel::description() const
{
    return std::string("");
}

} // namespace Perceptron

<|endoftext|>


<commit_before>
#pragma once

#include <proxc.hpp>
#include <src/aligned_alloc.hpp>

#include <memory>
#include <atomic>

PROXC_NAMESPACE_BEGIN

// Chase-Lev work-steal deque
//
// Dynamic Circular Work-Stealing Deque
// http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.170.1097&rep=rep1&type=pdf
//
// Correct and Efﬁcient Work-Stealing for Weak Memory Models
// http://www.di.ens.fr/~zappa/readings/ppopp13.pdf
template<typename T>
class WorkStealDeque
{
    class CircularArray
    {
    public:
        CircularArray(std::size_t n)
            : m_items(n) {}

        std::size_t size() const
        {
            return m_items.size();
        }

        T* get(std::size_t index)
        {
            return m_items[index % (size() - 1)];
        }

        void put(std::size_t index, T* item)
        {
            m_items[index & (size() - 1)] = item;
        }

        CircularArray* grow(std::size_t top, std::size_t bottom)
        {
            CircularArray *new_array = new CircularArray(size() * 2);
            new_array->m_prev.reset(this);
            for (std::size_t i = top; i != bottom; i++) {
                new_array->put(i, get(i));
            }
            return new_array;
        }

    private:
        proxc::AlignedArray<T*, 64> m_items;
        std::unique_ptr<CircularArray> m_prev;
    };

    static std::ptrdiff_t to_signed(std::size_t x)
    {
        static_assert(static_cast<std::size_t>(PTRDIFF_MAX) + 1 == static_cast<std::size_t>(PTRDIFF_MIN), "Wrong integer wrapping behaviour");
        if (x > static_cast<std::size_t>(PTRDIFF_MAX)) {
                return static_cast<std::ptrdiff_t>(x - static_cast<std::size_t>(PTRDIFF_MIN)) + PTRDIFF_MIN;
        } else {
            return static_cast<std::ptrdiff_t>(x);
        }
    }

public:
    WorkStealDeque()
        : m_array(new CircularArray(32)), m_top(0), m_bottom(0) {}

    ~WorkStealDeque()
    {
        std::size_t bottom = m_bottom.load(std::memory_order_relaxed);
        std::size_t top = m_top.load(std::memory_order_relaxed);
        CircularArray *array = m_array.load(std::memory_order_relaxed);
        for (std::size_t i = top; i != bottom; ++i) {
            // TODO free all residing tasks
        }
        delete array;
    }

    void push(T item)
    {
        std::size_t bottom = m_bottom.load(std::memory_order_relaxed);
        std::size_t top = m_top.load(std::memory_order_acquire);
        CircularArray *array = m_array.load(std::memory_order_relaxed);

        // Grow array if full
        if (to_signed(bottom - top) >= to_signed(array->size())) {
            array = array->grow(top, bottom);
            m_array.store(array, std::memory_order_release);
        }

        array->put(bottom, item.as_ptr());
        std::atomic_thread_fence(std::memory_order_release);
        m_bottom.store(bottom + 1, std::memory_order_relaxed);
    }

    T pop()
    {
        std::size_t bottom = m_bottom.load(std::memory_order_relaxed);
        std::size_t top = m_top.load(std::memory_order_relaxed);

        // Exit if deque is empty
        if (to_signed(bottom - top) <= 0) {
            return T();
        }

        bottom -= 1;

        // Bottom must be stored before top is read
        m_bottom.store(bottom, std::memory_order_relaxed);
        std::atomic_thread_fence(std::memory_order_seq_cst);
        top = m_top.load(std::memory_order_relaxed);

        // If deque is empty, restore bottom and exit
        if (to_signed(bottom - top) < 0) {
            m_bottom.store(bottom + 1, std::memory_order_relaxed);
            return T();
        }

        // Fetch item from deque
        CircularArray *array = m_array.load(std::memory_order_relaxed);
        T *item = array->get(bottom);

        // If this was the last item, check for races
        if (bottom == top) {
            if (!m_top.compare_exchange_strong(top, top + 1, std::memory_order_seq_cst, std::memory_order_relaxed)) {
                m_bottom.store(bottom + 1, std::memory_order_relaxed);
                return T();
            }
            m_bottom.store(bottom + 1, std::memory_order_relaxed);
        }
        return T::from_ptr(item);
    }

    T steal()
    {
        // Loop while CAS fails.
        while (true) {
            // Make sure top is read before bottom
            std::size_t top = m_top.load(std::memory_order_acquire);
            std::atomic_thread_fence(std::memory_order_seq_cst);
            std::size_t bottom = m_bottom.load(std::memory_order_acquire);

            // Exit if deque is empty
            if (to_signed(bottom - top) <= 0) {
                return T();
            }

            // Fetch item from deque
            CircularArray *array = m_array.load(std::memory_order_consume);
            T *item = array->get(top);

            // Attempy to increment top
            if (m_top.compare_exchange_weak(top, top + 1, std::memory_order_seq_cst, std::memory_order_relaxed)) {
                return T::from_ptr(item);
            }
        }
    }

private:
    std::atomic<CircularArray*> m_array;
    std::atomic<std::size_t> m_top, m_bottom;
};

PROXC_NAMESPACE_END

<commit_msg>Fixed header include of aligned_alloc<commit_after>
#pragma once

#include <proxc.hpp>

#include <memory>
#include <atomic>

#include "aligned_alloc.hpp"

PROXC_NAMESPACE_BEGIN

// Chase-Lev work-steal deque
//
// Dynamic Circular Work-Stealing Deque
// http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.170.1097&rep=rep1&type=pdf
//
// Correct and Efﬁcient Work-Stealing for Weak Memory Models
// http://www.di.ens.fr/~zappa/readings/ppopp13.pdf
template<typename T>
class WorkStealDeque
{
    class CircularArray
    {
    public:
        CircularArray(std::size_t n)
            : m_items(n) {}

        std::size_t size() const
        {
            return m_items.size();
        }

        T* get(std::size_t index)
        {
            return m_items[index % (size() - 1)];
        }

        void put(std::size_t index, T* item)
        {
            m_items[index & (size() - 1)] = item;
        }

        CircularArray* grow(std::size_t top, std::size_t bottom)
        {
            CircularArray *new_array = new CircularArray(size() * 2);
            new_array->m_prev.reset(this);
            for (std::size_t i = top; i != bottom; i++) {
                new_array->put(i, get(i));
            }
            return new_array;
        }

    private:
        proxc::AlignedArray<T*, 64> m_items;
        std::unique_ptr<CircularArray> m_prev;
    };

    static std::ptrdiff_t to_signed(std::size_t x)
    {
        static_assert(static_cast<std::size_t>(PTRDIFF_MAX) + 1 == static_cast<std::size_t>(PTRDIFF_MIN), "Wrong integer wrapping behaviour");
        if (x > static_cast<std::size_t>(PTRDIFF_MAX)) {
                return static_cast<std::ptrdiff_t>(x - static_cast<std::size_t>(PTRDIFF_MIN)) + PTRDIFF_MIN;
        } else {
            return static_cast<std::ptrdiff_t>(x);
        }
    }

public:
    WorkStealDeque()
        : m_array(new CircularArray(32)), m_top(0), m_bottom(0) {}

    ~WorkStealDeque()
    {
        std::size_t bottom = m_bottom.load(std::memory_order_relaxed);
        std::size_t top = m_top.load(std::memory_order_relaxed);
        CircularArray *array = m_array.load(std::memory_order_relaxed);
        for (std::size_t i = top; i != bottom; ++i) {
            // TODO free all residing tasks
        }
        delete array;
    }

    void push(T item)
    {
        std::size_t bottom = m_bottom.load(std::memory_order_relaxed);
        std::size_t top = m_top.load(std::memory_order_acquire);
        CircularArray *array = m_array.load(std::memory_order_relaxed);

        // Grow array if full
        if (to_signed(bottom - top) >= to_signed(array->size())) {
            array = array->grow(top, bottom);
            m_array.store(array, std::memory_order_release);
        }

        array->put(bottom, item.as_ptr());
        std::atomic_thread_fence(std::memory_order_release);
        m_bottom.store(bottom + 1, std::memory_order_relaxed);
    }

    T pop()
    {
        std::size_t bottom = m_bottom.load(std::memory_order_relaxed);
        std::size_t top = m_top.load(std::memory_order_relaxed);

        // Exit if deque is empty
        if (to_signed(bottom - top) <= 0) {
            return T();
        }

        bottom -= 1;

        // Bottom must be stored before top is read
        m_bottom.store(bottom, std::memory_order_relaxed);
        std::atomic_thread_fence(std::memory_order_seq_cst);
        top = m_top.load(std::memory_order_relaxed);

        // If deque is empty, restore bottom and exit
        if (to_signed(bottom - top) < 0) {
            m_bottom.store(bottom + 1, std::memory_order_relaxed);
            return T();
        }

        // Fetch item from deque
        CircularArray *array = m_array.load(std::memory_order_relaxed);
        T *item = array->get(bottom);

        // If this was the last item, check for races
        if (bottom == top) {
            if (!m_top.compare_exchange_strong(top, top + 1, std::memory_order_seq_cst, std::memory_order_relaxed)) {
                m_bottom.store(bottom + 1, std::memory_order_relaxed);
                return T();
            }
            m_bottom.store(bottom + 1, std::memory_order_relaxed);
        }
        return T::from_ptr(item);
    }

    T steal()
    {
        // Loop while CAS fails.
        while (true) {
            // Make sure top is read before bottom
            std::size_t top = m_top.load(std::memory_order_acquire);
            std::atomic_thread_fence(std::memory_order_seq_cst);
            std::size_t bottom = m_bottom.load(std::memory_order_acquire);

            // Exit if deque is empty
            if (to_signed(bottom - top) <= 0) {
                return T();
            }

            // Fetch item from deque
            CircularArray *array = m_array.load(std::memory_order_consume);
            T *item = array->get(top);

            // Attempy to increment top
            if (m_top.compare_exchange_weak(top, top + 1, std::memory_order_seq_cst, std::memory_order_relaxed)) {
                return T::from_ptr(item);
            }
        }
    }

private:
    std::atomic<CircularArray*> m_array;
    std::atomic<std::size_t> m_top, m_bottom;
};

PROXC_NAMESPACE_END

<|endoftext|>


<commit_before>/*************************************************************************/
/*  stream_peer_tcp.cpp                                                  */
/*************************************************************************/
/*                       This file is part of:                           */
/*                           GODOT ENGINE                                */
/*                      https://godotengine.org                          */
/*************************************************************************/
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur.                 */
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md).   */
/*                                                                       */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the       */
/* "Software"), to deal in the Software without restriction, including   */
/* without limitation the rights to use, copy, modify, merge, publish,   */
/* distribute, sublicense, and/or sell copies of the Software, and to    */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions:                                             */
/*                                                                       */
/* The above copyright notice and this permission notice shall be        */
/* included in all copies or substantial portions of the Software.       */
/*                                                                       */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
/*************************************************************************/

#include "stream_peer_tcp.h"

#include "core/config/project_settings.h"

Error StreamPeerTCP::poll() {
	if (status == STATUS_CONNECTED) {
		Error err;
		err = _sock->poll(NetSocket::POLL_TYPE_IN, 0);
		if (err == OK) {
			// FIN received
			if (_sock->get_available_bytes() == 0) {
				disconnect_from_host();
				return OK;
			}
		}
		// Also poll write
		err = _sock->poll(NetSocket::POLL_TYPE_IN_OUT, 0);
		if (err != OK && err != ERR_BUSY) {
			// Got an error
			disconnect_from_host();
			status = STATUS_ERROR;
			return err;
		}
	} else if (status != STATUS_CONNECTING) {
		return OK;
	}

	Error err = _sock->connect_to_host(peer_host, peer_port);

	if (err == OK) {
		status = STATUS_CONNECTED;
		return OK;
	} else if (err == ERR_BUSY) {
		// Check for connect timeout
		if (OS::get_singleton()->get_ticks_msec() > timeout) {
			disconnect_from_host();
			status = STATUS_ERROR;
			return ERR_CONNECTION_ERROR;
		}
		// Still trying to connect
		return OK;
	}

	disconnect_from_host();
	status = STATUS_ERROR;
	return ERR_CONNECTION_ERROR;
}

void StreamPeerTCP::accept_socket(Ref<NetSocket> p_sock, IPAddress p_host, uint16_t p_port) {
	_sock = p_sock;
	_sock->set_blocking_enabled(false);

	timeout = OS::get_singleton()->get_ticks_msec() + (((uint64_t)GLOBAL_GET("network/limits/tcp/connect_timeout_seconds")) * 1000);
	status = STATUS_CONNECTING;

	peer_host = p_host;
	peer_port = p_port;
}

Error StreamPeerTCP::bind(int p_port, const IPAddress &p_host) {
	ERR_FAIL_COND_V(!_sock.is_valid(), ERR_UNAVAILABLE);
	ERR_FAIL_COND_V(_sock->is_open(), ERR_ALREADY_IN_USE);
	ERR_FAIL_COND_V_MSG(p_port < 0 || p_port > 65535, ERR_INVALID_PARAMETER, "The local port number must be between 0 and 65535 (inclusive).");

	IP::Type ip_type = p_host.is_ipv4() ? IP::TYPE_IPV4 : IP::TYPE_IPV6;
	if (p_host.is_wildcard()) {
		ip_type = IP::TYPE_ANY;
	}
	Error err = _sock->open(NetSocket::TYPE_TCP, ip_type);
	if (err != OK) {
		return err;
	}
	_sock->set_blocking_enabled(false);
	return _sock->bind(p_host, p_port);
}

Error StreamPeerTCP::connect_to_host(const IPAddress &p_host, int p_port) {
	ERR_FAIL_COND_V(!_sock.is_valid(), ERR_UNAVAILABLE);
	ERR_FAIL_COND_V(status != STATUS_NONE, ERR_ALREADY_IN_USE);
	ERR_FAIL_COND_V(!p_host.is_valid(), ERR_INVALID_PARAMETER);
	ERR_FAIL_COND_V_MSG(p_port < 1 || p_port > 65535, ERR_INVALID_PARAMETER, "The remote port number must be between 1 and 65535 (inclusive).");

	if (!_sock->is_open()) {
		IP::Type ip_type = p_host.is_ipv4() ? IP::TYPE_IPV4 : IP::TYPE_IPV6;
		Error err = _sock->open(NetSocket::TYPE_TCP, ip_type);
		if (err != OK) {
			return err;
		}
		_sock->set_blocking_enabled(false);
	}

	timeout = OS::get_singleton()->get_ticks_msec() + (((uint64_t)GLOBAL_GET("network/limits/tcp/connect_timeout_seconds")) * 1000);
	Error err = _sock->connect_to_host(p_host, p_port);

	if (err == OK) {
		status = STATUS_CONNECTED;
	} else if (err == ERR_BUSY) {
		status = STATUS_CONNECTING;
	} else {
		ERR_PRINT("Connection to remote host failed!");
		disconnect_from_host();
		return FAILED;
	}

	peer_host = p_host;
	peer_port = p_port;

	return OK;
}

Error StreamPeerTCP::write(const uint8_t *p_data, int p_bytes, int &r_sent, bool p_block) {
	ERR_FAIL_COND_V(!_sock.is_valid(), ERR_UNAVAILABLE);

	if (status != STATUS_CONNECTED) {
		return FAILED;
	}

	Error err;
	int data_to_send = p_bytes;
	const uint8_t *offset = p_data;
	int total_sent = 0;

	while (data_to_send) {
		int sent_amount = 0;
		err = _sock->send(offset, data_to_send, sent_amount);

		if (err != OK) {
			if (err != ERR_BUSY) {
				disconnect_from_host();
				return FAILED;
			}

			if (!p_block) {
				r_sent = total_sent;
				return OK;
			}

			// Block and wait for the socket to accept more data
			err = _sock->poll(NetSocket::POLL_TYPE_OUT, -1);
			if (err != OK) {
				disconnect_from_host();
				return FAILED;
			}
		} else {
			data_to_send -= sent_amount;
			offset += sent_amount;
			total_sent += sent_amount;
		}
	}

	r_sent = total_sent;

	return OK;
}

Error StreamPeerTCP::read(uint8_t *p_buffer, int p_bytes, int &r_received, bool p_block) {
	if (status != STATUS_CONNECTED) {
		return FAILED;
	}

	Error err;
	int to_read = p_bytes;
	int total_read = 0;
	r_received = 0;

	while (to_read) {
		int read = 0;
		err = _sock->recv(p_buffer + total_read, to_read, read);

		if (err != OK) {
			if (err != ERR_BUSY) {
				disconnect_from_host();
				return FAILED;
			}

			if (!p_block) {
				r_received = total_read;
				return OK;
			}

			err = _sock->poll(NetSocket::POLL_TYPE_IN, -1);

			if (err != OK) {
				disconnect_from_host();
				return FAILED;
			}

		} else if (read == 0) {
			disconnect_from_host();
			r_received = total_read;
			return ERR_FILE_EOF;

		} else {
			to_read -= read;
			total_read += read;

			if (!p_block) {
				r_received = total_read;
				return OK;
			}
		}
	}

	r_received = total_read;

	return OK;
}

void StreamPeerTCP::set_no_delay(bool p_enabled) {
	ERR_FAIL_COND(!_sock.is_valid() || !_sock->is_open());
	_sock->set_tcp_no_delay_enabled(p_enabled);
}

StreamPeerTCP::Status StreamPeerTCP::get_status() const {
	return status;
}

void StreamPeerTCP::disconnect_from_host() {
	if (_sock.is_valid() && _sock->is_open()) {
		_sock->close();
	}

	timeout = 0;
	status = STATUS_NONE;
	peer_host = IPAddress();
	peer_port = 0;
}

Error StreamPeerTCP::wait(NetSocket::PollType p_type, int timeout) {
	ERR_FAIL_COND_V(_sock.is_null() || !_sock->is_open(), ERR_UNAVAILABLE);
	return _sock->poll(p_type, timeout);
}

Error StreamPeerTCP::put_data(const uint8_t *p_data, int p_bytes) {
	int total;
	return write(p_data, p_bytes, total, true);
}

Error StreamPeerTCP::put_partial_data(const uint8_t *p_data, int p_bytes, int &r_sent) {
	return write(p_data, p_bytes, r_sent, false);
}

Error StreamPeerTCP::get_data(uint8_t *p_buffer, int p_bytes) {
	int total;
	return read(p_buffer, p_bytes, total, true);
}

Error StreamPeerTCP::get_partial_data(uint8_t *p_buffer, int p_bytes, int &r_received) {
	return read(p_buffer, p_bytes, r_received, false);
}

int StreamPeerTCP::get_available_bytes() const {
	ERR_FAIL_COND_V(!_sock.is_valid(), -1);
	return _sock->get_available_bytes();
}

IPAddress StreamPeerTCP::get_connected_host() const {
	return peer_host;
}

int StreamPeerTCP::get_connected_port() const {
	return peer_port;
}

int StreamPeerTCP::get_local_port() const {
	uint16_t local_port;
	_sock->get_socket_address(nullptr, &local_port);
	return local_port;
}

Error StreamPeerTCP::_connect(const String &p_address, int p_port) {
	IPAddress ip;
	if (p_address.is_valid_ip_address()) {
		ip = p_address;
	} else {
		ip = IP::get_singleton()->resolve_hostname(p_address);
		if (!ip.is_valid()) {
			return ERR_CANT_RESOLVE;
		}
	}

	return connect_to_host(ip, p_port);
}

void StreamPeerTCP::_bind_methods() {
	ClassDB::bind_method(D_METHOD("bind", "port", "host"), &StreamPeerTCP::bind, DEFVAL("*"));
	ClassDB::bind_method(D_METHOD("connect_to_host", "host", "port"), &StreamPeerTCP::_connect);
	ClassDB::bind_method(D_METHOD("poll"), &StreamPeerTCP::poll);
	ClassDB::bind_method(D_METHOD("get_status"), &StreamPeerTCP::get_status);
	ClassDB::bind_method(D_METHOD("get_connected_host"), &StreamPeerTCP::get_connected_host);
	ClassDB::bind_method(D_METHOD("get_connected_port"), &StreamPeerTCP::get_connected_port);
	ClassDB::bind_method(D_METHOD("get_local_port"), &StreamPeerTCP::get_local_port);
	ClassDB::bind_method(D_METHOD("disconnect_from_host"), &StreamPeerTCP::disconnect_from_host);
	ClassDB::bind_method(D_METHOD("set_no_delay", "enabled"), &StreamPeerTCP::set_no_delay);

	BIND_ENUM_CONSTANT(STATUS_NONE);
	BIND_ENUM_CONSTANT(STATUS_CONNECTING);
	BIND_ENUM_CONSTANT(STATUS_CONNECTED);
	BIND_ENUM_CONSTANT(STATUS_ERROR);
}

StreamPeerTCP::StreamPeerTCP() :
		_sock(Ref<NetSocket>(NetSocket::create())) {
}

StreamPeerTCP::~StreamPeerTCP() {
	disconnect_from_host();
}
<commit_msg>[Net] Fix StreamPeerTCP accepted status.<commit_after>/*************************************************************************/
/*  stream_peer_tcp.cpp                                                  */
/*************************************************************************/
/*                       This file is part of:                           */
/*                           GODOT ENGINE                                */
/*                      https://godotengine.org                          */
/*************************************************************************/
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur.                 */
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md).   */
/*                                                                       */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the       */
/* "Software"), to deal in the Software without restriction, including   */
/* without limitation the rights to use, copy, modify, merge, publish,   */
/* distribute, sublicense, and/or sell copies of the Software, and to    */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions:                                             */
/*                                                                       */
/* The above copyright notice and this permission notice shall be        */
/* included in all copies or substantial portions of the Software.       */
/*                                                                       */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
/*************************************************************************/

#include "stream_peer_tcp.h"

#include "core/config/project_settings.h"

Error StreamPeerTCP::poll() {
	if (status == STATUS_CONNECTED) {
		Error err;
		err = _sock->poll(NetSocket::POLL_TYPE_IN, 0);
		if (err == OK) {
			// FIN received
			if (_sock->get_available_bytes() == 0) {
				disconnect_from_host();
				return OK;
			}
		}
		// Also poll write
		err = _sock->poll(NetSocket::POLL_TYPE_IN_OUT, 0);
		if (err != OK && err != ERR_BUSY) {
			// Got an error
			disconnect_from_host();
			status = STATUS_ERROR;
			return err;
		}
	} else if (status != STATUS_CONNECTING) {
		return OK;
	}

	Error err = _sock->connect_to_host(peer_host, peer_port);

	if (err == OK) {
		status = STATUS_CONNECTED;
		return OK;
	} else if (err == ERR_BUSY) {
		// Check for connect timeout
		if (OS::get_singleton()->get_ticks_msec() > timeout) {
			disconnect_from_host();
			status = STATUS_ERROR;
			return ERR_CONNECTION_ERROR;
		}
		// Still trying to connect
		return OK;
	}

	disconnect_from_host();
	status = STATUS_ERROR;
	return ERR_CONNECTION_ERROR;
}

void StreamPeerTCP::accept_socket(Ref<NetSocket> p_sock, IPAddress p_host, uint16_t p_port) {
	_sock = p_sock;
	_sock->set_blocking_enabled(false);

	timeout = OS::get_singleton()->get_ticks_msec() + (((uint64_t)GLOBAL_GET("network/limits/tcp/connect_timeout_seconds")) * 1000);
	status = STATUS_CONNECTED;

	peer_host = p_host;
	peer_port = p_port;
}

Error StreamPeerTCP::bind(int p_port, const IPAddress &p_host) {
	ERR_FAIL_COND_V(!_sock.is_valid(), ERR_UNAVAILABLE);
	ERR_FAIL_COND_V(_sock->is_open(), ERR_ALREADY_IN_USE);
	ERR_FAIL_COND_V_MSG(p_port < 0 || p_port > 65535, ERR_INVALID_PARAMETER, "The local port number must be between 0 and 65535 (inclusive).");

	IP::Type ip_type = p_host.is_ipv4() ? IP::TYPE_IPV4 : IP::TYPE_IPV6;
	if (p_host.is_wildcard()) {
		ip_type = IP::TYPE_ANY;
	}
	Error err = _sock->open(NetSocket::TYPE_TCP, ip_type);
	if (err != OK) {
		return err;
	}
	_sock->set_blocking_enabled(false);
	return _sock->bind(p_host, p_port);
}

Error StreamPeerTCP::connect_to_host(const IPAddress &p_host, int p_port) {
	ERR_FAIL_COND_V(!_sock.is_valid(), ERR_UNAVAILABLE);
	ERR_FAIL_COND_V(status != STATUS_NONE, ERR_ALREADY_IN_USE);
	ERR_FAIL_COND_V(!p_host.is_valid(), ERR_INVALID_PARAMETER);
	ERR_FAIL_COND_V_MSG(p_port < 1 || p_port > 65535, ERR_INVALID_PARAMETER, "The remote port number must be between 1 and 65535 (inclusive).");

	if (!_sock->is_open()) {
		IP::Type ip_type = p_host.is_ipv4() ? IP::TYPE_IPV4 : IP::TYPE_IPV6;
		Error err = _sock->open(NetSocket::TYPE_TCP, ip_type);
		if (err != OK) {
			return err;
		}
		_sock->set_blocking_enabled(false);
	}

	timeout = OS::get_singleton()->get_ticks_msec() + (((uint64_t)GLOBAL_GET("network/limits/tcp/connect_timeout_seconds")) * 1000);
	Error err = _sock->connect_to_host(p_host, p_port);

	if (err == OK) {
		status = STATUS_CONNECTED;
	} else if (err == ERR_BUSY) {
		status = STATUS_CONNECTING;
	} else {
		ERR_PRINT("Connection to remote host failed!");
		disconnect_from_host();
		return FAILED;
	}

	peer_host = p_host;
	peer_port = p_port;

	return OK;
}

Error StreamPeerTCP::write(const uint8_t *p_data, int p_bytes, int &r_sent, bool p_block) {
	ERR_FAIL_COND_V(!_sock.is_valid(), ERR_UNAVAILABLE);

	if (status != STATUS_CONNECTED) {
		return FAILED;
	}

	Error err;
	int data_to_send = p_bytes;
	const uint8_t *offset = p_data;
	int total_sent = 0;

	while (data_to_send) {
		int sent_amount = 0;
		err = _sock->send(offset, data_to_send, sent_amount);

		if (err != OK) {
			if (err != ERR_BUSY) {
				disconnect_from_host();
				return FAILED;
			}

			if (!p_block) {
				r_sent = total_sent;
				return OK;
			}

			// Block and wait for the socket to accept more data
			err = _sock->poll(NetSocket::POLL_TYPE_OUT, -1);
			if (err != OK) {
				disconnect_from_host();
				return FAILED;
			}
		} else {
			data_to_send -= sent_amount;
			offset += sent_amount;
			total_sent += sent_amount;
		}
	}

	r_sent = total_sent;

	return OK;
}

Error StreamPeerTCP::read(uint8_t *p_buffer, int p_bytes, int &r_received, bool p_block) {
	if (status != STATUS_CONNECTED) {
		return FAILED;
	}

	Error err;
	int to_read = p_bytes;
	int total_read = 0;
	r_received = 0;

	while (to_read) {
		int read = 0;
		err = _sock->recv(p_buffer + total_read, to_read, read);

		if (err != OK) {
			if (err != ERR_BUSY) {
				disconnect_from_host();
				return FAILED;
			}

			if (!p_block) {
				r_received = total_read;
				return OK;
			}

			err = _sock->poll(NetSocket::POLL_TYPE_IN, -1);

			if (err != OK) {
				disconnect_from_host();
				return FAILED;
			}

		} else if (read == 0) {
			disconnect_from_host();
			r_received = total_read;
			return ERR_FILE_EOF;

		} else {
			to_read -= read;
			total_read += read;

			if (!p_block) {
				r_received = total_read;
				return OK;
			}
		}
	}

	r_received = total_read;

	return OK;
}

void StreamPeerTCP::set_no_delay(bool p_enabled) {
	ERR_FAIL_COND(!_sock.is_valid() || !_sock->is_open());
	_sock->set_tcp_no_delay_enabled(p_enabled);
}

StreamPeerTCP::Status StreamPeerTCP::get_status() const {
	return status;
}

void StreamPeerTCP::disconnect_from_host() {
	if (_sock.is_valid() && _sock->is_open()) {
		_sock->close();
	}

	timeout = 0;
	status = STATUS_NONE;
	peer_host = IPAddress();
	peer_port = 0;
}

Error StreamPeerTCP::wait(NetSocket::PollType p_type, int timeout) {
	ERR_FAIL_COND_V(_sock.is_null() || !_sock->is_open(), ERR_UNAVAILABLE);
	return _sock->poll(p_type, timeout);
}

Error StreamPeerTCP::put_data(const uint8_t *p_data, int p_bytes) {
	int total;
	return write(p_data, p_bytes, total, true);
}

Error StreamPeerTCP::put_partial_data(const uint8_t *p_data, int p_bytes, int &r_sent) {
	return write(p_data, p_bytes, r_sent, false);
}

Error StreamPeerTCP::get_data(uint8_t *p_buffer, int p_bytes) {
	int total;
	return read(p_buffer, p_bytes, total, true);
}

Error StreamPeerTCP::get_partial_data(uint8_t *p_buffer, int p_bytes, int &r_received) {
	return read(p_buffer, p_bytes, r_received, false);
}

int StreamPeerTCP::get_available_bytes() const {
	ERR_FAIL_COND_V(!_sock.is_valid(), -1);
	return _sock->get_available_bytes();
}

IPAddress StreamPeerTCP::get_connected_host() const {
	return peer_host;
}

int StreamPeerTCP::get_connected_port() const {
	return peer_port;
}

int StreamPeerTCP::get_local_port() const {
	uint16_t local_port;
	_sock->get_socket_address(nullptr, &local_port);
	return local_port;
}

Error StreamPeerTCP::_connect(const String &p_address, int p_port) {
	IPAddress ip;
	if (p_address.is_valid_ip_address()) {
		ip = p_address;
	} else {
		ip = IP::get_singleton()->resolve_hostname(p_address);
		if (!ip.is_valid()) {
			return ERR_CANT_RESOLVE;
		}
	}

	return connect_to_host(ip, p_port);
}

void StreamPeerTCP::_bind_methods() {
	ClassDB::bind_method(D_METHOD("bind", "port", "host"), &StreamPeerTCP::bind, DEFVAL("*"));
	ClassDB::bind_method(D_METHOD("connect_to_host", "host", "port"), &StreamPeerTCP::_connect);
	ClassDB::bind_method(D_METHOD("poll"), &StreamPeerTCP::poll);
	ClassDB::bind_method(D_METHOD("get_status"), &StreamPeerTCP::get_status);
	ClassDB::bind_method(D_METHOD("get_connected_host"), &StreamPeerTCP::get_connected_host);
	ClassDB::bind_method(D_METHOD("get_connected_port"), &StreamPeerTCP::get_connected_port);
	ClassDB::bind_method(D_METHOD("get_local_port"), &StreamPeerTCP::get_local_port);
	ClassDB::bind_method(D_METHOD("disconnect_from_host"), &StreamPeerTCP::disconnect_from_host);
	ClassDB::bind_method(D_METHOD("set_no_delay", "enabled"), &StreamPeerTCP::set_no_delay);

	BIND_ENUM_CONSTANT(STATUS_NONE);
	BIND_ENUM_CONSTANT(STATUS_CONNECTING);
	BIND_ENUM_CONSTANT(STATUS_CONNECTED);
	BIND_ENUM_CONSTANT(STATUS_ERROR);
}

StreamPeerTCP::StreamPeerTCP() :
		_sock(Ref<NetSocket>(NetSocket::create())) {
}

StreamPeerTCP::~StreamPeerTCP() {
	disconnect_from_host();
}
<|endoftext|>


<commit_before>#ifndef APPLICATION_HPP_DEFINED
#define APPLICATION_HPP_DEFINED

#include "QuadKey.hpp"
#include "builders/BuilderContext.hpp"
#include "builders/ExternalBuilder.hpp"
#include "builders/TileBuilder.hpp"
#include "builders/buildings/LowPolyBuildingBuilder.hpp"
#include "builders/terrain/TerraBuilder.hpp"
#include "heightmap/FlatElevationProvider.hpp"
#include "index/GeoStore.hpp"
#include "index/InMemoryElementStore.hpp"
#include "index/PersistentElementStore.hpp"
#include "index/LodRange.hpp"
#include "index/StringTable.hpp"
#include "mapcss/MapCssParser.hpp"
#include "mapcss/StyleProvider.hpp"
#include "mapcss/StyleSheet.hpp"
#include "meshing/MeshTypes.hpp"

#include <cstdint>
#include <exception>
#include <fstream>
#include <string>
#include <memory>
#include <vector>
#include <unordered_map>

// Called when mesh is built.
typedef void OnMeshBuilt(const char* name,
                         const double* vertices, int vertexCount,
                         const int* triangles, int triCount,
                         const int* colors, int colorCount);

// Called when element is loaded.
typedef void OnElementLoaded(uint64_t id, const char** tags, int size,
                             const double* vertices, int vertexCount,
                             const char** style, int styleSize);

// Called when operation is completed.
typedef void OnError(const char* errorMessage);

// Exposes API for external usage.
class Application
{
    const std::string InMemoryStorageKey = "InMemory";
    const std::string PersistentStorageKey = "OnDisk";

    // Visits element in tile.
    struct TileElementVisitor : public utymap::entities::ElementVisitor
    {
        using Tags = std::vector<utymap::formats::Tag>;
        using Coordinates = std::vector<utymap::GeoCoordinate>;

        TileElementVisitor(utymap::index::StringTable& stringTable, utymap::mapcss::StyleProvider& styleProvider,
                           int levelOfDetail, OnElementLoaded* elementCallback) :
            stringTable_(stringTable), styleProvider_(styleProvider), levelOfDetail_(levelOfDetail),
                elementCallback_(elementCallback)
        {
        }

        void visitNode(const utymap::entities::Node& node)
        {
            visitElement(node, Coordinates{ node.coordinate });
        }

        void visitWay(const utymap::entities::Way& way)
        {
            visitElement(way, way.coordinates);
        }

        void visitArea(const utymap::entities::Area& area)
        {
            visitElement(area, area.coordinates);
        }

        void visitRelation(const utymap::entities::Relation& relation)
        {
            for (const auto& element : relation.elements)
                element->accept(*this);
        }
    private:

        void visitElement(const utymap::entities::Element& element, const Coordinates& coordinates)
        {
            // convert tags
            std::vector<const char*> ctags;
            tagStrings_.reserve(element.tags.size() * 2);
            ctags.reserve(element.tags.size() * 2);
            for (auto i = 0; i < element.tags.size(); ++i) {
                const utymap::entities::Tag& tag = element.tags[i];
                tagStrings_.push_back(stringTable_.getString(tag.key));
                tagStrings_.push_back(stringTable_.getString(tag.value));
                ctags.push_back(tagStrings_[tagStrings_.size() - 2].c_str());
                ctags.push_back(tagStrings_[tagStrings_.size() - 1].c_str());
            }
            // convert geometry
            std::vector<double> coords;
            coords.reserve(coordinates.size() * 2);
            for (auto i = 0; i < coordinates.size(); ++i) {
                const utymap::GeoCoordinate coordinate = coordinates[i];
                coords.push_back(coordinate.longitude);
                coords.push_back(coordinate.latitude);
            }
            // convert style
            utymap::mapcss::Style style = styleProvider_.forElement(element, levelOfDetail_);
            std::vector<const char*> cstyles;
            tagStrings_.reserve(style.declarations.size() * 2);
            cstyles.reserve(style.declarations.size());
            for (const auto pair : style.declarations) {
                styleStrings_.push_back(stringTable_.getString(pair.first));
                styleStrings_.push_back(*pair.second->value());
                cstyles.push_back(styleStrings_[styleStrings_.size() - 2].c_str());
                cstyles.push_back(styleStrings_[styleStrings_.size() - 1].c_str());
            }

            elementCallback_(element.id, ctags.data(), ctags.size(),
                coords.data(), coords.size(), cstyles.data(), cstyles.size());
            tagStrings_.clear();
            styleStrings_.clear();
        }
        utymap::index::StringTable& stringTable_;
        utymap::mapcss::StyleProvider& styleProvider_;
        int levelOfDetail_;
        OnElementLoaded* elementCallback_;
        std::vector<std::string> tagStrings_;   // holds temporary tag strings
        std::vector<std::string> styleStrings_; // holds temporary style strings
    };

public:
    // Composes object graph.
    Application(const char* stringPath, const char* dataPath, OnError* errorCallback) :
        stringTable_(stringPath), inMemoryStore_(stringTable_), persistentStore_(dataPath, stringTable_),
        geoStore_(stringTable_), eleProvider_(), tileBuilder_(geoStore_, stringTable_, eleProvider_)
    {
        geoStore_.registerStore(InMemoryStorageKey, inMemoryStore_);
        geoStore_.registerStore(PersistentStorageKey, persistentStore_);
        registerDefaultBuilders();
    }

    // Register stylesheet.
    void registerStylesheet(const char* path)
    {
        getStyleProvider(path);
    }

    // Register element builder.
    void registerElementBuilder(const char* name)
    {
        tileBuilder_.registerElementBuilder(name, [&](const utymap::builders::BuilderContext& context) {
            return std::shared_ptr<utymap::builders::ElementBuilder>(new utymap::builders::ExternalBuilder(context));
        });
    }

    // Adds data to persistent store.
    void addToPersistentStore(const char* styleFile, const char* path, const utymap::index::LodRange& range, OnError* errorCallback)
    {
        try {
            geoStore_.add(PersistentStorageKey, path, range, *getStyleProvider(styleFile).get());
        }
        catch (std::exception ex) {
            errorCallback(ex.what());
        }
    }

    // Adds data to in-memory store.
    void addToInMemoryStore(const char* styleFile, const char* path, const utymap::index::LodRange& range, OnError* errorCallback)
    {
        try {
            geoStore_.add(InMemoryStorageKey, path, range, *getStyleProvider(styleFile).get());
        }
        catch (std::exception ex) {
            errorCallback(ex.what());
        }
    }

    // Loads tile.
    void loadTile(const char* styleFile, const utymap::QuadKey& quadKey, OnMeshBuilt* meshCallback,
                  OnElementLoaded* elementCallback, OnError* errorCallback)
    {
        try {
            utymap::mapcss::StyleProvider& styleProvider = *getStyleProvider(styleFile);
            TileElementVisitor elementVisitor(stringTable_, styleProvider, quadKey.levelOfDetail, elementCallback);
            tileBuilder_.build(quadKey, styleProvider,
                [&meshCallback](const utymap::meshing::Mesh& mesh) {
                meshCallback(mesh.name.data(),
                    mesh.vertices.data(), mesh.vertices.size(),
                    mesh.triangles.data(), mesh.triangles.size(),
                    mesh.colors.data(), mesh.colors.size());
            }, [&elementVisitor](const utymap::entities::Element& element) {
                element.accept(elementVisitor);
            });
        }
        catch (std::exception ex) {
            errorCallback(ex.what());
        }
    }

private:
    std::shared_ptr<utymap::mapcss::StyleProvider> getStyleProvider(const std::string& filePath)
    {
        auto pair = styleProviders_.find(filePath);
        if (pair != styleProviders_.end())
            return pair->second;

        std::ifstream styleFile(filePath);
        if (!styleFile.good())
            throw std::invalid_argument(std::string("Cannot read mapcss file:") + filePath);

        // NOTE not safe, but don't want to use boost filesystem only for this task.
        std::string dir = filePath.substr(0, filePath.find_last_of("\\/") + 1);
        utymap::mapcss::MapCssParser parser(dir);
        utymap::mapcss::StyleSheet stylesheet = parser.parse(styleFile);
        styleProviders_[filePath] = std::shared_ptr<utymap::mapcss::StyleProvider>(
            new utymap::mapcss::StyleProvider(stylesheet, stringTable_));
        return styleProviders_[filePath];
    }

    void registerDefaultBuilders()
    {
        tileBuilder_.registerElementBuilder("terrain", [&](const utymap::builders::BuilderContext& context) {
            return std::shared_ptr<utymap::builders::ElementBuilder>(new utymap::builders::TerraBuilder(context));
        });

        tileBuilder_.registerElementBuilder("building", [&](const utymap::builders::BuilderContext& context) {
            return std::shared_ptr<utymap::builders::ElementBuilder>(new utymap::builders::LowPolyBuildingBuilder(context));
        });
    }

    utymap::index::StringTable stringTable_;
    utymap::index::InMemoryElementStore inMemoryStore_;
    utymap::index::PersistentElementStore persistentStore_;
    utymap::index::GeoStore geoStore_;
    utymap::heightmap::FlatElevationProvider eleProvider_;
    utymap::builders::TileBuilder tileBuilder_;

    std::unordered_map<std::string, std::shared_ptr<utymap::mapcss::StyleProvider>> styleProviders_;
};

#endif // APPLICATION_HPP_DEFINED
<commit_msg>core: catch exception by reference<commit_after>#ifndef APPLICATION_HPP_DEFINED
#define APPLICATION_HPP_DEFINED

#include "QuadKey.hpp"
#include "builders/BuilderContext.hpp"
#include "builders/ExternalBuilder.hpp"
#include "builders/TileBuilder.hpp"
#include "builders/buildings/LowPolyBuildingBuilder.hpp"
#include "builders/terrain/TerraBuilder.hpp"
#include "heightmap/FlatElevationProvider.hpp"
#include "index/GeoStore.hpp"
#include "index/InMemoryElementStore.hpp"
#include "index/PersistentElementStore.hpp"
#include "index/LodRange.hpp"
#include "index/StringTable.hpp"
#include "mapcss/MapCssParser.hpp"
#include "mapcss/StyleProvider.hpp"
#include "mapcss/StyleSheet.hpp"
#include "meshing/MeshTypes.hpp"

#include <cstdint>
#include <exception>
#include <fstream>
#include <string>
#include <memory>
#include <vector>
#include <unordered_map>

// Called when mesh is built.
typedef void OnMeshBuilt(const char* name,
                         const double* vertices, int vertexCount,
                         const int* triangles, int triCount,
                         const int* colors, int colorCount);

// Called when element is loaded.
typedef void OnElementLoaded(uint64_t id, const char** tags, int size,
                             const double* vertices, int vertexCount,
                             const char** style, int styleSize);

// Called when operation is completed.
typedef void OnError(const char* errorMessage);

// Exposes API for external usage.
class Application
{
    const std::string InMemoryStorageKey = "InMemory";
    const std::string PersistentStorageKey = "OnDisk";

    // Visits element in tile.
    struct TileElementVisitor : public utymap::entities::ElementVisitor
    {
        using Tags = std::vector<utymap::formats::Tag>;
        using Coordinates = std::vector<utymap::GeoCoordinate>;

        TileElementVisitor(utymap::index::StringTable& stringTable, utymap::mapcss::StyleProvider& styleProvider,
                           int levelOfDetail, OnElementLoaded* elementCallback) :
            stringTable_(stringTable), styleProvider_(styleProvider), levelOfDetail_(levelOfDetail),
                elementCallback_(elementCallback)
        {
        }

        void visitNode(const utymap::entities::Node& node)
        {
            visitElement(node, Coordinates{ node.coordinate });
        }

        void visitWay(const utymap::entities::Way& way)
        {
            visitElement(way, way.coordinates);
        }

        void visitArea(const utymap::entities::Area& area)
        {
            visitElement(area, area.coordinates);
        }

        void visitRelation(const utymap::entities::Relation& relation)
        {
            for (const auto& element : relation.elements)
                element->accept(*this);
        }
    private:

        void visitElement(const utymap::entities::Element& element, const Coordinates& coordinates)
        {
            // convert tags
            std::vector<const char*> ctags;
            tagStrings_.reserve(element.tags.size() * 2);
            ctags.reserve(element.tags.size() * 2);
            for (auto i = 0; i < element.tags.size(); ++i) {
                const utymap::entities::Tag& tag = element.tags[i];
                tagStrings_.push_back(stringTable_.getString(tag.key));
                tagStrings_.push_back(stringTable_.getString(tag.value));
                ctags.push_back(tagStrings_[tagStrings_.size() - 2].c_str());
                ctags.push_back(tagStrings_[tagStrings_.size() - 1].c_str());
            }
            // convert geometry
            std::vector<double> coords;
            coords.reserve(coordinates.size() * 2);
            for (auto i = 0; i < coordinates.size(); ++i) {
                const utymap::GeoCoordinate coordinate = coordinates[i];
                coords.push_back(coordinate.longitude);
                coords.push_back(coordinate.latitude);
            }
            // convert style
            utymap::mapcss::Style style = styleProvider_.forElement(element, levelOfDetail_);
            std::vector<const char*> cstyles;
            tagStrings_.reserve(style.declarations.size() * 2);
            cstyles.reserve(style.declarations.size());
            for (const auto pair : style.declarations) {
                styleStrings_.push_back(stringTable_.getString(pair.first));
                styleStrings_.push_back(*pair.second->value());
                cstyles.push_back(styleStrings_[styleStrings_.size() - 2].c_str());
                cstyles.push_back(styleStrings_[styleStrings_.size() - 1].c_str());
            }

            elementCallback_(element.id, ctags.data(), ctags.size(),
                coords.data(), coords.size(), cstyles.data(), cstyles.size());
            tagStrings_.clear();
            styleStrings_.clear();
        }
        utymap::index::StringTable& stringTable_;
        utymap::mapcss::StyleProvider& styleProvider_;
        int levelOfDetail_;
        OnElementLoaded* elementCallback_;
        std::vector<std::string> tagStrings_;   // holds temporary tag strings
        std::vector<std::string> styleStrings_; // holds temporary style strings
    };

public:
    // Composes object graph.
    Application(const char* stringPath, const char* dataPath, OnError* errorCallback) :
        stringTable_(stringPath), inMemoryStore_(stringTable_), persistentStore_(dataPath, stringTable_),
        geoStore_(stringTable_), eleProvider_(), tileBuilder_(geoStore_, stringTable_, eleProvider_)
    {
        geoStore_.registerStore(InMemoryStorageKey, inMemoryStore_);
        geoStore_.registerStore(PersistentStorageKey, persistentStore_);
        registerDefaultBuilders();
    }

    // Register stylesheet.
    void registerStylesheet(const char* path)
    {
        getStyleProvider(path);
    }

    // Register element builder.
    void registerElementBuilder(const char* name)
    {
        tileBuilder_.registerElementBuilder(name, [&](const utymap::builders::BuilderContext& context) {
            return std::shared_ptr<utymap::builders::ElementBuilder>(new utymap::builders::ExternalBuilder(context));
        });
    }

    // Adds data to persistent store.
    void addToPersistentStore(const char* styleFile, const char* path, const utymap::index::LodRange& range, OnError* errorCallback)
    {
        try {
            geoStore_.add(PersistentStorageKey, path, range, *getStyleProvider(styleFile).get());
        }
        catch (std::exception& ex) {
            errorCallback(ex.what());
        }
    }

    // Adds data to in-memory store.
    void addToInMemoryStore(const char* styleFile, const char* path, const utymap::index::LodRange& range, OnError* errorCallback)
    {
        try {
            geoStore_.add(InMemoryStorageKey, path, range, *getStyleProvider(styleFile).get());
        }
        catch (std::exception& ex) {
            errorCallback(ex.what());
        }
    }

    // Loads tile.
    void loadTile(const char* styleFile, const utymap::QuadKey& quadKey, OnMeshBuilt* meshCallback,
                  OnElementLoaded* elementCallback, OnError* errorCallback)
    {
        try {
            utymap::mapcss::StyleProvider& styleProvider = *getStyleProvider(styleFile);
            TileElementVisitor elementVisitor(stringTable_, styleProvider, quadKey.levelOfDetail, elementCallback);
            tileBuilder_.build(quadKey, styleProvider,
                [&meshCallback](const utymap::meshing::Mesh& mesh) {
                meshCallback(mesh.name.data(),
                    mesh.vertices.data(), mesh.vertices.size(),
                    mesh.triangles.data(), mesh.triangles.size(),
                    mesh.colors.data(), mesh.colors.size());
            }, [&elementVisitor](const utymap::entities::Element& element) {
                element.accept(elementVisitor);
            });
        }
        catch (std::exception& ex) {
            errorCallback(ex.what());
        }
    }

private:
    std::shared_ptr<utymap::mapcss::StyleProvider> getStyleProvider(const std::string& filePath)
    {
        auto pair = styleProviders_.find(filePath);
        if (pair != styleProviders_.end())
            return pair->second;

        std::ifstream styleFile(filePath);
        if (!styleFile.good())
            throw std::invalid_argument(std::string("Cannot read mapcss file:") + filePath);

        // NOTE not safe, but don't want to use boost filesystem only for this task.
        std::string dir = filePath.substr(0, filePath.find_last_of("\\/") + 1);
        utymap::mapcss::MapCssParser parser(dir);
        utymap::mapcss::StyleSheet stylesheet = parser.parse(styleFile);
        styleProviders_[filePath] = std::shared_ptr<utymap::mapcss::StyleProvider>(
            new utymap::mapcss::StyleProvider(stylesheet, stringTable_));
        return styleProviders_[filePath];
    }

    void registerDefaultBuilders()
    {
        tileBuilder_.registerElementBuilder("terrain", [&](const utymap::builders::BuilderContext& context) {
            return std::shared_ptr<utymap::builders::ElementBuilder>(new utymap::builders::TerraBuilder(context));
        });

        tileBuilder_.registerElementBuilder("building", [&](const utymap::builders::BuilderContext& context) {
            return std::shared_ptr<utymap::builders::ElementBuilder>(new utymap::builders::LowPolyBuildingBuilder(context));
        });
    }

    utymap::index::StringTable stringTable_;
    utymap::index::InMemoryElementStore inMemoryStore_;
    utymap::index::PersistentElementStore persistentStore_;
    utymap::index::GeoStore geoStore_;
    utymap::heightmap::FlatElevationProvider eleProvider_;
    utymap::builders::TileBuilder tileBuilder_;

    std::unordered_map<std::string, std::shared_ptr<utymap::mapcss::StyleProvider>> styleProviders_;
};

#endif // APPLICATION_HPP_DEFINED
<|endoftext|>


<commit_before><commit_msg>coverity#1242512 silence Arguments in wrong order<commit_after><|endoftext|>