<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;
}

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

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

    // Get arguments
    TxnWrap *tw = Nan::ObjectWrap::Unwrap<TxnWrap>(info[0]->ToObject());
    DbiWrap *dw = Nan::ObjectWrap::Unwrap<DbiWrap>(info[1]->ToObject());

    // 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->keyIsUint32 = dw->keyIsUint32;
    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,
    void (*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());

    if (setKey) {
        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;

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

    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->keyIsUint32);
    }

    if (convertFunc && 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, convertFunc(cw->data) };
        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 (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) -> void {
        argToKey(info[0], key, cw->keyIsUint32);
    }, nullptr, nullptr, nullptr);
}

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

static void fillDataFromArg1(CursorWrap* cw, Nan::NAN_METHOD_ARGS_TYPE info, MDB_val &data) {
    if (info[1]->IsString()) {
        CustomExternalStringResource::writeTo(info[2]->ToString(), &data);
    }
    else if (node::Buffer::HasInstance(info[1])) {
        data.mv_size = node::Buffer::Length(info[2]);
        data.mv_data = node::Buffer::Data(info[2]);
    }
    else if (info[1]->IsNumber()) {
        data.mv_size = sizeof(double);
        data.mv_data = new double;
        *((double*)data.mv_data) = info[1]->ToNumber()->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) -> void {
        argToKey(info[0], key, cw->keyIsUint32);
    }, 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) -> void {
        argToKey(info[0], key, cw->keyIsUint32);
    }, 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>Return value instead of key for getCurrentString et al in Cursor<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;
}

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

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

    // Get arguments
    TxnWrap *tw = Nan::ObjectWrap::Unwrap<TxnWrap>(info[0]->ToObject());
    DbiWrap *dw = Nan::ObjectWrap::Unwrap<DbiWrap>(info[1]->ToObject());

    // 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->keyIsUint32 = dw->keyIsUint32;
    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,
    void (*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());

    if (setKey) {
        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;

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

    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->keyIsUint32);
    }

    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) -> void {
        argToKey(info[0], key, cw->keyIsUint32);
    }, nullptr, nullptr, nullptr);
}

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

static void fillDataFromArg1(CursorWrap* cw, Nan::NAN_METHOD_ARGS_TYPE info, MDB_val &data) {
    if (info[1]->IsString()) {
        CustomExternalStringResource::writeTo(info[2]->ToString(), &data);
    }
    else if (node::Buffer::HasInstance(info[1])) {
        data.mv_size = node::Buffer::Length(info[2]);
        data.mv_data = node::Buffer::Data(info[2]);
    }
    else if (info[1]->IsNumber()) {
        data.mv_size = sizeof(double);
        data.mv_data = new double;
        *((double*)data.mv_data) = info[1]->ToNumber()->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) -> void {
        argToKey(info[0], key, cw->keyIsUint32);
    }, 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) -> void {
        argToKey(info[0], key, cw->keyIsUint32);
    }, 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>#ifndef EBITEN_GRAPHICS_AFFINE_MATRIX_HPP
#define EBITEN_GRAPHICS_AFFINE_MATRIX_HPP

#include <array>
#include <cassert>
#include <mutex>

namespace ebiten {
namespace graphics {

/*
 * | e00, e01, ..., e0D |
 * | e10, e11, ..., e1D |
 * |  : ,  : ,    ,  :  |
 * | eD0, eD1, ..., eDD |
 */
template<class Float, std::size_t Dimension, class Self>
class affine_matrix {
  static_assert(0 < Dimension, "Dimension must be more than 0");
#ifdef EBITEN_TEST
private:
  FRIEND_TEST(affine_matrix, element);
  FRIEND_TEST(affine_matrix, is_identity);
  FRIEND_TEST(affine_matrix, multiply);
#endif
private:
  static std::size_t const size_ = Dimension * (Dimension - 1);
  typedef std::array<Float, size_> elements_type;
  static std::once_flag identity_once_flag_;
protected:
  elements_type elements_;
protected:
  affine_matrix() {
    this->elements_.fill(0);
  }
public:
  template<std::size_t I, std::size_t J>
  Float
  element() const {
    static_assert(I < Dimension, "I must be less than Dimension");
    static_assert(J < Dimension, "J must be less than Dimension");
    if (I == Dimension - 1) {
      if (J == Dimension - 1) {
        return 1;
      }
      return 0;
    }
    return this->elements_[I * Dimension + J];
  }
  template<std::size_t I, std::size_t J>
  Float
  set_element(Float element) {
    static_assert(I < Dimension - 1, "I must be less than Dimension - 1");
    static_assert(J < Dimension,     "J must be less than Dimension");
    return this->elements_[I * Dimension + J] = element;
  }
  bool
  is_identity() const {
    typename elements_type::const_iterator it = this->elements_.begin();
    for (std::size_t i = 0; i < Dimension - 1; ++i) {
      for (std::size_t j = 0; j < Dimension; ++j, ++it) {
        if (i == j) {
          if (*it != 1) {
            return false;
          }
        } else {
          if (*it != 0) {
            return false;
          }
        }
      }
    }
    return true;
  }
  static Self
  identity() {
    static Self identity_;
    std::call_once(identity_once_flag_, initialize_identity, std::ref<Self>(identity_));
    return identity_;
  }
  Self
  multiply(Self const& lhs) const {
    Self result;
    elements_type const& lhs_elements = lhs.elements_;
    elements_type const& rhs_elements = this->elements_;
    typename elements_type::iterator it = result.elements_.begin();
    for (std::size_t i = 0; i < Dimension - 1; ++i) {
      for (std::size_t j = 0; j < Dimension; ++j, ++it) {
        Float e = 0;
        for (std::size_t k = 0; k < Dimension - 1; ++k) {
          e += lhs_elements[i * Dimension + k] * rhs_elements[k * Dimension + j];
        }
        if (j == Dimension - 1) {
          e += lhs_elements[i * Dimension + Dimension - 1];
        }
        *it = e;
      }
    }
    return result;
  }
private:
  static void
  initialize_identity(Self& identity) {
    typename elements_type::iterator it = identity.elements_.begin();
    for (std::size_t i = 0; i < Dimension - 1; ++i) {
      for (std::size_t j = 0; j < Dimension; ++j, ++it) {
        if (i == j) {
          *it = 1;
        } else {
          *it = 0;
        }
      }
    }
  }
};

template<class Float, std::size_t Dimension, class Self>
std::once_flag affine_matrix<Float, Dimension, Self>::identity_once_flag_;

}
}

#ifdef EBITEN_TEST

namespace ebiten {
namespace graphics {

template<class Float, std::size_t Dimension>
class affine_matrix_impl : public affine_matrix<Float,
                                                Dimension,
                                                affine_matrix_impl<Float, Dimension>> {
};

TEST(affine_matrix, element) {
  affine_matrix_impl<double, 4> m;
  m.set_element<0, 0>(1);
  m.set_element<0, 1>(2);
  m.set_element<0, 2>(3);
  EXPECT_EQ(1, (m.element<0, 0>()));
  EXPECT_EQ(2, (m.element<0, 1>()));
  EXPECT_EQ(3, (m.element<0, 2>()));
  EXPECT_EQ(0, (m.element<0, 3>()));
  EXPECT_EQ(0, (m.element<1, 0>()));
  EXPECT_EQ(0, (m.element<1, 1>()));
  EXPECT_EQ(0, (m.element<1, 2>()));
  EXPECT_EQ(0, (m.element<1, 3>()));
  EXPECT_EQ(0, (m.element<2, 0>()));
  EXPECT_EQ(0, (m.element<2, 1>()));
  EXPECT_EQ(0, (m.element<2, 2>()));
  EXPECT_EQ(0, (m.element<2, 3>()));
  EXPECT_EQ(0, (m.element<3, 0>()));
  EXPECT_EQ(0, (m.element<3, 1>()));
  EXPECT_EQ(0, (m.element<3, 2>()));
  EXPECT_EQ(1, (m.element<3, 3>()));
  m.set_element<1, 0>(4);
  m.set_element<1, 1>(5);
  m.set_element<2, 3>(6);
  EXPECT_EQ(1, (m.element<0, 0>()));
  EXPECT_EQ(2, (m.element<0, 1>()));
  EXPECT_EQ(3, (m.element<0, 2>()));
  EXPECT_EQ(0, (m.element<0, 3>()));
  EXPECT_EQ(4, (m.element<1, 0>()));
  EXPECT_EQ(5, (m.element<1, 1>()));
  EXPECT_EQ(0, (m.element<1, 2>()));
  EXPECT_EQ(0, (m.element<1, 3>()));
  EXPECT_EQ(0, (m.element<2, 0>()));
  EXPECT_EQ(0, (m.element<2, 1>()));
  EXPECT_EQ(0, (m.element<2, 2>()));
  EXPECT_EQ(6, (m.element<2, 3>()));
  EXPECT_EQ(0, (m.element<3, 0>()));
  EXPECT_EQ(0, (m.element<3, 1>()));
  EXPECT_EQ(0, (m.element<3, 2>()));
  EXPECT_EQ(1, (m.element<3, 3>()));
}

TEST(affine_matrix, is_identity) {
  affine_matrix_impl<double, 4> m1;
  m1.set_element<0, 0>(1);
  m1.set_element<1, 1>(1);
  m1.set_element<2, 2>(1);
  EXPECT_TRUE(m1.is_identity());
  affine_matrix_impl<double, 4> m2 = m1;
  EXPECT_TRUE(m2.is_identity());
  m2.set_element<0, 1>(1);
  EXPECT_FALSE(m2.is_identity());
}

TEST(affine_matrix, multiply) {
  {
    affine_matrix_impl<double, 3> m1 = affine_matrix_impl<double, 3>::identity();
    affine_matrix_impl<double, 3> m2 = affine_matrix_impl<double, 3>::identity();
    m1.set_element<0, 0>(2);
    m1.set_element<1, 1>(2);
    m2.set_element<0, 2>(1);
    m2.set_element<1, 2>(1);
    {
      affine_matrix_impl<double, 3> m3 = m1.multiply(m2);
      EXPECT_EQ(2, (m3.element<0, 0>()));
      EXPECT_EQ(0, (m3.element<0, 1>()));
      EXPECT_EQ(1, (m3.element<0, 2>()));
      EXPECT_EQ(0, (m3.element<1, 0>()));
      EXPECT_EQ(2, (m3.element<1, 1>()));
      EXPECT_EQ(1, (m3.element<1, 2>()));
      EXPECT_EQ(0, (m3.element<2, 0>()));
      EXPECT_EQ(0, (m3.element<2, 1>()));
      EXPECT_EQ(1, (m3.element<2, 2>()));
    }
    {
      affine_matrix_impl<double, 3> m3 = m2.multiply(m1);
      EXPECT_EQ(2, (m3.element<0, 0>()));
      EXPECT_EQ(0, (m3.element<0, 1>()));
      EXPECT_EQ(2, (m3.element<0, 2>()));
      EXPECT_EQ(0, (m3.element<1, 0>()));
      EXPECT_EQ(2, (m3.element<1, 1>()));
      EXPECT_EQ(2, (m3.element<1, 2>()));
      EXPECT_EQ(0, (m3.element<2, 0>()));
      EXPECT_EQ(0, (m3.element<2, 1>()));
      EXPECT_EQ(1, (m3.element<2, 2>()));
    }
  }
  {
    affine_matrix_impl<double, 4> m1;
    affine_matrix_impl<double, 4> m2;
  }
}

}
}

