<commit_before>#ifndef _SNARKFRONT_RANK_1_OPS_HPP_
#define _SNARKFRONT_RANK_1_OPS_HPP_

#include <algorithm>
#include <cassert>
#include <cstdint>
#include <vector>
#include "PowersOf2.hpp"
#include <Rank1DSL.hpp> // snarklib
#include "TLsingleton.hpp"

namespace snarkfront {

////////////////////////////////////////////////////////////////////////////////
// variable consistency, enforce valid values
//

// constrain rank-1 variable to 0 and 1 values
template <template <typename> class SYS, typename FR>
void rank1_booleanity(SYS<FR>& S,
                      const snarklib::R1Variable<FR>& x)
{
#ifdef USE_ASSERT
    assert(! x.zeroIndex());
#endif
    S.addConstraint(x * (FR::one() - x) == FR::zero()); // only roots are 0 and 1
}

template <template <typename> class SYS, typename FR>
void rank1_booleanity(SYS<FR>& S,
                      const snarklib::R1Term<FR>& x)
{
#ifdef USE_ASSERT
    assert(x.isVariable());
#endif
    rank1_booleanity(S, x.var());
}

template <template <typename> class SYS, typename FR>
void rank1_booleanity(SYS<FR>& S,
                      const std::vector<snarklib::R1Term<FR>>& x)
{
    for (const auto& a : x)
        rank1_booleanity(S, a);
}

// constrain between a scalar in [0, 2^k) and representation as k bits
template <template <typename> class SYS, typename FR>
void rank1_split(SYS<FR>& S,
                 const snarklib::R1Term<FR>& x,
                 const std::vector<snarklib::R1Term<FR>>& b)
{
#ifdef USE_ASSERT
    assert(x.isVariable());
#endif

    snarklib::R1Combination<FR> LC;
    LC.reserveTerms(b.size());

    for (std::size_t i = 0; i < b.size(); ++i) {
        if (! b[i].zeroTerm())
            LC.addTerm(
                TL<PowersOf2<FR>>::singleton()->lookUp(i) * b[i]);
    }

    S.addConstraint(LC == x);
}

////////////////////////////////////////////////////////////////////////////////
// operators
//

#define DEFN_R1OP(NAME, XYZ)                            \
template <typename FR>                                  \
class R1_ ## NAME                                       \
{                                                       \
public:                                                 \
    typedef FR FieldType;                               \
    static snarklib::R1Constraint<FR> constraint(       \
        const snarklib::R1Term<FR>& x,                  \
        const snarklib::R1Term<FR>& y,                  \
        const snarklib::R1Term<FR>& z) {                \
        return XYZ ;                                    \
    }                                                   \
};

// AND, OR, XOR, SAME, CMPLMNT
DEFN_R1OP(AND, x * y == z)
DEFN_R1OP(OR, x + y - z == x * y)
DEFN_R1OP(XOR, x + y - z == ((FR::one() + FR::one()) * x) * y)
DEFN_R1OP(SAME, x + y + z - FR::one() == ((FR::one() + FR::one()) * x) * y)
DEFN_R1OP(CMPLMNT, x + z == FR::one())

// ADD, SUB, MUL
DEFN_R1OP(ADD, x + y == z)
DEFN_R1OP(SUB, x - y == z)
DEFN_R1OP(MUL, x * y == z)

#undef DEFN_R1OP

////////////////////////////////////////////////////////////////////////////////
// function to apply operators
//

template <template <typename> class SYS, typename R1OP>
void rank1_op(
    SYS<typename R1OP::FieldType>& S,
    const snarklib::R1Term<typename R1OP::FieldType>& x,
    const snarklib::R1Term<typename R1OP::FieldType>& y,
    const snarklib::R1Term<typename R1OP::FieldType>& z)
{
    S.addConstraint(R1OP::constraint(x, y, z));
}

////////////////////////////////////////////////////////////////////////////////
// bit shift and rotate
//

template <typename FR>
void rank1_shiftleft(std::vector<snarklib::R1Term<FR>>& x,
                     const std::size_t n)
{
#ifdef USE_ASSERT
    assert(! x.empty());
#endif
    if (0 == n) return; // do nothing

    const auto N = n % x.size();

    for (std::size_t i = x.size() - 1; i >= N; --i) {
        x[i] = x[i - N];
    }

    for (std::size_t i = 0; i < N; ++i) {
        x[i] = snarklib::R1Term<FR>();
    }
}

template <typename FR>
void rank1_shiftright(std::vector<snarklib::R1Term<FR>>& x,
                      const std::size_t n)
{
#ifdef USE_ASSERT
    assert(! x.empty());
#endif
    if (0 == n) return; // do nothing

    const auto N = n % x.size();

    for (std::size_t i = 0; i < x.size() - N; ++i) {
        x[i] = x[i + N];
    }

    for (std::size_t i = x.size() - N; i < x.size(); ++i) {
        x[i] = snarklib::R1Term<FR>();
    }
}

template <typename FR>
void rank1_rotateleft(std::vector<snarklib::R1Term<FR>>& x,
                      const std::size_t n)
{
#ifdef USE_ASSERT
    assert(! x.empty());
#endif
    if (0 == n) return; // do nothing

    const auto N = n % x.size();

    std::vector<snarklib::R1Term<FR>> v(x.size());

    for (std::size_t i = 0; i < x.size(); ++i) {
        v[(i + N) % x.size()] = x[i];
    }

    x = v;
}

template <typename FR>
void rank1_rotateright(std::vector<snarklib::R1Term<FR>>& x,
                       const std::size_t n)
{
#ifdef USE_ASSERT
    assert(! x.empty());
#endif
    if (0 == n) return; // do nothing

    const auto N = n % x.size();

    std::vector<snarklib::R1Term<FR>> v(x.size());

    for (std::size_t i = 0; i < x.size(); ++i) {
        v[i] = x[(i + N) % x.size()];
    }

    x = v;
}

////////////////////////////////////////////////////////////////////////////////
// convert between bitwise word types
//

template <typename FR>
std::vector<snarklib::R1Term<FR>>
rank1_xword(
    const std::vector<snarklib::R1Term<FR>>& x,
    const std::size_t returnSize)
{
    std::vector<snarklib::R1Term<FR>> v(returnSize, FR::zero());

    if (1 == x.size()) {
        // convert bool to word
        for (std::size_t i = 0; i < returnSize; ++i)
            v[i] = x[0];

    } else {
        // convert between 32-bit and 64-bit words
        const auto N = std::min(returnSize, x.size());

        for (std::size_t i = 0; i < N; ++i)
            v[i] = x[i];
    }

    return v;
}

} // namespace snarkfront

#endif
<commit_msg>rank1_xword comments include bool and 8-bit<commit_after>#ifndef _SNARKFRONT_RANK_1_OPS_HPP_
#define _SNARKFRONT_RANK_1_OPS_HPP_

#include <algorithm>
#include <cassert>
#include <cstdint>
#include <vector>
#include "PowersOf2.hpp"
#include <Rank1DSL.hpp> // snarklib
#include "TLsingleton.hpp"

namespace snarkfront {

////////////////////////////////////////////////////////////////////////////////
// variable consistency, enforce valid values
//

// constrain rank-1 variable to 0 and 1 values
template <template <typename> class SYS, typename FR>
void rank1_booleanity(SYS<FR>& S,
                      const snarklib::R1Variable<FR>& x)
{
#ifdef USE_ASSERT
    assert(! x.zeroIndex());
#endif
    S.addConstraint(x * (FR::one() - x) == FR::zero()); // only roots are 0 and 1
}

template <template <typename> class SYS, typename FR>
void rank1_booleanity(SYS<FR>& S,
                      const snarklib::R1Term<FR>& x)
{
#ifdef USE_ASSERT
    assert(x.isVariable());
#endif
    rank1_booleanity(S, x.var());
}

template <template <typename> class SYS, typename FR>
void rank1_booleanity(SYS<FR>& S,
                      const std::vector<snarklib::R1Term<FR>>& x)
{
    for (const auto& a : x)
        rank1_booleanity(S, a);
}

// constrain between a scalar in [0, 2^k) and representation as k bits
template <template <typename> class SYS, typename FR>
void rank1_split(SYS<FR>& S,
                 const snarklib::R1Term<FR>& x,
                 const std::vector<snarklib::R1Term<FR>>& b)
{
#ifdef USE_ASSERT
    assert(x.isVariable());
#endif

    snarklib::R1Combination<FR> LC;
    LC.reserveTerms(b.size());

    for (std::size_t i = 0; i < b.size(); ++i) {
        if (! b[i].zeroTerm())
            LC.addTerm(
                TL<PowersOf2<FR>>::singleton()->lookUp(i) * b[i]);
    }

    S.addConstraint(LC == x);
}

////////////////////////////////////////////////////////////////////////////////
// operators
//

#define DEFN_R1OP(NAME, XYZ)                            \
template <typename FR>                                  \
class R1_ ## NAME                                       \
{                                                       \
public:                                                 \
    typedef FR FieldType;                               \
    static snarklib::R1Constraint<FR> constraint(       \
        const snarklib::R1Term<FR>& x,                  \
        const snarklib::R1Term<FR>& y,                  \
        const snarklib::R1Term<FR>& z) {                \
        return XYZ ;                                    \
    }                                                   \
};

// AND, OR, XOR, SAME, CMPLMNT
DEFN_R1OP(AND, x * y == z)
DEFN_R1OP(OR, x + y - z == x * y)
DEFN_R1OP(XOR, x + y - z == ((FR::one() + FR::one()) * x) * y)
DEFN_R1OP(SAME, x + y + z - FR::one() == ((FR::one() + FR::one()) * x) * y)
DEFN_R1OP(CMPLMNT, x + z == FR::one())

// ADD, SUB, MUL
DEFN_R1OP(ADD, x + y == z)
DEFN_R1OP(SUB, x - y == z)
DEFN_R1OP(MUL, x * y == z)

#undef DEFN_R1OP

////////////////////////////////////////////////////////////////////////////////
// function to apply operators
//

template <template <typename> class SYS, typename R1OP>
void rank1_op(
    SYS<typename R1OP::FieldType>& S,
    const snarklib::R1Term<typename R1OP::FieldType>& x,
    const snarklib::R1Term<typename R1OP::FieldType>& y,
    const snarklib::R1Term<typename R1OP::FieldType>& z)
{
    S.addConstraint(R1OP::constraint(x, y, z));
}

////////////////////////////////////////////////////////////////////////////////
// bit shift and rotate
//

template <typename FR>
void rank1_shiftleft(std::vector<snarklib::R1Term<FR>>& x,
                     const std::size_t n)
{
#ifdef USE_ASSERT
    assert(! x.empty());
#endif
    if (0 == n) return; // do nothing

    const auto N = n % x.size();

    for (std::size_t i = x.size() - 1; i >= N; --i) {
        x[i] = x[i - N];
    }

    for (std::size_t i = 0; i < N; ++i) {
        x[i] = snarklib::R1Term<FR>();
    }
}

template <typename FR>
void rank1_shiftright(std::vector<snarklib::R1Term<FR>>& x,
                      const std::size_t n)
{
#ifdef USE_ASSERT
    assert(! x.empty());
#endif
    if (0 == n) return; // do nothing

    const auto N = n % x.size();

    for (std::size_t i = 0; i < x.size() - N; ++i) {
        x[i] = x[i + N];
    }

    for (std::size_t i = x.size() - N; i < x.size(); ++i) {
        x[i] = snarklib::R1Term<FR>();
    }
}

template <typename FR>
void rank1_rotateleft(std::vector<snarklib::R1Term<FR>>& x,
                      const std::size_t n)
{
#ifdef USE_ASSERT
    assert(! x.empty());
#endif
    if (0 == n) return; // do nothing

    const auto N = n % x.size();

    std::vector<snarklib::R1Term<FR>> v(x.size());

    for (std::size_t i = 0; i < x.size(); ++i) {
        v[(i + N) % x.size()] = x[i];
    }

    x = v;
}

template <typename FR>
void rank1_rotateright(std::vector<snarklib::R1Term<FR>>& x,
                       const std::size_t n)
{
#ifdef USE_ASSERT
    assert(! x.empty());
#endif
    if (0 == n) return; // do nothing

    const auto N = n % x.size();

    std::vector<snarklib::R1Term<FR>> v(x.size());

    for (std::size_t i = 0; i < x.size(); ++i) {
        v[i] = x[(i + N) % x.size()];
    }

    x = v;
}

////////////////////////////////////////////////////////////////////////////////
// bitwise conversion between unsigned integers and bool
//

template <typename FR>
std::vector<snarklib::R1Term<FR>>
rank1_xword(
    const std::vector<snarklib::R1Term<FR>>& x,
    const std::size_t returnSize)
{
    std::vector<snarklib::R1Term<FR>> v(returnSize, FR::zero());

    if (1 == x.size()) {
        // convert bool to 8-bit, 32-bit, or 64-bit
        for (std::size_t i = 0; i < returnSize; ++i)
            v[i] = x[0];

    } else {
        // if destination type is bool, returnSize is 1 so takes 0th bit
        // otherwise, bitwise slices between unsigned integer types
        const auto N = std::min(returnSize, x.size());
        for (std::size_t i = 0; i < N; ++i)
            v[i] = x[i];
    }

    return v;
}

} // namespace snarkfront

