<commit_before>
// This file is part of node-lmdb, the Node.js binding for lmdb
// Copyright (c) 2013 Timur Kristóf
// Licensed to you under the terms of the MIT license
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:

// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

#include "node-lmdb.h"
#include <string.h>

using namespace v8;
using namespace node;

CursorWrap::CursorWrap(MDB_cursor *cursor) {
    this->cursor = cursor;
    this->freeKey = nullptr;
}

CursorWrap::~CursorWrap() {
    if (this->cursor) {
        this->dw->Unref();
        this->tw->Unref();
        mdb_cursor_close(this->cursor);
    }
}

NAN_METHOD(CursorWrap::ctor) {
    Nan::HandleScope scope;

    if (info.Length() < 2) {
      Nan::ThrowTypeError("Wrong number of arguments");
      return;
    }

    // Extra pessimism...
    Nan::MaybeLocal<v8::Object> arg0 = Nan::To<v8::Object>(info[0]);
    Nan::MaybeLocal<v8::Object> arg1 = Nan::To<v8::Object>(info[1]);
    if (arg0.IsEmpty() || arg1.IsEmpty()) {
      Nan::ThrowTypeError("Invalid argument");
      return;
    }
    TxnWrap *tw = Nan::ObjectWrap::Unwrap<TxnWrap>(arg0.ToLocalChecked());
    DbiWrap *dw = Nan::ObjectWrap::Unwrap<DbiWrap>(arg1.ToLocalChecked());

    // Support 4 cursor modes
    //   binaryKey parameter missing - legacy behaviour. Might get fixed later...
    //   binaryKey === true - keys are buffers - always
    //   binaryKey === false - keys MUST be utf16le zero terminated on disk. If not, throws.
    node_lmdb::KeyType kt = dw->keyIsUint32 ? node_lmdb::uint32Key : node_lmdb::legacyStringKey;
    if ((info.Length() > 2) && (kt != node_lmdb::uint32Key)) {
      bool arg2 = Nan::To<bool>(info[2]).FromMaybe(false);
      kt = arg2 ? node_lmdb::binaryKey : node_lmdb::stringKey;
    }

    // Open the cursor
    MDB_cursor *cursor;
    int rc = mdb_cursor_open(tw->txn, dw->dbi, &cursor);
    if (rc != 0) {
        return Nan::ThrowError(mdb_strerror(rc));
    }

    // Create wrapper
    CursorWrap* cw = new CursorWrap(cursor);
    cw->dw = dw;
    cw->dw->Ref();
    cw->tw = tw;
    cw->tw->Ref();
    cw->kt = kt;
    cw->Wrap(info.This());

    NanReturnThis();
}

NAN_METHOD(CursorWrap::close) {
    Nan::HandleScope scope;

    CursorWrap *cw = Nan::ObjectWrap::Unwrap<CursorWrap>(info.This());
    mdb_cursor_close(cw->cursor);
    cw->dw->Unref();
    cw->tw->Unref();
    cw->cursor = nullptr;
    return;
}

NAN_METHOD(CursorWrap::del) {
    Nan::HandleScope scope;

    CursorWrap *cw = Nan::ObjectWrap::Unwrap<CursorWrap>(info.This());
    // TODO: wrap MDB_NODUPDATA flag

    int rc = mdb_cursor_del(cw->cursor, 0);
    if (rc != 0) {
        return Nan::ThrowError(mdb_strerror(rc));
    }

    return;
}

Nan::NAN_METHOD_RETURN_TYPE CursorWrap::getCommon(
    Nan::NAN_METHOD_ARGS_TYPE info,
    MDB_cursor_op op,
    argtokey_callback_t (*setKey)(CursorWrap* cw, Nan::NAN_METHOD_ARGS_TYPE info, MDB_val&),
    void (*setData)(CursorWrap* cw, Nan::NAN_METHOD_ARGS_TYPE info, MDB_val&),
    void (*freeData)(CursorWrap* cw, Nan::NAN_METHOD_ARGS_TYPE info, MDB_val&),
    Local<Value> (*convertFunc)(MDB_val &data)
) {
    Nan::HandleScope scope;

    int al = info.Length();
    CursorWrap *cw = Nan::ObjectWrap::Unwrap<CursorWrap>(info.This());

    // When a new key is manually set
    if (setKey) {
        // If we must free the current cw->key, free it now
        if (cw->freeKey) {
            cw->freeKey(cw->key);
        }

        // Set new key and assign the deleter function
        cw->freeKey = setKey(cw, info, cw->key);
    }

    if (setData) {
        setData(cw, info, cw->data);
    }

    // Temporary thing, so that we can free up the data if we want to
    MDB_val tempdata;
    tempdata.mv_size = cw->data.mv_size;
    tempdata.mv_data = cw->data.mv_data;

    // Temporary bookkeeping for the current key
    MDB_val tempKey;
    tempKey.mv_size = cw->key.mv_size;
    tempKey.mv_data = cw->key.mv_data;

    int rc = mdb_cursor_get(cw->cursor, &(cw->key), &(cw->data), op);

    // When cw->key.mv_data changes, it means it points inside the database now,
    // so we should free the old key now.
    if (tempKey.mv_data != cw->key.mv_data) {
        if (cw->freeKey) {
            cw->freeKey(tempKey);
        }

        // No need to free the key that points to the database.
        cw->freeKey = nullptr;
    }

    if (rc == MDB_NOTFOUND) {
        return info.GetReturnValue().Set(Nan::Null());
    }
    else if (rc != 0) {
        return Nan::ThrowError(mdb_strerror(rc));
    }

    Local<Value> keyHandle = Nan::Undefined();
    if (cw->key.mv_size) {
        keyHandle = keyToHandle(cw->key, cw->kt);
    }

    Local<Value> dataHandle = Nan::Undefined();
    if (convertFunc) {
        dataHandle = convertFunc(cw->data);
        if (al > 0 && info[al - 1]->IsFunction()) {
            // In this case, we expect the key/data pair to be correctly filled
            const unsigned argc = 2;
            Local<Value> argv[argc] = { keyHandle, dataHandle };
            Nan::Callback *callback = new Nan::Callback(Local<Function>::Cast(info[info.Length() - 1]));
            callback->Call(argc, argv);
            delete callback;
        }
    }

    if (freeData) {
        freeData(cw, info, tempdata);
    }

    if (convertFunc) {
        return info.GetReturnValue().Set(dataHandle);
    }
    else if (cw->key.mv_size) {
        return info.GetReturnValue().Set(keyHandle);
    }

    return info.GetReturnValue().Set(Nan::True());
}

Nan::NAN_METHOD_RETURN_TYPE CursorWrap::getCommon(Nan::NAN_METHOD_ARGS_TYPE info, MDB_cursor_op op) {
    return getCommon(info, op, nullptr, nullptr, nullptr, nullptr);
}

NAN_METHOD(CursorWrap::getCurrentString) {
    return getCommon(info, MDB_GET_CURRENT, nullptr, nullptr, nullptr, valToString);
}

NAN_METHOD(CursorWrap::getCurrentStringUnsafe) {
    return getCommon(info, MDB_GET_CURRENT, nullptr, nullptr, nullptr, valToStringUnsafe);
}

NAN_METHOD(CursorWrap::getCurrentBinary) {
    return getCommon(info, MDB_GET_CURRENT, nullptr, nullptr, nullptr, valToBinary);
}

NAN_METHOD(CursorWrap::getCurrentBinaryUnsafe) {
    return getCommon(info, MDB_GET_CURRENT, nullptr, nullptr, nullptr, valToBinaryUnsafe);
}

NAN_METHOD(CursorWrap::getCurrentNumber) {
    return getCommon(info, MDB_GET_CURRENT, nullptr, nullptr, nullptr, valToNumber);
}

NAN_METHOD(CursorWrap::getCurrentBoolean) {
    return getCommon(info, MDB_GET_CURRENT, nullptr, nullptr, nullptr, valToBoolean);
}

#define MAKE_GET_FUNC(name, op) NAN_METHOD(CursorWrap::name) { return getCommon(info, op); }

MAKE_GET_FUNC(goToFirst, MDB_FIRST);

MAKE_GET_FUNC(goToLast, MDB_LAST);

MAKE_GET_FUNC(goToNext, MDB_NEXT);

MAKE_GET_FUNC(goToPrev, MDB_PREV);

MAKE_GET_FUNC(goToFirstDup, MDB_FIRST_DUP);

MAKE_GET_FUNC(goToLastDup, MDB_LAST_DUP);

MAKE_GET_FUNC(goToNextDup, MDB_NEXT_DUP);

MAKE_GET_FUNC(goToPrevDup, MDB_PREV_DUP);

NAN_METHOD(CursorWrap::goToKey) {
    return getCommon(info, MDB_SET, [](CursorWrap* cw, Nan::NAN_METHOD_ARGS_TYPE info, MDB_val &key) -> argtokey_callback_t {
        return argToKey(info[0], key, cw->kt);
    }, nullptr, nullptr, nullptr);
}

NAN_METHOD(CursorWrap::goToRange) {
    return getCommon(info, MDB_SET_RANGE, [](CursorWrap* cw, Nan::NAN_METHOD_ARGS_TYPE info, MDB_val &key) -> argtokey_callback_t {
        return argToKey(info[0], key, cw->kt);
    }, nullptr, nullptr, nullptr);
}

static void fillDataFromArg1(CursorWrap* cw, Nan::NAN_METHOD_ARGS_TYPE info, MDB_val &data) {
    if (info[1]->IsString()) {
        CustomExternalStringResource::writeTo(info[1]->ToString(), &data);
    }
    else if (node::Buffer::HasInstance(info[1])) {
        data.mv_size = node::Buffer::Length(info[1]);
        data.mv_data = node::Buffer::Data(info[1]);
    }
    else if (info[1]->IsNumber()) {
        data.mv_size = sizeof(double);
        data.mv_data = new double;
        *((double*)data.mv_data) = info[1]->ToNumber(Nan::GetCurrentContext()).ToLocalChecked()->Value();
    }
    else if (info[1]->IsBoolean()) {
        data.mv_size = sizeof(double);
        data.mv_data = new bool;
        *((bool*)data.mv_data) = info[1]->ToBoolean()->Value();
    }
    else {
        Nan::ThrowError("Invalid data type.");
    }
}

static void freeDataFromArg1(CursorWrap* cw, Nan::NAN_METHOD_ARGS_TYPE info, MDB_val &data) {
    if (info[1]->IsString()) {
        delete[] (uint16_t*)data.mv_data;
    }
    else if (node::Buffer::HasInstance(info[1])) {
        // I think the data is owned by the node::Buffer so we don't need to free it - need to clarify
    }
    else if (info[1]->IsNumber()) {
        delete (double*)data.mv_data;
    }
    else if (info[1]->IsBoolean()) {
        delete (bool*)data.mv_data;
    }
    else {
        Nan::ThrowError("Invalid data type.");
    }
}

NAN_METHOD(CursorWrap::goToDup) {
    return getCommon(info, MDB_GET_BOTH, [](CursorWrap* cw, Nan::NAN_METHOD_ARGS_TYPE info, MDB_val &key) -> argtokey_callback_t {
        return argToKey(info[0], key, cw->kt);
    }, fillDataFromArg1, freeDataFromArg1, nullptr);
}

NAN_METHOD(CursorWrap::goToDupRange) {
    return getCommon(info, MDB_GET_BOTH_RANGE, [](CursorWrap* cw, Nan::NAN_METHOD_ARGS_TYPE info, MDB_val &key) -> argtokey_callback_t {
        return argToKey(info[0], key, cw->kt);
    }, fillDataFromArg1, freeDataFromArg1, nullptr);
}