#endif

#endif
<commit_msg>Replaced affine_matrix#multiply with affine_matrix#*<commit_after>#ifndef EBITEN_GRAPHICS_AFFINE_MATRIX_HPP
#define EBITEN_GRAPHICS_AFFINE_MATRIX_HPP

#include <array>
#include <cassert>
#include <mutex>

namespace ebiten {
namespace graphics {

/*
 * | e00, e01, ..., e0D |
 * | e10, e11, ..., e1D |
 * |  : ,  : ,    ,  :  |
 * | eD0, eD1, ..., eDD |
 */
template<class Float, std::size_t Dimension, class Self>
class affine_matrix {
  static_assert(0 < Dimension, "Dimension must be more than 0");
#ifdef EBITEN_TEST
private:
  FRIEND_TEST(affine_matrix, element);
  FRIEND_TEST(affine_matrix, is_identity);
  FRIEND_TEST(affine_matrix, multiply);
#endif
private:
  static std::size_t const size_ = Dimension * (Dimension - 1);
  typedef std::array<Float, size_> elements_type;
  static std::once_flag identity_once_flag_;
protected:
  elements_type elements_;
protected:
  affine_matrix() {
    this->elements_.fill(0);
  }
public:
  template<std::size_t I, std::size_t J>
  Float
  element() const {
    static_assert(I < Dimension, "I must be less than Dimension");
    static_assert(J < Dimension, "J must be less than Dimension");
    if (I == Dimension - 1) {
      if (J == Dimension - 1) {
        return 1;
      }
      return 0;
    }
    return this->elements_[I * Dimension + J];
  }
  template<std::size_t I, std::size_t J>
  Float
  set_element(Float element) {
    static_assert(I < Dimension - 1, "I must be less than Dimension - 1");
    static_assert(J < Dimension,     "J must be less than Dimension");
    return this->elements_[I * Dimension + J] = element;
  }
  bool
  is_identity() const {
    typename elements_type::const_iterator it = this->elements_.begin();
    for (std::size_t i = 0; i < Dimension - 1; ++i) {
      for (std::size_t j = 0; j < Dimension; ++j, ++it) {
        if (i == j) {
          if (*it != 1) {
            return false;
          }
        } else {
          if (*it != 0) {
            return false;
          }
        }
      }
    }
    return true;
  }
  static Self
  identity() {
    static Self identity_;
    std::call_once(identity_once_flag_, initialize_identity, std::ref<Self>(identity_));
    return identity_;
  }
  Self
  operator *(Self const& rhs) const {
    Self result;
    elements_type const& lhs_elements = this->elements_;
    elements_type const& rhs_elements = rhs.elements_;
    typename elements_type::iterator it = result.elements_.begin();
    for (std::size_t i = 0; i < Dimension - 1; ++i) {
      for (std::size_t j = 0; j < Dimension; ++j, ++it) {
        Float e = 0;
        for (std::size_t k = 0; k < Dimension - 1; ++k) {
          e += lhs_elements[i * Dimension + k] * rhs_elements[k * Dimension + j];
        }
        if (j == Dimension - 1) {
          e += lhs_elements[i * Dimension + Dimension - 1];
        }
        *it = e;
      }
    }
    return result;
  }
private:
  static void
  initialize_identity(Self& identity) {
    typename elements_type::iterator it = identity.elements_.begin();
    for (std::size_t i = 0; i < Dimension - 1; ++i) {
      for (std::size_t j = 0; j < Dimension; ++j, ++it) {
        if (i == j) {
          *it = 1;
        } else {
          *it = 0;
        }
      }
    }
  }
};

template<class Float, std::size_t Dimension, class Self>
std::once_flag affine_matrix<Float, Dimension, Self>::identity_once_flag_;

}
}

#ifdef EBITEN_TEST