#endif
<|endoftext|>


<commit_before>/*
Copyright 2014 Adam Grandquist

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.
 */
/**
 * @author Adam Grandquist
 * @copyright Apache
 */

#include "ReQL-CPP.hpp"

#include "ReQL.hpp"
<commit_msg>Implement connection methods.<commit_after>/*
Copyright 2014 Adam Grandquist

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.
 */
/**
 * @author Adam Grandquist
 * @copyright Apache
 */

#include "ReQL-CPP.hpp"

#include "ReQL.hpp"

#include <cstdlib>

using namespace ReQL;

Connection::Connection() {
  conn = (struct _ReQL_Conn_s *)new _ReQL_Conn_t();
  conn = (struct _ReQL_Conn_s *)_reql_new_connection((_ReQL_Conn_t *)conn);

  if (!conn) {
    return;
  }

  char *buf;

  if (_reql_connect((_ReQL_Conn_t *)conn, &buf)) {
  }

  free(buf);
}

Connection::Connection(std::string host) {
  conn = (struct _ReQL_Conn_s *)new _ReQL_Conn_t();
  conn = (struct _ReQL_Conn_s *)_reql_new_connection((_ReQL_Conn_t *)conn);

  if (!conn) {
    return;
  }

  char *buf;

  if (_reql_connect((_ReQL_Conn_t *)conn, &buf)) {
  }

  free(buf);
}

Connection::Connection(std::string host, std::string port) {
  conn = (struct _ReQL_Conn_s *)new _ReQL_Conn_t();
  conn = (struct _ReQL_Conn_s *)_reql_new_connection((_ReQL_Conn_t *)conn);

  if (!conn) {
    return;
  }

  char *buf;

  if (_reql_connect((_ReQL_Conn_t *)conn, &buf)) {
  }

  free(buf);
}

Connection::Connection(std::string host, std::string port, std::string key) {
  conn = (struct _ReQL_Conn_s *)new _ReQL_Conn_t();
  conn = (struct _ReQL_Conn_s *)_reql_new_connection((_ReQL_Conn_t *)conn);

  if (!conn) {
    return;
  }

  char *buf;

  if (_reql_connect((_ReQL_Conn_t *)conn, &buf)) {
  }

  free(buf);
}

Connection connect() {
  return Connection();
}

Connection connect(std::string host) {
  return Connection(host);
}

Connection connect(std::string host, std::string port) {
  return Connection(host, port);
}

Connection connect(std::string host, std::string port, std::string key) {
  return Connection(host, port, key);
}
<|endoftext|>


<commit_before>/**
 * This file is part of the "libfnord" project
 *   Copyright (c) 2015 Paul Asmuth
 *
 * FnordMetric is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License v3.0. You should have received a
 * copy of the GNU General Public License along with this program. If not, see
 * <http://www.gnu.org/licenses/>.
 */
#include <fnord-base/io/fileutil.h>
#include <fnord-base/io/mmappedfile.h>
#include <fnord-base/util/binarymessagereader.h>
#include <fnord-base/util/binarymessagewriter.h>
#include <fnord-cstable/CSTableBuilder.h>
#include <fnord-cstable/CSTableWriter.h>
#include <fnord-cstable/CSTableReader.h>
#include <fnord-cstable/UInt64ColumnWriter.h>
#include <fnord-cstable/UInt64ColumnReader.h>
#include <fnord-cstable/RecordMaterializer.h>
#include <fnord-msg/MessageDecoder.h>
#include <fnord-tsdb/RecordSet.h>

namespace fnord {
namespace tsdb {

RecordSet::RecordSet(
    RefPtr<msg::MessageSchema> schema,
    const String& filename_prefix,
    RecordSetState state /* = RecordSetState{} */) :
    schema_(schema),
    filename_prefix_(filename_prefix),
    state_(state) {
  auto id_index_fn = [this] (uint64_t id, const void* data, size_t size) {
    commitlog_ids_.emplace(id);
  };

  for (const auto& o : state_.old_commitlogs) {
    loadCommitlog(o, id_index_fn);
  }

  if (!state.commitlog.isEmpty()) {
    loadCommitlog(state.commitlog.get(), id_index_fn);
  }
}

RecordSet::RecordSetState RecordSet::getState() const {
  std::unique_lock<std::mutex> lk(mutex_);
  return state_;
}

size_t RecordSet::commitlogSize() const {
  std::unique_lock<std::mutex> lk(mutex_);
  return commitlog_ids_.size();
}

void RecordSet::addRecord(uint64_t record_id, const Buffer& message) {
  util::BinaryMessageWriter buf;
  buf.appendUInt64(record_id);
  buf.appendVarUInt(message.size());
  buf.append(message.data(), message.size());

  std::unique_lock<std::mutex> lk(mutex_);

  if (commitlog_ids_.count(record_id) > 0) {
    return;
  }

  String commitlog;
  uint64_t commitlog_size;
  if (state_.commitlog.isEmpty()) {
    commitlog = filename_prefix_ + rnd_.hex64() + ".log";
    commitlog_size = 0;
  } else {
    commitlog = state_.commitlog.get();
    commitlog_size = state_.commitlog_size;
  }

  auto file = File::openFile(commitlog, File::O_WRITE | File::O_CREATEOROPEN);
  file.truncate(commitlog_size + buf.size());
  file.seekTo(commitlog_size);
  file.write(buf.data(), buf.size());

  state_.commitlog = Some(commitlog);
  state_.commitlog_size = commitlog_size + buf.size();
  commitlog_ids_.emplace(record_id);
}

void RecordSet::rollCommitlog() {
  std::unique_lock<std::mutex> lk(mutex_);
  if (state_.commitlog.isEmpty()) {
    return;
  }

  auto old_log = state_.commitlog.get();
  FileUtil::truncate(old_log, state_.commitlog_size);
  state_.old_commitlogs.emplace(old_log);
  state_.commitlog = None<String>();
  state_.commitlog_size = 0;
}

void RecordSet::compact() {
  std::unique_lock<std::mutex> compact_lk(compact_mutex_, std::defer_lock);
  if (!compact_lk.try_lock()) {
    return; // compaction is already running
  }

  std::unique_lock<std::mutex> lk(mutex_);
  auto snap = state_;
  lk.unlock();

  if (snap.old_commitlogs.size() == 0) {
    return;
  }

  cstable::CSTableBuilder outfile(schema_.get());
  cstable::UInt64ColumnWriter id_col(0, 0);

  Set<uint64_t> old_id_set;
  Set<uint64_t> new_id_set;

  if (!snap.datafile.isEmpty()) {
    cstable::CSTableReader reader(snap.datafile.get());
    cstable::RecordMaterializer record_reader(schema_, &reader);

    auto msgid_col_ref = reader.getColumnReader("__msgid");
    auto msgid_col = dynamic_cast<cstable::UInt64ColumnReader*>(msgid_col_ref.get());

    auto n = reader.numRecords();
    for (int i = 0; i < n; ++i) {
      uint64_t msgid;
      uint64_t r;
      uint64_t d;
      msgid_col->next(&r, &d, &msgid);
      old_id_set.emplace(msgid);

      msg::MessageObject record;
      record_reader.nextRecord(&record);

      outfile.addRecord(record);
      id_col.addDatum(0, 0, msgid);
    }
  }

  for (const auto& cl : snap.old_commitlogs) {
    loadCommitlog(cl, [this, &outfile, &old_id_set, &new_id_set, &id_col] (
        uint64_t id,
        const void* data,
        size_t size) {
      if (new_id_set.count(id) > 0) {
        return;
      }

      new_id_set.emplace(id);

      if (old_id_set.count(id) > 0) {
        return;
      }

      msg::MessageObject record;
      msg::MessageDecoder::decode(data, size, *schema_, &record);

      outfile.addRecord(record);
      id_col.addDatum(0, 0, id);
    });
  }

  auto outfile_path = filename_prefix_ + rnd_.hex64() + ".cst";
  cstable::CSTableWriter outfile_writer(
      outfile_path,
      outfile.numRecords());

  outfile.write(&outfile_writer);
  outfile_writer.addColumn("__msgid", &id_col);
  outfile_writer.commit();

  lk.lock();
  state_.datafile = Some(outfile_path);

  for (const auto& cl : snap.old_commitlogs) {
    state_.old_commitlogs.erase(cl);
  }

  for (const auto& id : new_id_set) {
    commitlog_ids_.erase(id);
  }
}

void RecordSet::loadCommitlog(
    const String& filename,
    Function<void (uint64_t, const void*, size_t)> fn) {
  io::MmappedFile mmap(File::openFile(filename, File::O_READ));
  util::BinaryMessageReader reader(mmap.data(), mmap.size());

  while (reader.remaining() > 0) {
    auto id = *reader.readUInt64();
    auto len = reader.readVarUInt();
    auto data = reader.read(len);

    fn(id, data, len);
  }
}

RecordSet::RecordSetState::RecordSetState() : commitlog_size(0) {}

} // namespace tsdb
} // namespace fnord

<commit_msg>neste record materialization...<commit_after>/**
 * This file is part of the "libfnord" project
 *   Copyright (c) 2015 Paul Asmuth
 *
 * FnordMetric is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License v3.0. You should have received a
 * copy of the GNU General Public License along with this program. If not, see
 * <http://www.gnu.org/licenses/>.
 */
#include <fnord-base/io/fileutil.h>
#include <fnord-base/io/mmappedfile.h>
#include <fnord-base/util/binarymessagereader.h>
#include <fnord-base/util/binarymessagewriter.h>
#include <fnord-cstable/CSTableBuilder.h>
#include <fnord-cstable/CSTableWriter.h>
#include <fnord-cstable/CSTableReader.h>
#include <fnord-cstable/UInt64ColumnWriter.h>
#include <fnord-cstable/UInt64ColumnReader.h>
#include <fnord-cstable/RecordMaterializer.h>
#include <fnord-msg/MessageDecoder.h>
#include <fnord-tsdb/RecordSet.h>

