<commit_before>#include "static_assert.h"
#include "nodemurmurhash.h"
#include "MurmurHash2.h"
#include "PMurHash.h"
#include "PMurHash128.h"
#include "murmurhashutils.h"
#include "inputdata.h"
#include "asyncworker.h"

NAN_INLINE void MurmurHash2_x64_64 (
    const void * key, int len, uint32_t seed, void * out)
{
  *(uint64_t *)out = MurmurHash64A( key, len, (uint64_t) seed );
}

NAN_INLINE void MurmurHash2_x86_64 (
    const void * key, int len, uint32_t seed, void * out)
{
  *(uint64_t *)out = MurmurHash64B( key, len, (uint64_t) seed );
}

namespace MurmurHash {
  using v8::Object;
  using v8::Uint32;
  using v8::Function;
  using v8::PropertyAttribute;
  using v8::ReadOnly;
  using v8::DontDelete;

  #define GET_ARG_OFFSET(INFO,INDEX,ARGC)                        \
            ((INDEX) + 1 < (ARGC)                                \
            ? Nan::To<int32_t>((INFO)[(INDEX) + 1]).FromMaybe(0) \
            : 0)

  #define GET_ARG_LENGTH(INFO,INDEX,ARGC,DEF)                      \
            ((INDEX) + 2 < (ARGC)                                  \
            ? Nan::To<int32_t>((INFO)[(INDEX) + 2]).FromMaybe(DEF) \
            : (DEF))

  /**
   * Calculate MurmurHash from data
   *
   * murmurHash(data[, callback])
   * murmurHash(data, output[, offset[, length]][, callback])
   * murmurHash(data{String}, encoding|output_type[, callback])
   * murmurHash(data, output_type[, seed][, callback])
   * murmurHash(data, seed[, output[, offset[, length]]][, callback])
   * murmurHash(data, seed[, output_type][, callback])
   * murmurHash(data, encoding, output_type[, callback])
   * murmurHash(data{String}, encoding, output[, offset[, length]][, callback])
   * murmurHash(data{String}, encoding, seed[, output[, offset[, length]]][, callback])
   * murmurHash(data{String}, encoding, seed[, output_type][, callback])
   *
   * @param {string|Buffer} data - a byte-string to calculate hash from
   * @param {string} encoding - data string encoding, should be:
   *       'utf8', 'ucs2', 'ascii', 'hex', 'base64' or 'binary';
   *       'binary' by default
   * @param {Uint32} seed - murmur hash seed, 0 by default
   * @param {Buffer} output - a Buffer object to write hash bytes to;
   *       the same object will be returned
   * @param {number} offset - start writing into output at offset byte;
   *       negative offset starts from the end of the output buffer
   * @param {number} length - a number of bytes to write from calculated hash;
   *       negative length starts from the end of the hash;
   *       if absolute value of length is larger than the size of calculated
   *       hash, bytes are written only up to the hash size
   * @param {string} output_type - a string indicating return type:
   *       'number' - for murmurHash32 an unsigned 32-bit integer,
   *                  other hashes - hexadecimal string
   *       'hex'    - hexadecimal string
   *       'base64' - base64 string
   *       'binary' - binary string
   *       'buffer' - a new Buffer object;
   *       'number' by default
   * @param {Function} callback - optional callback(err, result)
   *       if provided the hash will be calculated asynchronously using libuv
   *       worker queue, the return value in this instance will be `undefined`
   *       and the result will be provided to the callback function;
   *       Be carefull as reading and writing by multiple threads to the same
   *       memory may render undetermined results
   *
   * The order of bytes written to a Buffer or encoded string depends on
   * function's endianness.
   *
   * `data` and `output` arguments might reference the same Buffer object
   * or buffers referencing the same memory (views).
   *
   * @return {number|Buffer|String|undefined}
  **/
  template<MurmurHashFunctionType HashFunction, typename HashValueType, int32_t HashLength, ByteOrderType OutputByteOrder>
  NAN_METHOD(MurmurHash)
  {
    STATIC_ASSERT((HashSize & 0x1c) && (HashSize|(HashSize-1))+1 == HashSize<<1, "HashSize is not 4, 8 or 16");

    InputData data;

    OutputType outputType( DefaultOutputType );

    uint32_t seed = 0;

    /* parse args */
    int argc = std::min( 7, info.Length() );
    int outputTypeIndex = argc;
    int callbackIndex = -1;
    bool validEncoding = true;
    enum Nan::Encoding encoding = Nan::BUFFER;

    if ( argc > 0 && info[argc - 1]->IsFunction() ) {
      callbackIndex = --argc;
    }

    if ( argc > 0 ) {

      if ( info[0]->IsString() ) {
        encoding = Nan::BINARY;
      }

      if ( argc >= 2 ) {

        if ( info[1]->IsString() ) { // input_encoding or output_type
          if ( argc == 2 ) { // try output_type
            InputData::ReadEncodingString( info[1].As<String>() );
            outputType = InputData::DetermineOutputType();
            switch(outputType) {
              case HexStringOutputType:
              case BinaryStringOutputType:
              case Base64StringOutputType:
                // ambiguous if input is string
              case UnknownOutputType: // input_encoding
                if (encoding != Nan::BUFFER) {
                  validEncoding = InputData::DetermineEncoding( encoding );
                  outputType = DefaultOutputType; // revert to default
                }
                break;
              default:
                void(0); // unambiguous - "number" or "buffer"
            }
          } else if (encoding == Nan::BUFFER) { // output_type
            if ( info[2]->IsNumber() ) {
              InputData::ReadEncodingString( info[1].As<String>() );
              seed = Nan::To<uint32_t>(info[2]).FromMaybe(0U);
            } else if ( info[2]->IsString() ) {
              InputData::ReadEncodingString( info[2].As<String>() );
            } else {
              InputData::ReadEncodingString( info[1].As<String>() );
            }
            outputType = InputData::DetermineOutputType();
          } else { // try input_encoding
            InputData::ReadEncodingString( info[1].As<String>() );
            if ( !(validEncoding = InputData::DetermineEncoding( encoding )) ) {
              outputType = InputData::DetermineOutputType(); // try output_type
              if (outputType == UnknownOutputType) {
                outputType = DefaultOutputType;
              } else {
                validEncoding = true;
              if ( info[2]->IsNumber() ) seed = Nan::To<uint32_t>(info[2]).FromMaybe(0U);
              }
            }
            outputTypeIndex = 2; // continue from 2
          }
        } else {
          // output or seed
          if ( node::Buffer::HasInstance(info[1]) ) {
            outputType = ProvidedBufferOutputType;
            outputTypeIndex = 1;
          } else {
            if ( info[1]->IsNumber() ) seed = Nan::To<uint32_t>(info[1]).FromMaybe(0U);
            outputTypeIndex = 2; // continue from 2
          }
        }
        if ( outputType == DefaultOutputType ) { // output_type or output or seed
          for (; outputTypeIndex < argc; ++outputTypeIndex ) {
            if ( info[outputTypeIndex]->IsNumber() ) {
              seed = Nan::To<uint32_t>(info[outputTypeIndex]).FromMaybe(0U);
            } else if ( info[outputTypeIndex]->IsString() ) {
              InputData::ReadEncodingString( info[outputTypeIndex].As<String>() );
              outputType = InputData::DetermineOutputType();
              break;
            } else if ( node::Buffer::HasInstance(info[outputTypeIndex]) ) {
              outputType = ProvidedBufferOutputType;
              break;
            } else
              break;
          }
        }

      }

    }

    if ( callbackIndex > -1 ) {
      MurmurHashWorker<HashFunction,HashValueType,HashLength,OutputByteOrder> *asyncWorker;
      Nan::Callback *callback = new Nan::Callback(
                                  Local<Function>::Cast(info[callbackIndex]));

      if ( argc > 0 ) {
        asyncWorker = new MurmurHashWorker<HashFunction,HashValueType,HashLength,OutputByteOrder>(
          callback, outputType, seed, info[0], encoding, validEncoding);
      } else {
        asyncWorker = new MurmurHashWorker<HashFunction,HashValueType,HashLength,OutputByteOrder>(callback);
      }

      if (outputType == ProvidedBufferOutputType) {
        asyncWorker->SaveOutputBuffer(
            info[outputTypeIndex],
            GET_ARG_OFFSET(info, outputTypeIndex, argc),
            GET_ARG_LENGTH(info, outputTypeIndex, argc, HashSize));
      }

      Nan::AsyncQueueWorker(asyncWorker);

      info.GetReturnValue().Set(Nan::Undefined());

    } else {

      if ( argc > 0 ) {
        data.Setup( info[0], encoding, validEncoding );
      }

      if ( ! data.IsValid() )
        return Nan::ThrowTypeError(data.Error());

      Local<Value> result;

      HashValueType hash[HashLength];

      HashFunction( (const void *) *data, (int) data.length(), seed, (void *)hash );

      switch(outputType) {
        case DefaultOutputType:
        case NumberOutputType:
          if (HashSize == sizeof(uint32_t)) {
            result = Nan::New<Uint32>( (uint32_t) (*hash) );
          } else {
            result = HashToEncodedString<OutputByteOrder, HashLength>( hash, Nan::HEX );
          }
          break;

        case HexStringOutputType:
          result = HashToEncodedString<OutputByteOrder, HashLength>( hash, Nan::HEX );
          break;

        case BinaryStringOutputType:
          result = HashToEncodedString<OutputByteOrder, HashLength>( hash, Nan::BINARY );
          break;

        case Base64StringOutputType:
          result = HashToEncodedString<OutputByteOrder, HashLength>( hash, Nan::BASE64 );
          break;

        case BufferOutputType:
          result = Nan::NewBuffer( HashSize ).ToLocalChecked();
          WriteHashBytes<OutputByteOrder, HashLength>(hash, (uint8_t *) node::Buffer::Data(result));
          break;

        case ProvidedBufferOutputType:
          result = info[outputTypeIndex];
          WriteHashToBuffer<OutputByteOrder, HashLength>(
                hash,
                node::Buffer::Data(result),
                (int32_t) node::Buffer::Length(result),
                GET_ARG_OFFSET(info, outputTypeIndex, argc),
                GET_ARG_LENGTH(info, outputTypeIndex, argc, HashSize));
          break;

        default:
          return Nan::ThrowTypeError("Unknown output type: should be \"number\", \"buffer\", \"binary\", \"base64\" or \"hex\"");
      }

      info.GetReturnValue().Set(result);
    }
  }

  #undef GET_ARG_OFFSET
  #undef GET_ARG_LENGTH

  template<ByteOrderType OutputByteOrder>
  NAN_MODULE_INIT(InitWithOrder)
  {
    Nan::SetMethod(target, "murmurHash",       MurmurHash<        PMurHash32, uint32_t, 1, OutputByteOrder>);
    Nan::SetMethod(target, "murmurHash32",     MurmurHash<        PMurHash32, uint32_t, 1, OutputByteOrder>);
    Nan::SetMethod(target, "murmurHash64x64",  MurmurHash<MurmurHash2_x64_64, uint64_t, 1, OutputByteOrder>);
    Nan::SetMethod(target, "murmurHash64x86",  MurmurHash<MurmurHash2_x86_64, uint64_t, 1, OutputByteOrder>);
    Nan::SetMethod(target, "murmurHash128x64", MurmurHash<    PMurHash128x64, uint64_t, 2, OutputByteOrder>);
    Nan::SetMethod(target, "murmurHash128x86", MurmurHash<    PMurHash128x86, uint32_t, 4, OutputByteOrder>);
  #if defined(NODE_MURMURHASH_DEFAULT_32BIT)
    Nan::SetMethod(target, "murmurHash64",     MurmurHash<MurmurHash2_x86_64, uint64_t, 1, OutputByteOrder>);
    Nan::SetMethod(target, "murmurHash128",    MurmurHash<    PMurHash128x86, uint32_t, 4, OutputByteOrder>);
  #else
    Nan::SetMethod(target, "murmurHash64",     MurmurHash<MurmurHash2_x64_64, uint64_t, 1, OutputByteOrder>);
    Nan::SetMethod(target, "murmurHash128",    MurmurHash<    PMurHash128x64, uint64_t, 2, OutputByteOrder>);
  #endif
  }

  NAN_MODULE_INIT(Init)
  {
    InitWithOrder<MSBFirst>( target );

    Local<Object> bigEndian( Nan::New<Object>() );
    InitWithOrder<MSBFirst>( bigEndian );
    Nan::ForceSet(target, Nan::New<String>("BE").ToLocalChecked(), bigEndian,
                        static_cast<PropertyAttribute>(ReadOnly | DontDelete) ).FromJust();

    Local<Object> littleEndian( Nan::New<Object>() );
    InitWithOrder<LSBFirst>( littleEndian );
    Nan::ForceSet(target, Nan::New<String>("LE").ToLocalChecked(), littleEndian,
                        static_cast<PropertyAttribute>(ReadOnly | DontDelete) ).FromJust();

    Nan::ForceSet(target, Nan::New<String>("platform").ToLocalChecked(),
                  IsBigEndian() ? bigEndian : littleEndian,
                  static_cast<PropertyAttribute>(ReadOnly | DontDelete) ).FromJust();
  }

}

