<commit_before>//
//  database.cpp
//  Xapiand
//
//  Created by Germán M. Bravo on 2/23/15.
//  Copyright (c) 2015 Germán M. Bravo. All rights reserved.
//

#include "database.h"

Database::Database(Endpoints &endpoints_, bool writable_)
	: endpoints(endpoints_),
	  writable(writable_)
{
	hash = endpoints.hash(writable);
	reopen();
}

void
Database::reopen()
{
	// FIXME: Handle remote endpoints and figure out if the endpoint is a local database
	if (writable) {
		db = new Xapian::WritableDatabase(endpoints[0].path, Xapian::DB_CREATE_OR_OPEN);
	} else {
		db = new Xapian::Database(endpoints[0].path, Xapian::DB_CREATE_OR_OPEN);
		if (!writable) {
			std::vector<Endpoint>::const_iterator i(endpoints.begin());
			for (++i; i != endpoints.end(); ++i) {
				db->add_database(Xapian::Database((*i).path));
			}
		} else if (endpoints.size() != 1) {
			printf("ERROR: Expecting exactly one database.");
		}
	}
}

Database::~Database()
{
	delete db;
}

DatabaseQueue::~DatabaseQueue()
{
	//		std::queue<Database *>::const_iterator i(queue.begin());
	//		for (; i != databases.end(); ++i) {
	//			(*i).second.finish();
	//		}
}

DatabasePool::DatabasePool()
{
	pthread_mutex_init(&qmtx, 0);
}

DatabasePool::~DatabasePool()
{
	finish();
	pthread_mutex_destroy(&qmtx);
}

void DatabasePool::finish() {
	pthread_mutex_lock(&qmtx);

	finished = true;

	pthread_mutex_unlock(&qmtx);
}

bool
DatabasePool::checkout(Database **database, Endpoints &endpoints, bool writable)
{
	Database *database_ = NULL;

	pthread_mutex_lock(&qmtx);

	if (!finished && *database == NULL) {
		size_t hash = endpoints.hash(writable);
		DatabaseQueue &queue = databases[hash];

		if (!queue.pop(database_, 0)) {
			if (!writable || queue.instances_count == 0) {
				database_ = new Database(endpoints, writable);
				queue.instances_count++;
			}
			// FIXME: lock until a database is available if it can't get one
		}
		*database = database_;
	}

	pthread_mutex_unlock(&qmtx);

	return database_ != NULL;
}

void
DatabasePool::checkin(Database **database)
{
	pthread_mutex_lock(&qmtx);

	DatabaseQueue &queue = databases[(*database)->hash];

	queue.push(*database);

	*database = NULL;

	pthread_mutex_unlock(&qmtx);
}
<commit_msg>Initialization fixes<commit_after>//
//  database.cpp
//  Xapiand
//
//  Created by Germán M. Bravo on 2/23/15.
//  Copyright (c) 2015 Germán M. Bravo. All rights reserved.
//

#include "database.h"

Database::Database(Endpoints &endpoints_, bool writable_)
	: endpoints(endpoints_),
	  writable(writable_)
{
	hash = endpoints.hash(writable);
	reopen();
}

void
Database::reopen()
{
	// FIXME: Handle remote endpoints and figure out if the endpoint is a local database
	if (writable) {
		db = new Xapian::WritableDatabase(endpoints[0].path, Xapian::DB_CREATE_OR_OPEN);
	} else {
		db = new Xapian::Database(endpoints[0].path, Xapian::DB_CREATE_OR_OPEN);
		if (!writable) {
			std::vector<Endpoint>::const_iterator i(endpoints.begin());
			for (++i; i != endpoints.end(); ++i) {
				db->add_database(Xapian::Database((*i).path));
			}
		} else if (endpoints.size() != 1) {
			printf("ERROR: Expecting exactly one database.");
		}
	}
}

Database::~Database()
{
	delete db;
}

DatabaseQueue::DatabaseQueue()
	: count(0)
{
}

DatabaseQueue::~DatabaseQueue()
{
	//		std::queue<Database *>::const_iterator i(queue.begin());
	//		for (; i != databases.end(); ++i) {
	//			(*i).second.finish();
	//		}
}

DatabasePool::DatabasePool()
	: finished(false)
{
	pthread_mutex_init(&qmtx, 0);
}

DatabasePool::~DatabasePool()
{
	finish();
	pthread_mutex_destroy(&qmtx);
}

void DatabasePool::finish() {
	pthread_mutex_lock(&qmtx);

	finished = true;

	pthread_mutex_unlock(&qmtx);
}

bool
DatabasePool::checkout(Database **database, Endpoints &endpoints, bool writable)
{
	Database *database_ = NULL;

	pthread_mutex_lock(&qmtx);

	if (!finished && *database == NULL) {
		size_t hash = endpoints.hash(writable);
		DatabaseQueue &queue = databases[hash];

		if (!queue.pop(database_, 0)) {
			if (!writable || queue.count == 0) {
				database_ = new Database(endpoints, writable);
				queue.count++;
			}
			// FIXME: lock until a database is available if it can't get one
		}
		*database = database_;
	}

	pthread_mutex_unlock(&qmtx);

	return database_ != NULL;
}

void
DatabasePool::checkin(Database **database)
{
	pthread_mutex_lock(&qmtx);

	DatabaseQueue &queue = databases[(*database)->hash];

	queue.push(*database);

	*database = NULL;

	pthread_mutex_unlock(&qmtx);
}
<|endoftext|>


<commit_before>#include <string.h>
#include <v8.h>
#include <node.h>
#include <node_events.h>

#include "macros.h"
#include "database.h"
#include "statement.h"

using namespace node_sqlite3;

Persistent<FunctionTemplate> Database::constructor_template;

void Database::Init(Handle<Object> target) {
    HandleScope scope;

    Local<FunctionTemplate> t = FunctionTemplate::New(New);

    constructor_template = Persistent<FunctionTemplate>::New(t);
    constructor_template->Inherit(EventEmitter::constructor_template);
    constructor_template->InstanceTemplate()->SetInternalFieldCount(1);
    constructor_template->SetClassName(String::NewSymbol("Database"));

    NODE_SET_PROTOTYPE_METHOD(constructor_template, "close", Close);
    NODE_SET_PROTOTYPE_METHOD(constructor_template, "exec", Exec);
    NODE_SET_PROTOTYPE_METHOD(constructor_template, "serialize", Serialize);
    NODE_SET_PROTOTYPE_METHOD(constructor_template, "parallelize", Parallelize);

    target->Set(String::NewSymbol("Database"),
        constructor_template->GetFunction());
}

void Database::Process() {
    if (!open && locked && !queue.empty()) {
        EXCEPTION(String::New("Database handle is closed"), SQLITE_MISUSE, exception);
        Local<Value> argv[] = { exception };
        bool called = false;

        // Call all callbacks with the error object.
        while (!queue.empty()) {
            Call* call = queue.front();
            if (!call->baton->callback.IsEmpty() && call->baton->callback->IsFunction()) {
                TRY_CATCH_CALL(handle_, call->baton->callback, 1, argv);
                called = true;
            }
            queue.pop();
            // We don't call the actual callback, so we have to make sure that
            // the baton gets destroyed.
            delete call->baton;
            delete call;
        }

        // When we couldn't call a callback function, emit an error on the
        // Database object.
        if (!called) {
            Local<Value> args[] = { String::NewSymbol("error"), exception };
            EMIT_EVENT(handle_, 2, args);
        }
        return;
    }

    while (open && (!locked || pending == 0) && !queue.empty()) {
        Call* call = queue.front();

        if (call->exclusive && pending > 0) {
            break;
        }

        queue.pop();
        locked = call->exclusive;
        call->callback(call->baton);
        delete call;

        if (locked) break;
    }
}

void Database::Schedule(EIO_Callback callback, Baton* baton, bool exclusive) {
    if (!open && locked) {
        EXCEPTION(String::New("Database is closed"), SQLITE_MISUSE, exception);
        if (!baton->callback.IsEmpty() && baton->callback->IsFunction()) {
            Local<Value> argv[] = { exception };
            TRY_CATCH_CALL(handle_, baton->callback, 1, argv);
        }
        else {
            Local<Value> argv[] = { String::NewSymbol("error"), exception };
            EMIT_EVENT(handle_, 2, argv);
        }
        return;
    }

    if (!open || ((locked || exclusive || serialize) && pending > 0)) {
        queue.push(new Call(callback, baton, exclusive || serialize));
    }
    else {
        locked = exclusive;
        callback(baton);
    }
}

Handle<Value> Database::New(const Arguments& args) {
    HandleScope scope;

    if (!args.IsConstructCall()) {
        return ThrowException(Exception::TypeError(
            String::New("Use the new operator to create new Database objects"))
        );
    }

    REQUIRE_ARGUMENT_STRING(0, filename);
    int pos = 1;

    int mode = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE;
    if (args.Length() >= pos && args[pos]->IsInt32()) {
        mode = args[pos++]->Int32Value();
    }

    Local<Function> callback;
    if (args.Length() >= pos && args[pos]->IsFunction()) {
        callback = Local<Function>::Cast(args[pos++]);
    }

    Database* db = new Database();
    db->Wrap(args.This());

    args.This()->Set(String::NewSymbol("filename"), args[0]->ToString(), ReadOnly);
    args.This()->Set(String::NewSymbol("mode"), Integer::New(mode), ReadOnly);

    // Start opening the database.
    OpenBaton* baton = new OpenBaton(db, callback, *filename, SQLITE_OPEN_FULLMUTEX | mode);
    EIO_BeginOpen(baton);

    return args.This();
}

void Database::EIO_BeginOpen(Baton* baton) {
    eio_custom(EIO_Open, EIO_PRI_DEFAULT, EIO_AfterOpen, baton);
}

int Database::EIO_Open(eio_req *req) {
    OpenBaton* baton = static_cast<OpenBaton*>(req->data);
    Database* db = baton->db;

    baton->status = sqlite3_open_v2(
        baton->filename.c_str(),
        &db->handle,
        baton->mode,
        NULL
    );

    if (baton->status != SQLITE_OK) {
        baton->message = std::string(sqlite3_errmsg(db->handle));
        db->handle = NULL;
    }

    return 0;
}

int Database::EIO_AfterOpen(eio_req *req) {
    HandleScope scope;
    OpenBaton* baton = static_cast<OpenBaton*>(req->data);
    Database* db = baton->db;

    Local<Value> argv[1];
    if (baton->status != SQLITE_OK) {
        EXCEPTION(String::New(baton->message.c_str()), baton->status, exception);
        argv[0] = exception;
    }
    else {
        db->open = true;
        argv[0] = Local<Value>::New(Null());
    }

    if (!baton->callback.IsEmpty() && baton->callback->IsFunction()) {
        TRY_CATCH_CALL(db->handle_, baton->callback, 1, argv);
    }
    else if (!db->open) {
        Local<Value> args[] = { String::NewSymbol("error"), argv[0] };
        EMIT_EVENT(db->handle_, 2, args);
    }

    if (db->open) {
        Local<Value> args[] = { String::NewSymbol("open") };
        EMIT_EVENT(db->handle_, 1, args);
        db->Process();
    }

    delete baton;
    return 0;
}

Handle<Value> Database::Close(const Arguments& args) {
    HandleScope scope;
    Database* db = ObjectWrap::Unwrap<Database>(args.This());
    OPTIONAL_ARGUMENT_FUNCTION(0, callback);

    Baton* baton = new Baton(db, callback);
    db->Schedule(EIO_BeginClose, baton, true);

    return args.This();
}