namespace fnord {
namespace tsdb {

RecordSet::RecordSet(
    RefPtr<msg::MessageSchema> schema,
    const String& filename_prefix,
    RecordSetState state /* = RecordSetState{} */) :
    schema_(schema),
    filename_prefix_(filename_prefix),
    state_(state) {
  auto id_index_fn = [this] (uint64_t id, const void* data, size_t size) {
    commitlog_ids_.emplace(id);
  };

  for (const auto& o : state_.old_commitlogs) {
    loadCommitlog(o, id_index_fn);
  }

  if (!state.commitlog.isEmpty()) {
    loadCommitlog(state.commitlog.get(), id_index_fn);
  }
}

RecordSet::RecordSetState RecordSet::getState() const {
  std::unique_lock<std::mutex> lk(mutex_);
  return state_;
}

size_t RecordSet::commitlogSize() const {
  std::unique_lock<std::mutex> lk(mutex_);
  return commitlog_ids_.size();
}

void RecordSet::addRecord(uint64_t record_id, const Buffer& message) {
  util::BinaryMessageWriter buf;
  buf.appendUInt64(record_id);
  buf.appendVarUInt(message.size());
  buf.append(message.data(), message.size());

  std::unique_lock<std::mutex> lk(mutex_);

  if (commitlog_ids_.count(record_id) > 0) {
    return;
  }

  String commitlog;
  uint64_t commitlog_size;
  if (state_.commitlog.isEmpty()) {
    commitlog = filename_prefix_ + rnd_.hex64() + ".log";
    commitlog_size = 0;
  } else {
    commitlog = state_.commitlog.get();
    commitlog_size = state_.commitlog_size;
  }

  auto file = File::openFile(commitlog, File::O_WRITE | File::O_CREATEOROPEN);
  file.truncate(commitlog_size + buf.size());
  file.seekTo(commitlog_size);
  file.write(buf.data(), buf.size());

  state_.commitlog = Some(commitlog);
  state_.commitlog_size = commitlog_size + buf.size();
  commitlog_ids_.emplace(record_id);
}

void RecordSet::rollCommitlog() {
  std::unique_lock<std::mutex> lk(mutex_);
  if (state_.commitlog.isEmpty()) {
    return;
  }

  auto old_log = state_.commitlog.get();
  FileUtil::truncate(old_log, state_.commitlog_size);
  state_.old_commitlogs.emplace(old_log);
  state_.commitlog = None<String>();
  state_.commitlog_size = 0;
}

void RecordSet::compact() {
  std::unique_lock<std::mutex> compact_lk(compact_mutex_, std::defer_lock);
  if (!compact_lk.try_lock()) {
    return; // compaction is already running
  }

  std::unique_lock<std::mutex> lk(mutex_);
  auto snap = state_;
  lk.unlock();

  if (snap.old_commitlogs.size() == 0) {
    return;
  }

  cstable::CSTableBuilder outfile(schema_.get());
  cstable::UInt64ColumnWriter id_col(0, 0);

  Set<uint64_t> old_id_set;
  Set<uint64_t> new_id_set;

  if (!snap.datafile.isEmpty()) {
    cstable::CSTableReader reader(snap.datafile.get());
    cstable::RecordMaterializer record_reader(schema_.get(), &reader);

    auto msgid_col_ref = reader.getColumnReader("__msgid");
    auto msgid_col = dynamic_cast<cstable::UInt64ColumnReader*>(msgid_col_ref.get());

    auto n = reader.numRecords();
    for (int i = 0; i < n; ++i) {
      uint64_t msgid;
      uint64_t r;
      uint64_t d;
      msgid_col->next(&r, &d, &msgid);
      old_id_set.emplace(msgid);

      msg::MessageObject record;
      record_reader.nextRecord(&record);

      outfile.addRecord(record);
      id_col.addDatum(0, 0, msgid);
    }
  }

  for (const auto& cl : snap.old_commitlogs) {
    loadCommitlog(cl, [this, &outfile, &old_id_set, &new_id_set, &id_col] (
        uint64_t id,
        const void* data,
        size_t size) {
      if (new_id_set.count(id) > 0) {
        return;
      }

      new_id_set.emplace(id);

      if (old_id_set.count(id) > 0) {
        return;
      }

      msg::MessageObject record;
      msg::MessageDecoder::decode(data, size, *schema_, &record);

      outfile.addRecord(record);
      id_col.addDatum(0, 0, id);
    });
  }

  auto outfile_path = filename_prefix_ + rnd_.hex64() + ".cst";
  cstable::CSTableWriter outfile_writer(
      outfile_path,
      outfile.numRecords());

  outfile.write(&outfile_writer);
  outfile_writer.addColumn("__msgid", &id_col);
  outfile_writer.commit();

  lk.lock();
  state_.datafile = Some(outfile_path);

  for (const auto& cl : snap.old_commitlogs) {
    state_.old_commitlogs.erase(cl);
  }

  for (const auto& id : new_id_set) {
    commitlog_ids_.erase(id);
  }
}

void RecordSet::loadCommitlog(
    const String& filename,
    Function<void (uint64_t, const void*, size_t)> fn) {
  io::MmappedFile mmap(File::openFile(filename, File::O_READ));
  util::BinaryMessageReader reader(mmap.data(), mmap.size());

  while (reader.remaining() > 0) {
    auto id = *reader.readUInt64();
    auto len = reader.readVarUInt();
    auto data = reader.read(len);

    fn(id, data, len);
  }
}

RecordSet::RecordSetState::RecordSetState() : commitlog_size(0) {}

} // namespace tsdb
} // namespace fnord

<|endoftext|>


<commit_before>/* This file is part of https://github.com/borneq/IniParser
The MIT License (MIT), see file LICENSE */
#include "StrTools.h"

//http://stackoverflow.com/questions/1798112/removing-leading-and-trailing-spaces-from-a-string
string StrTools::trim(const string& str)
{
	const string whitespace = " \t";
	const auto strBegin = str.find_first_not_of(whitespace);
	if (strBegin == string::npos)
		return ""; // no content

	const auto strEnd = str.find_last_not_of(whitespace);
	const auto strRange = strEnd - strBegin + 1;

	return str.substr(strBegin, strRange);
}

string StrTools::trimLeft(const string& str)
{
	const auto strBegin = str.find_first_not_of(" \t");
	if (strBegin == string::npos)
		return ""; // no content
	return str.substr(strBegin, str.length() - strBegin);
}

string StrTools::trimRight(const string& str)
{
	const auto strEnd = str.find_last_not_of(" \t");
	if (strEnd == string::npos)
		return ""; // no content
	return str.substr(0, strEnd + 1);
}
<commit_msg>trim \r at line end because ini can have Windows endings and prog is on Linux<commit_after>/* This file is part of https://github.com/borneq/IniParser
The MIT License (MIT), see file LICENSE */
#include "StrTools.h"

//http://stackoverflow.com/questions/1798112/removing-leading-and-trailing-spaces-from-a-string
string StrTools::trim(const string& str)
{
	const string whitespace = " \t\r";
	const auto strBegin = str.find_first_not_of(whitespace);
	if (strBegin == string::npos)
		return ""; // no content

	const auto strEnd = str.find_last_not_of(whitespace);
	const auto strRange = strEnd - strBegin + 1;

	return str.substr(strBegin, strRange);
}

string StrTools::trimLeft(const string& str)
{
	const auto strBegin = str.find_first_not_of(" \t");
	if (strBegin == string::npos)
		return ""; // no content
	return str.substr(strBegin, str.length() - strBegin);
}

string StrTools::trimRight(const string& str)
{
	const auto strEnd = str.find_last_not_of(" \t\r");
	if (strEnd == string::npos)
		return ""; // no content
	return str.substr(0, strEnd + 1);
}
<|endoftext|>


<commit_before>//
// example macro for reconstruction of the TPC raw data
//
// The path to the Calibration parameters is for the moment hard-wired in the code
// Taken from /afs/
//
//

void recTPC(Int_t type, const char *filename="data.root")
{
  //
  // Set path to calibration data
  //
  // type variable = 0 - cosmic test
  //               = 1 - laser test
  AliCDBManager * man = AliCDBManager::Instance();
  man->SetDefaultStorage("local://$ALICE_ROOT");
  man->SetRun(0);
  man->SetSpecificStorage("TPC","local:///afs/cern.ch/user/m/mivanov/public/Calib");
  //
  // Set reconstruction parameters
  //
  AliLog::SetClassDebugLevel("AliTPCclustererMI",2);
  AliTPCRecoParam * tpcRecoParam = 0;
  if (type==0)  tpcRecoParam = AliTPCRecoParam::GetCosmicTestParam(kTRUE);
  if (type>0)  tpcRecoParam = AliTPCRecoParam::GetLaserTestParam(kTRUE);
  tpcRecoParam->Dump();
  AliTPCReconstructor::SetRecoParam(tpcRecoParam);
  AliTPCReconstructor::SetStreamLevel(1);
  //
  //
  //
  AliReconstruction rec;
  rec.SetSpecificStorage("TPC","local:///afs/cern.ch/user/m/mivanov/public/Calib");
  rec.SetLoadAlignData("");
  rec.SetWriteESDfriend(kTRUE);
  rec.SetInput(filename);
  rec.SetEquipmentIdMap("EquipmentIdMap.data");
  rec.SetRunReconstruction("TPC");
  rec.SetOption("TPC","PedestalSubtraction OldRCUFormat");
  //  rec.SetRunLocalReconstruction("");
  //  rec.SetRunTracking("TPC");
  rec.SetFillESD("TPC");
  rec.SetFillTriggerESD(kFALSE);
  rec.SetRunVertexFinder(kFALSE);
  AliMagFMaps* field = new AliMagFMaps("Maps","Maps", 2, 1., 10., 1);
  AliTracker::SetFieldMap(field,1);
  rec.SetWriteAlignmentData(kTRUE);
  rec.Run();
}

void recTracking(Int_t type, const char *filename="data.root", Int_t nevents=1)
{
  //
  // Set path to calibration data
  //
  AliCDBManager * man = AliCDBManager::Instance();
  man->SetDefaultStorage("local://$ALICE_ROOT");
  man->SetRun(0);
  man->SetSpecificStorage("TPC","local:///afs/cern.ch/user/m/mivanov/public/Calib");
  //
  // Set reconstruction parameters
  //
  AliLog::SetClassDebugLevel("AliTPCclustererMI",2);

  AliTPCRecoParam * tpcRecoParam = 0;
  if (type==0)  tpcRecoParam = AliTPCRecoParam::GetCosmicTestParam(kTRUE);
  if (type>0)  tpcRecoParam = AliTPCRecoParam::GetLaserTestParam(kTRUE);

  AliTPCReconstructor::SetRecoParam(tpcRecoParam);
  AliTPCReconstructor::SetStreamLevel(1);

  //
  //
  //
  AliReconstruction rec;
  //rec.SetSpecificStorage("TPC","local:///afs/cern.ch/user/m/mivanov/public/Calib");
  rec.SetLoadAlignData("");
  rec.SetWriteESDfriend(kTRUE);
  rec.SetInput(filename);
  rec.SetEquipmentIdMap("EquipmentIdMap.data");
  //rec.SetRunReconstruction("TPC");
  rec.SetOption("TPC","PedestalSubtraction OldRCUFormat");
  rec.SetRunLocalReconstruction("");
  rec.SetRunTracking("TPC");
  rec.SetFillESD("TPC");
  rec.SetFillTriggerESD(kFALSE);
  rec.SetRunVertexFinder(kFALSE);
  AliMagFMaps* field = new AliMagFMaps("Maps","Maps", 2, 1., 10., 1);
  AliTracker::SetFieldMap(field,1);
  rec.SetWriteAlignmentData(kTRUE);
  rec.Run(0,nevents);
}

<commit_msg>Changes due change of the CalibDB interface (Marian)<commit_after>//
// example macro for reconstruction of the TPC raw data
//
// The path to the Calibration parameters is for the moment hard-wired in the code
// Taken from /afs/
//
//

void recTPC(Int_t type, const char *filename="data.root")
{
  //
  // Set path to calibration data
  //
  // type variable = 0 - cosmic test
  //               = 1 - laser test
  AliCDBManager * man = AliCDBManager::Instance();
  man->SetDefaultStorage("local://$ALICE_ROOT");
  man->SetRun(0);
  man->SetSpecificStorage("TPC/*/*","local:///afs/cern.ch/user/m/mivanov/public/Calib");
  //
  // Set reconstruction parameters
  //
  AliLog::SetClassDebugLevel("AliTPCclustererMI",2);
  AliTPCRecoParam * tpcRecoParam = 0;
  if (type==0)  tpcRecoParam = AliTPCRecoParam::GetCosmicTestParam(kTRUE);
  if (type>0)  tpcRecoParam = AliTPCRecoParam::GetLaserTestParam(kTRUE);
  tpcRecoParam->Dump();
  AliTPCReconstructor::SetRecoParam(tpcRecoParam);
  AliTPCReconstructor::SetStreamLevel(1);
  //
  //
  //
  AliReconstruction rec;
  rec.SetDefaultStorage("local://$ALICE_ROOT");
  rec.SetSpecificStorage("TPC/*/*","local:///afs/cern.ch/user/m/mivanov/public/Calib");
  rec.SetLoadAlignData("");
  rec.SetWriteESDfriend(kTRUE);
  rec.SetInput(filename);
  rec.SetEquipmentIdMap("EquipmentIdMap.data");
  rec.SetRunReconstruction("TPC");
  rec.SetOption("TPC","PedestalSubtraction OldRCUFormat");
  //  rec.SetRunLocalReconstruction("");
  //  rec.SetRunTracking("TPC");
  rec.SetFillESD("TPC");
  rec.SetFillTriggerESD(kFALSE);
  rec.SetRunVertexFinder(kFALSE);
  AliMagFMaps* field = new AliMagFMaps("Maps","Maps", 2, 1., 10., 1);
  AliTracker::SetFieldMap(field,1);
  rec.SetWriteAlignmentData(kTRUE);
  rec.Run();
}