namespace ebiten {
namespace graphics {

template<class Float, std::size_t Dimension>
class affine_matrix_impl : public affine_matrix<Float,
                                                Dimension,
                                                affine_matrix_impl<Float, Dimension>> {
};

TEST(affine_matrix, element) {
  affine_matrix_impl<double, 4> m;
  m.set_element<0, 0>(1);
  m.set_element<0, 1>(2);
  m.set_element<0, 2>(3);
  EXPECT_EQ(1, (m.element<0, 0>()));
  EXPECT_EQ(2, (m.element<0, 1>()));
  EXPECT_EQ(3, (m.element<0, 2>()));
  EXPECT_EQ(0, (m.element<0, 3>()));
  EXPECT_EQ(0, (m.element<1, 0>()));
  EXPECT_EQ(0, (m.element<1, 1>()));
  EXPECT_EQ(0, (m.element<1, 2>()));
  EXPECT_EQ(0, (m.element<1, 3>()));
  EXPECT_EQ(0, (m.element<2, 0>()));
  EXPECT_EQ(0, (m.element<2, 1>()));
  EXPECT_EQ(0, (m.element<2, 2>()));
  EXPECT_EQ(0, (m.element<2, 3>()));
  EXPECT_EQ(0, (m.element<3, 0>()));
  EXPECT_EQ(0, (m.element<3, 1>()));
  EXPECT_EQ(0, (m.element<3, 2>()));
  EXPECT_EQ(1, (m.element<3, 3>()));
  m.set_element<1, 0>(4);
  m.set_element<1, 1>(5);
  m.set_element<2, 3>(6);
  EXPECT_EQ(1, (m.element<0, 0>()));
  EXPECT_EQ(2, (m.element<0, 1>()));
  EXPECT_EQ(3, (m.element<0, 2>()));
  EXPECT_EQ(0, (m.element<0, 3>()));
  EXPECT_EQ(4, (m.element<1, 0>()));
  EXPECT_EQ(5, (m.element<1, 1>()));
  EXPECT_EQ(0, (m.element<1, 2>()));
  EXPECT_EQ(0, (m.element<1, 3>()));
  EXPECT_EQ(0, (m.element<2, 0>()));
  EXPECT_EQ(0, (m.element<2, 1>()));
  EXPECT_EQ(0, (m.element<2, 2>()));
  EXPECT_EQ(6, (m.element<2, 3>()));
  EXPECT_EQ(0, (m.element<3, 0>()));
  EXPECT_EQ(0, (m.element<3, 1>()));
  EXPECT_EQ(0, (m.element<3, 2>()));
  EXPECT_EQ(1, (m.element<3, 3>()));
}

TEST(affine_matrix, is_identity) {
  affine_matrix_impl<double, 4> m1;
  m1.set_element<0, 0>(1);
  m1.set_element<1, 1>(1);
  m1.set_element<2, 2>(1);
  EXPECT_TRUE(m1.is_identity());
  affine_matrix_impl<double, 4> m2 = m1;
  EXPECT_TRUE(m2.is_identity());
  m2.set_element<0, 1>(1);
  EXPECT_FALSE(m2.is_identity());
}

TEST(affine_matrix, multiply) {
  {
    affine_matrix_impl<double, 3> m1 = affine_matrix_impl<double, 3>::identity();
    affine_matrix_impl<double, 3> m2 = affine_matrix_impl<double, 3>::identity();
    m1.set_element<0, 0>(2);
    m1.set_element<1, 1>(2);
    m2.set_element<0, 2>(1);
    m2.set_element<1, 2>(1);
    {
      affine_matrix_impl<double, 3> m3 = m2 * m1;
      EXPECT_EQ(2, (m3.element<0, 0>()));
      EXPECT_EQ(0, (m3.element<0, 1>()));
      EXPECT_EQ(1, (m3.element<0, 2>()));
      EXPECT_EQ(0, (m3.element<1, 0>()));
      EXPECT_EQ(2, (m3.element<1, 1>()));
      EXPECT_EQ(1, (m3.element<1, 2>()));
      EXPECT_EQ(0, (m3.element<2, 0>()));
      EXPECT_EQ(0, (m3.element<2, 1>()));
      EXPECT_EQ(1, (m3.element<2, 2>()));
    }
    {
      affine_matrix_impl<double, 3> m3 = m1 * m2;
      EXPECT_EQ(2, (m3.element<0, 0>()));
      EXPECT_EQ(0, (m3.element<0, 1>()));
      EXPECT_EQ(2, (m3.element<0, 2>()));
      EXPECT_EQ(0, (m3.element<1, 0>()));
      EXPECT_EQ(2, (m3.element<1, 1>()));
      EXPECT_EQ(2, (m3.element<1, 2>()));
      EXPECT_EQ(0, (m3.element<2, 0>()));
      EXPECT_EQ(0, (m3.element<2, 1>()));
      EXPECT_EQ(1, (m3.element<2, 2>()));
    }
  }
  {
    affine_matrix_impl<double, 4> m1;
    affine_matrix_impl<double, 4> m2;
    m1.set_element<0, 0>(1);
    m1.set_element<0, 1>(2);
    m1.set_element<0, 2>(3);
    m1.set_element<0, 3>(4);
    m1.set_element<1, 0>(5);
    m1.set_element<1, 1>(6);
    m1.set_element<1, 2>(7);
    m1.set_element<1, 3>(8);
    m1.set_element<2, 0>(9);
    m1.set_element<2, 1>(10);
    m1.set_element<2, 2>(11);
    m1.set_element<2, 3>(12);
    m2.set_element<0, 0>(13);
    m2.set_element<0, 1>(14);
    m2.set_element<0, 2>(15);
    m2.set_element<0, 3>(16);
    m2.set_element<1, 0>(17);
    m2.set_element<1, 1>(18);
    m2.set_element<1, 2>(19);
    m2.set_element<1, 3>(20);
    m2.set_element<2, 0>(21);
    m2.set_element<2, 1>(22);
    m2.set_element<2, 2>(23);
    m2.set_element<2, 3>(24);
    {
      affine_matrix_impl<double, 4> m3 = m2 * m1;
      EXPECT_EQ(218, (m3.element<0, 0>()));
      EXPECT_EQ(260, (m3.element<0, 1>()));
      EXPECT_EQ(302, (m3.element<0, 2>()));
      EXPECT_EQ(360, (m3.element<0, 3>()));
      EXPECT_EQ(278, (m3.element<1, 0>()));
      EXPECT_EQ(332, (m3.element<1, 1>()));
      EXPECT_EQ(386, (m3.element<1, 2>()));
      EXPECT_EQ(460, (m3.element<1, 3>()));
      EXPECT_EQ(338, (m3.element<2, 0>()));
      EXPECT_EQ(404, (m3.element<2, 1>()));
      EXPECT_EQ(470, (m3.element<2, 2>()));
      EXPECT_EQ(560, (m3.element<2, 3>()));
      EXPECT_EQ(0,   (m3.element<3, 0>()));
      EXPECT_EQ(0,   (m3.element<3, 1>()));
      EXPECT_EQ(0,   (m3.element<3, 2>()));
      EXPECT_EQ(1,   (m3.element<3, 3>()));
    }
    {
      affine_matrix_impl<double, 4> m3 = m1 * m2;
      EXPECT_EQ(110, (m3.element<0, 0>()));
      EXPECT_EQ(116, (m3.element<0, 1>()));
      EXPECT_EQ(122, (m3.element<0, 2>()));
      EXPECT_EQ(132, (m3.element<0, 3>()));
      EXPECT_EQ(314, (m3.element<1, 0>()));
      EXPECT_EQ(332, (m3.element<1, 1>()));
      EXPECT_EQ(350, (m3.element<1, 2>()));
      EXPECT_EQ(376, (m3.element<1, 3>()));
      EXPECT_EQ(518, (m3.element<2, 0>()));
      EXPECT_EQ(548, (m3.element<2, 1>()));
      EXPECT_EQ(578, (m3.element<2, 2>()));
      EXPECT_EQ(620, (m3.element<2, 3>()));
      EXPECT_EQ(0,   (m3.element<3, 0>()));
      EXPECT_EQ(0,   (m3.element<3, 1>()));
      EXPECT_EQ(0,   (m3.element<3, 2>()));
      EXPECT_EQ(1,   (m3.element<3, 3>()));
    }
  }
}

}
}

#endif

#endif
<|endoftext|>


<commit_before>#include "Camera.h"

Camera::Camera(int _id, ServoCommand &_servoCommand) {
  id = _id;
  servoCommand = &_servoCommand;

  panMin = 0;
  panMax = 0;
  tiltMin = 0;
  tiltMax = 0;

  panStart = 0;
  panTarget = 0;

  tiltStart = 0;
  tiltTarget = 0;

  animationStart = 0;
  animationDuration = 0;
  oneLastFrame = false;

  isLaserOn = false;
  isPaused = false;

  position.set(0, 0, 0);
  direction.set(0, 0, 0);
}

int Camera::getId() {
  return id;
}

void Camera::update() {
  if (isAnimating()) {
    cout << "\t" << id << "\t" << floor(getPan() * 100) << ", " << floor(getTilt() * 100) << endl;
    servoCommand->setServo(id * 2 + 0, ofMap(getPan(), 0, 1, panMin, panMax));
    servoCommand->setServo(id * 2 + 1, ofMap(getTilt(), 0, 1, tiltMin, tiltMax));
  }
  else {
    servoCommand->setServo(id * 2 + 0, 0);
    servoCommand->setServo(id * 2 + 1, 0);
  }
}

void Camera::setPanExtent(int min, int max) {
  panMin = min;
  panMax = max;
}

void Camera::setTiltExtent(int min, int max) {
  tiltMin = min;
  tiltMax = max;
}

float Camera::getPan() {
  if (isAnimating()) {
    return easeInOut(
          ofGetSystemTime() - animationStart,
          panStart,
          panTarget - panStart,
          animationDuration);
  }
  else {
    return panTarget;
  }
}

void Camera::setPan(float pan) {
  setPanAndTilt(pan, getTilt());
}

float Camera::getTilt() {
  if (isAnimating()) {
    return easeInOut(
          ofGetSystemTime() - animationStart,
          tiltStart,
          tiltTarget - tiltStart,
          animationDuration);
  }
  else {
    return tiltTarget;
  }
}

void Camera::setTilt(float tilt) {
  setPanAndTilt(getPan(), tilt);
}

void Camera::setPanAndTilt(float pan, float tilt) {
  cout << "Camera(" << id << ")" << "::setPanAndTilt(" << pan << ", " << tilt << ")" << endl;
  panStart = panTarget = pan;
  tiltStart = tiltTarget = tilt;

  animationStart = 0;
  animationDuration = 0;
  oneLastFrame = true;
}

void Camera::panAndTiltTo(float pan, float tilt) {
  panAndTiltTo(pan, tilt, calculateDuration(getPan(), getTilt(), pan, tilt));
}

void Camera::setPanAndTiltHome() {
  setPanAndTilt(0.5, 0.5);
}

void Camera::panAndTiltTo(float pan, float tilt, int duration) {
  cout << "Camera(" << id << ")" << "::panAndTiltTo(" << pan << ", " << tilt << ", " << duration << ")" << endl;
  panStart = getPan();
  tiltStart = getTilt();

  panTarget = pan;
  tiltTarget = tilt;

  animationStart = ofGetSystemTime();
  animationDuration = duration;
  oneLastFrame = true;
}

bool Camera::isAnimating() {
  if (ofGetSystemTime() < animationStart + animationDuration) {
    return true;
  }
  if (oneLastFrame) {
    oneLastFrame = false;
    return true;
  }
  return false;
}

bool Camera::getLaser() {
  return isLaserOn;
}

void Camera::setLaser(bool on) {
  isLaserOn = on;
}

bool Camera::getPaused() {
  return isPaused;
}

