<commit_before>/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/*
 *     Copyright 2014 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 "config.h"

#include "failover-table.h"
#include "atomic.h"

#include "cJSON.h"

FailoverTable::FailoverTable(size_t capacity) : max_entries(capacity) {
}

FailoverTable::FailoverTable() : max_entries(25) {
}

FailoverTable::FailoverTable(const FailoverTable& other) {
    max_entries = other.max_entries;
    table = other.table;
}

// This should probably be replaced with something better.
uint64_t FailoverTable::generateId() {
    return (uint64_t)gethrtime();
}

// Call when taking over as master to update failover table.
// id should be generated to be fairly likely to be unique.
void FailoverTable::createEntry(uint64_t id, uint64_t high_sequence) {
    entry_t entry;
    entry.first = id;
    entry.second = high_sequence;
    // Our failover table represents only *our* branch of history.
    // We must remove branches we've diverged from.
    pruneAbove(high_sequence);
    // and *then* add our entry
    table.push_front(entry);
    // Cap the size of the table
    while (table.size() > max_entries) {
        table.pop_back();
    }
}

// Where should client roll back to?
uint64_t FailoverTable::findRollbackPoint(uint64_t failover_id) {
    table_t::iterator it;
    for (it = table.begin(); it != table.end(); it++) {
        if ((*it).first == failover_id) {
            if (it != table.begin()) {
                it--;
                return (*it).second;
            }
            // Shouldn't happen, as you should check that the failover id is not
            // the most recent
            return 0;
        }
    }
    return 0;
}

// Client should be rolled back?
bool FailoverTable::needsRollback(uint64_t since, uint64_t failover_id) {
    if (since == 0) {
        // Never need to roll back if rolling forward from 0
        return false;
    }

    if (failover_id == table.begin()->first) {
        // Client is caught up w.r.t. failovers.
        return false;
    }

    uint64_t rollback_seq = findRollbackPoint(failover_id);
    if(since < rollback_seq) {
        // Client is behind the branch point, so a rollback would be
        // meaningless.
        return false;
    }

    return true;
}

// Prune entries above seq (Should call this any time we roll back!)
void FailoverTable::pruneAbove(uint64_t seq) {
    table_t::iterator it;
    for (it = table.begin(); it != table.end(); it++) {
        if ((*it).second >= seq) {
            it = table.erase(it);
        }
    }
}

std::string FailoverTable::toJSON() {
    cJSON* list = cJSON_CreateArray();
    table_t::iterator it;
    for(it = table.begin(); it != table.end(); it++) {
        cJSON* obj = cJSON_CreateObject();
        cJSON_AddNumberToObject(obj, "id", (*it).first);
        cJSON_AddNumberToObject(obj, "seq", (*it).second);
        cJSON_AddItemToArray(list, obj);
    }
    char* json = cJSON_PrintUnformatted(list);
    std::string ret(json);
    free(json);
    cJSON_Delete(list);
    return ret;
}

bool FailoverTable::loadFromJSON(cJSON* parsed) {
    table.clear();

    bool ok = false;
    entry_t e;

    if (parsed) {
        // Document must be an array
        ok = (parsed->type == cJSON_Array);
        if (!ok) goto error;

        for (cJSON* it = parsed->child; it != NULL; it = it->next) {
            // Inner elements must be objects
            ok = (it->type == cJSON_Object);
            if (!ok) goto error;

            // Transform row to entry
            ok = JSONtoEntry(it, e);
            if (!ok) goto error;

            // add to table
            table.push_back(e);
        }
    }
 error:
    return ok;
}

bool FailoverTable::JSONtoEntry(cJSON* jobj, entry_t& entry) {
    cJSON* jid = cJSON_GetObjectItem(jobj, "id");
    cJSON* jseq = cJSON_GetObjectItem(jobj, "seq");
    if (!(jid && jseq)) return false;

    if (jid->type != cJSON_Number) return false;
    if (jseq->type != cJSON_Number) return false;

    entry.first = (uint64_t) jid->valuedouble;
    entry.second = (uint64_t) jseq->valuedouble;
    return true;
}
<commit_msg>Don't use goto in the failover log class<commit_after>/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/*
 *     Copyright 2014 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 "config.h"

#include "failover-table.h"
#include "atomic.h"

#include "cJSON.h"

FailoverTable::FailoverTable(size_t capacity) : max_entries(capacity) {
}

FailoverTable::FailoverTable() : max_entries(25) {
}

FailoverTable::FailoverTable(const FailoverTable& other) {
    max_entries = other.max_entries;
    table = other.table;
}

// This should probably be replaced with something better.
uint64_t FailoverTable::generateId() {
    return (uint64_t)gethrtime();
}

// Call when taking over as master to update failover table.
// id should be generated to be fairly likely to be unique.
void FailoverTable::createEntry(uint64_t id, uint64_t high_sequence) {
    entry_t entry;
    entry.first = id;
    entry.second = high_sequence;
    // Our failover table represents only *our* branch of history.
    // We must remove branches we've diverged from.
    pruneAbove(high_sequence);
    // and *then* add our entry
    table.push_front(entry);
    // Cap the size of the table
    while (table.size() > max_entries) {
        table.pop_back();
    }
}

// Where should client roll back to?
uint64_t FailoverTable::findRollbackPoint(uint64_t failover_id) {
    table_t::iterator it;
    for (it = table.begin(); it != table.end(); it++) {
        if ((*it).first == failover_id) {
            if (it != table.begin()) {
                it--;
                return (*it).second;
            }
            // Shouldn't happen, as you should check that the failover id is not
            // the most recent
            return 0;
        }
    }
    return 0;
}

// Client should be rolled back?
bool FailoverTable::needsRollback(uint64_t since, uint64_t failover_id) {
    if (since == 0) {
        // Never need to roll back if rolling forward from 0
        return false;
    }

    if (failover_id == table.begin()->first) {
        // Client is caught up w.r.t. failovers.
        return false;
    }

    uint64_t rollback_seq = findRollbackPoint(failover_id);
    if(since < rollback_seq) {
        // Client is behind the branch point, so a rollback would be
        // meaningless.
        return false;
    }

    return true;
}

// Prune entries above seq (Should call this any time we roll back!)
void FailoverTable::pruneAbove(uint64_t seq) {
    table_t::iterator it;
    for (it = table.begin(); it != table.end(); it++) {
        if ((*it).second >= seq) {
            it = table.erase(it);
        }
    }
}

std::string FailoverTable::toJSON() {
    cJSON* list = cJSON_CreateArray();
    table_t::iterator it;
    for(it = table.begin(); it != table.end(); it++) {
        cJSON* obj = cJSON_CreateObject();
        cJSON_AddNumberToObject(obj, "id", (*it).first);
        cJSON_AddNumberToObject(obj, "seq", (*it).second);
        cJSON_AddItemToArray(list, obj);
    }
    char* json = cJSON_PrintUnformatted(list);
    std::string ret(json);
    free(json);
    cJSON_Delete(list);
    return ret;
}

bool FailoverTable::loadFromJSON(cJSON* parsed) {
    table.clear();

    bool ok = false;
    entry_t e;

    if (parsed) {
        // Document must be an array
        ok = (parsed->type == cJSON_Array);
        if (!ok) {
            return ok;
        }

        for (cJSON* it = parsed->child; it != NULL; it = it->next) {
            // Inner elements must be objects
            ok = (it->type == cJSON_Object);
            if (!ok) {
                return ok;
            }

            // Transform row to entry
            ok = JSONtoEntry(it, e);
            if (!ok) {
                return ok;
            }

            // add to table
            table.push_back(e);
        }
    }
    return ok;
}

bool FailoverTable::JSONtoEntry(cJSON* jobj, entry_t& entry) {
    cJSON* jid = cJSON_GetObjectItem(jobj, "id");
    cJSON* jseq = cJSON_GetObjectItem(jobj, "seq");
    if (!(jid && jseq)) return false;

    if (jid->type != cJSON_Number) return false;
    if (jseq->type != cJSON_Number) return false;

    entry.first = (uint64_t) jid->valuedouble;
    entry.second = (uint64_t) jseq->valuedouble;
    return true;
}
<|endoftext|>


<commit_before>// The libMesh Finite Element Library.
// Copyright (C) 2002-2016 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner

// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.

// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.

// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

// C++ includes

// Local includes
#include "libmesh/side.h"
#include "libmesh/edge_edge2.h"
#include "libmesh/face_quad4.h"

namespace libMesh
{

// ------------------------------------------------------------
// Quad class static member initialization
const unsigned int Quad4::side_nodes_map[4][2] =
  {
    {0, 1}, // Side 0
    {1, 2}, // Side 1
    {2, 3}, // Side 2
    {3, 0}  // Side 3
  };

#ifdef LIBMESH_ENABLE_AMR

const float Quad4::_embedding_matrix[4][4][4] =
  {
    // embedding matrix for child 0
    {
      // 0    1    2    3
      {1.0, 0.0, 0.0, 0.0}, // 0
      {0.5, 0.5, 0.0, 0.0}, // 1
      {.25, .25, .25, .25}, // 2
      {0.5, 0.0, 0.0, 0.5}  // 3
    },

    // embedding matrix for child 1
    {
      // 0    1    2    3
      {0.5, 0.5, 0.0, 0.0}, // 0
      {0.0, 1.0, 0.0, 0.0}, // 1
      {0.0, 0.5, 0.5, 0.0}, // 2
      {.25, .25, .25, .25}  // 3
    },

    // embedding matrix for child 2
    {
      // 0    1    2    3
      {0.5, 0.0, 0.0, 0.5}, // 0
      {.25, .25, .25, .25}, // 1
      {0.0, 0.0, 0.5, 0.5}, // 2
      {0.0, 0.0, 0.0, 1.0}  // 3
    },

    // embedding matrix for child 3
    {
      // 0    1    2    3
      {.25, .25, .25, .25}, // 0
      {0.0, 0.5, 0.5, 0.0}, // 1
      {0.0, 0.0, 1.0, 0.0}, // 2
      {0.0, 0.0, 0.5, 0.5}  // 3
    }
  };

#endif

// ------------------------------------------------------------
// Quad4 class member functions

bool Quad4::is_vertex(const unsigned int) const
{
  return true;
}

bool Quad4::is_edge(const unsigned int) const
{
  return false;
}

bool Quad4::is_face(const unsigned int) const
{
  return false;
}

bool Quad4::is_node_on_side(const unsigned int n,
                            const unsigned int s) const
{
  libmesh_assert_less (s, n_sides());
  for (unsigned int i = 0; i != 2; ++i)
    if (side_nodes_map[s][i] == n)
      return true;
  return false;
}

bool Quad4::has_affine_map() const
{
  Point v = this->point(3) - this->point(0);
  return (v.relative_fuzzy_equals(this->point(2) - this->point(1)));
}

UniquePtr<Elem> Quad4::build_side (const unsigned int i,
                                   bool proxy) const
{
  libmesh_assert_less (i, this->n_sides());

  if (proxy)
    return UniquePtr<Elem>(new Side<Edge2,Quad4>(this,i));

  else
    {
      Elem * edge = new Edge2;
      edge->subdomain_id() = this->subdomain_id();

      // Set the nodes
      for (unsigned n=0; n<edge->n_nodes(); ++n)
        edge->set_node(n) = this->get_node(Quad4::side_nodes_map[i][n]);

      return UniquePtr<Elem>(edge);
    }

  libmesh_error_msg("We'll never get here!");
  return UniquePtr<Elem>();
}

void Quad4::connectivity(const unsigned int libmesh_dbg_var(sf),
                         const IOPackage iop,
                         std::vector<dof_id_type> & conn) const
{
  libmesh_assert_less (sf, this->n_sub_elem());
  libmesh_assert_not_equal_to (iop, INVALID_IO_PACKAGE);

  // Create storage.
  conn.resize(4);

  switch (iop)
    {
    case TECPLOT:
      {
        conn[0] = this->node(0)+1;
        conn[1] = this->node(1)+1;
        conn[2] = this->node(2)+1;
        conn[3] = this->node(3)+1;
        return;
      }

    case VTK:
      {
        conn[0] = this->node(0);
        conn[1] = this->node(1);
        conn[2] = this->node(2);
        conn[3] = this->node(3);
        return;
      }

    default:
      libmesh_error_msg("Unsupported IO package " << iop);
    }
}

Real Quad4::volume () const
{
  // Make copies of our points.  It makes the subsequent calculations a bit
  // shorter and avoids dereferencing the same pointer multiple times.
  Point
    x0 = point(0), x1 = point(1),
    x2 = point(2), x3 = point(3);

  // Construct constant data vectors.
  // \vec{x}_{\xi}  = \vec{a1}*eta + \vec{b1}
  // \vec{x}_{\eta} = \vec{a2}*xi  + \vec{b2}
  // This is copy-pasted directly from the output of a Python script.
  Point
    a1 = x0/4 - x1/4 + x2/4 - x3/4,
    b1 = -x0/4 + x1/4 + x2/4 - x3/4,
    a2 = a1,
    b2 = -x0/4 - x1/4 + x2/4 + x3/4;

  // Check for quick return for parallelogram QUAD4.
  if (a1.relative_fuzzy_equals(Point(0,0,0)))
    return 4. * b1.cross(b2).norm();

  // Otherwise, use 2x2 quadrature to approximate the surface area.

  // 4-point rule, exact for bi-cubics.  The weights for this rule are
  // all equal to 1.
  const Real q[2] = {-std::sqrt(3.)/3, std::sqrt(3.)/3.};

  Real vol=0.;
  for (unsigned int i=0; i<2; ++i)
    for (unsigned int j=0; j<2; ++j)
      vol += (q[j]*a1 + b1).cross(q[i]*a2 + b2).norm();

  return vol;
}

} // namespace libMesh
<commit_msg>Add Quad4::volume() implementation based on Taylor-series.<commit_after>// The libMesh Finite Element Library.
// Copyright (C) 2002-2016 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner

// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.

// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.

// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

// C++ includes

// Local includes
#include "libmesh/side.h"
#include "libmesh/edge_edge2.h"
#include "libmesh/face_quad4.h"

namespace libMesh
{

// ------------------------------------------------------------
// Quad class static member initialization
const unsigned int Quad4::side_nodes_map[4][2] =
  {
    {0, 1}, // Side 0
    {1, 2}, // Side 1
    {2, 3}, // Side 2
    {3, 0}  // Side 3
  };

#ifdef LIBMESH_ENABLE_AMR

const float Quad4::_embedding_matrix[4][4][4] =
  {
    // embedding matrix for child 0
    {
      // 0    1    2    3
      {1.0, 0.0, 0.0, 0.0}, // 0
      {0.5, 0.5, 0.0, 0.0}, // 1
      {.25, .25, .25, .25}, // 2
      {0.5, 0.0, 0.0, 0.5}  // 3
    },

    // embedding matrix for child 1
    {
      // 0    1    2    3
      {0.5, 0.5, 0.0, 0.0}, // 0
      {0.0, 1.0, 0.0, 0.0}, // 1
      {0.0, 0.5, 0.5, 0.0}, // 2
      {.25, .25, .25, .25}  // 3
    },

    // embedding matrix for child 2
    {
      // 0    1    2    3
      {0.5, 0.0, 0.0, 0.5}, // 0
      {.25, .25, .25, .25}, // 1
      {0.0, 0.0, 0.5, 0.5}, // 2
      {0.0, 0.0, 0.0, 1.0}  // 3
    },

    // embedding matrix for child 3
    {
      // 0    1    2    3
      {.25, .25, .25, .25}, // 0
      {0.0, 0.5, 0.5, 0.0}, // 1
      {0.0, 0.0, 1.0, 0.0}, // 2
      {0.0, 0.0, 0.5, 0.5}  // 3
    }
  };

#endif

// ------------------------------------------------------------
// Quad4 class member functions

bool Quad4::is_vertex(const unsigned int) const
{
  return true;
}

bool Quad4::is_edge(const unsigned int) const
{
  return false;
}

bool Quad4::is_face(const unsigned int) const
{
  return false;
}

bool Quad4::is_node_on_side(const unsigned int n,
                            const unsigned int s) const
{
  libmesh_assert_less (s, n_sides());
  for (unsigned int i = 0; i != 2; ++i)
    if (side_nodes_map[s][i] == n)
      return true;
  return false;
}

bool Quad4::has_affine_map() const
{
  Point v = this->point(3) - this->point(0);
  return (v.relative_fuzzy_equals(this->point(2) - this->point(1)));
}

UniquePtr<Elem> Quad4::build_side (const unsigned int i,
                                   bool proxy) const
{
  libmesh_assert_less (i, this->n_sides());

  if (proxy)
    return UniquePtr<Elem>(new Side<Edge2,Quad4>(this,i));

  else
    {
      Elem * edge = new Edge2;
      edge->subdomain_id() = this->subdomain_id();

      // Set the nodes
      for (unsigned n=0; n<edge->n_nodes(); ++n)
        edge->set_node(n) = this->get_node(Quad4::side_nodes_map[i][n]);

      return UniquePtr<Elem>(edge);
    }

  libmesh_error_msg("We'll never get here!");
  return UniquePtr<Elem>();
}

void Quad4::connectivity(const unsigned int libmesh_dbg_var(sf),
                         const IOPackage iop,
                         std::vector<dof_id_type> & conn) const
{
  libmesh_assert_less (sf, this->n_sub_elem());
  libmesh_assert_not_equal_to (iop, INVALID_IO_PACKAGE);

  // Create storage.
  conn.resize(4);

  switch (iop)
    {
    case TECPLOT:
      {
        conn[0] = this->node(0)+1;
        conn[1] = this->node(1)+1;
        conn[2] = this->node(2)+1;
        conn[3] = this->node(3)+1;
        return;
      }

    case VTK:
      {
        conn[0] = this->node(0);
        conn[1] = this->node(1);
        conn[2] = this->node(2);
        conn[3] = this->node(3);
        return;
      }

    default:
      libmesh_error_msg("Unsupported IO package " << iop);
    }
}

Real Quad4::volume () const
{
  // Make copies of our points.  It makes the subsequent calculations a bit
  // shorter and avoids dereferencing the same pointer multiple times.
  Point
    x0 = point(0), x1 = point(1),
    x2 = point(2), x3 = point(3);

  // This volume formula is derived by replacing the volume integrand,
  // which contains a square root, with a Taylor series approximation,
  // and integrating it exactly (hence there is no quadrature).

  // Construct constant data vectors.
  // \vec{x}_{\xi}  = \vec{a}*eta + \vec{b}
  // \vec{x}_{\eta} = \vec{a}*xi  + \vec{c}
  Point
    a = x0/4 - x1/4 + x2/4 - x3/4,
    b = -x0/4 + x1/4 + x2/4 - x3/4,
    c = -x0/4 - x1/4 + x2/4 + x3/4;

  Point
    ba = b.cross(a),
    ac = a.cross(c),
    bc = b.cross(c);

  Real
    c00 = bc.norm_sq(),
    c10 = 2*(bc*ba),
    c01 = 2*(ac*bc),
    c20 = ba.norm_sq(),
    c02 = ac.norm_sq();

  Real sqrt_c00 = std::sqrt(c00);

  return 4*sqrt_c00 + 1./(3.*sqrt_c00) * (2.*(c02+c20) - (c10*c10 + c01*c01)/(2.*c00));
}

} // namespace libMesh
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2015 Luke San Antonio
 * All rights reserved.
 */