void recTracking(Int_t type, const char *filename="data.root", Int_t nevents=1)
{
  //
  // Set path to calibration data
  //
  AliCDBManager * man = AliCDBManager::Instance();
  man->SetDefaultStorage("local://$ALICE_ROOT");
  man->SetRun(0);
  man->SetSpecificStorage("TPC/*/*","local:///afs/cern.ch/user/m/mivanov/public/Calib");
  //
  // Set reconstruction parameters
  //
  AliLog::SetClassDebugLevel("AliTPCclustererMI",2);

  AliTPCRecoParam * tpcRecoParam = 0;
  if (type==0)  tpcRecoParam = AliTPCRecoParam::GetCosmicTestParam(kTRUE);
  if (type>0)  tpcRecoParam = AliTPCRecoParam::GetLaserTestParam(kTRUE);

  AliTPCReconstructor::SetRecoParam(tpcRecoParam);
  AliTPCReconstructor::SetStreamLevel(1);

  //
  //
  //
  AliReconstruction rec;
  rec.SetSpecificStorage("TPC/*/*","local:///afs/cern.ch/user/m/mivanov/public/Calib");
  rec.SetLoadAlignData("");
  rec.SetWriteESDfriend(kTRUE);
  rec.SetInput(filename);
  rec.SetEquipmentIdMap("EquipmentIdMap.data");
  //rec.SetRunReconstruction("TPC");
  rec.SetOption("TPC","PedestalSubtraction OldRCUFormat");
  rec.SetRunLocalReconstruction("");
  rec.SetRunTracking("TPC");
  rec.SetFillESD("TPC");
  rec.SetFillTriggerESD(kFALSE);
  rec.SetRunVertexFinder(kFALSE);
  AliMagFMaps* field = new AliMagFMaps("Maps","Maps", 2, 1., 10., 1);
  AliTracker::SetFieldMap(field,1);
  rec.SetWriteAlignmentData(kTRUE);
  rec.Run(0,nevents);
}

<|endoftext|>


<commit_before>/* Copyright 2017 The TensorFlow Authors. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/

#include "jvm_callback_op.h"

#include "exception.h"
#include "utilities.h"

#include "third_party/eigen3/unsupported/Eigen/CXX11/Tensor"
#include "tensorflow/c/c_api.h"
#include "tensorflow/c/c_api_internal.h"
#include "tensorflow/c/eager/c_api.h"
#include "tensorflow/c/eager/c_api_internal.h"
#include "tensorflow/core/common_runtime/eager/tensor_handle.h"
#include "tensorflow/core/framework/op.h"
#include "tensorflow/core/framework/op_kernel.h"
#include "tensorflow/core/framework/kernel_def.pb_text.h"
#include "tensorflow/core/framework/common_shape_fns.h"
#include "tensorflow/core/lib/core/coding.h"
#include "tensorflow/core/lib/core/errors.h"
#include "tensorflow/core/platform/macros.h"
#include "tensorflow/core/platform/mutex.h"

namespace tensorflow {
REGISTER_OP("JVMCallback")
    .Input("input: Tin")
    .Output("output: Tout")
    .Attr("id: int")
    .Attr("jvm_pointer: string")
    .Attr("registry_pointer: string")
    .Attr("Tin: list(type) >= 0")
    .Attr("Tout: list(type) >=0")
    .SetIsStateful()
    .SetShapeFn(shape_inference::UnknownShape)
    .Doc(R"doc(
Invokes a JVM callback function, `f` to compute `f(input)->output`.

This operation is considered stateful. For a stateless version, see
`JVMCallback`.

id: A unique ID representing a registered JVM callback function
  in this address space.
jvm_pointer: A pointer to an existing JVM instance represented as a
  string. This is the JVM that will be used when invoking this JVM
  callback.
registry_pointer: Pointer to the JVM callbacks registry class.
input: List of tensors that will provide input to the op.
output: Output tensors from the op.
Tin: Data types of the inputs to the op.
Tout: Data types of the outputs from the op.
      The length of the list specifies the number of outputs.
)doc");

REGISTER_OP("JVMCallbackStateless")
    .Input("input: Tin")
    .Output("output: Tout")
    .Attr("id: int")
    .Attr("jvm_pointer: string")
    .Attr("registry_pointer: string")
    .Attr("Tin: list(type) >= 0")
    .Attr("Tout: list(type) >= 0")
    .SetShapeFn(shape_inference::UnknownShape)
    .Doc(R"doc(
A stateless version of `JVMCallback`.
)doc");

namespace {
  // Given the 'call', prepares the inputs as a JNI long array that is appropriate for calling the registry.
  jlongArray MakeInputs(JVMCall* call) {
    unsigned long n = call->inputs.size();
    jlongArray inputs = call->env->NewLongArray(static_cast<jsize>(n));
    jlong* inputs_array = call->env->GetLongArrayElements(inputs, nullptr);
    for (int64 i = 0; i < n; ++i) {
      const Tensor& t = call->inputs[i];
      TFE_TensorHandle* tensor;
//      if (call->gpu) {
//        // TODO: Obtain the device from the tensor itself, rather than from the call.
//        tensor = new TFE_TensorHandle(t, call->device, call->device);
//      } else {
//        tensor = new TFE_TensorHandle(t, nullptr, nullptr);
//      }
      tensor = new TFE_TensorHandle(t, nullptr, nullptr);
      inputs_array[i] = reinterpret_cast<jlong>(tensor);
    }
    call->env->ReleaseLongArrayElements(inputs, inputs_array, 0);
    return inputs;
  }

  // Process the return values by converting them back to TensorFlow tensors and adding them to the call outputs.
  void ProcessOutputs(JVMCall* call, jlongArray call_outputs, TF_Status* status) {
    call->outputs.clear();
    jsize n = call->env->GetArrayLength(call_outputs);
    jlong* outputs_array = call->env->GetLongArrayElements(call_outputs, nullptr);
    for (int i = 0; i < n; ++i) {
      static_assert(sizeof(jlong) >= sizeof(TFE_TensorHandle*), "Cannot package C object pointers as a Java long");
      if (outputs_array[i] == 0) {
        status->status = errors::InvalidArgument("One of the op output tensors has been disposed already.");
        return;
      }
      auto* h = reinterpret_cast<TFE_TensorHandle*>(outputs_array[i]);
      if (h == nullptr) {
        status->status = errors::InvalidArgument("Could not obtain tensor handle to one of the outputs.");
        return;
      }
      if (!status->status.ok()) return;
      const tensorflow::Tensor* t = nullptr;
      status->status = h->handle->Tensor(&t);
      call->outputs.push_back(*t);
    }
    call->env->ReleaseLongArrayElements(call_outputs, outputs_array, 0);
  }

  // Calls the registered JVM function through the registry.
  Status CallJVMFunction(JVMCall* call) {
    // Prepare the call arguments.
    jlongArray call_inputs = MakeInputs(call);

    // Invoke the registry 'call' method.
    if (call->registry != nullptr && call->call_method_id != nullptr) {
      auto outputs = (jlongArray) call->env->CallStaticObjectMethod(
          call->registry, call->call_method_id, call->id, call_inputs);
      jthrowable exc(call->env->ExceptionOccurred());
      call->env->ExceptionClear();
      if (exc) {
        // Get the exception string representation to use as the error message.
        jclass throwableCls(call->env->FindClass("java/lang/Throwable"));
        jmethodID toString = call->env->GetMethodID(throwableCls, "toString", "()Ljava/lang/String;");
        jstring exc_string = (jstring) call->env->CallObjectMethod(exc, toString);
        const char* c_exc_string = call->env->GetStringUTFChars(exc_string, 0);
        tensorflow::StringPiece tf_exc_string(c_exc_string);
        call->env->ReleaseStringUTFChars(exc_string, c_exc_string);

        // Get the exception class name and convert it to a TensorFlow error code.
        jclass excObjCls(call->env->GetObjectClass(exc));
        jclass classCls(call->env->FindClass("java/lang/Class"));
        jmethodID getName(call->env->GetMethodID(classCls, "getName", "()Ljava/lang/String;"));
        jstring clsName(static_cast<jstring>(call->env->CallObjectMethod(excObjCls, getName)));
        const char* clsNameCString = call->env->GetStringUTFChars(clsName, 0);
        std::string clsNameCppString(clsNameCString);
        int error_code = tf_error_code(clsNameCppString);
        call->env->ReleaseStringUTFChars(clsName, clsNameCString);
        return tensorflow::Status((tensorflow::error::Code) error_code, tf_exc_string);
      }

      if (outputs == nullptr) {
        return errors::Unknown("Failed to run JVM callback function.");
      }

      // Process the return values and convert them back to TensorFlow tensors.
      auto* status = new TF_Status;
      ProcessOutputs(call, outputs, status);
      return status->status;
    } else {
      return errors::Unknown("Failed to run JVM callback function. Could not find registry class or its 'call' method.");
    }
  }

  struct JVMWrapper {
    JavaVM* jvm_;
    mutex lock;

	  JVMWrapper(JavaVM* jvm_) : jvm_(jvm_) { }
  };

  static std::vector<JVMWrapper*> jvms;

  struct JVMThreadHelper {
    JNIEnv* env;
	  JavaVM* jvm_;

    JVMThreadHelper() : jvm_(nullptr) { }

    ~JVMThreadHelper() {
      if (jvm_ != nullptr)
        jvm_->DetachCurrentThread();
    }

    void set_jvm(JavaVM* jvm) {
      if (jvm_ != nullptr) {
        if (jvm_ != jvm)
          throw "Multiple JVMs detected per thread.";
	       return;
      }
      jvm_ = jvm;
      int jvmEnvStatus = jvm_->GetEnv((void**) &env, JNI_VERSION_1_6);
      if (jvmEnvStatus == JNI_EDETACHED)
        jvm_->AttachCurrentThread((void**) &env, nullptr);
	  }
  };

  JVMWrapper& get_jvm_wrapper(JavaVM* jvm_) {
    for (JVMWrapper* wrapper : jvms)
      if (wrapper->jvm_ == jvm_)
        return *wrapper;

    /* the JVM isn't in the array */
    jvms.push_back(new JVMWrapper(jvm_));
    return **jvms.rbegin();
  }

  thread_local JVMThreadHelper jvm_thread;
}  // namespace

class JVMCallbackOp : public OpKernel {

public:
  explicit JVMCallbackOp(OpKernelConstruction* ctx) : OpKernel(ctx) {
    OP_REQUIRES_OK(ctx, ctx->GetAttr("id", &id_));
    std::string jvm_pointer;
    OP_REQUIRES_OK(ctx, ctx->GetAttr("jvm_pointer", &jvm_pointer));
    jvm_ = pointerFromString<JavaVM*>(jvm_pointer);
	  mutex_lock l(get_jvm_wrapper(jvm_).lock);
    jvm_thread.set_jvm(jvm_);
    JNIEnv* env = jvm_thread.env;
    std::string registry_pointer;
    OP_REQUIRES_OK(ctx, ctx->GetAttr("registry_pointer", &registry_pointer));
    registry_ = pointerFromString<jclass>(registry_pointer);
    call_method_id_ = env->GetStaticMethodID(registry_, "call", "(I[J)[J");
    gpu_ = ctx->device_type().type_string() == DEVICE_GPU;
  }