void Camera::setPaused(bool v) {
  isPaused = v;

  if (v) {
    panStart = panTarget = getPan();
    tiltStart = tiltTarget = getTilt();

    animationStart = 0;
    animationDuration = 0;
    oneLastFrame = false;
  }
}

ofVec3f Camera::getPosition() {
  return position;
}
void Camera::setPosition(ofVec3f v) {
  position = v;
}

ofVec3f Camera::getDirection() {
  return direction;
}
void Camera::setDirection(ofVec3f v) {
  direction = v;
}

void Camera::readSettings(ofxXmlSettings &settings) {
  panMin = settings.getValue("panMin", 0);
  panMax = settings.getValue("panMax", 0);
  tiltMin = settings.getValue("tiltMin", 0);
  tiltMax = settings.getValue("tiltMax", 0);
  position = ofVec3f(
    settings.getValue("position:x", 0.0),
    settings.getValue("position:y", 0.0),
    settings.getValue("position:z", 0.0));
  direction = ofVec3f(
  settings.getValue("direction:x", 0.0),
  settings.getValue("direction:y", 0.0),
  settings.getValue("direction:z", 0.0));
}

void Camera::pushSettings(ofxXmlSettings &settings) {
  settings.setValue("id", id);
  settings.setValue("tiltMin", (int) tiltMin);
  settings.setValue("tiltMax", (int) tiltMax);
  settings.setValue("panMin", (int) panMin);
  settings.setValue("panMax", (int) panMax);

  settings.setValue("position:x", position[0]);
  settings.setValue("position:y", position[1]);
  settings.setValue("position:z", position[2]);
  settings.setValue("direction:x", direction[0]);
  settings.setValue("direction:y", direction[1]);
  settings.setValue("direction:z", direction[2]);
}

int Camera::calculateDuration(float p0, float t0, float p1, float t1) {
    float dPan = p1 - p0;
    float dTilt = t1 - t0;
    float delta = sqrt(dPan * dPan + dTilt * dTilt);

    float rPan = panMax - panMin;
    float rTilt = tiltMax - tiltMin;
    float rDelta = sqrt(rPan * rPan + rTilt * rTilt);

    return floor(ofMap(delta / rDelta, 0, 1, 2000, 4000));
}

/**
 * Thanks, Penner! Quartic easing functions.
 * @see https://github.com/jesusgollonet/processing-penner-easing/blob/master/src/Quart.java
 *
 * t: current time, b: begInnIng value, c: change In value, d: duration
 */
float Camera::easeIn(float t, float b, float c, float d) {
  return c*(t/=d)*t*t*t + b;
}

float Camera::easeOut(float t, float b, float c, float d) {
  return -c * ((t=t/d-1)*t*t*t - 1) + b;
}

float Camera::easeInOut(float t, float b, float c, float d) {
  if ((t/=d/2) < 1) return c/2*t*t*t*t + b;
  return -c/2 * ((t-=2)*t*t*t - 2) + b;
}

<commit_msg>Fixed duration calculation.<commit_after>#include "Camera.h"

Camera::Camera(int _id, ServoCommand &_servoCommand) {
  id = _id;
  servoCommand = &_servoCommand;

  panMin = 0;
  panMax = 0;
  tiltMin = 0;
  tiltMax = 0;

  panStart = 0;
  panTarget = 0;

  tiltStart = 0;
  tiltTarget = 0;

  animationStart = 0;
  animationDuration = 0;
  oneLastFrame = false;

  isLaserOn = false;
  isPaused = false;

  position.set(0, 0, 0);
  direction.set(0, 0, 0);
}

int Camera::getId() {
  return id;
}

void Camera::update() {
  if (isAnimating()) {
    cout << "\t" << id << "\t" << floor(getPan() * 100) << ", " << floor(getTilt() * 100) << endl;
    servoCommand->setServo(id * 2 + 0, ofMap(getPan(), 0, 1, panMin, panMax));
    servoCommand->setServo(id * 2 + 1, ofMap(getTilt(), 0, 1, tiltMin, tiltMax));
  }
  else {
    servoCommand->setServo(id * 2 + 0, 0);
    servoCommand->setServo(id * 2 + 1, 0);
  }
}

void Camera::setPanExtent(int min, int max) {
  panMin = min;
  panMax = max;
}

void Camera::setTiltExtent(int min, int max) {
  tiltMin = min;
  tiltMax = max;
}

float Camera::getPan() {
  if (isAnimating()) {
    return easeInOut(
          ofGetSystemTime() - animationStart,
          panStart,
          panTarget - panStart,
          animationDuration);
  }
  else {
    return panTarget;
  }
}

void Camera::setPan(float pan) {
  setPanAndTilt(pan, getTilt());
}

float Camera::getTilt() {
  if (isAnimating()) {
    return easeInOut(
          ofGetSystemTime() - animationStart,
          tiltStart,
          tiltTarget - tiltStart,
          animationDuration);
  }
  else {
    return tiltTarget;
  }
}

void Camera::setTilt(float tilt) {
  setPanAndTilt(getPan(), tilt);
}

void Camera::setPanAndTilt(float pan, float tilt) {
  cout << "Camera(" << id << ")" << "::setPanAndTilt(" << pan << ", " << tilt << ")" << endl;
  panStart = panTarget = pan;
  tiltStart = tiltTarget = tilt;

  animationStart = 0;
  animationDuration = 0;
  oneLastFrame = true;
}

void Camera::panAndTiltTo(float pan, float tilt) {
  panAndTiltTo(pan, tilt, calculateDuration(getPan(), getTilt(), pan, tilt));
}

void Camera::setPanAndTiltHome() {
  setPanAndTilt(0.5, 0.5);
}

void Camera::panAndTiltTo(float pan, float tilt, int duration) {
  cout << "Camera(" << id << ")" << "::panAndTiltTo(" << pan << ", " << tilt << ", " << duration << ")" << endl;
  panStart = getPan();
  tiltStart = getTilt();

  panTarget = pan;
  tiltTarget = tilt;

  animationStart = ofGetSystemTime();
  animationDuration = duration;
  oneLastFrame = true;
}

bool Camera::isAnimating() {
  if (ofGetSystemTime() < animationStart + animationDuration) {
    return true;
  }
  if (oneLastFrame) {
    oneLastFrame = false;
    return true;
  }
  return false;
}

bool Camera::getLaser() {
  return isLaserOn;
}

void Camera::setLaser(bool on) {
  isLaserOn = on;
}

bool Camera::getPaused() {
  return isPaused;
}

void Camera::setPaused(bool v) {
  isPaused = v;

  if (v) {
    panStart = panTarget = getPan();
    tiltStart = tiltTarget = getTilt();

    animationStart = 0;
    animationDuration = 0;
    oneLastFrame = false;
  }
}

ofVec3f Camera::getPosition() {
  return position;
}
void Camera::setPosition(ofVec3f v) {
  position = v;
}

ofVec3f Camera::getDirection() {
  return direction;
}
void Camera::setDirection(ofVec3f v) {
  direction = v;
}

void Camera::readSettings(ofxXmlSettings &settings) {
  panMin = settings.getValue("panMin", 0);
  panMax = settings.getValue("panMax", 0);
  tiltMin = settings.getValue("tiltMin", 0);
  tiltMax = settings.getValue("tiltMax", 0);
  position = ofVec3f(
    settings.getValue("position:x", 0.0),
    settings.getValue("position:y", 0.0),
    settings.getValue("position:z", 0.0));
  direction = ofVec3f(
  settings.getValue("direction:x", 0.0),
  settings.getValue("direction:y", 0.0),
  settings.getValue("direction:z", 0.0));
}

void Camera::pushSettings(ofxXmlSettings &settings) {
  settings.setValue("id", id);
  settings.setValue("tiltMin", (int) tiltMin);
  settings.setValue("tiltMax", (int) tiltMax);
  settings.setValue("panMin", (int) panMin);
  settings.setValue("panMax", (int) panMax);

  settings.setValue("position:x", position[0]);
  settings.setValue("position:y", position[1]);
  settings.setValue("position:z", position[2]);
  settings.setValue("direction:x", direction[0]);
  settings.setValue("direction:y", direction[1]);
  settings.setValue("direction:z", direction[2]);
}

int Camera::calculateDuration(float p0, float t0, float p1, float t1) {
    float dPan = p1 - p0;
    float dTilt = t1 - t0;
    float delta = sqrt(dPan * dPan + dTilt * dTilt);

    float rDelta = sqrt(2);

    return floor(ofMap(delta / rDelta, 0, 1, 2000, 4000));
}

/**
 * Thanks, Penner! Quartic easing functions.
 * @see https://github.com/jesusgollonet/processing-penner-easing/blob/master/src/Quart.java
 *
 * t: current time, b: begInnIng value, c: change In value, d: duration
 */
float Camera::easeIn(float t, float b, float c, float d) {
  return c*(t/=d)*t*t*t + b;
}

float Camera::easeOut(float t, float b, float c, float d) {
  return -c * ((t=t/d-1)*t*t*t - 1) + b;
}