void Database::EIO_BeginClose(Baton* baton) {
    assert(baton->db->locked);
    assert(baton->db->open);
    assert(baton->db->handle);
    assert(baton->db->pending == 0);
    eio_custom(EIO_Close, EIO_PRI_DEFAULT, EIO_AfterClose, baton);
}

int Database::EIO_Close(eio_req *req) {
    Baton* baton = static_cast<Baton*>(req->data);
    Database* db = baton->db;

    baton->status = sqlite3_close(db->handle);

    if (baton->status != SQLITE_OK) {
        baton->message = std::string(sqlite3_errmsg(db->handle));
    }
    else {
        db->handle = NULL;
    }
    return 0;
}

int Database::EIO_AfterClose(eio_req *req) {
    HandleScope scope;
    Baton* baton = static_cast<Baton*>(req->data);
    Database* db = baton->db;

    Local<Value> argv[1];
    if (baton->status != SQLITE_OK) {
        EXCEPTION(String::New(baton->message.c_str()), baton->status, exception);
        argv[0] = exception;
    }
    else {
        db->open = false;
        // Leave db->locked to indicate that this db object has reached
        // the end of its life.
        argv[0] = Local<Value>::New(Null());
    }

    // Fire callbacks.
    if (!baton->callback.IsEmpty() && baton->callback->IsFunction()) {
        TRY_CATCH_CALL(db->handle_, baton->callback, 1, argv);
    }
    else if (db->open) {
        Local<Value> args[] = { String::NewSymbol("error"), argv[0] };
        EMIT_EVENT(db->handle_, 2, args);
    }

    assert(baton->db->locked);
    assert(!baton->db->open);
    assert(!baton->db->handle);
    assert(baton->db->pending == 0);

    if (!db->open) {
        Local<Value> args[] = { String::NewSymbol("close"), argv[0] };
        EMIT_EVENT(db->handle_, 1, args);
        db->Process();
    }

    delete baton;
    return 0;
}

Handle<Value> Database::Serialize(const Arguments& args) {
    HandleScope scope;
    Database* db = ObjectWrap::Unwrap<Database>(args.This());
    OPTIONAL_ARGUMENT_FUNCTION(0, callback);

    bool before = db->serialize;
    db->serialize = true;

    if (!callback.IsEmpty() && callback->IsFunction()) {
        TRY_CATCH_CALL(args.This(), callback, 0, NULL);
        db->serialize = before;
    }

    db->Process();

    return args.This();
}

Handle<Value> Database::Parallelize(const Arguments& args) {
    HandleScope scope;
    Database* db = ObjectWrap::Unwrap<Database>(args.This());
    OPTIONAL_ARGUMENT_FUNCTION(0, callback);

    bool before = db->serialize;
    db->serialize = false;

    if (!callback.IsEmpty() && callback->IsFunction()) {
        TRY_CATCH_CALL(args.This(), callback, 0, NULL);
        db->serialize = before;
    }

    db->Process();

    return args.This();
}

Handle<Value> Database::Exec(const Arguments& args) {
    HandleScope scope;
    Database* db = ObjectWrap::Unwrap<Database>(args.This());

    REQUIRE_ARGUMENT_STRING(0, sql);
    OPTIONAL_ARGUMENT_FUNCTION(1, callback);

    Baton* baton = new ExecBaton(db, callback, *sql);
    db->Schedule(EIO_BeginExec, baton, true);

    return args.This();
}

void Database::EIO_BeginExec(Baton* baton) {
    assert(baton->db->locked);
    assert(baton->db->open);
    assert(baton->db->handle);
    assert(baton->db->pending == 0);
    eio_custom(EIO_Exec, EIO_PRI_DEFAULT, EIO_AfterExec, baton);
}

int Database::EIO_Exec(eio_req *req) {
    ExecBaton* baton = static_cast<ExecBaton*>(req->data);

    char* message = NULL;
    baton->status = sqlite3_exec(
        baton->db->handle,
        baton->sql.c_str(),
        NULL,
        NULL,
        &message
    );

    if (baton->status != SQLITE_OK && message != NULL) {
        baton->message = std::string(message);
        sqlite3_free(message);
    }

    return 0;
}

int Database::EIO_AfterExec(eio_req *req) {
    HandleScope scope;
    Baton* baton = static_cast<ExecBaton*>(req->data);
    Database* db = baton->db;

    if (baton->status != SQLITE_OK) {
        EXCEPTION(String::New(baton->message.c_str()), baton->status, exception);

        if (!baton->callback.IsEmpty() && baton->callback->IsFunction()) {
            Local<Value> argv[] = { exception };
            TRY_CATCH_CALL(db->handle_, baton->callback, 1, argv);
        }
        else {
            Local<Value> args[] = { String::NewSymbol("error"), exception };
            EMIT_EVENT(db->handle_, 2, args);
        }
    }
    else if (!baton->callback.IsEmpty() && baton->callback->IsFunction()) {
        Local<Value> argv[] = { Local<Value>::New(Null()) };
        TRY_CATCH_CALL(db->handle_, baton->callback, 1, argv);
    }

    db->Process();

    delete baton;
    return 0;
}

/**
 * Override this so that we can properly close the database when this object
 * gets garbage collected.
 */
void Database::Wrap(Handle<Object> handle) {
    assert(handle_.IsEmpty());
    assert(handle->InternalFieldCount() > 0);
    handle_ = Persistent<Object>::New(handle);
    handle_->SetPointerInInternalField(0, this);
    handle_.MakeWeak(this, Destruct);
}

inline void Database::MakeWeak (void) {
    handle_.MakeWeak(this, Destruct);
}

void Database::Unref() {
    assert(!handle_.IsEmpty());
    assert(!handle_.IsWeak());
    assert(refs_ > 0);
    if (--refs_ == 0) { MakeWeak(); }
}

void Database::Destruct(Persistent<Value> value, void *data) {
    Database* db = static_cast<Database*>(data);
    if (db->handle) {
        eio_custom(EIO_Destruct, EIO_PRI_DEFAULT, EIO_AfterDestruct, db);
        ev_ref(EV_DEFAULT_UC);
    }
    else {
        delete db;
    }
}

int Database::EIO_Destruct(eio_req *req) {
    Database* db = static_cast<Database*>(req->data);

    sqlite3_close(db->handle);
    db->handle = NULL;

    return 0;
}

int Database::EIO_AfterDestruct(eio_req *req) {
    Database* db = static_cast<Database*>(req->data);
    ev_unref(EV_DEFAULT_UC);
    delete db;
    return 0;
}
<commit_msg>fix random "Use the new operator to create new Database objects" message<commit_after>#include <string.h>
#include <v8.h>
#include <node.h>
#include <node_events.h>

#include "macros.h"
#include "database.h"
#include "statement.h"

using namespace node_sqlite3;

Persistent<FunctionTemplate> Database::constructor_template;

void Database::Init(Handle<Object> target) {
    HandleScope scope;

    Local<FunctionTemplate> t = FunctionTemplate::New(New);

    constructor_template = Persistent<FunctionTemplate>::New(t);
    constructor_template->Inherit(EventEmitter::constructor_template);
    constructor_template->InstanceTemplate()->SetInternalFieldCount(1);
    constructor_template->SetClassName(String::NewSymbol("Database"));

    NODE_SET_PROTOTYPE_METHOD(constructor_template, "close", Close);
    NODE_SET_PROTOTYPE_METHOD(constructor_template, "exec", Exec);
    NODE_SET_PROTOTYPE_METHOD(constructor_template, "serialize", Serialize);
    NODE_SET_PROTOTYPE_METHOD(constructor_template, "parallelize", Parallelize);

    target->Set(String::NewSymbol("Database"),
        constructor_template->GetFunction());
}

void Database::Process() {
    if (!open && locked && !queue.empty()) {
        EXCEPTION(String::New("Database handle is closed"), SQLITE_MISUSE, exception);
        Local<Value> argv[] = { exception };
        bool called = false;

        // Call all callbacks with the error object.
        while (!queue.empty()) {
            Call* call = queue.front();
            if (!call->baton->callback.IsEmpty() && call->baton->callback->IsFunction()) {
                TRY_CATCH_CALL(handle_, call->baton->callback, 1, argv);
                called = true;
            }
            queue.pop();
            // We don't call the actual callback, so we have to make sure that
            // the baton gets destroyed.
            delete call->baton;
            delete call;
        }

        // When we couldn't call a callback function, emit an error on the
        // Database object.
        if (!called) {
            Local<Value> args[] = { String::NewSymbol("error"), exception };
            EMIT_EVENT(handle_, 2, args);
        }
        return;
    }

    while (open && (!locked || pending == 0) && !queue.empty()) {
        Call* call = queue.front();

        if (call->exclusive && pending > 0) {
            break;
        }

        queue.pop();
        locked = call->exclusive;
        call->callback(call->baton);
        delete call;

        if (locked) break;
    }
}

void Database::Schedule(EIO_Callback callback, Baton* baton, bool exclusive) {
    if (!open && locked) {
        EXCEPTION(String::New("Database is closed"), SQLITE_MISUSE, exception);
        if (!baton->callback.IsEmpty() && baton->callback->IsFunction()) {
            Local<Value> argv[] = { exception };
            TRY_CATCH_CALL(handle_, baton->callback, 1, argv);
        }
        else {
            Local<Value> argv[] = { String::NewSymbol("error"), exception };
            EMIT_EVENT(handle_, 2, argv);
        }
        return;
    }

    if (!open || ((locked || exclusive || serialize) && pending > 0)) {
        queue.push(new Call(callback, baton, exclusive || serialize));
    }
    else {
        locked = exclusive;
        callback(baton);
    }
}

Handle<Value> Database::New(const Arguments& args) {
    HandleScope scope;

    if (!Database::HasInstance(args.This())) {
        return ThrowException(Exception::TypeError(
            String::New("Use the new operator to create new Database objects"))
        );
    }

    REQUIRE_ARGUMENT_STRING(0, filename);
    int pos = 1;

    int mode = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE;
    if (args.Length() >= pos && args[pos]->IsInt32()) {
        mode = args[pos++]->Int32Value();
    }

    Local<Function> callback;
    if (args.Length() >= pos && args[pos]->IsFunction()) {
        callback = Local<Function>::Cast(args[pos++]);
    }

    Database* db = new Database();
    db->Wrap(args.This());

    args.This()->Set(String::NewSymbol("filename"), args[0]->ToString(), ReadOnly);
    args.This()->Set(String::NewSymbol("mode"), Integer::New(mode), ReadOnly);

    // Start opening the database.
    OpenBaton* baton = new OpenBaton(db, callback, *filename, SQLITE_OPEN_FULLMUTEX | mode);
    EIO_BeginOpen(baton);

    return args.This();
}

void Database::EIO_BeginOpen(Baton* baton) {
    eio_custom(EIO_Open, EIO_PRI_DEFAULT, EIO_AfterOpen, baton);
}

int Database::EIO_Open(eio_req *req) {
    OpenBaton* baton = static_cast<OpenBaton*>(req->data);
    Database* db = baton->db;

    baton->status = sqlite3_open_v2(
        baton->filename.c_str(),
        &db->handle,
        baton->mode,
        NULL
    );

    if (baton->status != SQLITE_OK) {
        baton->message = std::string(sqlite3_errmsg(db->handle));
        db->handle = NULL;
    }

    return 0;
}

