<commit_before>
/*
 * Copyright (C) 2010, Directed Edge, Inc. | Licensed under the MPL and LGPL
 */

#include <QActiveResource.h>
#include <QDateTime>
#include <ruby.h>
#include <QDebug>

typedef VALUE(*ARGS)(...);
typedef int(*ITERATOR)(...);

static VALUE rb_mQAR;
static VALUE rb_cQARHash;
static VALUE rb_cQARParamList;
static VALUE rb_cQARResource;

namespace Symbol
{
    const ID all = rb_intern("all");
    const ID at = rb_intern("at");
    const ID first = rb_intern("first");
    const ID follow_redirects = rb_intern("follow_redirects");
    const ID method_missing = rb_intern("method_missing");
    const ID New = rb_intern("new");
    const ID one = rb_intern("one");
    const ID params = rb_intern("params");
    const ID to_s = rb_intern("to_s");
    const ID last = rb_intern("last");
}

static QString to_s(VALUE value)
{
    VALUE s = rb_funcall(value, Symbol::to_s, 0);
    return QString::fromUtf8(StringValuePtr(s));
}

static VALUE to_value(const QVariant &v)
{
    switch(v.type())
    {
    case QVariant::Hash:
    {
        VALUE value = rb_funcall(rb_cQARHash, Symbol::New, 0);
        QHash<QString, QVariant> hash = v.toHash();

        for(QHash<QString, QVariant>::ConstIterator it = hash.begin(); it != hash.end(); ++it)
        {
            rb_hash_aset(value, ID2SYM(rb_intern(it.key().toUtf8())), to_value(it.value()));
        }

        return value;
    }
    case QVariant::List:
    {
        VALUE value = rb_ary_new();

        foreach(QVariant element, v.toList())
        {
            rb_ary_push(value, to_value(element));
        }

        return value;
    }
    case QVariant::Invalid:
        return Qnil;
    case QVariant::Bool:
        return v.toBool() ? Qtrue : Qfalse;
    case QVariant::Int:
        return rb_int_new(v.toInt());
    case QVariant::Double:
        return rb_float_new(v.toDouble());
    case QVariant::DateTime:
    {
        return rb_funcall(rb_cTime, Symbol::at, 1, rb_int_new(v.toDateTime().toTime_t()));
    }
    default:
        return rb_str_new2(v.toString().toUtf8());
    }
}

/*
 * Hash
 */

static VALUE hash_method_missing(VALUE self, VALUE method)
{
    VALUE value = rb_hash_aref(self, method);
    return (value == Qnil) ?
        rb_funcall(rb_cHash, ID2SYM(Symbol::method_missing), 1, method) : value;
}

/*
 * Resource
 */

static void resource_mark(QActiveResource::Resource *) {}
static void resource_free(QActiveResource::Resource *resource)
{
    delete resource;
}

static VALUE resource_allocate(VALUE klass)
{
    QActiveResource::Resource *resource = new QActiveResource::Resource;
    return Data_Wrap_Struct(klass, resource_mark, resource_free, resource);
}

static VALUE resource_initialize(VALUE self, VALUE base, VALUE resource)
{
    QActiveResource::Resource *r = 0;
    Data_Get_Struct(self, QActiveResource::Resource, r);
    r->setBase(QString::fromUtf8(rb_string_value_ptr(&base)));
    r->setResource(QString::fromUtf8(rb_string_value_ptr(&resource)));
}

/*
 * ParamList
 */

static VALUE param_list_mark(QActiveResource::ParamList *) {}
static VALUE param_list_free(QActiveResource::ParamList *params)
{
    delete params;
}

static VALUE param_list_allocate(VALUE klass)
{
    QActiveResource::ParamList *params = new QActiveResource::ParamList();
    return Data_Wrap_Struct(klass, param_list_mark, param_list_free, params);
}

static int params_hash_iterator(VALUE key, VALUE value, VALUE params)
{
    QActiveResource::ParamList *params_pointer;
    Data_Get_Struct(params, QActiveResource::ParamList, params_pointer);
    params_pointer->append(QActiveResource::Param(to_s(key), to_s(value)));
}

static VALUE resource_find(int argc, VALUE *argv, VALUE self)
{
    QActiveResource::Resource *resource = 0;
    Data_Get_Struct(self, class QActiveResource::Resource, resource);

    VALUE params = param_list_allocate(rb_cData);

    if(argc >= 2 && TYPE(argv[1]) == T_HASH)
    {
        VALUE params_hash = rb_hash_aref(argv[1], ID2SYM(Symbol::params));

        if(params_hash != Qnil)
        {
            rb_hash_foreach(params_hash, (ITERATOR) params_hash_iterator, params);
        }

        VALUE follow_redirects = rb_hash_aref(argv[1], ID2SYM(Symbol::follow_redirects));
        resource->setFollowRedirects(follow_redirects == Qtrue);
    }

    QActiveResource::ParamList *params_pointer;
    Data_Get_Struct(params, QActiveResource::ParamList, params_pointer);

    QString from;

    if(argc >= 1)
    {
        ID current = SYM2ID(argv[0]);

        if(current == Symbol::one)
        {
            return to_value(resource->find(QActiveResource::FindOne, from, *params_pointer));
        }
        else if(current == Symbol::first)
        {
            return to_value(resource->find(QActiveResource::FindFirst, from, *params_pointer));
        }
        else if(current == Symbol::last)
        {
            return to_value(resource->find(QActiveResource::FindLast, from, *params_pointer));
        }
        else if(current != Symbol::all)
        {
            return to_value(resource->find(to_s(argv[0])));
        }
    }

    QActiveResource::RecordList records =
        resource->find(QActiveResource::FindAll, from, *params_pointer);

    VALUE array = rb_ary_new2(records.length());

    for(int i = 0; i < records.length(); i++)
    {
        rb_ary_store(array, i, to_value(records[i]));
    }

    return array;
}

/*
 * QAR
 */

VALUE qar_extended(VALUE self, VALUE base)
{
    return Qnil;
}

extern "C"
{
    void Init_QAR(void)
    {
        rb_mQAR = rb_define_module("QAR");

        rb_cQARHash = rb_define_class_under(rb_mQAR, "Hash", rb_cHash);
        rb_define_method(rb_cQARHash, "method_missing", (ARGS) hash_method_missing, 1);

        rb_cQARParamList = rb_define_class_under(rb_mQAR, "ParamList", rb_cObject);
        rb_define_alloc_func(rb_cQARParamList, param_list_allocate);

        rb_cQARResource = rb_define_class_under(rb_mQAR, "Resource", rb_cObject);
        rb_define_alloc_func(rb_cQARResource, resource_allocate);
        rb_define_method(rb_cQARResource, "initialize", (ARGS) resource_initialize, 2);
        rb_define_method(rb_cQARResource, "find", (ARGS) resource_find, -1);

        rb_define_singleton_method(rb_mQAR, "extended", (ARGS) qar_extended, 1);
    }
}
<commit_msg>Make static<commit_after>
/*
 * Copyright (C) 2010, Directed Edge, Inc. | Licensed under the MPL and LGPL
 */

#include <QActiveResource.h>
#include <QDateTime>
#include <ruby.h>
#include <QDebug>

typedef VALUE(*ARGS)(...);
typedef int(*ITERATOR)(...);

static VALUE rb_mQAR;
static VALUE rb_cQARHash;
static VALUE rb_cQARParamList;
static VALUE rb_cQARResource;

namespace Symbol
{
    const ID all = rb_intern("all");
    const ID at = rb_intern("at");
    const ID first = rb_intern("first");
    const ID follow_redirects = rb_intern("follow_redirects");
    const ID method_missing = rb_intern("method_missing");
    const ID New = rb_intern("new");
    const ID one = rb_intern("one");
    const ID params = rb_intern("params");
    const ID to_s = rb_intern("to_s");
    const ID last = rb_intern("last");
}

static QString to_s(VALUE value)
{
    VALUE s = rb_funcall(value, Symbol::to_s, 0);
    return QString::fromUtf8(StringValuePtr(s));
}

static VALUE to_value(const QVariant &v)
{
    switch(v.type())
    {
    case QVariant::Hash:
    {
        VALUE value = rb_funcall(rb_cQARHash, Symbol::New, 0);
        QHash<QString, QVariant> hash = v.toHash();

        for(QHash<QString, QVariant>::ConstIterator it = hash.begin(); it != hash.end(); ++it)
        {
            rb_hash_aset(value, ID2SYM(rb_intern(it.key().toUtf8())), to_value(it.value()));
        }

        return value;
    }
    case QVariant::List:
    {
        VALUE value = rb_ary_new();

        foreach(QVariant element, v.toList())
        {
            rb_ary_push(value, to_value(element));
        }

        return value;
    }
    case QVariant::Invalid:
        return Qnil;
    case QVariant::Bool:
        return v.toBool() ? Qtrue : Qfalse;
    case QVariant::Int:
        return rb_int_new(v.toInt());
    case QVariant::Double:
        return rb_float_new(v.toDouble());
    case QVariant::DateTime:
    {
        return rb_funcall(rb_cTime, Symbol::at, 1, rb_int_new(v.toDateTime().toTime_t()));
    }
    default:
        return rb_str_new2(v.toString().toUtf8());
    }
}

/*
 * Hash
 */

static VALUE hash_method_missing(VALUE self, VALUE method)
{
    VALUE value = rb_hash_aref(self, method);
    return (value == Qnil) ?
        rb_funcall(rb_cHash, ID2SYM(Symbol::method_missing), 1, method) : value;
}

/*
 * Resource
 */

static void resource_mark(QActiveResource::Resource *) {}
static void resource_free(QActiveResource::Resource *resource)
{
    delete resource;
}

static VALUE resource_allocate(VALUE klass)
{
    QActiveResource::Resource *resource = new QActiveResource::Resource;
    return Data_Wrap_Struct(klass, resource_mark, resource_free, resource);
}

static VALUE resource_initialize(VALUE self, VALUE base, VALUE resource)
{
    QActiveResource::Resource *r = 0;
    Data_Get_Struct(self, QActiveResource::Resource, r);
    r->setBase(QString::fromUtf8(rb_string_value_ptr(&base)));
    r->setResource(QString::fromUtf8(rb_string_value_ptr(&resource)));
}