void CursorWrap::setupExports(Handle<Object> exports) {
    // CursorWrap: Prepare constructor template
    Local<FunctionTemplate> cursorTpl = Nan::New<FunctionTemplate>(CursorWrap::ctor);
    cursorTpl->SetClassName(Nan::New<String>("Cursor").ToLocalChecked());
    cursorTpl->InstanceTemplate()->SetInternalFieldCount(1);
    // CursorWrap: Add functions to the prototype
    cursorTpl->PrototypeTemplate()->Set(Nan::New<String>("close").ToLocalChecked(), Nan::New<FunctionTemplate>(CursorWrap::close));
    cursorTpl->PrototypeTemplate()->Set(Nan::New<String>("getCurrentString").ToLocalChecked(), Nan::New<FunctionTemplate>(CursorWrap::getCurrentString));
    cursorTpl->PrototypeTemplate()->Set(Nan::New<String>("getCurrentStringUnsafe").ToLocalChecked(), Nan::New<FunctionTemplate>(CursorWrap::getCurrentStringUnsafe));
    cursorTpl->PrototypeTemplate()->Set(Nan::New<String>("getCurrentBinary").ToLocalChecked(), Nan::New<FunctionTemplate>(CursorWrap::getCurrentBinary));
    cursorTpl->PrototypeTemplate()->Set(Nan::New<String>("getCurrentBinaryUnsafe").ToLocalChecked(), Nan::New<FunctionTemplate>(CursorWrap::getCurrentBinaryUnsafe));
    cursorTpl->PrototypeTemplate()->Set(Nan::New<String>("getCurrentNumber").ToLocalChecked(), Nan::New<FunctionTemplate>(CursorWrap::getCurrentNumber));
    cursorTpl->PrototypeTemplate()->Set(Nan::New<String>("getCurrentBoolean").ToLocalChecked(), Nan::New<FunctionTemplate>(CursorWrap::getCurrentBoolean));
    cursorTpl->PrototypeTemplate()->Set(Nan::New<String>("goToFirst").ToLocalChecked(), Nan::New<FunctionTemplate>(CursorWrap::goToFirst));
    cursorTpl->PrototypeTemplate()->Set(Nan::New<String>("goToLast").ToLocalChecked(), Nan::New<FunctionTemplate>(CursorWrap::goToLast));
    cursorTpl->PrototypeTemplate()->Set(Nan::New<String>("goToNext").ToLocalChecked(), Nan::New<FunctionTemplate>(CursorWrap::goToNext));
    cursorTpl->PrototypeTemplate()->Set(Nan::New<String>("goToPrev").ToLocalChecked(), Nan::New<FunctionTemplate>(CursorWrap::goToPrev));
    cursorTpl->PrototypeTemplate()->Set(Nan::New<String>("goToKey").ToLocalChecked(), Nan::New<FunctionTemplate>(CursorWrap::goToKey));
    cursorTpl->PrototypeTemplate()->Set(Nan::New<String>("goToRange").ToLocalChecked(), Nan::New<FunctionTemplate>(CursorWrap::goToRange));
    cursorTpl->PrototypeTemplate()->Set(Nan::New<String>("goToFirstDup").ToLocalChecked(), Nan::New<FunctionTemplate>(CursorWrap::goToFirstDup));
    cursorTpl->PrototypeTemplate()->Set(Nan::New<String>("goToLastDup").ToLocalChecked(), Nan::New<FunctionTemplate>(CursorWrap::goToLastDup));
    cursorTpl->PrototypeTemplate()->Set(Nan::New<String>("goToNextDup").ToLocalChecked(), Nan::New<FunctionTemplate>(CursorWrap::goToNextDup));
    cursorTpl->PrototypeTemplate()->Set(Nan::New<String>("goToPrevDup").ToLocalChecked(), Nan::New<FunctionTemplate>(CursorWrap::goToPrevDup));
    cursorTpl->PrototypeTemplate()->Set(Nan::New<String>("goToDup").ToLocalChecked(), Nan::New<FunctionTemplate>(CursorWrap::goToDup));
    cursorTpl->PrototypeTemplate()->Set(Nan::New<String>("goToDupRange").ToLocalChecked(), Nan::New<FunctionTemplate>(CursorWrap::goToDupRange));
    cursorTpl->PrototypeTemplate()->Set(Nan::New<String>("del").ToLocalChecked(), Nan::New<FunctionTemplate>(CursorWrap::del));

    // Set exports
    exports->Set(Nan::New<String>("Cursor").ToLocalChecked(), cursorTpl->GetFunction());
}
<commit_msg>Free key in cursor d'tor...<commit_after>
// This file is part of node-lmdb, the Node.js binding for lmdb
// Copyright (c) 2013 Timur Kristóf
// Licensed to you under the terms of the MIT license
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:

// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

#include "node-lmdb.h"
#include <string.h>

using namespace v8;
using namespace node;

CursorWrap::CursorWrap(MDB_cursor *cursor) {
    this->cursor = cursor;
    this->freeKey = nullptr;
}

CursorWrap::~CursorWrap() {
    if (this->cursor) {
        this->dw->Unref();
        this->tw->Unref();
        mdb_cursor_close(this->cursor);
    }
    if (freeKey) freeKey(key);
}

NAN_METHOD(CursorWrap::ctor) {
    Nan::HandleScope scope;

    if (info.Length() < 2) {
      Nan::ThrowTypeError("Wrong number of arguments");
      return;
    }

    // Extra pessimism...
    Nan::MaybeLocal<v8::Object> arg0 = Nan::To<v8::Object>(info[0]);
    Nan::MaybeLocal<v8::Object> arg1 = Nan::To<v8::Object>(info[1]);
    if (arg0.IsEmpty() || arg1.IsEmpty()) {
      Nan::ThrowTypeError("Invalid argument");
      return;
    }
    TxnWrap *tw = Nan::ObjectWrap::Unwrap<TxnWrap>(arg0.ToLocalChecked());
    DbiWrap *dw = Nan::ObjectWrap::Unwrap<DbiWrap>(arg1.ToLocalChecked());

    // Support 4 cursor modes
    //   binaryKey parameter missing - legacy behaviour. Might get fixed later...
    //   binaryKey === true - keys are buffers - always
    //   binaryKey === false - keys MUST be utf16le zero terminated on disk. If not, throws.
    node_lmdb::KeyType kt = dw->keyIsUint32 ? node_lmdb::uint32Key : node_lmdb::legacyStringKey;
    if ((info.Length() > 2) && (kt != node_lmdb::uint32Key)) {
      bool arg2 = Nan::To<bool>(info[2]).FromMaybe(false);
      kt = arg2 ? node_lmdb::binaryKey : node_lmdb::stringKey;
    }

    // Open the cursor
    MDB_cursor *cursor;
    int rc = mdb_cursor_open(tw->txn, dw->dbi, &cursor);
    if (rc != 0) {
        return Nan::ThrowError(mdb_strerror(rc));
    }

    // Create wrapper
    CursorWrap* cw = new CursorWrap(cursor);
    cw->dw = dw;
    cw->dw->Ref();
    cw->tw = tw;
    cw->tw->Ref();
    cw->kt = kt;
    cw->Wrap(info.This());

    NanReturnThis();
}

NAN_METHOD(CursorWrap::close) {
    Nan::HandleScope scope;

    CursorWrap *cw = Nan::ObjectWrap::Unwrap<CursorWrap>(info.This());
    mdb_cursor_close(cw->cursor);
    cw->dw->Unref();
    cw->tw->Unref();
    cw->cursor = nullptr;
    return;
}

NAN_METHOD(CursorWrap::del) {
    Nan::HandleScope scope;

    CursorWrap *cw = Nan::ObjectWrap::Unwrap<CursorWrap>(info.This());
    // TODO: wrap MDB_NODUPDATA flag

    int rc = mdb_cursor_del(cw->cursor, 0);
    if (rc != 0) {
        return Nan::ThrowError(mdb_strerror(rc));
    }

    return;
}

Nan::NAN_METHOD_RETURN_TYPE CursorWrap::getCommon(
    Nan::NAN_METHOD_ARGS_TYPE info,
    MDB_cursor_op op,
    argtokey_callback_t (*setKey)(CursorWrap* cw, Nan::NAN_METHOD_ARGS_TYPE info, MDB_val&),
    void (*setData)(CursorWrap* cw, Nan::NAN_METHOD_ARGS_TYPE info, MDB_val&),
    void (*freeData)(CursorWrap* cw, Nan::NAN_METHOD_ARGS_TYPE info, MDB_val&),
    Local<Value> (*convertFunc)(MDB_val &data)
) {
    Nan::HandleScope scope;

    int al = info.Length();
    CursorWrap *cw = Nan::ObjectWrap::Unwrap<CursorWrap>(info.This());

    // When a new key is manually set
    if (setKey) {
        // If we must free the current cw->key, free it now
        if (cw->freeKey) {
            cw->freeKey(cw->key);
        }

        // Set new key and assign the deleter function
        cw->freeKey = setKey(cw, info, cw->key);
    }

    if (setData) {
        setData(cw, info, cw->data);
    }

    // Temporary thing, so that we can free up the data if we want to
    MDB_val tempdata;
    tempdata.mv_size = cw->data.mv_size;
    tempdata.mv_data = cw->data.mv_data;

    // Temporary bookkeeping for the current key
    MDB_val tempKey;
    tempKey.mv_size = cw->key.mv_size;
    tempKey.mv_data = cw->key.mv_data;

    int rc = mdb_cursor_get(cw->cursor, &(cw->key), &(cw->data), op);

    // When cw->key.mv_data changes, it means it points inside the database now,
    // so we should free the old key now.
    if (tempKey.mv_data != cw->key.mv_data) {
        if (cw->freeKey) {
            cw->freeKey(tempKey);
        }

        // No need to free the key that points to the database.
        cw->freeKey = nullptr;
    }

    if (rc == MDB_NOTFOUND) {
        return info.GetReturnValue().Set(Nan::Null());
    }
    else if (rc != 0) {
        return Nan::ThrowError(mdb_strerror(rc));
    }

    Local<Value> keyHandle = Nan::Undefined();
    if (cw->key.mv_size) {
        keyHandle = keyToHandle(cw->key, cw->kt);
    }

    Local<Value> dataHandle = Nan::Undefined();
    if (convertFunc) {
        dataHandle = convertFunc(cw->data);
        if (al > 0 && info[al - 1]->IsFunction()) {
            // In this case, we expect the key/data pair to be correctly filled
            const unsigned argc = 2;
            Local<Value> argv[argc] = { keyHandle, dataHandle };
            Nan::Callback *callback = new Nan::Callback(Local<Function>::Cast(info[info.Length() - 1]));
            callback->Call(argc, argv);
            delete callback;
        }
    }

    if (freeData) {
        freeData(cw, info, tempdata);
    }

    if (convertFunc) {
        return info.GetReturnValue().Set(dataHandle);
    }
    else if (cw->key.mv_size) {
        return info.GetReturnValue().Set(keyHandle);
    }

    return info.GetReturnValue().Set(Nan::True());
}

Nan::NAN_METHOD_RETURN_TYPE CursorWrap::getCommon(Nan::NAN_METHOD_ARGS_TYPE info, MDB_cursor_op op) {
    return getCommon(info, op, nullptr, nullptr, nullptr, nullptr);
}

NAN_METHOD(CursorWrap::getCurrentString) {
    return getCommon(info, MDB_GET_CURRENT, nullptr, nullptr, nullptr, valToString);
}

NAN_METHOD(CursorWrap::getCurrentStringUnsafe) {
    return getCommon(info, MDB_GET_CURRENT, nullptr, nullptr, nullptr, valToStringUnsafe);
}

NAN_METHOD(CursorWrap::getCurrentBinary) {
    return getCommon(info, MDB_GET_CURRENT, nullptr, nullptr, nullptr, valToBinary);
}

NAN_METHOD(CursorWrap::getCurrentBinaryUnsafe) {
    return getCommon(info, MDB_GET_CURRENT, nullptr, nullptr, nullptr, valToBinaryUnsafe);
}

NAN_METHOD(CursorWrap::getCurrentNumber) {
    return getCommon(info, MDB_GET_CURRENT, nullptr, nullptr, nullptr, valToNumber);
}

NAN_METHOD(CursorWrap::getCurrentBoolean) {
    return getCommon(info, MDB_GET_CURRENT, nullptr, nullptr, nullptr, valToBoolean);
}

#define MAKE_GET_FUNC(name, op) NAN_METHOD(CursorWrap::name) { return getCommon(info, op); }

MAKE_GET_FUNC(goToFirst, MDB_FIRST);

MAKE_GET_FUNC(goToLast, MDB_LAST);

MAKE_GET_FUNC(goToNext, MDB_NEXT);

MAKE_GET_FUNC(goToPrev, MDB_PREV);

MAKE_GET_FUNC(goToFirstDup, MDB_FIRST_DUP);

MAKE_GET_FUNC(goToLastDup, MDB_LAST_DUP);

MAKE_GET_FUNC(goToNextDup, MDB_NEXT_DUP);

MAKE_GET_FUNC(goToPrevDup, MDB_PREV_DUP);

NAN_METHOD(CursorWrap::goToKey) {
    return getCommon(info, MDB_SET, [](CursorWrap* cw, Nan::NAN_METHOD_ARGS_TYPE info, MDB_val &key) -> argtokey_callback_t {
        return argToKey(info[0], key, cw->kt);
    }, nullptr, nullptr, nullptr);
}

NAN_METHOD(CursorWrap::goToRange) {
    return getCommon(info, MDB_SET_RANGE, [](CursorWrap* cw, Nan::NAN_METHOD_ARGS_TYPE info, MDB_val &key) -> argtokey_callback_t {
        return argToKey(info[0], key, cw->kt);
    }, nullptr, nullptr, nullptr);
}

