<commit_before>/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/*
 *     Copyright 2016 Couchbase, Inc
 *
 *   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 <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "libforestdb/forestdb.h"
#include "kvs_handle.h"
#include "file_handle.h"
#include "filemgr.h"
#include "fdb_internal.h"
#include "version.h"

extern int _kvs_cmp_name(struct avl_node *a, struct avl_node *b, void *aux);

FdbKvsHandle::FdbKvsHandle() :
    kvs(NULL), op_stats(NULL), fhandle(NULL), trie(NULL), staletree(NULL),
    seqtree(NULL), file(NULL), dhandle(NULL), bhandle(NULL),
    fileops(NULL), log_callback(), cur_header_revnum(0), rollback_revnum(0),
    last_hdr_bid(0), last_wal_flush_hdr_bid(0), kv_info_offset(0), shandle(NULL),
    seqnum(0), max_seqnum(0), txn(NULL), dirty_updates(0),
    node(NULL), num_iterators(0), handle_busy(nullptr) {

    memset(&kvs_config, 0, sizeof(kvs_config));
    memset(&config, 0, sizeof(config));
}

FdbKvsHandle::FdbKvsHandle(const FdbKvsHandle& kv_handle) {
    copyFromOtherHandle(kv_handle);
}

FdbKvsHandle::~FdbKvsHandle() {
    freeKvsInfo();
}

FdbKvsHandle& FdbKvsHandle::operator=(const FdbKvsHandle& kv_handle) {
    copyFromOtherHandle(kv_handle);
    return *this;
}

void FdbKvsHandle::freeKvsInfo() {
    if (kvs == NULL) {
        return;
    }
    delete kvs;
    kvs = NULL;
}

void FdbKvsHandle::initRootHandle() {
    if (!kvs) {
        kvs = new KvsInfo();
    }

    kvs->setKvsType(KVS_ROOT);
    kvs->setRootHandle(fhandle->getRootHandle());
    // super handle's ID is always 0
    kvs->setKvsId(0);
    // force custom cmp function
    spin_lock(&file->getKVHeader()->lock);
    kvs_config.custom_cmp = file->getKVHeader()->default_kvs_cmp;
    spin_unlock(&file->getKVHeader()->lock);
}

void FdbKvsHandle::createKvsInfo(FdbKvsHandle *root_handle,
                                 const char *kvs_name) {
    struct kvs_node query, *kvs_node;
    struct kvs_opened_node *opened_node;
    struct avl_node *a;

    if (root_handle == NULL) {
        // This handle is a super handle
        initRootHandle();
    } else {
        if (!kvs) {
            kvs = new KvsInfo();
        }
        // This handle is a sub handle (i.e., KV instance in a DB instance)
        kvs->setKvsType(KVS_SUB);
        kvs->setRootHandle(root_handle);

        if (kvs_name) {
            spin_lock(&file->getKVHeader()->lock);
            query.kvs_name = (char*)kvs_name;
            a = avl_search(file->getKVHeader()->idx_name, &query.avl_name,
                           _kvs_cmp_name);
            if (a == NULL) {
                // KV instance name is not found
                freeKvsInfo();
                spin_unlock(&file->getKVHeader()->lock);
                return;
            }
            kvs_node = _get_entry(a, struct kvs_node, avl_name);
            kvs->setKvsId(kvs_node->id);
            // force custom cmp function
            kvs_config.custom_cmp = kvs_node->custom_cmp;
            spin_unlock(&file->getKVHeader()->lock);
        } else {
            // snapshot of the root handle
            kvs->setKvsId(0);
        }

        opened_node = (struct kvs_opened_node *)
            calloc(1, sizeof(struct kvs_opened_node));
        opened_node->handle = this;

        node = opened_node;
        root_handle->fhandle->addKVHandle(&opened_node->le);
    }
}

void FdbKvsHandle::copyFromOtherHandle(const FdbKvsHandle& kv_handle) {
    kvs_config = kv_handle.kvs_config;
    file = kv_handle.file;

    freeKvsInfo();
    if (kv_handle.kvs) {
        kvs = new KvsInfo(*kv_handle.kvs);
    }

    op_stats = kv_handle.op_stats;
    fhandle = kv_handle.fhandle;

    trie = kv_handle.trie;
    if (ver_btreev2_format(file->getVersion())) {
        staletreeV2 = kv_handle.staletreeV2;
    } else {
        staletree = kv_handle.staletree;
    }
    if (kv_handle.kvs) {
        seqtrie = kv_handle.seqtrie;
    } else {
        if (ver_btreev2_format(file->getVersion())) {
            seqtreeV2 = kv_handle.seqtreeV2;
        } else {
            seqtree = kv_handle.seqtree;
        }
    }

    dhandle = kv_handle.dhandle;
    if (ver_btreev2_format(file->getVersion())) {
        bnodeMgr = kv_handle.bnodeMgr;
    } else {
        bhandle = kv_handle.bhandle;
    }
    fileops = kv_handle.fileops;

    config = kv_handle.config;
    log_callback = kv_handle.log_callback;

    cur_header_revnum.store(kv_handle.cur_header_revnum);
    rollback_revnum = kv_handle.rollback_revnum;
    last_hdr_bid.store(kv_handle.last_hdr_bid.load());
    last_wal_flush_hdr_bid = kv_handle.last_wal_flush_hdr_bid;
    kv_info_offset = kv_handle.kv_info_offset;

    shandle = kv_handle.shandle;
    seqnum = kv_handle.seqnum;
    max_seqnum = kv_handle.max_seqnum;
    filename = kv_handle.filename;
    txn = kv_handle.txn;

    dirty_updates = kv_handle.dirty_updates;
    node = kv_handle.node;
    num_iterators = kv_handle.num_iterators;
    handle_busy.store(kv_handle.handle_busy.load());
}

void FdbKvsHandle::resetIOHandles() {
    dhandle = nullptr;
    bnodeMgr = nullptr;
    trie = nullptr;
    seqtrie = nullptr;
    staletreeV2 = nullptr;
}

fdb_status FdbKvsHandle::freeIOHandles(bool useBtreeV2) { // LCOV_EXCL_START
    if (useBtreeV2) {
        delete bnodeMgr;
        delete dhandle;
        delete trie;
        if (kvs) {
            delete seqtrie;
        } else {
            delete seqtreeV2;
        }
        delete staletreeV2;
    } else {
        delete bhandle;
        delete dhandle;
        delete trie;
        if (kvs) {
            delete seqtrie;
        } else {
            if (seqtree) {
                delete seqtree->getKVOps();
                delete seqtree;
            }
        }
        if (staletree) {
            delete staletree->getKVOps();
            delete staletree;
        }
    }
    return FDB_RESULT_ALLOC_FAIL;
} // LCOV_EXCL_STOP

void FdbKvsHandle::initBusy() {
    handle_busy = nullptr;
}

bool FdbKvsHandle::beginBusy(func_name_t funcName) {
    func_name_t inverse = nullptr;
    if (handle_busy.compare_exchange_strong(inverse, funcName)) {
        return true;
    }
    func_name_t curFuncName = handle_busy.load();
    if (!curFuncName) {
        curFuncName = "(unknown)";// race condition; value lost before read
    }
    fdb_log(&log_callback, FDB_RESULT_HANDLE_BUSY,
            "%s() failed because handle %p is in use by %s()", funcName,
            reinterpret_cast<void *>(this), curFuncName);
    return false;
}

func_name_t FdbKvsHandle::suspendBusy(void) {
    func_name_t val = handle_busy.load();
    handle_busy.store(nullptr);
    return val;
}

bool FdbKvsHandle::resumeBusy(func_name_t funcName) {
    return beginBusy(funcName);
}

bool FdbKvsHandle::endBusy(func_name_t funcName) {
    func_name_t inverse = funcName;
    return handle_busy.compare_exchange_strong(inverse, nullptr);
}
<commit_msg>MB-22101 Do not compare __FUNCTION__ in endBusy() on Windows<commit_after>/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/*
 *     Copyright 2016 Couchbase, Inc
 *
 *   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 <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "libforestdb/forestdb.h"
#include "kvs_handle.h"
#include "file_handle.h"
#include "filemgr.h"
#include "fdb_internal.h"
#include "version.h"

extern int _kvs_cmp_name(struct avl_node *a, struct avl_node *b, void *aux);

FdbKvsHandle::FdbKvsHandle() :
    kvs(NULL), op_stats(NULL), fhandle(NULL), trie(NULL), staletree(NULL),
    seqtree(NULL), file(NULL), dhandle(NULL), bhandle(NULL),
    fileops(NULL), log_callback(), cur_header_revnum(0), rollback_revnum(0),
    last_hdr_bid(0), last_wal_flush_hdr_bid(0), kv_info_offset(0), shandle(NULL),
    seqnum(0), max_seqnum(0), txn(NULL), dirty_updates(0),
    node(NULL), num_iterators(0), handle_busy(nullptr) {

    memset(&kvs_config, 0, sizeof(kvs_config));
    memset(&config, 0, sizeof(config));
}

FdbKvsHandle::FdbKvsHandle(const FdbKvsHandle& kv_handle) {
    copyFromOtherHandle(kv_handle);
}

FdbKvsHandle::~FdbKvsHandle() {
    freeKvsInfo();
}

FdbKvsHandle& FdbKvsHandle::operator=(const FdbKvsHandle& kv_handle) {
    copyFromOtherHandle(kv_handle);
    return *this;
}

void FdbKvsHandle::freeKvsInfo() {
    if (kvs == NULL) {
        return;
    }
    delete kvs;
    kvs = NULL;
}

void FdbKvsHandle::initRootHandle() {
    if (!kvs) {
        kvs = new KvsInfo();
    }

    kvs->setKvsType(KVS_ROOT);
    kvs->setRootHandle(fhandle->getRootHandle());
    // super handle's ID is always 0
    kvs->setKvsId(0);
    // force custom cmp function
    spin_lock(&file->getKVHeader()->lock);
    kvs_config.custom_cmp = file->getKVHeader()->default_kvs_cmp;
    spin_unlock(&file->getKVHeader()->lock);
}

void FdbKvsHandle::createKvsInfo(FdbKvsHandle *root_handle,
                                 const char *kvs_name) {
    struct kvs_node query, *kvs_node;
    struct kvs_opened_node *opened_node;
    struct avl_node *a;

    if (root_handle == NULL) {
        // This handle is a super handle
        initRootHandle();
    } else {
        if (!kvs) {
            kvs = new KvsInfo();
        }
        // This handle is a sub handle (i.e., KV instance in a DB instance)
        kvs->setKvsType(KVS_SUB);
        kvs->setRootHandle(root_handle);

        if (kvs_name) {
            spin_lock(&file->getKVHeader()->lock);
            query.kvs_name = (char*)kvs_name;
            a = avl_search(file->getKVHeader()->idx_name, &query.avl_name,
                           _kvs_cmp_name);
            if (a == NULL) {
                // KV instance name is not found
                freeKvsInfo();
                spin_unlock(&file->getKVHeader()->lock);
                return;
            }
            kvs_node = _get_entry(a, struct kvs_node, avl_name);
            kvs->setKvsId(kvs_node->id);
            // force custom cmp function
            kvs_config.custom_cmp = kvs_node->custom_cmp;
            spin_unlock(&file->getKVHeader()->lock);
        } else {
            // snapshot of the root handle
            kvs->setKvsId(0);
        }

        opened_node = (struct kvs_opened_node *)
            calloc(1, sizeof(struct kvs_opened_node));
        opened_node->handle = this;

        node = opened_node;
        root_handle->fhandle->addKVHandle(&opened_node->le);
    }
}

void FdbKvsHandle::copyFromOtherHandle(const FdbKvsHandle& kv_handle) {
    kvs_config = kv_handle.kvs_config;
    file = kv_handle.file;

    freeKvsInfo();
    if (kv_handle.kvs) {
        kvs = new KvsInfo(*kv_handle.kvs);
    }

    op_stats = kv_handle.op_stats;
    fhandle = kv_handle.fhandle;

    trie = kv_handle.trie;
    if (ver_btreev2_format(file->getVersion())) {
        staletreeV2 = kv_handle.staletreeV2;
    } else {
        staletree = kv_handle.staletree;
    }
    if (kv_handle.kvs) {
        seqtrie = kv_handle.seqtrie;
    } else {
        if (ver_btreev2_format(file->getVersion())) {
            seqtreeV2 = kv_handle.seqtreeV2;
        } else {
            seqtree = kv_handle.seqtree;
        }
    }

    dhandle = kv_handle.dhandle;
    if (ver_btreev2_format(file->getVersion())) {
        bnodeMgr = kv_handle.bnodeMgr;
    } else {
        bhandle = kv_handle.bhandle;
    }
    fileops = kv_handle.fileops;

    config = kv_handle.config;
    log_callback = kv_handle.log_callback;

    cur_header_revnum.store(kv_handle.cur_header_revnum);
    rollback_revnum = kv_handle.rollback_revnum;
    last_hdr_bid.store(kv_handle.last_hdr_bid.load());
    last_wal_flush_hdr_bid = kv_handle.last_wal_flush_hdr_bid;
    kv_info_offset = kv_handle.kv_info_offset;

    shandle = kv_handle.shandle;
    seqnum = kv_handle.seqnum;
    max_seqnum = kv_handle.max_seqnum;
    filename = kv_handle.filename;
    txn = kv_handle.txn;

    dirty_updates = kv_handle.dirty_updates;
    node = kv_handle.node;
    num_iterators = kv_handle.num_iterators;
    handle_busy.store(kv_handle.handle_busy.load());
}

void FdbKvsHandle::resetIOHandles() {
    dhandle = nullptr;
    bnodeMgr = nullptr;
    trie = nullptr;
    seqtrie = nullptr;
    staletreeV2 = nullptr;
}

fdb_status FdbKvsHandle::freeIOHandles(bool useBtreeV2) { // LCOV_EXCL_START
    if (useBtreeV2) {
        delete bnodeMgr;
        delete dhandle;
        delete trie;
        if (kvs) {
            delete seqtrie;
        } else {
            delete seqtreeV2;
        }
        delete staletreeV2;
    } else {
        delete bhandle;
        delete dhandle;
        delete trie;
        if (kvs) {
            delete seqtrie;
        } else {
            if (seqtree) {
                delete seqtree->getKVOps();
                delete seqtree;
            }
        }
        if (staletree) {
            delete staletree->getKVOps();
            delete staletree;
        }
    }
    return FDB_RESULT_ALLOC_FAIL;
} // LCOV_EXCL_STOP

void FdbKvsHandle::initBusy() {
    handle_busy = nullptr;
}

bool FdbKvsHandle::beginBusy(func_name_t funcName) {
    func_name_t inverse = nullptr;
    if (handle_busy.compare_exchange_strong(inverse, funcName)) {
        return true;
    }
    func_name_t curFuncName = handle_busy.load();
    if (!curFuncName) {
        curFuncName = "(unknown)";// race condition; value lost before read
    }
    fdb_log(&log_callback, FDB_RESULT_HANDLE_BUSY,
            "%s() failed because handle %p is in use by %s()", funcName,
            reinterpret_cast<void *>(this), curFuncName);
    return false;
}

func_name_t FdbKvsHandle::suspendBusy(void) {
    func_name_t val = handle_busy.load();
    handle_busy.store(nullptr);
    return val;
}

bool FdbKvsHandle::resumeBusy(func_name_t funcName) {
    return beginBusy(funcName);
}

bool FdbKvsHandle::endBusy(func_name_t funcName) {
    // Windows does not provide constant __FUNCTION__ (i.e., funcName)
    // macro value, so it may have different memory address although
    // caller function names are the same.
    // Hence we always clear 'handle_busy' value regardless of its
    // current value.
#if defined(WIN32) || defined(_WIN32)
    (void)funcName;
    handle_busy.store(nullptr);
    return true;
#else
    func_name_t inverse = funcName;
    return handle_busy.compare_exchange_strong(inverse, nullptr);
#endif
}
<|endoftext|>


<commit_before>/*
 *
 * Author: Jeffrey Leung
 * Last edited: 2015-09-19
 *
 * This C++ file contains the implementation of the Labyrinth class, which uses
 * the Room class to create a 2-d mapping for a game.
 *
 */