/*
 * ParamList
 */

static VALUE param_list_mark(QActiveResource::ParamList *) {}
static VALUE param_list_free(QActiveResource::ParamList *params)
{
    delete params;
}

static VALUE param_list_allocate(VALUE klass)
{
    QActiveResource::ParamList *params = new QActiveResource::ParamList();
    return Data_Wrap_Struct(klass, param_list_mark, param_list_free, params);
}

static int params_hash_iterator(VALUE key, VALUE value, VALUE params)
{
    QActiveResource::ParamList *params_pointer;
    Data_Get_Struct(params, QActiveResource::ParamList, params_pointer);
    params_pointer->append(QActiveResource::Param(to_s(key), to_s(value)));
}

static VALUE resource_find(int argc, VALUE *argv, VALUE self)
{
    QActiveResource::Resource *resource = 0;
    Data_Get_Struct(self, class QActiveResource::Resource, resource);

    VALUE params = param_list_allocate(rb_cData);

    if(argc >= 2 && TYPE(argv[1]) == T_HASH)
    {
        VALUE params_hash = rb_hash_aref(argv[1], ID2SYM(Symbol::params));

        if(params_hash != Qnil)
        {
            rb_hash_foreach(params_hash, (ITERATOR) params_hash_iterator, params);
        }

        VALUE follow_redirects = rb_hash_aref(argv[1], ID2SYM(Symbol::follow_redirects));
        resource->setFollowRedirects(follow_redirects == Qtrue);
    }

    QActiveResource::ParamList *params_pointer;
    Data_Get_Struct(params, QActiveResource::ParamList, params_pointer);

    QString from;

    if(argc >= 1)
    {
        ID current = SYM2ID(argv[0]);

        if(current == Symbol::one)
        {
            return to_value(resource->find(QActiveResource::FindOne, from, *params_pointer));
        }
        else if(current == Symbol::first)
        {
            return to_value(resource->find(QActiveResource::FindFirst, from, *params_pointer));
        }
        else if(current == Symbol::last)
        {
            return to_value(resource->find(QActiveResource::FindLast, from, *params_pointer));
        }
        else if(current != Symbol::all)
        {
            return to_value(resource->find(to_s(argv[0])));
        }
    }

    QActiveResource::RecordList records =
        resource->find(QActiveResource::FindAll, from, *params_pointer);

    VALUE array = rb_ary_new2(records.length());

    for(int i = 0; i < records.length(); i++)
    {
        rb_ary_store(array, i, to_value(records[i]));
    }

    return array;
}

/*
 * QAR
 */

static VALUE qar_extended(VALUE self, VALUE base)
{
    return Qnil;
}

extern "C"
{
    void Init_QAR(void)
    {
        rb_mQAR = rb_define_module("QAR");

        rb_cQARHash = rb_define_class_under(rb_mQAR, "Hash", rb_cHash);
        rb_define_method(rb_cQARHash, "method_missing", (ARGS) hash_method_missing, 1);

        rb_cQARParamList = rb_define_class_under(rb_mQAR, "ParamList", rb_cObject);
        rb_define_alloc_func(rb_cQARParamList, param_list_allocate);

        rb_cQARResource = rb_define_class_under(rb_mQAR, "Resource", rb_cObject);
        rb_define_alloc_func(rb_cQARResource, resource_allocate);
        rb_define_method(rb_cQARResource, "initialize", (ARGS) resource_initialize, 2);
        rb_define_method(rb_cQARResource, "find", (ARGS) resource_find, -1);

        rb_define_singleton_method(rb_mQAR, "extended", (ARGS) qar_extended, 1);
    }
}
<|endoftext|>


<commit_before>#include "TFTKanji.h"

#define DBGLOG 0

TFTKanji::TFTKanji(ITKScreen* tft) :tft(tft) {
}

TFTKanji::~TFTKanji() {
  close();
}

int TFTKanji::open(SdFatBase* sd, const char* kanjifile, const char* ankfile) {
  int ret = kanjiFont.open(sd, kanjifile);
#if DBGLOG
  Serial.print("kanjiFont.open()=");
  Serial.println(ret);
#endif
  if (ret != 0) {
    return ret;
  }

  ret = ankFont.open(sd, ankfile);
#if DBGLOG
  Serial.print("ankFont.open()=");
  Serial.println(ret);
#endif
  if (ret != 0) {
    return ret;
  }
  return 0;
}

bool TFTKanji::close() {
  ankFont.close();
  return kanjiFont.close();
}

// cf. Adafruit_GFX::drawBitmap()
void drawBitmap(ITKScreen* tft, int16_t x, int16_t y,
    const uint8_t *bitmap, int16_t w, int16_t h,
    uint16_t color) {

  int16_t i, j, byteWidth = (w + 7) / 8;

  for (j=0; j<h; j++, bitmap += byteWidth) {
    for (i=0; i<w; i++) {
      if (*(bitmap + i / 8) & (128 >> (i & 7))) {
        tft->drawPixel(x + i, y + j, color);
      }
    }
  }
}

int loadFontAndDraw(ITKScreen* tft, int16_t x, int16_t y,
    Fontx2* font, uint16_t code, uint16_t color, uint16_t bgcolor) {
  int len = font->bitmapLen();
  uint8_t buf[len];
  int ret = font->load(code, buf, len);
  if (ret == 0) {
    // bgcolorがcolorと同じ場合はbgcolorでのfillは行わない。フラグ不要にするため
    // cf. Adafruit_GFX::setTextColor()
    if (bgcolor != color) {
      tft->fillRect(x, y, font->width(), font->height(), bgcolor);
    }
    drawBitmap(tft, x, y, buf, font->width(), font->height(), color);
  }
  return ret;
}

int TFTKanji::drawText(int16_t* x, int16_t* y, const char* str, uint16_t color, uint16_t bgcolor
#if WRAP_LONGLINE
    , bool wrap
#endif
    ) {
  uint16_t sjis1 = 0;
  const char* p = str;
  for (; *p != '\0'; p++) {
    uint8_t ch = (uint8_t)*p;
    uint16_t code;
    Fontx2* font;
#if DBGLOG
    Serial.print(ch, HEX);
#endif
    if (sjis1) { // SJIS 2nd byte
      code = (sjis1 << 8) | ch;
      sjis1 = 0;
      font = &kanjiFont;
    } else if (issjis1(ch)) { // SJIS 1st byte
      sjis1 = ch;
      continue;
    } else {
#if WRAP_NEWLINE
      if (ch == '\n') {
        *y += height();
        *x = 0;
        if (*y >= tft->height()) {
          break;
        } else {
          continue;
        }
      } else if (ch == '\r') { // ignore
        continue;
      }
#endif
      code = ch;
      font = &ankFont;
    }

    int16_t tftWidth = tft->width();
#if WRAP_LONGLINE
    if (wrap && *x + font->width() > tftWidth) {
      *y += height();
      *x = 0;
      if (*y >= tft->height()) {
        break;
      }
    }
#endif
    int ret = loadFontAndDraw(tft, *x, *y, font, code, color, bgcolor);
    if (ret < -1) {
      return ret;
    } // -1の場合、指定した文字のフォントデータ無し
    *x += font->width();
    if (*x >= tftWidth) {
      break;
    }
  }
  return p - str;
}
<commit_msg>改行での折り返し処理時の\r無視処理を削除。 スケッチサイズを減らすため。<commit_after>#include "TFTKanji.h"

#define DBGLOG 0

TFTKanji::TFTKanji(ITKScreen* tft) :tft(tft) {
}

TFTKanji::~TFTKanji() {
  close();
}

int TFTKanji::open(SdFatBase* sd, const char* kanjifile, const char* ankfile) {
  int ret = kanjiFont.open(sd, kanjifile);
#if DBGLOG
  Serial.print("kanjiFont.open()=");
  Serial.println(ret);
#endif
  if (ret != 0) {
    return ret;
  }

  ret = ankFont.open(sd, ankfile);
#if DBGLOG
  Serial.print("ankFont.open()=");
  Serial.println(ret);
#endif
  if (ret != 0) {
    return ret;
  }
  return 0;
}

bool TFTKanji::close() {
  ankFont.close();
  return kanjiFont.close();
}

// cf. Adafruit_GFX::drawBitmap()
void drawBitmap(ITKScreen* tft, int16_t x, int16_t y,
    const uint8_t *bitmap, int16_t w, int16_t h,
    uint16_t color) {

  int16_t i, j, byteWidth = (w + 7) / 8;

  for (j=0; j<h; j++, bitmap += byteWidth) {
    for (i=0; i<w; i++) {
      if (*(bitmap + i / 8) & (128 >> (i & 7))) {
        tft->drawPixel(x + i, y + j, color);
      }
    }
  }
}

int loadFontAndDraw(ITKScreen* tft, int16_t x, int16_t y,
    Fontx2* font, uint16_t code, uint16_t color, uint16_t bgcolor) {
  int len = font->bitmapLen();
  uint8_t buf[len];
  int ret = font->load(code, buf, len);
  if (ret == 0) {
    // bgcolorがcolorと同じ場合はbgcolorでのfillは行わない。フラグ不要にするため
    // cf. Adafruit_GFX::setTextColor()
    if (bgcolor != color) {
      tft->fillRect(x, y, font->width(), font->height(), bgcolor);
    }
    drawBitmap(tft, x, y, buf, font->width(), font->height(), color);
  }
  return ret;
}

static int wrapline(int16_t tftHeight, int fontHeight, int16_t* x, int16_t* y) {
  *y += fontHeight;
  *x = 0;
  if (*y >= tftHeight) {
    return 1;
  }
  return 0;
}