  void Compute(OpKernelContext* ctx) override {
	  mutex_lock l(get_jvm_wrapper(jvm_).lock);
    jvm_thread.set_jvm(jvm_);
    JNIEnv* env = jvm_thread.env;

    JVMCall call;
    call.env = env;
    call.registry = registry_;
    call.call_method_id = call_method_id_;
    call.device = dynamic_cast<Device*>(ctx->device());
    call.gpu = gpu_;
    call.id = id_;
    for (int i = 0; i < ctx->num_inputs(); ++i) {
      call.inputs.push_back(ctx->input(i));
    }

    Status s = CallJVMFunction(&call);

    OP_REQUIRES_OK(ctx, s);

    OP_REQUIRES(ctx, static_cast<int32>(call.outputs.size()) == ctx->num_outputs(),
                errors::InvalidArgument(id_, " returns ", call.outputs.size(),
                                        " values, but expects to see ",
                                        ctx->num_outputs(), " values."));
    for (size_t i = 0; i < call.outputs.size(); ++i) {
      const auto& t = call.outputs[i];
      OP_REQUIRES(
          ctx, t.dtype() == output_type(i),
          errors::InvalidArgument(i, "-th value returned by ", id_, " is ",
                                  DataTypeString(t.dtype()), ", but expects ",
                                  DataTypeString(output_type(i))));
      ctx->set_output(i, t);
    }
  }

private:
  int id_;
  JavaVM* jvm_;
  jclass registry_;
  jmethodID call_method_id_;

  // True if and only if this op has been placed on a GPU.
  bool gpu_;

  TF_DISALLOW_COPY_AND_ASSIGN(JVMCallbackOp);
};

namespace kernel_factory {
struct KernelRegistration {
  KernelRegistration(const KernelDef& d, StringPiece c,
                     kernel_factory::OpKernelRegistrar::Factory f)
      : def(d), kernel_class_name(c.ToString()), factory(f) {}
  const KernelDef def;
  const string kernel_class_name;
  const kernel_factory::OpKernelRegistrar::Factory factory;
};

auto jvmCallbackOpInitializer = []{
  auto* reg = reinterpret_cast<std::unordered_multimap<string, KernelRegistration>*>(GlobalKernelRegistry());
  if (reg->find(strings::StrCat("JVMCallback:", DeviceTypeString(DEVICE_CPU), ":")) == reg->end()) {
    REGISTER_KERNEL_BUILDER(Name("JVMCallback").Device(DEVICE_CPU), JVMCallbackOp);
    REGISTER_KERNEL_BUILDER(Name("JVMCallbackStateless").Device(DEVICE_CPU), JVMCallbackOp);
  }
  if (reg->find(strings::StrCat("JVMCallback:", DeviceTypeString(DEVICE_GPU), ":")) == reg->end()) {
    REGISTER_KERNEL_BUILDER(Name("JVMCallback").Device(DEVICE_GPU), JVMCallbackOp);
    REGISTER_KERNEL_BUILDER(Name("JVMCallbackStateless").Device(DEVICE_GPU), JVMCallbackOp);
  }
  return 0;
}();
}
}  // namespace tensorflow
<commit_msg>[JNI] Temporary bug fix.<commit_after>/* Copyright 2017 The TensorFlow Authors. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/

#include "jvm_callback_op.h"

#include "exception.h"
#include "utilities.h"

#include "third_party/eigen3/unsupported/Eigen/CXX11/Tensor"
#include "tensorflow/c/c_api.h"
#include "tensorflow/c/c_api_internal.h"
#include "tensorflow/c/eager/c_api.h"
#include "tensorflow/c/eager/c_api_internal.h"
#include "tensorflow/core/common_runtime/eager/tensor_handle.h"
#include "tensorflow/core/framework/op.h"
#include "tensorflow/core/framework/op_kernel.h"
#include "tensorflow/core/framework/kernel_def.pb_text.h"
#include "tensorflow/core/framework/common_shape_fns.h"
#include "tensorflow/core/lib/core/coding.h"
#include "tensorflow/core/lib/core/errors.h"
#include "tensorflow/core/platform/macros.h"
#include "tensorflow/core/platform/mutex.h"

namespace tensorflow {
REGISTER_OP("JVMCallback")
    .Input("input: Tin")
    .Output("output: Tout")
    .Attr("id: int")
    .Attr("jvm_pointer: string")
    .Attr("registry_pointer: string")
    .Attr("Tin: list(type) >= 0")
    .Attr("Tout: list(type) >=0")
    .SetIsStateful()
    .SetShapeFn(shape_inference::UnknownShape)
    .Doc(R"doc(
Invokes a JVM callback function, `f` to compute `f(input)->output`.

This operation is considered stateful. For a stateless version, see
`JVMCallback`.

id: A unique ID representing a registered JVM callback function
  in this address space.
jvm_pointer: A pointer to an existing JVM instance represented as a
  string. This is the JVM that will be used when invoking this JVM
  callback.
registry_pointer: Pointer to the JVM callbacks registry class.
input: List of tensors that will provide input to the op.
output: Output tensors from the op.
Tin: Data types of the inputs to the op.
Tout: Data types of the outputs from the op.
      The length of the list specifies the number of outputs.
)doc");

REGISTER_OP("JVMCallbackStateless")
    .Input("input: Tin")
    .Output("output: Tout")
    .Attr("id: int")
    .Attr("jvm_pointer: string")
    .Attr("registry_pointer: string")
    .Attr("Tin: list(type) >= 0")
    .Attr("Tout: list(type) >= 0")
    .SetShapeFn(shape_inference::UnknownShape)
    .Doc(R"doc(
A stateless version of `JVMCallback`.
)doc");

namespace {
  // Given the 'call', prepares the inputs as a JNI long array that is appropriate for calling the registry.
  jlongArray MakeInputs(JVMCall* call) {
    unsigned long n = call->inputs.size();
    jlongArray inputs = call->env->NewLongArray(static_cast<jsize>(n));
    jlong* inputs_array = call->env->GetLongArrayElements(inputs, nullptr);
    for (int64 i = 0; i < n; ++i) {
      const Tensor& t = call->inputs[i];
      TFE_TensorHandle* tensor;
//      if (call->gpu) {
//        // TODO: !!! [CALLBACK] Obtain the device from the tensor itself, rather than from the call.
//        tensor = new TFE_TensorHandle(t, call->device, call->device);
//      } else {
//        tensor = new TFE_TensorHandle(t, nullptr, nullptr);
//      }
      tensor = new TFE_TensorHandle(t, nullptr, nullptr);
      inputs_array[i] = reinterpret_cast<jlong>(tensor);
    }
    call->env->ReleaseLongArrayElements(inputs, inputs_array, 0);
    return inputs;
  }

  // Process the return values by converting them back to TensorFlow tensors and adding them to the call outputs.
  void ProcessOutputs(JVMCall* call, jlongArray call_outputs, TF_Status* status) {
    call->outputs.clear();
    jsize n = call->env->GetArrayLength(call_outputs);
    jlong* outputs_array = call->env->GetLongArrayElements(call_outputs, nullptr);
    for (int i = 0; i < n; ++i) {
      static_assert(sizeof(jlong) >= sizeof(TFE_TensorHandle*), "Cannot package C object pointers as a Java long");
      if (outputs_array[i] == 0) {
        status->status = errors::InvalidArgument("One of the op output tensors has been disposed already.");
        return;
      }
      auto* h = reinterpret_cast<TFE_TensorHandle*>(outputs_array[i]);
      if (h == nullptr) {
        status->status = errors::InvalidArgument("Could not obtain tensor handle to one of the outputs.");
        return;
      }
      if (!status->status.ok()) return;
      const tensorflow::Tensor* t = nullptr;
      status->status = h->handle->Tensor(&t);
      call->outputs.push_back(*t);
    }
    call->env->ReleaseLongArrayElements(call_outputs, outputs_array, 0);
  }

  // Calls the registered JVM function through the registry.
  Status CallJVMFunction(JVMCall* call) {
    // Prepare the call arguments.
    jlongArray call_inputs = MakeInputs(call);

    // Invoke the registry 'call' method.
    if (call->registry != nullptr && call->call_method_id != nullptr) {
      auto outputs = (jlongArray) call->env->CallStaticObjectMethod(
          call->registry, call->call_method_id, call->id, call_inputs);
      jthrowable exc(call->env->ExceptionOccurred());
      call->env->ExceptionClear();
      if (exc) {
        // Get the exception string representation to use as the error message.
        jclass throwableCls(call->env->FindClass("java/lang/Throwable"));
        jmethodID toString = call->env->GetMethodID(throwableCls, "toString", "()Ljava/lang/String;");
        jstring exc_string = (jstring) call->env->CallObjectMethod(exc, toString);
        const char* c_exc_string = call->env->GetStringUTFChars(exc_string, 0);
        tensorflow::StringPiece tf_exc_string(c_exc_string);
        call->env->ReleaseStringUTFChars(exc_string, c_exc_string);

        // Get the exception class name and convert it to a TensorFlow error code.
        jclass excObjCls(call->env->GetObjectClass(exc));
        jclass classCls(call->env->FindClass("java/lang/Class"));
        jmethodID getName(call->env->GetMethodID(classCls, "getName", "()Ljava/lang/String;"));
        jstring clsName(static_cast<jstring>(call->env->CallObjectMethod(excObjCls, getName)));
        const char* clsNameCString = call->env->GetStringUTFChars(clsName, 0);
        std::string clsNameCppString(clsNameCString);
        int error_code = tf_error_code(clsNameCppString);
        call->env->ReleaseStringUTFChars(clsName, clsNameCString);
        return tensorflow::Status((tensorflow::error::Code) error_code, tf_exc_string);
      }

      if (outputs == nullptr) {
        return errors::Unknown("Failed to run JVM callback function.");
      }

      // Process the return values and convert them back to TensorFlow tensors.
      auto* status = new TF_Status;
      ProcessOutputs(call, outputs, status);
      return status->status;
    } else {
      return errors::Unknown("Failed to run JVM callback function. Could not find registry class or its 'call' method.");
    }
  }

  struct JVMWrapper {
    JavaVM* jvm_;
    mutex lock;

	  JVMWrapper(JavaVM* jvm_) : jvm_(jvm_) { }
  };

  static std::vector<JVMWrapper*> jvms;

  struct JVMThreadHelper {
    JNIEnv* env;
	  JavaVM* jvm_;

    JVMThreadHelper() : jvm_(nullptr) { }

    ~JVMThreadHelper() {
      if (jvm_ != nullptr)
        jvm_->DetachCurrentThread();
    }

    void set_jvm(JavaVM* jvm) {
      if (jvm_ != nullptr) {
        if (jvm_ != jvm)
          throw "Multiple JVMs detected per thread.";
	       return;
      }
      jvm_ = jvm;
      int jvmEnvStatus = jvm_->GetEnv((void**) &env, JNI_VERSION_1_6);
      if (jvmEnvStatus == JNI_EDETACHED)
        jvm_->AttachCurrentThread((void**) &env, nullptr);
	  }
  };

  JVMWrapper& get_jvm_wrapper(JavaVM* jvm_) {
    for (JVMWrapper* wrapper : jvms)
      if (wrapper->jvm_ == jvm_)
        return *wrapper;

    /* the JVM isn't in the array */
    jvms.push_back(new JVMWrapper(jvm_));
    return **jvms.rbegin();
  }

  thread_local JVMThreadHelper jvm_thread;
}  // namespace

class JVMCallbackOp : public OpKernel {

public:
  explicit JVMCallbackOp(OpKernelConstruction* ctx) : OpKernel(ctx) {
    OP_REQUIRES_OK(ctx, ctx->GetAttr("id", &id_));
    std::string jvm_pointer;
    OP_REQUIRES_OK(ctx, ctx->GetAttr("jvm_pointer", &jvm_pointer));
    jvm_ = pointerFromString<JavaVM*>(jvm_pointer);
	  mutex_lock l(get_jvm_wrapper(jvm_).lock);
    jvm_thread.set_jvm(jvm_);
    JNIEnv* env = jvm_thread.env;
    std::string registry_pointer;
    OP_REQUIRES_OK(ctx, ctx->GetAttr("registry_pointer", &registry_pointer));
    registry_ = pointerFromString<jclass>(registry_pointer);
    call_method_id_ = env->GetStaticMethodID(registry_, "call", "(I[J)[J");
    gpu_ = ctx->device_type().type_string() == DEVICE_GPU;
  }