NODE_MODULE(murmurhash, MurmurHash::Init)
<commit_msg>replace deprecated Nan::ForceSet with Nan::DefineOwnProperty<commit_after>#include "static_assert.h"
#include "nodemurmurhash.h"
#include "MurmurHash2.h"
#include "PMurHash.h"
#include "PMurHash128.h"
#include "murmurhashutils.h"
#include "inputdata.h"
#include "asyncworker.h"

NAN_INLINE void MurmurHash2_x64_64 (
    const void * key, int len, uint32_t seed, void * out)
{
  *(uint64_t *)out = MurmurHash64A( key, len, (uint64_t) seed );
}

NAN_INLINE void MurmurHash2_x86_64 (
    const void * key, int len, uint32_t seed, void * out)
{
  *(uint64_t *)out = MurmurHash64B( key, len, (uint64_t) seed );
}

namespace MurmurHash {
  using v8::Object;
  using v8::Uint32;
  using v8::Function;
  using v8::PropertyAttribute;
  using v8::ReadOnly;
  using v8::DontDelete;

  #define GET_ARG_OFFSET(INFO,INDEX,ARGC)                        \
            ((INDEX) + 1 < (ARGC)                                \
            ? Nan::To<int32_t>((INFO)[(INDEX) + 1]).FromMaybe(0) \
            : 0)

  #define GET_ARG_LENGTH(INFO,INDEX,ARGC,DEF)                      \
            ((INDEX) + 2 < (ARGC)                                  \
            ? Nan::To<int32_t>((INFO)[(INDEX) + 2]).FromMaybe(DEF) \
            : (DEF))

  /**
   * Calculate MurmurHash from data
   *
   * murmurHash(data[, callback])
   * murmurHash(data, output[, offset[, length]][, callback])
   * murmurHash(data{String}, encoding|output_type[, callback])
   * murmurHash(data, output_type[, seed][, callback])
   * murmurHash(data, seed[, output[, offset[, length]]][, callback])
   * murmurHash(data, seed[, output_type][, callback])
   * murmurHash(data, encoding, output_type[, callback])
   * murmurHash(data{String}, encoding, output[, offset[, length]][, callback])
   * murmurHash(data{String}, encoding, seed[, output[, offset[, length]]][, callback])
   * murmurHash(data{String}, encoding, seed[, output_type][, callback])
   *
   * @param {string|Buffer} data - a byte-string to calculate hash from
   * @param {string} encoding - data string encoding, should be:
   *       'utf8', 'ucs2', 'ascii', 'hex', 'base64' or 'binary';
   *       'binary' by default
   * @param {Uint32} seed - murmur hash seed, 0 by default
   * @param {Buffer} output - a Buffer object to write hash bytes to;
   *       the same object will be returned
   * @param {number} offset - start writing into output at offset byte;
   *       negative offset starts from the end of the output buffer
   * @param {number} length - a number of bytes to write from calculated hash;
   *       negative length starts from the end of the hash;
   *       if absolute value of length is larger than the size of calculated
   *       hash, bytes are written only up to the hash size
   * @param {string} output_type - a string indicating return type:
   *       'number' - for murmurHash32 an unsigned 32-bit integer,
   *                  other hashes - hexadecimal string
   *       'hex'    - hexadecimal string
   *       'base64' - base64 string
   *       'binary' - binary string
   *       'buffer' - a new Buffer object;
   *       'number' by default
   * @param {Function} callback - optional callback(err, result)
   *       if provided the hash will be calculated asynchronously using libuv
   *       worker queue, the return value in this instance will be `undefined`
   *       and the result will be provided to the callback function;
   *       Be carefull as reading and writing by multiple threads to the same
   *       memory may render undetermined results
   *
   * The order of bytes written to a Buffer or encoded string depends on
   * function's endianness.
   *
   * `data` and `output` arguments might reference the same Buffer object
   * or buffers referencing the same memory (views).
   *
   * @return {number|Buffer|String|undefined}
  **/
  template<MurmurHashFunctionType HashFunction, typename HashValueType, int32_t HashLength, ByteOrderType OutputByteOrder>
  NAN_METHOD(MurmurHash)
  {
    STATIC_ASSERT((HashSize & 0x1c) && (HashSize|(HashSize-1))+1 == HashSize<<1, "HashSize is not 4, 8 or 16");

    InputData data;

    OutputType outputType( DefaultOutputType );

    uint32_t seed = 0;

    /* parse args */
    int argc = std::min( 7, info.Length() );
    int outputTypeIndex = argc;
    int callbackIndex = -1;
    bool validEncoding = true;
    enum Nan::Encoding encoding = Nan::BUFFER;

    if ( argc > 0 && info[argc - 1]->IsFunction() ) {
      callbackIndex = --argc;
    }

    if ( argc > 0 ) {

      if ( info[0]->IsString() ) {
        encoding = Nan::BINARY;
      }

      if ( argc >= 2 ) {

        if ( info[1]->IsString() ) { // input_encoding or output_type
          if ( argc == 2 ) { // try output_type
            InputData::ReadEncodingString( info[1].As<String>() );
            outputType = InputData::DetermineOutputType();
            switch(outputType) {
              case HexStringOutputType:
              case BinaryStringOutputType:
              case Base64StringOutputType:
                // ambiguous if input is string
              case UnknownOutputType: // input_encoding
                if (encoding != Nan::BUFFER) {
                  validEncoding = InputData::DetermineEncoding( encoding );
                  outputType = DefaultOutputType; // revert to default
                }
                break;
              default:
                void(0); // unambiguous - "number" or "buffer"
            }
          } else if (encoding == Nan::BUFFER) { // output_type
            if ( info[2]->IsNumber() ) {
              InputData::ReadEncodingString( info[1].As<String>() );
              seed = Nan::To<uint32_t>(info[2]).FromMaybe(0U);
            } else if ( info[2]->IsString() ) {
              InputData::ReadEncodingString( info[2].As<String>() );
            } else {
              InputData::ReadEncodingString( info[1].As<String>() );
            }
            outputType = InputData::DetermineOutputType();
          } else { // try input_encoding
            InputData::ReadEncodingString( info[1].As<String>() );
            if ( !(validEncoding = InputData::DetermineEncoding( encoding )) ) {
              outputType = InputData::DetermineOutputType(); // try output_type
              if (outputType == UnknownOutputType) {
                outputType = DefaultOutputType;
              } else {
                validEncoding = true;
              if ( info[2]->IsNumber() ) seed = Nan::To<uint32_t>(info[2]).FromMaybe(0U);
              }
            }
            outputTypeIndex = 2; // continue from 2
          }
        } else {
          // output or seed
          if ( node::Buffer::HasInstance(info[1]) ) {
            outputType = ProvidedBufferOutputType;
            outputTypeIndex = 1;
          } else {
            if ( info[1]->IsNumber() ) seed = Nan::To<uint32_t>(info[1]).FromMaybe(0U);
            outputTypeIndex = 2; // continue from 2
          }
        }
        if ( outputType == DefaultOutputType ) { // output_type or output or seed
          for (; outputTypeIndex < argc; ++outputTypeIndex ) {
            if ( info[outputTypeIndex]->IsNumber() ) {
              seed = Nan::To<uint32_t>(info[outputTypeIndex]).FromMaybe(0U);
            } else if ( info[outputTypeIndex]->IsString() ) {
              InputData::ReadEncodingString( info[outputTypeIndex].As<String>() );
              outputType = InputData::DetermineOutputType();
              break;
            } else if ( node::Buffer::HasInstance(info[outputTypeIndex]) ) {
              outputType = ProvidedBufferOutputType;
              break;
            } else
              break;
          }
        }

      }

    }

    if ( callbackIndex > -1 ) {
      MurmurHashWorker<HashFunction,HashValueType,HashLength,OutputByteOrder> *asyncWorker;
      Nan::Callback *callback = new Nan::Callback(
                                  Local<Function>::Cast(info[callbackIndex]));

      if ( argc > 0 ) {
        asyncWorker = new MurmurHashWorker<HashFunction,HashValueType,HashLength,OutputByteOrder>(
          callback, outputType, seed, info[0], encoding, validEncoding);
      } else {
        asyncWorker = new MurmurHashWorker<HashFunction,HashValueType,HashLength,OutputByteOrder>(callback);
      }

      if (outputType == ProvidedBufferOutputType) {
        asyncWorker->SaveOutputBuffer(
            info[outputTypeIndex],
            GET_ARG_OFFSET(info, outputTypeIndex, argc),
            GET_ARG_LENGTH(info, outputTypeIndex, argc, HashSize));
      }

      Nan::AsyncQueueWorker(asyncWorker);

      info.GetReturnValue().Set(Nan::Undefined());

    } else {

      if ( argc > 0 ) {
        data.Setup( info[0], encoding, validEncoding );
      }

      if ( ! data.IsValid() )
        return Nan::ThrowTypeError(data.Error());

      Local<Value> result;

      HashValueType hash[HashLength];

      HashFunction( (const void *) *data, (int) data.length(), seed, (void *)hash );

      switch(outputType) {
        case DefaultOutputType:
        case NumberOutputType:
          if (HashSize == sizeof(uint32_t)) {
            result = Nan::New<Uint32>( (uint32_t) (*hash) );
          } else {
            result = HashToEncodedString<OutputByteOrder, HashLength>( hash, Nan::HEX );
          }
          break;

        case HexStringOutputType:
          result = HashToEncodedString<OutputByteOrder, HashLength>( hash, Nan::HEX );
          break;

        case BinaryStringOutputType:
          result = HashToEncodedString<OutputByteOrder, HashLength>( hash, Nan::BINARY );
          break;

        case Base64StringOutputType:
          result = HashToEncodedString<OutputByteOrder, HashLength>( hash, Nan::BASE64 );
          break;

        case BufferOutputType:
          result = Nan::NewBuffer( HashSize ).ToLocalChecked();
          WriteHashBytes<OutputByteOrder, HashLength>(hash, (uint8_t *) node::Buffer::Data(result));
          break;

        case ProvidedBufferOutputType:
          result = info[outputTypeIndex];
          WriteHashToBuffer<OutputByteOrder, HashLength>(
                hash,
                node::Buffer::Data(result),
                (int32_t) node::Buffer::Length(result),
                GET_ARG_OFFSET(info, outputTypeIndex, argc),
                GET_ARG_LENGTH(info, outputTypeIndex, argc, HashSize));
          break;

        default:
          return Nan::ThrowTypeError("Unknown output type: should be \"number\", \"buffer\", \"binary\", \"base64\" or \"hex\"");
      }

      info.GetReturnValue().Set(result);
    }
  }

  #undef GET_ARG_OFFSET
  #undef GET_ARG_LENGTH

  template<ByteOrderType OutputByteOrder>
  NAN_MODULE_INIT(InitWithOrder)
  {
    Nan::SetMethod(target, "murmurHash",       MurmurHash<        PMurHash32, uint32_t, 1, OutputByteOrder>);
    Nan::SetMethod(target, "murmurHash32",     MurmurHash<        PMurHash32, uint32_t, 1, OutputByteOrder>);
    Nan::SetMethod(target, "murmurHash64x64",  MurmurHash<MurmurHash2_x64_64, uint64_t, 1, OutputByteOrder>);
    Nan::SetMethod(target, "murmurHash64x86",  MurmurHash<MurmurHash2_x86_64, uint64_t, 1, OutputByteOrder>);
    Nan::SetMethod(target, "murmurHash128x64", MurmurHash<    PMurHash128x64, uint64_t, 2, OutputByteOrder>);
    Nan::SetMethod(target, "murmurHash128x86", MurmurHash<    PMurHash128x86, uint32_t, 4, OutputByteOrder>);
  #if defined(NODE_MURMURHASH_DEFAULT_32BIT)
    Nan::SetMethod(target, "murmurHash64",     MurmurHash<MurmurHash2_x86_64, uint64_t, 1, OutputByteOrder>);
    Nan::SetMethod(target, "murmurHash128",    MurmurHash<    PMurHash128x86, uint32_t, 4, OutputByteOrder>);
  #else
    Nan::SetMethod(target, "murmurHash64",     MurmurHash<MurmurHash2_x64_64, uint64_t, 1, OutputByteOrder>);
    Nan::SetMethod(target, "murmurHash128",    MurmurHash<    PMurHash128x64, uint64_t, 2, OutputByteOrder>);
  #endif
  }