static void fillDataFromArg1(CursorWrap* cw, Nan::NAN_METHOD_ARGS_TYPE info, MDB_val &data) {
    if (info[1]->IsString()) {
        CustomExternalStringResource::writeTo(info[1]->ToString(), &data);
    }
    else if (node::Buffer::HasInstance(info[1])) {
        data.mv_size = node::Buffer::Length(info[1]);
        data.mv_data = node::Buffer::Data(info[1]);
    }
    else if (info[1]->IsNumber()) {
        data.mv_size = sizeof(double);
        data.mv_data = new double;
        *((double*)data.mv_data) = info[1]->ToNumber(Nan::GetCurrentContext()).ToLocalChecked()->Value();
    }
    else if (info[1]->IsBoolean()) {
        data.mv_size = sizeof(double);
        data.mv_data = new bool;
        *((bool*)data.mv_data) = info[1]->ToBoolean()->Value();
    }
    else {
        Nan::ThrowError("Invalid data type.");
    }
}

static void freeDataFromArg1(CursorWrap* cw, Nan::NAN_METHOD_ARGS_TYPE info, MDB_val &data) {
    if (info[1]->IsString()) {
        delete[] (uint16_t*)data.mv_data;
    }
    else if (node::Buffer::HasInstance(info[1])) {
        // I think the data is owned by the node::Buffer so we don't need to free it - need to clarify
    }
    else if (info[1]->IsNumber()) {
        delete (double*)data.mv_data;
    }
    else if (info[1]->IsBoolean()) {
        delete (bool*)data.mv_data;
    }
    else {
        Nan::ThrowError("Invalid data type.");
    }
}

NAN_METHOD(CursorWrap::goToDup) {
    return getCommon(info, MDB_GET_BOTH, [](CursorWrap* cw, Nan::NAN_METHOD_ARGS_TYPE info, MDB_val &key) -> argtokey_callback_t {
        return argToKey(info[0], key, cw->kt);
    }, fillDataFromArg1, freeDataFromArg1, nullptr);
}

NAN_METHOD(CursorWrap::goToDupRange) {
    return getCommon(info, MDB_GET_BOTH_RANGE, [](CursorWrap* cw, Nan::NAN_METHOD_ARGS_TYPE info, MDB_val &key) -> argtokey_callback_t {
        return argToKey(info[0], key, cw->kt);
    }, fillDataFromArg1, freeDataFromArg1, nullptr);
}

void CursorWrap::setupExports(Handle<Object> exports) {
    // CursorWrap: Prepare constructor template
    Local<FunctionTemplate> cursorTpl = Nan::New<FunctionTemplate>(CursorWrap::ctor);
    cursorTpl->SetClassName(Nan::New<String>("Cursor").ToLocalChecked());
    cursorTpl->InstanceTemplate()->SetInternalFieldCount(1);
    // CursorWrap: Add functions to the prototype
    cursorTpl->PrototypeTemplate()->Set(Nan::New<String>("close").ToLocalChecked(), Nan::New<FunctionTemplate>(CursorWrap::close));
    cursorTpl->PrototypeTemplate()->Set(Nan::New<String>("getCurrentString").ToLocalChecked(), Nan::New<FunctionTemplate>(CursorWrap::getCurrentString));
    cursorTpl->PrototypeTemplate()->Set(Nan::New<String>("getCurrentStringUnsafe").ToLocalChecked(), Nan::New<FunctionTemplate>(CursorWrap::getCurrentStringUnsafe));
    cursorTpl->PrototypeTemplate()->Set(Nan::New<String>("getCurrentBinary").ToLocalChecked(), Nan::New<FunctionTemplate>(CursorWrap::getCurrentBinary));
    cursorTpl->PrototypeTemplate()->Set(Nan::New<String>("getCurrentBinaryUnsafe").ToLocalChecked(), Nan::New<FunctionTemplate>(CursorWrap::getCurrentBinaryUnsafe));
    cursorTpl->PrototypeTemplate()->Set(Nan::New<String>("getCurrentNumber").ToLocalChecked(), Nan::New<FunctionTemplate>(CursorWrap::getCurrentNumber));
    cursorTpl->PrototypeTemplate()->Set(Nan::New<String>("getCurrentBoolean").ToLocalChecked(), Nan::New<FunctionTemplate>(CursorWrap::getCurrentBoolean));
    cursorTpl->PrototypeTemplate()->Set(Nan::New<String>("goToFirst").ToLocalChecked(), Nan::New<FunctionTemplate>(CursorWrap::goToFirst));
    cursorTpl->PrototypeTemplate()->Set(Nan::New<String>("goToLast").ToLocalChecked(), Nan::New<FunctionTemplate>(CursorWrap::goToLast));
    cursorTpl->PrototypeTemplate()->Set(Nan::New<String>("goToNext").ToLocalChecked(), Nan::New<FunctionTemplate>(CursorWrap::goToNext));
    cursorTpl->PrototypeTemplate()->Set(Nan::New<String>("goToPrev").ToLocalChecked(), Nan::New<FunctionTemplate>(CursorWrap::goToPrev));
    cursorTpl->PrototypeTemplate()->Set(Nan::New<String>("goToKey").ToLocalChecked(), Nan::New<FunctionTemplate>(CursorWrap::goToKey));
    cursorTpl->PrototypeTemplate()->Set(Nan::New<String>("goToRange").ToLocalChecked(), Nan::New<FunctionTemplate>(CursorWrap::goToRange));
    cursorTpl->PrototypeTemplate()->Set(Nan::New<String>("goToFirstDup").ToLocalChecked(), Nan::New<FunctionTemplate>(CursorWrap::goToFirstDup));
    cursorTpl->PrototypeTemplate()->Set(Nan::New<String>("goToLastDup").ToLocalChecked(), Nan::New<FunctionTemplate>(CursorWrap::goToLastDup));
    cursorTpl->PrototypeTemplate()->Set(Nan::New<String>("goToNextDup").ToLocalChecked(), Nan::New<FunctionTemplate>(CursorWrap::goToNextDup));
    cursorTpl->PrototypeTemplate()->Set(Nan::New<String>("goToPrevDup").ToLocalChecked(), Nan::New<FunctionTemplate>(CursorWrap::goToPrevDup));
    cursorTpl->PrototypeTemplate()->Set(Nan::New<String>("goToDup").ToLocalChecked(), Nan::New<FunctionTemplate>(CursorWrap::goToDup));
    cursorTpl->PrototypeTemplate()->Set(Nan::New<String>("goToDupRange").ToLocalChecked(), Nan::New<FunctionTemplate>(CursorWrap::goToDupRange));
    cursorTpl->PrototypeTemplate()->Set(Nan::New<String>("del").ToLocalChecked(), Nan::New<FunctionTemplate>(CursorWrap::del));

    // Set exports
    exports->Set(Nan::New<String>("Cursor").ToLocalChecked(), cursorTpl->GetFunction());
}
<|endoftext|>


<commit_before>/* Copyright 2010 SPARTA, Inc., dba Cobham Analytic Solutions
 *
 * This file is part of WATCHER.
 *
 *     WATCHER is free software: you can redistribute it and/or modify
 *     it under the terms of the GNU Affero General Public License as published by
 *     the Free Software Foundation, either version 3 of the License, or
 *     (at your option) any later version.
 *
 *     WATCHER 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 Affero General Public License for more details.
 *
 *     You should have received a copy of the GNU Affero General Public License
 *     along with Watcher.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <cstdlib>
#include <tr1/memory>

#include <qwt_plot_curve.h>
#include <qwt_plot_marker.h>
#include <qwt_legend.h>
#include <qwt_plot_picker.h>

#include <logger.h>

#include <libwatcher/watcherTypes.h>

#include "seriesGraphDialog.h"

namespace watcher {
namespace ui {

extern Timestamp EpochTS;

} //namespace
} //namespace

namespace {

    const int MinDetail = 5; // the smallest x-range the detail graph can display
    const QColor PlotBackgroundColor(255, 255, 255); // white

/** Convert a Watcher Timestamp to the offset from the first event in the database in seconds. */
double tsToOffset(watcher::Timestamp t)
{
    return (t - watcher::ui::EpochTS) / 1000.0;
}

} //namespace

namespace watcher {
namespace ui {

INIT_LOGGER(SeriesGraphDialog, "SeriesGraphDialog");

/** Contains information for each node in the dialog. */
class NodeInfo {
    QString id;
    std::vector<double> xdata;
    std::vector<double> ydata;

    //QwtPlotCurve is not a QObject, so QPointer is not an option.
    std::tr1::shared_ptr<QwtPlotCurve> detailCurve;
    std::tr1::shared_ptr<QwtPlotCurve> globalCurve;

    //this pointer is owned by the listWidget, and should be destroyed when the listWidget is destroyed
    QListWidgetItem *item; // widget item for this node

    bool attached; // are the QwtPlotCurve(s) attached to the QwtPlot?

    public:

    NodeInfo(const QString& id_, QwtPlot *detailPlot, QwtPlot *globalPlot) : id(id_), attached(false) {
	QPen pen(QColor(random() % 256, random() % 256, random() % 256));
	// randomly assign a different pen style
	const int numStyles = Qt::DashDotDotLine - Qt::SolidLine;
	pen.setStyle(Qt::PenStyle(Qt::SolidLine + rand() % numStyles));

	detailCurve.reset(new QwtPlotCurve(id_));
	detailCurve->setPen(pen);

	globalCurve.reset(new QwtPlotCurve(id_));
	globalCurve->setPen(pen);

	attachPlot(detailPlot, globalPlot);

	item = new QListWidgetItem(id_, 0, QListWidgetItem::UserType);
    }

    void data_point(qlonglong when, double value) {
	/* the x-axis (time) is number of seconds elapsed since the first event in the stream */
	double t = (double)(when - EpochTS) / 1000.0;
	if (xdata.size() && t <= xdata[0]) {
	    xdata.insert(xdata.begin(), t);
	    ydata.insert(ydata.begin(), value);
	} else {
	    xdata.push_back(t);
	    ydata.push_back(value);
	}
	detailCurve->setRawData(&xdata[0], &ydata[0], xdata.size());
	globalCurve->setRawData(&xdata[0], &ydata[0], xdata.size());
    }

    void attachPlot(QwtPlot *detailPlot, QwtPlot *globalPlot) {
	if (!attached) {
	    detailCurve->attach(detailPlot);
	    globalCurve->attach(globalPlot);
	    attached = true;
	}
    }

    void detachPlot() {
	if (attached) {
	    detailCurve->detach();
	    globalCurve->detach();
	    attached = false;
	}
    }