int TFTKanji::drawText(int16_t* x, int16_t* y, const char* str, uint16_t color, uint16_t bgcolor
#if WRAP_LONGLINE
    , bool wrap
#endif
    ) {
  uint16_t sjis1 = 0;
  const char* p = str;
  for (; *p != '\0'; p++) {
    uint8_t ch = (uint8_t)*p;
    uint16_t code;
    Fontx2* font;
#if DBGLOG
    Serial.print(ch, HEX);
#endif
    if (sjis1) { // SJIS 2nd byte
      code = (sjis1 << 8) | ch;
      sjis1 = 0;
      font = &kanjiFont;
    } else if (issjis1(ch)) { // SJIS 1st byte
      sjis1 = ch;
      continue;
    } else {
#if WRAP_NEWLINE
      if (ch == '\n') {
        if (wrapline(tft->height(), height(), x, y)) {
          break;
        } else {
          continue;
        }
      }
#endif
      code = ch;
      font = &ankFont;
    }

    int16_t tftWidth = tft->width();
#if WRAP_LONGLINE
    if (wrap && *x + font->width() > tftWidth) {
      if (wrapline(tft->height(), height(), x, y)) {
        break;
      }
    }
#endif
    int ret = loadFontAndDraw(tft, *x, *y, font, code, color, bgcolor);
    if (ret < -1) {
      return ret;
    } // -1の場合、指定した文字のフォントデータ無し
    *x += font->width();
    if (*x >= tftWidth) {
      break;
    }
  }
  return p - str;
}
<|endoftext|>


<commit_before>#include <cstdarg>
#include <cassert>
#include "blif_error.hpp"
#include "blifparse.hpp"

namespace blifparse {

//We wrap the actual blif_error to issolate custom handlers from vaargs
void blif_error_wrap(Callback& callback, const int line_no, const std::string& near_text, const char* fmt, ...) {
    va_list args;
    va_start(args, fmt);

    //We need to copy the args so we don't change them before the true formating
    va_list args_copy;
    va_copy(args_copy, args);

    //Determine the formatted length using a copy of the args
    int len = std::vsnprintf(nullptr, 0, fmt, args_copy);

    va_end(args_copy); //Clean-up

    //Negative if there is a problem with the format string
    assert(len >= 0 && "Problem decoding format string");

    size_t buf_size = len + 1; //For terminator

    //Allocate a buffer
    //  unique_ptr will free buffer automatically
    std::unique_ptr<char[]> buf(new char[buf_size]);

    //Format into the buffer using the original args
    len = std::vsnprintf(buf.get(), buf_size, fmt, args);

    va_end(args); //Clean-up

    assert(len >= 0 && "Problem decoding format string");
    assert(static_cast<size_t>(len) == buf_size - 1);

    //Build the string from the buffer
    std::string msg(buf.get(), len);

    //Call the error handler
    callback.parse_error(line_no, near_text, msg);
}

}
<commit_msg>Escape line endings in error message near text<commit_after>#include <cstdarg>
#include <cassert>
#include "blif_error.hpp"
#include "blifparse.hpp"

namespace blifparse {

std::string escape_string(const std::string& near_text);

//We wrap the actual blif_error to issolate custom handlers from vaargs
void blif_error_wrap(Callback& callback, const int line_no, const std::string& near_text, const char* fmt, ...) {
    va_list args;
    va_start(args, fmt);

    //We need to copy the args so we don't change them before the true formating
    va_list args_copy;
    va_copy(args_copy, args);

    //Determine the formatted length using a copy of the args
    int len = std::vsnprintf(nullptr, 0, fmt, args_copy);

    va_end(args_copy); //Clean-up

    //Negative if there is a problem with the format string
    assert(len >= 0 && "Problem decoding format string");

    size_t buf_size = len + 1; //For terminator

    //Allocate a buffer
    //  unique_ptr will free buffer automatically
    std::unique_ptr<char[]> buf(new char[buf_size]);

    //Format into the buffer using the original args
    len = std::vsnprintf(buf.get(), buf_size, fmt, args);

    va_end(args); //Clean-up

    assert(len >= 0 && "Problem decoding format string");
    assert(static_cast<size_t>(len) == buf_size - 1);

    //Build the string from the buffer
    std::string msg(buf.get(), len);

    //TODO: escape near_text
    std::string escaped_near_text = escape_string(near_text);

    //Call the error handler
    callback.parse_error(line_no, escaped_near_text, msg);
}

std::string escape_string(const std::string& near_text) {
    std::string escaped_text;

    for(char c : near_text) {

        if(c == '\n') {
            escaped_text += "\\n";
        } else if(c == '\r') {
            escaped_text += "\\r";
        } else {
            escaped_text += c;
        }
    }

    return escaped_text;
}

}
<|endoftext|>


<commit_before>/////////////////////////////////////////////////////////////////
/*
 *  Template of a read routine
 */
/////////////////////////////////////////////////////////////////
#include <NrrdIO/NrrdIOAPI.h>

#include <hxfield/HxUniformScalarField3.h>
#include <Amira/HxMessage.h>
#include <teem/nrrd.h>

NRRDIO_API
int NrrdReader(const char* filename)
{

	Nrrd *nrrd = nrrdNew();
	if ( nrrdLoad( nrrd, filename, NULL ) )
		throw biffGetDone(NRRD);

    if ( nrrd->dim > 3 )
	{
		theMsg->printf("ERROR: for now, nrrd input can only handle data with dimension 3 or less.");
		return 0;
	}

    const int dims[3] =
	{
		(nrrd->dim > 0) ? nrrd->axis[0].size : 1,
		(nrrd->dim > 1) ? nrrd->axis[1].size : 1,
		(nrrd->dim > 2) ? nrrd->axis[2].size : 1
	};

	HxUniformScalarField3* field = NULL;

	switch ( nrrd->type )
	{
		case nrrdTypeUChar:  field = new HxUniformScalarField3(dims,MC_UINT8,nrrd->data); break;
		case nrrdTypeChar:   field = new HxUniformScalarField3(dims,MC_INT8,nrrd->data); break;
		case nrrdTypeUShort: field = new HxUniformScalarField3(dims,MC_UINT16,nrrd->data); break;
		case nrrdTypeShort:  field = new HxUniformScalarField3(dims,MC_INT16,nrrd->data); break;
		case nrrdTypeInt:    field = new HxUniformScalarField3(dims,MC_INT32,nrrd->data); break;
		case nrrdTypeFloat:  field = new HxUniformScalarField3(dims,MC_FLOAT,nrrd->data); break;
		case nrrdTypeDouble: field = new HxUniformScalarField3(dims,MC_DOUBLE,nrrd->data); break;
		default: break;
	}

	if(field == NULL)
	{
		theMsg->printf("ERROR: unknown nrrd input type.");
		return 0;
	}

	// First fetch axis spacing
	double spacing[3] = { 1.0, 1.0, 1.0 };
	for ( size_t ax = 0; ax < nrrd->dim; ++ax )
	{
		switch ( nrrdSpacingCalculate( nrrd, ax, spacing+ax, nrrd->axis[ax].spaceDirection ) )
		{
			case nrrdSpacingStatusScalarNoSpace:
				break;
			case nrrdSpacingStatusDirection:
				break;
			case nrrdSpacingStatusScalarWithSpace:
				theMsg->printf("WARNING: nrrdSpacingCalculate returned nrrdSpacingStatusScalarWithSpace\n");
				spacing[ax] = nrrd->axis[ax].spacing;
				break;
			case nrrdSpacingStatusNone:
			default:
				theMsg->printf("WARNING: no pixel spacings in Nrrd for axis %d ; setting to 1.0\n",ax);
				spacing[ax] = 1.0;
				break;
		}
	}

	// Now let's set the physical dimensions
	// This is done by defining the bounding box, the range of the voxel centres
	// given in the order: xmin,xmax,ymin ...
	float *bbox = field->bbox();
	bbox[0] = isnan(nrrd->spaceOrigin[0])?0.0f:(float) nrrd->spaceOrigin[0];
	bbox[2] = isnan(nrrd->spaceOrigin[1])?0.0f:(float) nrrd->spaceOrigin[1];
	bbox[4] = isnan(nrrd->spaceOrigin[2])?0.0f:(float) nrrd->spaceOrigin[2];

	// When a dimension is 1, Amira still seems to have a defined spacing
	bbox[1] = bbox[0] + (float) spacing[0] * ( dims[0] == 1 ? 1 : (dims[0] - 1) );
	bbox[3] = bbox[2] + (float) spacing[1] * ( dims[1] == 1 ? 1 : (dims[1] - 1) );
	bbox[5] = bbox[4] + (float) spacing[2] * ( dims[2] == 1 ? 1 : (dims[2] - 1) );

	// Shouldn't need to check for data loading
	HxData::registerData(field, filename);

    return 1;
}

<commit_msg>doc: Add a proper file header and mention GPL>=3 license<commit_after>/////////////////////////////////////////////////////////////////
/*
 *  Amira Reader for the nrrd (Nearly Raw Raster Data) format:
 *  http://teem.sourceforge.net/nrrd/format.html
 *  Currently only supports 3d single channel data
 *  Copyright 2009 Gregory Jefferis. All rights reserved.
 *  License GPL >=3
 *  Certain portions of this code were copied/amended from the CMTK
 *  library: http://www.nitrc.org/projects/cmtk/
 */
/////////////////////////////////////////////////////////////////
#include <NrrdIO/NrrdIOAPI.h>

#include <hxfield/HxUniformScalarField3.h>
#include <Amira/HxMessage.h>
#include <teem/nrrd.h>