  NAN_MODULE_INIT(Init)
  {
    InitWithOrder<MSBFirst>( target );

    Local<Object> bigEndian( Nan::New<Object>() );
    InitWithOrder<MSBFirst>( bigEndian );
    Nan::DefineOwnProperty(target, Nan::New<String>("BE").ToLocalChecked(), bigEndian,
                        static_cast<PropertyAttribute>(ReadOnly | DontDelete) ).FromJust();

    Local<Object> littleEndian( Nan::New<Object>() );
    InitWithOrder<LSBFirst>( littleEndian );
    Nan::DefineOwnProperty(target, Nan::New<String>("LE").ToLocalChecked(), littleEndian,
                        static_cast<PropertyAttribute>(ReadOnly | DontDelete) ).FromJust();

    Nan::DefineOwnProperty(target, Nan::New<String>("platform").ToLocalChecked(),
                  IsBigEndian() ? bigEndian : littleEndian,
                  static_cast<PropertyAttribute>(ReadOnly | DontDelete) ).FromJust();
  }

}

NODE_MODULE(murmurhash, MurmurHash::Init)
<|endoftext|>


<commit_before>
#include "afgreader/src/reader.h"
#include "graph/edges_set.cpp"
#include <fstream>
#include <iostream>
#include <memory>

using std::shared_ptr;
using AMOS::Overlap;
using AMOS::Read;
using AMOS::Reader;
using Graph::EdgesSet;

const double EPSILON = 0.15;
const double ALPHA = 3;

bool compare_edges(const pair<int, shared_ptr<Overlap>>& lhs, const pair<int, shared_ptr<Overlap>>& rhs) {
  return lhs.first < rhs.first;
}

inline bool eq(double x, double y, double eps) {
  return y <= x + eps && x <= y + eps;
}

// tests if o1 is transitive edge considering o2 and o3.
bool is_transitive(const Overlap& o1, const Overlap& o2, const Overlap& o3) {
  auto A = o1.a_id;
  auto B = o1.b_id;
  auto C = o2.a_id;
  if (C == A) {
    C = o2.b_id;
  }
  if (o2.use_suffix(C) == o3.use_suffix(C)) {
    return false;
  }
  if (o1.use_suffix(A) != o2.use_suffix(A)) {
    return false;
  }
  if (o1.use_suffix(B) != o3.use_suffix(B)) {
    return false;
  }
  if (!eq(
        o2.hanging_length(A) + o3.hanging_length(C),
        o1.hanging_length(A),
        EPSILON * o1.length() + ALPHA)) {
    return false;
  }
  if (!eq(
        o2.hanging_length(C) + o3.hanging_length(B),
        o1.hanging_length(B),
        EPSILON * o1.length() + ALPHA)) {
    return false;
  }
  return true;
}

int get_non_transitives(vector<shared_ptr<Overlap>>* dst_container, vector<shared_ptr<Overlap>>& edges_list) {
  int valid = 0;

  // create that data structure to allow fast access to all edges coincident
  // with particular node.
  EdgesSet<shared_ptr<Overlap>> edges_set;
  for (const auto& edge : edges_list) {
    edges_set.add(edge->a_id, edge->b_id, edge);
    edges_set.add(edge->b_id, edge->a_id, edge);
  }

  // sort so we can reduce complexity on iterating edges.
  for (auto unsorted : edges_set) {
    unsorted.second.sort();
  }

  // we are iterating all (x, y), (x, a), (y, a) in hope that we will find that
  // (x,y) is transitive edge when considering (x, a) and (y, a).
  // x -> y, x -> a -> y; x -> y can be information overhead.
  for (const auto& overlap : edges_list) {
    auto& src = overlap->a_id;
    auto& dst = overlap->b_id;

    auto& v1 = edges_set[src], v2 = edges_set[dst];
    auto it1 = v1.begin(), it2 = v2.begin();

    bool transitive = false;
    while (!transitive && it1 != v1.end() && it2 != v2.end()) {
      if (it1->first == overlap->a_id || it1->first == overlap->b_id) {
        ++it1;
        continue;
      }
      if (it2->first == overlap->a_id || it2->first == overlap->b_id) {
        ++it2;
        continue;
      }
      if (it1->first == it2->first) {
        if (is_transitive(*overlap, *it1->second, *it2->second)) {
          transitive = true;
        }
        ++it1;
        ++it2;
      } else if (it1->first < it2->first) {
        ++it1;
      } else {
        ++it2;
      }
    }

    if (!transitive) {
      dst_container->emplace_back(overlap);
    }
  }

  return valid;
}

void fill_reads(vector<shared_ptr<Overlap>>& overlaps, map<uint32_t, shared_ptr<Read>>& reads) {
  for (auto o : overlaps) {
    o->a = reads[o->a_id];
    o->b = reads[o->b_id];
  }
}

int read_from_stream(istream& input, map<uint32_t, shared_ptr<Read>>& reads, vector<shared_ptr<Overlap>>& edges) {
  Reader reader(input);

  int stored = 0;
  while (reader.has_next()) {
    if (reader.next_type() == AMOS::OVERLAP) {
      Overlap* overlap = new Overlap();
      if (!reader.next(overlap)) {
        cerr << "Error while reading overlap" << endl;
        continue;
      }

      edges.emplace_back(shared_ptr<Overlap>(overlap));
      stored++;
    } else if (reader.next_type() == AMOS::READ) {
      Read *read = new Read();
      if (!reader.next(read)) {
        cerr << "Error while reading overlap" << endl;
        continue;
      }

      reads[read->iid] = shared_ptr<Read>(read);
      stored++;
    } else {
      reader.skip_next();
      continue;
    }
  }

  return stored;
}

int main(int argc, char **argv) {
  vector<istream*> input_streams;

  if (argc == 2) {
    input_streams.emplace_back(&cin);
  } else {
    for (int i = 1; i < argc; ++i) {
      istream* in_stream;
      if (strcmp(argv[i], "-") == 0) {
        in_stream = &cin;
      } else {
        in_stream = new ifstream(argv[i]);
      }

      input_streams.emplace_back(&cin);
    }
  }

  vector<shared_ptr<Overlap>> overlaps;
  map<uint32_t, shared_ptr<Read>> reads;

  for (auto in_stream : input_streams) {
    read_from_stream(*in_stream, reads, overlaps);
  }

  cerr << "Read " << overlaps.size() << " reads" << endl;
  cerr << "Read " << reads.size() << " overlaps" << endl;

  fill_reads(overlaps, reads);

  vector<shared_ptr<Overlap>> non_transitive_edges;
  get_non_transitives(&non_transitive_edges, overlaps);

  cerr << "Filtered " << (overlaps.size() - non_transitive_edges.size()) << " overlaps" << endl;
}
<commit_msg>add dependency<commit_after>
#include "afgreader/src/reader.h"
#include "graph/edges_set.cpp"
#include <cstring>
#include <fstream>
#include <iostream>
#include <memory>

using std::shared_ptr;
using AMOS::Overlap;
using AMOS::Read;
using AMOS::Reader;
using Graph::EdgesSet;

const double EPSILON = 0.15;
const double ALPHA = 3;

bool compare_edges(const pair<int, shared_ptr<Overlap>>& lhs, const pair<int, shared_ptr<Overlap>>& rhs) {
  return lhs.first < rhs.first;
}

inline bool eq(double x, double y, double eps) {
  return y <= x + eps && x <= y + eps;
}

// tests if o1 is transitive edge considering o2 and o3.
bool is_transitive(const Overlap& o1, const Overlap& o2, const Overlap& o3) {
  auto A = o1.a_id;
  auto B = o1.b_id;
  auto C = o2.a_id;
  if (C == A) {
    C = o2.b_id;
  }
  if (o2.use_suffix(C) == o3.use_suffix(C)) {
    return false;
  }
  if (o1.use_suffix(A) != o2.use_suffix(A)) {
    return false;
  }
  if (o1.use_suffix(B) != o3.use_suffix(B)) {
    return false;
  }
  if (!eq(
        o2.hanging_length(A) + o3.hanging_length(C),
        o1.hanging_length(A),
        EPSILON * o1.length() + ALPHA)) {
    return false;
  }
  if (!eq(
        o2.hanging_length(C) + o3.hanging_length(B),
        o1.hanging_length(B),
        EPSILON * o1.length() + ALPHA)) {
    return false;
  }
  return true;
}

int get_non_transitives(vector<shared_ptr<Overlap>>* dst_container, vector<shared_ptr<Overlap>>& edges_list) {
  int valid = 0;

  // create that data structure to allow fast access to all edges coincident
  // with particular node.
  EdgesSet<shared_ptr<Overlap>> edges_set;
  for (const auto& edge : edges_list) {
    edges_set.add(edge->a_id, edge->b_id, edge);
    edges_set.add(edge->b_id, edge->a_id, edge);
  }

  // sort so we can reduce complexity on iterating edges.
  for (auto unsorted : edges_set) {
    unsorted.second.sort();
  }

  // we are iterating all (x, y), (x, a), (y, a) in hope that we will find that
  // (x,y) is transitive edge when considering (x, a) and (y, a).
  // x -> y, x -> a -> y; x -> y can be information overhead.
  for (const auto& overlap : edges_list) {
    auto& src = overlap->a_id;
    auto& dst = overlap->b_id;

    auto& v1 = edges_set[src], v2 = edges_set[dst];
    auto it1 = v1.begin(), it2 = v2.begin();

    bool transitive = false;
    while (!transitive && it1 != v1.end() && it2 != v2.end()) {
      if (it1->first == overlap->a_id || it1->first == overlap->b_id) {
        ++it1;
        continue;
      }
      if (it2->first == overlap->a_id || it2->first == overlap->b_id) {
        ++it2;
        continue;
      }
      if (it1->first == it2->first) {
        if (is_transitive(*overlap, *it1->second, *it2->second)) {
          transitive = true;
        }
        ++it1;
        ++it2;
      } else if (it1->first < it2->first) {
        ++it1;
      } else {
        ++it2;
      }
    }

    if (!transitive) {
      dst_container->emplace_back(overlap);
    }
  }

  return valid;
}

void fill_reads(vector<shared_ptr<Overlap>>& overlaps, map<uint32_t, shared_ptr<Read>>& reads) {
  for (auto o : overlaps) {
    o->a = reads[o->a_id];
    o->b = reads[o->b_id];
  }
}

int read_from_stream(istream& input, map<uint32_t, shared_ptr<Read>>& reads, vector<shared_ptr<Overlap>>& edges) {
  Reader reader(input);

  int stored = 0;
  while (reader.has_next()) {
    if (reader.next_type() == AMOS::OVERLAP) {
      Overlap* overlap = new Overlap();
      if (!reader.next(overlap)) {
        cerr << "Error while reading overlap" << endl;
        continue;
      }

      edges.emplace_back(shared_ptr<Overlap>(overlap));
      stored++;
    } else if (reader.next_type() == AMOS::READ) {
      Read *read = new Read();
      if (!reader.next(read)) {
        cerr << "Error while reading overlap" << endl;
        continue;
      }

      reads[read->iid] = shared_ptr<Read>(read);
      stored++;
    } else {
      reader.skip_next();
      continue;
    }
  }

  return stored;
}

int main(int argc, char **argv) {
  vector<istream*> input_streams;

  if (argc == 2) {
    input_streams.emplace_back(&cin);
  } else {
    for (int i = 1; i < argc; ++i) {
      istream* in_stream;
      if (strcmp(argv[i], "-") == 0) {
        in_stream = &cin;
      } else {
        in_stream = new ifstream(argv[i]);
      }

      input_streams.emplace_back(&cin);
    }
  }

  vector<shared_ptr<Overlap>> overlaps;
  map<uint32_t, shared_ptr<Read>> reads;

  for (auto in_stream : input_streams) {
    read_from_stream(*in_stream, reads, overlaps);
  }

  cerr << "Read " << overlaps.size() << " reads" << endl;
  cerr << "Read " << reads.size() << " overlaps" << endl;

  fill_reads(overlaps, reads);

  vector<shared_ptr<Overlap>> non_transitive_edges;
  get_non_transitives(&non_transitive_edges, overlaps);

  cerr << "Filtered " << (overlaps.size() - non_transitive_edges.size()) << " overlaps" << endl;
}
<|endoftext|>


<commit_before>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: splitcelldlg.hxx,v $
 *
 *  $Revision: 1.2 $
 *
 *  last change: $Author: rt $ $Date: 2008-03-12 09:27:08 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/
#ifndef _SVX_SPLITCELLDLG_HXX
#define _SVX_SPLITCELLDLG_HXX

#include <vcl/fixed.hxx>
#include <vcl/field.hxx>
#include <vcl/imagebtn.hxx>
#include <vcl/button.hxx>

#include <svx/stddlg.hxx>