#include <iostream>

#include "../include/room_properties.hpp"
#include "../include/room.hpp"
#include "../include/labyrinth.hpp"

// Parameterized constructor
Labyrinth::Labyrinth( unsigned int x_size, unsigned int y_size )
{
  rooms_ = new Room*[ y_size ];
  for( unsigned int i = 0; i < y_size; ++i )
  {
    rooms_[i] = new Room[ x_size ];
  }

  x_size_ = x_size;
  y_size_ = y_size;
}
<commit_msg>Implementing Labyrinth destructor.<commit_after>/*
 *
 * Author: Jeffrey Leung
 * Last edited: 2015-09-19
 *
 * This C++ file contains the implementation of the Labyrinth class, which uses
 * the Room class to create a 2-d mapping for a game.
 *
 */

#include <iostream>

#include "../include/room_properties.hpp"
#include "../include/room.hpp"
#include "../include/labyrinth.hpp"

// Parameterized constructor
Labyrinth::Labyrinth( unsigned int x_size, unsigned int y_size )
{
  rooms_ = new Room*[ y_size ];
  for( unsigned int i = 0; i < y_size; ++i )
  {
    rooms_[i] = new Room[ x_size ];
  }

  x_size_ = x_size;
  y_size_ = y_size;
}

// Destructor
Labyrinth::~Labyrinth()
{
  for( unsigned int i = 0; i < y_size_; ++i )
  {
    delete [] rooms_[i];
  }
  delete [] rooms_;
}
<|endoftext|>


<commit_before>/*
 *
 * Author: Jeffrey Leung
 * Last edited: 2015-09-19
 *
 * This C++ file contains the implementation of the Labyrinth class, which uses
 * the Room class to create a 2-d mapping for a game.
 *
 */

#include <cmath>
#include <iostream>
#include <stdexcept>

#include "../include/room_properties.hpp"
#include "../include/room.hpp"
#include "../include/coordinate.hpp"
#include "../include/labyrinth.hpp"

// CONSTRUCTOR/DESTRUCTOR:

// Parameterized constructor
// An exception is thrown if:
//   A size of 0 is given (invalid_argument)
Labyrinth::Labyrinth( unsigned int x_size, unsigned int y_size )
{
  if( x_size == 0 )
  {
    if( y_size == 0 )
    {
      throw std::invalid_argument( "Error: Labyrinth() was given empty x and "\
        "y sizes." );
    }
    else
    {
      throw std::invalid_argument( "Error: Labyrinth() was given an empty "\
        "x size." );
    }
  }
  else if( y_size == 0 )
  {
    throw std::invalid_argument( "Error: Labyrinth() was given an empty "\
      "y size." );
  }

  rooms_ = new Room*[ y_size ];
  for( unsigned int i = 0; i < y_size; ++i )
  {
    rooms_[i] = new Room[ x_size ];
  }

  x_size_ = x_size;
  y_size_ = y_size;
}

// Destructor
Labyrinth::~Labyrinth()
{
  for( unsigned int i = 0; i < y_size_; ++i )
  {
    delete [] rooms_[i];
  }
  delete [] rooms_;
}

// SETUP:

// This method connects two Rooms by breaking their walls.
// An exception is thrown if:
//   The Rooms are already connected (logic_error)
//   The Rooms are the same (logic_error)
//   The Rooms are not adjacent (logic_error)
//   One or both Rooms are outside the Labyrinth (invalid_argument)
void Labyrinth::ConnectRooms( Coordinate rm_1, Coordinate rm_2 )
{
  if( rm_1 == rm_2 )
  {
    throw std::logic_error( "Error: ConnectRooms() was given the same "\
      "coordinate for the two Rooms." );
  }

  else if( !WithinBounds(rm_1) || !WithinBounds(rm_2) )
  {
    if( !WithinBounds(rm_1) )
    {
      if( !WithinBounds(rm_2) )
      {
        throw std::invalid_argument( "Error: ConnectRooms() was given invalid "\
          "coordinates for both rm_1 and rm_2." );
      }
      else
      {
        throw std::invalid_argument( "Error: ConnectRooms() was given "\
          "an invalid coordinate for rm_1." );
      }
    }

    else
    {
      throw std::invalid_argument( "Error: ConnectRooms() was given an "\
        "invalid coordinate for rm_2." );
    }
  }

  else if( !IsAdjacent(rm_1, rm_2) )
  {
    throw std::logic_error( "Error: ConnectRooms() was given two coordinates "\
      "which are not adjacent, and therefore cannot be connected." );
  }

  unsigned int x_distance = rm_2.x - rm_1.x;
  unsigned int y_distance = rm_2.y - rm_1.y;

  Direction break_wall_1;
  Direction break_wall_2;

  if( x_distance == 0 )
  {
    if( y_distance > 0 )  // rm_1 is north of rm_2
    {
      break_wall_1 = Direction::kSouth;
      break_wall_2 = Direction::kNorth;
    }
    else  // rm_1 is south of rm_2
    {
      break_wall_1 = Direction::kNorth;
      break_wall_2 = Direction::kSouth;
    }
  }

  else if( y_distance == 0 )
  {
    if( x_distance > 0 )  // rm_1 is west of rm_2
    {
      break_wall_1 = Direction::kEast;
      break_wall_2 = Direction::kWest;
    }
    else  // rm_1 is east of rm_2
    {
      break_wall_1 = Direction::kWest;
      break_wall_2 = Direction::kEast;
    }
  }

  else
  {
    throw std::logic_error( "Error: ConnectRooms() called IsAdjacent(), "\
      "which should have evaluated to false, but did not." );
  }

  RoomAt(rm_1).BreakWall(break_wall_1);
  RoomAt(rm_2).BreakWall(break_wall_2);
  return;
}

// PLAY:

// This method returns the type of RoomBorder in the given direction.
// An exception is thrown if:
//   The Room is outside the Labyrinth (invalid_argument)
//   Direction d is kNone (invalid_argument)
RoomBorder Labyrinth::DirectionCheck( Coordinate rm, Direction d ) const
{
  if( !WithinBounds(rm) )
  {
    throw std::invalid_argument( "Error: DirectionCheck() was given a "\
      "Coordinate outside of the Labyrinth." );
  }
  else if( d == Direction::kNone )
  {
    throw std::invalid_argument( "Error: DirectionCheck() was given an "\
      "invalid direction (kNone)." );
  }

  return RoomAt(rm).DirectionCheck(d);
}

// PRIVATE METHODS:

// This private method returns a reference to the Room at the given
// coordinate.
// An exception is thrown if:
//   The Room is outside the Labyrinth (invalid_argument)
Room& Labyrinth::RoomAt( Coordinate rm ) const
{
  if( !WithinBounds(rm) )
  {
    throw std::invalid_argument( "Error: RoomAt() was given an invalid "\
      "coordinate for rm." );
  }
  return rooms_[rm.x][rm.y];
}

// This private method returns true if the Room is within the bounds of
// the Labyrinth, and false otherwise.
bool Labyrinth::WithinBounds( Coordinate rm ) const
{
  return(    0 < rm.x    &&
          rm.x < x_size_ &&
             0 < rm.y    &&
          rm.y < y_size_ );
}

// This private method returns true if the two Rooms are adjacent, and
// false otherwise.
// An exception is thrown if:
//   One or both Rooms are outside the Labyrinth (invalid_argument)
//   The same Room is given twice (logic_error)
bool Labyrinth::IsAdjacent( Coordinate rm_1, Coordinate rm_2 ) const
{
  if( !WithinBounds(rm_1) )
  {
    if( !WithinBounds(rm_2) )
    {
      throw std::invalid_argument( "Error: IsAdjacent() was given invalid "\
      "coordinates for both rm_1 and rm_2." );
    }
    else
    {
      throw std::invalid_argument( "Error: IsAdjacent() was given an invalid "\
      "coordinate for rm_1." );
    }
  }
  else if( !WithinBounds(rm_2) )
  {
    throw std::invalid_argument( "Error: IsAdjacent() was given an invalid "\
      "coordinate for rm_2." );
  }

  if( rm_1 == rm_2 )
  {
    throw std::logic_error( "Error: IsAdjacent() was given the same "\
      "coordinate for the Rooms." );
  }

  unsigned int x_distance = abs( rm_1.x - rm_2.x );
  unsigned int y_distance = abs( rm_1.y - rm_2.y );

  if( ( x_distance == 0 && y_distance == 1 ) ||
      ( x_distance == 1 && y_distance == 0 ) )
  {
    return true;
  }
  return false;
}
<commit_msg>Removing unchecked manipulation of uncasted unsigned ints in Laby IsAdjacent().<commit_after>/*
 *
 * Author: Jeffrey Leung
 * Last edited: 2015-12-27
 *
 * This C++ file contains the implementation of the Labyrinth class, which uses
 * the Room class to create a 2-d mapping for a game.
 *
 */