NRRDIO_API
int NrrdReader(const char* filename)
{

	Nrrd *nrrd = nrrdNew();
	if ( nrrdLoad( nrrd, filename, NULL ) )
		throw biffGetDone(NRRD);

    if ( nrrd->dim > 3 )
	{
		theMsg->printf("ERROR: for now, nrrd input can only handle data with dimension 3 or less.");
		return 0;
	}

    const int dims[3] =
	{
		(nrrd->dim > 0) ? nrrd->axis[0].size : 1,
		(nrrd->dim > 1) ? nrrd->axis[1].size : 1,
		(nrrd->dim > 2) ? nrrd->axis[2].size : 1
	};

	HxUniformScalarField3* field = NULL;

	switch ( nrrd->type )
	{
		case nrrdTypeUChar:  field = new HxUniformScalarField3(dims,MC_UINT8,nrrd->data); break;
		case nrrdTypeChar:   field = new HxUniformScalarField3(dims,MC_INT8,nrrd->data); break;
		case nrrdTypeUShort: field = new HxUniformScalarField3(dims,MC_UINT16,nrrd->data); break;
		case nrrdTypeShort:  field = new HxUniformScalarField3(dims,MC_INT16,nrrd->data); break;
		case nrrdTypeInt:    field = new HxUniformScalarField3(dims,MC_INT32,nrrd->data); break;
		case nrrdTypeFloat:  field = new HxUniformScalarField3(dims,MC_FLOAT,nrrd->data); break;
		case nrrdTypeDouble: field = new HxUniformScalarField3(dims,MC_DOUBLE,nrrd->data); break;
		default: break;
	}

	if(field == NULL)
	{
		theMsg->printf("ERROR: unknown nrrd input type.");
		return 0;
	}

	// First fetch axis spacing
	double spacing[3] = { 1.0, 1.0, 1.0 };
	for ( size_t ax = 0; ax < nrrd->dim; ++ax )
	{
		switch ( nrrdSpacingCalculate( nrrd, ax, spacing+ax, nrrd->axis[ax].spaceDirection ) )
		{
			case nrrdSpacingStatusScalarNoSpace:
				break;
			case nrrdSpacingStatusDirection:
				break;
			case nrrdSpacingStatusScalarWithSpace:
				theMsg->printf("WARNING: nrrdSpacingCalculate returned nrrdSpacingStatusScalarWithSpace\n");
				spacing[ax] = nrrd->axis[ax].spacing;
				break;
			case nrrdSpacingStatusNone:
			default:
				theMsg->printf("WARNING: no pixel spacings in Nrrd for axis %d ; setting to 1.0\n",ax);
				spacing[ax] = 1.0;
				break;
		}
	}

	// Now let's set the physical dimensions
	// This is done by defining the bounding box, the range of the voxel centres
	// given in the order: xmin,xmax,ymin ...
	float *bbox = field->bbox();
	bbox[0] = isnan(nrrd->spaceOrigin[0])?0.0f:(float) nrrd->spaceOrigin[0];
	bbox[2] = isnan(nrrd->spaceOrigin[1])?0.0f:(float) nrrd->spaceOrigin[1];
	bbox[4] = isnan(nrrd->spaceOrigin[2])?0.0f:(float) nrrd->spaceOrigin[2];

	// When a dimension is 1, Amira still seems to have a defined spacing
	bbox[1] = bbox[0] + (float) spacing[0] * ( dims[0] == 1 ? 1 : (dims[0] - 1) );
	bbox[3] = bbox[2] + (float) spacing[1] * ( dims[1] == 1 ? 1 : (dims[1] - 1) );
	bbox[5] = bbox[4] + (float) spacing[2] * ( dims[2] == 1 ? 1 : (dims[2] - 1) );

	// Shouldn't need to check for data loading
	HxData::registerData(field, filename);

    return 1;
}

<|endoftext|>


<commit_before>/////////////////////////////////////////////////////////////////
/*
 *  Template of a write routine
 */
/////////////////////////////////////////////////////////////////
#include <NrrdIO/NrrdIOAPI.h>

#include <Amira/HxData.h>
#include <Amira/HxMessage.h>
#include <hxfield/HxUniformScalarField3.h>

NRRDIO_API
int NrrdWriter(HxUniformScalarField3* data, const char* filename)
{
    FILE* fp = fopen(filename,"wb");

    if (!fp) {
	theMsg->ioError(filename);
	return 0;
    }

    /*
     * Write data into file ...
     */

    fclose(fp);

    return 1;
}
<commit_msg>First functional version of NrrdWriter<commit_after>/////////////////////////////////////////////////////////////////
/*
 *  Template of a write routine
 */
/////////////////////////////////////////////////////////////////
#include <NrrdIO/NrrdIOAPI.h>

#include <Amira/HxData.h>
#include <Amira/HxMessage.h>
#include <hxfield/HxUniformScalarField3.h>
#include <teem/nrrd.h>

NRRDIO_API
int NrrdWriter(HxUniformScalarField3* field, const char* filename)
{
	int compressed = 0;
	// Identify data type
	int nrrdType = nrrdTypeUnknown;
	switch ( field->primType() )
    {
		case McPrimType::mc_uint8:  nrrdType = nrrdTypeUChar; break;
		case McPrimType::mc_int8:   nrrdType = nrrdTypeChar; break;
		case McPrimType::mc_uint16: nrrdType = nrrdTypeUShort; break;
		case McPrimType::mc_int16:  nrrdType = nrrdTypeShort; break;
		case McPrimType::mc_int32:  nrrdType = nrrdTypeInt; break;
		case McPrimType::mc_float:  nrrdType = nrrdTypeFloat; break;
		case McPrimType::mc_double: nrrdType = nrrdTypeDouble; break;
		default: break;
    }

	if(nrrdType == nrrdTypeUnknown)
	{
		theMsg->printf("ERROR: unsupported output type: %s for nrrd",field->primType().getName());
		return 0;
	}

	void* data = field->lattice.dataPtr();

	Nrrd *nrrd = nrrdNew();
	NrrdIoState *nios = nrrdIoStateNew();

	if ( compressed ) {
		if (nrrdEncodingGzip->available() )
		{
			nrrdIoStateEncodingSet( nios, nrrdEncodingGzip );
			nrrdIoStateSet( nios, nrrdIoStateZlibLevel, 9 );
		}
		else theMsg->printf("WARNING: Nrrd library does not support Gzip compression encoding.\nPlease add -DTEEM_ZLIB to compiler options when building Nrrd library.\n");
	}

	try
	{
		if ( nrrdWrap_va( nrrd, data, nrrdType, (size_t)3,
		    (size_t)field->lattice.dimsInt()[0],
		    (size_t)field->lattice.dimsInt()[1],
		    (size_t)field->lattice.dimsInt()[2] ) )
		{
			throw( biffGetDone(NRRD) );
		}

		nrrdSpaceDimensionSet( nrrd, 3 );

		// TODO: Would be nice to set space units.  How does Amira store this?
//		if ( writeVolume->MetaKeyExists(CMTK_META_SPACE_UNITS_STRING) )
//		{
//			nrrd->spaceUnits[0] = strdup( writeVolume->m_MetaInformation[CMTK_META_SPACE_UNITS_STRING].c_str() );
//			nrrd->spaceUnits[1] = strdup( writeVolume->m_MetaInformation[CMTK_META_SPACE_UNITS_STRING].c_str() );
//			nrrd->spaceUnits[2] = strdup( writeVolume->m_MetaInformation[CMTK_META_SPACE_UNITS_STRING].c_str() );
//		}

		int kind[NRRD_DIM_MAX] = { nrrdKindDomain, nrrdKindDomain, nrrdKindDomain };
		nrrdAxisInfoSet_nva( nrrd, nrrdAxisInfoKind, kind );

		// TODO: Would be nice to write some kind of space if this exists

		// Fetch bounding box information and voxel size
		float* bbox = field->bbox();
		McVec3f voxelSize = field->getVoxelSize();

		// Just deal with space directions orthogonal to data axes
		// TODO: Fetch transformation and use that
		double spaceDir[NRRD_DIM_MAX][NRRD_SPACE_DIM_MAX];
		for ( int i = 0; i < 3; ++i )
		{
			for ( int j = 0; j < 3; ++j )
			{
				if (i == j) spaceDir[i][j] = (double) voxelSize[i];
			}
		}
		nrrdAxisInfoSet_nva( nrrd, nrrdAxisInfoSpaceDirection, spaceDir );

		double origin[NRRD_DIM_MAX] = { bbox[0], bbox[2], bbox[4] };
		if ( nrrdSpaceOriginSet( nrrd, origin ) )
		{
			throw( biffGetDone(NRRD) );
		}

		nrrdAxisInfoSet_va( nrrd, nrrdAxisInfoLabel, "x", "y", "z" );

		if ( nrrdSave( filename, nrrd, nios ) )
		{
			throw( biffGetDone(NRRD) );
		}
    }
	catch ( char* err )
    {
		theMsg->printf("ERROR: NrrdIO library returned error '%s'\n", err);
		free( err );
		return 0;
    }

	nrrdIoStateNix( nios );
	nrrdNix(nrrd);

    return 1;
}
<|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-tsdb/StreamChunk.h>
#include <fnord-base/io/fileutil.h>
#include <fnord-base/uri.h>
#include <fnord-base/util/binarymessagewriter.h>
#include <fnord-base/wallclock.h>
#include <fnord-msg/MessageEncoder.h>
#include <fnord-msg/msg.h>

using namespace fnord;