float Camera::easeInOut(float t, float b, float c, float d) {
  if ((t/=d/2) < 1) return c/2*t*t*t*t + b;
  return -c/2 * ((t-=2)*t*t*t - 2) + b;
}

<|endoftext|>


<commit_before>/* This source file is part of the Tomviz project, https://tomviz.org/.
   It is released under the 3-Clause BSD License, see "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(const map<string, Variant>& m) : m_type(MAP)
{
  new (&m_value.mapVal) map<string, Variant>(m);
}

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

Variant::Variant(long l) : m_type(Variant::LONG)
{
  m_value.longVal = l;
}

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;
}

long Variant::toLong() const
{
  return m_value.longVal;
}

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;
}

map<string, Variant> Variant::toMap() const
{
  return m_value.mapVal;
}

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 LONG:
      m_value.longVal = v.toLong();
      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;
    case MAP:
      new (&m_value.mapVal) map<std::string, Variant>(v.toMap());
      break;
  }
}
} // namespace tomviz
<commit_msg>Ensure inplace new is cleaned up<commit_after>/* This source file is part of the Tomviz project, https://tomviz.org/.
   It is released under the 3-Clause BSD License, see "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(const map<string, Variant>& m) : m_type(MAP)
{
  new (&m_value.mapVal) map<string, Variant>(m);
}

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

Variant::Variant(long l) : m_type(Variant::LONG)
{
  m_value.longVal = l;
}

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>();
  } else if (m_type == MAP) {
    m_value.mapVal.~map<string, Variant>();
  }
}

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

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

long Variant::toLong() const
{
  return m_value.longVal;
}

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;
}

map<string, Variant> Variant::toMap() const
{
  return m_value.mapVal;
}

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 LONG:
      m_value.longVal = v.toLong();
      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;
    case MAP:
      new (&m_value.mapVal) map<std::string, Variant>(v.toMap());
      break;
  }
}
} // namespace tomviz
<|endoftext|>


<commit_before>#ifndef SHRINKWRAP_ZSTD_HPP
#define SHRINKWRAP_ZSTD_HPP

#ifndef ZSTD_STATIC_LINKING_ONLY
#define ZSTD_STATIC_LINKING_ONLY
#endif

#include <zstd.h>

#include <streambuf>
#include <stdio.h>
#include <vector>

namespace shrinkwrap
{
  namespace zstd
  {
    class ibuf : public std::streambuf
    {
    public:
      ibuf(FILE* fp)
        :
        strm_(ZSTD_createDStream()),
        input_({0}),
        compressed_buffer_(ZSTD_DStreamInSize()),
        decompressed_buffer_(ZSTD_DStreamOutSize()),
        current_block_position_(0),
        fp_(fp)
      {
        if (fp_)
        {
          res_ = ZSTD_initDStream(strm_); // 16 for GZIP only.
          if (ZSTD_isError(res_))
          {
            // TODO: handle error.
          }
        }
        char* end = ((char*) decompressed_buffer_.data()) + decompressed_buffer_.size();
        setg(end, end, end);
      }

      ibuf(const std::string& file_path) : ibuf(fopen(file_path.c_str(), "rb")) {}

#if !defined(__GNUC__) || defined(__clang__) || __GNUC__ > 4
      ibuf(ibuf&& src)
        :
        std::streambuf(std::move(src))
      {
        this->move(std::move(src));
      }

      ibuf& operator=(ibuf&& src)
      {
        if (&src != this)
        {
          std::streambuf::operator=(std::move(src));
          this->destroy();
          this->move(std::move(src));
        }

        return *this;
      }
#endif

      virtual ~ibuf()
      {
        this->destroy();
      }

    private:
      //ixzbuf(const ixzbuf& src) = delete;
      //ixzbuf& operator=(const ixzbuf& src) = delete;

      void destroy()
      {
        if (fp_)
        {
          ZSTD_freeDStream(strm_);
          fclose(fp_);
        }
      }

      void move(ibuf&& src)
      {
        strm_ = src.strm_;
        src.strm_ = nullptr;
        compressed_buffer_ = std::move(src.compressed_buffer_);
        decompressed_buffer_ = std::move(src.decompressed_buffer_);
        current_block_position_ = src.current_block_position_;
        fp_ = src.fp_;
        src.fp_ = nullptr;
        res_ = src.res_;
        input_ = src.input_;
      }

      void replenish_compressed_buffer()
      {
         input_ = {compressed_buffer_.data(), fread(compressed_buffer_.data(), 1, compressed_buffer_.size(), fp_), 0 };
      }

    protected:

      virtual std::streambuf::int_type underflow()
      {
        if (!fp_)
          return traits_type::eof();
        if (gptr() < egptr()) // buffer not exhausted
          return traits_type::to_int_type(*gptr());

        while (!ZSTD_isError(res_) && gptr() >= egptr() && (input_.pos < input_.size || !feof(fp_)))
        {
          if (input_.pos == input_.size && !feof(fp_))
          {
            replenish_compressed_buffer();
          }

          if (res_ == 0 && input_.pos < input_.size)
          {
            res_ = ZSTD_resetDStream(strm_);
            current_block_position_ = std::size_t(ftell(fp_)) - (input_.size - input_.pos);
          }

          ZSTD_outBuffer output = {decompressed_buffer_.data(), decompressed_buffer_.size(), 0};
          res_ = ZSTD_decompressStream(strm_, &output , &input_);

          if (!ZSTD_isError(res_))
          {
            char* start = ((char*) decompressed_buffer_.data());
            setg(start, start, start + output.pos);
          }
        }

        if (ZSTD_isError(res_))
          return traits_type::eof();
        else if (gptr() >= egptr())
          return traits_type::eof();

        return traits_type::to_int_type(*gptr());
      }

      virtual std::streambuf::pos_type seekoff(std::streambuf::off_type off, std::ios_base::seekdir way, std::ios_base::openmode which)
      {
        if (off == 0 && way == std::ios::cur)
        {
          if (egptr() - gptr() == 0 && res_ == 0)
          {
            std::uint64_t compressed_offset = std::size_t(ftell(fp_)) - (input_.size - input_.pos);
            return pos_type(off_type(compressed_offset));
          }
          else
          {
            std::uint64_t compressed_offset = current_block_position_;
            return pos_type(off_type(compressed_offset));
          }
        }
        return pos_type(off_type(-1));
      }

      virtual std::streambuf::pos_type seekpos(std::streambuf::pos_type pos, std::ios_base::openmode which)
      {
        std::uint64_t compressed_offset = static_cast<std::uint64_t>(pos);

        if (fp_ == 0 || sync())
          return pos_type(off_type(-1));

        long seek_amount = static_cast<long>(compressed_offset);
        if (fseek(fp_, seek_amount, SEEK_SET))
          return pos_type(off_type(-1));

        input_.src = nullptr;
        input_.pos = 0;
        input_.size = 0;
        res_ = 0;
        char* end = egptr();
        setg(end, end, end);

        return pos;
      }

    private:
      std::vector<std::uint8_t> compressed_buffer_;
      std::vector<std::uint8_t> decompressed_buffer_;
      ZSTD_DStream* strm_;
      ZSTD_inBuffer input_;
      FILE* fp_;
      std::size_t res_;
      std::size_t current_block_position_;
    };

    class obuf : public std::streambuf
    {
    public:
      obuf(FILE* fp)
        :
        strm_(ZSTD_createCStream()),
        fp_(fp),
        compressed_buffer_(ZSTD_CStreamOutSize()),
        decompressed_buffer_(ZSTD_CStreamInSize()),
        res_(0)
      {
        if (!fp_)
        {
          char* end = ((char*) decompressed_buffer_.data()) + decompressed_buffer_.size();
          setp(end, end);
        }
        else
        {
          res_ = ZSTD_initCStream(strm_, 3);
          if (ZSTD_isError(res_))
          {
            // TODO: handle error.
          }

          char* end = ((char*) decompressed_buffer_.data()) + decompressed_buffer_.size();
          setp((char*) decompressed_buffer_.data(), end);
        }
      }

      obuf(const std::string& file_path) : obuf(fopen(file_path.c_str(), "wb")) {}

#if !defined(__GNUC__) || defined(__clang__) || __GNUC__ > 4
      obuf(obuf&& src)
        :
        std::streambuf(std::move(src))
      {
        this->move(std::move(src));
      }

      obuf& operator=(obuf&& src)
      {
        if (&src != this)
        {
          std::streambuf::operator=(std::move(src));
          this->close();
          this->move(std::move(src));
        }

        return *this;
      }
#endif

      virtual ~obuf()
      {
        this->close();
      }

    private:
      void move(obuf&& src)
      {
        compressed_buffer_ = std::move(src.compressed_buffer_);
        decompressed_buffer_ = std::move(src.decompressed_buffer_);
        strm_ = src.strm_;
        fp_ = src.fp_;
        src.fp_ = nullptr;
        res_ = src.res_;
      }

      void close()
      {
        if (fp_)
        {
          sync();
          res_ = ZSTD_freeCStream(strm_);
          fclose(fp_);
          fp_ = nullptr;
        }
      }
    protected:
      virtual int overflow(int c)
      {
        if (!fp_)
          return traits_type::eof();

        if ((epptr() - pptr()) > 0)
        {
          assert(!"Put buffer not empty, this should never happen");
          this->sputc(static_cast<char>(0xFF & c));
        }
        else
        {
          ZSTD_inBuffer input = {decompressed_buffer_.data(), decompressed_buffer_.size(), 0};
          while (!ZSTD_isError(res_) && input.pos < input.size)
          {
            ZSTD_outBuffer output = {compressed_buffer_.data(), compressed_buffer_.size(), 0};
            res_ = ZSTD_compressStream(strm_, &output, &input);

            if (output.pos && !fwrite(compressed_buffer_.data(), output.pos, 1, fp_))
            {
              // TODO: handle error.
              return traits_type::eof();
            }
          }

          decompressed_buffer_[0] = reinterpret_cast<unsigned char&>(c);
          setp((char*) decompressed_buffer_.data() + 1, (char*) decompressed_buffer_.data() + decompressed_buffer_.size());
        }

        return (!ZSTD_isError(res_) ? traits_type::to_int_type(c) : traits_type::eof());
      }

      virtual int sync()
      {
        if (!fp_)
          return -1;

        ZSTD_inBuffer input = {decompressed_buffer_.data(), (decompressed_buffer_.size() - (epptr() - pptr())), 0};

        if (input.pos < input.size)
        {
          while (!ZSTD_isError(res_) && input.pos < input.size)
          {
            ZSTD_outBuffer output = {compressed_buffer_.data(), compressed_buffer_.size(), 0};
            res_ = ZSTD_compressStream(strm_, &output, &input);

            if (output.pos && !fwrite(compressed_buffer_.data(), output.pos, 1, fp_))
            {
              // TODO: handle error.
              return -1;
            }
          }

          while (!ZSTD_isError(res_) && res_ != 0)
          {
            ZSTD_outBuffer output = {compressed_buffer_.data(), compressed_buffer_.size(), 0};
            res_ = ZSTD_endStream(strm_, &output);
            if (output.pos && !fwrite(compressed_buffer_.data(), output.pos, 1, fp_))
            {
              // TODO: handle error.
              return -1;
            }
          }

          if (ZSTD_isError(res_))
            return -1;

          res_ = ZSTD_resetCStream(strm_, 0);

          setp((char*) decompressed_buffer_.data(), (char*) decompressed_buffer_.data() + decompressed_buffer_.size());
        }

        return 0;
      }

    private:
      std::vector<std::uint8_t> compressed_buffer_;
      std::vector<std::uint8_t> decompressed_buffer_;
      ZSTD_CStream* strm_;
      FILE* fp_;
      std::size_t res_;
    };

    class istream : public std::istream
    {
    public:
      istream(const std::string& file_path)
        :
        std::istream(&sbuf_),
        sbuf_(file_path)
      {
      }

#if !defined(__GNUC__) || defined(__clang__) || __GNUC__ > 4
      istream(istream&& src)
        :
        std::istream(&sbuf_),
        sbuf_(std::move(src.sbuf_))
      {
      }

      istream& operator=(istream&& src)
      {
        if (&src != this)
        {
          std::istream::operator=(std::move(src));
          sbuf_ = std::move(src.sbuf_);
        }
        return *this;
      }
#endif
    private:
      ::shrinkwrap::zstd::ibuf sbuf_;
    };

    class ostream : public std::ostream
    {
    public:
      ostream(const std::string& file_path)
        :
        std::ostream(&sbuf_),
        sbuf_(file_path)
      {
      }

#if !defined(__GNUC__) || defined(__clang__) || __GNUC__ > 4
      ostream(ostream&& src)
        :
        std::ostream(&sbuf_),
        sbuf_(std::move(src.sbuf_))
      {
      }

      ostream& operator=(ostream&& src)
      {
        if (&src != this)
        {
          std::ostream::operator=(std::move(src));
          sbuf_ = std::move(src.sbuf_);
        }
        return *this;
      }
#endif
    private:
      ::shrinkwrap::zstd::obuf sbuf_;
    };
  }
}

#endif //SHRINKWRAP_ZSTD_HPP<commit_msg>Adds optional compression level parameter to zstd::obuf.<commit_after>#ifndef SHRINKWRAP_ZSTD_HPP
#define SHRINKWRAP_ZSTD_HPP

#ifndef ZSTD_STATIC_LINKING_ONLY
#define ZSTD_STATIC_LINKING_ONLY
#endif

#include <zstd.h>

#include <streambuf>
#include <stdio.h>
#include <vector>

namespace shrinkwrap
{
  namespace zstd
  {
    class ibuf : public std::streambuf
    {
    public:
      ibuf(FILE* fp)
        :
        strm_(ZSTD_createDStream()),
        input_({0}),
        compressed_buffer_(ZSTD_DStreamInSize()),
        decompressed_buffer_(ZSTD_DStreamOutSize()),
        current_block_position_(0),
        fp_(fp)
      {
        if (fp_)
        {
          res_ = ZSTD_initDStream(strm_); // 16 for GZIP only.
          if (ZSTD_isError(res_))
          {
            // TODO: handle error.
          }
        }
        char* end = ((char*) decompressed_buffer_.data()) + decompressed_buffer_.size();
        setg(end, end, end);
      }

      ibuf(const std::string& file_path) : ibuf(fopen(file_path.c_str(), "rb")) {}

#if !defined(__GNUC__) || defined(__clang__) || __GNUC__ > 4
      ibuf(ibuf&& src)
        :
        std::streambuf(std::move(src))
      {
        this->move(std::move(src));
      }

      ibuf& operator=(ibuf&& src)
      {
        if (&src != this)
        {
          std::streambuf::operator=(std::move(src));
          this->destroy();
          this->move(std::move(src));
        }

        return *this;
      }
#endif

      virtual ~ibuf()
      {
        this->destroy();
      }

    private:
      //ixzbuf(const ixzbuf& src) = delete;
      //ixzbuf& operator=(const ixzbuf& src) = delete;

      void destroy()
      {
        if (fp_)
        {
          ZSTD_freeDStream(strm_);
          fclose(fp_);
        }
      }

      void move(ibuf&& src)
      {
        strm_ = src.strm_;
        src.strm_ = nullptr;
        compressed_buffer_ = std::move(src.compressed_buffer_);
        decompressed_buffer_ = std::move(src.decompressed_buffer_);
        current_block_position_ = src.current_block_position_;
        fp_ = src.fp_;
        src.fp_ = nullptr;
        res_ = src.res_;
        input_ = src.input_;
      }

      void replenish_compressed_buffer()
      {
         input_ = {compressed_buffer_.data(), fread(compressed_buffer_.data(), 1, compressed_buffer_.size(), fp_), 0 };
      }

    protected:

      virtual std::streambuf::int_type underflow()
      {
        if (!fp_)
          return traits_type::eof();
        if (gptr() < egptr()) // buffer not exhausted
          return traits_type::to_int_type(*gptr());

        while (!ZSTD_isError(res_) && gptr() >= egptr() && (input_.pos < input_.size || !feof(fp_)))
        {
          if (input_.pos == input_.size && !feof(fp_))
          {
            replenish_compressed_buffer();
          }

          if (res_ == 0 && input_.pos < input_.size)
          {
            res_ = ZSTD_resetDStream(strm_);
            current_block_position_ = std::size_t(ftell(fp_)) - (input_.size - input_.pos);
          }

          ZSTD_outBuffer output = {decompressed_buffer_.data(), decompressed_buffer_.size(), 0};
          res_ = ZSTD_decompressStream(strm_, &output , &input_);

          if (!ZSTD_isError(res_))
          {
            char* start = ((char*) decompressed_buffer_.data());
            setg(start, start, start + output.pos);
          }
        }

        if (ZSTD_isError(res_))
          return traits_type::eof();
        else if (gptr() >= egptr())
          return traits_type::eof();

        return traits_type::to_int_type(*gptr());
      }

      virtual std::streambuf::pos_type seekoff(std::streambuf::off_type off, std::ios_base::seekdir way, std::ios_base::openmode which)
      {
        if (off == 0 && way == std::ios::cur)
        {
          if (egptr() - gptr() == 0 && res_ == 0)
          {
            std::uint64_t compressed_offset = std::size_t(ftell(fp_)) - (input_.size - input_.pos);
            return pos_type(off_type(compressed_offset));
          }
          else
          {
            std::uint64_t compressed_offset = current_block_position_;
            return pos_type(off_type(compressed_offset));
          }
        }
        return pos_type(off_type(-1));
      }

      virtual std::streambuf::pos_type seekpos(std::streambuf::pos_type pos, std::ios_base::openmode which)
      {
        std::uint64_t compressed_offset = static_cast<std::uint64_t>(pos);

        if (fp_ == 0 || sync())
          return pos_type(off_type(-1));

        long seek_amount = static_cast<long>(compressed_offset);
        if (fseek(fp_, seek_amount, SEEK_SET))
          return pos_type(off_type(-1));

        input_.src = nullptr;
        input_.pos = 0;
        input_.size = 0;
        res_ = 0;
        char* end = egptr();
        setg(end, end, end);

        return pos;
      }

    private:
      std::vector<std::uint8_t> compressed_buffer_;
      std::vector<std::uint8_t> decompressed_buffer_;
      ZSTD_DStream* strm_;
      ZSTD_inBuffer input_;
      FILE* fp_;
      std::size_t res_;
      std::size_t current_block_position_;
    };

    class obuf : public std::streambuf
    {
    public:
      obuf(FILE* fp, int compression_level)
        :
        strm_(ZSTD_createCStream()),
        fp_(fp),
        compressed_buffer_(ZSTD_CStreamOutSize()),
        decompressed_buffer_(ZSTD_CStreamInSize()),
        res_(0)
      {
        if (!fp_)
        {
          char* end = ((char*) decompressed_buffer_.data()) + decompressed_buffer_.size();
          setp(end, end);
        }
        else
        {
          res_ = ZSTD_initCStream(strm_, compression_level);
          if (ZSTD_isError(res_))
          {
            // TODO: handle error.
          }

          char* end = ((char*) decompressed_buffer_.data()) + decompressed_buffer_.size();
          setp((char*) decompressed_buffer_.data(), end);
        }
      }

      obuf(const std::string& file_path, int compression_level = 3) : obuf(fopen(file_path.c_str(), "wb"), compression_level) {}

#if !defined(__GNUC__) || defined(__clang__) || __GNUC__ > 4
      obuf(obuf&& src)
        :
        std::streambuf(std::move(src))
      {
        this->move(std::move(src));
      }

      obuf& operator=(obuf&& src)
      {
        if (&src != this)
        {
          std::streambuf::operator=(std::move(src));
          this->close();
          this->move(std::move(src));
        }

        return *this;
      }
#endif

      virtual ~obuf()
      {
        this->close();
      }

    private:
      void move(obuf&& src)
      {
        compressed_buffer_ = std::move(src.compressed_buffer_);
        decompressed_buffer_ = std::move(src.decompressed_buffer_);
        strm_ = src.strm_;
        fp_ = src.fp_;
        src.fp_ = nullptr;
        res_ = src.res_;
      }

      void close()
      {
        if (fp_)
        {
          sync();
          res_ = ZSTD_freeCStream(strm_);
          fclose(fp_);
          fp_ = nullptr;
        }
      }
    protected:
      virtual int overflow(int c)
      {
        if (!fp_)
          return traits_type::eof();

        if ((epptr() - pptr()) > 0)
        {
          assert(!"Put buffer not empty, this should never happen");
          this->sputc(static_cast<char>(0xFF & c));
        }
        else
        {
          ZSTD_inBuffer input = {decompressed_buffer_.data(), decompressed_buffer_.size(), 0};
          while (!ZSTD_isError(res_) && input.pos < input.size)
          {
            ZSTD_outBuffer output = {compressed_buffer_.data(), compressed_buffer_.size(), 0};
            res_ = ZSTD_compressStream(strm_, &output, &input);

            if (output.pos && !fwrite(compressed_buffer_.data(), output.pos, 1, fp_))
            {
              // TODO: handle error.
              return traits_type::eof();
            }
          }

          decompressed_buffer_[0] = reinterpret_cast<unsigned char&>(c);
          setp((char*) decompressed_buffer_.data() + 1, (char*) decompressed_buffer_.data() + decompressed_buffer_.size());
        }

        return (!ZSTD_isError(res_) ? traits_type::to_int_type(c) : traits_type::eof());
      }

      virtual int sync()
      {
        if (!fp_)
          return -1;

        ZSTD_inBuffer input = {decompressed_buffer_.data(), (decompressed_buffer_.size() - (epptr() - pptr())), 0};

        if (input.pos < input.size)
        {
          while (!ZSTD_isError(res_) && input.pos < input.size)
          {
            ZSTD_outBuffer output = {compressed_buffer_.data(), compressed_buffer_.size(), 0};
            res_ = ZSTD_compressStream(strm_, &output, &input);

            if (output.pos && !fwrite(compressed_buffer_.data(), output.pos, 1, fp_))
            {
              // TODO: handle error.
              return -1;
            }
          }

          while (!ZSTD_isError(res_) && res_ != 0)
          {
            ZSTD_outBuffer output = {compressed_buffer_.data(), compressed_buffer_.size(), 0};
            res_ = ZSTD_endStream(strm_, &output);
            if (output.pos && !fwrite(compressed_buffer_.data(), output.pos, 1, fp_))
            {
              // TODO: handle error.
              return -1;
            }
          }

          if (ZSTD_isError(res_))
            return -1;

          res_ = ZSTD_resetCStream(strm_, 0);

          setp((char*) decompressed_buffer_.data(), (char*) decompressed_buffer_.data() + decompressed_buffer_.size());
        }

        return 0;
      }

    private:
      std::vector<std::uint8_t> compressed_buffer_;
      std::vector<std::uint8_t> decompressed_buffer_;
      ZSTD_CStream* strm_;
      FILE* fp_;
      std::size_t res_;
    };

    class istream : public std::istream
    {
    public:
      istream(const std::string& file_path)
        :
        std::istream(&sbuf_),
        sbuf_(file_path)
      {
      }

#if !defined(__GNUC__) || defined(__clang__) || __GNUC__ > 4
      istream(istream&& src)
        :
        std::istream(&sbuf_),
        sbuf_(std::move(src.sbuf_))
      {
      }

      istream& operator=(istream&& src)
      {
        if (&src != this)
        {
          std::istream::operator=(std::move(src));
          sbuf_ = std::move(src.sbuf_);
        }
        return *this;
      }
#endif
    private:
      ::shrinkwrap::zstd::ibuf sbuf_;
    };

    class ostream : public std::ostream
    {
    public:
      ostream(const std::string& file_path)
        :
        std::ostream(&sbuf_),
        sbuf_(file_path)
      {
      }

#if !defined(__GNUC__) || defined(__clang__) || __GNUC__ > 4
      ostream(ostream&& src)
        :
        std::ostream(&sbuf_),
        sbuf_(std::move(src.sbuf_))
      {
      }

      ostream& operator=(ostream&& src)
      {
        if (&src != this)
        {
          std::ostream::operator=(std::move(src));
          sbuf_ = std::move(src.sbuf_);
        }
        return *this;
      }
#endif
    private:
      ::shrinkwrap::zstd::obuf sbuf_;
    };
  }
}

#endif //SHRINKWRAP_ZSTD_HPP<|endoftext|>


<commit_before>#include <iostream>
#include <algorithm>
#include <iterator>
#include <vector>
#include <iomanip>
#include <map>
#include <utility>
#include <functional>
#include <unordered_set>

using ull = unsigned long long;

struct LogEntry {
    ull endtime, starttime;
    unsigned int bitrate;
    double streamed, prev_streamed, duration;
};

using LogEntries = std::vector<LogEntry>;

LogEntries reorder(const LogEntries&);
void repl(const LogEntries&, int);
bool compare(const LogEntry&, const LogEntry&);
std::istream &operator >> (std::istream&, LogEntry&);

#ifdef DEBUG
std::ostream &operator << (std::ostream&, const LogEntry&);

template <typename F, typename S>
std::ostream &operator << (std::ostream&, const std::pair<F, S>&);
#endif

int main() {

    std::ios_base::sync_with_stdio(false);
    std::cin.tie(nullptr);

    LogEntries logEntries;
    int logsize;
    std::cin >> logsize;

    std::copy_n(std::istream_iterator<LogEntry>(std::cin), logsize,
        std::back_inserter(logEntries));

    #ifdef DEBUG
    std::cout << "\n";
    for (auto& ent : logEntries) {
        std::cout << ent << '\n';
    }
    std::cout << "\n\n";
    #endif

    logEntries = std::move(reorder(logEntries));

    #ifdef DEBUG
    // std::cout << "Count: " << logEntries.size() << '\n';
    for (auto& ent : logEntries) {
        std::cout << ent << '\n';
    }
    std::cout << "\n\n";
    #endif

    if (std::cin >> logsize) {
        repl(logEntries, logsize);
    }

    return 0;
}

/**
 * @brief Reads time ranges and sells lambourghinis'
 * @details The repl stands for READ-EVALUATE-PRINT-LOOP.
 * This method in particular will read any time range given, determine which log entries
 * fall under this time range. It will then take the streams available under this
 * time range and print what percentage of those streams were fully contained
 * within the range given
 *
 * @param entries The collection of logs. This must have gone through the reorder
 * function before it can be used here
 * @param count The number of queries to expect
 */