  void Compute(OpKernelContext* ctx) override {
	  mutex_lock l(get_jvm_wrapper(jvm_).lock);
    jvm_thread.set_jvm(jvm_);
    JNIEnv* env = jvm_thread.env;

    JVMCall call;
    call.env = env;
    call.registry = registry_;
    call.call_method_id = call_method_id_;
    call.device = dynamic_cast<Device*>(ctx->device());
    call.gpu = gpu_;
    call.id = id_;
    for (int i = 0; i < ctx->num_inputs(); ++i) {
      call.inputs.push_back(ctx->input(i));
    }

    Status s = CallJVMFunction(&call);

    OP_REQUIRES_OK(ctx, s);

    OP_REQUIRES(ctx, static_cast<int32>(call.outputs.size()) == ctx->num_outputs(),
                errors::InvalidArgument(id_, " returns ", call.outputs.size(),
                                        " values, but expects to see ",
                                        ctx->num_outputs(), " values."));
    for (size_t i = 0; i < call.outputs.size(); ++i) {
      const auto& t = call.outputs[i];
      OP_REQUIRES(
          ctx, t.dtype() == output_type(i),
          errors::InvalidArgument(i, "-th value returned by ", id_, " is ",
                                  DataTypeString(t.dtype()), ", but expects ",
                                  DataTypeString(output_type(i))));
      ctx->set_output(i, t);
    }
  }

private:
  int id_;
  JavaVM* jvm_;
  jclass registry_;
  jmethodID call_method_id_;

  // True if and only if this op has been placed on a GPU.
  bool gpu_;

  TF_DISALLOW_COPY_AND_ASSIGN(JVMCallbackOp);
};

namespace kernel_factory {
struct KernelRegistration {
  KernelRegistration(const KernelDef& d, StringPiece c,
                     kernel_factory::OpKernelRegistrar::Factory f)
      : def(d), kernel_class_name(c.ToString()), factory(f) {}
  const KernelDef def;
  const string kernel_class_name;
  const kernel_factory::OpKernelRegistrar::Factory factory;
};

auto jvmCallbackOpInitializer = []{
  auto* reg = reinterpret_cast<std::unordered_multimap<string, KernelRegistration>*>(GlobalKernelRegistry());
  if (reg->find(strings::StrCat("JVMCallback:", DeviceTypeString(DEVICE_CPU), ":")) == reg->end()) {
    REGISTER_KERNEL_BUILDER(Name("JVMCallback").Device(DEVICE_CPU), JVMCallbackOp);
    REGISTER_KERNEL_BUILDER(Name("JVMCallbackStateless").Device(DEVICE_CPU), JVMCallbackOp);
  }
  // TODO: !!! [CALLBACK] Temporary.
//  if (reg->find(strings::StrCat("JVMCallback:", DeviceTypeString(DEVICE_GPU), ":")) == reg->end()) {
//    REGISTER_KERNEL_BUILDER(Name("JVMCallback").Device(DEVICE_GPU), JVMCallbackOp);
//    REGISTER_KERNEL_BUILDER(Name("JVMCallbackStateless").Device(DEVICE_GPU), JVMCallbackOp);
//  }
  return 0;
}();
}
}  // namespace tensorflow
<|endoftext|>


<commit_before>// casnddrv.cpp
// CoreAudio (MacOS X) Sound Driver for Crystal Space
//
// Created by mreda on Sun Nov 11 2001.
// Copyright (c) 2001 Matt Reda. All rights reserved.

#include "cssysdef.h"
#include "csver.h"
#include "cssys/sysfunc.h"
#include "csutil/scf.h"
#include "ivaria/reporter.h"
#include "iutil/objreg.h"
#include "isound/renderer.h"
#include "casnddrv.h"

#define SAMPLES_PER_BUFFER (8 * 1024)

CS_IMPLEMENT_PLUGIN

SCF_IMPLEMENT_FACTORY(csSoundDriverCoreAudio);

SCF_EXPORT_CLASS_TABLE(casnddrv)
	SCF_EXPORT_CLASS (csSoundDriverCoreAudio, "crystalspace.sound.driver.coreaudio",
                            "Crystal Space CoreAudio Sound driver for MacOS X")
SCF_EXPORT_CLASS_TABLE_END

SCF_IMPLEMENT_IBASE(csSoundDriverCoreAudio)
    SCF_IMPLEMENTS_INTERFACE(iSoundDriver)
    SCF_IMPLEMENTS_EMBEDDED_INTERFACE(iComponent)
SCF_IMPLEMENT_IBASE_END;

SCF_IMPLEMENT_EMBEDDED_IBASE(csSoundDriverCoreAudio::eiComponent)
    SCF_IMPLEMENTS_INTERFACE(iComponent)
SCF_IMPLEMENT_EMBEDDED_IBASE_END

// CoreAudio IO proc
static OSStatus AudioProc(AudioDeviceID inDevice, const AudioTimeStamp *inNow, const AudioBufferList *inInputData,
                            const AudioTimeStamp *inInputTime, AudioBufferList *outOutputData,
                            const AudioTimeStamp *inOutputTime, void *inClientData);

// Constructor
csSoundDriverCoreAudio::csSoundDriverCoreAudio(iBase *base)
{
    SCF_CONSTRUCT_IBASE(base);
    SCF_CONSTRUCT_EMBEDDED_IBASE(scfiComponent);
    reg = NULL;
    soundRender = NULL;
    memory = NULL;
    memorySize = 0;
    frequency = 0;
    is16Bit = false;
    isStereo = false;
    isPlaying = false;
}

// Destructor
csSoundDriverCoreAudio::~csSoundDriverCoreAudio()
{
    if (isPlaying == true)
        Close();
}

// Open
// Open the driver and begin playing
bool csSoundDriverCoreAudio::Open(iSoundRender *render, int freq, bool bit16, bool stereo)
{
    // Report driver information
    csReport(reg, CS_REPORTER_SEVERITY_NOTIFY, CS_SOUND_DRIVER,
            CS_PLATFORM_NAME " CoreAudio sound driver for Crystal Space "
            CS_VERSION_NUMBER "\nWritten by Matt Reda <mreda@mac.com>");

    OSStatus status;
    UInt32 propertySize, bufferSize;		// bufferSize is in bytes
    AudioStreamBasicDescription outStreamDesc;

    // Get output device
    propertySize = sizeof(audioDevice);
    status = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, &propertySize, &audioDevice);
    if ((status != 0) || (audioDevice == kAudioDeviceUnknown))
        return false;

    // Set buffer size
    propertySize = sizeof(bufferSize);
    bufferSize = SAMPLES_PER_BUFFER * sizeof(float);
    status = AudioDeviceSetProperty(audioDevice, NULL, 0, false,
                                        kAudioDevicePropertyBufferSize, propertySize, &bufferSize);
    if (status != 0)
        return false;

    // Get stream information
    propertySize = sizeof(outStreamDesc);
    status = AudioDeviceGetProperty(audioDevice, 0, false, kAudioDevicePropertyStreamFormat,
                                        &propertySize, &outStreamDesc);
    if (status != 0)
        return false;

    // Creation went ok, copy to local variables
    soundRender = render;
    isStereo = true;
    is16Bit = true;
    frequency = (int) outStreamDesc.mSampleRate;

    // Allocate memory
    memorySize = SAMPLES_PER_BUFFER * sizeof(short);
    memory = (short *) malloc(memorySize);

    // Add callback
    status = AudioDeviceAddIOProc(audioDevice, AudioProc, this);
    if (status != 0)
        return false;

    // Begin playback
    status = AudioDeviceStart(audioDevice, AudioProc);
    if (status != 0)
        return false;

  return true;
}

// Close
// Stop playback and clean up
void csSoundDriverCoreAudio::Close()
{
    if (isPlaying == true)
    {
        OSStatus status;
        status = AudioDeviceStop(audioDevice, AudioProc);
        if (status != 0)
            return;

        status = AudioDeviceRemoveIOProc(audioDevice, AudioProc);
        if (status != 0)
            return;

        free(memory);
        memorySize = 0;

        isPlaying = false;
    };
}

// LockMemory
// Return memor buffer and size
void csSoundDriverCoreAudio::LockMemory(void **mem, int *memsize)
{
    *mem = memory;
    *memsize = memorySize;
}

// UnlockMemory
void csSoundDriverCoreAudio::UnlockMemory()
{
    // Do nothing
}

// IsBackground
// Return true to indicate driver can play in background
bool csSoundDriverCoreAudio::IsBackground()
{
    return true;
}

// Is16Bits
// Return whether or not driver is set up for 16 bit playback
bool csSoundDriverCoreAudio::Is16Bits()
{
    return is16Bit;
}

// IsStereo
// Indicate whether the driver is set up for stereo playback
bool csSoundDriverCoreAudio::IsStereo()
{
    return isStereo;
}

// GetFrequency
// Return playback frequency
int csSoundDriverCoreAudio::GetFrequency()
{
    return frequency;
}

// IsHandleVoidSound
// Return false to indicate driver needs input to create silence
bool csSoundDriverCoreAudio::IsHandleVoidSound()
{
    return false;
}

// CreateSamples
// Ask the renderer for samples, and then scale them
void csSoundDriverCoreAudio::CreateSamples(float *buffer)
{
    // Create new samples
    soundRender->MixingFunction();

    // Copy and scale Crystal Space samples to float the CoreAudio can use
    for (int i = 0; i < SAMPLES_PER_BUFFER; i++)
        buffer[i] = memory[i] * (1.0f / SHRT_MAX);
};

// AudioProc
// Create samples in output buffer - that will be played automatically
static OSStatus AudioProc(AudioDeviceID inDevice, const AudioTimeStamp *inNow, const AudioBufferList *inInputData,
                            const AudioTimeStamp *inInputTime, AudioBufferList *outOutputData,
                            const AudioTimeStamp *inOutputTime, void *inClientData)
{
    csSoundDriverCoreAudio *driver = (csSoundDriverCoreAudio *) inClientData;
    float *buffer = (float *) outOutputData->mBuffers[0].mData;

    driver->CreateSamples(buffer);

    return 0;
};

<commit_msg>Fixed small bug that prevented the freeing of resources when the driver is closed<commit_after>// casnddrv.cpp
// CoreAudio (MacOS X) Sound Driver for Crystal Space
//
// Created by mreda on Sun Nov 11 2001.
// Copyright (c) 2001 Matt Reda. All rights reserved.

#include "cssysdef.h"
#include "csver.h"
#include "cssys/sysfunc.h"
#include "csutil/scf.h"
#include "ivaria/reporter.h"
#include "iutil/objreg.h"
#include "isound/renderer.h"
#include "casnddrv.h"

#define SAMPLES_PER_BUFFER (8 * 1024)

CS_IMPLEMENT_PLUGIN

SCF_IMPLEMENT_FACTORY(csSoundDriverCoreAudio);

SCF_EXPORT_CLASS_TABLE(casnddrv)
	SCF_EXPORT_CLASS (csSoundDriverCoreAudio, "crystalspace.sound.driver.coreaudio",
                            "Crystal Space CoreAudio Sound driver for MacOS X")
SCF_EXPORT_CLASS_TABLE_END

SCF_IMPLEMENT_IBASE(csSoundDriverCoreAudio)
    SCF_IMPLEMENTS_INTERFACE(iSoundDriver)
    SCF_IMPLEMENTS_EMBEDDED_INTERFACE(iComponent)
SCF_IMPLEMENT_IBASE_END;

SCF_IMPLEMENT_EMBEDDED_IBASE(csSoundDriverCoreAudio::eiComponent)
    SCF_IMPLEMENTS_INTERFACE(iComponent)
SCF_IMPLEMENT_EMBEDDED_IBASE_END

// CoreAudio IO proc
static OSStatus AudioProc(AudioDeviceID inDevice, const AudioTimeStamp *inNow, const AudioBufferList *inInputData,
                            const AudioTimeStamp *inInputTime, AudioBufferList *outOutputData,
                            const AudioTimeStamp *inOutputTime, void *inClientData);

// Constructor
csSoundDriverCoreAudio::csSoundDriverCoreAudio(iBase *base)
{
    SCF_CONSTRUCT_IBASE(base);
    SCF_CONSTRUCT_EMBEDDED_IBASE(scfiComponent);
    reg = NULL;
    soundRender = NULL;
    memory = NULL;
    memorySize = 0;
    frequency = 0;
    is16Bit = false;
    isStereo = false;
    isPlaying = false;
}

// Destructor
csSoundDriverCoreAudio::~csSoundDriverCoreAudio()
{
    if (isPlaying == true)
        Close();
}