int Database::EIO_AfterOpen(eio_req *req) {
    HandleScope scope;
    OpenBaton* baton = static_cast<OpenBaton*>(req->data);
    Database* db = baton->db;

    Local<Value> argv[1];
    if (baton->status != SQLITE_OK) {
        EXCEPTION(String::New(baton->message.c_str()), baton->status, exception);
        argv[0] = exception;
    }
    else {
        db->open = true;
        argv[0] = Local<Value>::New(Null());
    }

    if (!baton->callback.IsEmpty() && baton->callback->IsFunction()) {
        TRY_CATCH_CALL(db->handle_, baton->callback, 1, argv);
    }
    else if (!db->open) {
        Local<Value> args[] = { String::NewSymbol("error"), argv[0] };
        EMIT_EVENT(db->handle_, 2, args);
    }

    if (db->open) {
        Local<Value> args[] = { String::NewSymbol("open") };
        EMIT_EVENT(db->handle_, 1, args);
        db->Process();
    }

    delete baton;
    return 0;
}

Handle<Value> Database::Close(const Arguments& args) {
    HandleScope scope;
    Database* db = ObjectWrap::Unwrap<Database>(args.This());
    OPTIONAL_ARGUMENT_FUNCTION(0, callback);

    Baton* baton = new Baton(db, callback);
    db->Schedule(EIO_BeginClose, baton, true);

    return args.This();
}

void Database::EIO_BeginClose(Baton* baton) {
    assert(baton->db->locked);
    assert(baton->db->open);
    assert(baton->db->handle);
    assert(baton->db->pending == 0);
    eio_custom(EIO_Close, EIO_PRI_DEFAULT, EIO_AfterClose, baton);
}

int Database::EIO_Close(eio_req *req) {
    Baton* baton = static_cast<Baton*>(req->data);
    Database* db = baton->db;

    baton->status = sqlite3_close(db->handle);

    if (baton->status != SQLITE_OK) {
        baton->message = std::string(sqlite3_errmsg(db->handle));
    }
    else {
        db->handle = NULL;
    }
    return 0;
}

int Database::EIO_AfterClose(eio_req *req) {
    HandleScope scope;
    Baton* baton = static_cast<Baton*>(req->data);
    Database* db = baton->db;

    Local<Value> argv[1];
    if (baton->status != SQLITE_OK) {
        EXCEPTION(String::New(baton->message.c_str()), baton->status, exception);
        argv[0] = exception;
    }
    else {
        db->open = false;
        // Leave db->locked to indicate that this db object has reached
        // the end of its life.
        argv[0] = Local<Value>::New(Null());
    }

    // Fire callbacks.
    if (!baton->callback.IsEmpty() && baton->callback->IsFunction()) {
        TRY_CATCH_CALL(db->handle_, baton->callback, 1, argv);
    }
    else if (db->open) {
        Local<Value> args[] = { String::NewSymbol("error"), argv[0] };
        EMIT_EVENT(db->handle_, 2, args);
    }

    assert(baton->db->locked);
    assert(!baton->db->open);
    assert(!baton->db->handle);
    assert(baton->db->pending == 0);

    if (!db->open) {
        Local<Value> args[] = { String::NewSymbol("close"), argv[0] };
        EMIT_EVENT(db->handle_, 1, args);
        db->Process();
    }

    delete baton;
    return 0;
}

Handle<Value> Database::Serialize(const Arguments& args) {
    HandleScope scope;
    Database* db = ObjectWrap::Unwrap<Database>(args.This());
    OPTIONAL_ARGUMENT_FUNCTION(0, callback);

    bool before = db->serialize;
    db->serialize = true;

    if (!callback.IsEmpty() && callback->IsFunction()) {
        TRY_CATCH_CALL(args.This(), callback, 0, NULL);
        db->serialize = before;
    }

    db->Process();

    return args.This();
}

Handle<Value> Database::Parallelize(const Arguments& args) {
    HandleScope scope;
    Database* db = ObjectWrap::Unwrap<Database>(args.This());
    OPTIONAL_ARGUMENT_FUNCTION(0, callback);

    bool before = db->serialize;
    db->serialize = false;

    if (!callback.IsEmpty() && callback->IsFunction()) {
        TRY_CATCH_CALL(args.This(), callback, 0, NULL);
        db->serialize = before;
    }

    db->Process();

    return args.This();
}

Handle<Value> Database::Exec(const Arguments& args) {
    HandleScope scope;
    Database* db = ObjectWrap::Unwrap<Database>(args.This());

    REQUIRE_ARGUMENT_STRING(0, sql);
    OPTIONAL_ARGUMENT_FUNCTION(1, callback);

    Baton* baton = new ExecBaton(db, callback, *sql);
    db->Schedule(EIO_BeginExec, baton, true);

    return args.This();
}

void Database::EIO_BeginExec(Baton* baton) {
    assert(baton->db->locked);
    assert(baton->db->open);
    assert(baton->db->handle);
    assert(baton->db->pending == 0);
    eio_custom(EIO_Exec, EIO_PRI_DEFAULT, EIO_AfterExec, baton);
}

int Database::EIO_Exec(eio_req *req) {
    ExecBaton* baton = static_cast<ExecBaton*>(req->data);

    char* message = NULL;
    baton->status = sqlite3_exec(
        baton->db->handle,
        baton->sql.c_str(),
        NULL,
        NULL,
        &message
    );

    if (baton->status != SQLITE_OK && message != NULL) {
        baton->message = std::string(message);
        sqlite3_free(message);
    }

    return 0;
}

int Database::EIO_AfterExec(eio_req *req) {
    HandleScope scope;
    Baton* baton = static_cast<ExecBaton*>(req->data);
    Database* db = baton->db;

    if (baton->status != SQLITE_OK) {
        EXCEPTION(String::New(baton->message.c_str()), baton->status, exception);

        if (!baton->callback.IsEmpty() && baton->callback->IsFunction()) {
            Local<Value> argv[] = { exception };
            TRY_CATCH_CALL(db->handle_, baton->callback, 1, argv);
        }
        else {
            Local<Value> args[] = { String::NewSymbol("error"), exception };
            EMIT_EVENT(db->handle_, 2, args);
        }
    }
    else if (!baton->callback.IsEmpty() && baton->callback->IsFunction()) {
        Local<Value> argv[] = { Local<Value>::New(Null()) };
        TRY_CATCH_CALL(db->handle_, baton->callback, 1, argv);
    }

    db->Process();

    delete baton;
    return 0;
}

/**
 * Override this so that we can properly close the database when this object
 * gets garbage collected.
 */
void Database::Wrap(Handle<Object> handle) {
    assert(handle_.IsEmpty());
    assert(handle->InternalFieldCount() > 0);
    handle_ = Persistent<Object>::New(handle);
    handle_->SetPointerInInternalField(0, this);
    handle_.MakeWeak(this, Destruct);
}

inline void Database::MakeWeak (void) {
    handle_.MakeWeak(this, Destruct);
}

void Database::Unref() {
    assert(!handle_.IsEmpty());
    assert(!handle_.IsWeak());
    assert(refs_ > 0);
    if (--refs_ == 0) { MakeWeak(); }
}

void Database::Destruct(Persistent<Value> value, void *data) {
    Database* db = static_cast<Database*>(data);
    if (db->handle) {
        eio_custom(EIO_Destruct, EIO_PRI_DEFAULT, EIO_AfterDestruct, db);
        ev_ref(EV_DEFAULT_UC);
    }
    else {
        delete db;
    }
}

int Database::EIO_Destruct(eio_req *req) {
    Database* db = static_cast<Database*>(req->data);

    sqlite3_close(db->handle);
    db->handle = NULL;

    return 0;
}

int Database::EIO_AfterDestruct(eio_req *req) {
    Database* db = static_cast<Database*>(req->data);
    ev_unref(EV_DEFAULT_UC);
    delete db;
    return 0;
}
<|endoftext|>


<commit_before>/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
#ifndef STORED_VALUE_H
#define STORED_VALUE_H 1

#include "locks.hh"

extern "C" {
    extern rel_time_t (*ep_current_time)();
}

// Forward declaration for StoredValue
class HashTable;

class StoredValue {
public:
    StoredValue(const Item &itm, StoredValue *n) :
        key(itm.getKey()), value(itm.getValue()),
        flags(itm.getFlags()), exptime(itm.getExptime()), dirtied(0), next(n),
        cas(itm.getCas())
    {
        markDirty();
    }

    StoredValue(const Item &itm, StoredValue *n, bool setDirty) :
        key(itm.getKey()), value(itm.getValue()),
        flags(itm.getFlags()), exptime(itm.getExptime()), dirtied(0), next(n),
        cas(itm.getCas())
    {
        if (setDirty) {
            markDirty();
        } else {
            markClean(NULL, NULL);
        }
    }

    ~StoredValue() {
    }
    void markDirty() {
        data_age = ep_current_time();
        if (!isDirty()) {
            dirtied = data_age;
        }
    }

    void reDirty(rel_time_t dirtyAge, rel_time_t dataAge) {
        data_age = dataAge;
        dirtied = dirtyAge;
    }

    // returns time this object was dirtied.
    void markClean(rel_time_t *dirtyAge, rel_time_t *dataAge) {
        if (dirtyAge) {
            *dirtyAge = dirtied;
        }
        if (dataAge) {
            *dataAge = data_age;
        }
        dirtied = 0;
        data_age = 0;
    }

    bool isDirty() const {
        return dirtied != 0;
    }

    bool isClean() const {
        return dirtied == 0;
    }

    const std::string &getKey() const {
        return key;
    }

    value_t getValue() const {
        return value;
    }

    rel_time_t getExptime() const {
        return exptime;
    }

    uint32_t getFlags() const {
        return flags;
    }

    void setValue(value_t v,
                  uint32_t newFlags, rel_time_t newExp, uint64_t theCas) {
        cas = theCas;
        flags = newFlags;
        exptime = newExp;
        value = v;
        markDirty();
    }

    uint64_t getCas() const {
        return cas;
    }

    // for stats
    rel_time_t getDirtied() const {
        return dirtied;
    }

    rel_time_t getDataAge() const {
        return data_age;
    }

private:

    friend class HashTable;

    std::string key;
    value_t value;
    uint32_t flags;
    rel_time_t exptime;
    rel_time_t dirtied;
    rel_time_t data_age;
    StoredValue *next;
    uint64_t cas;
    DISALLOW_COPY_AND_ASSIGN(StoredValue);
};

typedef enum {
    NOT_FOUND, INVALID_CAS, WAS_CLEAN, WAS_DIRTY
} mutation_type_t;

class HashTableVisitor {
public:
    virtual ~HashTableVisitor() {}
    virtual void visit(StoredValue *v) = 0;
};

class HashTable {
public:

    // Construct with number of buckets and locks.
    HashTable(size_t s = 196613, size_t l = 193) {
        size = s;
        n_locks = l;
        active = true;
        values = (StoredValue**)calloc(s, sizeof(StoredValue**));
        mutexes = new Mutex[l];
    }

    ~HashTable() {
        clear();
        delete []mutexes;
        free(values);
    }

    void clear() {
        assert(active);
        for (int i = 0; i < (int)size; i++) {
            LockHolder lh(getMutex(i));
            while (values[i]) {
                StoredValue *v = values[i];
                values[i] = v->next;
                delete v;
            }
        }
    }

    StoredValue *find(std::string &key) {
        assert(active);
        int bucket_num = bucket(key);
        LockHolder lh(getMutex(bucket_num));
        return unlocked_find(key, bucket_num);
    }

    mutation_type_t set(const Item &val) {
        assert(active);
        mutation_type_t rv = NOT_FOUND;
        int bucket_num = bucket(val.getKey());
        LockHolder lh(getMutex(bucket_num));
        StoredValue *v = unlocked_find(val.getKey(), bucket_num);
        Item &itm = const_cast<Item&>(val);
        if (v) {
            if (val.getCas() != 0 && val.getCas() != v->getCas()) {
                return INVALID_CAS;
            }
            itm.setCas();
            rv = v->isClean() ? WAS_CLEAN : WAS_DIRTY;
            v->setValue(itm.getValue(),
                        itm.getFlags(), itm.getExptime(),
                        itm.getCas());
        } else {
            if (itm.getCas() != 0) {
                return INVALID_CAS;
            }
            itm.setCas();
            v = new StoredValue(itm, values[bucket_num]);
            values[bucket_num] = v;
        }
        return rv;
    }