#include <cmath>
#include <iostream>
#include <stdexcept>

#include "../include/room_properties.hpp"
#include "../include/room.hpp"
#include "../include/coordinate.hpp"
#include "../include/labyrinth.hpp"

// CONSTRUCTOR/DESTRUCTOR:

// Parameterized constructor
// An exception is thrown if:
//   A size of 0 is given (invalid_argument)
Labyrinth::Labyrinth( unsigned int x_size, unsigned int y_size )
{
  if( x_size == 0 )
  {
    if( y_size == 0 )
    {
      throw std::invalid_argument( "Error: Labyrinth() was given empty x and "\
        "y sizes." );
    }
    else
    {
      throw std::invalid_argument( "Error: Labyrinth() was given an empty "\
        "x size." );
    }
  }
  else if( y_size == 0 )
  {
    throw std::invalid_argument( "Error: Labyrinth() was given an empty "\
      "y size." );
  }

  rooms_ = new Room*[ y_size ];
  for( unsigned int i = 0; i < y_size; ++i )
  {
    rooms_[i] = new Room[ x_size ];
  }

  x_size_ = x_size;
  y_size_ = y_size;
}

// Destructor
Labyrinth::~Labyrinth()
{
  for( unsigned int i = 0; i < y_size_; ++i )
  {
    delete [] rooms_[i];
  }
  delete [] rooms_;
}

// SETUP:

// This method connects two Rooms by breaking their walls.
// An exception is thrown if:
//   The Rooms are already connected (logic_error)
//   The Rooms are the same (logic_error)
//   The Rooms are not adjacent (logic_error)
//   One or both Rooms are outside the Labyrinth (invalid_argument)
void Labyrinth::ConnectRooms( Coordinate rm_1, Coordinate rm_2 )
{
  if( rm_1 == rm_2 )
  {
    throw std::logic_error( "Error: ConnectRooms() was given the same "\
      "coordinate for the two Rooms." );
  }

  else if( !WithinBounds(rm_1) || !WithinBounds(rm_2) )
  {
    if( !WithinBounds(rm_1) )
    {
      if( !WithinBounds(rm_2) )
      {
        throw std::invalid_argument( "Error: ConnectRooms() was given invalid "\
          "coordinates for both rm_1 and rm_2." );
      }
      else
      {
        throw std::invalid_argument( "Error: ConnectRooms() was given "\
          "an invalid coordinate for rm_1." );
      }
    }

    else
    {
      throw std::invalid_argument( "Error: ConnectRooms() was given an "\
        "invalid coordinate for rm_2." );
    }
  }

  else if( !IsAdjacent(rm_1, rm_2) )
  {
    throw std::logic_error( "Error: ConnectRooms() was given two coordinates "\
      "which are not adjacent, and therefore cannot be connected." );
  }

  unsigned int x_distance = rm_2.x - rm_1.x;
  unsigned int y_distance = rm_2.y - rm_1.y;

  Direction break_wall_1;
  Direction break_wall_2;

  if( x_distance == 0 )
  {
    if( y_distance > 0 )  // rm_1 is north of rm_2
    {
      break_wall_1 = Direction::kSouth;
      break_wall_2 = Direction::kNorth;
    }
    else  // rm_1 is south of rm_2
    {
      break_wall_1 = Direction::kNorth;
      break_wall_2 = Direction::kSouth;
    }
  }

  else if( y_distance == 0 )
  {
    if( x_distance > 0 )  // rm_1 is west of rm_2
    {
      break_wall_1 = Direction::kEast;
      break_wall_2 = Direction::kWest;
    }
    else  // rm_1 is east of rm_2
    {
      break_wall_1 = Direction::kWest;
      break_wall_2 = Direction::kEast;
    }
  }

  else
  {
    throw std::logic_error( "Error: ConnectRooms() called IsAdjacent(), "\
      "which should have evaluated to false, but did not." );
  }

  RoomAt(rm_1).BreakWall(break_wall_1);
  RoomAt(rm_2).BreakWall(break_wall_2);
  return;
}

// PLAY:

// This method returns the type of RoomBorder in the given direction.
// An exception is thrown if:
//   The Room is outside the Labyrinth (invalid_argument)
//   Direction d is kNone (invalid_argument)
RoomBorder Labyrinth::DirectionCheck( Coordinate rm, Direction d ) const
{
  if( !WithinBounds(rm) )
  {
    throw std::invalid_argument( "Error: DirectionCheck() was given a "\
      "Coordinate outside of the Labyrinth." );
  }
  else if( d == Direction::kNone )
  {
    throw std::invalid_argument( "Error: DirectionCheck() was given an "\
      "invalid direction (kNone)." );
  }

  return RoomAt(rm).DirectionCheck(d);
}

// PRIVATE METHODS:

// This private method returns a reference to the Room at the given
// coordinate.
// An exception is thrown if:
//   The Room is outside the Labyrinth (invalid_argument)
Room& Labyrinth::RoomAt( Coordinate rm ) const
{
  if( !WithinBounds(rm) )
  {
    throw std::invalid_argument( "Error: RoomAt() was given an invalid "\
      "coordinate for rm." );
  }
  return rooms_[rm.x][rm.y];
}

// This private method returns true if the Room is within the bounds of
// the Labyrinth, and false otherwise.
bool Labyrinth::WithinBounds( Coordinate rm ) const
{
  return(    0 < rm.x    &&
          rm.x < x_size_ &&
             0 < rm.y    &&
          rm.y < y_size_ );
}

// This private method returns true if the two Rooms are adjacent, and
// false otherwise.
// An exception is thrown if:
//   One or both Rooms are outside the Labyrinth (invalid_argument)
//   The same Room is given twice (logic_error)
bool Labyrinth::IsAdjacent( Coordinate rm_1, Coordinate rm_2 ) const
{
  if( !WithinBounds(rm_1) )
  {
    if( !WithinBounds(rm_2) )
    {
      throw std::invalid_argument( "Error: IsAdjacent() was given invalid "\
      "coordinates for both rm_1 and rm_2." );
    }
    else
    {
      throw std::invalid_argument( "Error: IsAdjacent() was given an invalid "\
      "coordinate for rm_1." );
    }
  }
  else if( !WithinBounds(rm_2) )
  {
    throw std::invalid_argument( "Error: IsAdjacent() was given an invalid "\
      "coordinate for rm_2." );
  }

  if( rm_1 == rm_2 )
  {
    throw std::logic_error( "Error: IsAdjacent() was given the same "\
      "coordinate for the Rooms." );
  }

  unsigned int x_distance;
  if( rm_1.x > rm_2.x )
  {
    x_distance = rm_1.x - rm_2.x;
  }
  else
  {
    x_distance = rm_2.x - rm_1.x;
  }

  unsigned int y_distance;
  if( rm_1.y > rm_2.y )
  {
    y_distance = rm_1.y - rm_2.y;
  }
  else
  {
    y_distance = rm_2.y - rm_1.y;
  }

  if( ( x_distance == 0 && y_distance == 1 ) ||
      ( x_distance == 1 && y_distance == 0 ) )
  {
    return true;
  }
  return false;
}
<|endoftext|>


<commit_before>//============================================================================
// MCKL/include/mckl/core/memory.hpp
//----------------------------------------------------------------------------
// MCKL: Monte Carlo Kernel Library
//----------------------------------------------------------------------------
// Copyright (c) 2013-2017, Yan Zhou
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
//   Redistributions of source code must retain the above copyright notice,
//   this list of conditions and the following disclaimer.
//
//   Redistributions in binary form must reproduce the above copyright notice,
//   this list of conditions and the following disclaimer in the documentation
//   and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//============================================================================

#ifndef MCKL_CORE_MEMORY_HPP
#define MCKL_CORE_MEMORY_HPP

#include <mckl/internal/config.h>

#include <cstdlib>
#include <cstring>
#include <memory>
#include <new>
#include <vector>

#if MCKL_HAS_POSIX
#include <stdlib.h>
#elif defined(MCKL_MSVC)
#include <malloc.h>
#endif

#if MCKL_HAS_JEMALLOC
#include <jemalloc/jemalloc.h>
#endif

#if MCKL_HAS_TBB
#include <tbb/scalable_allocator.h>
#endif

/// \brief The default alignment for scalar type
/// \ingroup Config
#ifndef MCKL_ALIGNMENT
#define MCKL_ALIGNMENT 32
#endif

/// \brief The minimum alignment for any type
/// \ingroup Config
#ifndef MCKL_MINIMUM_ALIGNMENT
#define MCKL_MINIMUM_ALIGNMENT 16
#endif

#if MCKL_ALIGNMENT < MCKL_MINIMUM_ALIGNMENT
#undef MCKL_ALIGNEMNT
#define MCKL_ALIGNMENT MCKL_MINIMUM_ALIGNMENT
#endif

/// \brief Default allocation type
/// \ingroup Config
#ifndef MCKL_MEMORY_TYPE
#if MCKL_USE_JEMALLOC
#define MCKL_MEMORY_TYPE ::mckl::MemoryJEM
#elif MCKL_USE_TBB_MALLOC
#define MCKL_MEMORY_TYPE ::mckl::MemoryTBB
#elif MCKL_HAS_POSIX || defined(MCKL_MSVC)
#define MCKL_MEMORY_TYPE ::mckl::MemorySYS
#else
#define MCKL_MEMORY_TYPE ::mckl::MemorySTD
#endif
#endif

namespace mckl
{

namespace internal
{

template <std::size_t Alignment, typename UIntType>
inline constexpr std::size_t alignment_round0(UIntType n)
{
    static_assert(Alignment != 0 && (Alignment & (Alignment - 1)) == 0,
        "**alignment_round0** used with Alignment other than a power of two "
        "positive integer");

    static_assert(Alignment >= sizeof(void *),
        "**alignment_round0** used with Alignment less than sizeof(void *)");

    static_assert(std::is_unsigned<UIntType>::value,
        "alignment_round0** used with UIntType other than unsigned integer "
        "types");

    constexpr std::size_t addn = Alignment - 1;
    constexpr std::size_t mask = ~addn;

    return (n + addn) & mask;
}

template <std::size_t Alignment, typename UIntType>
inline constexpr std::size_t alignment_round(UIntType n)
{
    return n == 0 ? Alignment : alignment_round0<Alignment>(n);
}

} // namespace mckl::internal

/// \brief Alignment of types
/// \ingroup Core
///
/// \details
/// * For scalar types, defined to the maximum of `MCKL_ALIGNMENT` and
/// `alignof(T)`
/// * For all other types, define to the maximum of `MCKL_MINIMUM_ALIGNMENT`
/// and `alignof(T)` as `value`.
template <typename T>
constexpr std::size_t AlignOf = std::integral_constant<std::size_t,
    std::is_scalar<T>::value ?
        (alignof(T) > MCKL_ALIGNMENT ? alignof(T) : MCKL_ALIGNMENT) :
        (alignof(T) > MCKL_MINIMUM_ALIGNMENT ? alignof(T) :
                                               MCKL_MINIMUM_ALIGNMENT)>::value;

/// \brief Memory allocation using the standard library
/// \ingroup Core
///
/// \tparam Alignment The alignment of the allocated memory
template <std::size_t Alignment>
class MemorySTD
{
    static_assert(Alignment != 0 && (Alignment & (Alignment - 1)) == 0,
        "**MemorySTD** used with Alignment other than a power of two positive "
        "integer");

    static_assert(Alignment >= sizeof(void *),
        "**MemorySTD** used with Alignment less than sizeof(void *)");