#include "shader.h"

#include <istream>
#include <fstream>
#include <string>
#include "glad/glad.h"

#include "../../common/log.h"
#include "driver.h"

#define LOC_BAIL(location) if(location == -1) return

#define USE_THIS_SHADER() driver_->use_shader(*this)

// TODO: Log bad uniform locations.

namespace game { namespace gfx { namespace gl
{
  std::string load_stream(std::istream& stream) noexcept
  {
    std::string ret;
    while(!stream.eof() && stream.good())
    {
      ret.push_back(stream.get());
    }
    return ret;
  }
  void compile_shader(GLuint shade, std::string const& data)
  {
    int len = data.size();
    auto data_cstr = data.data();
    glShaderSource(shade, 1, &data_cstr, &len);

    glCompileShader(shade);

    GLint result = 0;
    glGetShaderiv(shade, GL_COMPILE_STATUS, &result);
    if(result == GL_FALSE)
    {
      // Compilation failed.
      constexpr size_t info_log_length = 2048;
      auto info_log = new char[info_log_length];

      glGetShaderInfoLog(shade, info_log_length - 1, NULL, info_log);

      info_log[info_log_length - 1] = '\0';
      log_e("Shader compilation failed (info log):\n%", info_log);

      delete[] info_log;
    }
  }
  GL_Shader::GL_Shader(Driver& d) noexcept : driver_(&d)
  {
    prog_ = glCreateProgram();
  }
  GL_Shader::~GL_Shader() noexcept
  {
    if(prog_) glDeleteProgram(prog_);
    if(f_shade_) glDeleteShader(f_shade_);
    if(v_shade_) glDeleteShader(v_shade_);
  }
  void GL_Shader::load_vertex_part(std::string const& str) noexcept
  {
    v_shade_ = glCreateShader(GL_VERTEX_SHADER);
    glAttachShader(prog_, v_shade_);

    std::ifstream file{str};
    compile_shader(v_shade_, load_stream(file));

    try_load_();
  }
  void GL_Shader::load_fragment_part(std::string const& str) noexcept
  {
    f_shade_ = glCreateShader(GL_FRAGMENT_SHADER);
    glAttachShader(prog_, f_shade_);

    std::ifstream file{str};
    compile_shader(f_shade_, load_stream(file));

    try_load_();
  }

  void GL_Shader::try_load_() noexcept
  {
    if(!v_shade_ || !f_shade_) return;

    glLinkProgram(prog_);
    linked_ = true;
  }

  int GL_Shader::get_location(std::string const& str) noexcept
  {
    if(!linked_) return -1;
    return glGetUniformLocation(prog_, str.data());
  }

  void GL_Shader::set_matrix(int loc, glm::mat4 const& mat) noexcept
  {
    LOC_BAIL(loc);

    USE_THIS_SHADER();
    glUniformMatrix4fv(loc, 1, GL_FALSE, &mat[0][0]);
  }

  void GL_Shader::set_integer(int loc, int tex_unit) noexcept
  {
    LOC_BAIL(loc);

    USE_THIS_SHADER();
    glUniform1i(loc, tex_unit);
  }

  void GL_Shader::set_vec2(int loc, glm::vec2 const& v) noexcept
  {
    LOC_BAIL(loc);

    USE_THIS_SHADER();
    glUniform2fv(loc, 1, &v[0]);
  }
  void GL_Shader::set_vec3(int loc, glm::vec3 const& v) noexcept
  {
    LOC_BAIL(loc);

    USE_THIS_SHADER();
    glUniform3fv(loc, 1, &v[0]);
  }
  void GL_Shader::set_vec4(int loc, glm::vec4 const& v) noexcept
  {
    LOC_BAIL(loc);

    USE_THIS_SHADER();
    glUniform4fv(loc, 1, &v[0]);
  }
  void GL_Shader::set_float(int loc, float f) noexcept
  {
    LOC_BAIL(loc);

    USE_THIS_SHADER();
    glUniform1f(loc, f);
  }
  void GL_Shader::use() noexcept
  {
    if(!linked_) return;
    glUseProgram(prog_);
  }
} } }

#undef LOC_BAIL
<commit_msg>Prevent the eof character from being added to the shader text buffer<commit_after>/*
 * Copyright (C) 2015 Luke San Antonio
 * All rights reserved.
 */
#include "shader.h"

#include <istream>
#include <fstream>
#include <string>
#include "glad/glad.h"

#include "../../common/log.h"
#include "driver.h"

#define LOC_BAIL(location) if(location == -1) return

#define USE_THIS_SHADER() driver_->use_shader(*this)

// TODO: Log bad uniform locations.

namespace game { namespace gfx { namespace gl
{
  std::string load_stream(std::istream& stream) noexcept
  {
    std::string ret;
    while(!stream.eof() && stream.good())
    {
      auto c = stream.get();
      if(std::istream::traits_type::not_eof(c))
      {
        ret.push_back(c);
      }
    }
    return ret;
  }
  void compile_shader(GLuint shade, std::string const& data)
  {
    int len = data.size();
    auto data_cstr = data.data();
    glShaderSource(shade, 1, &data_cstr, &len);

    glCompileShader(shade);

    GLint result = 0;
    glGetShaderiv(shade, GL_COMPILE_STATUS, &result);
    if(result == GL_FALSE)
    {
      // Compilation failed.
      constexpr size_t info_log_length = 2048;
      auto info_log = new char[info_log_length];

      glGetShaderInfoLog(shade, info_log_length - 1, NULL, info_log);

      info_log[info_log_length - 1] = '\0';
      log_e("Shader compilation failed (info log):\n%", info_log);

      delete[] info_log;
    }
  }
  GL_Shader::GL_Shader(Driver& d) noexcept : driver_(&d)
  {
    prog_ = glCreateProgram();
  }
  GL_Shader::~GL_Shader() noexcept
  {
    if(prog_) glDeleteProgram(prog_);
    if(f_shade_) glDeleteShader(f_shade_);
    if(v_shade_) glDeleteShader(v_shade_);
  }
  void GL_Shader::load_vertex_part(std::string const& str) noexcept
  {
    v_shade_ = glCreateShader(GL_VERTEX_SHADER);
    glAttachShader(prog_, v_shade_);

    std::ifstream file{str};
    compile_shader(v_shade_, load_stream(file));

    try_load_();
  }
  void GL_Shader::load_fragment_part(std::string const& str) noexcept
  {
    f_shade_ = glCreateShader(GL_FRAGMENT_SHADER);
    glAttachShader(prog_, f_shade_);

    std::ifstream file{str};
    compile_shader(f_shade_, load_stream(file));

    try_load_();
  }