// Open
// Open the driver and begin playing
bool csSoundDriverCoreAudio::Open(iSoundRender *render, int freq, bool bit16, bool stereo)
{
    // Report driver information
    csReport(reg, CS_REPORTER_SEVERITY_NOTIFY, CS_SOUND_DRIVER,
            CS_PLATFORM_NAME " CoreAudio sound driver for Crystal Space "
            CS_VERSION_NUMBER "\nWritten by Matt Reda <mreda@mac.com>");

    OSStatus status;
    UInt32 propertySize, bufferSize;		// bufferSize is in bytes
    AudioStreamBasicDescription outStreamDesc;

    // Get output device
    propertySize = sizeof(audioDevice);
    status = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, &propertySize, &audioDevice);
    if ((status != 0) || (audioDevice == kAudioDeviceUnknown))
        return false;

    // Set buffer size
    propertySize = sizeof(bufferSize);
    bufferSize = SAMPLES_PER_BUFFER * sizeof(float);
    status = AudioDeviceSetProperty(audioDevice, NULL, 0, false,
                                        kAudioDevicePropertyBufferSize, propertySize, &bufferSize);
    if (status != 0)
        return false;

    // Get stream information
    propertySize = sizeof(outStreamDesc);
    status = AudioDeviceGetProperty(audioDevice, 0, false, kAudioDevicePropertyStreamFormat,
                                        &propertySize, &outStreamDesc);
    if (status != 0)
        return false;

    // Creation went ok, copy to local variables
    soundRender = render;
    isStereo = (outStreamDesc.mChannelsPerFrame == 2);
    is16Bit = true;
    frequency = (int) outStreamDesc.mSampleRate;

    // Allocate memory
    memorySize = SAMPLES_PER_BUFFER * sizeof(short);
    memory = (short *) malloc(memorySize);

    // Add callback
    status = AudioDeviceAddIOProc(audioDevice, AudioProc, this);
    if (status != 0)
        return false;

    // Begin playback
    status = AudioDeviceStart(audioDevice, AudioProc);
    if (status != 0)
        return false;

    // Indicate that Initialization has completed
    isPlaying = true;

    return true;
}

// Close
// Stop playback and clean up
void csSoundDriverCoreAudio::Close()
{
    if (isPlaying == true)
    {
        OSStatus status;
        status = AudioDeviceStop(audioDevice, AudioProc);
        if (status != 0)
            return;

        status = AudioDeviceRemoveIOProc(audioDevice, AudioProc);
        if (status != 0)
            return;

        free(memory);
        memorySize = 0;

        isPlaying = false;
    };
}

// LockMemory
// Return memor buffer and size
void csSoundDriverCoreAudio::LockMemory(void **mem, int *memsize)
{
    *mem = memory;
    *memsize = memorySize;
}

// UnlockMemory
void csSoundDriverCoreAudio::UnlockMemory()
{
    // Do nothing
}

// IsBackground
// Return true to indicate driver can play in background
bool csSoundDriverCoreAudio::IsBackground()
{
    return true;
}

// Is16Bits
// Return whether or not driver is set up for 16 bit playback
bool csSoundDriverCoreAudio::Is16Bits()
{
    return is16Bit;
}

// IsStereo
// Indicate whether the driver is set up for stereo playback
bool csSoundDriverCoreAudio::IsStereo()
{
    return isStereo;
}

// GetFrequency
// Return playback frequency
int csSoundDriverCoreAudio::GetFrequency()
{
    return frequency;
}

// IsHandleVoidSound
// Return false to indicate driver needs input to create silence
bool csSoundDriverCoreAudio::IsHandleVoidSound()
{
    return false;
}

// CreateSamples
// Ask the renderer for samples, and then scale them
void csSoundDriverCoreAudio::CreateSamples(float *buffer)
{
    // Create new samples
    soundRender->MixingFunction();

    // Copy and scale Crystal Space samples to the floats that CoreAudio can use
    float scaleFactor = 1.0f / SHRT_MAX;
    for (int i = 0; i < SAMPLES_PER_BUFFER; i++)
        buffer[i] = memory[i] * scaleFactor;
};

// AudioProc
// Create samples in output buffer - that will be played automatically
static OSStatus AudioProc(AudioDeviceID inDevice, const AudioTimeStamp *inNow, const AudioBufferList *inInputData,
                            const AudioTimeStamp *inInputTime, AudioBufferList *outOutputData,
                            const AudioTimeStamp *inOutputTime, void *inClientData)
{
    csSoundDriverCoreAudio *driver = (csSoundDriverCoreAudio *) inClientData;
    float *buffer = (float *) outOutputData->mBuffers[0].mData;

    driver->CreateSamples(buffer);

    return 0;
};

<|endoftext|>


<commit_before>//////////////////////////////////////////////////////////////////////////
//
//  Copyright (c) 2008-2009, Image Engine Design 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 Image Engine Design nor the names of any
//       other contributors to this software 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.
//
//////////////////////////////////////////////////////////////////////////

#include <cassert>
#include <algorithm>

#include "IECoreMaya/Parameter.h"
#include "IECoreMaya/ToMayaObjectConverter.h"
#include "IECoreMaya/FromMayaObjectConverter.h"
#include "IECoreMaya/FloatSplineParameterHandler.h"
#include "IECoreMaya/MArrayIter.h"

#include "IECore/SplineParameter.h"

#include "maya/MFnCompoundAttribute.h"
#include "maya/MRampAttribute.h"
#include "maya/MFloatArray.h"
#include "maya/MIntArray.h"
#include "maya/MGlobal.h"
#include "maya/MFnDagNode.h"

using namespace IECoreMaya;
using namespace Imath;
using namespace boost;

template<> ParameterHandler::Description< FloatSplineParameterHandler<  IECore::Splineff > >
	FloatSplineParameterHandler<  IECore::Splineff >::g_registrar( IECore::SplineffParameter::staticTypeId() );

template<> ParameterHandler::Description< FloatSplineParameterHandler<  IECore::Splinedd > >
	FloatSplineParameterHandler<  IECore::Splinedd >::g_registrar( IECore::SplineddParameter::staticTypeId() );

template<typename S>
MStatus FloatSplineParameterHandler<S>::update( IECore::ConstParameterPtr parameter, MObject &attribute ) const
{
	assert( parameter );

	typename IECore::TypedParameter< S >::ConstPtr p = IECore::runTimeCast<const IECore::TypedParameter< S > >( parameter );
	if( !p )
	{
		return MS::kFailure;
	}

	MFnCompoundAttribute fnCAttr( attribute );
	if( !fnCAttr.hasObj( attribute ) )
	{
		return MS::kFailure;
	}

	/// \todo See if the attribute is of type CurveRamp - can't do this yet as we can't construct
	/// an MRampAttribute from just the MObject. We need either the node, too, or an MPlug

	return MS::kSuccess;
}

template<typename S>
MObject FloatSplineParameterHandler<S>::create( IECore::ConstParameterPtr parameter, const MString &attributeName ) const
{
	assert( parameter );

	typename IECore::TypedParameter< S >::ConstPtr p = IECore::runTimeCast<const IECore::TypedParameter< S > >( parameter );
	if( !p )
	{
		return MObject::kNullObj;
	}

	MRampAttribute fnRAttr;
	MObject result = fnRAttr.createCurveRamp( attributeName, attributeName );

	update( parameter, result );
	return result;
}

template<typename S>
MStatus FloatSplineParameterHandler<S>::setValue( IECore::ConstParameterPtr parameter, MPlug &plug ) const
{
	assert( parameter );
	typename IECore::TypedParameter< S >::ConstPtr p = IECore::runTimeCast<const IECore::TypedParameter< S > >( parameter );
	if( !p )
	{
		return MS::kFailure;
	}

	MRampAttribute fnRAttr( plug );
	if ( !fnRAttr.isCurveRamp() )
	{
		return MS::kFailure;
	}

	const S &spline = p->getTypedValue();

	MStatus s;
	MIntArray indicesToReuse;
	plug.getExistingArrayAttributeIndices( indicesToReuse, &s );
	assert( s );

	int nextNewLogicalIndex = 0;
	if( indicesToReuse.length() )
	{
		nextNewLogicalIndex = 1 + *std::max_element( MArrayIter<MIntArray>::begin( indicesToReuse ), MArrayIter<MIntArray>::end( indicesToReuse ) );
	}

	assert( indicesToReuse.length() == fnRAttr.getNumEntries() );

	size_t pointsSizeMinus2 = spline.points.size() - 2;
	unsigned pointIndex = 0;
	unsigned numExpectedPoints = 0;
	for ( typename S::PointContainer::const_iterator it = spline.points.begin(); it != spline.points.end(); ++it, ++pointIndex )
	{
		// we commonly double up the endpoints on cortex splines to force interpolation to the end.
		// maya does this implicitly, so we skip duplicated endpoints when passing the splines into maya.
		// this avoids users having to be responsible for managing the duplicates, and gives them some consistency
		// with the splines they edit elsewhere in maya.
		if( pointIndex==1 && *it == *spline.points.begin() || pointIndex==pointsSizeMinus2 && *it == *spline.points.rbegin()  )
		{
			continue;
		}

		MPlug pointPlug;
		if( indicesToReuse.length() )
		{
			pointPlug = plug.elementByLogicalIndex( indicesToReuse[0] );
			indicesToReuse.remove( 0 );
		}
		else
		{
			// this creates us a new spline point for us, and avoids the bug in MRampAttribute::addEntries which
			// somehow manages to create duplicate logical indexes.
			pointPlug = plug.elementByLogicalIndex( nextNewLogicalIndex++ );
		}

		s = pointPlug.child( 0 ).setValue( it->first ); assert( s );
		s = pointPlug.child( 1 ).setValue( it->second ); assert( s );
		s = pointPlug.child( 2 ).setValue( MRampAttribute::kSpline ); assert( s );

		numExpectedPoints++;
	}

	// delete any of the original indices which we didn't reuse. we can't use MRampAttrubute::deleteEntries
	// here as it's utterly unreliable.
	if( indicesToReuse.length() )
	{
		MString plugName = plug.name();
		MObject node = plug.node();
		MFnDagNode fnDAGN( node );
		if( fnDAGN.hasObj( node ) )
		{
			plugName = fnDAGN.fullPathName() + "." + plug.partialName();
		}
		for( unsigned i=0; i<indicesToReuse.length(); i++ )
		{
			// using mel because there's no equivalant api method as far as i know.
			MString command = "removeMultiInstance -b true \"" + plugName + "[" + indicesToReuse[i] + "]\"";
			s = MGlobal::executeCommand( command );
			assert( s );
			if( !s )
			{
				return s;
			}
		}
	}

#ifndef NDEBUG
	{
		MIntArray allLogicalIndices; plug.getExistingArrayAttributeIndices( allLogicalIndices );
		assert( fnRAttr.getNumEntries() == numExpectedPoints );
		assert( fnRAttr.getNumEntries() == allLogicalIndices.length() );

		// the MRampAttribute has the wonderful "feature" that addEntries() is somehow capable
		// of creating duplicate logical array indices, which causes no end of trouble
		// down the line. check that we've managed to avoid this pitfall.
		std::set<int> uniqueIndices;
		std::copy(
			MArrayIter<MIntArray>::begin( allLogicalIndices ),
			MArrayIter<MIntArray>::end( allLogicalIndices ),
			std::insert_iterator<std::set<int> >( uniqueIndices, uniqueIndices.begin() )
		);
		assert( uniqueIndices.size()==allLogicalIndices.length() );

		// then check that every element of the ramp has a suitable equivalent in
		// the original spline
		MIntArray indices;
		MFloatArray positions;
		MFloatArray values;
		MIntArray interps;
		fnRAttr.getEntries( indices, positions, values, interps, &s );
		assert( s );
		assert( numExpectedPoints == positions.length() );
		assert( numExpectedPoints == values.length() );
		assert( numExpectedPoints == interps.length() );
		assert( numExpectedPoints == indices.length() );

		for ( unsigned i = 0; i < positions.length(); i++ )
		{
			float position = positions[ i ];
			float value = values[ i ];

			bool found = false;

			for ( typename S::PointContainer::const_iterator it = spline.points.begin(); it != spline.points.end() && !found; ++it )
			{
				if ( fabs( it->first - position ) < 1.e-3f && fabs( it->second - value ) < 1.e-3f )
				{
					found = true;
				}
			}
			assert( found );
		}
	}
#endif

	return MS::kSuccess;
}