class SvxSplitTableDlg : public SvxStandardDialog
{
    FixedText           maCountLbl;
    NumericField        maCountEdit;
    FixedLine           maCountFL;
    ImageRadioButton    maHorzBox;
    ImageRadioButton    maVertBox;
    CheckBox            maPropCB;
    FixedLine           maDirFL;
    OKButton            maOKBtn;
    CancelButton        maCancelBtn;
    HelpButton          maHelpBtn;

protected:
    virtual void Apply();

public:
    SvxSplitTableDlg(Window *pParent );
    DECL_LINK( ClickHdl, Button * );

    bool IsHorizontal() const;
    bool IsProportional() const;
    long GetCount() const;
};

#endif
<commit_msg>INTEGRATION: CWS changefileheader (1.2.36); FILE MERGED 2008/03/31 14:18:16 rt 1.2.36.1: #i87441# Change license header to LPGL v3.<commit_after>/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * Copyright 2008 by Sun Microsystems, Inc.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * $RCSfile: splitcelldlg.hxx,v $
 * $Revision: 1.3 $
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/
#ifndef _SVX_SPLITCELLDLG_HXX
#define _SVX_SPLITCELLDLG_HXX

#include <vcl/fixed.hxx>
#include <vcl/field.hxx>
#include <vcl/imagebtn.hxx>
#include <vcl/button.hxx>

#include <svx/stddlg.hxx>

class SvxSplitTableDlg : public SvxStandardDialog
{
    FixedText           maCountLbl;
    NumericField        maCountEdit;
    FixedLine           maCountFL;
    ImageRadioButton    maHorzBox;
    ImageRadioButton    maVertBox;
    CheckBox            maPropCB;
    FixedLine           maDirFL;
    OKButton            maOKBtn;
    CancelButton        maCancelBtn;
    HelpButton          maHelpBtn;

protected:
    virtual void Apply();

public:
    SvxSplitTableDlg(Window *pParent );
    DECL_LINK( ClickHdl, Button * );

    bool IsHorizontal() const;
    bool IsProportional() const;
    long GetCount() const;
};

#endif
<|endoftext|>


<commit_before>#include <semaphore.h>
#include <fcntl.h>
#include <sys/stat.h>

#include <mist/defines.h>
#include "input.h"
#include <sstream>
#include <fstream>
#include <iterator>

namespace Mist {
  Input * Input::singleton = NULL;

  void Input::userCallback(char * data, size_t len, unsigned int id){
    for (int i = 0; i < 5; i++){
      long tid = ((long)(data[i*6]) << 24) | ((long)(data[i*6+1]) << 16) | ((long)(data[i*6+2]) << 8) | ((long)(data[i*6+3]));
      if (tid){
        long keyNum = ((long)(data[i*6+4]) << 8) | ((long)(data[i*6+5]));
        bufferFrame(tid, keyNum + 1);//Try buffer next frame
      }
    }
  }

  void Input::doNothing(char * data, size_t len, unsigned int id){
    DEBUG_MSG(DLVL_DONTEVEN, "Doing 'nothing'");
    singleton->userCallback(data, 30, id);//call the userCallback for this input
  }

  Input::Input(Util::Config * cfg) {
    config = cfg;
    JSON::Value option;
    option["long"] = "json";
    option["short"] = "j";
    option["help"] = "Output MistIn info in JSON format, then exit.";
    option["value"].append(0ll);
    config->addOption("json", option);
    option.null();
    option["arg_num"] = 1ll;
    option["arg"] = "string";
    option["help"] = "Name of the input file or - for stdin";
    option["value"].append("-");
    config->addOption("input", option);
    option.null();
    option["arg_num"] = 2ll;
    option["arg"] = "string";
    option["help"] = "Name of the output file or - for stdout";
    option["value"].append("-");
    config->addOption("output", option);
    option.null();
    option["arg"] = "string";
    option["short"] = "s";
    option["long"] = "stream";
    option["help"] = "The name of the stream that this connector will transmit.";
    config->addOption("streamname", option);
    option.null();
    option["short"] = "p";
    option["long"] = "player";
    option["help"] = "Makes this connector into a player";
    config->addOption("player", option);

    packTime = 0;
    lastActive = Util::epoch();
    playing = 0;
    playUntil = 0;

    singleton = this;
    isBuffer = false;
  }

  void Input::checkHeaderTimes(std::string streamFile){
    if ( streamFile == "-" ){
      return;
    }
    std::string headerFile = streamFile + ".dtsh";
    FILE * tmp = fopen(headerFile.c_str(),"r");
    if (tmp == NULL) {
      INFO_MSG("can't open file: %s (no header times compared, nothing removed)", headerFile.c_str() );
	  return;
	}
	struct stat bufStream;
    struct stat bufHeader;
    //fstat(fileno(streamFile), &bufStream);
    //fstat(fileno(tmp), &bufHeader);
    if (stat(streamFile.c_str(), &bufStream) !=0 || stat(headerFile.c_str(), &bufHeader) !=0){
      ERROR_MSG("could not get file info (no header times compared, nothing removed)");
      fclose(tmp);
      return;
	}

	int timeStream = bufStream.st_mtime;
    int timeHeader = bufHeader.st_mtime;
    fclose(tmp);
    INFO_MSG("time header: %i time stream: %i ", timeHeader, timeStream);
    if (timeHeader < timeStream) {
      INFO_MSG("removing old header file: %s ",headerFile.c_str());
      //delete filename
      remove(headerFile.c_str());
    }
  }
  int Input::run() {
    if (config->getBool("json")) {
      std::cerr << capa.toString() << std::endl;
      return 0;
    }
    if (!setup()) {
      std::cerr << config->getString("cmd") << " setup failed." << std::endl;
      return 0;
    }
    checkHeaderTimes(config->getString("input"));
    if (!readHeader()) {
      std::cerr << "Reading header for " << config->getString("input") << " failed." << std::endl;
      return 0;
    }
    parseHeader();

    if (!config->getBool("player")){
      //check filename for no -
      if (config->getString("output") != "-"){
        std::string filename = config->getString("output");
        if (filename.size() < 5 || filename.substr(filename.size() - 5) != ".dtsc"){
          filename += ".dtsc";
        }
        //output to dtsc
        DTSC::Meta newMeta = myMeta;
        newMeta.reset();
        JSON::Value tempVal;
        std::ofstream file(filename.c_str());
        long long int bpos = 0;
        seek(0);
        getNext();
        while (lastPack){
          tempVal = lastPack.toJSON();
          tempVal["bpos"] = bpos;
          newMeta.update(tempVal);
          file << std::string(lastPack.getData(), lastPack.getDataLen());
          bpos += lastPack.getDataLen();
          getNext();
        }
        //close file
        file.close();
        //create header
        file.open((filename+".dtsh").c_str());
        file << newMeta.toJSON().toNetPacked();
        file.close();
      }else{
        DEBUG_MSG(DLVL_FAIL,"No filename specified, exiting");
      }
    }else{
      //after this player functionality

#ifdef __CYGWIN__
  metaPage.init(config->getString("streamname"), 8 * 1024 * 1024, true);
#else
  metaPage.init(config->getString("streamname"), (isBuffer ? 8 * 1024 * 1024 : myMeta.getSendLen()), true);
#endif
      myMeta.writeTo(metaPage.mapped);
      userPage.init(config->getString("streamname") + "_users", 30, true);

      if (!isBuffer){
        for (std::map<int,DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){
          bufferFrame(it->first, 1);
        }
      }

      DEBUG_MSG(DLVL_DONTEVEN,"Pre-While");

      long long int activityCounter = Util::getMS();
      while ((Util::getMS() - activityCounter) < 10000){//10 second timeout
        DEBUG_MSG(DLVL_HIGH, "Timer running");
        Util::sleep(1000);
        removeUnused();
        userPage.parseEach(doNothing);
        if (userPage.amount){
          activityCounter = Util::getMS();
          DEBUG_MSG(DLVL_HIGH, "Connected users: %d", userPage.amount);
        }
      }
      DEBUG_MSG(DLVL_DEVEL,"Closing clean");
      //end player functionality
    }
    return 0;
  }

  void Input::removeUnused(){
    for (std::map<unsigned int, std::map<unsigned int, unsigned int> >::iterator it = pageCounter.begin(); it != pageCounter.end(); it++){
      for (std::map<unsigned int, unsigned int>::iterator it2 = it->second.begin(); it2 != it->second.end(); it2++){
        it2->second--;
      }
      bool change = true;
      while (change){
        change = false;
        for (std::map<unsigned int, unsigned int>::iterator it2 = it->second.begin(); it2 != it->second.end(); it2++){
          if (!it2->second){
            dataPages[it->first].erase(it2->first);
            pageCounter[it->first].erase(it2->first);
            change = true;
            break;
          }
        }
      }
    }
  }

  void Input::parseHeader(){
    DEBUG_MSG(DLVL_DONTEVEN,"Parsing the header");
    //Select all tracks for parsing header
    selectedTracks.clear();
    std::stringstream trackSpec;
    for (std::map<int, DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++) {
      DEBUG_MSG(DLVL_VERYHIGH, "Track %d encountered", it->first);
      if (trackSpec.str() != ""){
        trackSpec << " ";
      }
      trackSpec << it->first;
      DEBUG_MSG(DLVL_VERYHIGH, "Trackspec now %s", trackSpec.str().c_str());
      for (std::deque<DTSC::Key>::iterator it2 = it->second.keys.begin(); it2 != it->second.keys.end(); it2++){
        keyTimes[it->first].insert(it2->getTime());
      }
    }
    trackSelect(trackSpec.str());

    std::map<int, DTSCPageData> curData;
    std::map<int, booking> bookKeeping;

    seek(0);
    getNext();

    while(lastPack){//loop through all
      int tid = lastPack.getTrackId();
      if (!tid){
        getNext(false);
        continue;
      }
      if (!bookKeeping.count(tid)){
        bookKeeping[tid].first = 1;
        bookKeeping[tid].curPart = 0;
        bookKeeping[tid].curKey = 0;

        curData[tid].lastKeyTime = 0xFFFFFFFF;
        curData[tid].keyNum = 1;
        curData[tid].partNum = 0;
        curData[tid].dataSize = 0;
        curData[tid].curOffset = 0;
        curData[tid].firstTime = myMeta.tracks[tid].keys[0].getTime();

        char tmpId[20];
        sprintf(tmpId, "%d", tid);
        indexPages[tid].init(config->getString("streamname") + tmpId, 8 * 1024, true);//Pages of 8kb in size, room for 512 parts.
      }
      if (myMeta.tracks[tid].keys[bookKeeping[tid].curKey].getParts() == curData[tid].partNum){
        if (curData[tid].dataSize > 8 * 1024 * 1024) {
          pagesByTrack[tid][bookKeeping[tid].first] = curData[tid];
          bookKeeping[tid].first += curData[tid].keyNum;
          curData[tid].keyNum = 0;
          curData[tid].dataSize = 0;
          curData[tid].firstTime = myMeta.tracks[tid].keys[bookKeeping[tid].curKey].getTime();
        }
        bookKeeping[tid].curKey++;
        curData[tid].keyNum++;
        curData[tid].partNum = 0;
      }
      curData[tid].dataSize += lastPack.getDataLen();
      curData[tid].partNum ++;
      bookKeeping[tid].curPart ++;
      getNext(false);
    }
    for (std::map<int, DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++) {
      if (curData.count(it->first) && !pagesByTrack[it->first].count(bookKeeping[it->first].first)){
        pagesByTrack[it->first][bookKeeping[it->first].first] = curData[it->first];
      }
      if (!pagesByTrack.count(it->first)){
        DEBUG_MSG(DLVL_WARN, "No pages for track %d found", it->first);
      }else{
        DEBUG_MSG(DLVL_DEVEL, "Track %d (%s) split into %lu pages", it->first, myMeta.tracks[it->first].codec.c_str(), pagesByTrack[it->first].size());
        for (std::map<int, DTSCPageData>::iterator it2 = pagesByTrack[it->first].begin(); it2 != pagesByTrack[it->first].end(); it2++){
          DEBUG_MSG(DLVL_DEVEL, "Page %u-%u", it2->first, it2->first + it2->second.keyNum - 1);
        }
      }
    }
  }