    public:
    static constexpr std::size_t alignment() { return Alignment; }

    static void *allocate(std::size_t n, const void * = nullptr) noexcept
    {
        const std::size_t m = internal::alignment_round0<Alignment>(
            n + sizeof(std::uintptr_t) + Alignment);
        if (m < n)
            return nullptr;

        std::uintptr_t *const address =
            static_cast<std::uintptr_t *>(std::malloc(m));
        if (address == nullptr)
            return nullptr;

        std::uintptr_t *const ptr = reinterpret_cast<std::uintptr_t *>(
            internal::alignment_round0<Alignment>(
                reinterpret_cast<std::uintptr_t>(address + 1)));
        ptr[-1] = reinterpret_cast<std::uintptr_t>(address);

        return static_cast<void *>(ptr);
    }

    static void deallocate(void *ptr, std::size_t = 0) noexcept
    {
        if (ptr != nullptr) {
            const std::uintptr_t address =
                static_cast<std::uintptr_t *>(ptr)[-1];
            std::free(reinterpret_cast<void *>(address));
        }
    }
}; // class MemorySTD

#if MCKL_HAS_POSIX

/// \brief Memory allocation using native system functions
/// \ingroup Core
///
/// \tparam Alignment The alignment of the allocated memory
template <std::size_t Alignment>
class MemorySYS
{
    static_assert(Alignment != 0 && (Alignment & (Alignment - 1)) == 0,
        "**MemorySYS** used with Alignment other than a power of two positive "
        "integer");

    static_assert(Alignment >= sizeof(void *),
        "**MemorySYS** used with Alignment less than sizeof(void *)");

    public:
    static constexpr std::size_t alignment() { return Alignment; }

    static void *allocate(std::size_t n, const void * = nullptr) noexcept
    {
        const std::size_t m = internal::alignment_round<Alignment>(n);

        if (m < n)
            return nullptr;

        void *ptr = nullptr;
        if (::posix_memalign(&ptr, Alignment, m) != 0)
            ptr = nullptr;

        return ptr;
    }

    static void deallocate(void *ptr, std::size_t = 0) noexcept
    {
        if (ptr != nullptr)
            ::free(ptr);
    }
}; // class MemorySYS

#elif defined(MCKL_MSVC)

template <std::size_t Alignment>
class MemorySYS
{
    static_assert(Alignment != 0 && (Alignment & (Alignment - 1)) == 0,
        "**MemorySYS** used with Alignment other than a power of two positive "
        "integer");

    static_assert(Alignment >= sizeof(void *),
        "**MemorySYS** used with Alignment less than sizeof(void *)");

    public:
    static constexpr std::size_t alignment() { return Alignment; }

    static void *allocate(std::size_t n, const void * = nullptr) noexcept
    {
        const std::size_t m = internal::alignment_round<Alignment>(n);

        return m < n ? nullptr : _aligned_malloc(m, Alignment);
    }

    static void deallocate(void *ptr, std::size_t = 0) noexcept
    {
        if (ptr != nullptr)
            _aligned_free(ptr);
    }
}; // class MemorySYS

#endif // MCKL_HAS_POSIX

#if MCKL_HAS_JEMALLOC

/// \brief Memory allocation using jemalloc
/// \ingroup Core
///
/// \tparam Alignment The alignment of the allocated memory
template <std::size_t Alignment>
class MemoryJEM
{
    static_assert(Alignment != 0 && (Alignment & (Alignment - 1)) == 0,
        "**MemoryJEM** used with Alignment other than a power of two positive "
        "integer");

    static_assert(Alignment >= sizeof(void *),
        "**MemoryJEM** used with Alignment less than sizeof(void *)");

    public:
    static constexpr std::size_t alignment() { return Alignment; }

    static void *allocate(std::size_t n, const void * = nullptr) noexcept
    {
        const std::size_t m = internal::alignment_round<Alignment>(n);

        return m < n ? nullptr : ::je_aligned_alloc(Alignment, m);
    }

    static void deallocate(void *ptr, std::size_t = 0) noexcept
    {
        if (ptr != nullptr)
            ::je_free(ptr);
    }
}; // class MemoryJEM

#endif // MCKL_HAS_JEMALLOC

#if MCKL_HAS_TBB

/// \brief Memory allocation using Intel TBB
/// \ingroup Core
///
/// \tparam Alignment The alignment of the allocated memory
template <std::size_t Alignment>
class MemoryTBB
{
    static_assert(Alignment != 0 && (Alignment & (Alignment - 1)) == 0,
        "**MemoryTBB** used with Alignment other than a power of two positive "
        "integer");

    static_assert(Alignment >= sizeof(void *),
        "**MemoryTBB** used with Alignment less than sizeof(void *)");

    public:
    static constexpr std::size_t alignment() { return Alignment; }

    static void *allocate(std::size_t n, const void * = nullptr) noexcept
    {
        const std::size_t m = internal::alignment_round<Alignment>(n);

        return m < n ? nullptr : scalable_aligned_malloc(m, Alignment);
    }

    static void deallocate(void *ptr, std::size_t = 0) noexcept
    {
        if (ptr != nullptr)
            scalable_aligned_free(ptr);
    }
}; // class MemoryTBB

#endif // MCKL_HAS_TBB

/// \brief Default memory allocation policy
/// \ingroup Core
///
/// \tparam Alignment The alignment of the allocated memory
///
/// \details
/// This template is aliased to the configuration macro  `MCKL_MEMORY_TYPE`.
/// The default precedence is as the following,
/// * MemoryJEM
/// * MemoryTBB
/// * MemorySYS
/// * MemorySTD
template <std::size_t Alignment>
using Memory = MCKL_MEMORY_TYPE<Alignment>;

/// \brief Standard library compatible allocator with policies
/// \ingroup Core
///
/// \tparam T The value type
/// \tparam Mem The memory allocation policy
///
/// \sa Allocator<void, Mem>
/// \sa Allocator<const void, Mem>
/// \sa [std::allocator](http://en.cppreference.com/w/cpp/memory/allocator)
template <typename T, typename Mem = Memory<AlignOf<T>>>
class Allocator : public std::allocator<T>
{
    public:
    template <typename U>
    class rebind
    {
        public:
        using other = Allocator<U, Mem>;
    };

    Allocator() = default;

    template <typename U>
    Allocator(Allocator<U, Mem> &&other) noexcept
        : std::allocator<T>(std::move(static_cast<std::allocator<U>>(other)))
    {
    }

    T *allocate(std::size_t n, const void *hint = nullptr)
    {
        const std::size_t m = n * sizeof(T);
        if (m < n)
            throw std::bad_alloc();

        T *ptr = static_cast<T *>(Mem::allocate(m, hint));
        if (ptr == nullptr)
            throw std::bad_alloc();

        return ptr;
    }

    void deallocate(T *ptr, std::size_t size = 0) noexcept(
        noexcept(Mem::deallocate(ptr, size)))
    {
        Mem::deallocate(ptr, size);
    }
}; // class Allocator

template <typename Mem>
class Allocator<void, Mem>
{
    public:
    using value_type = void;
    using pointer = void *;
    using const_pointer = const void *;
    using propagate_on_container_move_assignment = std::true_type;
    using is_always_equal = std::true_type;

    template <typename U>
    class rebind
    {
        public:
        using other = Allocator<U, Mem>;
    };
}; // class Allocator

template <typename Mem>
class Allocator<const void, Mem>
{
    public:
    using value_type = const void;
    using pointer = const void *;
    using const_pointer = const void *;
    using propagate_on_container_move_assignment = std::true_type;
    using is_always_equal = std::true_type;

    template <typename U>
    class rebind
    {
        public:
        using other = Allocator<U, Mem>;
    };
}; // class Allocator

/// \relates Allocator
template <typename T1, typename T2, typename Mem1, typename Mem2>
inline constexpr bool operator==(
    const Allocator<T1, Mem1> &, const Allocator<T2, Mem2> &)
{
    return std::is_same<Mem1, Mem2>::value;
}

/// \relates Allocator
template <typename T1, typename T2, typename Mem1, typename Mem2>
inline constexpr bool operator!=(
    const Allocator<T1, Mem1> &, const Allocator<T2, Mem2> &)
{
    return !std::is_same<Mem1, Mem2>::value;
}

/// \brief std::vector with Allocator as the default allocator
/// \ingroup Core
template <typename T, typename Alloc = Allocator<T>>
using Vector = std::vector<T, Alloc>;

} // namespace mckl

#endif // MCKL_CORE_MEMORY_HPP
<commit_msg>no construct for scalar types<commit_after>//============================================================================
// MCKL/include/mckl/core/memory.hpp
//----------------------------------------------------------------------------
// MCKL: Monte Carlo Kernel Library
//----------------------------------------------------------------------------
// Copyright (c) 2013-2017, Yan Zhou
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
//   Redistributions of source code must retain the above copyright notice,
//   this list of conditions and the following disclaimer.
//
//   Redistributions in binary form must reproduce the above copyright notice,
//   this list of conditions and the following disclaimer in the documentation
//   and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//============================================================================

#ifndef MCKL_CORE_MEMORY_HPP
#define MCKL_CORE_MEMORY_HPP

#include <mckl/internal/config.h>

#include <cstdlib>
#include <cstring>
#include <memory>
#include <new>
#include <vector>

#if MCKL_HAS_POSIX
#include <stdlib.h>
#elif defined(MCKL_MSVC)
#include <malloc.h>
#endif

#if MCKL_HAS_JEMALLOC
#include <jemalloc/jemalloc.h>
#endif

#if MCKL_HAS_TBB
#include <tbb/scalable_allocator.h>
#endif

/// \brief The default alignment for scalar type
/// \ingroup Config
#ifndef MCKL_ALIGNMENT
#define MCKL_ALIGNMENT 32
#endif

/// \brief The minimum alignment for any type
/// \ingroup Config
#ifndef MCKL_MINIMUM_ALIGNMENT
#define MCKL_MINIMUM_ALIGNMENT 16
#endif

#if MCKL_ALIGNMENT < MCKL_MINIMUM_ALIGNMENT
#undef MCKL_ALIGNEMNT
#define MCKL_ALIGNMENT MCKL_MINIMUM_ALIGNMENT
#endif

/// \brief Default allocation type
/// \ingroup Config
#ifndef MCKL_MEMORY_TYPE
#if MCKL_USE_JEMALLOC
#define MCKL_MEMORY_TYPE ::mckl::MemoryJEM
#elif MCKL_USE_TBB_MALLOC
#define MCKL_MEMORY_TYPE ::mckl::MemoryTBB
#elif MCKL_HAS_POSIX || defined(MCKL_MSVC)
#define MCKL_MEMORY_TYPE ::mckl::MemorySYS
#else
#define MCKL_MEMORY_TYPE ::mckl::MemorySTD
#endif
#endif

namespace mckl
{

namespace internal
{

template <std::size_t Alignment, typename UIntType>
inline constexpr std::size_t alignment_round0(UIntType n)
{
    static_assert(Alignment != 0 && (Alignment & (Alignment - 1)) == 0,
        "**alignment_round0** used with Alignment other than a power of two "
        "positive integer");

    static_assert(Alignment >= sizeof(void *),
        "**alignment_round0** used with Alignment less than sizeof(void *)");

    static_assert(std::is_unsigned<UIntType>::value,
        "alignment_round0** used with UIntType other than unsigned integer "
        "types");

    constexpr std::size_t addn = Alignment - 1;
    constexpr std::size_t mask = ~addn;

    return (n + addn) & mask;
}

template <std::size_t Alignment, typename UIntType>
inline constexpr std::size_t alignment_round(UIntType n)
{
    return n == 0 ? Alignment : alignment_round0<Alignment>(n);
}

} // namespace mckl::internal

/// \brief Alignment of types
/// \ingroup Core
///
/// \details
/// * For scalar types, defined to the maximum of `MCKL_ALIGNMENT` and
/// `alignof(T)`
/// * For all other types, define to the maximum of `MCKL_MINIMUM_ALIGNMENT`
/// and `alignof(T)` as `value`.
template <typename T>
constexpr std::size_t AlignOf = std::integral_constant<std::size_t,
    std::is_scalar<T>::value ?
        (alignof(T) > MCKL_ALIGNMENT ? alignof(T) : MCKL_ALIGNMENT) :
        (alignof(T) > MCKL_MINIMUM_ALIGNMENT ? alignof(T) :
                                               MCKL_MINIMUM_ALIGNMENT)>::value;

/// \brief Memory allocation using the standard library
/// \ingroup Core
///
/// \tparam Alignment The alignment of the allocated memory
template <std::size_t Alignment>
class MemorySTD
{
    static_assert(Alignment != 0 && (Alignment & (Alignment - 1)) == 0,
        "**MemorySTD** used with Alignment other than a power of two positive "
        "integer");