    QListWidgetItem* getItem() { return item; }
    QString& getId() { return id; }

}; // NodeInfo

SeriesGraphDialog::SeriesGraphDialog(const QString& name) : firstEvent(-1), lastEvent(0), detailBegin(0), detailEnd(0)
{
    TRACE_ENTER();

    setupUi(this);
    setWindowTitle(QString::fromUtf8("DataWatcher: ") + name);

    globalPlot->insertLegend(new QwtLegend);
    globalPlot->setAxisTitle(QwtPlot::xBottom, QString::fromUtf8("time (s)"));
    globalPlot->setAxisTitle(QwtPlot::yLeft, name);
    globalPlot->setCanvasBackground(PlotBackgroundColor);

    detailPlot->insertLegend(new QwtLegend);
    detailPlot->setAxisTitle(QwtPlot::xBottom, QString::fromUtf8("time (s)"));
    detailPlot->setAxisTitle(QwtPlot::yLeft, name);
    detailPlot->setCanvasBackground(PlotBackgroundColor);

    detailTimeMarker.reset(new QwtPlotMarker);
    detailTimeMarker->setLineStyle(QwtPlotMarker::VLine);
    detailTimeMarker->setLinePen(QPen(QColor(0, 255, 0)));
    detailTimeMarker->attach(detailPlot);

    detailBeginMarker.reset(new QwtPlotMarker);
    detailBeginMarker->setLineStyle(QwtPlotMarker::VLine);
    detailBeginMarker->setLinePen(QPen(QColor(0, 0, 255)));
    detailBeginMarker->attach(globalPlot);

    detailEndMarker.reset(new QwtPlotMarker);
    detailEndMarker->setLineStyle(QwtPlotMarker::VLine);
    detailEndMarker->setLinePen(QPen(QColor(0, 0, 255)));
    detailEndMarker->attach(globalPlot);

    globalTimeMarker.reset(new QwtPlotMarker);
    globalTimeMarker->setLineStyle(QwtPlotMarker::VLine);
    globalTimeMarker->setLinePen(QPen(QColor(0, 255, 0)));
    globalTimeMarker->attach(globalPlot);

    QObject::connect(beginSlider, SIGNAL(valueChanged(int)), this, SLOT(setDetailBegin(int)));
    QObject::connect(endSlider, SIGNAL(valueChanged(int)), this, SLOT(setDetailEnd(int)));

    //beginSlider->setTickInterval(MinDetail);
    //beginSlider->setTickPosition(QSlider::TicksBelow);
    beginSlider->setTracking(true);

    //endSlider->setTickInterval(MinDetail);
    //endSlider->setTickPosition(QSlider::TicksBelow);
    endSlider->setTracking(true);

    detailPicker.reset(new QwtPlotPicker(QwtPlot::xBottom, QwtPlot::yLeft, detailPlot->canvas()));
    detailPicker->setSelectionFlags(QwtPicker::PointSelection);
    globalPicker.reset(new QwtPlotPicker(QwtPlot::xBottom, QwtPlot::yLeft, globalPlot->canvas()));
    globalPicker->setSelectionFlags(QwtPicker::PointSelection);
    QObject::connect(detailPicker.get(), SIGNAL(selected(const QwtDoublePoint&)), this, SLOT(plotClicked(const QwtDoublePoint&)));
    QObject::connect(globalPicker.get(), SIGNAL(selected(const QwtDoublePoint&)), this, SLOT(plotClicked(const QwtDoublePoint&)));

    QObject::connect(listWidget, SIGNAL(itemSelectionChanged()), this, SLOT(selectionChanged()));

    TRACE_EXIT();
}

SeriesGraphDialog::~SeriesGraphDialog()
{
    TRACE_ENTER();
    TRACE_EXIT();
}

/** Add a data point for a node to this series.
 * @param fromID id of the sender of this data point
 * @param when the timestamp when the data point was generated (x-axis)
 * @param value the value of the data point (y-axis)
 */
void SeriesGraphDialog::dataPoint(const QString& fromID, qlonglong when, double value)
{
    TRACE_ENTER();

    /* ignore events with timestamps less than the max received so far.  this is to avoid
     * duplicate values when seeking/rewinding.
     */
    if (when >= lastEvent) {
	NodeInfoPtr nodeInfo;
	NodeMap::iterator it = nodeMap.find(fromID);
	if (it == nodeMap.end()) {
	    // not found
	    nodeInfo = NodeInfoPtr(new NodeInfo(fromID, detailPlot, globalPlot));
	    nodeMap[fromID] = nodeInfo;
	    listWidget->addItem(nodeInfo->getItem());
	    // automatically select new nodes
	    int row = listWidget->row(nodeInfo->getItem());
	    listWidget->setCurrentRow(row, QItemSelectionModel::SelectCurrent);
	} else {
	    nodeInfo = it->second;
	}
	nodeInfo->data_point(when, value);
    }

    if (when < firstEvent) {
	firstEvent = when;
	double start = tsToOffset(firstEvent);
	beginSlider->setMinimum(start);
	endSlider->setMinimum(start);
    }
    if (when > lastEvent) {
	lastEvent = when;
	double end = tsToOffset(lastEvent);
	beginSlider->setMaximum(end);
	if (detailEnd == endSlider->maximum()) {
	    // auto-scrolling
	    endSlider->setMaximum(end);
	    endSlider->setValue(end);
	} else {
	    endSlider->setMaximum(end);
	}
    }

    handleClock(when);
    TRACE_EXIT();
}

/** Updates the current time and redraws the plot.
 * @param t timestamp to use as the current time.
 */
void SeriesGraphDialog::handleClock(qlonglong t)
{
    TRACE_ENTER();

    globalTimeMarker->setXValue(tsToOffset(t));
    detailTimeMarker->setXValue(tsToOffset(t));
    replot();

    TRACE_EXIT();
}

void SeriesGraphDialog::adjustDetail()
{
    // force a reasonable minimum width for the x-axis
    if (detailEnd - detailBegin < MinDetail)
	detailEnd = detailBegin + MinDetail;
    detailPlot->setAxisScale(QwtPlot::xBottom, detailBegin, detailEnd);

    detailBeginMarker->setXValue(detailBegin);
    detailEndMarker->setXValue(detailEnd);

    replot();
}

/** Slot for receiving the value from the slider that controls the starting offset of the detail graph. */
void SeriesGraphDialog::setDetailBegin(int val)
{
    TRACE_ENTER();
    detailBegin = val;
    adjustDetail();
    TRACE_EXIT();
}

/** Slot for receiving the value from the slider that controls the ending offset of the detail graph. */
void SeriesGraphDialog::setDetailEnd(int val)
{
    TRACE_ENTER();
    detailEnd = val;
    adjustDetail();
    TRACE_EXIT();
}

/** Slot for receiving the coordinates when the user clicks on the plot. */
void SeriesGraphDialog::plotClicked(const QwtDoublePoint& p)
{
    TRACE_ENTER();
    LOG_INFO("user clicked on the point (" << p.x() << ", " << p.y() << ")");
    emit seekStream(EpochTS + p.x() * 1000.0);
    TRACE_EXIT();
}

/** Slot for handling changes to the node list widget selection. */
void SeriesGraphDialog::selectionChanged()
{
    TRACE_ENTER();
    QList<QListWidgetItem *> selection = listWidget->selectedItems();
    for (NodeMap::iterator it = nodeMap.begin(); it != nodeMap.end(); ++it) {
	NodeInfoPtr node = it->second;

	if (selection.contains(node->getItem())) {
	    LOG_INFO("selection contains " << node->getId().toStdString());
	    node->attachPlot(detailPlot, globalPlot);
	} else
	    node->detachPlot();
    }
    replot();
    TRACE_EXIT();
}

/** Update both graphs. */
void SeriesGraphDialog::replot()
{
    TRACE_ENTER();
    detailPlot->replot();
    globalPlot->replot();
    TRACE_EXIT();
}

} // namespace
} // namespace

// vim:sw=4
<commit_msg>Backed out changeset 3fcadc7c5966<commit_after>/* Copyright 2010 SPARTA, Inc., dba Cobham Analytic Solutions
 *
 * This file is part of WATCHER.
 *
 *     WATCHER is free software: you can redistribute it and/or modify
 *     it under the terms of the GNU Affero General Public License as published by
 *     the Free Software Foundation, either version 3 of the License, or
 *     (at your option) any later version.
 *
 *     WATCHER 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 Affero General Public License for more details.
 *
 *     You should have received a copy of the GNU Affero General Public License
 *     along with Watcher.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <cstdlib>
#include <tr1/memory>

#include <qwt_plot_curve.h>
#include <qwt_plot_marker.h>
#include <qwt_legend.h>
#include <qwt_plot_picker.h>

#include <logger.h>

#include <libwatcher/watcherTypes.h>

#include "seriesGraphDialog.h"

namespace watcher {
namespace ui {

extern Timestamp EpochTS;

} //namespace
} //namespace

namespace {

    const int MinDetail = 5; // the smallest x-range the detail graph can display
    const QColor PlotBackgroundColor(255, 255, 255); // white

/** Convert a Watcher Timestamp to the offset from the first event in the database in seconds. */
double tsToOffset(watcher::Timestamp t)
{
    return (t - watcher::ui::EpochTS) / 1000.0;
}

} //namespace

namespace watcher {
namespace ui {

INIT_LOGGER(SeriesGraphDialog, "SeriesGraphDialog");

/** Contains information for each node in the dialog. */
class NodeInfo {
    QString id;
    std::vector<double> xdata;
    std::vector<double> ydata;

    //QwtPlotCurve is not a QObject, so QPointer is not an option.
    std::tr1::shared_ptr<QwtPlotCurve> detailCurve;
    std::tr1::shared_ptr<QwtPlotCurve> globalCurve;

    //this pointer is owned by the listWidget, and should be destroyed when the listWidget is destroyed
    QListWidgetItem *item; // widget item for this node

    bool attached; // are the QwtPlotCurve(s) attached to the QwtPlot?

    public:

    NodeInfo(const QString& id_, QwtPlot *detailPlot, QwtPlot *globalPlot) : id(id_), attached(false) {
	QPen pen(QColor(random() % 256, random() % 256, random() % 256));

	detailCurve.reset(new QwtPlotCurve(id_));
	detailCurve->setPen(pen);

	globalCurve.reset(new QwtPlotCurve(id_));
	globalCurve->setPen(pen);

	attachPlot(detailPlot, globalPlot);

	item = new QListWidgetItem(id_, 0, QListWidgetItem::UserType);
    }

    void data_point(qlonglong when, double value) {
	/* the x-axis (time) is number of seconds elapsed since the first event in the stream */
	double t = (double)(when - EpochTS) / 1000.0;
	if (xdata.size() && t <= xdata[0]) {
	    xdata.insert(xdata.begin(), t);
	    ydata.insert(ydata.begin(), value);
	} else {
	    xdata.push_back(t);
	    ydata.push_back(value);
	}
	detailCurve->setRawData(&xdata[0], &ydata[0], xdata.size());
	globalCurve->setRawData(&xdata[0], &ydata[0], xdata.size());
    }

    void attachPlot(QwtPlot *detailPlot, QwtPlot *globalPlot) {
	if (!attached) {
	    detailCurve->attach(detailPlot);
	    globalCurve->attach(globalPlot);
	    attached = true;
	}
    }

    void detachPlot() {
	if (attached) {
	    detailCurve->detach();
	    globalCurve->detach();
	    attached = false;
	}
    }