namespace tsdb {

RefPtr<StreamChunk> StreamChunk::create(
    const SHA1Hash& partition_key,
    const String& stream_key,
    StreamConfig* config,
    TSDBNodeRef* node) {
  return RefPtr<StreamChunk>(
      new StreamChunk(
          partition_key,
          stream_key,
          config,
          node));
}

RefPtr<StreamChunk> StreamChunk::reopen(
    const SHA1Hash& partition_key,
    const StreamChunkState& state,
    StreamConfig* config,
    TSDBNodeRef* node) {
  return RefPtr<StreamChunk>(
      new StreamChunk(
          partition_key,
          state,
          config,
          node));
}

StreamChunk::StreamChunk(
    const SHA1Hash& partition_key,
    const String& stream_key,
    StreamConfig* config,
    TSDBNodeRef* node) :
    stream_key_(stream_key),
    key_(partition_key),
    config_(config),
    node_(node),
    records_(
        FileUtil::joinPaths(
            node->db_path,
            StringUtil::stripShell(stream_key) + ".")),
    last_compaction_(0) {
  records_.setMaxDatafileSize(config_->max_sstable_size());
}

StreamChunk::StreamChunk(
    const SHA1Hash& partition_key,
    const StreamChunkState& state,
    StreamConfig* config,
    TSDBNodeRef* node) :
    stream_key_(state.stream_key),
    key_(partition_key),
    config_(config),
    node_(node),
    records_(
        FileUtil::joinPaths(
            node->db_path,
            StringUtil::stripShell(state.stream_key) + "."),
        state.record_state),
    repl_offsets_(state.repl_offsets),
    last_compaction_(0) {
  scheduleCompaction();
  node_->compactionq.insert(this, WallClock::unixMicros());
  node_->replicationq.insert(this, WallClock::unixMicros());
  records_.setMaxDatafileSize(config_->max_sstable_size());
}

void StreamChunk::insertRecord(
    const SHA1Hash& record_id,
    const Buffer& record) {
  std::unique_lock<std::mutex> lk(mutex_);

  fnord::logTrace(
      "tsdb",
      "Insert 1 record into stream='$0'",
      stream_key_);

  auto old_ver = records_.version();
  records_.addRecord(record_id, record);
  if (records_.version() != old_ver) {
    commitState();
  }

  scheduleCompaction();
}

void StreamChunk::insertRecords(const Vector<RecordRef>& records) {
  std::unique_lock<std::mutex> lk(mutex_);

  fnord::logTrace(
      "tsdb",
      "Insert $0 records into stream='$1'",
      records.size(),
      stream_key_);

  auto old_ver = records_.version();
  records_.addRecords(records);
  if (records_.version() != old_ver) {
    commitState();
  }

  scheduleCompaction();
}

void StreamChunk::scheduleCompaction() {
  auto now = WallClock::unixMicros();
  auto interval = config_->compaction_interval();
  auto last = last_compaction_.unixMicros();
  uint64_t compaction_delay = 0;

  if (last + interval > now) {
    compaction_delay = (last + interval) - now;
  }

  node_->compactionq.insert(this, now + compaction_delay);
}

void StreamChunk::compact() {
  std::unique_lock<std::mutex> lk(mutex_);
  last_compaction_ = DateTime::now();
  lk.unlock();

  fnord::logDebug(
      "tsdb.replication",
      "Compacting partition; stream='$0' partition='$1'",
      stream_key_,
      key_.toString());

  Set<String> deleted_files;
  records_.compact(&deleted_files);

  lk.lock();
  commitState();
  lk.unlock();

  node_->replicationq.insert(this, WallClock::unixMicros());

  for (const auto& f : deleted_files) {
    FileUtil::rm(f);
  }
}

void StreamChunk::replicate() {
  std::unique_lock<std::mutex> lk(replication_mutex_);

  auto cur_offset = records_.lastOffset();
  auto replicas = node_->replication_scheme->replicasFor(key_);
  bool dirty = false;
  bool needs_replication = false;
  bool has_error = false;

  for (const auto& r : replicas) {
    auto& off = repl_offsets_[r.unique_id];

    if (off < cur_offset) {
      try {
        auto rep_offset = replicateTo(r.addr, off);
        dirty = true;
        off = rep_offset;
        if (off < cur_offset) {
          needs_replication = true;
        }
      } catch (const std::exception& e) {
        has_error = true;

        fnord::logError(
          "tsdb.replication",
          e,
          "Error while replicating stream '$0' to '$1'",
          stream_key_,
          r.addr);
      }
    }
  }

  for (auto cur = repl_offsets_.begin(); cur != repl_offsets_.end(); ) {
    bool found;
    for (const auto& r : replicas) {
      if (r.unique_id == cur->first) {
        found = true;
        break;
      }
    }

    if (found) {
      ++cur;
    } else {
      cur = repl_offsets_.erase(cur);
      dirty = true;
    }
  }

  if (dirty) {
    std::unique_lock<std::mutex> lk(mutex_);
    commitState();
  }

  if (needs_replication) {
    node_->replicationq.insert(this, WallClock::unixMicros());
  } else if (has_error) {
    node_->replicationq.insert(
        this,
        WallClock::unixMicros() + 30 * kMicrosPerSecond);
  }
}

uint64_t StreamChunk::replicateTo(const String& addr, uint64_t offset) {
  size_t batch_size = 1024;
  util::BinaryMessageWriter batch;

  auto start_offset = records_.firstOffset();
  if (start_offset > offset) {
    offset = start_offset;
  }

  size_t n = 0;
  records_.fetchRecords(offset, batch_size, [this, &batch, &n] (
      const SHA1Hash& record_id,
      const void* record_data,
      size_t record_size) {
    ++n;

    batch.append(record_id.data(), record_id.size());
    batch.appendVarUInt(record_size);
    batch.append(record_data, record_size);
  });

  fnord::logDebug(
      "tsdb.replication",
      "Replicating to $0; stream='$1' partition='$2' offset=$3",
      addr,
      stream_key_,
      key_.toString(),
      offset);

  URI uri(StringUtil::format(
      "http://$0/tsdb/replicate?stream=$1&chunk=$2",
      addr,
      URI::urlEncode(stream_key_),
      URI::urlEncode(key_.toString())));

  http::HTTPRequest req(http::HTTPMessage::M_POST, uri.pathAndQuery());
  req.addHeader("Host", uri.hostAndPort());
  req.addHeader("Content-Type", "application/fnord-msg");
  req.addBody(batch.data(), batch.size());

  auto res = node_->http->executeRequest(req);
  res.wait();

  const auto& r = res.get();
  if (r.statusCode() != 201) {
    RAISEF(kRuntimeError, "received non-201 response: $0", r.body().toString());
  }

  return offset + n;
}

Vector<String> StreamChunk::listFiles() const {
  return records_.listDatafiles();
}

PartitionInfo StreamChunk::partitionInfo() const {
  PartitionInfo pi;
  pi.set_partition_key(key_.toString());
  pi.set_stream_key(stream_key_);
  pi.set_version(records_.version());
  pi.set_exists(true);
  return pi;
}

void StreamChunk::commitState() {
  StreamChunkState state;
  state.record_state = records_.getState();
  state.stream_key = stream_key_;
  state.repl_offsets = repl_offsets_;

  util::BinaryMessageWriter buf;
  state.encode(&buf);

  auto txn = node_->db->startTransaction(false);
  txn->update(key_.data(), key_.size(), buf.data(), buf.size());
  txn->commit();
}

void StreamChunkState::encode(
    util::BinaryMessageWriter* writer) const {
  writer->appendLenencString(stream_key);
  record_state.encode(writer);

  writer->appendVarUInt(repl_offsets.size());
  for (const auto& ro : repl_offsets) {
    writer->appendVarUInt(ro.first);
    writer->appendVarUInt(ro.second);
  }

  writer->appendVarUInt(record_state.version);
}

void StreamChunkState::decode(util::BinaryMessageReader* reader) {
  stream_key = reader->readLenencString();
  record_state.decode(reader);

  auto nrepl_offsets = reader->readVarUInt();
  for (int i = 0; i < nrepl_offsets; ++i) {
    auto id = reader->readVarUInt();
    auto off = reader->readVarUInt();
    repl_offsets.emplace(id, off);
  }

  record_state.version = reader->readVarUInt();
}

}
<commit_msg>fetch replicas for partition key<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-tsdb/StreamChunk.h>
#include <fnord-base/io/fileutil.h>
#include <fnord-base/uri.h>
#include <fnord-base/util/binarymessagewriter.h>
#include <fnord-base/wallclock.h>
#include <fnord-msg/MessageEncoder.h>
#include <fnord-msg/msg.h>

using namespace fnord;