    static_assert(Alignment >= sizeof(void *),
        "**MemorySTD** used with Alignment less than sizeof(void *)");

    public:
    static constexpr std::size_t alignment() { return Alignment; }

    static void *allocate(std::size_t n, const void * = nullptr) noexcept
    {
        const std::size_t m = internal::alignment_round0<Alignment>(
            n + sizeof(std::uintptr_t) + Alignment);
        if (m < n)
            return nullptr;

        std::uintptr_t *const address =
            static_cast<std::uintptr_t *>(std::malloc(m));
        if (address == nullptr)
            return nullptr;

        std::uintptr_t *const ptr = reinterpret_cast<std::uintptr_t *>(
            internal::alignment_round0<Alignment>(
                reinterpret_cast<std::uintptr_t>(address + 1)));
        ptr[-1] = reinterpret_cast<std::uintptr_t>(address);

        return static_cast<void *>(ptr);
    }

    static void deallocate(void *ptr, std::size_t = 0) noexcept
    {
        if (ptr != nullptr) {
            const std::uintptr_t address =
                static_cast<std::uintptr_t *>(ptr)[-1];
            std::free(reinterpret_cast<void *>(address));
        }
    }
}; // class MemorySTD

#if MCKL_HAS_POSIX

/// \brief Memory allocation using native system functions
/// \ingroup Core
///
/// \tparam Alignment The alignment of the allocated memory
template <std::size_t Alignment>
class MemorySYS
{
    static_assert(Alignment != 0 && (Alignment & (Alignment - 1)) == 0,
        "**MemorySYS** used with Alignment other than a power of two positive "
        "integer");

    static_assert(Alignment >= sizeof(void *),
        "**MemorySYS** used with Alignment less than sizeof(void *)");

    public:
    static constexpr std::size_t alignment() { return Alignment; }

    static void *allocate(std::size_t n, const void * = nullptr) noexcept
    {
        const std::size_t m = internal::alignment_round<Alignment>(n);

        if (m < n)
            return nullptr;

        void *ptr = nullptr;
        if (::posix_memalign(&ptr, Alignment, m) != 0)
            ptr = nullptr;

        return ptr;
    }

    static void deallocate(void *ptr, std::size_t = 0) noexcept
    {
        if (ptr != nullptr)
            ::free(ptr);
    }
}; // class MemorySYS

#elif defined(MCKL_MSVC)

template <std::size_t Alignment>
class MemorySYS
{
    static_assert(Alignment != 0 && (Alignment & (Alignment - 1)) == 0,
        "**MemorySYS** used with Alignment other than a power of two positive "
        "integer");

    static_assert(Alignment >= sizeof(void *),
        "**MemorySYS** used with Alignment less than sizeof(void *)");

    public:
    static constexpr std::size_t alignment() { return Alignment; }

    static void *allocate(std::size_t n, const void * = nullptr) noexcept
    {
        const std::size_t m = internal::alignment_round<Alignment>(n);

        return m < n ? nullptr : _aligned_malloc(m, Alignment);
    }

    static void deallocate(void *ptr, std::size_t = 0) noexcept
    {
        if (ptr != nullptr)
            _aligned_free(ptr);
    }
}; // class MemorySYS

#endif // MCKL_HAS_POSIX

#if MCKL_HAS_JEMALLOC

/// \brief Memory allocation using jemalloc
/// \ingroup Core
///
/// \tparam Alignment The alignment of the allocated memory
template <std::size_t Alignment>
class MemoryJEM
{
    static_assert(Alignment != 0 && (Alignment & (Alignment - 1)) == 0,
        "**MemoryJEM** used with Alignment other than a power of two positive "
        "integer");

    static_assert(Alignment >= sizeof(void *),
        "**MemoryJEM** used with Alignment less than sizeof(void *)");

    public:
    static constexpr std::size_t alignment() { return Alignment; }

    static void *allocate(std::size_t n, const void * = nullptr) noexcept
    {
        const std::size_t m = internal::alignment_round<Alignment>(n);

        return m < n ? nullptr : ::je_aligned_alloc(Alignment, m);
    }

    static void deallocate(void *ptr, std::size_t = 0) noexcept
    {
        if (ptr != nullptr)
            ::je_free(ptr);
    }
}; // class MemoryJEM

#endif // MCKL_HAS_JEMALLOC

#if MCKL_HAS_TBB

/// \brief Memory allocation using Intel TBB
/// \ingroup Core
///
/// \tparam Alignment The alignment of the allocated memory
template <std::size_t Alignment>
class MemoryTBB
{
    static_assert(Alignment != 0 && (Alignment & (Alignment - 1)) == 0,
        "**MemoryTBB** used with Alignment other than a power of two positive "
        "integer");

    static_assert(Alignment >= sizeof(void *),
        "**MemoryTBB** used with Alignment less than sizeof(void *)");

    public:
    static constexpr std::size_t alignment() { return Alignment; }

    static void *allocate(std::size_t n, const void * = nullptr) noexcept
    {
        const std::size_t m = internal::alignment_round<Alignment>(n);

        return m < n ? nullptr : scalable_aligned_malloc(m, Alignment);
    }

    static void deallocate(void *ptr, std::size_t = 0) noexcept
    {
        if (ptr != nullptr)
            scalable_aligned_free(ptr);
    }
}; // class MemoryTBB

#endif // MCKL_HAS_TBB

/// \brief Default memory allocation policy
/// \ingroup Core
///
/// \tparam Alignment The alignment of the allocated memory
///
/// \details
/// This template is aliased to the configuration macro  `MCKL_MEMORY_TYPE`.
/// The default precedence is as the following,
/// * MemoryJEM
/// * MemoryTBB
/// * MemorySYS
/// * MemorySTD
template <std::size_t Alignment>
using Memory = MCKL_MEMORY_TYPE<Alignment>;

/// \brief Standard library compatible allocator with policies
/// \ingroup Core
///
/// \tparam T The value type
/// \tparam Mem The memory allocation policy
///
/// \sa Allocator<void, Mem>
/// \sa Allocator<const void, Mem>
/// \sa [std::allocator](http://en.cppreference.com/w/cpp/memory/allocator)
template <typename T, typename Mem = Memory<AlignOf<T>>>
class Allocator : public std::allocator<T>
{
    public:
    template <typename U>
    class rebind
    {
        public:
        using other = Allocator<U, Mem>;
    };

    Allocator() = default;

    template <typename U>
    Allocator(const Allocator<U, Mem> &other) noexcept
        : std::allocator<T>(static_cast<std::allocator<U>>(other))
    {
    }

    T *allocate(std::size_t n, const void *hint = nullptr)
    {
        const std::size_t m = n * sizeof(T);
        if (m < n)
            throw std::bad_alloc();

        T *ptr = static_cast<T *>(Mem::allocate(m, hint));
        if (ptr == nullptr)
            throw std::bad_alloc();

        return ptr;
    }

    void deallocate(T *ptr, std::size_t size = 0) noexcept(
        noexcept(Mem::deallocate(ptr, size)))
    {
        Mem::deallocate(ptr, size);
    }

    template <typename U>
    void construct(U *p)
    {
        construct_dispatch(p, std::is_scalar<U>());
    }

    private:
    template <typename U>
    void construct_dispatch(U *, std::true_type)
    {
    }

    template <typename U>
    void construct_dispatch(U *p, std::false_type)
    {
        ::new (static_cast<void *>(p)) U();
    }
}; // class Allocator

template <typename Mem>
class Allocator<void, Mem>
{
    public:
    using value_type = void;
    using pointer = void *;
    using const_pointer = const void *;
    using propagate_on_container_move_assignment = std::true_type;
    using is_always_equal = std::true_type;

    template <typename U>
    class rebind
    {
        public:
        using other = Allocator<U, Mem>;
    };
}; // class Allocator

template <typename Mem>
class Allocator<const void, Mem>
{
    public:
    using value_type = const void;
    using pointer = const void *;
    using const_pointer = const void *;
    using propagate_on_container_move_assignment = std::true_type;
    using is_always_equal = std::true_type;

    template <typename U>
    class rebind
    {
        public:
        using other = Allocator<U, Mem>;
    };
}; // class Allocator

/// \relates Allocator
template <typename T1, typename T2, typename Mem1, typename Mem2>
inline constexpr bool operator==(
    const Allocator<T1, Mem1> &, const Allocator<T2, Mem2> &)
{
    return std::is_same<Mem1, Mem2>::value;
}

/// \relates Allocator
template <typename T1, typename T2, typename Mem1, typename Mem2>
inline constexpr bool operator!=(
    const Allocator<T1, Mem1> &, const Allocator<T2, Mem2> &)
{
    return !std::is_same<Mem1, Mem2>::value;
}

/// \brief std::vector with Allocator as the default allocator
/// \ingroup Core
template <typename T, typename Alloc = Allocator<T>>
using Vector = std::vector<T, Alloc>;

} // namespace mckl

#endif // MCKL_CORE_MEMORY_HPP
<|endoftext|>


<commit_before>/*
 * Configuration.
 *
 * author: Max Kellermann <mk@cm4all.com>
 */

#ifndef BENG_LB_CONFIG_H
#define BENG_LB_CONFIG_H

#include "address_list.hxx"
#include "sticky.h"
#include "ssl_config.hxx"
#include "net/AllocatedSocketAddress.hxx"

#include <http/status.h>

#include <glib.h>

#include <map>
#include <list>
#include <vector>
#include <string>

struct pool;
class Error;

enum lb_protocol {
    LB_PROTOCOL_HTTP,
    LB_PROTOCOL_TCP,
};

struct lb_control_config {
    AllocatedSocketAddress bind_address;
};

struct lb_monitor_config {
    std::string name;

    /**
     * Time in seconds between two monitor checks.
     */
    unsigned interval = 10;

    /**
     * If the monitor does not produce a result after this timeout
     * [seconds], it is assumed to be negative.
     */
    unsigned timeout = 0;

    enum class Type {
        NONE,
        PING,
        CONNECT,
        TCP_EXPECT,
    } type = Type::NONE;

    /**
     * The timeout for establishing a connection.  Only applicable for
     * #Type::TCP_EXPECT.  0 means no special setting present.
     */
    unsigned connect_timeout = 0;

    /**
     * For #Type::TCP_EXPECT: a string that is sent to the peer
     * after the connection has been established.  May be empty.
     */
    std::string send;

    /**
     * For #Type::TCP_EXPECT: a string that is expected to be
     * received from the peer after the #send string has been sent.
     */
    std::string expect;

    /**
     * For #Type::TCP_EXPECT: if that string is received from the
     * peer (instead of #expect), then the node is assumed to be
     * shutting down gracefully, and will only get sticky requests.
     */
    std::string fade_expect;

    explicit lb_monitor_config(const char *_name)
        :name(_name) {}
};

struct lb_node_config {
    std::string name;

    AllocatedSocketAddress address;

    /**
     * The Tomcat "jvmRoute" setting of this node.  It is used for
     * #STICKY_JVM_ROUTE.
     */
    std::string jvm_route;

    explicit lb_node_config(const char *_name)
        :name(_name) {}

    lb_node_config(const char *_name, AllocatedSocketAddress &&_address)
        :name(_name), address(std::move(_address)) {}

    lb_node_config(lb_node_config &&src)
        :name(std::move(src.name)), address(std::move(src.address)),
         jvm_route(std::move(src.jvm_route)) {}
};

struct lb_member_config {
    const struct lb_node_config *node = nullptr;

    unsigned port = 0;
};

struct lb_fallback_config {
    http_status_t status;

    /**
     * The "Location" response header.
     */
    std::string location;

    std::string message;