    QListWidgetItem* getItem() { return item; }
    QString& getId() { return id; }

}; // NodeInfo

SeriesGraphDialog::SeriesGraphDialog(const QString& name) : firstEvent(-1), lastEvent(0), detailBegin(0), detailEnd(0)
{
    TRACE_ENTER();

    setupUi(this);
    setWindowTitle(QString::fromUtf8("DataWatcher: ") + name);

    globalPlot->insertLegend(new QwtLegend);
    globalPlot->setAxisTitle(QwtPlot::xBottom, QString::fromUtf8("time (s)"));
    globalPlot->setAxisTitle(QwtPlot::yLeft, name);
    globalPlot->setCanvasBackground(PlotBackgroundColor);

    detailPlot->insertLegend(new QwtLegend);
    detailPlot->setAxisTitle(QwtPlot::xBottom, QString::fromUtf8("time (s)"));
    detailPlot->setAxisTitle(QwtPlot::yLeft, name);
    detailPlot->setCanvasBackground(PlotBackgroundColor);

    detailTimeMarker.reset(new QwtPlotMarker);
    detailTimeMarker->setLineStyle(QwtPlotMarker::VLine);
    detailTimeMarker->setLinePen(QPen(QColor(0, 255, 0)));
    detailTimeMarker->attach(detailPlot);

    detailBeginMarker.reset(new QwtPlotMarker);
    detailBeginMarker->setLineStyle(QwtPlotMarker::VLine);
    detailBeginMarker->setLinePen(QPen(QColor(0, 0, 255)));
    detailBeginMarker->attach(globalPlot);

    detailEndMarker.reset(new QwtPlotMarker);
    detailEndMarker->setLineStyle(QwtPlotMarker::VLine);
    detailEndMarker->setLinePen(QPen(QColor(0, 0, 255)));
    detailEndMarker->attach(globalPlot);

    globalTimeMarker.reset(new QwtPlotMarker);
    globalTimeMarker->setLineStyle(QwtPlotMarker::VLine);
    globalTimeMarker->setLinePen(QPen(QColor(0, 255, 0)));
    globalTimeMarker->attach(globalPlot);

    QObject::connect(beginSlider, SIGNAL(valueChanged(int)), this, SLOT(setDetailBegin(int)));
    QObject::connect(endSlider, SIGNAL(valueChanged(int)), this, SLOT(setDetailEnd(int)));

    //beginSlider->setTickInterval(MinDetail);
    //beginSlider->setTickPosition(QSlider::TicksBelow);
    beginSlider->setTracking(true);

    //endSlider->setTickInterval(MinDetail);
    //endSlider->setTickPosition(QSlider::TicksBelow);
    endSlider->setTracking(true);

    detailPicker.reset(new QwtPlotPicker(QwtPlot::xBottom, QwtPlot::yLeft, detailPlot->canvas()));
    detailPicker->setSelectionFlags(QwtPicker::PointSelection);
    globalPicker.reset(new QwtPlotPicker(QwtPlot::xBottom, QwtPlot::yLeft, globalPlot->canvas()));
    globalPicker->setSelectionFlags(QwtPicker::PointSelection);
    QObject::connect(detailPicker.get(), SIGNAL(selected(const QwtDoublePoint&)), this, SLOT(plotClicked(const QwtDoublePoint&)));
    QObject::connect(globalPicker.get(), SIGNAL(selected(const QwtDoublePoint&)), this, SLOT(plotClicked(const QwtDoublePoint&)));

    QObject::connect(listWidget, SIGNAL(itemSelectionChanged()), this, SLOT(selectionChanged()));

    TRACE_EXIT();
}

SeriesGraphDialog::~SeriesGraphDialog()
{
    TRACE_ENTER();
    TRACE_EXIT();
}

/** Add a data point for a node to this series.
 * @param fromID id of the sender of this data point
 * @param when the timestamp when the data point was generated (x-axis)
 * @param value the value of the data point (y-axis)
 */
void SeriesGraphDialog::dataPoint(const QString& fromID, qlonglong when, double value)
{
    TRACE_ENTER();

    /* ignore events with timestamps less than the max received so far.  this is to avoid
     * duplicate values when seeking/rewinding.
     */
    if (when >= lastEvent) {
	NodeInfoPtr nodeInfo;
	NodeMap::iterator it = nodeMap.find(fromID);
	if (it == nodeMap.end()) {
	    // not found
	    nodeInfo = NodeInfoPtr(new NodeInfo(fromID, detailPlot, globalPlot));
	    nodeMap[fromID] = nodeInfo;
	    listWidget->addItem(nodeInfo->getItem());
	    // automatically select new nodes
	    int row = listWidget->row(nodeInfo->getItem());
	    listWidget->setCurrentRow(row, QItemSelectionModel::SelectCurrent);
	} else {
	    nodeInfo = it->second;
	}
	nodeInfo->data_point(when, value);
    }

    if (when < firstEvent) {
	firstEvent = when;
	double start = tsToOffset(firstEvent);
	beginSlider->setMinimum(start);
	endSlider->setMinimum(start);
    }
    if (when > lastEvent) {
	lastEvent = when;
	double end = tsToOffset(lastEvent);
	beginSlider->setMaximum(end);
	if (detailEnd == endSlider->maximum()) {
	    // auto-scrolling
	    endSlider->setMaximum(end);
	    endSlider->setValue(end);
	} else {
	    endSlider->setMaximum(end);
	}
    }

    handleClock(when);
    TRACE_EXIT();
}

/** Updates the current time and redraws the plot.
 * @param t timestamp to use as the current time.
 */
void SeriesGraphDialog::handleClock(qlonglong t)
{
    TRACE_ENTER();

    globalTimeMarker->setXValue(tsToOffset(t));
    detailTimeMarker->setXValue(tsToOffset(t));
    replot();

    TRACE_EXIT();
}

void SeriesGraphDialog::adjustDetail()
{
    // force a reasonable minimum width for the x-axis
    if (detailEnd - detailBegin < MinDetail)
	detailEnd = detailBegin + MinDetail;
    detailPlot->setAxisScale(QwtPlot::xBottom, detailBegin, detailEnd);

    detailBeginMarker->setXValue(detailBegin);
    detailEndMarker->setXValue(detailEnd);

    replot();
}

/** Slot for receiving the value from the slider that controls the starting offset of the detail graph. */
void SeriesGraphDialog::setDetailBegin(int val)
{
    TRACE_ENTER();
    detailBegin = val;
    adjustDetail();
    TRACE_EXIT();
}

/** Slot for receiving the value from the slider that controls the ending offset of the detail graph. */
void SeriesGraphDialog::setDetailEnd(int val)
{
    TRACE_ENTER();
    detailEnd = val;
    adjustDetail();
    TRACE_EXIT();
}

/** Slot for receiving the coordinates when the user clicks on the plot. */
void SeriesGraphDialog::plotClicked(const QwtDoublePoint& p)
{
    TRACE_ENTER();
    LOG_INFO("user clicked on the point (" << p.x() << ", " << p.y() << ")");
    emit seekStream(EpochTS + p.x() * 1000.0);
    TRACE_EXIT();
}

/** Slot for handling changes to the node list widget selection. */
void SeriesGraphDialog::selectionChanged()
{
    TRACE_ENTER();
    QList<QListWidgetItem *> selection = listWidget->selectedItems();
    for (NodeMap::iterator it = nodeMap.begin(); it != nodeMap.end(); ++it) {
	NodeInfoPtr node = it->second;

	if (selection.contains(node->getItem())) {
	    LOG_INFO("selection contains " << node->getId().toStdString());
	    node->attachPlot(detailPlot, globalPlot);
	} else
	    node->detachPlot();
    }
    replot();
    TRACE_EXIT();
}

/** Update both graphs. */
void SeriesGraphDialog::replot()
{
    TRACE_ENTER();
    detailPlot->replot();
    globalPlot->replot();
    TRACE_EXIT();
}

} // namespace
} // namespace

// vim:sw=4
<|endoftext|>


<commit_before>/* Copyright 2016 Streampunk Media Ltd

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

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

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

/* -LICENSE-START-
 ** Copyright (c) 2010 Blackmagic Design
 **
 ** Permission is hereby granted, free of charge, to any person or organization
 ** obtaining a copy of the software and accompanying documentation covered by
 ** this license (the "Software") to use, reproduce, display, distribute,
 ** execute, and transmit the Software, and to prepare derivative works of the
 ** Software, and to permit third-parties to whom the Software is furnished to
 ** do so, all subject to the following:
 **
 ** The copyright notices in the Software and this entire statement, including
 ** the above license grant, this restriction and the following disclaimer,
 ** must be included in all copies of the Software, in whole or in part, and
 ** all derivative works of the Software, unless such copies or derivative
 ** works are solely in the form of machine-executable object code generated by
 ** a source language processor.
 **
 ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
 ** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
 ** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
 ** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 ** DEALINGS IN THE SOFTWARE.
 ** -LICENSE-END-
 */

#include "Capture.h"

namespace streampunk {

using v8::Function;
using v8::FunctionCallbackInfo;
using v8::FunctionTemplate;
using v8::Isolate;
using v8::Local;
using v8::Number;
using v8::Object;
using v8::Persistent;
using v8::String;
using v8::Value;
using v8::EscapableHandleScope;
using v8::HandleScope;
using v8::Exception;

Persistent<Function> Capture::constructor;

Capture::Capture(uint32_t deviceIndex, uint32_t displayMode,
    uint32_t pixelFormat) : deviceIndex_(deviceIndex),
    displayMode_(displayMode), pixelFormat_(pixelFormat), latestFrame_(NULL) {
  async = new uv_async_t;
  uv_async_init(uv_default_loop(), async, FrameCallback);
  async->data = this;
}

Capture::~Capture() {
  if (!captureCB_.IsEmpty())
    captureCB_.Reset();
}

void Capture::Init(Local<Object> exports) {
  Isolate* isolate = exports->GetIsolate();

  #ifdef WIN32
  HRESULT result;
  result = CoInitialize(NULL);
	if (FAILED(result))
	{
		fprintf(stderr, "Initialization of COM failed - result = %08x.\n", result);
	}
  #endif

  // Prepare constructor template
  Local<FunctionTemplate> tpl = FunctionTemplate::New(isolate, New);
  tpl->SetClassName(String::NewFromUtf8(isolate, "Capture"));
  tpl->InstanceTemplate()->SetInternalFieldCount(1);

  // Prototype
  NODE_SET_PROTOTYPE_METHOD(tpl, "init", BMInit);
  NODE_SET_PROTOTYPE_METHOD(tpl, "doCapture", DoCapture);
  NODE_SET_PROTOTYPE_METHOD(tpl, "stop", StopCapture);

  constructor.Reset(isolate, tpl->GetFunction());
  exports->Set(String::NewFromUtf8(isolate, "Capture"),
               tpl->GetFunction());
}

void Capture::New(const FunctionCallbackInfo<Value>& args) {
  Isolate* isolate = args.GetIsolate();

  if (args.IsConstructCall()) {
    // Invoked as constructor: `new Capture(...)`
    double deviceIndex = args[0]->IsUndefined() ? 0 : args[0]->NumberValue();
    double displayMode = args[1]->IsUndefined() ? 0 : args[1]->NumberValue();
    double pixelFormat = args[2]->IsUndefined() ? 0 : args[2]->NumberValue();
    Capture* obj = new Capture(deviceIndex, displayMode, pixelFormat);
    obj->Wrap(args.This());
    args.GetReturnValue().Set(args.This());
  } else {
    // Invoked as plain function `Capture(...)`, turn into construct call.
    const int argc = 3;
    Local<Value> argv[argc] = { args[0], args[1], args[2] };
    Local<Function> cons = Local<Function>::New(isolate, constructor);
    args.GetReturnValue().Set(cons->NewInstance(argc, argv));
  }
}

void Capture::BMInit(const FunctionCallbackInfo<Value>& args) {
  Isolate* isolate = args.GetIsolate();

  IDeckLinkIterator* deckLinkIterator;
  HRESULT	result;
  IDeckLinkAPIInformation *deckLinkAPIInformation;
  IDeckLink* deckLink;
  #ifdef WIN32
  CoCreateInstance(CLSID_CDeckLinkIterator, NULL, CLSCTX_ALL, IID_IDeckLinkIterator, (void**)&deckLinkIterator);
  #else
  deckLinkIterator = CreateDeckLinkIteratorInstance();
  #endif
  result = deckLinkIterator->QueryInterface(IID_IDeckLinkAPIInformation, (void**)&deckLinkAPIInformation);
  if (result != S_OK) {
    isolate->ThrowException(Exception::Error(
      String::NewFromUtf8(isolate, "Error connecting to DeckLinkAPI.")));
  }
  Capture* obj = ObjectWrap::Unwrap<Capture>(args.Holder());

  for ( uint32_t x = 0 ; x <= obj->deviceIndex_ ; x++ ) {
    if (deckLinkIterator->Next(&deckLink) != S_OK) {
      args.GetReturnValue().Set(Undefined(isolate));
      return;
    }
  }

  obj->m_deckLink = deckLink;

  IDeckLinkInput *deckLinkInput;
  if (deckLink->QueryInterface(IID_IDeckLinkInput, (void **)&deckLinkInput) != S_OK)
	{
    isolate->ThrowException(Exception::Error(
      String::NewFromUtf8(isolate, "Could not obtain the DeckLink Input interface.")));
	}
  obj->m_deckLinkInput = deckLinkInput;
  if (deckLinkInput)
    args.GetReturnValue().Set(String::NewFromUtf8(isolate, "made it!"));
  else
    args.GetReturnValue().Set(String::NewFromUtf8(isolate, "sad :-("));
}

void Capture::DoCapture(const FunctionCallbackInfo<Value>& args) {
  Isolate* isolate = args.GetIsolate();

  Local<Function> cb = Local<Function>::Cast(args[0]);
  Capture* obj = ObjectWrap::Unwrap<Capture>(args.Holder());
  obj->captureCB_.Reset(isolate, cb);

  obj->setupDeckLinkInput();

  args.GetReturnValue().Set(String::NewFromUtf8(isolate, "Capture started."));
}

void Capture::StopCapture(const v8::FunctionCallbackInfo<v8::Value>& args) {
  Isolate* isolate = args.GetIsolate();

  Capture* obj = ObjectWrap::Unwrap<Capture>(args.Holder());

  obj->cleanupDeckLinkInput();

  args.GetReturnValue().Set(String::NewFromUtf8(isolate, "Capture stopped."));
}

// Stop video input
void Capture::cleanupDeckLinkInput()
{
	m_deckLinkInput->StopStreams();
	m_deckLinkInput->DisableVideoInput();
	m_deckLinkInput->SetCallback(NULL);
}

bool Capture::setupDeckLinkInput() {
  // bool result = false;
  IDeckLinkDisplayModeIterator*	displayModeIterator = NULL;
  IDeckLinkDisplayMode*			deckLinkDisplayMode = NULL;

  m_width = -1;

  // get frame scale and duration for the video mode
  if (m_deckLinkInput->GetDisplayModeIterator(&displayModeIterator) != S_OK)
    return false;

  while (displayModeIterator->Next(&deckLinkDisplayMode) == S_OK)
  {
    if (deckLinkDisplayMode->GetDisplayMode() == displayMode_)
    {
      m_width = deckLinkDisplayMode->GetWidth();
      m_height = deckLinkDisplayMode->GetHeight();
      deckLinkDisplayMode->GetFrameRate(&m_frameDuration, &m_timeScale);
      deckLinkDisplayMode->Release();

      break;
    }

    deckLinkDisplayMode->Release();
  }

  printf("Width %li Height %li\n", m_width, m_height);

  displayModeIterator->Release();

  if (m_width == -1)
    return false;

  m_deckLinkInput->SetCallback(this);

  if (m_deckLinkInput->EnableVideoInput((BMDDisplayMode) displayMode_, (BMDPixelFormat) pixelFormat_, bmdVideoInputFlagDefault) != S_OK)
	  return false;

  if (m_deckLinkInput->StartStreams() != S_OK)
    return false;

  return true;
}

HRESULT	Capture::VideoInputFrameArrived (IDeckLinkVideoInputFrame* arrivedFrame, IDeckLinkAudioInputPacket*)
{
  arrivedFrame->AddRef();
  latestFrame_ = arrivedFrame;
  uv_async_send(async);
  return S_OK;
}

HRESULT	Capture::VideoInputFormatChanged (BMDVideoInputFormatChangedEvents notificationEvents, IDeckLinkDisplayMode* newDisplayMode, BMDDetectedVideoInputFormatFlags detectedSignalFlags) {
  return S_OK;
};

void Capture::TestUV() {
  uv_async_send(async);
}

void FreeCallback(char* data, void* hint) {
  Isolate* isolate = v8::Isolate::GetCurrent();
  IDeckLinkVideoInputFrame* frame = static_cast<IDeckLinkVideoInputFrame*>(hint);
  isolate->AdjustAmountOfExternalAllocatedMemory(-(frame->GetRowBytes() * frame->GetHeight()));
  frame->Release();
}

void Capture::FrameCallback(uv_async_t *handle) {
  Isolate* isolate = v8::Isolate::GetCurrent();
  HandleScope scope(isolate);
  Capture *capture = static_cast<Capture*>(handle->data);
  Local<Function> cb = Local<Function>::New(isolate, capture->captureCB_);
  char* new_data;
  capture->latestFrame_->GetBytes((void**) &new_data);
  long new_data_size = capture->latestFrame_->GetRowBytes() * capture->latestFrame_->GetHeight();
  // Local<Object> b = node::Buffer::New(isolate, new_data, new_data_size,
  //   FreeCallback, capture->latestFrame_).ToLocalChecked();
  Local<Object> b = node::Buffer::Copy(isolate, new_data, new_data_size).ToLocalChecked();
  capture->latestFrame_->Release();
  // long extSize = isolate->AdjustAmountOfExternalAllocatedMemory(new_data_size);
  // if (extSize > 100000000) {
  //   isolate->LowMemoryNotification();
  //   printf("Requesting bin collection.\n");
  // }
  Local<Value> argv[1] = { b };
  cb->Call(Null(isolate), 1, argv);
}

}
<commit_msg>Chaning over to buffer copy rather than frame release.<commit_after>/* Copyright 2016 Streampunk Media Ltd

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

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

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

/* -LICENSE-START-
 ** Copyright (c) 2010 Blackmagic Design
 **
 ** Permission is hereby granted, free of charge, to any person or organization
 ** obtaining a copy of the software and accompanying documentation covered by
 ** this license (the "Software") to use, reproduce, display, distribute,
 ** execute, and transmit the Software, and to prepare derivative works of the
 ** Software, and to permit third-parties to whom the Software is furnished to
 ** do so, all subject to the following:
 **
 ** The copyright notices in the Software and this entire statement, including
 ** the above license grant, this restriction and the following disclaimer,
 ** must be included in all copies of the Software, in whole or in part, and
 ** all derivative works of the Software, unless such copies or derivative
 ** works are solely in the form of machine-executable object code generated by
 ** a source language processor.
 **
 ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
 ** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
 ** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
 ** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 ** DEALINGS IN THE SOFTWARE.
 ** -LICENSE-END-
 */