  bool Input::bufferFrame(int track, int keyNum){
    if (keyNum < 1){keyNum = 1;}
    if (!pagesByTrack.count(track)){
      return false;
    }
    std::map<int, DTSCPageData>::iterator it = pagesByTrack[track].upper_bound(keyNum);
    if (it != pagesByTrack[track].begin()){
      it--;
    }
    int pageNum = it->first;
    pageCounter[track][pageNum] = 15;///Keep page 15seconds in memory after last use

    DEBUG_MSG(DLVL_DONTEVEN, "Attempting to buffer page %d key %d->%d", track, keyNum, pageNum);
    if (dataPages[track].count(pageNum)){
      return true;
    }
    char pageId[100];
    int pageIdLen = sprintf(pageId, "%s%d_%d", config->getString("streamname").c_str(), track, pageNum);
    std::string tmpString(pageId, pageIdLen);
#ifdef __CYGWIN__
    dataPages[track][pageNum].init(tmpString, 26 * 1024 * 1024, true);
#else
    dataPages[track][pageNum].init(tmpString, it->second.dataSize, true);
#endif
    DEBUG_MSG(DLVL_DEVEL, "Buffering track %d page %d through %d", track, pageNum, pageNum-1 + it->second.keyNum);

    std::stringstream trackSpec;
    trackSpec << track;
    trackSelect(trackSpec.str());
    seek(myMeta.tracks[track].keys[pageNum-1].getTime());
    long long unsigned int stopTime = myMeta.tracks[track].lastms + 1;
    if ((int)myMeta.tracks[track].keys.size() > pageNum-1 + it->second.keyNum){
      stopTime = myMeta.tracks[track].keys[pageNum-1 + it->second.keyNum].getTime();
    }
    DEBUG_MSG(DLVL_HIGH, "Playing from %ld to %llu", myMeta.tracks[track].keys[pageNum-1].getTime(), stopTime);
    it->second.curOffset = 0;
    getNext();
    //in case earlier seeking was inprecise, seek to the exact point
    while (lastPack && lastPack.getTime() < (unsigned long long)myMeta.tracks[track].keys[pageNum-1].getTime()){
      getNext();
    }
    while (lastPack && lastPack.getTime() < stopTime){
      if (it->second.curOffset + lastPack.getDataLen() > pagesByTrack[track][pageNum].dataSize){
        DEBUG_MSG(DLVL_WARN, "Trying to write %u bytes on pos %llu where size is %llu (time: %llu / %llu, track %u page %u)", lastPack.getDataLen(), it->second.curOffset, pagesByTrack[track][pageNum].dataSize, lastPack.getTime(), stopTime, track, pageNum);
        break;
      }else{
        memcpy(dataPages[track][pageNum].mapped + it->second.curOffset, lastPack.getData(), lastPack.getDataLen());
        it->second.curOffset += lastPack.getDataLen();
      }
      getNext();
    }
    for (int i = 0; i < indexPages[track].len / 8; i++){
      if (((long long int*)indexPages[track].mapped)[i] == 0){
        ((long long int*)indexPages[track].mapped)[i] = (((long long int)htonl(pageNum)) << 32) | htonl(it->second.keyNum);
        break;
      }
    }
    DEBUG_MSG(DLVL_DEVEL, "Done buffering page %d for track %d", pageNum, track);
    return true;
  }

  bool Input::atKeyFrame(){
    static std::map<int, unsigned long long> lastSeen;
    //not in keyTimes? We're not at a keyframe.
    unsigned int c = keyTimes[lastPack.getTrackId()].count(lastPack.getTime());
    if (!c){
      return false;
    }
    //skip double times
    if (lastSeen.count(lastPack.getTrackId()) && lastSeen[lastPack.getTrackId()] == lastPack.getTime()){
      return false;
    }
    //set last seen, and return true
    lastSeen[lastPack.getTrackId()] = lastPack.getTime();
    return true;
  }

  void Input::play(int until) {
    playing = -1;
    playUntil = until;
    initialTime = 0;
    benchMark = Util::getMS();
  }

  void Input::playOnce() {
    if (playing <= 0) {
      playing = 1;
    }
    ++playing;
    benchMark = Util::getMS();
  }

  void Input::quitPlay() {
    playing = 0;
  }
}

<commit_msg>Improved MistIn* player timer accuracy.<commit_after>#include <semaphore.h>
#include <fcntl.h>
#include <sys/stat.h>

#include <mist/defines.h>
#include "input.h"
#include <sstream>
#include <fstream>
#include <iterator>

namespace Mist {
  Input * Input::singleton = NULL;

  void Input::userCallback(char * data, size_t len, unsigned int id){
    for (int i = 0; i < 5; i++){
      long tid = ((long)(data[i*6]) << 24) | ((long)(data[i*6+1]) << 16) | ((long)(data[i*6+2]) << 8) | ((long)(data[i*6+3]));
      if (tid){
        long keyNum = ((long)(data[i*6+4]) << 8) | ((long)(data[i*6+5]));
        bufferFrame(tid, keyNum + 1);//Try buffer next frame
      }
    }
  }

  void Input::doNothing(char * data, size_t len, unsigned int id){
    DEBUG_MSG(DLVL_DONTEVEN, "Doing 'nothing'");
    singleton->userCallback(data, 30, id);//call the userCallback for this input
  }

  Input::Input(Util::Config * cfg) {
    config = cfg;
    JSON::Value option;
    option["long"] = "json";
    option["short"] = "j";
    option["help"] = "Output MistIn info in JSON format, then exit.";
    option["value"].append(0ll);
    config->addOption("json", option);
    option.null();
    option["arg_num"] = 1ll;
    option["arg"] = "string";
    option["help"] = "Name of the input file or - for stdin";
    option["value"].append("-");
    config->addOption("input", option);
    option.null();
    option["arg_num"] = 2ll;
    option["arg"] = "string";
    option["help"] = "Name of the output file or - for stdout";
    option["value"].append("-");
    config->addOption("output", option);
    option.null();
    option["arg"] = "string";
    option["short"] = "s";
    option["long"] = "stream";
    option["help"] = "The name of the stream that this connector will transmit.";
    config->addOption("streamname", option);
    option.null();
    option["short"] = "p";
    option["long"] = "player";
    option["help"] = "Makes this connector into a player";
    config->addOption("player", option);

    packTime = 0;
    lastActive = Util::epoch();
    playing = 0;
    playUntil = 0;

    singleton = this;
    isBuffer = false;
  }

  void Input::checkHeaderTimes(std::string streamFile){
    if ( streamFile == "-" ){
      return;
    }
    std::string headerFile = streamFile + ".dtsh";
    FILE * tmp = fopen(headerFile.c_str(),"r");
    if (tmp == NULL) {
      INFO_MSG("can't open file: %s (no header times compared, nothing removed)", headerFile.c_str() );
	  return;
	}
	struct stat bufStream;
    struct stat bufHeader;
    //fstat(fileno(streamFile), &bufStream);
    //fstat(fileno(tmp), &bufHeader);
    if (stat(streamFile.c_str(), &bufStream) !=0 || stat(headerFile.c_str(), &bufHeader) !=0){
      ERROR_MSG("could not get file info (no header times compared, nothing removed)");
      fclose(tmp);
      return;
	}

	int timeStream = bufStream.st_mtime;
    int timeHeader = bufHeader.st_mtime;
    fclose(tmp);
    INFO_MSG("time header: %i time stream: %i ", timeHeader, timeStream);
    if (timeHeader < timeStream) {
      INFO_MSG("removing old header file: %s ",headerFile.c_str());
      //delete filename
      remove(headerFile.c_str());
    }
  }
  int Input::run() {
    if (config->getBool("json")) {
      std::cerr << capa.toString() << std::endl;
      return 0;
    }
    if (!setup()) {
      std::cerr << config->getString("cmd") << " setup failed." << std::endl;
      return 0;
    }
    checkHeaderTimes(config->getString("input"));
    if (!readHeader()) {
      std::cerr << "Reading header for " << config->getString("input") << " failed." << std::endl;
      return 0;
    }
    parseHeader();

    if (!config->getBool("player")){
      //check filename for no -
      if (config->getString("output") != "-"){
        std::string filename = config->getString("output");
        if (filename.size() < 5 || filename.substr(filename.size() - 5) != ".dtsc"){
          filename += ".dtsc";
        }
        //output to dtsc
        DTSC::Meta newMeta = myMeta;
        newMeta.reset();
        JSON::Value tempVal;
        std::ofstream file(filename.c_str());
        long long int bpos = 0;
        seek(0);
        getNext();
        while (lastPack){
          tempVal = lastPack.toJSON();
          tempVal["bpos"] = bpos;
          newMeta.update(tempVal);
          file << std::string(lastPack.getData(), lastPack.getDataLen());
          bpos += lastPack.getDataLen();
          getNext();
        }
        //close file
        file.close();
        //create header
        file.open((filename+".dtsh").c_str());
        file << newMeta.toJSON().toNetPacked();
        file.close();
      }else{
        DEBUG_MSG(DLVL_FAIL,"No filename specified, exiting");
      }
    }else{
      //after this player functionality

#ifdef __CYGWIN__
  metaPage.init(config->getString("streamname"), 8 * 1024 * 1024, true);
#else
  metaPage.init(config->getString("streamname"), (isBuffer ? 8 * 1024 * 1024 : myMeta.getSendLen()), true);
#endif
      myMeta.writeTo(metaPage.mapped);
      userPage.init(config->getString("streamname") + "_users", 30, true);

      if (!isBuffer){
        for (std::map<int,DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){
          bufferFrame(it->first, 1);
        }
      }

      DEBUG_MSG(DLVL_DONTEVEN,"Pre-While");

      long long int activityCounter = Util::bootSecs();
      while ((Util::getMS() - activityCounter) < 10){//10 second timeout
        DEBUG_MSG(DLVL_INSANE, "Timer running");
        Util::wait(1000);
        removeUnused();
        userPage.parseEach(doNothing);
        if (userPage.amount){
          activityCounter = Util::bootSecs();
          DEBUG_MSG(DLVL_HIGH, "Connected users: %d", userPage.amount);
        }
      }
      DEBUG_MSG(DLVL_DEVEL,"Closing clean");
      //end player functionality
    }
    return 0;
  }

  void Input::removeUnused(){
    for (std::map<unsigned int, std::map<unsigned int, unsigned int> >::iterator it = pageCounter.begin(); it != pageCounter.end(); it++){
      for (std::map<unsigned int, unsigned int>::iterator it2 = it->second.begin(); it2 != it->second.end(); it2++){
        it2->second--;
      }
      bool change = true;
      while (change){
        change = false;
        for (std::map<unsigned int, unsigned int>::iterator it2 = it->second.begin(); it2 != it->second.end(); it2++){
          if (!it2->second){
            dataPages[it->first].erase(it2->first);
            pageCounter[it->first].erase(it2->first);
            change = true;
            break;
          }
        }
      }
    }
  }

  void Input::parseHeader(){
    DEBUG_MSG(DLVL_DONTEVEN,"Parsing the header");
    //Select all tracks for parsing header
    selectedTracks.clear();
    std::stringstream trackSpec;
    for (std::map<int, DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++) {
      DEBUG_MSG(DLVL_VERYHIGH, "Track %d encountered", it->first);
      if (trackSpec.str() != ""){
        trackSpec << " ";
      }
      trackSpec << it->first;
      DEBUG_MSG(DLVL_VERYHIGH, "Trackspec now %s", trackSpec.str().c_str());
      for (std::deque<DTSC::Key>::iterator it2 = it->second.keys.begin(); it2 != it->second.keys.end(); it2++){
        keyTimes[it->first].insert(it2->getTime());
      }
    }
    trackSelect(trackSpec.str());

    std::map<int, DTSCPageData> curData;
    std::map<int, booking> bookKeeping;

    seek(0);
    getNext();

    while(lastPack){//loop through all
      int tid = lastPack.getTrackId();
      if (!tid){
        getNext(false);
        continue;
      }
      if (!bookKeeping.count(tid)){
        bookKeeping[tid].first = 1;
        bookKeeping[tid].curPart = 0;
        bookKeeping[tid].curKey = 0;

        curData[tid].lastKeyTime = 0xFFFFFFFF;
        curData[tid].keyNum = 1;
        curData[tid].partNum = 0;
        curData[tid].dataSize = 0;
        curData[tid].curOffset = 0;
        curData[tid].firstTime = myMeta.tracks[tid].keys[0].getTime();

        char tmpId[20];
        sprintf(tmpId, "%d", tid);
        indexPages[tid].init(config->getString("streamname") + tmpId, 8 * 1024, true);//Pages of 8kb in size, room for 512 parts.
      }
      if (myMeta.tracks[tid].keys[bookKeeping[tid].curKey].getParts() == curData[tid].partNum){
        if (curData[tid].dataSize > 8 * 1024 * 1024) {
          pagesByTrack[tid][bookKeeping[tid].first] = curData[tid];
          bookKeeping[tid].first += curData[tid].keyNum;
          curData[tid].keyNum = 0;
          curData[tid].dataSize = 0;
          curData[tid].firstTime = myMeta.tracks[tid].keys[bookKeeping[tid].curKey].getTime();
        }
        bookKeeping[tid].curKey++;
        curData[tid].keyNum++;
        curData[tid].partNum = 0;
      }
      curData[tid].dataSize += lastPack.getDataLen();
      curData[tid].partNum ++;
      bookKeeping[tid].curPart ++;
      getNext(false);
    }
    for (std::map<int, DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++) {
      if (curData.count(it->first) && !pagesByTrack[it->first].count(bookKeeping[it->first].first)){
        pagesByTrack[it->first][bookKeeping[it->first].first] = curData[it->first];
      }
      if (!pagesByTrack.count(it->first)){
        DEBUG_MSG(DLVL_WARN, "No pages for track %d found", it->first);
      }else{
        DEBUG_MSG(DLVL_DEVEL, "Track %d (%s) split into %lu pages", it->first, myMeta.tracks[it->first].codec.c_str(), pagesByTrack[it->first].size());
        for (std::map<int, DTSCPageData>::iterator it2 = pagesByTrack[it->first].begin(); it2 != pagesByTrack[it->first].end(); it2++){
          DEBUG_MSG(DLVL_DEVEL, "Page %u-%u", it2->first, it2->first + it2->second.keyNum - 1);
        }
      }
    }
  }