  void GL_Shader::try_load_() noexcept
  {
    if(!v_shade_ || !f_shade_) return;

    glLinkProgram(prog_);
    linked_ = true;
  }

  int GL_Shader::get_location(std::string const& str) noexcept
  {
    if(!linked_) return -1;
    return glGetUniformLocation(prog_, str.data());
  }

  void GL_Shader::set_matrix(int loc, glm::mat4 const& mat) noexcept
  {
    LOC_BAIL(loc);

    USE_THIS_SHADER();
    glUniformMatrix4fv(loc, 1, GL_FALSE, &mat[0][0]);
  }

  void GL_Shader::set_integer(int loc, int tex_unit) noexcept
  {
    LOC_BAIL(loc);

    USE_THIS_SHADER();
    glUniform1i(loc, tex_unit);
  }

  void GL_Shader::set_vec2(int loc, glm::vec2 const& v) noexcept
  {
    LOC_BAIL(loc);

    USE_THIS_SHADER();
    glUniform2fv(loc, 1, &v[0]);
  }
  void GL_Shader::set_vec3(int loc, glm::vec3 const& v) noexcept
  {
    LOC_BAIL(loc);

    USE_THIS_SHADER();
    glUniform3fv(loc, 1, &v[0]);
  }
  void GL_Shader::set_vec4(int loc, glm::vec4 const& v) noexcept
  {
    LOC_BAIL(loc);

    USE_THIS_SHADER();
    glUniform4fv(loc, 1, &v[0]);
  }
  void GL_Shader::set_float(int loc, float f) noexcept
  {
    LOC_BAIL(loc);

    USE_THIS_SHADER();
    glUniform1f(loc, f);
  }
  void GL_Shader::use() noexcept
  {
    if(!linked_) return;
    glUseProgram(prog_);
  }
} } }

#undef LOC_BAIL
<|endoftext|>


<commit_before>// Copyright 2007, Google Inc.
// 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.
//     * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// 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
// OWNER 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.
//
// Author: wan@google.com (Zhanyong Wan)

// Google Test - The Google C++ Testing Framework
//
// This file implements a universal value printer that can print a
// value of any type T:
//
//   void ::testing::internal::UniversalPrinter<T>::Print(value, ostream_ptr);
//
// It uses the << operator when possible, and prints the bytes in the
// object otherwise.  A user can override its behavior for a class
// type Foo by defining either operator<<(::std::ostream&, const Foo&)
// or void PrintTo(const Foo&, ::std::ostream*) in the namespace that
// defines Foo.

#include "gtest/gtest-printers.h"
#include <ctype.h>
#include <stdio.h>
#include <ostream>  // NOLINT
#include <string>
#include "gtest/internal/gtest-port.h"

namespace testing {

namespace {

using ::std::ostream;

#if GTEST_OS_WINDOWS_MOBILE  // Windows CE does not define _snprintf_s.
# define snprintf _snprintf
#elif _MSC_VER >= 1400  // VC 8.0 and later deprecate snprintf and _snprintf.
# define snprintf _snprintf_s
#elif _MSC_VER
# define snprintf _snprintf
#endif  // GTEST_OS_WINDOWS_MOBILE

// Prints a segment of bytes in the given object.
void PrintByteSegmentInObjectTo(const unsigned char* obj_bytes, size_t start,
                                size_t count, ostream* os) {
  char text[5] = "";
  for (size_t i = 0; i != count; i++) {
    const size_t j = start + i;
    if (i != 0) {
      // Organizes the bytes into groups of 2 for easy parsing by
      // human.
      if ((j % 2) == 0)
        *os << ' ';
      else
        *os << '-';
    }
    snprintf(text, sizeof(text), "%02X", obj_bytes[j]);
    *os << text;
  }
}

// Prints the bytes in the given value to the given ostream.
void PrintBytesInObjectToImpl(const unsigned char* obj_bytes, size_t count,
                              ostream* os) {
  // Tells the user how big the object is.
  *os << count << "-byte object <";

  const size_t kThreshold = 132;
  const size_t kChunkSize = 64;
  // If the object size is bigger than kThreshold, we'll have to omit
  // some details by printing only the first and the last kChunkSize
  // bytes.
  // TODO(wan): let the user control the threshold using a flag.
  if (count < kThreshold) {
    PrintByteSegmentInObjectTo(obj_bytes, 0, count, os);
  } else {
    PrintByteSegmentInObjectTo(obj_bytes, 0, kChunkSize, os);
    *os << " ... ";
    // Rounds up to 2-byte boundary.
    const size_t resume_pos = (count - kChunkSize + 1)/2*2;
    PrintByteSegmentInObjectTo(obj_bytes, resume_pos, count - resume_pos, os);
  }
  *os << ">";
}

}  // namespace

namespace internal2 {

// Delegates to PrintBytesInObjectToImpl() to print the bytes in the
// given object.  The delegation simplifies the implementation, which
// uses the << operator and thus is easier done outside of the
// ::testing::internal namespace, which contains a << operator that
// sometimes conflicts with the one in STL.
void PrintBytesInObjectTo(const unsigned char* obj_bytes, size_t count,
                          ostream* os) {
  PrintBytesInObjectToImpl(obj_bytes, count, os);
}

}  // namespace internal2

namespace internal {

// Depending on the value of a char (or wchar_t), we print it in one
// of three formats:
//   - as is if it's a printable ASCII (e.g. 'a', '2', ' '),
//   - as a hexidecimal escape sequence (e.g. '\x7F'), or
//   - as a special escape sequence (e.g. '\r', '\n').
enum CharFormat {
  kAsIs,
  kHexEscape,
  kSpecialEscape
};

// Returns true if c is a printable ASCII character.  We test the
// value of c directly instead of calling isprint(), which is buggy on
// Windows Mobile.
inline bool IsPrintableAscii(wchar_t c) {
  return 0x20 <= c && c <= 0x7E;
}

// Prints a wide or narrow char c as a character literal without the
// quotes, escaping it when necessary; returns how c was formatted.
// The template argument UnsignedChar is the unsigned version of Char,
// which is the type of c.
template <typename UnsignedChar, typename Char>
static CharFormat PrintAsCharLiteralTo(Char c, ostream* os) {
  switch (static_cast<wchar_t>(c)) {
    case L'\0':
      *os << "\\0";
      break;
    case L'\'':
      *os << "\\'";
      break;
    case L'\\':
      *os << "\\\\";
      break;
    case L'\a':
      *os << "\\a";
      break;
    case L'\b':
      *os << "\\b";
      break;
    case L'\f':
      *os << "\\f";
      break;
    case L'\n':
      *os << "\\n";
      break;
    case L'\r':
      *os << "\\r";
      break;
    case L'\t':
      *os << "\\t";
      break;
    case L'\v':
      *os << "\\v";
      break;
    default:
      if (IsPrintableAscii(c)) {
        *os << static_cast<char>(c);
        return kAsIs;
      } else {
        *os << String::Format("\\x%X", static_cast<UnsignedChar>(c));
        return kHexEscape;
      }
  }
  return kSpecialEscape;
}

// Prints a char c as if it's part of a string literal, escaping it when
// necessary; returns how c was formatted.
static CharFormat PrintAsWideStringLiteralTo(wchar_t c, ostream* os) {
  switch (c) {
    case L'\'':
      *os << "'";
      return kAsIs;
    case L'"':
      *os << "\\\"";
      return kSpecialEscape;
    default:
      return PrintAsCharLiteralTo<wchar_t>(c, os);
  }
}

// Prints a char c as if it's part of a string literal, escaping it when
// necessary; returns how c was formatted.
static CharFormat PrintAsNarrowStringLiteralTo(char c, ostream* os) {
  return PrintAsWideStringLiteralTo(static_cast<unsigned char>(c), os);
}

// Prints a wide or narrow character c and its code.  '\0' is printed
// as "'\\0'", other unprintable characters are also properly escaped
// using the standard C++ escape sequence.  The template argument
// UnsignedChar is the unsigned version of Char, which is the type of c.
template <typename UnsignedChar, typename Char>
void PrintCharAndCodeTo(Char c, ostream* os) {
  // First, print c as a literal in the most readable form we can find.
  *os << ((sizeof(c) > 1) ? "L'" : "'");
  const CharFormat format = PrintAsCharLiteralTo<UnsignedChar>(c, os);
  *os << "'";

  // To aid user debugging, we also print c's code in decimal, unless
  // it's 0 (in which case c was printed as '\\0', making the code
  // obvious).
  if (c == 0)
    return;
  *os << " (" << String::Format("%d", c).c_str();

  // For more convenience, we print c's code again in hexidecimal,
  // unless c was already printed in the form '\x##' or the code is in
  // [1, 9].
  if (format == kHexEscape || (1 <= c && c <= 9)) {
    // Do nothing.
  } else {
    *os << String::Format(", 0x%X",
                          static_cast<UnsignedChar>(c)).c_str();
  }
  *os << ")";
}

void PrintTo(unsigned char c, ::std::ostream* os) {
  PrintCharAndCodeTo<unsigned char>(c, os);
}
void PrintTo(signed char c, ::std::ostream* os) {
  PrintCharAndCodeTo<unsigned char>(c, os);
}

// Prints a wchar_t as a symbol if it is printable or as its internal
// code otherwise and also as its code.  L'\0' is printed as "L'\\0'".
void PrintTo(wchar_t wc, ostream* os) {
  PrintCharAndCodeTo<wchar_t>(wc, os);
}

// Prints the given array of characters to the ostream.
// The array starts at *begin, the length is len, it may include '\0' characters
// and may not be null-terminated.
static void PrintCharsAsStringTo(const char* begin, size_t len, ostream* os) {
  *os << "\"";
  bool is_previous_hex = false;
  for (size_t index = 0; index < len; ++index) {
    const char cur = begin[index];
    if (is_previous_hex && IsXDigit(cur)) {
      // Previous character is of '\x..' form and this character can be
      // interpreted as another hexadecimal digit in its number. Break string to
      // disambiguate.
      *os << "\" \"";
    }
    is_previous_hex = PrintAsNarrowStringLiteralTo(cur, os) == kHexEscape;
  }
  *os << "\"";
}

// Prints a (const) char array of 'len' elements, starting at address 'begin'.
void UniversalPrintArray(const char* begin, size_t len, ostream* os) {
  PrintCharsAsStringTo(begin, len, os);
}

// Prints the given array of wide characters to the ostream.
// The array starts at *begin, the length is len, it may include L'\0'
// characters and may not be null-terminated.
static void PrintWideCharsAsStringTo(const wchar_t* begin, size_t len,
                                     ostream* os) {
  *os << "L\"";
  bool is_previous_hex = false;
  for (size_t index = 0; index < len; ++index) {
    const wchar_t cur = begin[index];
    if (is_previous_hex && 0 <= cur && cur < 128 &&
        IsXDigit(static_cast<char>(cur))) {
      // Previous character is of '\x..' form and this character can be
      // interpreted as another hexadecimal digit in its number. Break string to
      // disambiguate.
      *os << "\" L\"";
    }
    is_previous_hex = PrintAsWideStringLiteralTo(cur, os) == kHexEscape;
  }
  *os << "\"";
}

// Prints the given C string to the ostream.
void PrintTo(const char* s, ostream* os) {
  if (s == NULL) {
    *os << "NULL";
  } else {
    *os << ImplicitCast_<const void*>(s) << " pointing to ";
    PrintCharsAsStringTo(s, strlen(s), os);
  }
}

// MSVC compiler can be configured to define whar_t as a typedef
// of unsigned short. Defining an overload for const wchar_t* in that case
// would cause pointers to unsigned shorts be printed as wide strings,
// possibly accessing more memory than intended and causing invalid
// memory accesses. MSVC defines _NATIVE_WCHAR_T_DEFINED symbol when
// wchar_t is implemented as a native type.
#if !defined(_MSC_VER) || defined(_NATIVE_WCHAR_T_DEFINED)
// Prints the given wide C string to the ostream.
void PrintTo(const wchar_t* s, ostream* os) {
  if (s == NULL) {
    *os << "NULL";
  } else {
    *os << ImplicitCast_<const void*>(s) << " pointing to ";
    PrintWideCharsAsStringTo(s, wcslen(s), os);
  }
}
#endif  // wchar_t is native

// Prints a ::string object.
#if GTEST_HAS_GLOBAL_STRING
void PrintStringTo(const ::string& s, ostream* os) {
  PrintCharsAsStringTo(s.data(), s.size(), os);
}
#endif  // GTEST_HAS_GLOBAL_STRING

void PrintStringTo(const ::std::string& s, ostream* os) {
  PrintCharsAsStringTo(s.data(), s.size(), os);
}

// Prints a ::wstring object.
#if GTEST_HAS_GLOBAL_WSTRING
void PrintWideStringTo(const ::wstring& s, ostream* os) {
  PrintWideCharsAsStringTo(s.data(), s.size(), os);
}
#endif  // GTEST_HAS_GLOBAL_WSTRING

#if GTEST_HAS_STD_WSTRING
void PrintWideStringTo(const ::std::wstring& s, ostream* os) {
  PrintWideCharsAsStringTo(s.data(), s.size(), os);
}
#endif  // GTEST_HAS_STD_WSTRING

}  // namespace internal

}  // namespace testing
<commit_msg>Simplifies ASCII character detection in gtest-printers.h. This also makes it possible to build Google Test on MinGW.<commit_after>// Copyright 2007, Google Inc.
// 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.
//     * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// 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
// OWNER 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.
//
// Author: wan@google.com (Zhanyong Wan)