void repl(const LogEntries& entries, int count) {
    LogEntries::const_iterator hi, lo;
    LogEntry query;
    std::cout.precision(3);
    std::cout.setf(std::ios_base::fixed);

    for (std::cin >> query.starttime >> query.endtime; count--;
        std::cin >> query.starttime >> query.endtime) {

        std::tie(lo, hi) = std::equal_range(entries.begin(), entries.end(),
            query, compare);

        hi--;

        if (query.starttime < lo->starttime) {
            query.starttime = lo->starttime;
        }

        if (query.endtime > hi->endtime) {
            query.endtime = hi->endtime;
        }

        if (hi != lo) {
            query.streamed = (hi->prev_streamed - lo->prev_streamed - lo->streamed) +
            ((lo->endtime - query.starttime) / lo->duration * lo->streamed) +
            ((query.endtime - hi->starttime) / hi->duration * hi->streamed);
        } else {
            query.streamed = (query.endtime - query.starttime) / 1000 * lo->bitrate;
        }

        std::cout << query.streamed << '\n';
    }
}

/**
 * @brief Aranges the logs into non overlapping ranges for easier usage
 * @details The logs are taken one by one and we cut them up into smaller
 * logs which do not overlap. By doing this, it is easier to work with ranges
 * because you don't have to worry about anything they overlap
 *
 * @param entries The initial entries we got as input
 */