  bool Input::bufferFrame(int track, int keyNum){
    if (keyNum < 1){keyNum = 1;}
    if (!pagesByTrack.count(track)){
      return false;
    }
    std::map<int, DTSCPageData>::iterator it = pagesByTrack[track].upper_bound(keyNum);
    if (it != pagesByTrack[track].begin()){
      it--;
    }
    int pageNum = it->first;
    pageCounter[track][pageNum] = 15;///Keep page 15seconds in memory after last use

    DEBUG_MSG(DLVL_DONTEVEN, "Attempting to buffer page %d key %d->%d", track, keyNum, pageNum);
    if (dataPages[track].count(pageNum)){
      return true;
    }
    char pageId[100];
    int pageIdLen = sprintf(pageId, "%s%d_%d", config->getString("streamname").c_str(), track, pageNum);
    std::string tmpString(pageId, pageIdLen);
#ifdef __CYGWIN__
    dataPages[track][pageNum].init(tmpString, 26 * 1024 * 1024, true);
#else
    dataPages[track][pageNum].init(tmpString, it->second.dataSize, true);
#endif
    DEBUG_MSG(DLVL_DEVEL, "Buffering track %d page %d through %d", track, pageNum, pageNum-1 + it->second.keyNum);

    std::stringstream trackSpec;
    trackSpec << track;
    trackSelect(trackSpec.str());
    seek(myMeta.tracks[track].keys[pageNum-1].getTime());
    long long unsigned int stopTime = myMeta.tracks[track].lastms + 1;
    if ((int)myMeta.tracks[track].keys.size() > pageNum-1 + it->second.keyNum){
      stopTime = myMeta.tracks[track].keys[pageNum-1 + it->second.keyNum].getTime();
    }
    DEBUG_MSG(DLVL_HIGH, "Playing from %ld to %llu", myMeta.tracks[track].keys[pageNum-1].getTime(), stopTime);
    it->second.curOffset = 0;
    getNext();
    //in case earlier seeking was inprecise, seek to the exact point
    while (lastPack && lastPack.getTime() < (unsigned long long)myMeta.tracks[track].keys[pageNum-1].getTime()){
      getNext();
    }
    while (lastPack && lastPack.getTime() < stopTime){
      if (it->second.curOffset + lastPack.getDataLen() > pagesByTrack[track][pageNum].dataSize){
        DEBUG_MSG(DLVL_WARN, "Trying to write %u bytes on pos %llu where size is %llu (time: %llu / %llu, track %u page %u)", lastPack.getDataLen(), it->second.curOffset, pagesByTrack[track][pageNum].dataSize, lastPack.getTime(), stopTime, track, pageNum);
        break;
      }else{
        memcpy(dataPages[track][pageNum].mapped + it->second.curOffset, lastPack.getData(), lastPack.getDataLen());
        it->second.curOffset += lastPack.getDataLen();
      }
      getNext();
    }
    for (int i = 0; i < indexPages[track].len / 8; i++){
      if (((long long int*)indexPages[track].mapped)[i] == 0){
        ((long long int*)indexPages[track].mapped)[i] = (((long long int)htonl(pageNum)) << 32) | htonl(it->second.keyNum);
        break;
      }
    }
    DEBUG_MSG(DLVL_DEVEL, "Done buffering page %d for track %d", pageNum, track);
    return true;
  }

  bool Input::atKeyFrame(){
    static std::map<int, unsigned long long> lastSeen;
    //not in keyTimes? We're not at a keyframe.
    unsigned int c = keyTimes[lastPack.getTrackId()].count(lastPack.getTime());
    if (!c){
      return false;
    }
    //skip double times
    if (lastSeen.count(lastPack.getTrackId()) && lastSeen[lastPack.getTrackId()] == lastPack.getTime()){
      return false;
    }
    //set last seen, and return true
    lastSeen[lastPack.getTrackId()] = lastPack.getTime();
    return true;
  }

  void Input::play(int until) {
    playing = -1;
    playUntil = until;
    initialTime = 0;
    benchMark = Util::getMS();
  }

  void Input::playOnce() {
    if (playing <= 0) {
      playing = 1;
    }
    ++playing;
    benchMark = Util::getMS();
  }

  void Input::quitPlay() {
    playing = 0;
  }
}

<|endoftext|>


<commit_before>#include <semaphore.h>
#include <fcntl.h>
#include <sys/stat.h>

#include <mist/defines.h>
#include "input.h"
#include <sstream>
#include <fstream>
#include <iterator>

namespace Mist {
  Input * Input::singleton = NULL;

  void Input::userCallback(char * data, size_t len, unsigned int id){
    long tid = ((long)(data[0]) << 24) | ((long)(data[1]) << 16) | ((long)(data[2]) << 8) | ((long)(data[3]));
    long keyNum = ((long)(data[4]) << 8) | ((long)(data[5]));
    bufferFrame(tid, keyNum + 1);//Try buffer next frame
  }

  void Input::doNothing(char * data, size_t len, unsigned int id){
    DEBUG_MSG(DLVL_DONTEVEN, "Doing 'nothing'");
    for (int i = 0; i < 5; i++){
      int tmp = ((long)(data[i*6]) << 24) | ((long)(data[i*6 + 1]) << 16) | ((long)(data[i*6 + 2]) << 8) | data[i*6 + 3];
      if (tmp){
        singleton->userCallback(data + (i*6), 6, id);//call the userCallback for this input
      }
    }
  }

  Input::Input(Util::Config * cfg) {
    config = cfg;
    JSON::Value option;
    option["long"] = "json";
    option["short"] = "j";
    option["help"] = "Output MistIn info in JSON format, then exit.";
    option["value"].append(0ll);
    config->addOption("json", option);
    option.null();
    option["arg_num"] = 1ll;
    option["arg"] = "string";
    option["help"] = "Name of the input file or - for stdin";
    option["value"].append("-");
    config->addOption("input", option);
    option.null();
    option["arg_num"] = 2ll;
    option["arg"] = "string";
    option["help"] = "Name of the output file or - for stdout";
    option["value"].append("-");
    config->addOption("output", option);
    option.null();
    option["arg"] = "string";
    option["short"] = "s";
    option["long"] = "stream";
    option["help"] = "The name of the stream that this connector will transmit.";
    config->addOption("streamname", option);
    option.null();
    option["short"] = "p";
    option["long"] = "player";
    option["help"] = "Makes this connector into a player";
    config->addOption("player", option);

    packTime = 0;
    lastActive = Util::epoch();
    playing = 0;
    playUntil = 0;

    singleton = this;
    isBuffer = false;
  }

  int Input::run() {
    if (config->getBool("json")) {
      std::cerr << capa.toString() << std::endl;
      return 0;
    }
    if (!setup()) {
      std::cerr << config->getString("cmd") << " setup failed." << std::endl;
      return 0;
    }
    if (!readHeader()) {
      std::cerr << "Reading header for " << config->getString("input") << " failed." << std::endl;
      return 0;
    }
    parseHeader();

    if (!config->getBool("player")){
      //check filename for no -
      if (config->getString("output") != "-"){
        //output to dtsc
        DTSC::Meta newMeta = myMeta;
        newMeta.reset();
        JSON::Value tempVal;
        std::ofstream file(config->getString("output").c_str());
        long long int bpos = 0;
        seek(0);
        getNext();
        while (lastPack){
          tempVal = lastPack.toJSON();
          tempVal["bpos"] = bpos;
          newMeta.update(tempVal);
          file << std::string(lastPack.getData(), lastPack.getDataLen());
          bpos += lastPack.getDataLen();
          getNext();
        }
        //close file
        file.close();
        //create header
        file.open((config->getString("output")+".dtsh").c_str());
        file << newMeta.toJSON().toNetPacked();
        file.close();
      }else{
        DEBUG_MSG(DLVL_FAIL,"No filename specified, exiting");
      }
    }else{
      //after this player functionality

#ifdef __CYGWIN__
  metaPage.init(config->getString("streamname"), 8 * 1024 * 1024, true);
#else
  metaPage.init(config->getString("streamname"), (isBuffer ? 8 * 1024 * 1024 : myMeta.getSendLen()), true);
#endif
      myMeta.writeTo(metaPage.mapped);
      userPage.init(config->getString("streamname") + "_users", 30, true);

      if (!isBuffer){
        for (std::map<int,DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){
          bufferFrame(it->first, 1);
        }
      }

      IPC::semaphore waiting(std::string("/wait_" + config->getString("streamname")).c_str(), O_CREAT | O_RDWR, ACCESSPERMS, 0);
      if (!waiting){
        DEBUG_MSG(DLVL_FAIL, "Failed to open semaphore - cancelling");
        return -1;
      }
      waiting.post();
      waiting.close();

      DEBUG_MSG(DLVL_HIGH,"Pre-While");

      long long int activityCounter = Util::getMS();
      while ((Util::getMS() - activityCounter) < 10000){//10 second timeout
        DEBUG_MSG(DLVL_HIGH, "Timer running");
        Util::sleep(1000);
        removeUnused();
        userPage.parseEach(doNothing);
        if (userPage.amount){
          activityCounter = Util::getMS();
          DEBUG_MSG(DLVL_HIGH, "Connected users: %d", userPage.amount);
        }
      }
      DEBUG_MSG(DLVL_DEVEL,"Closing clean");
      //end player functionality
    }
    return 0;
  }

  void Input::removeUnused(){
    for (std::map<unsigned int, std::map<unsigned int, unsigned int> >::iterator it = pageCounter.begin(); it != pageCounter.end(); it++){
      for (std::map<unsigned int, unsigned int>::iterator it2 = it->second.begin(); it2 != it->second.end(); it2++){
        it2->second--;
      }
      bool change = true;
      while (change){
        change = false;
        for (std::map<unsigned int, unsigned int>::iterator it2 = it->second.begin(); it2 != it->second.end(); it2++){
          if (!it2->second){
            dataPages[it->first].erase(it2->first);
            pageCounter[it->first].erase(it2->first);
            change = true;
            break;
          }
        }
      }
    }
  }

  void Input::parseHeader(){
    DEBUG_MSG(DLVL_DEVEL,"Parsing the header");
    //Select all tracks for parsing header
    selectedTracks.clear();
    std::stringstream trackSpec;
    for (std::map<int, DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++) {
      DEBUG_MSG(DLVL_VERYHIGH, "Track %d encountered", it->first);
      //selectedTracks.insert(it->first);
      if (trackSpec.str() != ""){
        trackSpec << " ";
      }
      trackSpec << it->first;
      DEBUG_MSG(DLVL_VERYHIGH, "Trackspec now %s", trackSpec.str().c_str());
      for (std::deque<DTSC::Key>::iterator it2 = it->second.keys.begin(); it2 != it->second.keys.end(); it2++){
        keyTimes[it->first].insert(it2->getTime());
      }
    }
    trackSelect(trackSpec.str());

    std::map<int, DTSCPageData> curData;
    std::map<int, booking> bookKeeping;

    seek(0);
    getNext();

    while(lastPack){//loop through all
      int tid = lastPack.getTrackId();
      if (!bookKeeping.count(tid)){
        bookKeeping[tid].first = 1;
        bookKeeping[tid].curPart = 0;
        bookKeeping[tid].curKey = 0;

        curData[tid].lastKeyTime = 0xFFFFFFFF;
        curData[tid].keyNum = 1;
        curData[tid].partNum = 0;
        curData[tid].dataSize = 0;
        curData[tid].curOffset = 0;
        curData[tid].firstTime = myMeta.tracks[tid].keys[0].getTime();

        char tmpId[20];
        sprintf(tmpId, "%d", tid);
        indexPages[tid].init(config->getString("streamname") + tmpId, 8 * 1024, true);//Pages of 8kb in size, room for 512 parts.
      }
      if (myMeta.tracks[tid].keys[bookKeeping[tid].curKey].getParts() == curData[tid].partNum){
        if (curData[tid].dataSize > 8 * 1024 * 1024) {
          pagesByTrack[tid][bookKeeping[tid].first] = curData[tid];
          bookKeeping[tid].first += curData[tid].keyNum;
          curData[tid].keyNum = 0;
          curData[tid].dataSize = 0;
          curData[tid].firstTime = myMeta.tracks[tid].keys[bookKeeping[tid].curKey].getTime();
        }
        bookKeeping[tid].curKey++;
        curData[tid].keyNum++;
        curData[tid].partNum = 0;
      }
      curData[tid].dataSize += lastPack.getDataLen();
      curData[tid].partNum ++;
      bookKeeping[tid].curPart ++;
      getNext(false);
    }
    for (std::map<int, DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++) {
      if (curData.count(it->first) && !pagesByTrack[it->first].count(bookKeeping[it->first].first)){
        pagesByTrack[it->first][bookKeeping[it->first].first] = curData[it->first];
      }
      if (!pagesByTrack.count(it->first)){
        DEBUG_MSG(DLVL_WARN, "No pages for track %d found", it->first);
      }else{
        DEBUG_MSG(DLVL_DEVEL, "Track %d (%s) split into %lu pages", it->first, myMeta.tracks[it->first].codec.c_str(), pagesByTrack[it->first].size());
        for (std::map<int, DTSCPageData>::iterator it2 = pagesByTrack[it->first].begin(); it2 != pagesByTrack[it->first].end(); it2++){
          DEBUG_MSG(DLVL_DEVEL, "Page %u-%u", it2->first, it2->first + it2->second.keyNum - 1);
        }
      }
    }
  }