// Google Test - The Google C++ Testing Framework
//
// This file implements a universal value printer that can print a
// value of any type T:
//
//   void ::testing::internal::UniversalPrinter<T>::Print(value, ostream_ptr);
//
// It uses the << operator when possible, and prints the bytes in the
// object otherwise.  A user can override its behavior for a class
// type Foo by defining either operator<<(::std::ostream&, const Foo&)
// or void PrintTo(const Foo&, ::std::ostream*) in the namespace that
// defines Foo.

#include "gtest/gtest-printers.h"
#include <ctype.h>
#include <stdio.h>
#include <ostream>  // NOLINT
#include <string>
#include "gtest/internal/gtest-port.h"

namespace testing {

namespace {

using ::std::ostream;

#if GTEST_OS_WINDOWS_MOBILE  // Windows CE does not define _snprintf_s.
# define snprintf _snprintf
#elif _MSC_VER >= 1400  // VC 8.0 and later deprecate snprintf and _snprintf.
# define snprintf _snprintf_s
#elif _MSC_VER
# define snprintf _snprintf
#endif  // GTEST_OS_WINDOWS_MOBILE

// Prints a segment of bytes in the given object.
void PrintByteSegmentInObjectTo(const unsigned char* obj_bytes, size_t start,
                                size_t count, ostream* os) {
  char text[5] = "";
  for (size_t i = 0; i != count; i++) {
    const size_t j = start + i;
    if (i != 0) {
      // Organizes the bytes into groups of 2 for easy parsing by
      // human.
      if ((j % 2) == 0)
        *os << ' ';
      else
        *os << '-';
    }
    snprintf(text, sizeof(text), "%02X", obj_bytes[j]);
    *os << text;
  }
}

// Prints the bytes in the given value to the given ostream.
void PrintBytesInObjectToImpl(const unsigned char* obj_bytes, size_t count,
                              ostream* os) {
  // Tells the user how big the object is.
  *os << count << "-byte object <";

  const size_t kThreshold = 132;
  const size_t kChunkSize = 64;
  // If the object size is bigger than kThreshold, we'll have to omit
  // some details by printing only the first and the last kChunkSize
  // bytes.
  // TODO(wan): let the user control the threshold using a flag.
  if (count < kThreshold) {
    PrintByteSegmentInObjectTo(obj_bytes, 0, count, os);
  } else {
    PrintByteSegmentInObjectTo(obj_bytes, 0, kChunkSize, os);
    *os << " ... ";
    // Rounds up to 2-byte boundary.
    const size_t resume_pos = (count - kChunkSize + 1)/2*2;
    PrintByteSegmentInObjectTo(obj_bytes, resume_pos, count - resume_pos, os);
  }
  *os << ">";
}

}  // namespace

namespace internal2 {

// Delegates to PrintBytesInObjectToImpl() to print the bytes in the
// given object.  The delegation simplifies the implementation, which
// uses the << operator and thus is easier done outside of the
// ::testing::internal namespace, which contains a << operator that
// sometimes conflicts with the one in STL.
void PrintBytesInObjectTo(const unsigned char* obj_bytes, size_t count,
                          ostream* os) {
  PrintBytesInObjectToImpl(obj_bytes, count, os);
}

}  // namespace internal2

namespace internal {

// Depending on the value of a char (or wchar_t), we print it in one
// of three formats:
//   - as is if it's a printable ASCII (e.g. 'a', '2', ' '),
//   - as a hexidecimal escape sequence (e.g. '\x7F'), or
//   - as a special escape sequence (e.g. '\r', '\n').
enum CharFormat {
  kAsIs,
  kHexEscape,
  kSpecialEscape
};

// Returns true if c is a printable ASCII character.  We test the
// value of c directly instead of calling isprint(), which is buggy on
// Windows Mobile.
inline bool IsPrintableAscii(wchar_t c) {
  return 0x20 <= c && c <= 0x7E;
}

// Prints a wide or narrow char c as a character literal without the
// quotes, escaping it when necessary; returns how c was formatted.
// The template argument UnsignedChar is the unsigned version of Char,
// which is the type of c.
template <typename UnsignedChar, typename Char>
static CharFormat PrintAsCharLiteralTo(Char c, ostream* os) {
  switch (static_cast<wchar_t>(c)) {
    case L'\0':
      *os << "\\0";
      break;
    case L'\'':
      *os << "\\'";
      break;
    case L'\\':
      *os << "\\\\";
      break;
    case L'\a':
      *os << "\\a";
      break;
    case L'\b':
      *os << "\\b";
      break;
    case L'\f':
      *os << "\\f";
      break;
    case L'\n':
      *os << "\\n";
      break;
    case L'\r':
      *os << "\\r";
      break;
    case L'\t':
      *os << "\\t";
      break;
    case L'\v':
      *os << "\\v";
      break;
    default:
      if (IsPrintableAscii(c)) {
        *os << static_cast<char>(c);
        return kAsIs;
      } else {
        *os << String::Format("\\x%X", static_cast<UnsignedChar>(c));
        return kHexEscape;
      }
  }
  return kSpecialEscape;
}

// Prints a char c as if it's part of a string literal, escaping it when
// necessary; returns how c was formatted.
static CharFormat PrintAsWideStringLiteralTo(wchar_t c, ostream* os) {
  switch (c) {
    case L'\'':
      *os << "'";
      return kAsIs;
    case L'"':
      *os << "\\\"";
      return kSpecialEscape;
    default:
      return PrintAsCharLiteralTo<wchar_t>(c, os);
  }
}

// Prints a char c as if it's part of a string literal, escaping it when
// necessary; returns how c was formatted.
static CharFormat PrintAsNarrowStringLiteralTo(char c, ostream* os) {
  return PrintAsWideStringLiteralTo(static_cast<unsigned char>(c), os);
}

// Prints a wide or narrow character c and its code.  '\0' is printed
// as "'\\0'", other unprintable characters are also properly escaped
// using the standard C++ escape sequence.  The template argument
// UnsignedChar is the unsigned version of Char, which is the type of c.
template <typename UnsignedChar, typename Char>
void PrintCharAndCodeTo(Char c, ostream* os) {
  // First, print c as a literal in the most readable form we can find.
  *os << ((sizeof(c) > 1) ? "L'" : "'");
  const CharFormat format = PrintAsCharLiteralTo<UnsignedChar>(c, os);
  *os << "'";

  // To aid user debugging, we also print c's code in decimal, unless
  // it's 0 (in which case c was printed as '\\0', making the code
  // obvious).
  if (c == 0)
    return;
  *os << " (" << String::Format("%d", c).c_str();

  // For more convenience, we print c's code again in hexidecimal,
  // unless c was already printed in the form '\x##' or the code is in
  // [1, 9].
  if (format == kHexEscape || (1 <= c && c <= 9)) {
    // Do nothing.
  } else {
    *os << String::Format(", 0x%X",
                          static_cast<UnsignedChar>(c)).c_str();
  }
  *os << ")";
}

void PrintTo(unsigned char c, ::std::ostream* os) {
  PrintCharAndCodeTo<unsigned char>(c, os);
}
void PrintTo(signed char c, ::std::ostream* os) {
  PrintCharAndCodeTo<unsigned char>(c, os);
}

// Prints a wchar_t as a symbol if it is printable or as its internal
// code otherwise and also as its code.  L'\0' is printed as "L'\\0'".
void PrintTo(wchar_t wc, ostream* os) {
  PrintCharAndCodeTo<wchar_t>(wc, os);
}

// Prints the given array of characters to the ostream.
// The array starts at *begin, the length is len, it may include '\0' characters
// and may not be null-terminated.
static void PrintCharsAsStringTo(const char* begin, size_t len, ostream* os) {
  *os << "\"";
  bool is_previous_hex = false;
  for (size_t index = 0; index < len; ++index) {
    const char cur = begin[index];
    if (is_previous_hex && IsXDigit(cur)) {
      // Previous character is of '\x..' form and this character can be
      // interpreted as another hexadecimal digit in its number. Break string to
      // disambiguate.
      *os << "\" \"";
    }
    is_previous_hex = PrintAsNarrowStringLiteralTo(cur, os) == kHexEscape;
  }
  *os << "\"";
}

// Prints a (const) char array of 'len' elements, starting at address 'begin'.
void UniversalPrintArray(const char* begin, size_t len, ostream* os) {
  PrintCharsAsStringTo(begin, len, os);
}

// Prints the given array of wide characters to the ostream.
// The array starts at *begin, the length is len, it may include L'\0'
// characters and may not be null-terminated.
static void PrintWideCharsAsStringTo(const wchar_t* begin, size_t len,
                                     ostream* os) {
  *os << "L\"";
  bool is_previous_hex = false;
  for (size_t index = 0; index < len; ++index) {
    const wchar_t cur = begin[index];
    if (is_previous_hex && isascii(cur) && IsXDigit(static_cast<char>(cur))) {
      // Previous character is of '\x..' form and this character can be
      // interpreted as another hexadecimal digit in its number. Break string to
      // disambiguate.
      *os << "\" L\"";
    }
    is_previous_hex = PrintAsWideStringLiteralTo(cur, os) == kHexEscape;
  }
  *os << "\"";
}

// Prints the given C string to the ostream.
void PrintTo(const char* s, ostream* os) {
  if (s == NULL) {
    *os << "NULL";
  } else {
    *os << ImplicitCast_<const void*>(s) << " pointing to ";
    PrintCharsAsStringTo(s, strlen(s), os);
  }
}

// MSVC compiler can be configured to define whar_t as a typedef
// of unsigned short. Defining an overload for const wchar_t* in that case
// would cause pointers to unsigned shorts be printed as wide strings,
// possibly accessing more memory than intended and causing invalid
// memory accesses. MSVC defines _NATIVE_WCHAR_T_DEFINED symbol when
// wchar_t is implemented as a native type.
#if !defined(_MSC_VER) || defined(_NATIVE_WCHAR_T_DEFINED)
// Prints the given wide C string to the ostream.
void PrintTo(const wchar_t* s, ostream* os) {
  if (s == NULL) {
    *os << "NULL";
  } else {
    *os << ImplicitCast_<const void*>(s) << " pointing to ";
    PrintWideCharsAsStringTo(s, wcslen(s), os);
  }
}
#endif  // wchar_t is native

// Prints a ::string object.
#if GTEST_HAS_GLOBAL_STRING
void PrintStringTo(const ::string& s, ostream* os) {
  PrintCharsAsStringTo(s.data(), s.size(), os);
}
#endif  // GTEST_HAS_GLOBAL_STRING

void PrintStringTo(const ::std::string& s, ostream* os) {
  PrintCharsAsStringTo(s.data(), s.size(), os);
}

// Prints a ::wstring object.
#if GTEST_HAS_GLOBAL_WSTRING
void PrintWideStringTo(const ::wstring& s, ostream* os) {
  PrintWideCharsAsStringTo(s.data(), s.size(), os);
}
#endif  // GTEST_HAS_GLOBAL_WSTRING

#if GTEST_HAS_STD_WSTRING
void PrintWideStringTo(const ::std::wstring& s, ostream* os) {
  PrintWideCharsAsStringTo(s.data(), s.size(), os);
}
#endif  // GTEST_HAS_STD_WSTRING

}  // namespace internal

}  // namespace testing
<|endoftext|>


<commit_before>#include "util.h"
#include "h5spikewriter.h"