template<typename S>
MStatus FloatSplineParameterHandler<S>::setValue( const MPlug &plug, IECore::ParameterPtr parameter ) const
{
	assert( parameter );

	typename IECore::TypedParameter< S >::Ptr p = IECore::runTimeCast<IECore::TypedParameter< S > >( parameter );
	if( !p )
	{
		return MS::kFailure;
	}

	S spline;

	MStatus s;
	MRampAttribute fnRAttr( plug, &s );
	assert( s );

	if ( !fnRAttr.isCurveRamp() )
	{
		return MS::kFailure;
	}

	if ( fnRAttr.getNumEntries( &s ) > 0 )
	{
		assert( s );

		MIntArray indices;
		MFloatArray positions;
		MFloatArray values;
		MIntArray interps;
		fnRAttr.getEntries( indices, positions, values, interps, &s );
		assert( s );

		if ( positions.length() != values.length() || positions.length() != interps.length() || positions.length() != indices.length() )
		{
			return MS::kFailure;
		}

		for ( unsigned i = 0; i < positions.length(); i ++)
		{
			spline.points.insert(
				typename S::PointContainer::value_type(
					static_cast< typename S::XType >( positions[i] ), static_cast< typename S::YType >( values[ i ] )
				)
			);
		}
	}

	// maya seems to do an implicit doubling up of the end points to cause interpolation to the ends.
	// our spline has no such implicit behaviour so we explicitly double up.
	if( spline.points.size() )
	{
#ifndef NDEBUG
		size_t oldSplineSize = spline.points.size();
#endif

		assert( spline.points.begin()->first <= spline.points.rbegin()->first );
		spline.points.insert( *spline.points.begin() );
		spline.points.insert( *spline.points.rbegin() );
		assert( spline.points.size() == oldSplineSize + 2 );
	}

	p->setTypedValue( spline );

	if( spline.points.size() )
	{
		assert( spline.points.size() >= 2 );
		assert( spline.points.size() == fnRAttr.getNumEntries() + 2 );
	}

	return MS::kSuccess;
}
<commit_msg>Got interpolation working as spline interpolation again.<commit_after>//////////////////////////////////////////////////////////////////////////
//
//  Copyright (c) 2008-2009, Image Engine Design 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 Image Engine Design nor the names of any
//       other contributors to this software 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.
//
//////////////////////////////////////////////////////////////////////////

#include <cassert>
#include <algorithm>

#include "IECoreMaya/Parameter.h"
#include "IECoreMaya/ToMayaObjectConverter.h"
#include "IECoreMaya/FromMayaObjectConverter.h"
#include "IECoreMaya/FloatSplineParameterHandler.h"
#include "IECoreMaya/MArrayIter.h"

#include "IECore/SplineParameter.h"

#include "maya/MFnCompoundAttribute.h"
#include "maya/MRampAttribute.h"
#include "maya/MFloatArray.h"
#include "maya/MIntArray.h"
#include "maya/MGlobal.h"
#include "maya/MFnDagNode.h"

using namespace IECoreMaya;
using namespace Imath;
using namespace boost;

template<> ParameterHandler::Description< FloatSplineParameterHandler<  IECore::Splineff > >
	FloatSplineParameterHandler<  IECore::Splineff >::g_registrar( IECore::SplineffParameter::staticTypeId() );

template<> ParameterHandler::Description< FloatSplineParameterHandler<  IECore::Splinedd > >
	FloatSplineParameterHandler<  IECore::Splinedd >::g_registrar( IECore::SplineddParameter::staticTypeId() );

template<typename S>
MStatus FloatSplineParameterHandler<S>::update( IECore::ConstParameterPtr parameter, MObject &attribute ) const
{
	assert( parameter );

	typename IECore::TypedParameter< S >::ConstPtr p = IECore::runTimeCast<const IECore::TypedParameter< S > >( parameter );
	if( !p )
	{
		return MS::kFailure;
	}

	MFnCompoundAttribute fnCAttr( attribute );
	if( !fnCAttr.hasObj( attribute ) )
	{
		return MS::kFailure;
	}

	/// \todo See if the attribute is of type CurveRamp - can't do this yet as we can't construct
	/// an MRampAttribute from just the MObject. We need either the node, too, or an MPlug

	return MS::kSuccess;
}

template<typename S>
MObject FloatSplineParameterHandler<S>::create( IECore::ConstParameterPtr parameter, const MString &attributeName ) const
{
	assert( parameter );

	typename IECore::TypedParameter< S >::ConstPtr p = IECore::runTimeCast<const IECore::TypedParameter< S > >( parameter );
	if( !p )
	{
		return MObject::kNullObj;
	}

	MRampAttribute fnRAttr;
	MObject result = fnRAttr.createCurveRamp( attributeName, attributeName );

	update( parameter, result );
	return result;
}

template<typename S>
MStatus FloatSplineParameterHandler<S>::setValue( IECore::ConstParameterPtr parameter, MPlug &plug ) const
{
	assert( parameter );
	typename IECore::TypedParameter< S >::ConstPtr p = IECore::runTimeCast<const IECore::TypedParameter< S > >( parameter );
	if( !p )
	{
		return MS::kFailure;
	}

	MRampAttribute fnRAttr( plug );
	if ( !fnRAttr.isCurveRamp() )
	{
		return MS::kFailure;
	}

	const S &spline = p->getTypedValue();

	MStatus s;
	MIntArray indicesToReuse;
	plug.getExistingArrayAttributeIndices( indicesToReuse, &s );
	assert( s );

	int nextNewLogicalIndex = 0;
	if( indicesToReuse.length() )
	{
		nextNewLogicalIndex = 1 + *std::max_element( MArrayIter<MIntArray>::begin( indicesToReuse ), MArrayIter<MIntArray>::end( indicesToReuse ) );
	}

	assert( indicesToReuse.length() == fnRAttr.getNumEntries() );

	size_t pointsSizeMinus2 = spline.points.size() - 2;
	unsigned pointIndex = 0;
	unsigned numExpectedPoints = 0;
	for ( typename S::PointContainer::const_iterator it = spline.points.begin(); it != spline.points.end(); ++it, ++pointIndex )
	{
		// we commonly double up the endpoints on cortex splines to force interpolation to the end.
		// maya does this implicitly, so we skip duplicated endpoints when passing the splines into maya.
		// this avoids users having to be responsible for managing the duplicates, and gives them some consistency
		// with the splines they edit elsewhere in maya.
		if( pointIndex==1 && *it == *spline.points.begin() || pointIndex==pointsSizeMinus2 && *it == *spline.points.rbegin()  )
		{
			continue;
		}

		MPlug pointPlug;
		if( indicesToReuse.length() )
		{
			pointPlug = plug.elementByLogicalIndex( indicesToReuse[0] );
			indicesToReuse.remove( 0 );
		}
		else
		{
			// this creates us a new spline point for us, and avoids the bug in MRampAttribute::addEntries which
			// somehow manages to create duplicate logical indexes.
			pointPlug = plug.elementByLogicalIndex( nextNewLogicalIndex++ );
		}

		s = pointPlug.child( 0 ).setValue( it->first ); assert( s );
		s = pointPlug.child( 1 ).setValue( it->second ); assert( s );
		// hardcoding interpolation of 3 (spline) because the MRampAttribute::MInterpolation enum values don't actually
		// correspond to the necessary plug values at all.
		s = pointPlug.child( 2 ).setValue( 3 ); assert( s );

		numExpectedPoints++;
	}

	// delete any of the original indices which we didn't reuse. we can't use MRampAttrubute::deleteEntries
	// here as it's utterly unreliable.
	if( indicesToReuse.length() )
	{
		MString plugName = plug.name();
		MObject node = plug.node();
		MFnDagNode fnDAGN( node );
		if( fnDAGN.hasObj( node ) )
		{
			plugName = fnDAGN.fullPathName() + "." + plug.partialName();
		}
		for( unsigned i=0; i<indicesToReuse.length(); i++ )
		{
			// using mel because there's no equivalant api method as far as i know.
			MString command = "removeMultiInstance -b true \"" + plugName + "[" + indicesToReuse[i] + "]\"";
			s = MGlobal::executeCommand( command );
			assert( s );
			if( !s )
			{
				return s;
			}
		}
	}

#ifndef NDEBUG
	{
		MIntArray allLogicalIndices; plug.getExistingArrayAttributeIndices( allLogicalIndices );
		assert( fnRAttr.getNumEntries() == numExpectedPoints );
		assert( fnRAttr.getNumEntries() == allLogicalIndices.length() );

		// the MRampAttribute has the wonderful "feature" that addEntries() is somehow capable
		// of creating duplicate logical array indices, which causes no end of trouble
		// down the line. check that we've managed to avoid this pitfall.
		std::set<int> uniqueIndices;
		std::copy(
			MArrayIter<MIntArray>::begin( allLogicalIndices ),
			MArrayIter<MIntArray>::end( allLogicalIndices ),
			std::insert_iterator<std::set<int> >( uniqueIndices, uniqueIndices.begin() )
		);
		assert( uniqueIndices.size()==allLogicalIndices.length() );

		// then check that every element of the ramp has a suitable equivalent in
		// the original spline
		MIntArray indices;
		MFloatArray positions;
		MFloatArray values;
		MIntArray interps;
		fnRAttr.getEntries( indices, positions, values, interps, &s );
		assert( s );
		assert( numExpectedPoints == positions.length() );
		assert( numExpectedPoints == values.length() );
		assert( numExpectedPoints == interps.length() );
		assert( numExpectedPoints == indices.length() );

		for ( unsigned i = 0; i < positions.length(); i++ )
		{
			float position = positions[ i ];
			float value = values[ i ];

			bool found = false;

			for ( typename S::PointContainer::const_iterator it = spline.points.begin(); it != spline.points.end() && !found; ++it )
			{
				if ( fabs( it->first - position ) < 1.e-3f && fabs( it->second - value ) < 1.e-3f )
				{
					found = true;
				}
			}
			assert( found );
		}
	}
#endif

	return MS::kSuccess;
}

template<typename S>
MStatus FloatSplineParameterHandler<S>::setValue( const MPlug &plug, IECore::ParameterPtr parameter ) const
{
	assert( parameter );

	typename IECore::TypedParameter< S >::Ptr p = IECore::runTimeCast<IECore::TypedParameter< S > >( parameter );
	if( !p )
	{
		return MS::kFailure;
	}

	S spline;

	MStatus s;
	MRampAttribute fnRAttr( plug, &s );
	assert( s );

	if ( !fnRAttr.isCurveRamp() )
	{
		return MS::kFailure;
	}

	if ( fnRAttr.getNumEntries( &s ) > 0 )
	{
		assert( s );

		MIntArray indices;
		MFloatArray positions;
		MFloatArray values;
		MIntArray interps;
		fnRAttr.getEntries( indices, positions, values, interps, &s );
		assert( s );

		if ( positions.length() != values.length() || positions.length() != interps.length() || positions.length() != indices.length() )
		{
			return MS::kFailure;
		}

		for ( unsigned i = 0; i < positions.length(); i ++)
		{
			spline.points.insert(
				typename S::PointContainer::value_type(
					static_cast< typename S::XType >( positions[i] ), static_cast< typename S::YType >( values[ i ] )
				)
			);
		}
	}

	// maya seems to do an implicit doubling up of the end points to cause interpolation to the ends.
	// our spline has no such implicit behaviour so we explicitly double up.
	if( spline.points.size() )
	{
#ifndef NDEBUG
		size_t oldSplineSize = spline.points.size();
#endif

		assert( spline.points.begin()->first <= spline.points.rbegin()->first );
		spline.points.insert( *spline.points.begin() );
		spline.points.insert( *spline.points.rbegin() );
		assert( spline.points.size() == oldSplineSize + 2 );
	}

	p->setTypedValue( spline );

	if( spline.points.size() )
	{
		assert( spline.points.size() >= 2 );
		assert( spline.points.size() == fnRAttr.getNumEntries() + 2 );
	}

	return MS::kSuccess;
}
<|endoftext|>