namespace tsdb {

RefPtr<StreamChunk> StreamChunk::create(
    const SHA1Hash& partition_key,
    const String& stream_key,
    StreamConfig* config,
    TSDBNodeRef* node) {
  return RefPtr<StreamChunk>(
      new StreamChunk(
          partition_key,
          stream_key,
          config,
          node));
}

RefPtr<StreamChunk> StreamChunk::reopen(
    const SHA1Hash& partition_key,
    const StreamChunkState& state,
    StreamConfig* config,
    TSDBNodeRef* node) {
  return RefPtr<StreamChunk>(
      new StreamChunk(
          partition_key,
          state,
          config,
          node));
}

StreamChunk::StreamChunk(
    const SHA1Hash& partition_key,
    const String& stream_key,
    StreamConfig* config,
    TSDBNodeRef* node) :
    stream_key_(stream_key),
    key_(partition_key),
    config_(config),
    node_(node),
    records_(
        FileUtil::joinPaths(
            node->db_path,
            StringUtil::stripShell(stream_key) + ".")),
    last_compaction_(0) {
  records_.setMaxDatafileSize(config_->max_sstable_size());
}

StreamChunk::StreamChunk(
    const SHA1Hash& partition_key,
    const StreamChunkState& state,
    StreamConfig* config,
    TSDBNodeRef* node) :
    stream_key_(state.stream_key),
    key_(partition_key),
    config_(config),
    node_(node),
    records_(
        FileUtil::joinPaths(
            node->db_path,
            StringUtil::stripShell(state.stream_key) + "."),
        state.record_state),
    repl_offsets_(state.repl_offsets),
    last_compaction_(0) {
  scheduleCompaction();
  node_->compactionq.insert(this, WallClock::unixMicros());
  node_->replicationq.insert(this, WallClock::unixMicros());
  records_.setMaxDatafileSize(config_->max_sstable_size());
}

void StreamChunk::insertRecord(
    const SHA1Hash& record_id,
    const Buffer& record) {
  std::unique_lock<std::mutex> lk(mutex_);

  fnord::logTrace(
      "tsdb",
      "Insert 1 record into stream='$0'",
      stream_key_);

  auto old_ver = records_.version();
  records_.addRecord(record_id, record);
  if (records_.version() != old_ver) {
    commitState();
  }

  scheduleCompaction();
}

void StreamChunk::insertRecords(const Vector<RecordRef>& records) {
  std::unique_lock<std::mutex> lk(mutex_);

  fnord::logTrace(
      "tsdb",
      "Insert $0 records into stream='$1'",
      records.size(),
      stream_key_);

  auto old_ver = records_.version();
  records_.addRecords(records);
  if (records_.version() != old_ver) {
    commitState();
  }

  scheduleCompaction();
}

void StreamChunk::scheduleCompaction() {
  auto now = WallClock::unixMicros();
  auto interval = config_->compaction_interval();
  auto last = last_compaction_.unixMicros();
  uint64_t compaction_delay = 0;

  if (last + interval > now) {
    compaction_delay = (last + interval) - now;
  }

  node_->compactionq.insert(this, now + compaction_delay);
}

void StreamChunk::compact() {
  std::unique_lock<std::mutex> lk(mutex_);
  last_compaction_ = DateTime::now();
  lk.unlock();

  fnord::logDebug(
      "tsdb.replication",
      "Compacting partition; stream='$0' partition='$1'",
      stream_key_,
      key_.toString());

  Set<String> deleted_files;
  records_.compact(&deleted_files);

  lk.lock();
  commitState();
  lk.unlock();

  node_->replicationq.insert(this, WallClock::unixMicros());

  for (const auto& f : deleted_files) {
    FileUtil::rm(f);
  }
}

void StreamChunk::replicate() {
  std::unique_lock<std::mutex> lk(replication_mutex_);

  auto cur_offset = records_.lastOffset();
  auto replicas = node_->replication_scheme->replicasFor(
      String((char*) key_.data(), key_.size()));

  bool dirty = false;
  bool needs_replication = false;
  bool has_error = false;

  for (const auto& r : replicas) {
    auto& off = repl_offsets_[r.unique_id];

    if (off < cur_offset) {
      try {
        auto rep_offset = replicateTo(r.addr, off);
        dirty = true;
        off = rep_offset;
        if (off < cur_offset) {
          needs_replication = true;
        }
      } catch (const std::exception& e) {
        has_error = true;

        fnord::logError(
          "tsdb.replication",
          e,
          "Error while replicating stream '$0' to '$1'",
          stream_key_,
          r.addr);
      }
    }
  }

  for (auto cur = repl_offsets_.begin(); cur != repl_offsets_.end(); ) {
    bool found;
    for (const auto& r : replicas) {
      if (r.unique_id == cur->first) {
        found = true;
        break;
      }
    }

    if (found) {
      ++cur;
    } else {
      cur = repl_offsets_.erase(cur);
      dirty = true;
    }
  }

  if (dirty) {
    std::unique_lock<std::mutex> lk(mutex_);
    commitState();
  }

  if (needs_replication) {
    node_->replicationq.insert(this, WallClock::unixMicros());
  } else if (has_error) {
    node_->replicationq.insert(
        this,
        WallClock::unixMicros() + 30 * kMicrosPerSecond);
  }
}

uint64_t StreamChunk::replicateTo(const String& addr, uint64_t offset) {
  size_t batch_size = 1024;
  util::BinaryMessageWriter batch;

  auto start_offset = records_.firstOffset();
  if (start_offset > offset) {
    offset = start_offset;
  }

  size_t n = 0;
  records_.fetchRecords(offset, batch_size, [this, &batch, &n] (
      const SHA1Hash& record_id,
      const void* record_data,
      size_t record_size) {
    ++n;

    batch.append(record_id.data(), record_id.size());
    batch.appendVarUInt(record_size);
    batch.append(record_data, record_size);
  });

  fnord::logDebug(
      "tsdb.replication",
      "Replicating to $0; stream='$1' partition='$2' offset=$3",
      addr,
      stream_key_,
      key_.toString(),
      offset);

  URI uri(StringUtil::format(
      "http://$0/tsdb/replicate?stream=$1&chunk=$2",
      addr,
      URI::urlEncode(stream_key_),
      URI::urlEncode(key_.toString())));

  http::HTTPRequest req(http::HTTPMessage::M_POST, uri.pathAndQuery());
  req.addHeader("Host", uri.hostAndPort());
  req.addHeader("Content-Type", "application/fnord-msg");
  req.addBody(batch.data(), batch.size());

  auto res = node_->http->executeRequest(req);
  res.wait();

  const auto& r = res.get();
  if (r.statusCode() != 201) {
    RAISEF(kRuntimeError, "received non-201 response: $0", r.body().toString());
  }

  return offset + n;
}

Vector<String> StreamChunk::listFiles() const {
  return records_.listDatafiles();
}

PartitionInfo StreamChunk::partitionInfo() const {
  PartitionInfo pi;
  pi.set_partition_key(key_.toString());
  pi.set_stream_key(stream_key_);
  pi.set_version(records_.version());
  pi.set_exists(true);
  return pi;
}

void StreamChunk::commitState() {
  StreamChunkState state;
  state.record_state = records_.getState();
  state.stream_key = stream_key_;
  state.repl_offsets = repl_offsets_;

  util::BinaryMessageWriter buf;
  state.encode(&buf);

  auto txn = node_->db->startTransaction(false);
  txn->update(key_.data(), key_.size(), buf.data(), buf.size());
  txn->commit();
}

void StreamChunkState::encode(
    util::BinaryMessageWriter* writer) const {
  writer->appendLenencString(stream_key);
  record_state.encode(writer);

  writer->appendVarUInt(repl_offsets.size());
  for (const auto& ro : repl_offsets) {
    writer->appendVarUInt(ro.first);
    writer->appendVarUInt(ro.second);
  }

  writer->appendVarUInt(record_state.version);
}

void StreamChunkState::decode(util::BinaryMessageReader* reader) {
  stream_key = reader->readLenencString();
  record_state.decode(reader);

  auto nrepl_offsets = reader->readVarUInt();
  for (int i = 0; i < nrepl_offsets; ++i) {
    auto id = reader->readVarUInt();
    auto off = reader->readVarUInt();
    repl_offsets.emplace(id, off);
  }

  record_state.version = reader->readVarUInt();
}

}
<|endoftext|>


<commit_before>//=============================================================================
// ■ audio.cpp
//-----------------------------------------------------------------------------
//   提供声音支持。
//=============================================================================

#include "global.hpp"

namespace Audio {
	//-------------------------------------------------------------------------
	// ● 全局变量
	//-------------------------------------------------------------------------
	const size_t vf_buffer_size = 4096;
	PaStream* wave_stream;
	struct callback_data data;
	struct active_sound* active_sounds[16] = {NULL};
	size_t active_sound_count = 0;
	// 为了避免反复计算，将正弦值存储在这里。其分布为
	// [0] = sin 0
	// [64] = sin ⅛π
	// [128] = sin ¼π
	// [192] = sin ⅜π
	float sine_table[256];
	const size_t sine_table_size = ARRAY_SIZE(sine_table);
	//-------------------------------------------------------------------------
	// ● 初始化
	//-------------------------------------------------------------------------
	void init() {
		ensure_no_error(Pa_Initialize());
		populate_sine_table();
		data.type = 0;
		// 44100Hz
		data.sample_rate = 44100.0d;
		ensure_no_error(Pa_OpenDefaultStream(
			&wave_stream,
			// 无声输入 - 立体声输出、32位浮点数
			0, 2, paFloat32,
			data.sample_rate,
			// 256格缓冲区
			256,
			play_callback, &data
		));
		ensure_no_error(Pa_StartStream(wave_stream));
	}
	//-------------------------------------------------------------------------
	// ● 释放
	//-------------------------------------------------------------------------
	void wobuzhidaozhegefangfayinggaijiaoshenmemingzi() {
		ensure_no_error(Pa_StopStream(wave_stream));
		ensure_no_error(Pa_CloseStream(wave_stream));
		Pa_Terminate();
	}
	//-------------------------------------------------------------------------
	// ● 统一处理错误
	//-------------------------------------------------------------------------
	void ensure_no_error(PaError err) {
		if (err >= 0) return;
		Pa_Terminate();
		rb_raise(
			rb_eRuntimeError,
			"PortAudio error %d: %s",
			err, Pa_GetErrorText(err)
		);
	}
	//-------------------------------------------------------------------------
	// ● 内部使用的回调函数
	//-------------------------------------------------------------------------
	int play_callback(
		const void* input_buffer UNUSED,
		void* output_buffer,
		unsigned long frame_count,
		const PaStreamCallbackTimeInfo* time_info UNUSED,
		PaStreamCallbackFlags status_flags UNUSED,
		void* user_data
	) {
		float* output = (float*) output_buffer;
		struct callback_data* data = (struct callback_data*) user_data;
		// Magic. 吔屎啦PortAudio！
		#define FEED_AUDIO_DATA(value) do { \
			*output++ = (value); \
			*output++ = (value); \
			frame_count--; \
		} while (false)
		while (frame_count > 0) switch (data->type) {
			case 0:
				FEED_AUDIO_DATA(.0f);
				break;
			case 1:
				get_next_triangle_value(&data->data.triangle);
				FEED_AUDIO_DATA(data->data.triangle.value);
				break;
			case 2:
			default:
				get_next_sine_value(&data->data.sine);
				FEED_AUDIO_DATA(data->data.sine.value);
				break;
			case 3:
				*((float*) 0) = .0f; // 音频就是爆炸！
				FEED_AUDIO_DATA(.0f);
				break;
			case 4:
				FEED_AUDIO_DATA((float) (
					(double) rand() / (double) RAND_MAX * 2.0d - 1.0d
				));
				break;
		}
		#undef FEED_AUDIO_DATA
		return paContinue;
	}
	//-------------------------------------------------------------------------
	// ● 停止播放
	//-------------------------------------------------------------------------
	void stop() {
		data.type = 0;
	}
	//-------------------------------------------------------------------------
	// ● 播放三角波
	//    freq : 频率（Hz）
	//-------------------------------------------------------------------------
	void play_triangle(float freq) {
		data.data.triangle.value = -1.0f;
		data.data.triangle.delta = 2.0f / ((float) data.sample_rate / freq / 2);
		data.type = 1;
	}
	//-------------------------------------------------------------------------
	// ● 计算下一个值使输出呈三角波形
	//-------------------------------------------------------------------------
	void get_next_triangle_value(struct triangle_data* data) {
		data->value += data->delta;
		if (data->value > 1.0f) {
			data->value = 2.0f - data->value;
			data->delta = -data->delta;
		} else if (data->value < -1.0f) {
			data->value = -2.0f - data->value;
			data->delta = -data->delta;
		}
	}
	//-------------------------------------------------------------------------
	// ● 播放正弦波
	//    freq : 频率（Hz）
	//-------------------------------------------------------------------------
	void play_sine(float freq) {
		data.data.sine.index = .0f;
		data.data.sine.index_delta =
			sine_table_size / ((float) data.sample_rate / 4 / freq);
		data.data.sine.minus = false;
		data.type = 2;
	}
	//-------------------------------------------------------------------------
	// ● 向正弦表中填充数据
	//-------------------------------------------------------------------------
	void populate_sine_table() {
		float k = 0.5f / (float) sine_table_size * Util::PIf;
		for (size_t i = 0; i < sine_table_size; i++) sine_table[i] = sin(i * k);
	}
	//-------------------------------------------------------------------------
	// ● 计算正弦函数的下一值
	//-------------------------------------------------------------------------
	void get_next_sine_value(struct sine_data* data) {
		data->index += data->index_delta;
		if (data->index > (float) sine_table_size) {
			data->index = sine_table_size * 2.0f - data->index;
			data->index_delta = -data->index_delta;
		} else if (data->index < 0) {
			data->index = -data->index;
			data->index_delta = -data->index_delta;
			data->minus = !data->minus;
		}
		data->value = sine_table[(size_t) (int) data->index];
		if (data->minus) data->value = -data->value;
	}
	//-------------------------------------------------------------------------
	// ● 播放声音
	//-------------------------------------------------------------------------
	void play_sound(const char* filename) {
		compact_active_sounds_array();
		log("play sound %s", filename);
		struct active_sound* sound = new struct active_sound;
		sound->stream = NULL;
		// sound->file
		sound->file = fopen(filename, "rb");
		if (!sound->file) {
			delete sound;
			rb_raise(rb_eIOError, "can't open this file: %s", filename);
		}
		// sound->vf
		if (ov_open_callbacks(
			sound->file, &sound->vf,
			NULL, 0, OV_CALLBACKS_NOCLOSE
		) < 0) {
			delete sound;
			fclose(sound->file);
			rb_raise(rb_eRuntimeError, "can't open ogg vorbis file: %s", filename);
		}
		// sound->stream
		ensure_no_error(Pa_OpenDefaultStream(
			&sound->stream, 0, 2, paFloat32, 44100,
			256, play_sound_callback, sound
		));
		// sound->*_head
		sound->play_head = 0;
		sound->load_head = 0;
		// etc
		sound->eof = false;
		active_sounds[active_sound_count] = sound;
		active_sound_count++;
		decode_vorbis(sound);
		// sound->decode_thread
		sound->decode_thread = new thread(decode_vorbis_thread, sound);
		ensure_no_error(Pa_StartStream(sound->stream));
	}
	//-------------------------------------------------------------------------
	// ● 扔掉active_sounds中已经播放完的条目
	//-------------------------------------------------------------------------
	void compact_active_sounds_array() {
		size_t size = ARRAY_SIZE(active_sounds);
		for (size_t i = 0; i < size; i++) {
			if (!active_sounds[i]) continue;
			PaError active = Pa_IsStreamActive(active_sounds[i]->stream);
			ensure_no_error(active);
			if (!active) {
				Pa_CloseStream(active_sounds[i]->stream);
				ov_clear(&active_sounds[i]->vf);
				fclose(active_sounds[i]->file);
				active_sounds[i]->decode_thread->join();
				delete active_sounds[i]->decode_thread;
				delete active_sounds[i];
				active_sounds[i] = NULL;
			}
		}
	}
	//-------------------------------------------------------------------------
	// ● 播放声音的回调函数
	//-------------------------------------------------------------------------
	int play_sound_callback(
		const void* input_buffer UNUSED,
		void* output_buffer,
		unsigned long frame_count,
		const PaStreamCallbackTimeInfo* time_info UNUSED,
		PaStreamCallbackFlags status_flags UNUSED,
		void* user_data
	) {
		float* output = (float*) output_buffer;
		struct active_sound* sound = (struct active_sound*) user_data;
		while (frame_count > 0) {
			if (sound->play_head < sound->load_head) {
				size_t index = sound->play_head % vf_buffer_size;
				*output++ = sound->vf_buffer[0][index];
				*output++ = sound->vf_buffer[1][index];
				sound->play_head++;
			} else {
				TWICE *output++ = .0f;
			}
			frame_count--;
		}
		return sound->eof ? paComplete : paContinue;
	}
	//-------------------------------------------------------------------------
	// ● 解码文件来填active_sound结构中的缓冲区
	//-------------------------------------------------------------------------
	void decode_vorbis(struct active_sound* sound) {
		while (sound->load_head - sound->play_head < vf_buffer_size) {
			// After ov_read_float(), tmp_buffer will be changed.
			float** tmp_buffer;
			long ret = ov_read_float(
				&sound->vf,
				&tmp_buffer,
				vf_buffer_size,
				&sound->bitstream
			);
			if (ret > 0) {
				for (long i = 0; i < ret; i++) {
					size_t j = (sound->load_head + i) % vf_buffer_size;
					sound->vf_buffer[0][j] = tmp_buffer[0][i];
					sound->vf_buffer[1][j] = tmp_buffer[1][i];
				}
				sound->load_head += ret;
			} else if (ret == 0) {
				while (sound->load_head - sound->play_head < vf_buffer_size) {
					size_t j = sound->load_head % vf_buffer_size;
					sound->vf_buffer[0][j] = .0f;
					sound->vf_buffer[1][j] = .0f;
					sound->load_head++;
				}
				sound->eof = true;
				break;
			} else if (ret == OV_EBADLINK) {
				rb_raise(rb_eIOError, "bad vorbis data (OV_EBADLINK)");
			} else if (ret == OV_EINVAL) {
				rb_raise(rb_eIOError, "bad vorbis data (OV_EINVAL)");
			}
			// We must not free(tmp_buffer). It isn't ours.
		}
	}
	//-------------------------------------------------------------------------
	// ● 解码线程函数
	//-------------------------------------------------------------------------
	void decode_vorbis_thread(struct active_sound* sound) {
		while (!sound->eof) {
			decode_vorbis(sound);
		}
	}
}
<commit_msg>Audio.play_sound艹成了！<commit_after>//=============================================================================
// ■ audio.cpp
//-----------------------------------------------------------------------------
//   提供声音支持。
//=============================================================================