H5SpikeWriter::H5SpikeWriter()
{
	m_nc = 0;
	m_nu = 0;
	m_nwf = 0;
	m_h5groups.clear();
	m_h5Dtk.clear();
	m_h5Dts.clear();
	m_h5Dwf.clear();
	m_ns.clear();
	m_q = new ReaderWriterQueue<SPIKE *>(H5S_BUF_SIZE);
}
H5SpikeWriter::~H5SpikeWriter()
{
	delete m_q;
	m_q = NULL;
}
bool H5SpikeWriter::open(const char *fn, size_t nc, size_t nu, size_t nwf);
{
	if (isEnabled())
		return false;
	if (!H5Writer::open(fn)) {
		return false;
	}

	// Create a group
	m_h5topgroup = H5Gcreate2(m_h5file, "/Spikes",
								H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);

	if (m_h5topgroup < 0) {
		close();
		return false;
	}

	// create a group for each channel and for each unit in each channel
	for (size_t i=1; i<=nc; i++) { // 1-indexed
		char buf[32];
		sprintf(buf, "/Spikes/%03i", i);
		m_h5topgroup = H5Gcreate2(m_h5file, buf,
								H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
		for (size_t j=0; j<=nu; j++) { // 1-indexed, zero is unsorted
			sprintf(buf, "/Spikes/%03i/%03i", i, j);
			m_h5topgroup = H5Gcreate2(m_h5file, buf,
								H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
		}
	}
	/*

	// create analog dataspace
	// do not allow numchans to grow
	// but allow the number of samples to be unlimited
	hsize_t init_dims[2] = {nc, 0};
	hsize_t max_dims[2] = {nc, H5S_UNLIMITED};
	hid_t ds = H5Screate_simple(2, init_dims, max_dims);
	if (ds < 0) {
		close();
		return false;
	}
	// Create a dataset creation property list and set it to use chunking
	// Online advice is to, "keep chunks above 10KiB or so"
	// Realistically it should probably be something that can fit into L1 cache
	// so: definitely under 64 KB
	// 32*32*2 bytes ~= 2 KB
	// 32*128*2 bytes ~= 8 KB
	// also, perhaps compression will work better with slightly larger blocks
	hid_t prop = H5Pcreate(H5P_DATASET_CREATE);
	if (m_shuffle)
		shuffleDataset(prop);
	if (m_deflate)
		deflateDataset(prop);
	hsize_t chunk_dims[2] = {nc < 32 ? nc : 32, 128};
	H5Pset_chunk(prop, 2, chunk_dims);
	// Create the analog dataset
	m_h5Dsamples = H5Dcreate(m_h5file, "/Analog/Samples", H5T_STD_I16LE,
	                         ds, H5P_DEFAULT, prop, H5P_DEFAULT);
	if (m_h5Dsamples < 0) {
		close();
		return false;
	}
	m_h5dataspaces.push_back(ds);
	m_h5props.push_back(prop);

	// create tick dataspace
	// allow the number of samples to be unlimited
	init_dims[0] 	= 0;
	max_dims[0] 	= H5S_UNLIMITED;
	ds = H5Screate_simple(1, init_dims, max_dims);
	if (ds < 0) {
		close();
		return false;
	}
	//Create a dataset creation property list and set it to use chunking
	prop = H5Pcreate(H5P_DATASET_CREATE);
	if (m_shuffle)
		shuffleDataset(prop);
	if (m_deflate)
		deflateDataset(prop);
	chunk_dims[0] = 128;
	H5Pset_chunk(prop, 1, chunk_dims);
	// Create the tick dataset
	m_h5Dtk = H5Dcreate(m_h5file, "/Analog/Ticks", H5T_STD_I64LE,
	                    ds, H5P_DEFAULT, prop, H5P_DEFAULT);

	if (m_h5Dtk < 0) {
		close();
		return false;
	}
	m_h5dataspaces.push_back(ds);
	m_h5props.push_back(prop);

	// create timestamp dataspace
	// allow the number of samples to be unlimited
	init_dims[0] 	= 0;
	max_dims[0] 	= H5S_UNLIMITED;
	ds = H5Screate_simple(1, init_dims, max_dims);
	if (ds < 0) {
		close();
		return false;
	}
	//Create a dataset creation property list and set it to use chunking
	prop = H5Pcreate(H5P_DATASET_CREATE);
	if (m_shuffle)
		shuffleDataset(prop);
	if (m_deflate)
		deflateDataset(prop);
	chunk_dims[0] = 128;
	H5Pset_chunk(prop, 1, chunk_dims);
	// Create the tick dataset
	m_h5Dts = H5Dcreate(m_h5file, "/Analog/Timestamps", H5T_IEEE_F64LE,
	                    ds, H5P_DEFAULT, prop, H5P_DEFAULT);

	if (m_h5Dts < 0) {
		close();
		return false;
	}
	m_h5dataspaces.push_back(ds);
	m_h5props.push_back(prop);

	m_nc = nc;

	enable();
	*/
	return true;
}
/*
bool H5SpikeWriter::close()
{
	disable();

	m_ns = 0;
	m_nc = 0;

	if (m_h5Dts > 0) {
		H5Dclose(m_h5Dts);
		m_h5Dts = 0;
	}

	if (m_h5Dtk > 0) {
		H5Dclose(m_h5Dtk);
		m_h5Dtk = 0;
	}

	if (m_h5Dsamples > 0) {
		H5Dclose(m_h5Dsamples);
		m_h5Dsamples = 0;
	}

	return H5Writer::close();
}

bool H5SpikeWriter::add(AD *o)	// call from a single producer thread
{
	if (!isEnabled()) {
		delete[] (o->data);
		delete[] (o->tk);
		delete[] (o->ts);
		delete o;
		return false;
	}
	return m_q->enqueue(o);	// todo: what if this (memory alloc) fails
}

bool H5SpikeWriter::write()   // call from a single consumer thread
{
	if (!isEnabled())
		return false;

	//Lock so that the file isnt closed out from under us
	lock_guard<mutex> lock(m_mtx); // very important!!!

	bool dequeued;
	do {
		AD *o;
		dequeued = m_q->try_dequeue(o);
		if (dequeued) {

			if (o->nc != m_nc) {
				warn("well this is embarassing");
			}

			// extend sample dataset to fit new data
			// TODO: CHECK FOR ERROR
			hsize_t new_dims[2] = {m_nc, m_ns + o->ns};
			H5Dset_extent(m_h5Dsamples, new_dims);
			// select hyperslab in extended oprtion of dataset
			hid_t filespace = H5Dget_space(m_h5Dsamples);
			hsize_t offset[2] = {0, m_ns};
			hsize_t packet_dims[2] = {m_nc, o->ns};
			H5Sselect_hyperslab(filespace, H5S_SELECT_SET, offset, NULL,
			                    packet_dims, NULL);
			// Define memory space for new data
			// TODO CHECK FOR ERROR
			hid_t memspace = H5Screate_simple(2, packet_dims, NULL);
			// Write the dataset.
			// TODO: CHECK FOR ERROR
			H5Dwrite(m_h5Dsamples, H5T_NATIVE_INT16, memspace, filespace,
			         H5P_DEFAULT, o->data);
			H5Sclose(memspace);
			H5Sclose(filespace);

			// extend tick dataset to fit new data
			// TODO: CHECK FOR ERROR
			new_dims[0] = m_ns + o->ns;
			H5Dset_extent(m_h5Dtk, new_dims);
			// select hyperslab in extended oprtion of dataset
			filespace = H5Dget_space(m_h5Dtk);
			offset[0] = m_ns;
			packet_dims[0] = o->ns;
			H5Sselect_hyperslab(filespace, H5S_SELECT_SET, offset, NULL,
			                    packet_dims, NULL);
			// Define memory space for new data
			// TODO CHECK FOR ERROR
			memspace = H5Screate_simple(1, packet_dims, NULL);
			// Write the dataset.
			// TODO: CHECK FOR ERROR
			H5Dwrite(m_h5Dtk, H5T_NATIVE_INT64, memspace, filespace,
			         H5P_DEFAULT, o->tk);
			H5Sclose(memspace);
			H5Sclose(filespace);

			// extend ts dataset to fit new data
			// TODO: CHECK FOR ERROR
			new_dims[0] = m_ns + o->ns;
			H5Dset_extent(m_h5Dts, new_dims);
			// select hyperslab in extended oprtion of dataset
			filespace = H5Dget_space(m_h5Dts);
			offset[0] = m_ns;
			packet_dims[0] = o->ns;
			H5Sselect_hyperslab(filespace, H5S_SELECT_SET, offset, NULL,
			                    packet_dims, NULL);
			// Define memory space for new data
			// TODO CHECK FOR ERROR
			memspace = H5Screate_simple(1, packet_dims, NULL);
			// Write the dataset.
			// TODO: CHECK FOR ERROR
			H5Dwrite(m_h5Dts, H5T_NATIVE_DOUBLE, memspace, filespace,
			         H5P_DEFAULT, o->ts);
			H5Sclose(memspace);
			H5Sclose(filespace);

			m_ns += o->ns; // increment sample pointer

			//if (m_os.fail()) { // write lost, should we requeue?
			//	fprintf(stderr,"ERROR: %s write failed!\n", name());
			//}
			delete[] (o->data);
			delete[] (o->tk);
			delete[] (o->ts);
			delete o; // free the memory that was pointed to
		}
	} while (dequeued);

	return true;
}

size_t H5SpikeWriter::capacity()
{
	return m_q ? m_q->size_approx() : 0;
}
size_t H5SpikeWriter::bytes()
{
	return m_ns * m_nc * sizeof(i16);
}
bool H5SpikeWriter::setMetaData(double sr, float *scale, char *name, int slen)
{
	hid_t ds, attr, atype;

	ds = H5Screate(H5S_SCALAR);
	attr = H5Acreate(m_h5group, "Sampling Rate", H5T_IEEE_F64LE, ds,
	                 H5P_DEFAULT, H5P_DEFAULT);
	H5Awrite(attr, H5T_NATIVE_DOUBLE, &sr); // TODO: CHECK ERROR
	H5Aclose(attr); // TODO: check error
	H5Sclose(ds);

	hsize_t dims = m_nc;
	ds = H5Screate_simple(1, &dims, NULL);
	attr = H5Acreate(m_h5group, "Scale Factor", H5T_IEEE_F32LE, ds,
	                 H5P_DEFAULT, H5P_DEFAULT);
	H5Awrite(attr, H5T_NATIVE_FLOAT, scale); // TODO: CHECK ERROR
	H5Aclose(attr); // TODO: check error
	H5Sclose(ds);

	ds = H5Screate_simple(1, &dims, NULL);
	atype = H5Tcopy(H5T_C_S1);
	H5Tset_size(atype, slen);

	H5Tset_strpad(atype, H5T_STR_NULLTERM);
	attr = H5Acreate(m_h5group, "Channel Name", atype, ds,
	                 H5P_DEFAULT, H5P_DEFAULT);
	H5Awrite(attr, atype, name); // TODO: CHECK ERROR
	H5Aclose(attr);
	H5Sclose(ds);

	return true;
}
*/
<commit_msg>now put each spikechan group into a vector<commit_after>#include "util.h"
#include "h5spikewriter.h"

H5SpikeWriter::H5SpikeWriter()
{
	m_nc = 0;
	m_nu = 0;
	m_nwf = 0;
	m_h5groups.clear();
	m_h5Dtk.clear();
	m_h5Dts.clear();
	m_h5Dwf.clear();
	m_ns.clear();
	m_q = new ReaderWriterQueue<SPIKE *>(H5S_BUF_SIZE);
}
H5SpikeWriter::~H5SpikeWriter()
{
	delete m_q;
	m_q = NULL;
}
bool H5SpikeWriter::open(const char *fn, size_t nc, size_t nu, size_t nwf);
{
	if (isEnabled())
		return false;
	if (!H5Writer::open(fn)) {
		return false;
	}

	// Create a group
	m_h5topgroup = H5Gcreate2(m_h5file, "/Spikes",
	                          H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);

	if (m_h5topgroup < 0) {
		close();
		return false;
	}

	// create a group for each channel and for each unit in each channel
	hid_t group;
	for (size_t i=1; i<=nc; i++) { // 1-indexed
		char buf[32];
		sprintf(buf, "/Spikes/%03i", i);
		group = H5Gcreate2(m_h5file, buf,
		                   H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
		if (group < 0) {
			close();
			return false;
		}
		m_h5groups.push_back(group);
		for (size_t j=0; j<=nu; j++) { // 1-indexed, zero is unsorted
			sprintf(buf, "/Spikes/%03i/%03i", i, j);
			group = H5Gcreate2(m_h5file, buf,
			                   H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
			if (group < 0) {
				close();
				return false;
			}
			m_h5groups.push_back(group);
		}
	}
	/*

	// create analog dataspace
	// do not allow numchans to grow
	// but allow the number of samples to be unlimited
	hsize_t init_dims[2] = {nc, 0};
	hsize_t max_dims[2] = {nc, H5S_UNLIMITED};
	hid_t ds = H5Screate_simple(2, init_dims, max_dims);
	if (ds < 0) {
		close();
		return false;
	}
	// Create a dataset creation property list and set it to use chunking
	// Online advice is to, "keep chunks above 10KiB or so"
	// Realistically it should probably be something that can fit into L1 cache
	// so: definitely under 64 KB
	// 32*32*2 bytes ~= 2 KB
	// 32*128*2 bytes ~= 8 KB
	// also, perhaps compression will work better with slightly larger blocks
	hid_t prop = H5Pcreate(H5P_DATASET_CREATE);
	if (m_shuffle)
		shuffleDataset(prop);
	if (m_deflate)
		deflateDataset(prop);
	hsize_t chunk_dims[2] = {nc < 32 ? nc : 32, 128};
	H5Pset_chunk(prop, 2, chunk_dims);
	// Create the analog dataset
	m_h5Dsamples = H5Dcreate(m_h5file, "/Analog/Samples", H5T_STD_I16LE,
	                         ds, H5P_DEFAULT, prop, H5P_DEFAULT);
	if (m_h5Dsamples < 0) {
		close();
		return false;
	}
	m_h5dataspaces.push_back(ds);
	m_h5props.push_back(prop);

	// create tick dataspace
	// allow the number of samples to be unlimited
	init_dims[0] 	= 0;
	max_dims[0] 	= H5S_UNLIMITED;
	ds = H5Screate_simple(1, init_dims, max_dims);
	if (ds < 0) {
		close();
		return false;
	}
	//Create a dataset creation property list and set it to use chunking
	prop = H5Pcreate(H5P_DATASET_CREATE);
	if (m_shuffle)
		shuffleDataset(prop);
	if (m_deflate)
		deflateDataset(prop);
	chunk_dims[0] = 128;
	H5Pset_chunk(prop, 1, chunk_dims);
	// Create the tick dataset
	m_h5Dtk = H5Dcreate(m_h5file, "/Analog/Ticks", H5T_STD_I64LE,
	                    ds, H5P_DEFAULT, prop, H5P_DEFAULT);

	if (m_h5Dtk < 0) {
		close();
		return false;
	}
	m_h5dataspaces.push_back(ds);
	m_h5props.push_back(prop);

	// create timestamp dataspace
	// allow the number of samples to be unlimited
	init_dims[0] 	= 0;
	max_dims[0] 	= H5S_UNLIMITED;
	ds = H5Screate_simple(1, init_dims, max_dims);
	if (ds < 0) {
		close();
		return false;
	}
	//Create a dataset creation property list and set it to use chunking
	prop = H5Pcreate(H5P_DATASET_CREATE);
	if (m_shuffle)
		shuffleDataset(prop);
	if (m_deflate)
		deflateDataset(prop);
	chunk_dims[0] = 128;
	H5Pset_chunk(prop, 1, chunk_dims);
	// Create the tick dataset
	m_h5Dts = H5Dcreate(m_h5file, "/Analog/Timestamps", H5T_IEEE_F64LE,
	                    ds, H5P_DEFAULT, prop, H5P_DEFAULT);

	if (m_h5Dts < 0) {
		close();
		return false;
	}
	m_h5dataspaces.push_back(ds);
	m_h5props.push_back(prop);

	m_nc = nc;

	enable();
	*/
	return true;
}
/*
bool H5SpikeWriter::close()
{
	disable();

	m_ns = 0;
	m_nc = 0;

	if (m_h5Dts > 0) {
		H5Dclose(m_h5Dts);
		m_h5Dts = 0;
	}

	if (m_h5Dtk > 0) {
		H5Dclose(m_h5Dtk);
		m_h5Dtk = 0;
	}

	if (m_h5Dsamples > 0) {
		H5Dclose(m_h5Dsamples);
		m_h5Dsamples = 0;
	}

	return H5Writer::close();
}

bool H5SpikeWriter::add(AD *o)	// call from a single producer thread
{
	if (!isEnabled()) {
		delete[] (o->data);
		delete[] (o->tk);
		delete[] (o->ts);
		delete o;
		return false;
	}
	return m_q->enqueue(o);	// todo: what if this (memory alloc) fails
}

bool H5SpikeWriter::write()   // call from a single consumer thread
{
	if (!isEnabled())
		return false;

	//Lock so that the file isnt closed out from under us
	lock_guard<mutex> lock(m_mtx); // very important!!!

	bool dequeued;
	do {
		AD *o;
		dequeued = m_q->try_dequeue(o);
		if (dequeued) {

			if (o->nc != m_nc) {
				warn("well this is embarassing");
			}

			// extend sample dataset to fit new data
			// TODO: CHECK FOR ERROR
			hsize_t new_dims[2] = {m_nc, m_ns + o->ns};
			H5Dset_extent(m_h5Dsamples, new_dims);
			// select hyperslab in extended oprtion of dataset
			hid_t filespace = H5Dget_space(m_h5Dsamples);
			hsize_t offset[2] = {0, m_ns};
			hsize_t packet_dims[2] = {m_nc, o->ns};
			H5Sselect_hyperslab(filespace, H5S_SELECT_SET, offset, NULL,
			                    packet_dims, NULL);
			// Define memory space for new data
			// TODO CHECK FOR ERROR
			hid_t memspace = H5Screate_simple(2, packet_dims, NULL);
			// Write the dataset.
			// TODO: CHECK FOR ERROR
			H5Dwrite(m_h5Dsamples, H5T_NATIVE_INT16, memspace, filespace,
			         H5P_DEFAULT, o->data);
			H5Sclose(memspace);
			H5Sclose(filespace);

			// extend tick dataset to fit new data
			// TODO: CHECK FOR ERROR
			new_dims[0] = m_ns + o->ns;
			H5Dset_extent(m_h5Dtk, new_dims);
			// select hyperslab in extended oprtion of dataset
			filespace = H5Dget_space(m_h5Dtk);
			offset[0] = m_ns;
			packet_dims[0] = o->ns;
			H5Sselect_hyperslab(filespace, H5S_SELECT_SET, offset, NULL,
			                    packet_dims, NULL);
			// Define memory space for new data
			// TODO CHECK FOR ERROR
			memspace = H5Screate_simple(1, packet_dims, NULL);
			// Write the dataset.
			// TODO: CHECK FOR ERROR
			H5Dwrite(m_h5Dtk, H5T_NATIVE_INT64, memspace, filespace,
			         H5P_DEFAULT, o->tk);
			H5Sclose(memspace);
			H5Sclose(filespace);

			// extend ts dataset to fit new data
			// TODO: CHECK FOR ERROR
			new_dims[0] = m_ns + o->ns;
			H5Dset_extent(m_h5Dts, new_dims);
			// select hyperslab in extended oprtion of dataset
			filespace = H5Dget_space(m_h5Dts);
			offset[0] = m_ns;
			packet_dims[0] = o->ns;
			H5Sselect_hyperslab(filespace, H5S_SELECT_SET, offset, NULL,
			                    packet_dims, NULL);
			// Define memory space for new data
			// TODO CHECK FOR ERROR
			memspace = H5Screate_simple(1, packet_dims, NULL);
			// Write the dataset.
			// TODO: CHECK FOR ERROR
			H5Dwrite(m_h5Dts, H5T_NATIVE_DOUBLE, memspace, filespace,
			         H5P_DEFAULT, o->ts);
			H5Sclose(memspace);
			H5Sclose(filespace);

			m_ns += o->ns; // increment sample pointer

			//if (m_os.fail()) { // write lost, should we requeue?
			//	fprintf(stderr,"ERROR: %s write failed!\n", name());
			//}
			delete[] (o->data);
			delete[] (o->tk);
			delete[] (o->ts);
			delete o; // free the memory that was pointed to
		}
	} while (dequeued);

	return true;
}

size_t H5SpikeWriter::capacity()
{
	return m_q ? m_q->size_approx() : 0;
}
size_t H5SpikeWriter::bytes()
{
	return m_ns * m_nc * sizeof(i16);
}
bool H5SpikeWriter::setMetaData(double sr, float *scale, char *name, int slen)
{
	hid_t ds, attr, atype;

	ds = H5Screate(H5S_SCALAR);
	attr = H5Acreate(m_h5group, "Sampling Rate", H5T_IEEE_F64LE, ds,
	                 H5P_DEFAULT, H5P_DEFAULT);
	H5Awrite(attr, H5T_NATIVE_DOUBLE, &sr); // TODO: CHECK ERROR
	H5Aclose(attr); // TODO: check error
	H5Sclose(ds);

	hsize_t dims = m_nc;
	ds = H5Screate_simple(1, &dims, NULL);
	attr = H5Acreate(m_h5group, "Scale Factor", H5T_IEEE_F32LE, ds,
	                 H5P_DEFAULT, H5P_DEFAULT);
	H5Awrite(attr, H5T_NATIVE_FLOAT, scale); // TODO: CHECK ERROR
	H5Aclose(attr); // TODO: check error
	H5Sclose(ds);

	ds = H5Screate_simple(1, &dims, NULL);
	atype = H5Tcopy(H5T_C_S1);
	H5Tset_size(atype, slen);

	H5Tset_strpad(atype, H5T_STR_NULLTERM);
	attr = H5Acreate(m_h5group, "Channel Name", atype, ds,
	                 H5P_DEFAULT, H5P_DEFAULT);
	H5Awrite(attr, atype, name); // TODO: CHECK ERROR
	H5Aclose(attr);
	H5Sclose(ds);

	return true;
}
*/
<|endoftext|>


<commit_before>#include <vector>
#include <memory>
#include <string>
#include <sstream>
#include <exception>
#include "../third_party/bitmap/bitmap_image.hpp"
#include "../vis/colormap.hpp"
#include "../vis/image.hpp"

#pragma once

namespace io
{
    class BmpWriter
    {
        public:
            BmpWriter(std::shared_ptr<vis::Image> image)
                : image(image)
            {}

            /**
             * Export to file.
             * \param file name
             * \param colormap
             * \param upscale value
             */
            void write(std::string const&, size_t const& = 1) const;
        protected:
            BmpWriter() = delete;
            BmpWriter(BmpWriter const&) = delete;
            BmpWriter& operator= (BmpWriter const&) = delete;
        private:
            std::shared_ptr<vis::Image> image;
    };
}
<commit_msg>Added documentation for BmpWriter<commit_after>#include <vector>
#include <memory>
#include <string>
#include <sstream>
#include <exception>
#include "../third_party/bitmap/bitmap_image.hpp"
#include "../vis/colormap.hpp"
#include "../vis/image.hpp"

#pragma once

namespace io
{
    /**
     * Class to write image data to a Bitmap file.
     */
    class BmpWriter
    {
        public:
            /**
             * Constructor.
             *
             * \param image Image object containing pixel values
             */
            BmpWriter(std::shared_ptr<vis::Image> image)
                : image(image)
            {}

            /**
             * Export to file.
             * \param file name
             * \param upscale value
             */
            void write(std::string const&, size_t const& = 1) const;
        protected:
            BmpWriter() = delete;
            BmpWriter(BmpWriter const&) = delete;
            BmpWriter& operator= (BmpWriter const&) = delete;
        private:
            /**
             * Reference to pixel data.
             */
            std::shared_ptr<vis::Image> image;
    };
}
<|endoftext|>


<commit_before>// This file is part of Playslave-C++.
// Playslave-C++ is licenced under the MIT license: see LICENSE.txt.

/**
 * @file
 * Implementation of the non-virtual aspects of the IoReactor class.
 * @see io/io_reactor.hpp
 * @see io/io_reactor_asio.hpp
 * @see io/io_reactor_asio.cpp
 * @see io/io_reactor_std.hpp
 * @see io/io_reactor_std.cpp
 */

/* This is forced because we require C++11, which has std::chrono, anyway.
   Some environments fail Boost's std::chrono detection, and don't set this
   flag, which causes compilation failures. */
#ifndef BOOST_ASIO_HAS_STD_CHRONO
#define BOOST_ASIO_HAS_STD_CHRONO
#endif

#include <csignal>              // SIG*
#include <string>               // std::string

extern "C" {
#include <uv.h>
}

#include "../player/player.hpp" // Player
#include "../cmd.hpp"           // CommandHandler
#include "../errors.hpp"
#include "../messages.h"                        // MSG_*
#include "io_reactor.hpp"                       // IoReactor
#include "io_response.hpp"                      // ResponseCode
#include <boost/asio.hpp>                       // boost::asio::*
#include <boost/asio/high_resolution_timer.hpp> // boost::asio::high_resolution_timer

const std::chrono::nanoseconds IoReactor::PLAYER_UPDATE_PERIOD(1000);

IoReactor::IoReactor(Player &player, CommandHandler &handler,
                     const std::string &address, const std::string &port)
    : player(player),
      handler(handler),
      io_service(),
      acceptor(io_service),
      signals(io_service),
      manager(),
      new_connection(),
      loop(uv_loop_new())
{
	InitSignals();
	InitAcceptor(address, port);
	DoAccept();
	DoUpdateTimer();
}

void IoReactor::Run()
{
	io_service.run();
}

void IoReactor::HandleCommand(const std::string &line)
{
	bool valid = this->handler.Handle(line);
	if (valid) {
		Respond(ResponseCode::OKAY, line);
	} else {
		Respond(ResponseCode::WHAT, MSG_CMD_INVALID);
	}
}

void IoReactor::InitSignals()
{
	this->signals.add(SIGINT);
	this->signals.add(SIGTERM);
#ifdef SIGQUIT
	this->signals.add(SIGQUIT);
#endif // SIGQUIT

	this->signals.async_wait([this](boost::system::error_code,
	                                int) { End(); });
}

void IoReactor::DoUpdateTimer()
{
	auto tick = std::chrono::duration_cast<
	                std::chrono::high_resolution_clock::duration>(
	                PLAYER_UPDATE_PERIOD);
	boost::asio::high_resolution_timer t(this->io_service, tick);
	t.async_wait([this](boost::system::error_code) {
		bool running = this->player.Update();
		if (running) {
			DoUpdateTimer();
		} else {
			End();
		}
	});
}

void IoReactor::InitAcceptor(const std::string &address,
                             const std::string &port)
{
	boost::asio::ip::tcp::resolver resolver(io_service);
	boost::asio::ip::tcp::endpoint endpoint =
	                *resolver.resolve({ address, port });
	this->acceptor.open(endpoint.protocol());
	this->acceptor.set_option(
	                boost::asio::ip::tcp::acceptor::reuse_address(true));
	this->acceptor.bind(endpoint);
	this->acceptor.listen();
}

void IoReactor::DoAccept()
{
	auto cmd = [this](const std::string &line) { HandleCommand(line); };
	this->new_connection.reset(new TcpConnection(
	                cmd, this->manager, this->io_service, this->player));
	auto on_accept = [this](boost::system::error_code ec) {
		if (!ec) {
			this->manager.Start(this->new_connection);
		}
		if (this->acceptor.is_open()) {
			DoAccept();
		}
	};
	this->acceptor.async_accept(this->new_connection->Socket(), on_accept);
}

void IoReactor::RespondRaw(const std::string &string)
{
	this->manager.Send(string);
}

void IoReactor::End()
{
	this->acceptor.close();
	this->manager.StopAll();
	this->io_service.stop();
}

//
// TcpConnection
//

TcpConnection::TcpConnection(std::function<void(const std::string &)> cmd,
                             TcpConnectionManager &manager,
                             boost::asio::io_service &io_service,
                             const Player &player)
    : socket(io_service),
      strand(io_service),
      outbox(),
      cmd(cmd),
      manager(manager),
      player(player),
      closing(false)
{
}

void TcpConnection::Start()
{
	this->player.WelcomeClient(*this);
	DoRead();
}

void TcpConnection::Stop()
{
	this->closing = true;
	boost::system::error_code ignored_ec;
	this->socket.shutdown(boost::asio::ip::tcp::socket::shutdown_both,
	                      ignored_ec);
	this->socket.close();
}

boost::asio::ip::tcp::socket &TcpConnection::Socket()
{
	return socket;
}

void TcpConnection::DoRead()
{
	// This is needed to keep the TcpConnection alive while it performs the
	// read.  Otherwise, it'd be destructed as soon as it goes out of the
	// connection manager's list and cause illegal memory accesses.
	auto self(shared_from_this());

	boost::asio::async_read_until(socket, data, "\n",
	                              [this,
	                               self](const boost::system::error_code &
	                                                     ec,
	                                     std::size_t) {
		if (!ec) {
			std::istream is(&data);
			std::string s;
			std::getline(is, s);

			// If the line ended with CRLF, getline will miss the
			// CR, so we need to remove it from the line here.
			if (s.back() == '\r') {
				s.pop_back();
			}

			this->cmd(s);

			DoRead();
		} else if (ec != boost::asio::error::operation_aborted) {
			this->manager.Stop(shared_from_this());
		}
	});
}

void TcpConnection::Send(const std::string &string)
{
	if (this->closing) {
		return;
	}

	// This somewhat complex combination of a strand and queue ensure that
	// only one process actually writes to a TcpConnection at a given time.
	// Otherwise, writes could interrupt each other.
	this->strand.post([this, string]() {
		this->outbox.push_back(string);
		// If this string is the only one in the queue, then the last
		// chain
		// of DoWrite()s will have ended and we need to start a new one.
		if (this->outbox.size() == 1) {
			DoWrite();
		}
	});
}

void TcpConnection::DoWrite()
{
	// This is needed to keep the TcpConnection alive while it performs the
	// write.  Otherwise, it'd be destructed as soon as it goes out of the
	// connection manager's list and cause illegal memory accesses.
	auto self(shared_from_this());

	std::string string = this->outbox[0];
	// This is called after the write has finished.
	auto write_cb = [this, self](const boost::system::error_code &ec,
	                             std::size_t) {
		if (!ec) {
			this->outbox.pop_front();
			// Keep writing until and unless the outbox is emptied.
			// After that, the next Send will start DoWrite()ing
			// again.
			if (!this->outbox.empty()) {
				DoWrite();
			}
		} else if (ec != boost::asio::error::operation_aborted) {
			this->manager.Stop(shared_from_this());
		}
	};

	// Only add the newline character at the final opportunity.
	// Makes for easier debug lines.
	assert(string.back() != '\n');
	string += '\n';
	boost::asio::async_write(
	                this->socket,
	                boost::asio::buffer(string.c_str(), string.size()),
	                this->strand.wrap(write_cb));
}

void TcpConnection::RespondRaw(const std::string &string)
{
	Send(string);
}

//
// TcpConnectionManager
//

TcpConnectionManager::TcpConnectionManager()
{
}

void TcpConnectionManager::Start(TcpConnection::Pointer c)
{
	this->connections.insert(c);
	c->Start();
}

void TcpConnectionManager::Stop(TcpConnection::Pointer c)
{
	c->Stop();
	this->connections.erase(c);
}

void TcpConnectionManager::StopAll()
{
	for (auto c : this->connections) {
		c->Stop();
	}
	this->connections.clear();
}

void TcpConnectionManager::Send(const std::string &string)
{
	Debug() << "Send to all:" << string << std::endl;
	for (auto c : this->connections) {
		c->Send(string);
	}
}
<commit_msg>Add uv_run call.<commit_after>// This file is part of Playslave-C++.
// Playslave-C++ is licenced under the MIT license: see LICENSE.txt.

/**
 * @file
 * Implementation of the non-virtual aspects of the IoReactor class.
 * @see io/io_reactor.hpp
 * @see io/io_reactor_asio.hpp
 * @see io/io_reactor_asio.cpp
 * @see io/io_reactor_std.hpp
 * @see io/io_reactor_std.cpp
 */

/* This is forced because we require C++11, which has std::chrono, anyway.
   Some environments fail Boost's std::chrono detection, and don't set this
   flag, which causes compilation failures. */
#ifndef BOOST_ASIO_HAS_STD_CHRONO
#define BOOST_ASIO_HAS_STD_CHRONO
#endif

#include <csignal>              // SIG*
#include <string>               // std::string

extern "C" {
#include <uv.h>
}

#include "../player/player.hpp" // Player
#include "../cmd.hpp"           // CommandHandler
#include "../errors.hpp"
#include "../messages.h"                        // MSG_*
#include "io_reactor.hpp"                       // IoReactor
#include "io_response.hpp"                      // ResponseCode
#include <boost/asio.hpp>                       // boost::asio::*
#include <boost/asio/high_resolution_timer.hpp> // boost::asio::high_resolution_timer

const std::chrono::nanoseconds IoReactor::PLAYER_UPDATE_PERIOD(1000);

IoReactor::IoReactor(Player &player, CommandHandler &handler,
                     const std::string &address, const std::string &port)
    : player(player),
      handler(handler),
      io_service(),
      acceptor(io_service),
      signals(io_service),
      manager(),
      new_connection(),
      loop(uv_loop_new())
{
	InitSignals();
	InitAcceptor(address, port);
	DoAccept();
	DoUpdateTimer();
}

void IoReactor::Run()
{
	uv_run(loop, UV_RUN_DEFAULT);
	io_service.run();
}

void IoReactor::HandleCommand(const std::string &line)
{
	bool valid = this->handler.Handle(line);
	if (valid) {
		Respond(ResponseCode::OKAY, line);
	} else {
		Respond(ResponseCode::WHAT, MSG_CMD_INVALID);
	}
}

void IoReactor::InitSignals()
{
	this->signals.add(SIGINT);
	this->signals.add(SIGTERM);
#ifdef SIGQUIT
	this->signals.add(SIGQUIT);
#endif // SIGQUIT

	this->signals.async_wait([this](boost::system::error_code,
	                                int) { End(); });
}

void IoReactor::DoUpdateTimer()
{
	auto tick = std::chrono::duration_cast<
	                std::chrono::high_resolution_clock::duration>(
	                PLAYER_UPDATE_PERIOD);
	boost::asio::high_resolution_timer t(this->io_service, tick);
	t.async_wait([this](boost::system::error_code) {
		bool running = this->player.Update();
		if (running) {
			DoUpdateTimer();
		} else {
			End();
		}
	});
}

void IoReactor::InitAcceptor(const std::string &address,
                             const std::string &port)
{
	boost::asio::ip::tcp::resolver resolver(io_service);
	boost::asio::ip::tcp::endpoint endpoint =
	                *resolver.resolve({ address, port });
	this->acceptor.open(endpoint.protocol());
	this->acceptor.set_option(
	                boost::asio::ip::tcp::acceptor::reuse_address(true));
	this->acceptor.bind(endpoint);
	this->acceptor.listen();
}

void IoReactor::DoAccept()
{
	auto cmd = [this](const std::string &line) { HandleCommand(line); };
	this->new_connection.reset(new TcpConnection(
	                cmd, this->manager, this->io_service, this->player));
	auto on_accept = [this](boost::system::error_code ec) {
		if (!ec) {
			this->manager.Start(this->new_connection);
		}
		if (this->acceptor.is_open()) {
			DoAccept();
		}
	};
	this->acceptor.async_accept(this->new_connection->Socket(), on_accept);
}

void IoReactor::RespondRaw(const std::string &string)
{
	this->manager.Send(string);
}

void IoReactor::End()
{
	this->acceptor.close();
	this->manager.StopAll();
	this->io_service.stop();
}

//
// TcpConnection
//

TcpConnection::TcpConnection(std::function<void(const std::string &)> cmd,
                             TcpConnectionManager &manager,
                             boost::asio::io_service &io_service,
                             const Player &player)
    : socket(io_service),
      strand(io_service),
      outbox(),
      cmd(cmd),
      manager(manager),
      player(player),
      closing(false)
{
}

void TcpConnection::Start()
{
	this->player.WelcomeClient(*this);
	DoRead();
}

void TcpConnection::Stop()
{
	this->closing = true;
	boost::system::error_code ignored_ec;
	this->socket.shutdown(boost::asio::ip::tcp::socket::shutdown_both,
	                      ignored_ec);
	this->socket.close();
}

boost::asio::ip::tcp::socket &TcpConnection::Socket()
{
	return socket;
}

void TcpConnection::DoRead()
{
	// This is needed to keep the TcpConnection alive while it performs the
	// read.  Otherwise, it'd be destructed as soon as it goes out of the
	// connection manager's list and cause illegal memory accesses.
	auto self(shared_from_this());

	boost::asio::async_read_until(socket, data, "\n",
	                              [this,
	                               self](const boost::system::error_code &
	                                                     ec,
	                                     std::size_t) {
		if (!ec) {
			std::istream is(&data);
			std::string s;
			std::getline(is, s);

			// If the line ended with CRLF, getline will miss the
			// CR, so we need to remove it from the line here.
			if (s.back() == '\r') {
				s.pop_back();
			}

			this->cmd(s);

			DoRead();
		} else if (ec != boost::asio::error::operation_aborted) {
			this->manager.Stop(shared_from_this());
		}
	});
}

void TcpConnection::Send(const std::string &string)
{
	if (this->closing) {
		return;
	}

	// This somewhat complex combination of a strand and queue ensure that
	// only one process actually writes to a TcpConnection at a given time.
	// Otherwise, writes could interrupt each other.
	this->strand.post([this, string]() {
		this->outbox.push_back(string);
		// If this string is the only one in the queue, then the last
		// chain
		// of DoWrite()s will have ended and we need to start a new one.
		if (this->outbox.size() == 1) {
			DoWrite();
		}
	});
}

void TcpConnection::DoWrite()
{
	// This is needed to keep the TcpConnection alive while it performs the
	// write.  Otherwise, it'd be destructed as soon as it goes out of the
	// connection manager's list and cause illegal memory accesses.
	auto self(shared_from_this());

	std::string string = this->outbox[0];
	// This is called after the write has finished.
	auto write_cb = [this, self](const boost::system::error_code &ec,
	                             std::size_t) {
		if (!ec) {
			this->outbox.pop_front();
			// Keep writing until and unless the outbox is emptied.
			// After that, the next Send will start DoWrite()ing
			// again.
			if (!this->outbox.empty()) {
				DoWrite();
			}
		} else if (ec != boost::asio::error::operation_aborted) {
			this->manager.Stop(shared_from_this());
		}
	};

	// Only add the newline character at the final opportunity.
	// Makes for easier debug lines.
	assert(string.back() != '\n');
	string += '\n';
	boost::asio::async_write(
	                this->socket,
	                boost::asio::buffer(string.c_str(), string.size()),
	                this->strand.wrap(write_cb));
}

void TcpConnection::RespondRaw(const std::string &string)
{
	Send(string);
}

//
// TcpConnectionManager
//

TcpConnectionManager::TcpConnectionManager()
{
}

void TcpConnectionManager::Start(TcpConnection::Pointer c)
{
	this->connections.insert(c);
	c->Start();
}

void TcpConnectionManager::Stop(TcpConnection::Pointer c)
{
	c->Stop();
	this->connections.erase(c);
}

void TcpConnectionManager::StopAll()
{
	for (auto c : this->connections) {
		c->Stop();
	}
	this->connections.clear();
}

void TcpConnectionManager::Send(const std::string &string)
{
	Debug() << "Send to all:" << string << std::endl;
	for (auto c : this->connections) {
		c->Send(string);
	}
}
<|endoftext|>


<commit_before>// This file is part of Playslave-C++.
// Playslave-C++ is licenced under the MIT license: see LICENSE.txt.

/**
 * @file
 * Implementation of the non-virtual aspects of the IoReactor class.
 * @see io/io_reactor.hpp
 */

#include <csignal>              // SIG*
#include <string>               // std::string

extern "C" {
#include <uv.h>
}

#include "../player/player.hpp" // Player
#include "../cmd.hpp"           // CommandHandler
#include "../errors.hpp"
#include "../messages.h"                        // MSG_*
#include "io_reactor.hpp"                       // IoReactor
#include "io_response.hpp"                      // ResponseCode

const std::uint16_t IoReactor::PLAYER_UPDATE_PERIOD = 10; // ms

void AllocBuffer(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf)
{
	*buf = uv_buf_init((char *)malloc(suggested_size), suggested_size);
}

void ReadCallback(uv_stream_t *stream, ssize_t nread, const uv_buf_t *buf)
{
	TcpResponseSink *tcp = static_cast<TcpResponseSink *>(stream->data);
	tcp->Read(stream, nread, buf);
}

void OnNewConnection(uv_stream_t *server, int status)
{
	if (status == -1) {
		return;
	}
	IoReactor *reactor = static_cast<IoReactor *>(server->data);
	reactor->NewConnection(server);
}

void IoReactor::NewConnection(uv_stream_t *server)
{
	uv_tcp_t *client = (uv_tcp_t *)malloc(sizeof(uv_tcp_t));
	uv_tcp_init(uv_default_loop(), client);

	if (uv_accept(server, (uv_stream_t *)client) == 0) {
		auto tcp = std::make_shared<TcpResponseSink>(*this, client, this->handler);
		this->player.WelcomeClient(*tcp);
		this->connections.insert(tcp);
		client->data = static_cast<void *>(tcp.get());

		uv_read_start((uv_stream_t *)client, AllocBuffer, ReadCallback);
	} else {
		uv_close((uv_handle_t *)client, nullptr);
	}
}

void IoReactor::RemoveConnection(TcpResponseSink &conn)
{
	this->connections.erase(std::make_shared<TcpResponseSink>(conn));
}

IoReactor::IoReactor(Player &player, CommandHandler &handler,
                     const std::string &address, const std::string &port)
    : player(player),
      handler(handler)
{
	InitAcceptor(address, port);
	DoUpdateTimer();
}

void IoReactor::Run()
{
	uv_run(uv_default_loop(), UV_RUN_DEFAULT);
}

void UpdateTimerCallback(uv_timer_t *handle)
{
	Player *player = static_cast<Player *>(handle->data);
	bool running = player->Update();
	if (!running) {
		uv_stop(uv_default_loop());
	}
}

void CloseCallback(uv_handle_t *handle)
{
	TcpResponseSink *tcp = static_cast<TcpResponseSink *>(handle->data);
	tcp->Close();
}

void IoReactor::DoUpdateTimer()
{
	uv_timer_init(uv_default_loop(), &this->updater);
	this->updater.data = static_cast<void *>(&this->player);
	uv_timer_start(&this->updater, UpdateTimerCallback, 0, PLAYER_UPDATE_PERIOD);
}

void IoReactor::InitAcceptor(const std::string &address,
                             const std::string &port)
{
	int uport = std::stoi(port);

	uv_tcp_init(uv_default_loop(), &this->server);
	this->server.data = static_cast<void *>(this);

	struct sockaddr_in bind_addr;
	uv_ip4_addr(address.c_str(), uport, &bind_addr);
	uv_tcp_bind(&this->server, (const sockaddr *)&bind_addr, 0);

	int r = uv_listen((uv_stream_t *)&this->server, 128, OnNewConnection);
}

struct WriteReq {
	uv_write_t req;
	uv_buf_t buf;
};

void RespondCallback(uv_write_t *req, int status)
{
	WriteReq *wr = (WriteReq *)req;
	delete[] wr->buf.base;
	delete wr;
}

void IoReactor::RespondRaw(const std::string &string) const
{
	for (const std::shared_ptr<TcpResponseSink> &conn : this->connections) {
		conn->RespondRaw(string);
	}
}

void IoReactor::End()
{
	uv_stop(uv_default_loop());
}

//
// TcpResponseSink
//

TcpResponseSink::TcpResponseSink(IoReactor &parent, uv_tcp_t *tcp, CommandHandler &handler)
	: parent(parent),
	  tcp(tcp),
	  tokeniser(handler, *this)
{
}

void TcpResponseSink::RespondRaw(const std::string &string) const
{
	unsigned int l = string.length();
	const char *s = string.c_str();

	WriteReq *req = new WriteReq;
	req->buf = uv_buf_init(new char[l + 1], l + 1);
	memcpy(req->buf.base, s, l);
	req->buf.base[l] = '\n';

	uv_write((uv_write_t *)req, (uv_stream_t *)tcp, &req->buf, 1, RespondCallback);
}

void TcpResponseSink::Read(uv_stream_t *stream, ssize_t nread, const uv_buf_t *buf)
{
	if (nread < 0) {
		if (nread == UV_EOF) {
			uv_close((uv_handle_t *)stream, CloseCallback);
		}

		return;
	}

	this->tokeniser.Feed(buf->base, (buf->base) + nread);
}

void TcpResponseSink::Close()
{
	Debug() << "Closing client connection" << std::endl;
	this->parent.RemoveConnection(*this);
}<commit_msg>Use const auto &.<commit_after>// This file is part of Playslave-C++.
// Playslave-C++ is licenced under the MIT license: see LICENSE.txt.

/**
 * @file
 * Implementation of the non-virtual aspects of the IoReactor class.
 * @see io/io_reactor.hpp
 */

#include <csignal>              // SIG*
#include <string>               // std::string

extern "C" {
#include <uv.h>
}

#include "../player/player.hpp" // Player
#include "../cmd.hpp"           // CommandHandler
#include "../errors.hpp"
#include "../messages.h"                        // MSG_*
#include "io_reactor.hpp"                       // IoReactor
#include "io_response.hpp"                      // ResponseCode

const std::uint16_t IoReactor::PLAYER_UPDATE_PERIOD = 10; // ms

void AllocBuffer(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf)
{
	*buf = uv_buf_init((char *)malloc(suggested_size), suggested_size);
}

void ReadCallback(uv_stream_t *stream, ssize_t nread, const uv_buf_t *buf)
{
	TcpResponseSink *tcp = static_cast<TcpResponseSink *>(stream->data);
	tcp->Read(stream, nread, buf);
}

void OnNewConnection(uv_stream_t *server, int status)
{
	if (status == -1) {
		return;
	}
	IoReactor *reactor = static_cast<IoReactor *>(server->data);
	reactor->NewConnection(server);
}

void IoReactor::NewConnection(uv_stream_t *server)
{
	uv_tcp_t *client = (uv_tcp_t *)malloc(sizeof(uv_tcp_t));
	uv_tcp_init(uv_default_loop(), client);

	if (uv_accept(server, (uv_stream_t *)client) == 0) {
		auto tcp = std::make_shared<TcpResponseSink>(*this, client, this->handler);
		this->player.WelcomeClient(*tcp);
		this->connections.insert(tcp);
		client->data = static_cast<void *>(tcp.get());

		uv_read_start((uv_stream_t *)client, AllocBuffer, ReadCallback);
	} else {
		uv_close((uv_handle_t *)client, nullptr);
	}
}

void IoReactor::RemoveConnection(TcpResponseSink &conn)
{
	this->connections.erase(std::make_shared<TcpResponseSink>(conn));
}

IoReactor::IoReactor(Player &player, CommandHandler &handler,
                     const std::string &address, const std::string &port)
    : player(player),
      handler(handler)
{
	InitAcceptor(address, port);
	DoUpdateTimer();
}

void IoReactor::Run()
{
	uv_run(uv_default_loop(), UV_RUN_DEFAULT);
}

void UpdateTimerCallback(uv_timer_t *handle)
{
	Player *player = static_cast<Player *>(handle->data);
	bool running = player->Update();
	if (!running) {
		uv_stop(uv_default_loop());
	}
}

void CloseCallback(uv_handle_t *handle)
{
	TcpResponseSink *tcp = static_cast<TcpResponseSink *>(handle->data);
	tcp->Close();
}

void IoReactor::DoUpdateTimer()
{
	uv_timer_init(uv_default_loop(), &this->updater);
	this->updater.data = static_cast<void *>(&this->player);
	uv_timer_start(&this->updater, UpdateTimerCallback, 0, PLAYER_UPDATE_PERIOD);
}

void IoReactor::InitAcceptor(const std::string &address,
                             const std::string &port)
{
	int uport = std::stoi(port);

	uv_tcp_init(uv_default_loop(), &this->server);
	this->server.data = static_cast<void *>(this);

	struct sockaddr_in bind_addr;
	uv_ip4_addr(address.c_str(), uport, &bind_addr);
	uv_tcp_bind(&this->server, (const sockaddr *)&bind_addr, 0);

	int r = uv_listen((uv_stream_t *)&this->server, 128, OnNewConnection);
}

struct WriteReq {
	uv_write_t req;
	uv_buf_t buf;
};

void RespondCallback(uv_write_t *req, int status)
{
	WriteReq *wr = (WriteReq *)req;
	delete[] wr->buf.base;
	delete wr;
}

void IoReactor::RespondRaw(const std::string &string) const
{
	for (const auto &conn : this->connections) {
		conn->RespondRaw(string);
	}
}

void IoReactor::End()
{
	uv_stop(uv_default_loop());
}

//
// TcpResponseSink
//

TcpResponseSink::TcpResponseSink(IoReactor &parent, uv_tcp_t *tcp, CommandHandler &handler)
	: parent(parent),
	  tcp(tcp),
	  tokeniser(handler, *this)
{
}

void TcpResponseSink::RespondRaw(const std::string &string) const
{
	unsigned int l = string.length();
	const char *s = string.c_str();

	WriteReq *req = new WriteReq;
	req->buf = uv_buf_init(new char[l + 1], l + 1);
	memcpy(req->buf.base, s, l);
	req->buf.base[l] = '\n';

	uv_write((uv_write_t *)req, (uv_stream_t *)tcp, &req->buf, 1, RespondCallback);
}

void TcpResponseSink::Read(uv_stream_t *stream, ssize_t nread, const uv_buf_t *buf)
{
	if (nread < 0) {
		if (nread == UV_EOF) {
			uv_close((uv_handle_t *)stream, CloseCallback);
		}

		return;
	}

	this->tokeniser.Feed(buf->base, (buf->base) + nread);
}

void TcpResponseSink::Close()
{
	Debug() << "Closing client connection" << std::endl;
	this->parent.RemoveConnection(*this);
}<|endoftext|>