LogEntries reorder(const LogEntries &entries) {
    using index_set = std::unordered_set<std::size_t>;

    std::map<ull, index_set> rng_to_entries;
    std::map<ull, index_set>::const_iterator iter;

    for (std::size_t v = 0; v < entries.size(); v++) {
        rng_to_entries[entries[v].starttime].insert(v);
        rng_to_entries[entries[v].endtime].insert(v);
    }

    LogEntries merged;
    index_set main_set, var_set;

    unsigned int curr_bitrate = 0;

    for (iter = rng_to_entries.begin(); ++iter != rng_to_entries.end(); ) {
        --iter;
        LogEntry range = {};
        std::tie(range.starttime, var_set) = *iter++;
        range.endtime = iter->first;
        range.duration = range.endtime - range.starttime;

        // determine all entries that overlap this range
        for (auto &v : var_set) {
            if (main_set.erase(v) == 0) {
                curr_bitrate += entries[v].bitrate;
                main_set.insert(v);
            } else {
                curr_bitrate -= entries[v].bitrate;
            }
        }

        // set the bitrate of this range to be an aggregate of bitrate of the overlaps
        range.bitrate = curr_bitrate;

        // calculate the amount streamed
        range.streamed = range.duration / 1000 * range.bitrate;

        if (!merged.empty()) {
            range.prev_streamed = merged.back().streamed + merged.back().prev_streamed;
        }
        merged.push_back(range);
    }
    return merged;
}