#include "Capture.h"

namespace streampunk {

using v8::Function;
using v8::FunctionCallbackInfo;
using v8::FunctionTemplate;
using v8::Isolate;
using v8::Local;
using v8::Number;
using v8::Object;
using v8::Persistent;
using v8::String;
using v8::Value;
using v8::EscapableHandleScope;
using v8::HandleScope;
using v8::Exception;

Persistent<Function> Capture::constructor;

Capture::Capture(uint32_t deviceIndex, uint32_t displayMode,
    uint32_t pixelFormat) : deviceIndex_(deviceIndex),
    displayMode_(displayMode), pixelFormat_(pixelFormat), latestFrame_(NULL) {
  async = new uv_async_t;
  uv_async_init(uv_default_loop(), async, FrameCallback);
  async->data = this;
}

Capture::~Capture() {
  if (!captureCB_.IsEmpty())
    captureCB_.Reset();
}

void Capture::Init(Local<Object> exports) {
  Isolate* isolate = exports->GetIsolate();

  #ifdef WIN32
  HRESULT result;
  result = CoInitialize(NULL);
	if (FAILED(result))
	{
		fprintf(stderr, "Initialization of COM failed - result = %08x.\n", result);
	}
  #endif

  // Prepare constructor template
  Local<FunctionTemplate> tpl = FunctionTemplate::New(isolate, New);
  tpl->SetClassName(String::NewFromUtf8(isolate, "Capture"));
  tpl->InstanceTemplate()->SetInternalFieldCount(1);

  // Prototype
  NODE_SET_PROTOTYPE_METHOD(tpl, "init", BMInit);
  NODE_SET_PROTOTYPE_METHOD(tpl, "doCapture", DoCapture);
  NODE_SET_PROTOTYPE_METHOD(tpl, "stop", StopCapture);

  constructor.Reset(isolate, tpl->GetFunction());
  exports->Set(String::NewFromUtf8(isolate, "Capture"),
               tpl->GetFunction());
}

void Capture::New(const FunctionCallbackInfo<Value>& args) {
  Isolate* isolate = args.GetIsolate();

  if (args.IsConstructCall()) {
    // Invoked as constructor: `new Capture(...)`
    double deviceIndex = args[0]->IsUndefined() ? 0 : args[0]->NumberValue();
    double displayMode = args[1]->IsUndefined() ? 0 : args[1]->NumberValue();
    double pixelFormat = args[2]->IsUndefined() ? 0 : args[2]->NumberValue();
    Capture* obj = new Capture(deviceIndex, displayMode, pixelFormat);
    obj->Wrap(args.This());
    args.GetReturnValue().Set(args.This());
  } else {
    // Invoked as plain function `Capture(...)`, turn into construct call.
    const int argc = 3;
    Local<Value> argv[argc] = { args[0], args[1], args[2] };
    Local<Function> cons = Local<Function>::New(isolate, constructor);
    args.GetReturnValue().Set(cons->NewInstance(argc, argv));
  }
}

void Capture::BMInit(const FunctionCallbackInfo<Value>& args) {
  Isolate* isolate = args.GetIsolate();

  IDeckLinkIterator* deckLinkIterator;
  HRESULT	result;
  IDeckLinkAPIInformation *deckLinkAPIInformation;
  IDeckLink* deckLink;
  #ifdef WIN32
  CoCreateInstance(CLSID_CDeckLinkIterator, NULL, CLSCTX_ALL, IID_IDeckLinkIterator, (void**)&deckLinkIterator);
  #else
  deckLinkIterator = CreateDeckLinkIteratorInstance();
  #endif
  result = deckLinkIterator->QueryInterface(IID_IDeckLinkAPIInformation, (void**)&deckLinkAPIInformation);
  if (result != S_OK) {
    isolate->ThrowException(Exception::Error(
      String::NewFromUtf8(isolate, "Error connecting to DeckLinkAPI.")));
  }
  Capture* obj = ObjectWrap::Unwrap<Capture>(args.Holder());

  for ( uint32_t x = 0 ; x <= obj->deviceIndex_ ; x++ ) {
    if (deckLinkIterator->Next(&deckLink) != S_OK) {
      args.GetReturnValue().Set(Undefined(isolate));
      return;
    }
  }

  obj->m_deckLink = deckLink;

  IDeckLinkInput *deckLinkInput;
  if (deckLink->QueryInterface(IID_IDeckLinkInput, (void **)&deckLinkInput) != S_OK)
	{
    isolate->ThrowException(Exception::Error(
      String::NewFromUtf8(isolate, "Could not obtain the DeckLink Input interface.")));
	}
  obj->m_deckLinkInput = deckLinkInput;
  if (deckLinkInput)
    args.GetReturnValue().Set(String::NewFromUtf8(isolate, "made it!"));
  else
    args.GetReturnValue().Set(String::NewFromUtf8(isolate, "sad :-("));
}

void Capture::DoCapture(const FunctionCallbackInfo<Value>& args) {
  Isolate* isolate = args.GetIsolate();

  Local<Function> cb = Local<Function>::Cast(args[0]);
  Capture* obj = ObjectWrap::Unwrap<Capture>(args.Holder());
  obj->captureCB_.Reset(isolate, cb);

  obj->setupDeckLinkInput();

  args.GetReturnValue().Set(String::NewFromUtf8(isolate, "Capture started."));
}

void Capture::StopCapture(const v8::FunctionCallbackInfo<v8::Value>& args) {
  Isolate* isolate = args.GetIsolate();

  Capture* obj = ObjectWrap::Unwrap<Capture>(args.Holder());

  obj->cleanupDeckLinkInput();

  args.GetReturnValue().Set(String::NewFromUtf8(isolate, "Capture stopped."));
}

// Stop video input
void Capture::cleanupDeckLinkInput()
{
	m_deckLinkInput->StopStreams();
	m_deckLinkInput->DisableVideoInput();
	m_deckLinkInput->SetCallback(NULL);
}

bool Capture::setupDeckLinkInput() {
  // bool result = false;
  IDeckLinkDisplayModeIterator*	displayModeIterator = NULL;
  IDeckLinkDisplayMode*			deckLinkDisplayMode = NULL;

  m_width = -1;

  // get frame scale and duration for the video mode
  if (m_deckLinkInput->GetDisplayModeIterator(&displayModeIterator) != S_OK)
    return false;

  while (displayModeIterator->Next(&deckLinkDisplayMode) == S_OK)
  {
    if (deckLinkDisplayMode->GetDisplayMode() == displayMode_)
    {
      m_width = deckLinkDisplayMode->GetWidth();
      m_height = deckLinkDisplayMode->GetHeight();
      deckLinkDisplayMode->GetFrameRate(&m_frameDuration, &m_timeScale);
      deckLinkDisplayMode->Release();

      break;
    }

    deckLinkDisplayMode->Release();
  }

  printf("Width %li Height %li\n", m_width, m_height);

  displayModeIterator->Release();

  if (m_width == -1)
    return false;

  m_deckLinkInput->SetCallback(this);

  if (m_deckLinkInput->EnableVideoInput((BMDDisplayMode) displayMode_, (BMDPixelFormat) pixelFormat_, bmdVideoInputFlagDefault) != S_OK)
	  return false;

  if (m_deckLinkInput->StartStreams() != S_OK)
    return false;

  return true;
}

HRESULT	Capture::VideoInputFrameArrived (IDeckLinkVideoInputFrame* arrivedFrame, IDeckLinkAudioInputPacket*)
{
  arrivedFrame->AddRef();
  latestFrame_ = arrivedFrame;
  uv_async_send(async);
  return S_OK;
}

HRESULT	Capture::VideoInputFormatChanged (BMDVideoInputFormatChangedEvents notificationEvents, IDeckLinkDisplayMode* newDisplayMode, BMDDetectedVideoInputFormatFlags detectedSignalFlags) {
  return S_OK;
};

void Capture::TestUV() {
  uv_async_send(async);
}

// void FreeCallback(char* data, void* hint) {
//   Isolate* isolate = v8::Isolate::GetCurrent();
//   IDeckLinkVideoInputFrame* frame = static_cast<IDeckLinkVideoInputFrame*>(hint);
//   isolate->AdjustAmountOfExternalAllocatedMemory(-(frame->GetRowBytes() * frame->GetHeight()));
//   frame->Release();
// }

void Capture::FrameCallback(uv_async_t *handle) {
  Isolate* isolate = v8::Isolate::GetCurrent();
  HandleScope scope(isolate);
  Capture *capture = static_cast<Capture*>(handle->data);
  Local<Function> cb = Local<Function>::New(isolate, capture->captureCB_);
  char* new_data;
  capture->latestFrame_->GetBytes((void**) &new_data);
  long new_data_size = capture->latestFrame_->GetRowBytes() * capture->latestFrame_->GetHeight();
  // Local<Object> b = node::Buffer::New(isolate, new_data, new_data_size,
  //   FreeCallback, capture->latestFrame_).ToLocalChecked();
  Local<Object> b = node::Buffer::Copy(isolate, new_data, new_data_size).ToLocalChecked();
  capture->latestFrame_->Release();
  // long extSize = isolate->AdjustAmountOfExternalAllocatedMemory(new_data_size);
  // if (extSize > 100000000) {
  //   isolate->LowMemoryNotification();
  //   printf("Requesting bin collection.\n");
  // }
  Local<Value> argv[1] = { b };
  cb->Call(Null(isolate), 1, argv);
}

}
<|endoftext|>


<commit_before>/******************************************************************************

  This source file is part of the tomviz project.

  Copyright Kitware, Inc.

  This source code is released under the New BSD License, (the "License").

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

******************************************************************************/
#include "Variant.h"