  bool Input::bufferFrame(int track, int keyNum){
    if (keyNum < 1){keyNum = 1;}
    if (!pagesByTrack.count(track)){
      return false;
    }
    std::map<int, DTSCPageData>::iterator it = pagesByTrack[track].upper_bound(keyNum);
    if (it != pagesByTrack[track].begin()){
      it--;
    }
    int pageNum = it->first;
    pageCounter[track][pageNum] = 15;///Keep page 15seconds in memory after last use

    DEBUG_MSG(DLVL_DONTEVEN, "Attempting to buffer page %d key %d->%d", track, keyNum, pageNum);
    if (dataPages[track].count(pageNum)){
      return true;
    }
    char pageId[100];
    int pageIdLen = sprintf(pageId, "%s%d_%d", config->getString("streamname").c_str(), track, pageNum);
    std::string tmpString(pageId, pageIdLen);
#ifdef __CYGWIN__
    dataPages[track][pageNum].init(tmpString, 26 * 1024 * 1024, true);
#else
    dataPages[track][pageNum].init(tmpString, it->second.dataSize, true);
#endif
    DEBUG_MSG(DLVL_DEVEL, "Buffering track %d page %d through %d", track, pageNum, pageNum-1 + it->second.keyNum);

    std::stringstream trackSpec;
    trackSpec << track;
    trackSelect(trackSpec.str());
    seek(myMeta.tracks[track].keys[pageNum-1].getTime());
    long long unsigned int stopTime = myMeta.tracks[track].lastms + 1;
    if ((int)myMeta.tracks[track].keys.size() > pageNum-1 + it->second.keyNum){
      stopTime = myMeta.tracks[track].keys[pageNum-1 + it->second.keyNum].getTime();
    }
    DEBUG_MSG(DLVL_HIGH, "Playing from %ld to %llu", myMeta.tracks[track].keys[pageNum-1].getTime(), stopTime);
    it->second.curOffset = 0;
    getNext();
    while (lastPack && lastPack.getTime() < stopTime){
      if (it->second.curOffset + lastPack.getDataLen() > pagesByTrack[track][pageNum].dataSize){
        DEBUG_MSG(DLVL_WARN, "Trying to write %u bytes on pos %llu where size is %llu (time: %llu / %llu, track %u page %u)", lastPack.getDataLen(), it->second.curOffset, pagesByTrack[track][pageNum].dataSize, lastPack.getTime(), stopTime, track, pageNum);
        break;
      }else{
        memcpy(dataPages[track][pageNum].mapped + it->second.curOffset, lastPack.getData(), lastPack.getDataLen());
        it->second.curOffset += lastPack.getDataLen();
      }
      getNext();
    }
    for (int i = 0; i < indexPages[track].len / 8; i++){
      if (((long long int*)indexPages[track].mapped)[i] == 0){
        ((long long int*)indexPages[track].mapped)[i] = (((long long int)htonl(pageNum)) << 32) | htonl(it->second.keyNum);
        break;
      }
    }
    return true;
  }

  bool Input::atKeyFrame(){
    static std::map<int, int> lastSeen;
    //not in keyTimes? We're not at a keyframe.
    unsigned int c = keyTimes[lastPack.getTrackId()].count(lastPack.getTime());
    if (!c){
      return false;
    }
    //skip double times
    if (lastSeen.count(lastPack.getTrackId()) && lastSeen[lastPack.getTrackId()] == lastPack.getTime()){
      return false;
    }
    //set last seen, and return true
    lastSeen[lastPack.getTrackId()] = lastPack.getTime();
    return true;
  }

  void Input::play(int until) {
    playing = -1;
    playUntil = until;
    initialTime = 0;
    benchMark = Util::getMS();
  }

  void Input::playOnce() {
    if (playing <= 0) {
      playing = 1;
    }
    ++playing;
    benchMark = Util::getMS();
  }

  void Input::quitPlay() {
    playing = 0;
  }
}

<commit_msg>MistIn processes now automatically add .dtsc to the end of output file, if missing.<commit_after>#include <semaphore.h>
#include <fcntl.h>
#include <sys/stat.h>

#include <mist/defines.h>
#include "input.h"
#include <sstream>
#include <fstream>
#include <iterator>

namespace Mist {
  Input * Input::singleton = NULL;

  void Input::userCallback(char * data, size_t len, unsigned int id){
    long tid = ((long)(data[0]) << 24) | ((long)(data[1]) << 16) | ((long)(data[2]) << 8) | ((long)(data[3]));
    long keyNum = ((long)(data[4]) << 8) | ((long)(data[5]));
    bufferFrame(tid, keyNum + 1);//Try buffer next frame
  }

  void Input::doNothing(char * data, size_t len, unsigned int id){
    DEBUG_MSG(DLVL_DONTEVEN, "Doing 'nothing'");
    for (int i = 0; i < 5; i++){
      int tmp = ((long)(data[i*6]) << 24) | ((long)(data[i*6 + 1]) << 16) | ((long)(data[i*6 + 2]) << 8) | data[i*6 + 3];
      if (tmp){
        singleton->userCallback(data + (i*6), 6, id);//call the userCallback for this input
      }
    }
  }

  Input::Input(Util::Config * cfg) {
    config = cfg;
    JSON::Value option;
    option["long"] = "json";
    option["short"] = "j";
    option["help"] = "Output MistIn info in JSON format, then exit.";
    option["value"].append(0ll);
    config->addOption("json", option);
    option.null();
    option["arg_num"] = 1ll;
    option["arg"] = "string";
    option["help"] = "Name of the input file or - for stdin";
    option["value"].append("-");
    config->addOption("input", option);
    option.null();
    option["arg_num"] = 2ll;
    option["arg"] = "string";
    option["help"] = "Name of the output file or - for stdout";
    option["value"].append("-");
    config->addOption("output", option);
    option.null();
    option["arg"] = "string";
    option["short"] = "s";
    option["long"] = "stream";
    option["help"] = "The name of the stream that this connector will transmit.";
    config->addOption("streamname", option);
    option.null();
    option["short"] = "p";
    option["long"] = "player";
    option["help"] = "Makes this connector into a player";
    config->addOption("player", option);

    packTime = 0;
    lastActive = Util::epoch();
    playing = 0;
    playUntil = 0;

    singleton = this;
    isBuffer = false;
  }

  int Input::run() {
    if (config->getBool("json")) {
      std::cerr << capa.toString() << std::endl;
      return 0;
    }
    if (!setup()) {
      std::cerr << config->getString("cmd") << " setup failed." << std::endl;
      return 0;
    }
    if (!readHeader()) {
      std::cerr << "Reading header for " << config->getString("input") << " failed." << std::endl;
      return 0;
    }
    parseHeader();

    if (!config->getBool("player")){
      //check filename for no -
      if (config->getString("output") != "-"){
        std::string filename = config->getString("output");
        if (filename.size() < 5 || filename.substr(filename.size() - 5) != ".dtsc"){
          filename += ".dtsc";
        }
        //output to dtsc
        DTSC::Meta newMeta = myMeta;
        newMeta.reset();
        JSON::Value tempVal;
        std::ofstream file(filename.c_str());
        long long int bpos = 0;
        seek(0);
        getNext();
        while (lastPack){
          tempVal = lastPack.toJSON();
          tempVal["bpos"] = bpos;
          newMeta.update(tempVal);
          file << std::string(lastPack.getData(), lastPack.getDataLen());
          bpos += lastPack.getDataLen();
          getNext();
        }
        //close file
        file.close();
        //create header
        file.open((filename+".dtsh").c_str());
        file << newMeta.toJSON().toNetPacked();
        file.close();
      }else{
        DEBUG_MSG(DLVL_FAIL,"No filename specified, exiting");
      }
    }else{
      //after this player functionality

#ifdef __CYGWIN__
  metaPage.init(config->getString("streamname"), 8 * 1024 * 1024, true);
#else
  metaPage.init(config->getString("streamname"), (isBuffer ? 8 * 1024 * 1024 : myMeta.getSendLen()), true);
#endif
      myMeta.writeTo(metaPage.mapped);
      userPage.init(config->getString("streamname") + "_users", 30, true);

      if (!isBuffer){
        for (std::map<int,DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){
          bufferFrame(it->first, 1);
        }
      }

      IPC::semaphore waiting(std::string("/wait_" + config->getString("streamname")).c_str(), O_CREAT | O_RDWR, ACCESSPERMS, 0);
      if (!waiting){
        DEBUG_MSG(DLVL_FAIL, "Failed to open semaphore - cancelling");
        return -1;
      }
      waiting.post();
      waiting.close();

      DEBUG_MSG(DLVL_HIGH,"Pre-While");

      long long int activityCounter = Util::getMS();
      while ((Util::getMS() - activityCounter) < 10000){//10 second timeout
        DEBUG_MSG(DLVL_HIGH, "Timer running");
        Util::sleep(1000);
        removeUnused();
        userPage.parseEach(doNothing);
        if (userPage.amount){
          activityCounter = Util::getMS();
          DEBUG_MSG(DLVL_HIGH, "Connected users: %d", userPage.amount);
        }
      }
      DEBUG_MSG(DLVL_DEVEL,"Closing clean");
      //end player functionality
    }
    return 0;
  }

  void Input::removeUnused(){
    for (std::map<unsigned int, std::map<unsigned int, unsigned int> >::iterator it = pageCounter.begin(); it != pageCounter.end(); it++){
      for (std::map<unsigned int, unsigned int>::iterator it2 = it->second.begin(); it2 != it->second.end(); it2++){
        it2->second--;
      }
      bool change = true;
      while (change){
        change = false;
        for (std::map<unsigned int, unsigned int>::iterator it2 = it->second.begin(); it2 != it->second.end(); it2++){
          if (!it2->second){
            dataPages[it->first].erase(it2->first);
            pageCounter[it->first].erase(it2->first);
            change = true;
            break;
          }
        }
      }
    }
  }