    bool IsDefined() const {
        return !location.empty() || !message.empty();
    }
};

struct lb_cluster_config {
    std::string name;

    /**
     * The protocol that is spoken on this cluster.
     */
    enum lb_protocol protocol = LB_PROTOCOL_HTTP;

    /**
     * Use the client's source IP for the connection to the backend?
     * This is implemented using IP_TRANSPARENT and requires the
     * "tproxy" Linux kernel module.
     */
    bool transparent_source = false;

    bool mangle_via = false;

    struct lb_fallback_config fallback;

    enum sticky_mode sticky_mode = STICKY_NONE;

    std::string session_cookie = "beng_proxy_session";

    const struct lb_monitor_config *monitor = nullptr;

    std::vector<lb_member_config> members;

    /**
     * A list of node addresses.
     */
    AddressList address_list;

    explicit lb_cluster_config(const char *_name)
        :name(_name) {}

    /**
     * Returns the member index of the node with the specified
     * jvm_route value, or -1 if not found.
     */
    gcc_pure
    int FindJVMRoute(const char *jvm_route) const;
};

struct lb_attribute_reference {
    enum class Type {
        METHOD,
        URI,
        HEADER,
    } type;

    std::string name;

    template<typename N>
    lb_attribute_reference(Type _type, N &&_name)
        :type(_type), name(std::forward<N>(_name)) {}
};

struct lb_branch_config;

struct lb_goto {
    const lb_cluster_config *cluster = nullptr;
    const lb_branch_config *branch = nullptr;

    lb_goto() = default;

    explicit lb_goto(lb_cluster_config *_cluster)
        :cluster(_cluster), branch(nullptr) {}

    explicit lb_goto(lb_branch_config *_branch)
        :cluster(nullptr), branch(_branch) {}

    bool IsDefined() const {
        return cluster != nullptr || branch != nullptr;
    }

    gcc_pure
    lb_protocol GetProtocol() const;

    gcc_pure
    const char *GetName() const;
};

struct lb_condition_config {
    lb_attribute_reference attribute_reference;

    enum class Operator {
        EQUALS,
        REGEX,
    };

    Operator op;

    bool negate;

    std::string string;
    GRegex *regex = nullptr;

    lb_condition_config(lb_attribute_reference &&a, bool _negate,
                        const char *_string)
        :attribute_reference(std::move(a)), op(Operator::EQUALS),
         negate(_negate), string(_string) {}

    lb_condition_config(lb_attribute_reference &&a, bool _negate,
                        GRegex *_regex)
        :attribute_reference(std::move(a)), op(Operator::REGEX),
         negate(_negate), regex(_regex) {}

    lb_condition_config(const lb_condition_config &other)
        :attribute_reference(other.attribute_reference),
         op(other.op), negate(other.negate),
         string(other.string),
         regex(other.op == Operator::REGEX
               ? g_regex_ref(other.regex)
               : nullptr) {}

    lb_condition_config(lb_condition_config &&other)
        :attribute_reference(std::move(other.attribute_reference)),
         op(other.op), negate(other.negate),
         string(std::move(other.string)),
         regex(other.regex) {
        other.regex = nullptr;
    }

    ~lb_condition_config() {
        if (regex != nullptr)
            g_regex_unref(regex);
    }

    gcc_pure
    bool Match(const char *value) const {
        switch (op) {
        case Operator::EQUALS:
            return (string == value) ^ negate;
            break;

        case Operator::REGEX:
            return g_regex_match(regex, value, GRegexMatchFlags(0),
                                 nullptr) ^ negate;
            break;
        }

        gcc_unreachable();
    }
};

struct lb_goto_if_config {
    lb_condition_config condition;

    lb_goto destination;

    lb_goto_if_config(lb_condition_config &&c, lb_goto d)
        :condition(std::move(c)), destination(d) {}
};

/**
 * An object that distributes connections or requests to the "real"
 * cluster.
 */
struct lb_branch_config {
    std::string name;

    lb_goto fallback;

    std::list<lb_goto_if_config> conditions;

    explicit lb_branch_config(const char *_name)
        :name(_name) {}

    bool HasFallback() const {
        return fallback.IsDefined();
    }

    lb_protocol GetProtocol() const {
        return fallback.GetProtocol();
    }
};

inline lb_protocol
lb_goto::GetProtocol() const
{
    assert(IsDefined());

    return cluster != nullptr
        ? cluster->protocol
        : branch->GetProtocol();
}

inline const char *
lb_goto::GetName() const
{
    assert(IsDefined());

    return cluster != nullptr
        ? cluster->name.c_str()
        : branch->name.c_str();
}

struct lb_listener_config {
    std::string name;

    AllocatedSocketAddress bind_address;

    lb_goto destination;

    bool verbose_response = false;

    bool ssl = false;

    struct ssl_config ssl_config;

    explicit lb_listener_config(const char *_name)
        :name(_name) {}
};

struct lb_config {
    std::list<lb_control_config> controls;

    std::map<std::string, lb_monitor_config> monitors;

    std::map<std::string, lb_node_config> nodes;

    std::map<std::string, lb_cluster_config> clusters;
    std::map<std::string, lb_branch_config> branches;

    std::list<lb_listener_config> listeners;

    template<typename T>
    gcc_pure
    const lb_monitor_config *FindMonitor(T &&t) const {
        const auto i = monitors.find(std::forward<T>(t));
        return i != monitors.end()
            ? &i->second
            : nullptr;
    }

    template<typename T>
    gcc_pure
    const lb_node_config *FindNode(T &&t) const {
        const auto i = nodes.find(std::forward<T>(t));
        return i != nodes.end()
            ? &i->second
            : nullptr;
    }

    template<typename T>
    gcc_pure
    const lb_cluster_config *FindCluster(T &&t) const {
        const auto i = clusters.find(std::forward<T>(t));
        return i != clusters.end()
            ? &i->second
            : nullptr;
    }

    template<typename T>
    gcc_pure
    const lb_goto FindGoto(T &&t) const {
        lb_goto g;

        g.cluster = FindCluster(t);
        if (g.cluster == nullptr)
            g.branch = FindBranch(std::forward<T>(t));

        return g;
    }

    template<typename T>
    gcc_pure
    const lb_branch_config *FindBranch(T &&t) const {
        const auto i = branches.find(std::forward<T>(t));
        return i != branches.end()
            ? &i->second
            : nullptr;
    }

    template<typename T>
    gcc_pure
    const lb_listener_config *FindListener(T &&t) const {
        for (const auto &i : listeners)
            if (i.name == t)
                return &i;

        return nullptr;
    }
};

/**
 * Load and parse the specified configuration file.
 */
struct lb_config *
lb_config_load(struct pool *pool, const char *path, Error &error_r);

#endif
<commit_msg>lb_config: "delete" a few copy constructors<commit_after>/*
 * Configuration.
 *
 * author: Max Kellermann <mk@cm4all.com>
 */

#ifndef BENG_LB_CONFIG_H
#define BENG_LB_CONFIG_H

#include "address_list.hxx"
#include "sticky.h"
#include "ssl_config.hxx"
#include "net/AllocatedSocketAddress.hxx"

#include <http/status.h>

#include <glib.h>

#include <map>
#include <list>
#include <vector>
#include <string>

struct pool;
class Error;

enum lb_protocol {
    LB_PROTOCOL_HTTP,
    LB_PROTOCOL_TCP,
};

struct lb_control_config {
    AllocatedSocketAddress bind_address;
};

struct lb_monitor_config {
    std::string name;

    /**
     * Time in seconds between two monitor checks.
     */
    unsigned interval = 10;

    /**
     * If the monitor does not produce a result after this timeout
     * [seconds], it is assumed to be negative.
     */
    unsigned timeout = 0;

    enum class Type {
        NONE,
        PING,
        CONNECT,
        TCP_EXPECT,
    } type = Type::NONE;

    /**
     * The timeout for establishing a connection.  Only applicable for
     * #Type::TCP_EXPECT.  0 means no special setting present.
     */
    unsigned connect_timeout = 0;

    /**
     * For #Type::TCP_EXPECT: a string that is sent to the peer
     * after the connection has been established.  May be empty.
     */
    std::string send;

    /**
     * For #Type::TCP_EXPECT: a string that is expected to be
     * received from the peer after the #send string has been sent.
     */
    std::string expect;

    /**
     * For #Type::TCP_EXPECT: if that string is received from the
     * peer (instead of #expect), then the node is assumed to be
     * shutting down gracefully, and will only get sticky requests.
     */
    std::string fade_expect;

    explicit lb_monitor_config(const char *_name)
        :name(_name) {}
};

struct lb_node_config {
    std::string name;

    AllocatedSocketAddress address;

    /**
     * The Tomcat "jvmRoute" setting of this node.  It is used for
     * #STICKY_JVM_ROUTE.
     */
    std::string jvm_route;

    explicit lb_node_config(const char *_name)
        :name(_name) {}

    lb_node_config(const char *_name, AllocatedSocketAddress &&_address)
        :name(_name), address(std::move(_address)) {}

    lb_node_config(lb_node_config &&src)
        :name(std::move(src.name)), address(std::move(src.address)),
         jvm_route(std::move(src.jvm_route)) {}
};

struct lb_member_config {
    const struct lb_node_config *node = nullptr;

    unsigned port = 0;
};

struct lb_fallback_config {
    http_status_t status;

    /**
     * The "Location" response header.
     */
    std::string location;

    std::string message;

    bool IsDefined() const {
        return !location.empty() || !message.empty();
    }
};

struct lb_cluster_config {
    std::string name;

    /**
     * The protocol that is spoken on this cluster.
     */
    enum lb_protocol protocol = LB_PROTOCOL_HTTP;

    /**
     * Use the client's source IP for the connection to the backend?
     * This is implemented using IP_TRANSPARENT and requires the
     * "tproxy" Linux kernel module.
     */
    bool transparent_source = false;

    bool mangle_via = false;

    struct lb_fallback_config fallback;

    enum sticky_mode sticky_mode = STICKY_NONE;

    std::string session_cookie = "beng_proxy_session";

    const struct lb_monitor_config *monitor = nullptr;

    std::vector<lb_member_config> members;

    /**
     * A list of node addresses.
     */
    AddressList address_list;

    explicit lb_cluster_config(const char *_name)
        :name(_name) {}

    /**
     * Returns the member index of the node with the specified
     * jvm_route value, or -1 if not found.
     */
    gcc_pure
    int FindJVMRoute(const char *jvm_route) const;
};

struct lb_attribute_reference {
    enum class Type {
        METHOD,
        URI,
        HEADER,
    } type;

    std::string name;

    template<typename N>
    lb_attribute_reference(Type _type, N &&_name)
        :type(_type), name(std::forward<N>(_name)) {}
};

struct lb_branch_config;

struct lb_goto {
    const lb_cluster_config *cluster = nullptr;
    const lb_branch_config *branch = nullptr;

    lb_goto() = default;

    explicit lb_goto(lb_cluster_config *_cluster)
        :cluster(_cluster), branch(nullptr) {}

    explicit lb_goto(lb_branch_config *_branch)
        :cluster(nullptr), branch(_branch) {}

    bool IsDefined() const {
        return cluster != nullptr || branch != nullptr;
    }

    gcc_pure
    lb_protocol GetProtocol() const;

    gcc_pure
    const char *GetName() const;
};

struct lb_condition_config {
    lb_attribute_reference attribute_reference;

    enum class Operator {
        EQUALS,
        REGEX,
    };

    Operator op;

    bool negate;

    std::string string;
    GRegex *regex = nullptr;

    lb_condition_config(lb_attribute_reference &&a, bool _negate,
                        const char *_string)
        :attribute_reference(std::move(a)), op(Operator::EQUALS),
         negate(_negate), string(_string) {}

    lb_condition_config(lb_attribute_reference &&a, bool _negate,
                        GRegex *_regex)
        :attribute_reference(std::move(a)), op(Operator::REGEX),
         negate(_negate), regex(_regex) {}

    lb_condition_config(lb_condition_config &&other)
        :attribute_reference(std::move(other.attribute_reference)),
         op(other.op), negate(other.negate),
         string(std::move(other.string)),
         regex(other.regex) {
        other.regex = nullptr;
    }