namespace tomviz {

Variant::Variant()
{
  m_type = Variant::INVALID;
}

Variant::Variant(const Variant& v)
{
  copy(v);
}

Variant& Variant::operator=(const Variant& v)
{
  this->~Variant();
  copy(v);

  return *this;
}

Variant::Variant(const std::string& str) : m_type(Variant::STRING)
{
  new (&m_value.stringVal) std::string(str);
}

Variant::Variant(const std::vector<Variant>& l) : m_type(LIST)
{
  new (&m_value.listVal) std::vector<Variant>(l);
}

Variant::Variant(int i) : m_type(Variant::INTEGER)
{
  m_value.integerVal = i;
}

Variant::Variant(double d) : m_type(Variant::DOUBLE)
{
  m_value.doubleVal = d;
}

Variant::Variant(bool b) : m_type(Variant::BOOL)
{
  m_value.boolVal = b;
}

Variant::~Variant()
{
  if (m_type == Variant::STRING) {
    m_value.stringVal.std::string::~string();
  } else if (m_type == LIST) {
    m_value.listVal.~vector<Variant>();
  }
}

bool Variant::toBool() const
{
  return m_value.boolVal;
}

int Variant::toInteger() const
{
  return m_value.integerVal;
}

double Variant::toDouble() const
{
  return m_value.doubleVal;
}

std::string Variant::toString() const
{
  return m_value.stringVal;
}

std::vector<Variant> Variant::toList() const
{
  return m_value.listVal;
}

Variant::Type Variant::type() const
{
  return m_type;
}

void Variant::copy(const Variant& v)
{
  m_type = v.type();
  switch (v.type()) {
    case INVALID:
      // do nothing
      break;
    case INTEGER:
      m_value.integerVal = v.toInteger();
      break;
    case DOUBLE:
      m_value.doubleVal = v.toDouble();
      break;
    case BOOL:
      m_value.boolVal = v.toBool();
      break;
    case STRING:
      new (&m_value.stringVal) std::string(v.toString());
      break;
    case LIST:
      new (&m_value.listVal) std::vector<Variant>(v.toList());
      break;
  }
}
}
<commit_msg>Fix clang compile error<commit_after>/******************************************************************************

  This source file is part of the tomviz project.

  Copyright Kitware, Inc.

  This source code is released under the New BSD License, (the "License").

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

******************************************************************************/
#include "Variant.h"

using namespace std;

namespace tomviz {

Variant::Variant()
{
  m_type = Variant::INVALID;
}

Variant::Variant(const Variant& v)
{
  copy(v);
}

Variant& Variant::operator=(const Variant& v)
{
  this->~Variant();
  copy(v);

  return *this;
}

Variant::Variant(const string& str) : m_type(Variant::STRING)
{
  new (&m_value.stringVal) string(str);
}

Variant::Variant(const vector<Variant>& l) : m_type(LIST)
{
  new (&m_value.listVal) vector<Variant>(l);
}

Variant::Variant(int i) : m_type(Variant::INTEGER)
{
  m_value.integerVal = i;
}

Variant::Variant(double d) : m_type(Variant::DOUBLE)
{
  m_value.doubleVal = d;
}

Variant::Variant(bool b) : m_type(Variant::BOOL)
{
  m_value.boolVal = b;
}

Variant::~Variant()
{
  if (m_type == Variant::STRING) {
    m_value.stringVal.~string();
  } else if (m_type == LIST) {
    m_value.listVal.~vector<Variant>();
  }
}

bool Variant::toBool() const
{
  return m_value.boolVal;
}

int Variant::toInteger() const
{
  return m_value.integerVal;
}

double Variant::toDouble() const
{
  return m_value.doubleVal;
}

string Variant::toString() const
{
  return m_value.stringVal;
}

vector<Variant> Variant::toList() const
{
  return m_value.listVal;
}

Variant::Type Variant::type() const
{
  return m_type;
}

void Variant::copy(const Variant& v)
{
  m_type = v.type();
  switch (v.type()) {
    case INVALID:
      // do nothing
      break;
    case INTEGER:
      m_value.integerVal = v.toInteger();
      break;
    case DOUBLE:
      m_value.doubleVal = v.toDouble();
      break;
    case BOOL:
      m_value.boolVal = v.toBool();
      break;
    case STRING:
      new (&m_value.stringVal) string(v.toString());
      break;
    case LIST:
      new (&m_value.listVal) vector<Variant>(v.toList());
      break;
  }
}
}
<|endoftext|>


<commit_before>#ifndef SIMDIFY_UTIL_1B
#define SIMDIFY_UTIL_1B

#include "inline.hpp"
#include <iterator>

#if defined(__GNUC__) // GCC, Clang

namespace simd {
    // least significant "1" bit index; result undefined if in == 0
    SIMDIFY_FORCE_INLINE int ls1b(unsigned int in) { return __builtin_ctz(in); }
    // most significant "1" bit index; result undefined if in == 0
    SIMDIFY_FORCE_INLINE int ms1b(unsigned int in) { return (8*int(sizeof(unsigned int)) - 1) - __builtin_clz(in); }
    // least significant "1" bit index; result undefined if in == 0
    SIMDIFY_FORCE_INLINE int ls1b(unsigned long in) { return __builtin_ctzl(in); }
    // most significant "1" bit index; result undefined if in == 0
    SIMDIFY_FORCE_INLINE int ms1b(unsigned long in) { return (8*int(sizeof(unsigned long)) - 1) - __builtin_clzl(in); }
    // least significant "1" bit index; result undefined if in == 0
    SIMDIFY_FORCE_INLINE int ls1b(unsigned long long in) { return __builtin_ctzll(in); }
    // most significant "1" bit index; result undefined if in == 0
    SIMDIFY_FORCE_INLINE int ms1b(unsigned long long in) { return (8*int(sizeof(unsigned long long)) - 1) - __builtin_clzll(in); }
}

#elif defined(_MSC_VER) // Visual Studio

#include <intrin.h>

namespace simd {
    // least significant "1" bit index; result undefined if in == 0
    SIMDIFY_FORCE_INLINE unsigned long ls1b(unsigned __int32 in) { unsigned long res; _BitScanForward(&res, in); return res; }
    // most significant "1" bit index; result undefined if in == 0
    SIMDIFY_FORCE_INLINE unsigned long ms1b(unsigned __int32 in) { unsigned long res; _BitScanReverse(&res, in); return res; }

#if defined(_M_X64)
    // least significant "1" bit index; result undefined if in == 0
    SIMDIFY_FORCE_INLINE unsigned long ls1b(unsigned __int64 in) { unsigned long res; _BitScanForward64(&res, in); return res; }
    // most significant "1" bit index; result undefined if in == 0
    SIMDIFY_FORCE_INLINE unsigned long ms1b(unsigned __int64 in) { unsigned long res; _BitScanReverse64(&res, in); return res; }
#endif
}

#else

#error "util_1b.h: incompatible compiler"

#endif

namespace simd {

    using bit_t = unsigned;

    // provides indices of set (1) bits, ordered from least significant to most significant
    struct bit_iterator : std::iterator<std::input_iterator_tag, bit_t> {
        bit_t mask;

        SIMDIFY_FORCE_INLINE bit_iterator(bit_t mask_) : mask(mask_) {}
        SIMDIFY_FORCE_INLINE bit_t operator*() const { return bit_t(ls1b(mask)); }
        SIMDIFY_FORCE_INLINE bit_t operator->() const { return bit_t(ls1b(mask)); }
        SIMDIFY_FORCE_INLINE bit_iterator& operator++() { mask = mask & (mask - 1); return *this; }
        SIMDIFY_FORCE_INLINE bit_iterator operator++(int) { bit_iterator r = mask; operator++(); return r; }
        SIMDIFY_FORCE_INLINE bool operator!=(const bit_iterator& rhs) const { return mask != rhs.mask; }
    };

    struct bit_field {
        bit_t field;

        SIMDIFY_FORCE_INLINE bit_field(bit_t field_) : field(field_) {}
        SIMDIFY_FORCE_INLINE bit_iterator begin() const { return field; }
        SIMDIFY_FORCE_INLINE bit_iterator end() const { return 0; }
    };

}

#endif // SIMDIFY_UTIL_1B
<commit_msg>Added bswap for little/big endian conversions<commit_after>#ifndef SIMDIFY_UTIL_1B
#define SIMDIFY_UTIL_1B

#include "inline.hpp"
#include <iterator>

#if defined(__GNUC__) // GCC, Clang

namespace simd {
    // least significant "1" bit index; result undefined if in == 0
    SIMDIFY_FORCE_INLINE int ls1b(unsigned int in) { return __builtin_ctz(in); }
    // most significant "1" bit index; result undefined if in == 0
    SIMDIFY_FORCE_INLINE int ms1b(unsigned int in) { return (8*int(sizeof(unsigned int)) - 1) - __builtin_clz(in); }
    // least significant "1" bit index; result undefined if in == 0
    SIMDIFY_FORCE_INLINE int ls1b(unsigned long in) { return __builtin_ctzl(in); }
    // most significant "1" bit index; result undefined if in == 0
    SIMDIFY_FORCE_INLINE int ms1b(unsigned long in) { return (8*int(sizeof(unsigned long)) - 1) - __builtin_clzl(in); }
    // least significant "1" bit index; result undefined if in == 0
    SIMDIFY_FORCE_INLINE int ls1b(unsigned long long in) { return __builtin_ctzll(in); }
    // most significant "1" bit index; result undefined if in == 0
    SIMDIFY_FORCE_INLINE int ms1b(unsigned long long in) { return (8*int(sizeof(unsigned long long)) - 1) - __builtin_clzll(in); }
    // convert little/big endian
    SIMDIFY_FORCE_INLINE uint16_t bswap(uint16_t in) { return __builtin_bswap16(in); }
    // convert little/big endian
    SIMDIFY_FORCE_INLINE uint32_t bswap(uint32_t in) { return __builtin_bswap32(in); }
    // convert little/big endian
    SIMDIFY_FORCE_INLINE uint64_t bswap(uint64_t in) { return __builtin_bswap64(in); }
}

#elif defined(_MSC_VER) // Visual Studio

#include <intrin.h>

namespace simd {
    // least significant "1" bit index; result undefined if in == 0
    SIMDIFY_FORCE_INLINE unsigned long ls1b(unsigned __int32 in) { unsigned long res; _BitScanForward(&res, in); return res; }
    // most significant "1" bit index; result undefined if in == 0
    SIMDIFY_FORCE_INLINE unsigned long ms1b(unsigned __int32 in) { unsigned long res; _BitScanReverse(&res, in); return res; }
    // convert little/big endian
    SIMDIFY_FORCE_INLINE uint16_t bswap(uint16_t in) { return _byteswap_ushort(in); }
    // convert little/big endian
    SIMDIFY_FORCE_INLINE uint32_t bswap(uint32_t in) { return _byteswap_ulong(in); }
    // convert little/big endian
    SIMDIFY_FORCE_INLINE uint64_t bswap(uint64_t in) { return _byteswap_uint64(in); }

#if defined(_M_X64)
    // least significant "1" bit index; result undefined if in == 0
    SIMDIFY_FORCE_INLINE unsigned long ls1b(unsigned __int64 in) { unsigned long res; _BitScanForward64(&res, in); return res; }
    // most significant "1" bit index; result undefined if in == 0
    SIMDIFY_FORCE_INLINE unsigned long ms1b(unsigned __int64 in) { unsigned long res; _BitScanReverse64(&res, in); return res; }
#endif
}

#else

#error "util_1b.h: incompatible compiler"

#endif

namespace simd {

    using bit_t = unsigned;

    // provides indices of set (1) bits, ordered from least significant to most significant
    struct bit_iterator : std::iterator<std::input_iterator_tag, bit_t> {
        bit_t mask;

        SIMDIFY_FORCE_INLINE bit_iterator(bit_t mask_) : mask(mask_) {}
        SIMDIFY_FORCE_INLINE bit_t operator*() const { return bit_t(ls1b(mask)); }
        SIMDIFY_FORCE_INLINE bit_t operator->() const { return bit_t(ls1b(mask)); }
        SIMDIFY_FORCE_INLINE bit_iterator& operator++() { mask = mask & (mask - 1); return *this; }
        SIMDIFY_FORCE_INLINE bit_iterator operator++(int) { bit_iterator r = mask; operator++(); return r; }
        SIMDIFY_FORCE_INLINE bool operator!=(const bit_iterator& rhs) const { return mask != rhs.mask; }
    };

    struct bit_field {
        bit_t field;

        SIMDIFY_FORCE_INLINE bit_field(bit_t field_) : field(field_) {}
        SIMDIFY_FORCE_INLINE bit_iterator begin() const { return field; }
        SIMDIFY_FORCE_INLINE bit_iterator end() const { return 0; }
    };

}

#endif // SIMDIFY_UTIL_1B
<|endoftext|>