    bool add(const Item &val, bool isDirty = true) {
        assert(active);
        int bucket_num = bucket(val.getKey());
        LockHolder lh(getMutex(bucket_num));
        StoredValue *v = unlocked_find(val.getKey(), bucket_num);
        if (v) {
            return false;
        } else {
            Item &itm = const_cast<Item&>(val);
            itm.setCas();
            v = new StoredValue(itm, values[bucket_num], isDirty);
            values[bucket_num] = v;
        }

        return true;
    }

    StoredValue *unlocked_find(const std::string &key, int bucket_num) {
        StoredValue *v = values[bucket_num];
        while (v) {
            if (key.compare(v->key) == 0) {
                return v;
            }
            v = v->next;
        }
        return NULL;
    }

    inline int bucket(const std::string &key) {
        assert(active);
        int h=5381;
        int i=0;
        const char *str = key.c_str();

        for(i=0; str[i] != 0x00; i++) {
            h = ((h << 5) + h) ^ str[i];
        }

        return abs(h) % (int)size;
    }

    // Get the mutex for a bucket (for doing your own lock management)
    inline Mutex &getMutex(int bucket_num) {
        assert(active);
        assert(bucket_num < (int)size);
        assert(bucket_num >= 0);
        int lock_num = bucket_num % (int)n_locks;
        assert(lock_num < (int)n_locks);
        assert(lock_num >= 0);
        return mutexes[lock_num];
    }

    // True if it existed
    bool del(const std::string &key) {
        assert(active);
        int bucket_num = bucket(key);
        LockHolder lh(getMutex(bucket_num));

        StoredValue *v = values[bucket_num];

        // Special case empty bucket.
        if (!v) {
            return false;
        }

        // Special case the first one
        if (key.compare(v->key) == 0) {
            values[bucket_num] = v->next;
            delete v;
            return true;
        }

        while (v->next) {
            if (key.compare(v->next->key) == 0) {
                StoredValue *tmp = v->next;
                v->next = v->next->next;
                delete tmp;
                return true;
            } else {
                v = v->next;
            }
        }

        return false;
    }

    void visit(HashTableVisitor &visitor) {
        for (int i = 0; i < (int)size; i++) {
            LockHolder lh(getMutex(i));
            StoredValue *v = values[i];
            while (v) {
                visitor.visit(v);
                v = v->next;
            }
        }
    }

private:
    size_t            size;
    size_t            n_locks;
    bool              active;
    StoredValue     **values;
    Mutex            *mutexes;

    DISALLOW_COPY_AND_ASSIGN(HashTable);
};

#endif /* STORED_VALUE_H */
<commit_msg>Use new[] for hash table values.<commit_after>/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
#ifndef STORED_VALUE_H
#define STORED_VALUE_H 1

#include <algorithm>

#include "locks.hh"

extern "C" {
    extern rel_time_t (*ep_current_time)();
}

// Forward declaration for StoredValue
class HashTable;

class StoredValue {
public:
    StoredValue(const Item &itm, StoredValue *n) :
        key(itm.getKey()), value(itm.getValue()),
        flags(itm.getFlags()), exptime(itm.getExptime()), dirtied(0), next(n),
        cas(itm.getCas())
    {
        markDirty();
    }

    StoredValue(const Item &itm, StoredValue *n, bool setDirty) :
        key(itm.getKey()), value(itm.getValue()),
        flags(itm.getFlags()), exptime(itm.getExptime()), dirtied(0), next(n),
        cas(itm.getCas())
    {
        if (setDirty) {
            markDirty();
        } else {
            markClean(NULL, NULL);
        }
    }

    ~StoredValue() {
    }
    void markDirty() {
        data_age = ep_current_time();
        if (!isDirty()) {
            dirtied = data_age;
        }
    }

    void reDirty(rel_time_t dirtyAge, rel_time_t dataAge) {
        data_age = dataAge;
        dirtied = dirtyAge;
    }

    // returns time this object was dirtied.
    void markClean(rel_time_t *dirtyAge, rel_time_t *dataAge) {
        if (dirtyAge) {
            *dirtyAge = dirtied;
        }
        if (dataAge) {
            *dataAge = data_age;
        }
        dirtied = 0;
        data_age = 0;
    }

    bool isDirty() const {
        return dirtied != 0;
    }

    bool isClean() const {
        return dirtied == 0;
    }

    const std::string &getKey() const {
        return key;
    }

    value_t getValue() const {
        return value;
    }

    rel_time_t getExptime() const {
        return exptime;
    }

    uint32_t getFlags() const {
        return flags;
    }

    void setValue(value_t v,
                  uint32_t newFlags, rel_time_t newExp, uint64_t theCas) {
        cas = theCas;
        flags = newFlags;
        exptime = newExp;
        value = v;
        markDirty();
    }

    uint64_t getCas() const {
        return cas;
    }

    // for stats
    rel_time_t getDirtied() const {
        return dirtied;
    }

    rel_time_t getDataAge() const {
        return data_age;
    }

private:

    friend class HashTable;

    std::string key;
    value_t value;
    uint32_t flags;
    rel_time_t exptime;
    rel_time_t dirtied;
    rel_time_t data_age;
    StoredValue *next;
    uint64_t cas;
    DISALLOW_COPY_AND_ASSIGN(StoredValue);
};

typedef enum {
    NOT_FOUND, INVALID_CAS, WAS_CLEAN, WAS_DIRTY
} mutation_type_t;

class HashTableVisitor {
public:
    virtual ~HashTableVisitor() {}
    virtual void visit(StoredValue *v) = 0;
};

class HashTable {
public:

    // Construct with number of buckets and locks.
    HashTable(size_t s = 196613, size_t l = 193) {
        size = s;
        n_locks = l;
        active = true;
        values = new StoredValue*[s];
        std::fill_n(values, s, static_cast<StoredValue*>(NULL));
        mutexes = new Mutex[l];
    }

    ~HashTable() {
        clear();
        delete []mutexes;
        delete []values;
    }

    void clear() {
        assert(active);
        for (int i = 0; i < (int)size; i++) {
            LockHolder lh(getMutex(i));
            while (values[i]) {
                StoredValue *v = values[i];
                values[i] = v->next;
                delete v;
            }
        }
    }

    StoredValue *find(std::string &key) {
        assert(active);
        int bucket_num = bucket(key);
        LockHolder lh(getMutex(bucket_num));
        return unlocked_find(key, bucket_num);
    }

    mutation_type_t set(const Item &val) {
        assert(active);
        mutation_type_t rv = NOT_FOUND;
        int bucket_num = bucket(val.getKey());
        LockHolder lh(getMutex(bucket_num));
        StoredValue *v = unlocked_find(val.getKey(), bucket_num);
        Item &itm = const_cast<Item&>(val);
        if (v) {
            if (val.getCas() != 0 && val.getCas() != v->getCas()) {
                return INVALID_CAS;
            }
            itm.setCas();
            rv = v->isClean() ? WAS_CLEAN : WAS_DIRTY;
            v->setValue(itm.getValue(),
                        itm.getFlags(), itm.getExptime(),
                        itm.getCas());
        } else {
            if (itm.getCas() != 0) {
                return INVALID_CAS;
            }
            itm.setCas();
            v = new StoredValue(itm, values[bucket_num]);
            values[bucket_num] = v;
        }
        return rv;
    }

    bool add(const Item &val, bool isDirty = true) {
        assert(active);
        int bucket_num = bucket(val.getKey());
        LockHolder lh(getMutex(bucket_num));
        StoredValue *v = unlocked_find(val.getKey(), bucket_num);
        if (v) {
            return false;
        } else {
            Item &itm = const_cast<Item&>(val);
            itm.setCas();
            v = new StoredValue(itm, values[bucket_num], isDirty);
            values[bucket_num] = v;
        }

        return true;
    }

    StoredValue *unlocked_find(const std::string &key, int bucket_num) {
        StoredValue *v = values[bucket_num];
        while (v) {
            if (key.compare(v->key) == 0) {
                return v;
            }
            v = v->next;
        }
        return NULL;
    }

    inline int bucket(const std::string &key) {
        assert(active);
        int h=5381;
        int i=0;
        const char *str = key.c_str();

        for(i=0; str[i] != 0x00; i++) {
            h = ((h << 5) + h) ^ str[i];
        }

        return abs(h) % (int)size;
    }

    // Get the mutex for a bucket (for doing your own lock management)
    inline Mutex &getMutex(int bucket_num) {
        assert(active);
        assert(bucket_num < (int)size);
        assert(bucket_num >= 0);
        int lock_num = bucket_num % (int)n_locks;
        assert(lock_num < (int)n_locks);
        assert(lock_num >= 0);
        return mutexes[lock_num];
    }

    // True if it existed
    bool del(const std::string &key) {
        assert(active);
        int bucket_num = bucket(key);
        LockHolder lh(getMutex(bucket_num));

        StoredValue *v = values[bucket_num];

        // Special case empty bucket.
        if (!v) {
            return false;
        }

        // Special case the first one
        if (key.compare(v->key) == 0) {
            values[bucket_num] = v->next;
            delete v;
            return true;
        }

        while (v->next) {
            if (key.compare(v->next->key) == 0) {
                StoredValue *tmp = v->next;
                v->next = v->next->next;
                delete tmp;
                return true;
            } else {
                v = v->next;
            }
        }

        return false;
    }

    void visit(HashTableVisitor &visitor) {
        for (int i = 0; i < (int)size; i++) {
            LockHolder lh(getMutex(i));
            StoredValue *v = values[i];
            while (v) {
                visitor.visit(v);
                v = v->next;
            }
        }
    }

private:
    size_t            size;
    size_t            n_locks;
    bool              active;
    StoredValue     **values;
    Mutex            *mutexes;

    DISALLOW_COPY_AND_ASSIGN(HashTable);
};

#endif /* STORED_VALUE_H */
<|endoftext|>