    ~lb_condition_config() {
        if (regex != nullptr)
            g_regex_unref(regex);
    }

    lb_condition_config(const lb_condition_config &) = delete;
    lb_condition_config &operator=(const lb_condition_config &) = delete;

    gcc_pure
    bool Match(const char *value) const {
        switch (op) {
        case Operator::EQUALS:
            return (string == value) ^ negate;
            break;

        case Operator::REGEX:
            return g_regex_match(regex, value, GRegexMatchFlags(0),
                                 nullptr) ^ negate;
            break;
        }

        gcc_unreachable();
    }
};

struct lb_goto_if_config {
    lb_condition_config condition;

    lb_goto destination;

    lb_goto_if_config(lb_condition_config &&c, lb_goto d)
        :condition(std::move(c)), destination(d) {}
};

/**
 * An object that distributes connections or requests to the "real"
 * cluster.
 */
struct lb_branch_config {
    std::string name;

    lb_goto fallback;

    std::list<lb_goto_if_config> conditions;

    explicit lb_branch_config(const char *_name)
        :name(_name) {}

    lb_branch_config(lb_branch_config &&) = default;

    lb_branch_config(const lb_branch_config &) = delete;
    lb_branch_config &operator=(const lb_branch_config &) = delete;

    bool HasFallback() const {
        return fallback.IsDefined();
    }

    lb_protocol GetProtocol() const {
        return fallback.GetProtocol();
    }
};

inline lb_protocol
lb_goto::GetProtocol() const
{
    assert(IsDefined());

    return cluster != nullptr
        ? cluster->protocol
        : branch->GetProtocol();
}

inline const char *
lb_goto::GetName() const
{
    assert(IsDefined());

    return cluster != nullptr
        ? cluster->name.c_str()
        : branch->name.c_str();
}

struct lb_listener_config {
    std::string name;

    AllocatedSocketAddress bind_address;

    lb_goto destination;

    bool verbose_response = false;

    bool ssl = false;

    struct ssl_config ssl_config;

    explicit lb_listener_config(const char *_name)
        :name(_name) {}
};

struct lb_config {
    std::list<lb_control_config> controls;

    std::map<std::string, lb_monitor_config> monitors;

    std::map<std::string, lb_node_config> nodes;

    std::map<std::string, lb_cluster_config> clusters;
    std::map<std::string, lb_branch_config> branches;

    std::list<lb_listener_config> listeners;

    template<typename T>
    gcc_pure
    const lb_monitor_config *FindMonitor(T &&t) const {
        const auto i = monitors.find(std::forward<T>(t));
        return i != monitors.end()
            ? &i->second
            : nullptr;
    }

    template<typename T>
    gcc_pure
    const lb_node_config *FindNode(T &&t) const {
        const auto i = nodes.find(std::forward<T>(t));
        return i != nodes.end()
            ? &i->second
            : nullptr;
    }

    template<typename T>
    gcc_pure
    const lb_cluster_config *FindCluster(T &&t) const {
        const auto i = clusters.find(std::forward<T>(t));
        return i != clusters.end()
            ? &i->second
            : nullptr;
    }

    template<typename T>
    gcc_pure
    const lb_goto FindGoto(T &&t) const {
        lb_goto g;

        g.cluster = FindCluster(t);
        if (g.cluster == nullptr)
            g.branch = FindBranch(std::forward<T>(t));

        return g;
    }

    template<typename T>
    gcc_pure
    const lb_branch_config *FindBranch(T &&t) const {
        const auto i = branches.find(std::forward<T>(t));
        return i != branches.end()
            ? &i->second
            : nullptr;
    }

    template<typename T>
    gcc_pure
    const lb_listener_config *FindListener(T &&t) const {
        for (const auto &i : listeners)
            if (i.name == t)
                return &i;

        return nullptr;
    }
};

/**
 * Load and parse the specified configuration file.
 */
struct lb_config *
lb_config_load(struct pool *pool, const char *path, Error &error_r);

#endif
<|endoftext|>


<commit_before>#include "node_zipfile.hpp"

// stl
#include <sstream>
#include <vector>
//#include <iostream>
#include <cstring>

#include <node_buffer.h>
#include <node_version.h>

#define TOSTR(obj) (*String::Utf8Value((obj)->ToString()))

Persistent<FunctionTemplate> ZipFile::constructor;

void ZipFile::Initialize(Handle<Object> target) {

    HandleScope scope;

    constructor = Persistent<FunctionTemplate>::New(FunctionTemplate::New(ZipFile::New));
    constructor->InstanceTemplate()->SetInternalFieldCount(1);
    constructor->SetClassName(String::NewSymbol("ZipFile"));

    // functions
    NODE_SET_PROTOTYPE_METHOD(constructor, "readFileSync", readFileSync);
    NODE_SET_PROTOTYPE_METHOD(constructor, "readFile", readFile);

    // properties
    constructor->InstanceTemplate()->SetAccessor(String::NewSymbol("count"), get_prop);
    constructor->InstanceTemplate()->SetAccessor(String::NewSymbol("names"), get_prop);

    target->Set(String::NewSymbol("ZipFile"),constructor->GetFunction());
}

ZipFile::ZipFile(std::string const& file_name) :
  ObjectWrap(),
  file_name_(file_name),
  archive_() {}

ZipFile::~ZipFile() {
    zip_close(archive_);
}

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

    if (!args.IsConstructCall())
        return ThrowException(String::New("Cannot call constructor as function, you need to use 'new' keyword"));

    if (args.Length() != 1 || !args[0]->IsString())
        return ThrowException(Exception::TypeError(
          String::New("first argument must be a path to a zipfile")));

    std::string input_file = TOSTR(args[0]);
    struct zip *za;
    int err;
    char errstr[1024];
  	if ((za=zip_open(input_file.c_str(), 0, &err)) == NULL) {
  	    zip_error_to_str(errstr, sizeof(errstr), err, errno);
  	    std::stringstream s;
  	    s << "cannot open file: " << input_file << " error: " << errstr << "\n";
        return ThrowException(Exception::Error(
            String::New(s.str().c_str())));
  	}

    ZipFile* zf = new ZipFile(input_file);
    zf->archive_ = za;
    zf->Wrap(args.This());
    return args.This();

}

Handle<Value> ZipFile::get_prop(Local<String> property,
                         const AccessorInfo& info)
{
    HandleScope scope;
    ZipFile* zf = ObjectWrap::Unwrap<ZipFile>(info.This());
    std::string a = TOSTR(property);
    if (a == "count") {
          int num = zip_get_num_files(zf->archive_);
          return scope.Close(Integer::New(num));
    }
    if (a == "names") {
          int num = zip_get_num_files(zf->archive_);
          Local<Array> a = Array::New(num);
          int i = 0;
          for (i=0; i<num; i++) {
              struct zip_stat st;
              zip_stat_index(zf->archive_, i, 0, &st);
              a->Set(i,String::New(st.name));
          }
          return scope.Close(a);
    }
    return Undefined();
}

Handle<Value> ZipFile::readFileSync(const Arguments& args)
{
    HandleScope scope;

    if (args.Length() != 1 || !args[0]->IsString())
        return ThrowException(Exception::TypeError(
          String::New("first argument must be a file name inside the zip")));

    std::string name = TOSTR(args[0]);

    // TODO - enforce valid index
    ZipFile* zf = ObjectWrap::Unwrap<ZipFile>(args.This());

    struct zip_file *zf_ptr;

    int idx = -1;
    int num = zip_get_num_files(zf->archive_);
    int i = 0;
    for (i=0; i<num; i++) {
        struct zip_stat st;
        zip_stat_index(zf->archive_, i, 0, &st);
        if (st.name == name) {
            idx = i;
        }
    }

    if (idx == -1) {
  	    std::stringstream s;
  	    s << "No file found by the name of: '" << name << "\n";
        return ThrowException(Exception::Error(String::New(s.str().c_str())));
    }

    if ((zf_ptr=zip_fopen_index(zf->archive_, idx, 0)) == NULL) {
  	    zip_fclose(zf_ptr);
  	    std::stringstream s;
  	    s << "cannot open file #" << idx << " in " << name << ": archive error: " << zip_strerror(zf->archive_) << "\n";
  	    return ThrowException(Exception::Error(String::New(s.str().c_str())));
    }

    struct zip_stat st;
    zip_stat_index(zf->archive_, idx, 0, &st);

    std::vector<unsigned char> data;
    data.clear();
    data.resize( st.size );

    int result =  0;
    result = (int)zip_fread( zf_ptr, reinterpret_cast<void*> (&data[0]), data.size() );

    if (result < 0) {
        zip_fclose(zf_ptr);
  	    std::stringstream s;
  	    s << "error reading file #" << idx << " in " << name << ": archive error: " << zip_file_strerror(zf_ptr) << "\n";
  	    return ThrowException(Exception::Error(String::New(s.str().c_str())));
    }

    #if NODE_VERSION_AT_LEAST(0,3,0)
        node::Buffer *retbuf = Buffer::New((char *)&data[0],data.size());
    #else
        node::Buffer *retbuf = Buffer::New(data.size());
        std::memcpy(retbuf->data(), (char *)&data[0], data.size());
    #endif

    zip_fclose(zf_ptr);
    return scope.Close(retbuf->handle_);
}

typedef struct {
    ZipFile* zf;
    struct zip *za;
    std::string name;
    bool error;
    std::string error_name;
    std::vector<unsigned char> data;
    //Handle<Function> fn;
    Persistent<Function> cb;
} closure_t;

Handle<Value> ZipFile::readFile(const Arguments& args)
{
    HandleScope scope;

    if (args.Length() < 2)
        return ThrowException(Exception::TypeError(
          String::New("requires two arguments, the name of a file and a callback")));

    // first arg must be name
    if(!args[0]->IsString())
        return ThrowException(Exception::TypeError(
          String::New("first argument must be a file name inside the zip")));

    // last arg must be function callback
    if (!args[args.Length()-1]->IsFunction())
        return ThrowException(Exception::TypeError(
                  String::New("last argument must be a callback function")));

    std::string name = TOSTR(args[0]);

    ZipFile* zf = ObjectWrap::Unwrap<ZipFile>(args.This());

    closure_t *closure = new closure_t();

    // libzip is not threadsafe so we cannot use the zf->archive_
    // instead we open a new zip archive for each thread
    struct zip *za;
    int err;
    char errstr[1024];
  	if ((za=zip_open(zf->file_name_.c_str() , 0, &err)) == NULL) {
  	    zip_error_to_str(errstr, sizeof(errstr), err, errno);
  	    std::stringstream s;
  	    s << "cannot open file: " << zf->file_name_ << " error: " << errstr << "\n";
        zip_close(za);
        return ThrowException(Exception::Error(
            String::New(s.str().c_str())));
  	}

    closure->zf = zf;
    closure->za = za;
    closure->error = false;
    closure->name = name;
    closure->cb = Persistent<Function>::New(Handle<Function>::Cast(args[args.Length()-1]));
    eio_custom(EIO_ReadFile, EIO_PRI_DEFAULT, EIO_AfterReadFile, closure);
    ev_ref(EV_DEFAULT_UC);
    zf->Ref();
    return Undefined();
}

int ZipFile::EIO_ReadFile(eio_req *req)
{
    closure_t *closure = static_cast<closure_t *>(req->data);

    struct zip_file *zf_ptr=NULL;

    int idx = -1;
    int num = zip_get_num_files(closure->za);
    int i = 0;
    for (i=0; i<num; i++) {
        struct zip_stat st;
        zip_stat_index(closure->za, i, 0, &st);
        if (st.name == closure->name) {
            idx = i;
        }
    }

    if (idx == -1) {
  	    std::stringstream s;
  	    s << "No file found by the name of: '" << closure->name << "\n";
        closure->error = true;
        closure->error_name = s.str();

    } else {

        if ((zf_ptr = zip_fopen_index(closure->za, idx, 0)) == NULL) {
      	    std::stringstream s;
      	    s << "cannot open file #" << idx << " in "
      	      << closure->name << ": archive error: " << zip_strerror(closure->za) << "\n";
            closure->error = true;
            closure->error_name = s.str();

        } else {

            struct zip_stat st;
            zip_stat_index(closure->za, idx, 0, &st);
            closure->data.clear();
            closure->data.resize( st.size );

            int result =  0;
            result = (int)zip_fread( zf_ptr, reinterpret_cast<void*> (&closure->data[0]), closure->data.size() );

            if (result < 0) {
          	    std::stringstream s;
          	    s << "error reading file #" << idx << " in "
          	      << closure->name << ": archive error: " << zip_file_strerror(zf_ptr) << "\n";
                closure->error = true;
                closure->error_name = s.str();

            }
        }
    }

    //zip_close(closure->za);
    zip_fclose(zf_ptr);
    return 0;
}