<commit_before>/******************************************************************************
 *                       ____    _    _____                                   *
 *                      / ___|  / \  |  ___|    C++                           *
 *                     | |     / _ \ | |_       Actor                         *
 *                     | |___ / ___ \|  _|      Framework                     *
 *                      \____/_/   \_|_|                                      *
 *                                                                            *
 * Copyright (C) 2011 - 2014                                                  *
 * Dominik Charousset <dominik.charousset (at) haw-hamburg.de>                *
 *                                                                            *
 * Distributed under the terms and conditions of the BSD 3-Clause License or  *
 * (at your option) under the terms and conditions of the Boost Software      *
 * License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE.       *
 *                                                                            *
 * If you did not receive a copy of the license files, see                    *
 * http://opensource.org/licenses/BSD-3-Clause and                            *
 * http://www.boost.org/LICENSE_1_0.txt.                                      *
 ******************************************************************************/

#ifndef CAF_CONFIG_HPP
#define CAF_CONFIG_HPP

// Config pararameters defined by the build system (usually CMake):
//
// CAF_ENABLE_RUNTIME_CHECKS:
//   - check requirements at runtime
//
// CAF_LOG_LEVEL:
//   - denotes the amount of logging, ranging from error messages only (0)
//     to complete traces (4)

/**
 * Denotes the libcaf version in the format {MAJOR}{MINOR}{PATCH},
 * whereas each number is a two-digit decimal number without
 * leading zeros (e.g. 900 is version 0.9.0).
 */
#define CAF_VERSION 1101

#define CAF_MAJOR_VERSION (CAF_VERSION / 10000)
#define CAF_MINOR_VERSION ((CAF_VERSION / 100) % 100)
#define CAF_PATCH_VERSION (CAF_VERSION % 100)

// sets CAF_DEPRECATED, CAF_ANNOTATE_FALLTHROUGH,
// CAF_PUSH_WARNINGS and CAF_POP_WARNINGS
#if defined(__clang__)
#  define CAF_CLANG
#  define CAF_DEPRECATED __attribute__((__deprecated__))
#  define CAF_PUSH_WARNINGS                                                    \
    _Pragma("clang diagnostic push")                                           \
    _Pragma("clang diagnostic ignored \"-Wall\"")                              \
    _Pragma("clang diagnostic ignored \"-Wextra\"")                            \
    _Pragma("clang diagnostic ignored \"-Werror\"")                            \
    _Pragma("clang diagnostic ignored \"-Wdeprecated\"")                       \
    _Pragma("clang diagnostic ignored \"-Wdisabled-macro-expansion\"")         \
    _Pragma("clang diagnostic ignored \"-Wextra-semi\"")                       \
    _Pragma("clang diagnostic ignored \"-Wdocumentation\"")                    \
    _Pragma("clang diagnostic ignored \"-Wweak-vtables\"")                     \
    _Pragma("clang diagnostic ignored \"-Wunused-parameter\"")                 \
    _Pragma("clang diagnostic ignored \"-Wswitch-enum\"")                      \
    _Pragma("clang diagnostic ignored \"-Wshadow\"")                           \
    _Pragma("clang diagnostic ignored \"-Wconversion\"")                       \
    _Pragma("clang diagnostic ignored \"-Wcast-align\"")                       \
    _Pragma("clang diagnostic ignored \"-Wundef\"")                            \
    _Pragma("clang diagnostic ignored \"-Wnested-anon-types\"")
#  define CAF_POP_WARNINGS                          \
    _Pragma("clang diagnostic pop")
#  define CAF_ANNOTATE_FALLTHROUGH [[clang::fallthrough]]
#elif defined(__GNUC__)
#  define CAF_GCC
#  define CAF_DEPRECATED __attribute__((__deprecated__))
#  define CAF_PUSH_WARNINGS
#  define CAF_POP_WARNINGS
#  define CAF_ANNOTATE_FALLTHROUGH static_cast<void>(0)
#elif defined(_MSC_VER)
#  define CAF_MSVC
#  define CAF_DEPRECATED
#  define CAF_PUSH_WARNINGS
#  define CAF_POP_WARNINGS
#  define CAF_ANNOTATE_FALLTHROUGH static_cast<void>(0)
#else
#  define CAF_DEPRECATED
#  define CAF_PUSH_WARNINGS
#  define CAF_POP_WARNINGS
#  define CAF_ANNOTATE_FALLTHROUGH static_cast<void>(0)
#endif

// detect OS
#if defined(__APPLE__)
#  define CAF_MACOS
#  ifndef _GLIBCXX_HAS_GTHREADS
#  define _GLIBCXX_HAS_GTHREADS
#  endif
#elif defined(__linux__)
#  define CAF_LINUX
#   include <linux/version.h>
#   if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16)
#   define CAF_POLL_IMPL
#   endif
#elif defined(__FreeBSD__)
#  define CAF_BSD
#elif defined(WIN32) || defined(_WIN32)
#  define CAF_WINDOWS
#else
#  error Platform and/or compiler not supportet
#endif

#include <memory>
#include <cstdio>
#include <cstdlib>

// import backtrace and backtrace_symbols_fd into caf::detail
#ifdef CAF_WINDOWS
#include "caf/detail/execinfo_windows.hpp"
#else
#include <execinfo.h>
namespace caf {
namespace detail {
using ::backtrace;
using ::backtrace_symbols_fd;
} // namespace detail
} // namespace caf

#endif

#ifdef CAF_ENABLE_RUNTIME_CHECKS
#   define CAF_REQUIRE__(stmt, file, line)                                     \
    printf("%s:%u: requirement failed '%s'\n", file, line, stmt); {            \
      void* array[10];                                                         \
      auto caf_bt_size = ::caf::detail::backtrace(array, 10);                  \
      ::caf::detail::backtrace_symbols_fd(array, caf_bt_size, 2);              \
    } abort()
#   define CAF_REQUIRE(stmt)                                                   \
    if (static_cast<bool>(stmt) == false) {                                    \
      CAF_REQUIRE__(#stmt, __FILE__, __LINE__);                                \
    } static_cast<void>(0)
#else
#   define CAF_REQUIRE(unused) static_cast<void>(0)
#endif

#define CAF_CRITICAL__(error, file, line) {                                    \
    printf("%s:%u: critical error: '%s'\n", file, line, error);                \
    exit(7);                                                                   \
  } static_cast<void>(0)

#define CAF_CRITICAL(error) CAF_CRITICAL__(error, __FILE__, __LINE__)

#endif // CAF_CONFIG_HPP
<commit_msg>Change version to 0.11.2<commit_after>/******************************************************************************
 *                       ____    _    _____                                   *
 *                      / ___|  / \  |  ___|    C++                           *
 *                     | |     / _ \ | |_       Actor                         *
 *                     | |___ / ___ \|  _|      Framework                     *
 *                      \____/_/   \_|_|                                      *
 *                                                                            *
 * Copyright (C) 2011 - 2014                                                  *
 * Dominik Charousset <dominik.charousset (at) haw-hamburg.de>                *
 *                                                                            *
 * Distributed under the terms and conditions of the BSD 3-Clause License or  *
 * (at your option) under the terms and conditions of the Boost Software      *
 * License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE.       *
 *                                                                            *
 * If you did not receive a copy of the license files, see                    *
 * http://opensource.org/licenses/BSD-3-Clause and                            *
 * http://www.boost.org/LICENSE_1_0.txt.                                      *
 ******************************************************************************/

#ifndef CAF_CONFIG_HPP
#define CAF_CONFIG_HPP

// Config pararameters defined by the build system (usually CMake):
//
// CAF_ENABLE_RUNTIME_CHECKS:
//   - check requirements at runtime
//
// CAF_LOG_LEVEL:
//   - denotes the amount of logging, ranging from error messages only (0)
//     to complete traces (4)

/**
 * Denotes the libcaf version in the format {MAJOR}{MINOR}{PATCH},
 * whereas each number is a two-digit decimal number without
 * leading zeros (e.g. 900 is version 0.9.0).
 */
#define CAF_VERSION 1102

#define CAF_MAJOR_VERSION (CAF_VERSION / 10000)
#define CAF_MINOR_VERSION ((CAF_VERSION / 100) % 100)
#define CAF_PATCH_VERSION (CAF_VERSION % 100)

// sets CAF_DEPRECATED, CAF_ANNOTATE_FALLTHROUGH,
// CAF_PUSH_WARNINGS and CAF_POP_WARNINGS
#if defined(__clang__)
#  define CAF_CLANG
#  define CAF_DEPRECATED __attribute__((__deprecated__))
#  define CAF_PUSH_WARNINGS                                                    \
    _Pragma("clang diagnostic push")                                           \
    _Pragma("clang diagnostic ignored \"-Wall\"")                              \
    _Pragma("clang diagnostic ignored \"-Wextra\"")                            \
    _Pragma("clang diagnostic ignored \"-Werror\"")                            \
    _Pragma("clang diagnostic ignored \"-Wdeprecated\"")                       \
    _Pragma("clang diagnostic ignored \"-Wdisabled-macro-expansion\"")         \
    _Pragma("clang diagnostic ignored \"-Wextra-semi\"")                       \
    _Pragma("clang diagnostic ignored \"-Wdocumentation\"")                    \
    _Pragma("clang diagnostic ignored \"-Wweak-vtables\"")                     \
    _Pragma("clang diagnostic ignored \"-Wunused-parameter\"")                 \
    _Pragma("clang diagnostic ignored \"-Wswitch-enum\"")                      \
    _Pragma("clang diagnostic ignored \"-Wshadow\"")                           \
    _Pragma("clang diagnostic ignored \"-Wconversion\"")                       \
    _Pragma("clang diagnostic ignored \"-Wcast-align\"")                       \
    _Pragma("clang diagnostic ignored \"-Wundef\"")                            \
    _Pragma("clang diagnostic ignored \"-Wnested-anon-types\"")
#  define CAF_POP_WARNINGS                          \
    _Pragma("clang diagnostic pop")
#  define CAF_ANNOTATE_FALLTHROUGH [[clang::fallthrough]]
#elif defined(__GNUC__)
#  define CAF_GCC
#  define CAF_DEPRECATED __attribute__((__deprecated__))
#  define CAF_PUSH_WARNINGS
#  define CAF_POP_WARNINGS
#  define CAF_ANNOTATE_FALLTHROUGH static_cast<void>(0)
#elif defined(_MSC_VER)
#  define CAF_MSVC
#  define CAF_DEPRECATED
#  define CAF_PUSH_WARNINGS
#  define CAF_POP_WARNINGS
#  define CAF_ANNOTATE_FALLTHROUGH static_cast<void>(0)
#else
#  define CAF_DEPRECATED
#  define CAF_PUSH_WARNINGS
#  define CAF_POP_WARNINGS
#  define CAF_ANNOTATE_FALLTHROUGH static_cast<void>(0)
#endif

// detect OS
#if defined(__APPLE__)
#  define CAF_MACOS
#  ifndef _GLIBCXX_HAS_GTHREADS
#  define _GLIBCXX_HAS_GTHREADS
#  endif
#elif defined(__linux__)
#  define CAF_LINUX
#   include <linux/version.h>
#   if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16)
#   define CAF_POLL_IMPL
#   endif
#elif defined(__FreeBSD__)
#  define CAF_BSD
#elif defined(WIN32) || defined(_WIN32)
#  define CAF_WINDOWS
#else
#  error Platform and/or compiler not supportet
#endif

#include <memory>
#include <cstdio>
#include <cstdlib>

// import backtrace and backtrace_symbols_fd into caf::detail
#ifdef CAF_WINDOWS
#include "caf/detail/execinfo_windows.hpp"
#else
#include <execinfo.h>
namespace caf {
namespace detail {
using ::backtrace;
using ::backtrace_symbols_fd;
} // namespace detail
} // namespace caf

#endif

#ifdef CAF_ENABLE_RUNTIME_CHECKS
#   define CAF_REQUIRE__(stmt, file, line)                                     \
    printf("%s:%u: requirement failed '%s'\n", file, line, stmt); {            \
      void* array[10];                                                         \
      auto caf_bt_size = ::caf::detail::backtrace(array, 10);                  \
      ::caf::detail::backtrace_symbols_fd(array, caf_bt_size, 2);              \
    } abort()
#   define CAF_REQUIRE(stmt)                                                   \
    if (static_cast<bool>(stmt) == false) {                                    \
      CAF_REQUIRE__(#stmt, __FILE__, __LINE__);                                \
    } static_cast<void>(0)
#else
#   define CAF_REQUIRE(unused) static_cast<void>(0)
#endif

#define CAF_CRITICAL__(error, file, line) {                                    \
    printf("%s:%u: critical error: '%s'\n", file, line, error);                \
    exit(7);                                                                   \
  } static_cast<void>(0)

#define CAF_CRITICAL(error) CAF_CRITICAL__(error, __FILE__, __LINE__)

#endif // CAF_CONFIG_HPP
<|endoftext|>