<commit_before>/*
 * Copyright 2013 Matthew Harvey
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "date_parser.hpp"
#include "date.hpp"
#include <boost/algorithm/string.hpp>
#include <boost/date_time/gregorian/gregorian.hpp>
#include <boost/lambda/lambda.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/optional.hpp>
#include <jewel/assert.hpp>
#include <jewel/log.hpp>
#include <jewel/optional.hpp>
#include <wx/datetime.h>
#include <wx/intl.h>
#include <wx/string.h>
#include <algorithm>
#include <exception>
#include <iterator>
#include <map>
#include <string>
#include <unordered_set>
#include <vector>

using boost::algorithm::split;
using boost::lexical_cast;
using boost::optional;
using jewel::Log;
using jewel::value;
using std::back_inserter;
using std::begin;
using std::end;
using std::find;
using std::find_first_of;
using std::map;
using std::out_of_range;
using std::string;
using std::transform;
using std::unordered_set;
using std::vector;

namespace gregorian = boost::gregorian;
namespace lambda = boost::lambda;

namespace dcm
{

namespace
{
	// Returns true if and only if c is one of the special characters that
	// can appear in a strftime format string representing a field date or
	// time field.
	// TODO MEDIUM PRIORITY There characters under the C++11 standard in
	// addition to the below.
	bool is_date_time_format_char(char c)
	{
		static char const chars_a[] =
		{	'a', 'A', 'b', 'B', 'c', 'd', 'H', 'I', 'j', 'm', 'M', 'P',
			'S', 'U', 'w', 'W', 'x', 'X', 'y', 'Y', 'Z'
		};
		static unordered_set<char> const chars(begin(chars_a), end(chars_a));
		return chars.find(c) != chars.end();
	}

	enum class DateComponentType: unsigned char
	{
		day,
		month,
		year
	};

	map<char, DateComponentType> const&
	ordinary_date_format_chars_map()
	{
		typedef map<char, DateComponentType> Map;
		static Map ret;
		if (ret.empty())
		{
			ret['d'] = DateComponentType::day;   // day of month (01-31)
			ret['m'] = DateComponentType::month; // month (01-12)
			ret['y'] = DateComponentType::year;  // year in century (00-99)
			ret['Y'] = DateComponentType::year;  // full year
#			ifndef NDEBUG
				for (auto const& elem: ret)
				{
					JEWEL_ASSERT (is_date_time_format_char(elem.first));
				}
#			endif  // NDEBUG
		}
		JEWEL_ASSERT (!ret.empty());
		return ret;
	}

	// Returns true if and only if c is one of the special characters that
	// can appear in a strftime format string representing a field in a tm
	// struct, which is a date field and not a time field, and which
	// represents in numerical format, either the day of the month, the
	// month, or the year.
	bool is_ordinary_date_format_char(char c)
	{
		map<char, DateComponentType> const& map =
			ordinary_date_format_chars_map();
		return map.find(c) != map.end();
	}

	// Examines a strftime-like date format string. If the string is an
	// "ordinary" date format string, returns an optional initialized with the
	// character that separates the fields in that string; otherwise, returns
	// an uninitialized optional. For this purpose, the string is an
	// "ordinary" date format string if and only if: (a) there are exactly
	// three date fields, being a day-of-month, month, and year field
	// (not necessarily in that order), such that each of these fields is
	// represented by a character for which is_ordinary_date_format_char
	// returns true (along with the usual '%' and possible other modifiers
	// between the '%' and the format char); and (b) there is exactly one
	// separator character, which appears twice in the string, separating
	// the first and second fields, and separating the second and third
	// fields.
	optional<char> separating_char(wxString const& str)
	{
		// Analyse the string; if at any point we find anything inconsistent
		// with "ordinary date format", then we return an uninitialized
		// optional.
		JEWEL_LOG_TRACE();
		JEWEL_LOG_VALUE(Log::info, str);
		optional<char> separator;
		wxString::const_iterator it = str.begin();
		wxString::const_iterator const end = str.end();
		size_t num_fields = 0;
		while (it != end)
		{
			if (*it == '%')
			{
				// Skip over any "modifiers"
				while ((it != end) && !is_date_time_format_char(*it)) ++it;

				// If we've reached the end without find a
				// date_time_format_char, then it's not ordinary format.
				if (it == end)
				{
					return optional<char>();
				}
				JEWEL_ASSERT (is_date_time_format_char(*it));

				// If our format char is not ordinary, then it's not
				// ordinary format.
				if (!is_ordinary_date_format_char(*it))
				{
					return optional<char>();
				}
				JEWEL_ASSERT (is_ordinary_date_format_char(*it));
				++num_fields;
				++it;

				// Have we found all the fields? Then we should
				// be at the end.
				if (num_fields == 3)
				{
					JEWEL_ASSERT (separator || (it != end));
					return separator;
				}
				JEWEL_ASSERT (num_fields < 3);

				// To be an ordinary format string,
				// the next character should be a separator, and not another
				// format char, and not the end. If we already
				// have encountered a separator, then this should be an
				// instance of the same separator.
				if
				(	(it == end) ||
					(is_date_time_format_char(*it)) ||
					(separator && (*separator != *it))
				)
				{
					return optional<char>();
				}
				separator = *it;
				++it;
			}
			else
			{
				return optional<char>();
			}
		}
		return separator;
	}

	optional<DateComponentType> maybe_date_component_type(char c)
	{
		optional<DateComponentType> ret;
		switch (c)
		{
		case 'd':
			ret = DateComponentType::day;
			break;
		case 'm':
			ret = DateComponentType::month;
			break;
		case 'y':  // fall through
		case 'Y':
			ret = DateComponentType::year;
			break;
		default:
			;  // do nothing and fall through
		}
		JEWEL_ASSERT
		(	static_cast<bool>(ret) ==
			is_ordinary_date_format_char(c)
		);
		return ret;
	}

	// Should only call if we know the last character of s is such as
	// to return an initialized optional when passed to date_component_type.
	DateComponentType date_component_type(wxString const s)
	{
		JEWEL_ASSERT (!s.IsEmpty());
		return value(maybe_date_component_type(static_cast<char>(s.Last())));
	}

	void fill_missing_components(map<DateComponentType, int>& out)
	{
		switch (out.size())
		{
		case 0:
			out[DateComponentType::day] = today().day();
			goto fill_month;
		case 1:
			if (out.find(DateComponentType::day) == out.end()) return;
			fill_month: out[DateComponentType::month] = today().month();
			goto fill_year;
		case 2:
			if (out.find(DateComponentType::month) == out.end()) return;
			fill_year: out[DateComponentType::year] = today().year();
			// fall through
		default:
			return;
		}
	}

	optional<gregorian::date>
	tolerant_parse_aux
	(	wxString const& p_target,
		wxString const& p_format
	)
	{
		optional<char> const maybe_sep = separating_char(p_format);
		if (!maybe_sep)
		{
			return optional<gregorian::date>();
		}
		JEWEL_ASSERT (maybe_sep);
		char const sep = *maybe_sep;
		JEWEL_LOG_VALUE(Log::trace, sep);
		vector<wxString> target_fields;
		vector<wxString> format_fields;
		split(target_fields, p_target, (lambda::_1 == sep));
		split(format_fields, p_format, (lambda::_1 == sep));
		if ((target_fields.size() > 3) || (format_fields.size() != 3))
		{
			return optional<gregorian::date>();
		}
		JEWEL_ASSERT (target_fields.size() <= 3);
		JEWEL_ASSERT (format_fields.size() == 3);
		vector<DateComponentType> component_types;
		transform
		(	format_fields.begin(),
			format_fields.end(),
			back_inserter(component_types),
			date_component_type
		);
		JEWEL_ASSERT (component_types.size() == 3);
		map<DateComponentType, int> components;

		// bare scope
		{
			vector<wxString>::size_type j = 0;
			vector<wxString>::size_type const szj = target_fields.size();
			for ( ; j != szj; ++j)
			{
				wxString field = target_fields[j];
				if ((*(field.begin()) == '0') && (field.size() > 1))
				{
					field = wxString(field.begin() + 1, field.end());
				}
				int val = -1;
				try
				{
					val = lexical_cast<int>(field);
				}
				catch (boost::bad_lexical_cast&)
				{
					return optional<gregorian::date>();
				}
				JEWEL_ASSERT (val >= 0);
				JEWEL_ASSERT (j < 3);
				DateComponentType component_type = component_types[j];
				if (target_fields.size() == 1)
				{
					component_type = DateComponentType::day;
				}
				else if (target_fields.size() == 2)
				{
					DateComponentType const day_and_month[] =
					{	DateComponentType::day,
						DateComponentType::month
					};
					DateComponentType const first = *find_first_of
					(	component_types.begin(),
						component_types.end(),
						begin(day_and_month),
						end(day_and_month)
					);
					switch (j)
					{
					case 0:
						component_type = first;
						break;
					case 1:
						component_type =
						(	(first == DateComponentType::day)?
							DateComponentType::month:
							DateComponentType::day
						);
						break;
					default:
						JEWEL_HARD_ASSERT (false);
					}
				}
				components[component_type] = val;
			}
		}  // end bare scope

		fill_missing_components(components);
		if (components.size() != 3)
		{
			return optional<gregorian::date>();
		}
		if (components.at(DateComponentType::year) < 100)
		{
			components[DateComponentType::year] += 2000;
		}
		try
		{
			JEWEL_LOG_TRACE();
			JEWEL_ASSERT (components.size() == 3);
			return optional<gregorian::date>
			(	gregorian::date
				(	components.at(DateComponentType::year),
					components.at(DateComponentType::month),
					components.at(DateComponentType::day)
				)
			);
		}
		catch (out_of_range&)
		{
			return optional<gregorian::date>();
		}
	}

}  // end anonymous namespace

DateParser::DateParser
(	wxString const& p_primary_format,
	wxString const& p_secondary_format
):
	m_primary_format(p_primary_format),
	m_secondary_format(p_secondary_format)
{
}

optional<gregorian::date>
DateParser::parse(wxString const& p_string, bool p_be_tolerant) const
{
	optional<gregorian::date> ret;
	wxString::const_iterator parsed_to_position;
	wxDateTime date_wx;

	wxString const* const formats[] =
	{	&m_primary_format,
		&m_secondary_format
	};
	for (wxString const* const format: formats)
	{
		date_wx.ParseFormat
		(	p_string,
			*(format),
			&parsed_to_position
		);
		if (parsed_to_position == p_string.end())
		{
			// Parsing was successful
			int year = date_wx.GetYear();
			if (year < 100) year += 2000;
			int const month = static_cast<int>(date_wx.GetMonth()) + 1;
			int const day = date_wx.GetDay();
			try
			{
				ret = gregorian::date(year, month, day);
				return ret;
			}
			catch (boost::exception&)
			{
			}
		}
	}
	if (p_be_tolerant)
	{
		return tolerant_parse(p_string);
	}
	return ret;
}

optional<gregorian::date>
DateParser::tolerant_parse(wxString const& p_string) const
{
	JEWEL_LOG_TRACE();
	optional<gregorian::date> const ret =
		tolerant_parse_aux(p_string, m_primary_format);
	return (ret? ret: tolerant_parse_aux(p_string, m_secondary_format));
}

}  // namespace dcm
<commit_msg>Minor simplification and comment edit in date_parser.cpp.<commit_after>/*
 * Copyright 2013 Matthew Harvey
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "date_parser.hpp"
#include "date.hpp"
#include <boost/algorithm/string.hpp>
#include <boost/date_time/gregorian/gregorian.hpp>
#include <boost/lambda/lambda.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/optional.hpp>
#include <jewel/assert.hpp>
#include <jewel/log.hpp>
#include <jewel/optional.hpp>
#include <wx/datetime.h>
#include <wx/intl.h>
#include <wx/string.h>
#include <algorithm>
#include <exception>
#include <iterator>
#include <map>
#include <string>
#include <unordered_set>
#include <vector>

using boost::algorithm::split;
using boost::lexical_cast;
using boost::optional;
using jewel::Log;
using jewel::value;
using std::back_inserter;
using std::begin;
using std::end;
using std::find;
using std::find_first_of;
using std::map;
using std::out_of_range;
using std::string;
using std::transform;
using std::unordered_set;
using std::vector;

namespace gregorian = boost::gregorian;
namespace lambda = boost::lambda;

namespace dcm
{

namespace
{
	// Returns true if and only if c is one of the special characters that
	// can appear in a strftime format string representing a field date or
	// time field.
	// TODO MEDIUM PRIORITY There are characters under the C++11 standard in
	// addition to the below.
	bool is_date_time_format_char(char c)
	{
		static unordered_set<char> const chars
		{	'a', 'A', 'b', 'B', 'c', 'd', 'H', 'I', 'j', 'm', 'M', 'P',
			'S', 'U', 'w', 'W', 'x', 'X', 'y', 'Y', 'Z'
		};
		return chars.find(c) != chars.end();
	}

	enum class DateComponentType: unsigned char
	{
		day,
		month,
		year
	};

	map<char, DateComponentType> const&
	ordinary_date_format_chars_map()
	{
		typedef map<char, DateComponentType> Map;
		static Map ret;
		if (ret.empty())
		{
			ret['d'] = DateComponentType::day;   // day of month (01-31)
			ret['m'] = DateComponentType::month; // month (01-12)
			ret['y'] = DateComponentType::year;  // year in century (00-99)
			ret['Y'] = DateComponentType::year;  // full year
#			ifndef NDEBUG
				for (auto const& elem: ret)
				{
					JEWEL_ASSERT (is_date_time_format_char(elem.first));
				}
#			endif  // NDEBUG
		}
		JEWEL_ASSERT (!ret.empty());
		return ret;
	}

	// Returns true if and only if c is one of the special characters that
	// can appear in a strftime format string representing a field in a tm
	// struct, which is a date field and not a time field, and which
	// represents in numerical format, either the day of the month, the
	// month, or the year.
	bool is_ordinary_date_format_char(char c)
	{
		map<char, DateComponentType> const& map =
			ordinary_date_format_chars_map();
		return map.find(c) != map.end();
	}

	// Examines a strftime-like date format string. If the string is an
	// "ordinary" date format string, returns an optional initialized with the
	// character that separates the fields in that string; otherwise, returns
	// an uninitialized optional. For this purpose, the string is an
	// "ordinary" date format string if and only if: (a) there are exactly
	// three date fields, being a day-of-month, month, and year field
	// (not necessarily in that order), such that each of these fields is
	// represented by a character for which is_ordinary_date_format_char
	// returns true (along with the usual '%' and possible other modifiers
	// between the '%' and the format char); and (b) there is exactly one
	// separator character, which appears twice in the string, separating
	// the first and second fields, and separating the second and third
	// fields.
	optional<char> separating_char(wxString const& str)
	{
		// Analyse the string; if at any point we find anything inconsistent
		// with "ordinary date format", then we return an uninitialized
		// optional.
		JEWEL_LOG_TRACE();
		JEWEL_LOG_VALUE(Log::info, str);
		optional<char> separator;
		wxString::const_iterator it = str.begin();
		wxString::const_iterator const end = str.end();
		size_t num_fields = 0;
		while (it != end)
		{
			if (*it == '%')
			{
				// Skip over any "modifiers"
				while ((it != end) && !is_date_time_format_char(*it)) ++it;

				// If we've reached the end without find a
				// date_time_format_char, then it's not ordinary format.
				if (it == end)
				{
					return optional<char>();
				}
				JEWEL_ASSERT (is_date_time_format_char(*it));

				// If our format char is not ordinary, then it's not
				// ordinary format.
				if (!is_ordinary_date_format_char(*it))
				{
					return optional<char>();
				}
				JEWEL_ASSERT (is_ordinary_date_format_char(*it));
				++num_fields;
				++it;

				// Have we found all the fields? Then we should
				// be at the end.
				if (num_fields == 3)
				{
					JEWEL_ASSERT (separator || (it != end));
					return separator;
				}
				JEWEL_ASSERT (num_fields < 3);

				// To be an ordinary format string,
				// the next character should be a separator, and not another
				// format char, and not the end. If we already
				// have encountered a separator, then this should be an
				// instance of the same separator.
				if
				(	(it == end) ||
					(is_date_time_format_char(*it)) ||
					(separator && (*separator != *it))
				)
				{
					return optional<char>();
				}
				separator = *it;
				++it;
			}
			else
			{
				return optional<char>();
			}
		}
		return separator;
	}

	optional<DateComponentType> maybe_date_component_type(char c)
	{
		optional<DateComponentType> ret;
		switch (c)
		{
		case 'd':
			ret = DateComponentType::day;
			break;
		case 'm':
			ret = DateComponentType::month;
			break;
		case 'y':  // fall through
		case 'Y':
			ret = DateComponentType::year;
			break;
		default:
			;  // do nothing and fall through
		}
		JEWEL_ASSERT
		(	static_cast<bool>(ret) ==
			is_ordinary_date_format_char(c)
		);
		return ret;
	}

	// Should only call if we know the last character of s is such as
	// to return an initialized optional when passed to date_component_type.
	DateComponentType date_component_type(wxString const s)
	{
		JEWEL_ASSERT (!s.IsEmpty());
		return value(maybe_date_component_type(static_cast<char>(s.Last())));
	}

	void fill_missing_components(map<DateComponentType, int>& out)
	{
		switch (out.size())
		{
		case 0:
			out[DateComponentType::day] = today().day();
			goto fill_month;
		case 1:
			if (out.find(DateComponentType::day) == out.end()) return;
			fill_month: out[DateComponentType::month] = today().month();
			goto fill_year;
		case 2:
			if (out.find(DateComponentType::month) == out.end()) return;
			fill_year: out[DateComponentType::year] = today().year();
			// fall through
		default:
			return;
		}
	}

	optional<gregorian::date>
	tolerant_parse_aux
	(	wxString const& p_target,
		wxString const& p_format
	)
	{
		optional<char> const maybe_sep = separating_char(p_format);
		if (!maybe_sep)
		{
			return optional<gregorian::date>();
		}
		JEWEL_ASSERT (maybe_sep);
		char const sep = *maybe_sep;
		JEWEL_LOG_VALUE(Log::trace, sep);
		vector<wxString> target_fields;
		vector<wxString> format_fields;
		split(target_fields, p_target, (lambda::_1 == sep));
		split(format_fields, p_format, (lambda::_1 == sep));
		if ((target_fields.size() > 3) || (format_fields.size() != 3))
		{
			return optional<gregorian::date>();
		}
		JEWEL_ASSERT (target_fields.size() <= 3);
		JEWEL_ASSERT (format_fields.size() == 3);
		vector<DateComponentType> component_types;
		transform
		(	format_fields.begin(),
			format_fields.end(),
			back_inserter(component_types),
			date_component_type
		);
		JEWEL_ASSERT (component_types.size() == 3);
		map<DateComponentType, int> components;

		// bare scope
		{
			vector<wxString>::size_type j = 0;
			vector<wxString>::size_type const szj = target_fields.size();
			for ( ; j != szj; ++j)
			{
				wxString field = target_fields[j];
				if ((*(field.begin()) == '0') && (field.size() > 1))
				{
					field = wxString(field.begin() + 1, field.end());
				}
				int val = -1;
				try
				{
					val = lexical_cast<int>(field);
				}
				catch (boost::bad_lexical_cast&)
				{
					return optional<gregorian::date>();
				}
				JEWEL_ASSERT (val >= 0);
				JEWEL_ASSERT (j < 3);
				DateComponentType component_type = component_types[j];
				if (target_fields.size() == 1)
				{
					component_type = DateComponentType::day;
				}
				else if (target_fields.size() == 2)
				{
					DateComponentType const day_and_month[] =
					{	DateComponentType::day,
						DateComponentType::month
					};
					DateComponentType const first = *find_first_of
					(	component_types.begin(),
						component_types.end(),
						begin(day_and_month),
						end(day_and_month)
					);
					switch (j)
					{
					case 0:
						component_type = first;
						break;
					case 1:
						component_type =
						(	(first == DateComponentType::day)?
							DateComponentType::month:
							DateComponentType::day
						);
						break;
					default:
						JEWEL_HARD_ASSERT (false);
					}
				}
				components[component_type] = val;
			}
		}  // end bare scope

		fill_missing_components(components);
		if (components.size() != 3)
		{
			return optional<gregorian::date>();
		}
		if (components.at(DateComponentType::year) < 100)
		{
			components[DateComponentType::year] += 2000;
		}
		try
		{
			JEWEL_LOG_TRACE();
			JEWEL_ASSERT (components.size() == 3);
			return optional<gregorian::date>
			(	gregorian::date
				(	components.at(DateComponentType::year),
					components.at(DateComponentType::month),
					components.at(DateComponentType::day)
				)
			);
		}
		catch (out_of_range&)
		{
			return optional<gregorian::date>();
		}
	}

}  // end anonymous namespace

DateParser::DateParser
(	wxString const& p_primary_format,
	wxString const& p_secondary_format
):
	m_primary_format(p_primary_format),
	m_secondary_format(p_secondary_format)
{
}

optional<gregorian::date>
DateParser::parse(wxString const& p_string, bool p_be_tolerant) const
{
	optional<gregorian::date> ret;
	wxString::const_iterator parsed_to_position;
	wxDateTime date_wx;

	wxString const* const formats[] =
	{	&m_primary_format,
		&m_secondary_format
	};
	for (wxString const* const format: formats)
	{
		date_wx.ParseFormat
		(	p_string,
			*(format),
			&parsed_to_position
		);
		if (parsed_to_position == p_string.end())
		{
			// Parsing was successful
			int year = date_wx.GetYear();
			if (year < 100) year += 2000;
			int const month = static_cast<int>(date_wx.GetMonth()) + 1;
			int const day = date_wx.GetDay();
			try
			{
				ret = gregorian::date(year, month, day);
				return ret;
			}
			catch (boost::exception&)
			{
			}
		}
	}
	if (p_be_tolerant)
	{
		return tolerant_parse(p_string);
	}
	return ret;
}

optional<gregorian::date>
DateParser::tolerant_parse(wxString const& p_string) const
{
	JEWEL_LOG_TRACE();
	optional<gregorian::date> const ret =
		tolerant_parse_aux(p_string, m_primary_format);
	return (ret? ret: tolerant_parse_aux(p_string, m_secondary_format));
}

}  // namespace dcm
<|endoftext|>


<commit_before>/*************************************************************************/
/*  random_pcg.cpp                                                       */
/*************************************************************************/
/*                       This file is part of:                           */
/*                           GODOT ENGINE                                */
/*                      https://godotengine.org                          */
/*************************************************************************/
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur.                 */
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md).   */
/*                                                                       */
/* 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 "random_pcg.h"

#include "core/os/os.h"

RandomPCG::RandomPCG(uint64_t p_seed, uint64_t p_inc) :
		pcg(),
		current_inc(p_inc) {
	seed(p_seed);
}

void RandomPCG::randomize() {
	seed(OS::get_singleton()->get_ticks_usec() * pcg.state + PCG_DEFAULT_INC_64);
}

double RandomPCG::random(double p_from, double p_to) {
	return randd() * (p_to - p_from) + p_from;
}

float RandomPCG::random(float p_from, float p_to) {
	return randf() * (p_to - p_from) + p_from;
}

int RandomPCG::random(int p_from, int p_to) {
	if (p_from == p_to) {
		return p_from;
	}
	return rand(abs(p_from - p_to) + 1) + MIN(p_from, p_to);
}
<commit_msg>Make randomize() use unix time too<commit_after>/*************************************************************************/
/*  random_pcg.cpp                                                       */
/*************************************************************************/
/*                       This file is part of:                           */
/*                           GODOT ENGINE                                */
/*                      https://godotengine.org                          */
/*************************************************************************/
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur.                 */
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md).   */
/*                                                                       */
/* 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 "random_pcg.h"

#include "core/os/os.h"

RandomPCG::RandomPCG(uint64_t p_seed, uint64_t p_inc) :
		pcg(),
		current_inc(p_inc) {
	seed(p_seed);
}

void RandomPCG::randomize() {
	seed((OS::get_singleton()->get_unix_time() + OS::get_singleton()->get_ticks_usec()) * pcg.state + PCG_DEFAULT_INC_64);
}

double RandomPCG::random(double p_from, double p_to) {
	return randd() * (p_to - p_from) + p_from;
}

float RandomPCG::random(float p_from, float p_to) {
	return randf() * (p_to - p_from) + p_from;
}

int RandomPCG::random(int p_from, int p_to) {
	if (p_from == p_to) {
		return p_from;
	}
	return rand(abs(p_from - p_to) + 1) + MIN(p_from, p_to);
}
<|endoftext|>


<commit_before>#include "style.h"

#include "scene/scene.h"
#include "scene/sceneLayer.h"
#include "scene/light.h"
#include "tile/tile.h"
#include "gl/vboMesh.h"
#include "view/view.h"
#include "csscolorparser.hpp"
#include "geom.h" // for CLAMP

#include <sstream>

namespace Tangram {

std::unordered_map<Style::StyleCacheKey, StyleParamMap> Style::s_styleParamMapCache;
std::mutex Style::s_cacheMutex;

using namespace Tangram;

Style::Style(std::string _name, GLenum _drawMode) : m_name(_name), m_drawMode(_drawMode) {
}

Style::~Style() {
    m_layers.clear();
}

uint32_t Style::parseColorProp(const std::string& _colorPropStr) {
    uint32_t color = 0;

    if (isdigit(_colorPropStr.front())) {
        // try to parse as comma-separated rgba components
        float r, g, b;
        if (sscanf(_colorPropStr.c_str(), "%f,%f,%f", &r, &g, &b) == 3) {
            color = 0xff000000
                | (CLAMP(static_cast<uint32_t>(r * 255.), 0, 255)) << 16
                | (CLAMP(static_cast<uint32_t>(g * 255.), 0, 255)) << 8
                | (CLAMP(static_cast<uint32_t>(b * 255.), 0, 255));

        } else {
            color = 0xffff00ff;
        }
    } else { // parse as css color or #hex-num
        bool isValid;
        color = CSSColorParser::parse(_colorPropStr, isValid).getInt();
        if (!isValid) {
            color = 0xffff00ff;
        }
    }
    return color;
}

void Style::build(const std::vector<std::unique_ptr<Light>>& _lights) {

    constructVertexLayout();
    constructShaderProgram();

    switch (m_lightingType) {
        case LightingType::vertex:
            m_shaderProgram->addSourceBlock("defines", "#define TANGRAM_LIGHTING_VERTEX\n", false);
            break;
        case LightingType::fragment:
            m_shaderProgram->addSourceBlock("defines", "#define TANGRAM_LIGHTING_FRAGMENT\n", false);
            break;
        default:
            break;
    }

    m_material->injectOnProgram(m_shaderProgram);

    for (auto& light : _lights) {
        light->injectOnProgram(m_shaderProgram);
    }

}

void Style::setMaterial(const std::shared_ptr<Material>& _material) {

    m_material = _material;

}

void Style::setLightingType(LightingType _type){

    m_lightingType = _type;

}

void Style::addLayer(std::shared_ptr<SceneLayer> _layer) {

    m_layers.push_back(std::move(_layer));

}

void Style::applyLayerFiltering(const Feature& _feature, const Context& _ctx, StyleCacheKey& _uniqueID,
                                   StyleParamMap& _styleParamMapMix, std::shared_ptr<SceneLayer> _uberLayer) const {

    std::vector<std::shared_ptr<SceneLayer>> sLayers;
    sLayers.reserve(_uberLayer->getSublayers().size() + 1);
    sLayers.push_back(_uberLayer);

    auto sLayerItr = sLayers.begin();

    // A BFS traversal of the SceneLayer graph
    while (sLayerItr != sLayers.end()) {

        auto sceneLyr = *sLayerItr;

        if (sceneLyr->getFilter().eval(_feature, _ctx)) { // filter matches

            _uniqueID.set(sceneLyr->getID());
            {
                std::lock_guard<std::mutex> lock(s_cacheMutex);

                // Get or create cache entry
                auto& entry = s_styleParamMapCache[_uniqueID];

                if (!entry.empty()) {
                    _styleParamMapMix = entry;

                } else {
                    // Update StyleParam with subLayer parameters

                    auto& layerStyleParamMap = sceneLyr->getStyleParamMap();
                    for(auto& styleParam : layerStyleParamMap) {
                        _styleParamMapMix[styleParam.first] = styleParam.second;
                    }
                    entry = _styleParamMapMix;
                }
            }

            // Append sLayers with sublayers of this layer
            auto& ssLayers = sceneLyr->getSublayers();
            sLayerItr = sLayers.insert(sLayers.end(), ssLayers.begin(), ssLayers.end());
        } else {
            sLayerItr++;
        }
    }
}

void Style::addData(TileData& _data, Tile& _tile) {

    std::shared_ptr<VboMesh> mesh(newMesh());

    onBeginBuildTile(*mesh);

    Context ctx;
    ctx["$zoom"] = Value(_tile.getID().z);

    for (auto& layer : _data.layers) {

        // Skip any layers that this style doesn't have a rule for
        auto it = m_layers.begin();
        while (it != m_layers.end() && (*it)->getName() != layer.name) { ++it; }
        if (it == m_layers.end()) { continue; }

        // Loop over all features
        for (auto& feature : layer.features) {

            StyleCacheKey uniqueID(0);
            StyleParamMap styleParamMapMix;
            applyLayerFiltering(feature, ctx, uniqueID, styleParamMapMix, (*it));

            if(uniqueID.any()) { // if a layer matched then uniqueID should be > 0

                switch (feature.geometryType) {
                    case GeometryType::points:
                        // Build points
                        for (auto& point : feature.points) {
                            buildPoint(point, styleParamMapMix, feature.props, *mesh, _tile);
                        }
                        break;
                    case GeometryType::lines:
                        // Build lines
                        for (auto& line : feature.lines) {
                            buildLine(line, styleParamMapMix, feature.props, *mesh, _tile);
                        }
                        break;
                    case GeometryType::polygons:
                        // Build polygons
                        for (auto& polygon : feature.polygons) {
                            buildPolygon(polygon, styleParamMapMix, feature.props, *mesh, _tile);
                        }
                        break;
                    default:
                        break;
                }
            }
        }
    }

    onEndBuildTile(*mesh);

    if (mesh->numVertices() == 0) {
        mesh.reset();
    } else {
        mesh->compileVertexBuffer();

        _tile.addMesh(*this, mesh);
    }
}

void Style::onBeginDrawFrame(const std::shared_ptr<View>& _view, const std::shared_ptr<Scene>& _scene) {

    m_material->setupProgram(m_shaderProgram);

    // Set up lights
    for (const auto& light : _scene->getLights()) {
        light->setupProgram(_view, m_shaderProgram);
    }

    m_shaderProgram->setUniformf("u_zoom", _view->getZoom());

    // default capabilities
    RenderState::blending(GL_FALSE);
    RenderState::depthTest(GL_TRUE);
}

void Style::onBeginBuildTile(VboMesh& _mesh) const {
    // No-op by default
}

void Style::onEndBuildTile(VboMesh& _mesh) const {
    // No-op by default
}

void Style::buildPoint(Point& _point, const StyleParamMap& _styleParamMap, Properties& _props, VboMesh& _mesh, Tile& _tile) const {
    // No-op by default
}

void Style::buildLine(Line& _line, const StyleParamMap& _styleParamMap, Properties& _props, VboMesh& _mesh, Tile& _tile) const {
    // No-op by default
}

void Style::buildPolygon(Polygon& _polygon, const StyleParamMap& _styleParamMap, Properties& _props, VboMesh& _mesh, Tile& _tile) const {
    // No-op by default
}

}
<commit_msg>remove invalid color hint<commit_after>#include "style.h"

#include "scene/scene.h"
#include "scene/sceneLayer.h"
#include "scene/light.h"
#include "tile/tile.h"
#include "gl/vboMesh.h"
#include "view/view.h"
#include "csscolorparser.hpp"
#include "geom.h" // for CLAMP

#include <sstream>

namespace Tangram {

std::unordered_map<Style::StyleCacheKey, StyleParamMap> Style::s_styleParamMapCache;
std::mutex Style::s_cacheMutex;

using namespace Tangram;

Style::Style(std::string _name, GLenum _drawMode) : m_name(_name), m_drawMode(_drawMode) {
}

Style::~Style() {
    m_layers.clear();
}

uint32_t Style::parseColorProp(const std::string& _colorPropStr) {
    uint32_t color = 0;

    if (isdigit(_colorPropStr.front())) {
        // try to parse as comma-separated rgba components
        float r, g, b;
        if (sscanf(_colorPropStr.c_str(), "%f,%f,%f", &r, &g, &b) == 3) {
            color = 0xff000000
                | (CLAMP(static_cast<uint32_t>(r * 255.), 0, 255)) << 16
                | (CLAMP(static_cast<uint32_t>(g * 255.), 0, 255)) << 8
                | (CLAMP(static_cast<uint32_t>(b * 255.), 0, 255));
        }
    } else {
        // parse as css color or #hex-num
        color = CSSColorParser::parse(_colorPropStr).getInt();
    }
    return color;
}

void Style::build(const std::vector<std::unique_ptr<Light>>& _lights) {

    constructVertexLayout();
    constructShaderProgram();

    switch (m_lightingType) {
        case LightingType::vertex:
            m_shaderProgram->addSourceBlock("defines", "#define TANGRAM_LIGHTING_VERTEX\n", false);
            break;
        case LightingType::fragment:
            m_shaderProgram->addSourceBlock("defines", "#define TANGRAM_LIGHTING_FRAGMENT\n", false);
            break;
        default:
            break;
    }

    m_material->injectOnProgram(m_shaderProgram);

    for (auto& light : _lights) {
        light->injectOnProgram(m_shaderProgram);
    }

}

void Style::setMaterial(const std::shared_ptr<Material>& _material) {

    m_material = _material;

}

void Style::setLightingType(LightingType _type){

    m_lightingType = _type;

}

void Style::addLayer(std::shared_ptr<SceneLayer> _layer) {

    m_layers.push_back(std::move(_layer));

}

void Style::applyLayerFiltering(const Feature& _feature, const Context& _ctx, StyleCacheKey& _uniqueID,
                                   StyleParamMap& _styleParamMapMix, std::shared_ptr<SceneLayer> _uberLayer) const {

    std::vector<std::shared_ptr<SceneLayer>> sLayers;
    sLayers.reserve(_uberLayer->getSublayers().size() + 1);
    sLayers.push_back(_uberLayer);

    auto sLayerItr = sLayers.begin();

    // A BFS traversal of the SceneLayer graph
    while (sLayerItr != sLayers.end()) {

        auto sceneLyr = *sLayerItr;

        if (sceneLyr->getFilter().eval(_feature, _ctx)) { // filter matches

            _uniqueID.set(sceneLyr->getID());
            {
                std::lock_guard<std::mutex> lock(s_cacheMutex);

                // Get or create cache entry
                auto& entry = s_styleParamMapCache[_uniqueID];

                if (!entry.empty()) {
                    _styleParamMapMix = entry;

                } else {
                    // Update StyleParam with subLayer parameters

                    auto& layerStyleParamMap = sceneLyr->getStyleParamMap();
                    for(auto& styleParam : layerStyleParamMap) {
                        _styleParamMapMix[styleParam.first] = styleParam.second;
                    }
                    entry = _styleParamMapMix;
                }
            }

            // Append sLayers with sublayers of this layer
            auto& ssLayers = sceneLyr->getSublayers();
            sLayerItr = sLayers.insert(sLayers.end(), ssLayers.begin(), ssLayers.end());
        } else {
            sLayerItr++;
        }
    }
}

void Style::addData(TileData& _data, Tile& _tile) {

    std::shared_ptr<VboMesh> mesh(newMesh());

    onBeginBuildTile(*mesh);

    Context ctx;
    ctx["$zoom"] = Value(_tile.getID().z);

    for (auto& layer : _data.layers) {

        // Skip any layers that this style doesn't have a rule for
        auto it = m_layers.begin();
        while (it != m_layers.end() && (*it)->getName() != layer.name) { ++it; }
        if (it == m_layers.end()) { continue; }

        // Loop over all features
        for (auto& feature : layer.features) {

            StyleCacheKey uniqueID(0);
            StyleParamMap styleParamMapMix;
            applyLayerFiltering(feature, ctx, uniqueID, styleParamMapMix, (*it));

            if(uniqueID.any()) { // if a layer matched then uniqueID should be > 0

                switch (feature.geometryType) {
                    case GeometryType::points:
                        // Build points
                        for (auto& point : feature.points) {
                            buildPoint(point, styleParamMapMix, feature.props, *mesh, _tile);
                        }
                        break;
                    case GeometryType::lines:
                        // Build lines
                        for (auto& line : feature.lines) {
                            buildLine(line, styleParamMapMix, feature.props, *mesh, _tile);
                        }
                        break;
                    case GeometryType::polygons:
                        // Build polygons
                        for (auto& polygon : feature.polygons) {
                            buildPolygon(polygon, styleParamMapMix, feature.props, *mesh, _tile);
                        }
                        break;
                    default:
                        break;
                }
            }
        }
    }

    onEndBuildTile(*mesh);

    if (mesh->numVertices() == 0) {
        mesh.reset();
    } else {
        mesh->compileVertexBuffer();

        _tile.addMesh(*this, mesh);
    }
}

void Style::onBeginDrawFrame(const std::shared_ptr<View>& _view, const std::shared_ptr<Scene>& _scene) {

    m_material->setupProgram(m_shaderProgram);

    // Set up lights
    for (const auto& light : _scene->getLights()) {
        light->setupProgram(_view, m_shaderProgram);
    }

    m_shaderProgram->setUniformf("u_zoom", _view->getZoom());

    // default capabilities
    RenderState::blending(GL_FALSE);
    RenderState::depthTest(GL_TRUE);
}

void Style::onBeginBuildTile(VboMesh& _mesh) const {
    // No-op by default
}

void Style::onEndBuildTile(VboMesh& _mesh) const {
    // No-op by default
}

void Style::buildPoint(Point& _point, const StyleParamMap& _styleParamMap, Properties& _props, VboMesh& _mesh, Tile& _tile) const {
    // No-op by default
}

void Style::buildLine(Line& _line, const StyleParamMap& _styleParamMap, Properties& _props, VboMesh& _mesh, Tile& _tile) const {
    // No-op by default
}

void Style::buildPolygon(Polygon& _polygon, const StyleParamMap& _styleParamMap, Properties& _props, VboMesh& _mesh, Tile& _tile) const {
    // No-op by default
}

}
<|endoftext|>


<commit_before>//
//  yas_worker.cpp
//

#include "yas_worker.h"

#include <atomic>
#include <thread>

#include "yas_stl_utils.h"

using namespace yas;

struct workable::resource {
    std::vector<task_f> tasks;
    std::atomic<bool> is_continue{true};

    resource(std::vector<task_f> &&tasks) : tasks(std::move(tasks)) {
    }
};

worker::worker(std::chrono::milliseconds const &duration) : _sleep_duration(duration) {
}

worker::~worker() {
    this->stop();
}

void worker::add_task(uint32_t const priority, task_f &&task) {
    if (this->_resource) {
        throw std::runtime_error("worker add_task() - already started.");
    }

    this->_tasks.emplace(priority, std::move(task));
}

void worker::start() {
    if (this->_resource) {
        throw std::runtime_error("worker start() - already started.");
    }

    if (this->_tasks.size() == 0) {
        throw std::runtime_error("worker start() - task is empty.");
    }

    auto tasks = to_vector<task_f>(this->_tasks, [](auto const &pair) { return pair.second; });

    this->_resource = std::make_shared<resource>(std::move(tasks));

    std::thread thread{[resource = this->_resource, sleep_duration = this->_sleep_duration] {
        while (resource->is_continue) {
            while (resource->is_continue) {
                bool processed = false;
                std::vector<std::size_t> completed;
                std::size_t idx = 0;

                for (auto const &task : resource->tasks) {
                    auto const result = task();

                    if (result == task_result::processed) {
                        processed = true;
                        std::this_thread::yield();
                        break;
                    } else if (result == task_result::completed) {
                        completed.emplace_back(idx);
                    }

                    ++idx;
                }

                if (completed.size() > 0) {
                    std::reverse(completed.begin(), completed.end());
                    for (auto const &idx : completed) {
                        erase_at(resource->tasks, idx);
                    }
                }

                if (!processed) {
                    break;
                }
            }
            std::this_thread::sleep_for(sleep_duration);
        }
    }};

    thread.detach();
}

void worker::stop() {
    if (this->_resource) {
        this->_resource->is_continue = false;
        this->_resource = nullptr;
    }
}

worker_ptr worker::make_shared() {
    return worker::make_shared(std::chrono::milliseconds{10});
}

worker_ptr worker::make_shared(std::chrono::milliseconds const &duration) {
    return worker_ptr(new worker{duration});
}
<commit_msg>separate process<commit_after>//
//  yas_worker.cpp
//

#include "yas_worker.h"

#include <atomic>
#include <thread>

#include "yas_stl_utils.h"

using namespace yas;

namespace yas::worker_utils {
bool process(std::vector<workable::task_f> &tasks) {
    bool processed = false;
    std::vector<std::size_t> completed;
    std::size_t idx = 0;

    for (auto const &task : tasks) {
        auto const result = task();

        if (result == workable::task_result::processed) {
            processed = true;
            std::this_thread::yield();
            break;
        } else if (result == workable::task_result::completed) {
            completed.emplace_back(idx);
        }

        ++idx;
    }

    if (completed.size() > 0) {
        std::reverse(completed.begin(), completed.end());
        for (auto const &idx : completed) {
            erase_at(tasks, idx);
        }
    }

    return processed;
}
}  // namespace yas::worker_utils

struct workable::resource {
    std::vector<task_f> tasks;
    std::atomic<bool> is_continue{true};

    resource(std::vector<task_f> &&tasks) : tasks(std::move(tasks)) {
    }
};

worker::worker(std::chrono::milliseconds const &duration) : _sleep_duration(duration) {
}

worker::~worker() {
    this->stop();
}

void worker::add_task(uint32_t const priority, task_f &&task) {
    if (this->_resource) {
        throw std::runtime_error("worker add_task() - already started.");
    }

    this->_tasks.emplace(priority, std::move(task));
}

void worker::start() {
    if (this->_resource) {
        throw std::runtime_error("worker start() - already started.");
    }

    if (this->_tasks.size() == 0) {
        throw std::runtime_error("worker start() - task is empty.");
    }

    auto tasks = to_vector<task_f>(this->_tasks, [](auto const &pair) { return pair.second; });

    this->_resource = std::make_shared<resource>(std::move(tasks));

    std::thread thread{[resource = this->_resource, sleep_duration = this->_sleep_duration] {
        while (resource->is_continue) {
            while (resource->is_continue) {
                if (!worker_utils::process(resource->tasks)) {
                    break;
                }
            }
            std::this_thread::sleep_for(sleep_duration);
        }
    }};

    thread.detach();
}

void worker::stop() {
    if (this->_resource) {
        this->_resource->is_continue = false;
        this->_resource = nullptr;
    }
}

worker_ptr worker::make_shared() {
    return worker::make_shared(std::chrono::milliseconds{10});
}

worker_ptr worker::make_shared(std::chrono::milliseconds const &duration) {
    return worker_ptr(new worker{duration});
}
<|endoftext|>


<commit_before>#include <iostream>
#include <string>
#include <cppdom/cppdom.h>

bool configureInput( const std::string& filename )
{
   cppdom::XMLContextPtr ctx( new cppdom::XMLContext );
   cppdom::XMLDocument doc( ctx );

   // load a xml document from a file
   try
   {
      doc.loadFile( filename );
   }
   catch (cppdom::XMLError e)
   {
      std::cerr << "Error: " << e.getString() << std::endl;
      if (e.getInfo().size())
      {
         std::cerr << "File: " << e.getInfo() << std::endl;
      }
      if (e.getError() != cppdom::xml_filename_invalid &&
          e.getError() != cppdom::xml_file_access)
      {
/*
         e.show_error( ctx );
         e.show_line( ctx, filename );
*/
         std::cout << "Error: (need to impl the show functions)" << std::endl;
      }
      return false;
   }

   std::cerr << "succesfully loaded " << filename << std::endl;

   cppdom::XMLNodeList nl = doc.getChild( "gameinput" )->getChildren();
   cppdom::XMLNodeListIterator it = nl.begin();
   while (it != nl.end())
   {
      std::cerr << "in name: " << (*it)->getName() << std::endl;
      try
      {
         cppdom::XMLAttributes& attr = (*it)->getAttrMap();
         std::cout << "attr: " << attr.get( "action" ) << "\n" << std::flush;
         std::cout << "attr: " << attr.get( "device" ) << "\n" << std::flush;
         std::cout << "attr: " << attr.get( "input" ) << "\n" << std::flush;

         cppdom::XMLNode* parent = (*it)->getParent();
         assert(parent != NULL && parent->getName() == std::string("gameinput"));
      }
      catch (cppdom::XMLError e)
      {
         std::cerr << "Error: " << e.getString() << std::endl;
         it++;
         continue;
      }
      it++;
   }

   return true;
}