inline bool compare(const LogEntry& lhs, const LogEntry& rhs) {
    return lhs.endtime <= rhs.starttime;
}

std::istream &operator >> (std::istream& iss, LogEntry& entry) {
    iss >> entry.endtime >> entry.duration >> entry.bitrate;
    entry.starttime = entry.endtime - entry.duration;
    return iss;
}

std::ostream &operator << (std::ostream& oss, const LogEntry& entry) {
    oss << "start=" << entry.starttime << ", end=" << entry.endtime
    << ", bitrate(s)=" << entry.bitrate << ", streamed(kb)="
    << std::setprecision(3) << std::fixed << entry.streamed;

    return oss;
}

template <typename F, typename S>
std::ostream &operator << (std::ostream& oss, const std::pair<F, S> &p) {
    return oss << "(First=>" << p.first << ", second=>" << p.second << ")";
}
<commit_msg>Still giving wrong answer...where is this bug??<commit_after>#include <iostream>
#include <algorithm>
#include <iterator>
#include <vector>
#include <iomanip>
#include <map>
#include <utility>
#include <unordered_set>

using ull = unsigned long long;

struct LogEntry {
    ull endtime, starttime;
    unsigned int bitrate;
    double streamed, prev_streamed, duration;
};

using LogEntries = std::vector<LogEntry>;

LogEntries reorder(const LogEntries&);
void repl(const LogEntries&, int);
bool compare(const LogEntry&, const LogEntry&);
std::istream &operator >> (std::istream&, LogEntry&);

#ifdef DEBUG
std::ostream &operator << (std::ostream&, const LogEntry&);

template <typename F, typename S>
std::ostream &operator << (std::ostream&, const std::pair<F, S>&);
#endif

int main() {

    std::ios_base::sync_with_stdio(false);
    std::cin.tie(nullptr);

    LogEntries logEntries;
    int logsize;
    std::cin >> logsize;

    std::copy_n(std::istream_iterator<LogEntry>(std::cin), logsize,
        std::back_inserter(logEntries));

    #ifdef DEBUG
    std::cout << "\n";
    for (auto& ent : logEntries) {
        std::cout << ent << '\n';
    }
    std::cout << "\n\n";
    #endif

    logEntries = std::move(reorder(logEntries));

    #ifdef DEBUG
    std::cout << "Count: " << logEntries.size() << '\n';
    for (auto& ent : logEntries) {
        std::cout << ent << '\n';
    }
    std::cout << "\n\n";
    #endif

    if (std::cin >> logsize) {
        repl(logEntries, logsize);
    }

    return 0;
}

/**
 * @brief Reads time ranges and sells lambourghinis'
 * @details The repl stands for READ-EVALUATE-PRINT-LOOP.
 * This method in particular will read any time range given, determine which log entries
 * fall under this time range. It will then take the streams available under this
 * time range and print what percentage of those streams were fully contained
 * within the range given
 *
 * @param entries The collection of logs. This must have gone through the reorder
 * function before it can be used here
 * @param count The number of queries to expect
 */
void repl(const LogEntries& entries, int count) {
    LogEntries::const_iterator hi, lo;
    LogEntry query;
    std::cout.precision(3);
    std::cout.setf(std::ios_base::fixed);

    for (std::cin >> query.starttime >> query.endtime; count--;
        std::cin >> query.starttime >> query.endtime) {

        std::tie(lo, hi) = std::equal_range(entries.begin(), entries.end(),
            query, compare);

        hi--;

        if (query.starttime < lo->starttime) {
            query.starttime = lo->starttime;
        }

        if (query.endtime > hi->endtime) {
            query.endtime = hi->endtime;
        }

        if (hi != lo) {
            query.streamed = (hi->prev_streamed - lo->prev_streamed - lo->streamed) +
            ((lo->endtime - query.starttime) / lo->duration * lo->streamed) +
            ((query.endtime - hi->starttime) / hi->duration * hi->streamed);
        } else {
            query.duration = query.endtime - query.starttime;
            query.streamed = query.duration / 1000 * lo->bitrate;
        }
        std::cout << query.streamed << '\n';
    }
}

/**
 * @brief Aranges the logs into non overlapping ranges for easier usage
 * @details The logs are taken one by one and we cut them up into smaller
 * logs which do not overlap. By doing this, it is easier to work with ranges
 * because you don't have to worry about anything they overlap
 *
 * @param entries The initial entries we got as input
 */
LogEntries reorder(const LogEntries &entries) {
    using index_set = std::unordered_set<std::size_t>;

    std::map<ull, index_set> rng_to_entries;
    std::map<ull, index_set>::const_iterator iter;

    for (std::size_t v = 0; v < entries.size(); v++) {
        rng_to_entries[entries[v].starttime].insert(v);
        rng_to_entries[entries[v].endtime].insert(v);
    }

    LogEntries merged;
    index_set main_set, var_set;

    unsigned int curr_bitrate = 0;

    for (iter = rng_to_entries.begin(); ++iter != rng_to_entries.end(); ) {
        --iter;
        LogEntry range = {};
        std::tie(range.starttime, var_set) = *iter++;
        range.endtime = iter->first;
        range.duration = range.endtime - range.starttime;

        // determine all entries that overlap this range
        for (auto &v : var_set) {
            if (main_set.erase(v) == 0) {
                curr_bitrate += entries[v].bitrate;
                main_set.insert(v);
            } else {
                curr_bitrate -= entries[v].bitrate;
            }
        }

        // set the bitrate of this range to be an aggregate of bitrate of the overlaps
        range.bitrate = curr_bitrate;

        // calculate the amount streamed
        range.streamed = range.duration / 1000 * range.bitrate;

        if (!merged.empty()) {
            range.prev_streamed = merged.back().streamed + merged.back().prev_streamed;
        }
        merged.push_back(range);
    }
    return merged;
}

inline bool compare(const LogEntry& lhs, const LogEntry& rhs) {
    return lhs.endtime <= rhs.starttime;
}

std::istream &operator >> (std::istream& iss, LogEntry& entry) {
    iss >> entry.endtime >> entry.duration >> entry.bitrate;
    entry.starttime = entry.endtime - entry.duration;
    return iss;
}

std::ostream &operator << (std::ostream& oss, const LogEntry& entry) {
    oss << "start=" << entry.starttime << ", end=" << entry.endtime
    << ", bitrate(s)=" << entry.bitrate << ", streamed(kb)="
    << std::setprecision(3) << std::fixed << entry.streamed;

    return oss;
}

template <typename F, typename S>
std::ostream &operator << (std::ostream& oss, const std::pair<F, S> &p) {
    return oss << "(First=>" << p.first << ", second=>" << p.second << ")";
}
<|endoftext|>