#include "global.hpp"

namespace Audio {
	//-------------------------------------------------------------------------
	// ● 全局变量
	//-------------------------------------------------------------------------
	const size_t vf_buffer_size = 4096;
	PaStream* wave_stream;
	struct callback_data data;
	struct active_sound* active_sounds[16] = {NULL};
	size_t active_sound_count = 0;
	// 为了避免反复计算，将正弦值存储在这里。其分布为
	// [0] = sin 0
	// [64] = sin ⅛π
	// [128] = sin ¼π
	// [192] = sin ⅜π
	float sine_table[256];
	const size_t sine_table_size = ARRAY_SIZE(sine_table);
	//-------------------------------------------------------------------------
	// ● 初始化
	//-------------------------------------------------------------------------
	void init() {
		ensure_no_error(Pa_Initialize());
		populate_sine_table();
		data.type = 0;
		// 44100Hz
		data.sample_rate = 44100.0d;
		ensure_no_error(Pa_OpenDefaultStream(
			&wave_stream,
			// 无声输入 - 立体声输出、32位浮点数
			0, 2, paFloat32,
			data.sample_rate,
			// 256格缓冲区
			256,
			play_callback, &data
		));
		ensure_no_error(Pa_StartStream(wave_stream));
	}
	//-------------------------------------------------------------------------
	// ● 释放
	//-------------------------------------------------------------------------
	void wobuzhidaozhegefangfayinggaijiaoshenmemingzi() {
		ensure_no_error(Pa_StopStream(wave_stream));
		ensure_no_error(Pa_CloseStream(wave_stream));
		Pa_Terminate();
	}
	//-------------------------------------------------------------------------
	// ● 统一处理错误
	//-------------------------------------------------------------------------
	void ensure_no_error(PaError err) {
		if (err >= 0) return;
		Pa_Terminate();
		rb_raise(
			rb_eRuntimeError,
			"PortAudio error %d: %s",
			err, Pa_GetErrorText(err)
		);
	}
	//-------------------------------------------------------------------------
	// ● 播放波形时使用的回调函数
	//-------------------------------------------------------------------------
	int play_callback(
		const void* input_buffer UNUSED,
		void* output_buffer,
		unsigned long frame_count,
		const PaStreamCallbackTimeInfo* time_info UNUSED,
		PaStreamCallbackFlags status_flags UNUSED,
		void* user_data
	) {
		float* output = (float*) output_buffer;
		struct callback_data* data = (struct callback_data*) user_data;
		// Magic. 吔屎啦PortAudio！
		#define FEED_AUDIO_DATA(value) do { \
			*output++ = (value); \
			*output++ = (value); \
			frame_count--; \
		} while (false)
		while (frame_count > 0) switch (data->type) {
			case 0:
				FEED_AUDIO_DATA(.0f);
				break;
			case 1:
				get_next_triangle_value(&data->data.triangle);
				FEED_AUDIO_DATA(data->data.triangle.value);
				break;
			case 2:
			default:
				get_next_sine_value(&data->data.sine);
				FEED_AUDIO_DATA(data->data.sine.value);
				break;
			case 3:
				*((float*) 0) = .0f; // 音频就是爆炸！
				FEED_AUDIO_DATA(.0f);
				break;
			case 4:
				FEED_AUDIO_DATA((float) (
					(double) rand() / (double) RAND_MAX * 2.0d - 1.0d
				));
				break;
		}
		#undef FEED_AUDIO_DATA
		return paContinue;
	}
	//-------------------------------------------------------------------------
	// ● 停止播放
	//-------------------------------------------------------------------------
	void stop() {
		data.type = 0;
	}
	//-------------------------------------------------------------------------
	// ● 播放三角波
	//    freq : 频率（Hz）
	//-------------------------------------------------------------------------
	void play_triangle(float freq) {
		data.data.triangle.value = -1.0f;
		data.data.triangle.delta = 2.0f / ((float) data.sample_rate / freq / 2);
		data.type = 1;
	}
	//-------------------------------------------------------------------------
	// ● 计算下一个值使输出呈三角波形
	//-------------------------------------------------------------------------
	void get_next_triangle_value(struct triangle_data* data) {
		data->value += data->delta;
		if (data->value > 1.0f) {
			data->value = 2.0f - data->value;
			data->delta = -data->delta;
		} else if (data->value < -1.0f) {
			data->value = -2.0f - data->value;
			data->delta = -data->delta;
		}
	}
	//-------------------------------------------------------------------------
	// ● 播放正弦波
	//    freq : 频率（Hz）
	//-------------------------------------------------------------------------
	void play_sine(float freq) {
		data.data.sine.index = .0f;
		data.data.sine.index_delta =
			sine_table_size / ((float) data.sample_rate / 4 / freq);
		data.data.sine.minus = false;
		data.type = 2;
	}
	//-------------------------------------------------------------------------
	// ● 向正弦表中填充数据
	//-------------------------------------------------------------------------
	void populate_sine_table() {
		float k = 0.5f / (float) sine_table_size * Util::PIf;
		for (size_t i = 0; i < sine_table_size; i++) sine_table[i] = sin(i * k);
	}
	//-------------------------------------------------------------------------
	// ● 计算正弦函数的下一值
	//-------------------------------------------------------------------------
	void get_next_sine_value(struct sine_data* data) {
		data->index += data->index_delta;
		if (data->index > (float) sine_table_size) {
			data->index = sine_table_size * 2.0f - data->index;
			data->index_delta = -data->index_delta;
		} else if (data->index < 0) {
			data->index = -data->index;
			data->index_delta = -data->index_delta;
			data->minus = !data->minus;
		}
		data->value = sine_table[(size_t) (int) data->index];
		if (data->minus) data->value = -data->value;
	}
	//-------------------------------------------------------------------------
	// ● 播放声音
	//-------------------------------------------------------------------------
	void play_sound(const char* filename) {
		compact_active_sounds_array();
		log("play sound %s", filename);
		struct active_sound* sound = new struct active_sound;
		sound->stream = NULL;
		// sound->file
		sound->file = fopen(filename, "rb");
		if (!sound->file) {
			delete sound;
			rb_raise(rb_eIOError, "can't open this file: %s", filename);
		}
		// sound->vf
		if (ov_open_callbacks(
			sound->file, &sound->vf,
			NULL, 0, OV_CALLBACKS_NOCLOSE
		) < 0) {
			delete sound;
			fclose(sound->file);
			rb_raise(rb_eRuntimeError, "can't open ogg vorbis file: %s", filename);
		}
		// sound->stream
		ensure_no_error(Pa_OpenDefaultStream(
			&sound->stream, 0, 2, paFloat32, 44100,
			256, play_sound_callback, sound
		));
		// sound->*_head
		sound->play_head = 0;
		sound->load_head = 0;
		// etc
		sound->eof = false;
		active_sounds[active_sound_count] = sound;
		active_sound_count++;
		decode_vorbis(sound);
		// sound->decode_thread
		sound->decode_thread = new thread(decode_vorbis_thread, sound);
		ensure_no_error(Pa_StartStream(sound->stream));
	}
	//-------------------------------------------------------------------------
	// ● 扔掉active_sounds中已经播放完的条目
	//-------------------------------------------------------------------------
	void compact_active_sounds_array() {
		size_t size = ARRAY_SIZE(active_sounds);
		for (size_t i = 0; i < size; i++) {
			if (!active_sounds[i]) continue;
			PaError active = Pa_IsStreamActive(active_sounds[i]->stream);
			ensure_no_error(active);
			if (!active) {
				Pa_CloseStream(active_sounds[i]->stream);
				ov_clear(&active_sounds[i]->vf);
				fclose(active_sounds[i]->file);
				active_sounds[i]->decode_thread->join();
				delete active_sounds[i]->decode_thread;
				delete active_sounds[i];
				active_sounds[i] = NULL;
			}
		}
	}
	//-------------------------------------------------------------------------
	// ● 播放声音的回调函数
	//-------------------------------------------------------------------------
	int play_sound_callback(
		const void* input_buffer UNUSED,
		void* output_buffer,
		unsigned long frame_count,
		const PaStreamCallbackTimeInfo* time_info UNUSED,
		PaStreamCallbackFlags status_flags UNUSED,
		void* user_data
	) {
		float* output = (float*) output_buffer;
		struct active_sound* sound = (struct active_sound*) user_data;
		while (frame_count > 0) {
			if (sound->play_head < sound->load_head) {
				size_t index = sound->play_head % vf_buffer_size;
				*output++ = sound->vf_buffer[0][index];
				*output++ = sound->vf_buffer[1][index];
				sound->play_head++;
			} else {
				TWICE *output++ = .0f;
			}
			frame_count--;
		}
		return sound->eof ? paComplete : paContinue;
	}
	//-------------------------------------------------------------------------
	// ● 解码文件来填active_sound结构中的缓冲区
	//-------------------------------------------------------------------------
	void decode_vorbis(struct active_sound* sound) {
		#ifdef DEBUG
			FILE* f = fopen("rubbish.raw", "wb");
		#endif
		size_t t;
		while ((t = sound->load_head - sound->play_head) < vf_buffer_size) {
			// After ov_read_float(), tmp_buffer will be changed.
			float** tmp_buffer;
			long ret = ov_read_float(
				&sound->vf,
				&tmp_buffer,
				vf_buffer_size - t,
				&sound->bitstream
			);
			if (ret > 0) {
				for (long i = 0; i < ret; i++) {
					size_t j = (sound->load_head + i) % vf_buffer_size;
					sound->vf_buffer[0][j] = tmp_buffer[0][i];
					sound->vf_buffer[1][j] = tmp_buffer[1][i];
					#ifdef DEBUG
						fprintf(f, "%ld %zu\r\n", i, j);
						fwrite(&tmp_buffer[0][i], sizeof(float), 1, f);
					#endif
				}
				sound->load_head += ret;
			} else if (ret == 0) {
				while (sound->load_head - sound->play_head < vf_buffer_size) {
					size_t j = sound->load_head % vf_buffer_size;
					sound->vf_buffer[0][j] = .0f;
					sound->vf_buffer[1][j] = .0f;
					sound->load_head++;
				}
				sound->eof = true;
				break;
			} else if (ret == OV_EBADLINK) {
				rb_raise(rb_eIOError, "bad vorbis data (OV_EBADLINK)");
			} else if (ret == OV_EINVAL) {
				rb_raise(rb_eIOError, "bad vorbis data (OV_EINVAL)");
			}
			// We must not free(tmp_buffer). It isn't ours.
		}
		#ifdef DEBUG
			fclose(f);
			exit(10);
		#endif
	}
	//-------------------------------------------------------------------------
	// ● 解码线程函数
	//-------------------------------------------------------------------------
	void decode_vorbis_thread(struct active_sound* sound) {
		while (!sound->eof) decode_vorbis(sound);
	}
}
<|endoftext|>