int ZipFile::EIO_AfterReadFile(eio_req *req)
{
    HandleScope scope;

    closure_t *closure = static_cast<closure_t *>(req->data);
    ev_unref(EV_DEFAULT_UC);

    TryCatch try_catch;

    if (closure->error) {
        Local<Value> argv[1] = { Exception::Error(String::New(closure->error_name.c_str())) };
        //Local<Value> argv[2] = { Exception::Error(String::New(closure->error_name.c_str())),
        //                         Local<Value>::New(Null()) };
        closure->cb->Call(Context::GetCurrent()->Global(), 2, argv);
    } else {
        #if NODE_VERSION_AT_LEAST(0,3,0)
          node::Buffer *retbuf = Buffer::New((char *)&closure->data[0],closure->data.size());
        #else
          node::Buffer *retbuf = Buffer::New(closure->data.size());
          std::memcpy(retbuf->data(), (char *)&closure->data[0], closure->data.size());
        #endif
        Local<Value> argv[2] = { Local<Value>::New(Null()), Local<Value>::New(retbuf->handle_) };
        closure->cb->Call(Context::GetCurrent()->Global(), 2, argv);
    }

    if (try_catch.HasCaught()) {
      FatalException(try_catch);
      //try_catch.ReThrow();
    }

    closure->zf->Unref();
    closure->cb.Dispose();
    delete closure;
    return 0;
}

<commit_msg>let go of zip archive opened for each thread<commit_after>#include "node_zipfile.hpp"

// stl
#include <sstream>
#include <vector>
#include <cstring>

#include <node_buffer.h>
#include <node_version.h>

#define TOSTR(obj) (*String::Utf8Value((obj)->ToString()))

Persistent<FunctionTemplate> ZipFile::constructor;

void ZipFile::Initialize(Handle<Object> target) {

    HandleScope scope;

    constructor = Persistent<FunctionTemplate>::New(FunctionTemplate::New(ZipFile::New));
    constructor->InstanceTemplate()->SetInternalFieldCount(1);
    constructor->SetClassName(String::NewSymbol("ZipFile"));

    // functions
    NODE_SET_PROTOTYPE_METHOD(constructor, "readFileSync", readFileSync);
    NODE_SET_PROTOTYPE_METHOD(constructor, "readFile", readFile);

    // properties
    constructor->InstanceTemplate()->SetAccessor(String::NewSymbol("count"), get_prop);
    constructor->InstanceTemplate()->SetAccessor(String::NewSymbol("names"), get_prop);

    target->Set(String::NewSymbol("ZipFile"),constructor->GetFunction());
}

ZipFile::ZipFile(std::string const& file_name) :
  ObjectWrap(),
  file_name_(file_name),
  archive_() {}

ZipFile::~ZipFile() {
    zip_close(archive_);
}

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

    if (!args.IsConstructCall())
        return ThrowException(String::New("Cannot call constructor as function, you need to use 'new' keyword"));

    if (args.Length() != 1 || !args[0]->IsString())
        return ThrowException(Exception::TypeError(
          String::New("first argument must be a path to a zipfile")));

    std::string input_file = TOSTR(args[0]);
    struct zip *za;
    int err;
    char errstr[1024];
  	if ((za=zip_open(input_file.c_str(), 0, &err)) == NULL) {
  	    zip_error_to_str(errstr, sizeof(errstr), err, errno);
  	    std::stringstream s;
  	    s << "cannot open file: " << input_file << " error: " << errstr << "\n";
        return ThrowException(Exception::Error(
            String::New(s.str().c_str())));
  	}

    ZipFile* zf = new ZipFile(input_file);
    zf->archive_ = za;
    zf->Wrap(args.This());
    return args.This();

}

Handle<Value> ZipFile::get_prop(Local<String> property,
                         const AccessorInfo& info)
{
    HandleScope scope;
    ZipFile* zf = ObjectWrap::Unwrap<ZipFile>(info.This());
    std::string a = TOSTR(property);
    if (a == "count") {
          int num = zip_get_num_files(zf->archive_);
          return scope.Close(Integer::New(num));
    }
    if (a == "names") {
          int num = zip_get_num_files(zf->archive_);
          Local<Array> a = Array::New(num);
          int i = 0;
          for (i=0; i<num; i++) {
              struct zip_stat st;
              zip_stat_index(zf->archive_, i, 0, &st);
              a->Set(i,String::New(st.name));
          }
          return scope.Close(a);
    }
    return Undefined();
}

Handle<Value> ZipFile::readFileSync(const Arguments& args)
{
    HandleScope scope;

    if (args.Length() != 1 || !args[0]->IsString())
        return ThrowException(Exception::TypeError(
          String::New("first argument must be a file name inside the zip")));

    std::string name = TOSTR(args[0]);

    // TODO - enforce valid index
    ZipFile* zf = ObjectWrap::Unwrap<ZipFile>(args.This());

    struct zip_file *zf_ptr;

    int idx = -1;
    int num = zip_get_num_files(zf->archive_);
    int i = 0;
    for (i=0; i<num; i++) {
        struct zip_stat st;
        zip_stat_index(zf->archive_, i, 0, &st);
        if (st.name == name) {
            idx = i;
        }
    }

    if (idx == -1) {
  	    std::stringstream s;
  	    s << "No file found by the name of: '" << name << "\n";
        return ThrowException(Exception::Error(String::New(s.str().c_str())));
    }

    if ((zf_ptr=zip_fopen_index(zf->archive_, idx, 0)) == NULL) {
  	    zip_fclose(zf_ptr);
  	    std::stringstream s;
  	    s << "cannot open file #" << idx << " in " << name << ": archive error: " << zip_strerror(zf->archive_) << "\n";
  	    return ThrowException(Exception::Error(String::New(s.str().c_str())));
    }

    struct zip_stat st;
    zip_stat_index(zf->archive_, idx, 0, &st);

    std::vector<unsigned char> data;
    data.clear();
    data.resize( st.size );

    int result =  0;
    result = (int)zip_fread( zf_ptr, reinterpret_cast<void*> (&data[0]), data.size() );

    if (result < 0) {
        zip_fclose(zf_ptr);
  	    std::stringstream s;
  	    s << "error reading file #" << idx << " in " << name << ": archive error: " << zip_file_strerror(zf_ptr) << "\n";
  	    return ThrowException(Exception::Error(String::New(s.str().c_str())));
    }

    #if NODE_VERSION_AT_LEAST(0,3,0)
        node::Buffer *retbuf = Buffer::New((char *)&data[0],data.size());
    #else
        node::Buffer *retbuf = Buffer::New(data.size());
        std::memcpy(retbuf->data(), (char *)&data[0], data.size());
    #endif

    zip_fclose(zf_ptr);
    return scope.Close(retbuf->handle_);
}

typedef struct {
    ZipFile* zf;
    struct zip *za;
    std::string name;
    bool error;
    std::string error_name;
    std::vector<unsigned char> data;
    Persistent<Function> cb;
} closure_t;

Handle<Value> ZipFile::readFile(const Arguments& args)
{
    HandleScope scope;

    if (args.Length() < 2)
        return ThrowException(Exception::TypeError(
          String::New("requires two arguments, the name of a file and a callback")));

    // first arg must be name
    if(!args[0]->IsString())
        return ThrowException(Exception::TypeError(
          String::New("first argument must be a file name inside the zip")));

    // last arg must be function callback
    if (!args[args.Length()-1]->IsFunction())
        return ThrowException(Exception::TypeError(
                  String::New("last argument must be a callback function")));

    std::string name = TOSTR(args[0]);

    ZipFile* zf = ObjectWrap::Unwrap<ZipFile>(args.This());

    closure_t *closure = new closure_t();

    // libzip is not threadsafe so we cannot use the zf->archive_
    // instead we open a new zip archive for each thread
    struct zip *za;
    int err;
    char errstr[1024];
  	if ((za=zip_open(zf->file_name_.c_str() , 0, &err)) == NULL) {
  	    zip_error_to_str(errstr, sizeof(errstr), err, errno);
  	    std::stringstream s;
  	    s << "cannot open file: " << zf->file_name_ << " error: " << errstr << "\n";
        zip_close(za);
        return ThrowException(Exception::Error(
            String::New(s.str().c_str())));
  	}

    closure->zf = zf;
    closure->za = za;
    closure->error = false;
    closure->name = name;
    closure->cb = Persistent<Function>::New(Handle<Function>::Cast(args[args.Length()-1]));
    eio_custom(EIO_ReadFile, EIO_PRI_DEFAULT, EIO_AfterReadFile, closure);
    ev_ref(EV_DEFAULT_UC);
    zf->Ref();
    return Undefined();
}

int ZipFile::EIO_ReadFile(eio_req *req)
{
    closure_t *closure = static_cast<closure_t *>(req->data);

    struct zip_file *zf_ptr=NULL;

    int idx = -1;
    int num = zip_get_num_files(closure->za);
    int i = 0;
    for (i=0; i<num; i++) {
        struct zip_stat st;
        zip_stat_index(closure->za, i, 0, &st);
        if (st.name == closure->name) {
            idx = i;
        }
    }

    if (idx == -1) {
  	    std::stringstream s;
  	    s << "No file found by the name of: '" << closure->name << "\n";
        closure->error = true;
        closure->error_name = s.str();

    } else {

        if ((zf_ptr = zip_fopen_index(closure->za, idx, 0)) == NULL) {
      	    std::stringstream s;
      	    s << "cannot open file #" << idx << " in "
      	      << closure->name << ": archive error: " << zip_strerror(closure->za) << "\n";
            closure->error = true;
            closure->error_name = s.str();

        } else {

            struct zip_stat st;
            zip_stat_index(closure->za, idx, 0, &st);
            closure->data.clear();
            closure->data.resize( st.size );

            int result =  0;
            result = (int)zip_fread( zf_ptr, reinterpret_cast<void*> (&closure->data[0]), closure->data.size() );

            if (result < 0) {
          	    std::stringstream s;
          	    s << "error reading file #" << idx << " in "
          	      << closure->name << ": archive error: " << zip_file_strerror(zf_ptr) << "\n";
                closure->error = true;
                closure->error_name = s.str();

            }
        }
    }

    zip_close(closure->za);
    zip_fclose(zf_ptr);
    return 0;
}

int ZipFile::EIO_AfterReadFile(eio_req *req)
{
    HandleScope scope;

    closure_t *closure = static_cast<closure_t *>(req->data);
    ev_unref(EV_DEFAULT_UC);

    TryCatch try_catch;

    if (closure->error) {
        Local<Value> argv[1] = { Exception::Error(String::New(closure->error_name.c_str())) };
        //Local<Value> argv[2] = { Exception::Error(String::New(closure->error_name.c_str())),
        //                         Local<Value>::New(Null()) };
        closure->cb->Call(Context::GetCurrent()->Global(), 2, argv);
    } else {
        #if NODE_VERSION_AT_LEAST(0,3,0)
          node::Buffer *retbuf = Buffer::New((char *)&closure->data[0],closure->data.size());
        #else
          node::Buffer *retbuf = Buffer::New(closure->data.size());
          std::memcpy(retbuf->data(), (char *)&closure->data[0], closure->data.size());
        #endif
        Local<Value> argv[2] = { Local<Value>::New(Null()), Local<Value>::New(retbuf->handle_) };
        closure->cb->Call(Context::GetCurrent()->Global(), 2, argv);
    }

    if (try_catch.HasCaught()) {
      FatalException(try_catch);
      //try_catch.ReThrow();
    }

    closure->zf->Unref();
    closure->cb.Dispose();
    delete closure;
    return 0;
}

<|endoftext|>