/** Main function */
int main()
{
   // Just call single function to load and process input file
   configureInput( "game.xml" );
   return 1;
}
<commit_msg>compiles under win32 now, added assert.h<commit_after>#include <assert.h>
#include <iostream>
#include <string>
#include <cppdom/cppdom.h>

bool configureInput( const std::string& filename )
{
   cppdom::XMLContextPtr ctx( new cppdom::XMLContext );
   cppdom::XMLDocument doc( ctx );

   // load a xml document from a file
   try
   {
      doc.loadFile( filename );
   }
   catch (cppdom::XMLError e)
   {
      std::cerr << "Error: " << e.getString() << std::endl;
      if (e.getInfo().size())
      {
         std::cerr << "File: " << e.getInfo() << std::endl;
      }
      if (e.getError() != cppdom::xml_filename_invalid &&
          e.getError() != cppdom::xml_file_access)
      {
/*
         e.show_error( ctx );
         e.show_line( ctx, filename );
*/
         std::cout << "Error: (need to impl the show functions)" << std::endl;
      }
      return false;
   }

   std::cerr << "succesfully loaded " << filename << std::endl;

   cppdom::XMLNodeList nl = doc.getChild( "gameinput" )->getChildren();
   cppdom::XMLNodeListIterator it = nl.begin();
   while (it != nl.end())
   {
      std::cerr << "in name: " << (*it)->getName() << std::endl;
      try
      {
         cppdom::XMLAttributes& attr = (*it)->getAttrMap();
         std::cout << "attr: " << attr.get( "action" ) << "\n" << std::flush;
         std::cout << "attr: " << attr.get( "device" ) << "\n" << std::flush;
         std::cout << "attr: " << attr.get( "input" ) << "\n" << std::flush;

         cppdom::XMLNode* parent = (*it)->getParent();
         assert(parent != NULL && parent->getName() == std::string("gameinput"));
      }
      catch (cppdom::XMLError e)
      {
         std::cerr << "Error: " << e.getString() << std::endl;
         it++;
         continue;
      }
      it++;
   }

   return true;
}

/** Main function */
int main()
{
   // Just call single function to load and process input file
   configureInput( "game.xml" );
   return 1;
}
<|endoftext|>