<commit_before>// RUN: %srcroot/test/runtime/run-scheduler-test.py %s -gxx "%gxx" -llvmgcc "%llvmgcc" -projbindir "%projbindir" -ternruntime "%ternruntime"  -ternbcruntime "%ternbcruntime"

#include <sys/time.h>
#include <time.h>
#include <errno.h>
#include <assert.h>
#include <pthread.h>
#include <semaphore.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>

#define N 100

pthread_mutex_t mu = PTHREAD_MUTEX_INITIALIZER;

struct timespec oldts;
struct timeval oldtv;
time_t oldtt;

//  require mu hold before calling this function
void check_time(bool init = false)
{
  struct timespec ts;
  struct timeval tv;
  time_t tt;
  gettimeofday(&tv, NULL);
  clock_gettime(CLOCK_REALTIME, &ts);
  time(&tt);

  if (!init)
  {
    if (tv.tv_sec < oldtv.tv_sec ||
      tv.tv_sec == oldtv.tv_sec && tv.tv_usec < oldtv.tv_usec)
      assert(0 && "gettimeofday is not monotonic");

    if (ts.tv_sec < oldts.tv_sec ||
      ts.tv_sec == oldts.tv_sec && ts.tv_nsec < oldts.tv_nsec)
      assert(0 && "clock_gettime is not monotonic");

    if (tt < oldtt)
      assert(0 && "time is not monotonic");
  }

  oldts = ts;
  oldtv = tv;
  oldtt = tt;
}

void* thread_func(void*) {
  for(unsigned i=0;i<100;++i)
    sched_yield();

  pthread_mutex_lock(&mu);

  check_time();

  pthread_mutex_unlock(&mu);
}

int main(int argc, char *argv[], char *env[]) {
  int ret;
  pthread_t th[N];

  check_time(true);
  for (int i = 0; i < N; ++i)
    pthread_create(&th[i], NULL, thread_func, NULL);

  for (int i = 0; i < N; ++i)
    pthread_join(th[i], NULL);

  printf("test done\n");

  return 0;
}

// CHECK indicates expected output checked by FileCheck; auto-generated by appending -gen to the RUN command above.
// CHECK:      test done
<commit_msg>add sleep:w into sleep-gettime-test.cpp<commit_after>// RUN: %srcroot/test/runtime/run-scheduler-test.py %s -gxx "%gxx" -llvmgcc "%llvmgcc" -projbindir "%projbindir" -ternruntime "%ternruntime"  -ternbcruntime "%ternbcruntime"

#include <sys/time.h>
#include <time.h>
#include <errno.h>
#include <assert.h>
#include <pthread.h>
#include <semaphore.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>

#define N 100

pthread_mutex_t mu = PTHREAD_MUTEX_INITIALIZER;

struct timespec oldts;
struct timeval oldtv;
time_t oldtt;

//  require mu hold before calling this function
void check_time(bool init = false)
{
  struct timespec ts;
  struct timeval tv;
  time_t tt;
  gettimeofday(&tv, NULL);
  clock_gettime(CLOCK_REALTIME, &ts);
  time(&tt);

  if (!init)
  {
    if (tv.tv_sec < oldtv.tv_sec ||
      tv.tv_sec == oldtv.tv_sec && tv.tv_usec < oldtv.tv_usec)
      assert(0 && "gettimeofday is not monotonic");

    if (ts.tv_sec < oldts.tv_sec ||
      ts.tv_sec == oldts.tv_sec && ts.tv_nsec < oldts.tv_nsec)
      assert(0 && "clock_gettime is not monotonic");

    if (tt < oldtt)
      assert(0 && "time is not monotonic");
  }

  oldts = ts;
  oldtv = tv;
  oldtt = tt;
}

void* thread_func(void*) {
  for(unsigned i=0;i<100;++i)
    sched_yield();

  pthread_mutex_lock(&mu);

  check_time();
  usleep(10);

  pthread_mutex_unlock(&mu);
}

int main(int argc, char *argv[], char *env[]) {
  int ret;
  pthread_t th[N];

  check_time(true);
  for (int i = 0; i < N; ++i)
    pthread_create(&th[i], NULL, thread_func, NULL);

  for (int i = 0; i < N; ++i)
    pthread_join(th[i], NULL);

  printf("test done\n");

  return 0;
}

// CHECK indicates expected output checked by FileCheck; auto-generated by appending -gen to the RUN command above.
// CHECK:      test done
<|endoftext|>