  void Input::parseHeader(){
    DEBUG_MSG(DLVL_DEVEL,"Parsing the header");
    //Select all tracks for parsing header
    selectedTracks.clear();
    std::stringstream trackSpec;
    for (std::map<int, DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++) {
      DEBUG_MSG(DLVL_VERYHIGH, "Track %d encountered", it->first);
      //selectedTracks.insert(it->first);
      if (trackSpec.str() != ""){
        trackSpec << " ";
      }
      trackSpec << it->first;
      DEBUG_MSG(DLVL_VERYHIGH, "Trackspec now %s", trackSpec.str().c_str());
      for (std::deque<DTSC::Key>::iterator it2 = it->second.keys.begin(); it2 != it->second.keys.end(); it2++){
        keyTimes[it->first].insert(it2->getTime());
      }
    }
    trackSelect(trackSpec.str());

    std::map<int, DTSCPageData> curData;
    std::map<int, booking> bookKeeping;

    seek(0);
    getNext();

    while(lastPack){//loop through all
      int tid = lastPack.getTrackId();
      if (!bookKeeping.count(tid)){
        bookKeeping[tid].first = 1;
        bookKeeping[tid].curPart = 0;
        bookKeeping[tid].curKey = 0;

        curData[tid].lastKeyTime = 0xFFFFFFFF;
        curData[tid].keyNum = 1;
        curData[tid].partNum = 0;
        curData[tid].dataSize = 0;
        curData[tid].curOffset = 0;
        curData[tid].firstTime = myMeta.tracks[tid].keys[0].getTime();

        char tmpId[20];
        sprintf(tmpId, "%d", tid);
        indexPages[tid].init(config->getString("streamname") + tmpId, 8 * 1024, true);//Pages of 8kb in size, room for 512 parts.
      }
      if (myMeta.tracks[tid].keys[bookKeeping[tid].curKey].getParts() == curData[tid].partNum){
        if (curData[tid].dataSize > 8 * 1024 * 1024) {
          pagesByTrack[tid][bookKeeping[tid].first] = curData[tid];
          bookKeeping[tid].first += curData[tid].keyNum;
          curData[tid].keyNum = 0;
          curData[tid].dataSize = 0;
          curData[tid].firstTime = myMeta.tracks[tid].keys[bookKeeping[tid].curKey].getTime();
        }
        bookKeeping[tid].curKey++;
        curData[tid].keyNum++;
        curData[tid].partNum = 0;
      }
      curData[tid].dataSize += lastPack.getDataLen();
      curData[tid].partNum ++;
      bookKeeping[tid].curPart ++;
      getNext(false);
    }
    for (std::map<int, DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++) {
      if (curData.count(it->first) && !pagesByTrack[it->first].count(bookKeeping[it->first].first)){
        pagesByTrack[it->first][bookKeeping[it->first].first] = curData[it->first];
      }
      if (!pagesByTrack.count(it->first)){
        DEBUG_MSG(DLVL_WARN, "No pages for track %d found", it->first);
      }else{
        DEBUG_MSG(DLVL_DEVEL, "Track %d (%s) split into %lu pages", it->first, myMeta.tracks[it->first].codec.c_str(), pagesByTrack[it->first].size());
        for (std::map<int, DTSCPageData>::iterator it2 = pagesByTrack[it->first].begin(); it2 != pagesByTrack[it->first].end(); it2++){
          DEBUG_MSG(DLVL_DEVEL, "Page %u-%u", it2->first, it2->first + it2->second.keyNum - 1);
        }
      }
    }
  }

  bool Input::bufferFrame(int track, int keyNum){
    if (keyNum < 1){keyNum = 1;}
    if (!pagesByTrack.count(track)){
      return false;
    }
    std::map<int, DTSCPageData>::iterator it = pagesByTrack[track].upper_bound(keyNum);
    if (it != pagesByTrack[track].begin()){
      it--;
    }
    int pageNum = it->first;
    pageCounter[track][pageNum] = 15;///Keep page 15seconds in memory after last use

    DEBUG_MSG(DLVL_DONTEVEN, "Attempting to buffer page %d key %d->%d", track, keyNum, pageNum);
    if (dataPages[track].count(pageNum)){
      return true;
    }
    char pageId[100];
    int pageIdLen = sprintf(pageId, "%s%d_%d", config->getString("streamname").c_str(), track, pageNum);
    std::string tmpString(pageId, pageIdLen);
#ifdef __CYGWIN__
    dataPages[track][pageNum].init(tmpString, 26 * 1024 * 1024, true);
#else
    dataPages[track][pageNum].init(tmpString, it->second.dataSize, true);
#endif
    DEBUG_MSG(DLVL_DEVEL, "Buffering track %d page %d through %d", track, pageNum, pageNum-1 + it->second.keyNum);

    std::stringstream trackSpec;
    trackSpec << track;
    trackSelect(trackSpec.str());
    seek(myMeta.tracks[track].keys[pageNum-1].getTime());
    long long unsigned int stopTime = myMeta.tracks[track].lastms + 1;
    if ((int)myMeta.tracks[track].keys.size() > pageNum-1 + it->second.keyNum){
      stopTime = myMeta.tracks[track].keys[pageNum-1 + it->second.keyNum].getTime();
    }
    DEBUG_MSG(DLVL_HIGH, "Playing from %ld to %llu", myMeta.tracks[track].keys[pageNum-1].getTime(), stopTime);
    it->second.curOffset = 0;
    getNext();
    while (lastPack && lastPack.getTime() < stopTime){
      if (it->second.curOffset + lastPack.getDataLen() > pagesByTrack[track][pageNum].dataSize){
        DEBUG_MSG(DLVL_WARN, "Trying to write %u bytes on pos %llu where size is %llu (time: %llu / %llu, track %u page %u)", lastPack.getDataLen(), it->second.curOffset, pagesByTrack[track][pageNum].dataSize, lastPack.getTime(), stopTime, track, pageNum);
        break;
      }else{
        memcpy(dataPages[track][pageNum].mapped + it->second.curOffset, lastPack.getData(), lastPack.getDataLen());
        it->second.curOffset += lastPack.getDataLen();
      }
      getNext();
    }
    for (int i = 0; i < indexPages[track].len / 8; i++){
      if (((long long int*)indexPages[track].mapped)[i] == 0){
        ((long long int*)indexPages[track].mapped)[i] = (((long long int)htonl(pageNum)) << 32) | htonl(it->second.keyNum);
        break;
      }
    }
    return true;
  }

  bool Input::atKeyFrame(){
    static std::map<int, int> lastSeen;
    //not in keyTimes? We're not at a keyframe.
    unsigned int c = keyTimes[lastPack.getTrackId()].count(lastPack.getTime());
    if (!c){
      return false;
    }
    //skip double times
    if (lastSeen.count(lastPack.getTrackId()) && lastSeen[lastPack.getTrackId()] == lastPack.getTime()){
      return false;
    }
    //set last seen, and return true
    lastSeen[lastPack.getTrackId()] = lastPack.getTime();
    return true;
  }

  void Input::play(int until) {
    playing = -1;
    playUntil = until;
    initialTime = 0;
    benchMark = Util::getMS();
  }

  void Input::playOnce() {
    if (playing <= 0) {
      playing = 1;
    }
    ++playing;
    benchMark = Util::getMS();
  }

  void Input::quitPlay() {
    playing = 0;
  }
}

<|endoftext|>


<commit_before>#include "./window.h"
#include <QOpenGLContext>
#include <QDebug>
#include <QCoreApplication>
#include <QKeyEvent>
#include <QStateMachine>
#include <QAbstractState>
#include <QAbstractTransition>
#include <iostream>
#include "./graphics/gl.h"
#include "./abstract_scene.h"

Window::Window(std::shared_ptr<AbstractScene> scene, QWindow *parent)
  : QQuickView(parent), scene(scene)
{
  setClearBeforeRendering(false);

  connect(this, SIGNAL(widthChanged(int)), this, SLOT(resizeOpenGL()));
  connect(this, SIGNAL(heightChanged(int)), this, SLOT(resizeOpenGL()));

  connect(reinterpret_cast<QObject *>(engine()), SIGNAL(quit()), this,
          SLOT(close()));

  connect(this, SIGNAL(beforeRendering()), this, SLOT(render()),
          Qt::DirectConnection);

  auto format = createSurfaceFormat();
  setFormat(format);

  timer.start();
}

Window::~Window()
{
  delete gl;
}

QSurfaceFormat Window::createSurfaceFormat()
{
  QSurfaceFormat format;
  format.setDepthBufferSize(24);
  format.setMajorVersion(4);
  format.setMinorVersion(3);
  // format.setSamples(4);

  return format;
}

void Window::initializeOpenGL()
{
  context = openglContext();
  gl = new Graphics::Gl();
  gl->initialize(context, size());

  glAssert(gl->glDisable(GL_CULL_FACE));
  glAssert(gl->glDisable(GL_DEPTH_TEST));
  glAssert(gl->glDisable(GL_STENCIL_TEST));
  glAssert(gl->glDisable(GL_BLEND));
  glAssert(gl->glDepthMask(GL_FALSE));
  // gl->glEnable(GL_DEPTH_TEST);
  // gl->glEnable(GL_BLEND);
  // gl->glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}

void Window::keyReleaseEvent(QKeyEvent *event)
{
  QQuickView::keyReleaseEvent(event);
  keysPressed -= static_cast<Qt::Key>(event->key());
}

void Window::keyPressEvent(QKeyEvent *event)
{
  QQuickView::keyPressEvent(event);
  keysPressed += static_cast<Qt::Key>(event->key());
}

void Window::handleLazyInitialization()
{
  static bool initialized = false;
  if (!initialized)
  {
    initializeOpenGL();

    scene->setContext(context, gl);
    scene->resize(size().width(), size().height());
    scene->initialize();
    initialized = true;
  }
}

void Window::render()
{
  handleLazyInitialization();

  update();
  scene->render();

  // Use to check for missing release calls
  // resetOpenGLState();

  QQuickView::update();
}

void Window::resizeOpenGL()
{
  if (!gl)
    return;

  scene->resize(width(), height());
  gl->setSize(this->size());
}

void Window::update()
{
  double frameTime = timer.restart() / 1000.0;
  updateAverageFrameTime(frameTime);

  scene->update(frameTime, keysPressed);
}

void Window::toggleFullscreen()
{
  setVisibility(visibility() == QWindow::Windowed ? QWindow::FullScreen
                                                  : QWindow::Windowed);
}

void Window::updateAverageFrameTime(double frameTime)
{
  runningTime += frameTime;
  ++framesInSecond;

  if (runningTime > 1.0)
  {
    avgFrameTime = runningTime / framesInSecond;
    emit averageFrameTimeUpdated();

    framesInSecond = 0;
    runningTime = 0;
  }
}

<commit_msg>Reenable multi-sampling.<commit_after>#include "./window.h"
#include <QOpenGLContext>
#include <QDebug>
#include <QCoreApplication>
#include <QKeyEvent>
#include <QStateMachine>
#include <QAbstractState>
#include <QAbstractTransition>
#include <iostream>
#include "./graphics/gl.h"
#include "./abstract_scene.h"

Window::Window(std::shared_ptr<AbstractScene> scene, QWindow *parent)
  : QQuickView(parent), scene(scene)
{
  setClearBeforeRendering(false);

  connect(this, SIGNAL(widthChanged(int)), this, SLOT(resizeOpenGL()));
  connect(this, SIGNAL(heightChanged(int)), this, SLOT(resizeOpenGL()));

  connect(reinterpret_cast<QObject *>(engine()), SIGNAL(quit()), this,
          SLOT(close()));

  connect(this, SIGNAL(beforeRendering()), this, SLOT(render()),
          Qt::DirectConnection);

  auto format = createSurfaceFormat();
  setFormat(format);

  timer.start();
}

Window::~Window()
{
  delete gl;
}

QSurfaceFormat Window::createSurfaceFormat()
{
  QSurfaceFormat format;
  format.setDepthBufferSize(24);
  format.setMajorVersion(4);
  format.setMinorVersion(3);
  format.setSamples(4);

  return format;
}

void Window::initializeOpenGL()
{
  context = openglContext();
  gl = new Graphics::Gl();
  gl->initialize(context, size());

  glAssert(gl->glDisable(GL_CULL_FACE));
  glAssert(gl->glDisable(GL_DEPTH_TEST));
  glAssert(gl->glDisable(GL_STENCIL_TEST));
  glAssert(gl->glDisable(GL_BLEND));
  glAssert(gl->glDepthMask(GL_FALSE));
  // gl->glEnable(GL_DEPTH_TEST);
  // gl->glEnable(GL_BLEND);
  // gl->glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}

void Window::keyReleaseEvent(QKeyEvent *event)
{
  QQuickView::keyReleaseEvent(event);
  keysPressed -= static_cast<Qt::Key>(event->key());
}

void Window::keyPressEvent(QKeyEvent *event)
{
  QQuickView::keyPressEvent(event);
  keysPressed += static_cast<Qt::Key>(event->key());
}

void Window::handleLazyInitialization()
{
  static bool initialized = false;
  if (!initialized)
  {
    initializeOpenGL();

    scene->setContext(context, gl);
    scene->resize(size().width(), size().height());
    scene->initialize();
    initialized = true;
  }
}

void Window::render()
{
  handleLazyInitialization();

  update();
  scene->render();

  // Use to check for missing release calls
  // resetOpenGLState();

  QQuickView::update();
}

void Window::resizeOpenGL()
{
  if (!gl)
    return;

  scene->resize(width(), height());
  gl->setSize(this->size());
}

void Window::update()
{
  double frameTime = timer.restart() / 1000.0;
  updateAverageFrameTime(frameTime);

  scene->update(frameTime, keysPressed);
}

void Window::toggleFullscreen()
{
  setVisibility(visibility() == QWindow::Windowed ? QWindow::FullScreen
                                                  : QWindow::Windowed);
}

void Window::updateAverageFrameTime(double frameTime)
{
  runningTime += frameTime;
  ++framesInSecond;

  if (runningTime > 1.0)
  {
    avgFrameTime = runningTime / framesInSecond;
    emit averageFrameTimeUpdated();

    framesInSecond = 0;
    runningTime = 0;
  }
}

<|endoftext|>