<commit_before>// Copyright 2010, Camilo Aguilar. Cloudescape, LLC.
#include "bindings.h"

/* size of the event structure, not counting name */
#define EVENT_SIZE  (sizeof (struct inotify_event))

/* reasonable guess as to size of 1024 events */
#define BUF_LEN        (1024 * (EVENT_SIZE + 16))

namespace NodeInotify {
    static Persistent<String> path_sym;
    static Persistent<String> watch_for_sym;
    static Persistent<String> callback_sym;
    static Persistent<String> persistent_sym;
    static Persistent<String> watch_sym;
    static Persistent<String> mask_sym;
    static Persistent<String> cookie_sym;
    static Persistent<String> name_sym;

    void Inotify::Initialize(Handle<Object> target) {
        Local<FunctionTemplate> t = FunctionTemplate::New(Inotify::New);

        t->Inherit(EventEmitter::constructor_template);
        t->InstanceTemplate()->SetInternalFieldCount(1);

        NODE_SET_PROTOTYPE_METHOD(t, "addWatch",
                                      Inotify::AddWatch);
        NODE_SET_PROTOTYPE_METHOD(t, "removeWatch",
                                      Inotify::RemoveWatch);
        NODE_SET_PROTOTYPE_METHOD(t, "close",
                                      Inotify::Close);

        //Constants initialization
        NODE_DEFINE_CONSTANT(t, IN_ACCESS); //File was accessed (read)
        NODE_DEFINE_CONSTANT(t, IN_ATTRIB); //Metadata changed, e.g., permissions, timestamps,
                                                      //extended attributes, link count (since Linux 2.6.25),
                                                      //UID, GID, etc.
        NODE_DEFINE_CONSTANT(t, IN_CLOSE_WRITE); //File opened for writing was closed
        NODE_DEFINE_CONSTANT(t, IN_CLOSE_NOWRITE); //File not opened for writing was closed
        NODE_DEFINE_CONSTANT(t, IN_CREATE); //File/directory created in watched directory
        NODE_DEFINE_CONSTANT(t, IN_DELETE); //File/directory deleted from watched directory
        NODE_DEFINE_CONSTANT(t, IN_DELETE_SELF); //Watched file/directory was itself deleted
        NODE_DEFINE_CONSTANT(t, IN_MODIFY); //File was modified
        NODE_DEFINE_CONSTANT(t, IN_MOVE_SELF); //Watched file/directory was itself moved
        NODE_DEFINE_CONSTANT(t, IN_MOVED_FROM); //File moved out of watched directory
        NODE_DEFINE_CONSTANT(t, IN_MOVED_TO); //File moved into watched directory
        NODE_DEFINE_CONSTANT(t, IN_OPEN); //File was opened
        NODE_DEFINE_CONSTANT(t, IN_IGNORED); // Watch was removed explicitly (inotify.watch.rm) or
                                                       //automatically (file was deleted, or file system was
                                                        //unmounted)
        NODE_DEFINE_CONSTANT(t, IN_ISDIR); //Subject of this event is a directory
        NODE_DEFINE_CONSTANT(t, IN_Q_OVERFLOW); //Event queue overflowed (wd is -1 for this event)
        NODE_DEFINE_CONSTANT(t, IN_UNMOUNT); //File system containing watched object was unmounted
        NODE_DEFINE_CONSTANT(t, IN_ALL_EVENTS);

        NODE_DEFINE_CONSTANT(t, IN_ONLYDIR); // Only watch the path if it is a directory.
        NODE_DEFINE_CONSTANT(t, IN_DONT_FOLLOW); // Do not follow a sym link
        NODE_DEFINE_CONSTANT(t, IN_ONESHOT); // Only send event once
        NODE_DEFINE_CONSTANT(t, IN_MASK_ADD); //Add (OR) events to watch mask for this pathname if it
                                                        //already exists (instead of replacing mask).

        NODE_DEFINE_CONSTANT(t, IN_CLOSE); // (IN_CLOSE_WRITE | IN_CLOSE_NOWRITE)  Close
        NODE_DEFINE_CONSTANT(t, IN_MOVE);  //  (IN_MOVED_FROM | IN_MOVED_TO)  Moves

        path_sym        = NODE_PSYMBOL("path");
        watch_for_sym   = NODE_PSYMBOL("watch_for");
        callback_sym    = NODE_PSYMBOL("callback");
        persistent_sym  = NODE_PSYMBOL("persistent");

        watch_sym       = NODE_PSYMBOL("watch");
        mask_sym        = NODE_PSYMBOL("mask");
        cookie_sym      = NODE_PSYMBOL("cookie");
        name_sym        = NODE_PSYMBOL("name");

        Local<ObjectTemplate> object_tmpl = t->InstanceTemplate();
        object_tmpl->SetAccessor(persistent_sym, Inotify::GetPersistent);

        t->SetClassName(String::NewSymbol("Inotify"));
        target->Set(String::NewSymbol("Inotify"), t->GetFunction());
    }

    Inotify::Inotify() : EventEmitter() {
        ev_init(&read_watcher, Inotify::Callback);
        read_watcher.data = this;  //preserving my reference to use it inside Inotify::Callback
        persistent = true;
    }

    Inotify::Inotify(bool nonpersistent) : EventEmitter() {
        ev_init(&read_watcher, Inotify::Callback);
        read_watcher.data = this;  //preserving my reference to use it inside Inotify::Callback
        persistent = nonpersistent;
    }

    Inotify::~Inotify() {
        if(!persistent) {
            ev_ref(EV_DEFAULT_UC);
        }
        ev_io_stop(EV_DEFAULT_UC_ &read_watcher);
        assert(!ev_is_active(&read_watcher));
        assert(!ev_is_pending(&read_watcher));
    }

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

        Inotify *inotify = NULL;
        if(args.Length() == 1 && args[0]->IsBoolean()) {
            inotify = new Inotify(args[0]->IsTrue());
        } else {
            inotify = new Inotify();
        }

 	inotify->fd = inotify_init();

        if(inotify->fd == -1) {
            ThrowException(String::New(strerror(errno)));
            return Null();
        }

	int flags = fcntl(inotify->fd, F_GETFL);
	if(flags == -1) {
	    flags = 0;
	}

	fcntl(inotify->fd, F_SETFL, flags | O_NONBLOCK);

        ev_io_set(&inotify->read_watcher, inotify->fd, EV_READ);
        ev_io_start(EV_DEFAULT_UC_ &inotify->read_watcher);

        Local<Object> obj = args.This();
        inotify->Wrap(obj);

        if(!inotify->persistent) {
            ev_unref(EV_DEFAULT_UC);
        }
        /*Increment object references to avoid be GCed while
         I'm waiting for inotify events in th ev_pool.
         Also, the object is not weak anymore */
        inotify->Ref();

        return scope.Close(obj);
    }

    Handle<Value> Inotify::AddWatch(const Arguments& args) {
        HandleScope scope;
        uint32_t mask = 0;
        int watch_descriptor = 0;

        if(args.Length() < 1 || !args[0]->IsObject()) {
            return ThrowException(Exception::TypeError(
            String::New("You must specify an object as first argument")));
        }

        Local<Object> args_ = args[0]->ToObject();

        if(!args_->Has(path_sym)) {
            return ThrowException(Exception::TypeError(
            String::New("You must specify a path to watch for events")));
        }

        if(!args_->Has(callback_sym) ||
            !args_->Get(callback_sym)->IsFunction()) {
            return ThrowException(Exception::TypeError(
            String::New("You must specify a callback function")));
        }

        if(!args_->Has(watch_for_sym)) {
            mask |= IN_ALL_EVENTS;
        } else {
            if(!args_->Get(watch_for_sym)->IsInt32()) {
                return ThrowException(Exception::TypeError(
                String::New("You must specify OR'ed set of events")));
            }
            mask |= args_->Get(watch_for_sym)->Int32Value();
            if(mask == 0) {
                return ThrowException(Exception::TypeError(
                String::New("You must specify OR'ed set of events")));
            }
       }

        String::Utf8Value path(args_->Get(path_sym));

        Inotify *inotify = ObjectWrap::Unwrap<Inotify>(args.This());

        /* add watch */
        watch_descriptor = inotify_add_watch(inotify->fd, (const char *) *path, mask);

        Local<Integer> descriptor = Integer::New(watch_descriptor);

        //Local<Function> callback = Local<Function>::Cast(args_->Get(callback_sym));
        inotify->handle_->Set(descriptor, args_->Get(callback_sym));

        return scope.Close(descriptor);
    }

    Handle<Value> Inotify::RemoveWatch(const Arguments& args) {
        HandleScope scope;
        uint32_t watch = 0;
        int ret = -1;

        if(args.Length() == 0 || !args[0]->IsInt32()) {
            return ThrowException(Exception::TypeError(
            String::New("You must specify a valid watcher descriptor as argument")));
        }
        watch = args[0]->Int32Value();

        Inotify *inotify = ObjectWrap::Unwrap<Inotify>(args.This());

        ret = inotify_rm_watch(inotify->fd, watch);
        if(ret == -1) {
            ThrowException(String::New(strerror(errno)));
            return False();
        }

        return True();
    }

    Handle<Value> Inotify::Close(const Arguments& args) {
        HandleScope scope;
        int ret = -1;

        Inotify *inotify = ObjectWrap::Unwrap<Inotify>(args.This());
        ret = close(inotify->fd);

        if(ret == -1) {
            ThrowException(String::New(strerror(errno)));
            return False();
        }

        if(!inotify->persistent) {
            ev_ref(EV_DEFAULT_UC);
        }

        ev_io_stop(EV_DEFAULT_UC_ &inotify->read_watcher);

        /*Eliminating reference created inside of Inotify::New.
        The object is also weak again.
        Now v8 can do its stuff and GC the object.
        */
        inotify->Unref();

        return True();
    }

    void Inotify::Callback(EV_P_ ev_io *watcher, int revents) {
        HandleScope scope;

        Inotify *inotify = static_cast<Inotify*>(watcher->data);
        assert(watcher == &inotify->read_watcher);

        char buffer[BUF_LEN];

        //int length = read(inotify->fd, buffer, BUF_LEN);

        Local<Value> argv[1];
        TryCatch try_catch;

        int i = 0;
        while (i < read(inotify->fd, buffer, BUF_LEN)) {
            struct inotify_event *event;
            event = (struct inotify_event *) &buffer[i];

            Local<Object> obj = Object::New();
            obj->Set(watch_sym, Integer::New(event->wd));
            obj->Set(mask_sym, Integer::New(event->mask));
            obj->Set(cookie_sym, Integer::New(event->cookie));

            if(event->len) {
                obj->Set(name_sym, String::New(event->name));
            }
            argv[0] = obj;

            inotify->Ref();
            Local<Value> callback_ = inotify->handle_->Get(Integer::New(event->wd));
            Local<Function> callback = Local<Function>::Cast(callback_);

            callback->Call(inotify->handle_, 1, argv);
            inotify->Unref();

            if(event->mask & IN_IGNORED) {
                //deleting callback because the watch was removed
                Local<Value> wd = Integer::New(event->wd);
                inotify->handle_->Delete(wd->ToString());
            }

            if (try_catch.HasCaught()) {
                FatalException(try_catch);
            }

            i += EVENT_SIZE + event->len;
        }
    }

    Handle<Value> Inotify::GetPersistent(Local<String> property,
                                        const AccessorInfo& info) {
        Inotify *inotify = ObjectWrap::Unwrap<Inotify>(info.This());

        return inotify->persistent ? True() : False();
     }
}//namespace NodeInotify

<commit_msg>fix read loop to pick up all events instead of just one per buffer<commit_after>// Copyright 2010, Camilo Aguilar. Cloudescape, LLC.
#include "bindings.h"

/* size of the event structure, not counting name */
#define EVENT_SIZE  (sizeof (struct inotify_event))

/* reasonable guess as to size of 1024 events */
#define BUF_LEN        (1024 * (EVENT_SIZE + 16))

namespace NodeInotify {
    static Persistent<String> path_sym;
    static Persistent<String> watch_for_sym;
    static Persistent<String> callback_sym;
    static Persistent<String> persistent_sym;
    static Persistent<String> watch_sym;
    static Persistent<String> mask_sym;
    static Persistent<String> cookie_sym;
    static Persistent<String> name_sym;

    void Inotify::Initialize(Handle<Object> target) {
        Local<FunctionTemplate> t = FunctionTemplate::New(Inotify::New);

        t->Inherit(EventEmitter::constructor_template);
        t->InstanceTemplate()->SetInternalFieldCount(1);

        NODE_SET_PROTOTYPE_METHOD(t, "addWatch",
                                      Inotify::AddWatch);
        NODE_SET_PROTOTYPE_METHOD(t, "removeWatch",
                                      Inotify::RemoveWatch);
        NODE_SET_PROTOTYPE_METHOD(t, "close",
                                      Inotify::Close);

        //Constants initialization
        NODE_DEFINE_CONSTANT(t, IN_ACCESS); //File was accessed (read)
        NODE_DEFINE_CONSTANT(t, IN_ATTRIB); //Metadata changed, e.g., permissions, timestamps,
                                                      //extended attributes, link count (since Linux 2.6.25),
                                                      //UID, GID, etc.
        NODE_DEFINE_CONSTANT(t, IN_CLOSE_WRITE); //File opened for writing was closed
        NODE_DEFINE_CONSTANT(t, IN_CLOSE_NOWRITE); //File not opened for writing was closed
        NODE_DEFINE_CONSTANT(t, IN_CREATE); //File/directory created in watched directory
        NODE_DEFINE_CONSTANT(t, IN_DELETE); //File/directory deleted from watched directory
        NODE_DEFINE_CONSTANT(t, IN_DELETE_SELF); //Watched file/directory was itself deleted
        NODE_DEFINE_CONSTANT(t, IN_MODIFY); //File was modified
        NODE_DEFINE_CONSTANT(t, IN_MOVE_SELF); //Watched file/directory was itself moved
        NODE_DEFINE_CONSTANT(t, IN_MOVED_FROM); //File moved out of watched directory
        NODE_DEFINE_CONSTANT(t, IN_MOVED_TO); //File moved into watched directory
        NODE_DEFINE_CONSTANT(t, IN_OPEN); //File was opened
        NODE_DEFINE_CONSTANT(t, IN_IGNORED); // Watch was removed explicitly (inotify.watch.rm) or
                                                       //automatically (file was deleted, or file system was
                                                        //unmounted)
        NODE_DEFINE_CONSTANT(t, IN_ISDIR); //Subject of this event is a directory
        NODE_DEFINE_CONSTANT(t, IN_Q_OVERFLOW); //Event queue overflowed (wd is -1 for this event)
        NODE_DEFINE_CONSTANT(t, IN_UNMOUNT); //File system containing watched object was unmounted
        NODE_DEFINE_CONSTANT(t, IN_ALL_EVENTS);

        NODE_DEFINE_CONSTANT(t, IN_ONLYDIR); // Only watch the path if it is a directory.
        NODE_DEFINE_CONSTANT(t, IN_DONT_FOLLOW); // Do not follow a sym link
        NODE_DEFINE_CONSTANT(t, IN_ONESHOT); // Only send event once
        NODE_DEFINE_CONSTANT(t, IN_MASK_ADD); //Add (OR) events to watch mask for this pathname if it
                                                        //already exists (instead of replacing mask).

        NODE_DEFINE_CONSTANT(t, IN_CLOSE); // (IN_CLOSE_WRITE | IN_CLOSE_NOWRITE)  Close
        NODE_DEFINE_CONSTANT(t, IN_MOVE);  //  (IN_MOVED_FROM | IN_MOVED_TO)  Moves

        path_sym        = NODE_PSYMBOL("path");
        watch_for_sym   = NODE_PSYMBOL("watch_for");
        callback_sym    = NODE_PSYMBOL("callback");
        persistent_sym  = NODE_PSYMBOL("persistent");

        watch_sym       = NODE_PSYMBOL("watch");
        mask_sym        = NODE_PSYMBOL("mask");
        cookie_sym      = NODE_PSYMBOL("cookie");
        name_sym        = NODE_PSYMBOL("name");

        Local<ObjectTemplate> object_tmpl = t->InstanceTemplate();
        object_tmpl->SetAccessor(persistent_sym, Inotify::GetPersistent);

        t->SetClassName(String::NewSymbol("Inotify"));
        target->Set(String::NewSymbol("Inotify"), t->GetFunction());
    }

    Inotify::Inotify() : EventEmitter() {
        ev_init(&read_watcher, Inotify::Callback);
        read_watcher.data = this;  //preserving my reference to use it inside Inotify::Callback
        persistent = true;
    }

    Inotify::Inotify(bool nonpersistent) : EventEmitter() {
        ev_init(&read_watcher, Inotify::Callback);
        read_watcher.data = this;  //preserving my reference to use it inside Inotify::Callback
        persistent = nonpersistent;
    }

    Inotify::~Inotify() {
        if(!persistent) {
            ev_ref(EV_DEFAULT_UC);
        }
        ev_io_stop(EV_DEFAULT_UC_ &read_watcher);
        assert(!ev_is_active(&read_watcher));
        assert(!ev_is_pending(&read_watcher));
    }

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

        Inotify *inotify = NULL;
        if(args.Length() == 1 && args[0]->IsBoolean()) {
            inotify = new Inotify(args[0]->IsTrue());
        } else {
            inotify = new Inotify();
        }

 	inotify->fd = inotify_init();

        if(inotify->fd == -1) {
            ThrowException(String::New(strerror(errno)));
            return Null();
        }

	int flags = fcntl(inotify->fd, F_GETFL);
	if(flags == -1) {
	    flags = 0;
	}

	fcntl(inotify->fd, F_SETFL, flags | O_NONBLOCK);

        ev_io_set(&inotify->read_watcher, inotify->fd, EV_READ);
        ev_io_start(EV_DEFAULT_UC_ &inotify->read_watcher);

        Local<Object> obj = args.This();
        inotify->Wrap(obj);

        if(!inotify->persistent) {
            ev_unref(EV_DEFAULT_UC);
        }
        /*Increment object references to avoid be GCed while
         I'm waiting for inotify events in th ev_pool.
         Also, the object is not weak anymore */
        inotify->Ref();

        return scope.Close(obj);
    }

    Handle<Value> Inotify::AddWatch(const Arguments& args) {
        HandleScope scope;
        uint32_t mask = 0;
        int watch_descriptor = 0;

        if(args.Length() < 1 || !args[0]->IsObject()) {
            return ThrowException(Exception::TypeError(
            String::New("You must specify an object as first argument")));
        }

        Local<Object> args_ = args[0]->ToObject();

        if(!args_->Has(path_sym)) {
            return ThrowException(Exception::TypeError(
            String::New("You must specify a path to watch for events")));
        }

        if(!args_->Has(callback_sym) ||
            !args_->Get(callback_sym)->IsFunction()) {
            return ThrowException(Exception::TypeError(
            String::New("You must specify a callback function")));
        }

        if(!args_->Has(watch_for_sym)) {
            mask |= IN_ALL_EVENTS;
        } else {
            if(!args_->Get(watch_for_sym)->IsInt32()) {
                return ThrowException(Exception::TypeError(
                String::New("You must specify OR'ed set of events")));
            }
            mask |= args_->Get(watch_for_sym)->Int32Value();
            if(mask == 0) {
                return ThrowException(Exception::TypeError(
                String::New("You must specify OR'ed set of events")));
            }
       }

        String::Utf8Value path(args_->Get(path_sym));

        Inotify *inotify = ObjectWrap::Unwrap<Inotify>(args.This());

        /* add watch */
        watch_descriptor = inotify_add_watch(inotify->fd, (const char *) *path, mask);

        Local<Integer> descriptor = Integer::New(watch_descriptor);

        //Local<Function> callback = Local<Function>::Cast(args_->Get(callback_sym));
        inotify->handle_->Set(descriptor, args_->Get(callback_sym));

        return scope.Close(descriptor);
    }

    Handle<Value> Inotify::RemoveWatch(const Arguments& args) {
        HandleScope scope;
        uint32_t watch = 0;
        int ret = -1;

        if(args.Length() == 0 || !args[0]->IsInt32()) {
            return ThrowException(Exception::TypeError(
            String::New("You must specify a valid watcher descriptor as argument")));
        }
        watch = args[0]->Int32Value();

        Inotify *inotify = ObjectWrap::Unwrap<Inotify>(args.This());

        ret = inotify_rm_watch(inotify->fd, watch);
        if(ret == -1) {
            ThrowException(String::New(strerror(errno)));
            return False();
        }

        return True();
    }

    Handle<Value> Inotify::Close(const Arguments& args) {
        HandleScope scope;
        int ret = -1;

        Inotify *inotify = ObjectWrap::Unwrap<Inotify>(args.This());
        ret = close(inotify->fd);

        if(ret == -1) {
            ThrowException(String::New(strerror(errno)));
            return False();
        }

        if(!inotify->persistent) {
            ev_ref(EV_DEFAULT_UC);
        }

        ev_io_stop(EV_DEFAULT_UC_ &inotify->read_watcher);

        /*Eliminating reference created inside of Inotify::New.
        The object is also weak again.
        Now v8 can do its stuff and GC the object.
        */
        inotify->Unref();

        return True();
    }

    void Inotify::Callback(EV_P_ ev_io *watcher, int revents) {
        HandleScope scope;

        Inotify *inotify = static_cast<Inotify*>(watcher->data);
        assert(watcher == &inotify->read_watcher);

        char buffer[BUF_LEN];

        //int length = read(inotify->fd, buffer, BUF_LEN);

        Local<Value> argv[1];
        TryCatch try_catch;

        int i = 0;
	int sz = 0;
        while ((sz = read(inotify->fd, buffer, BUF_LEN)) > 0) {
	  struct inotify_event *event;
	  for (i = 0; i <= (sz-EVENT_SIZE); i += (EVENT_SIZE + event->len)) {
            event = (struct inotify_event *) &buffer[i];

            Local<Object> obj = Object::New();
            obj->Set(watch_sym, Integer::New(event->wd));
            obj->Set(mask_sym, Integer::New(event->mask));
            obj->Set(cookie_sym, Integer::New(event->cookie));

            if(event->len) {
                obj->Set(name_sym, String::New(event->name));
            }
            argv[0] = obj;

            inotify->Ref();
            Local<Value> callback_ = inotify->handle_->Get(Integer::New(event->wd));
            Local<Function> callback = Local<Function>::Cast(callback_);

            callback->Call(inotify->handle_, 1, argv);
            inotify->Unref();

            if(event->mask & IN_IGNORED) {
                //deleting callback because the watch was removed
                Local<Value> wd = Integer::New(event->wd);
                inotify->handle_->Delete(wd->ToString());
            }

            if (try_catch.HasCaught()) {
                FatalException(try_catch);
            }
	  }
	}
    }

    Handle<Value> Inotify::GetPersistent(Local<String> property,
                                        const AccessorInfo& info) {
        Inotify *inotify = ObjectWrap::Unwrap<Inotify>(info.This());

        return inotify->persistent ? True() : False();
     }
}//namespace NodeInotify

<|endoftext|>


<commit_before>/*
 * Copyright 2015 Cloudius Systems
 */

#ifndef DB_SERIALIZER_HH_
#define DB_SERIALIZER_HH_

#include "utils/data_input.hh"
#include "utils/data_output.hh"
#include "bytes.hh"
#include "mutation.hh"
#include "keys.hh"
#include "database_fwd.hh"

namespace db {
/**
 * Serialization objects for various types and using "internal" format. (Not CQL, origin whatnot).
 * The design rationale is that a "serializer" can be instantiated for an object, and will contain
 * the obj + size, and is usable as a functor.
 *
 * Serialization can also be done "explicitly" through the static method "write"
 * (Not using "serialize", because writing "serializer<apa>::serialize" all the time is tiring and redundant)
 * though care should be takes than data will fit of course.
 */
template<typename T>
class serializer {
public:
    typedef T type;
    typedef data_output output;
    typedef data_input input;
    typedef serializer<T> _MyType;

    serializer(const type&);

    // apply to memory, must be at least size() large.
    const _MyType& operator()(output& out) const {
        write(out, _item);
        return *this;
    }

    static void write(output&, const T&);
    static void read(T&, input&);
    static T read(input&);
    static void skip(input& in);

    size_t size() const {
        return _size;
    }
private:
    const T& _item;
    size_t _size;
};

template<> serializer<utils::UUID>::serializer(const utils::UUID &);
template<> void serializer<utils::UUID>::write(output&, const type&);
template<> void serializer<utils::UUID>::read(utils::UUID&, input&);
template<> void serializer<utils::UUID>::skip(input&);
template<> utils::UUID serializer<utils::UUID>::read(input&);

template<> serializer<bytes>::serializer(const bytes &);
template<> void serializer<bytes>::write(output&, const type&);
template<> void serializer<bytes>::read(bytes&, input&);

template<> serializer<bytes_view>::serializer(const bytes_view&);
template<> void serializer<bytes_view>::write(output&, const type&);
template<> void serializer<bytes_view>::read(bytes_view&, input&);
template<> bytes_view serializer<bytes_view>::read(input&);

template<> serializer<sstring>::serializer(const sstring&);
template<> void serializer<sstring>::write(output&, const type&);
template<> void serializer<sstring>::read(sstring&, input&);

template<> serializer<tombstone>::serializer(const tombstone &);
template<> void serializer<tombstone>::write(output&, const type&);
template<> void serializer<tombstone>::read(tombstone&, input&);

template<> serializer<atomic_cell_view>::serializer(const atomic_cell_view &);
template<> void serializer<atomic_cell_view>::write(output&, const type&);
template<> void serializer<atomic_cell_view>::read(atomic_cell_view&, input&);
template<> atomic_cell_view serializer<atomic_cell_view>::read(input&);

template<> serializer<collection_mutation::view>::serializer(const collection_mutation::view &);
template<> void serializer<collection_mutation::view>::write(output&, const type&);
template<> void serializer<collection_mutation::view>::read(collection_mutation::view&, input&);

template<> serializer<row>::serializer(const row &);
template<> void serializer<row>::write(output&, const type&);
template<> void serializer<row>::read(row&, input&);

template<> serializer<mutation_partition>::serializer(const mutation_partition &);
template<> void serializer<mutation_partition>::write(output&, const type&);
template<> void serializer<mutation_partition>::read(mutation_partition&, input&);
template<> mutation_partition serializer<mutation_partition>::read(input&) = delete;

template<> serializer<mutation>::serializer(const mutation &);
template<> void serializer<mutation>::write(output&, const type&);
template<> void serializer<mutation>::read(mutation&, input&) = delete;
template<> mutation serializer<mutation>::read(input&);

template<> serializer<partition_key_view>::serializer(const partition_key_view &);
template<> void serializer<partition_key_view>::write(output&, const partition_key_view&);
template<> void serializer<partition_key_view>::read(partition_key_view&, input&);
template<> partition_key_view serializer<partition_key_view>::read(input&);
template<> void serializer<partition_key_view>::skip(input&);

template<> serializer<clustering_key_view>::serializer(const clustering_key_view &);
template<> void serializer<clustering_key_view>::write(output&, const clustering_key_view&);
template<> void serializer<clustering_key_view>::read(clustering_key_view&, input&);
template<> clustering_key_view serializer<clustering_key_view>::read(input&);

template<> serializer<clustering_key_prefix_view>::serializer(const clustering_key_prefix_view &);
template<> void serializer<clustering_key_prefix_view>::write(output&, const clustering_key_prefix_view&);
template<> void serializer<clustering_key_prefix_view>::read(clustering_key_prefix_view&, input&);
template<> clustering_key_prefix_view serializer<clustering_key_prefix_view>::read(input&);

template<typename T>
T serializer<T>::read(input& in) {
    type t;
    read(t, in);
    return t;
}

extern template class serializer<tombstone>;
extern template class serializer<bytes>;
extern template class serializer<bytes_view>;
extern template class serializer<sstring>;
extern template class serializer<utils::UUID>;
extern template class serializer<partition_key_view>;
extern template class serializer<clustering_key_view>;
extern template class serializer<clustering_key_prefix_view>;

typedef serializer<tombstone> tombstone_serializer;
typedef serializer<bytes> bytes_serializer; // Compatible with bytes_view_serializer
typedef serializer<bytes_view> bytes_view_serializer; // Compatible with bytes_serializer
typedef serializer<sstring> sstring_serializer;
typedef serializer<atomic_cell_view> atomic_cell_view_serializer;
typedef serializer<collection_mutation::view> collection_mutation_view_serializer;
typedef serializer<utils::UUID> uuid_serializer;
typedef serializer<partition_key_view> partition_key_view_serializer;
typedef serializer<clustering_key_view> clustering_key_view_serializer;
typedef serializer<clustering_key_prefix_view> clustering_key_prefix_view_serializer;

}

#endif /* DB_SERIALIZER_HH_ */
<commit_msg>remove leftovers from db/serializer.hh<commit_after>/*
 * Copyright 2015 Cloudius Systems
 */

#ifndef DB_SERIALIZER_HH_
#define DB_SERIALIZER_HH_

#include "utils/data_input.hh"
#include "utils/data_output.hh"
#include "bytes.hh"
#include "mutation.hh"
#include "keys.hh"
#include "database_fwd.hh"

namespace db {
/**
 * Serialization objects for various types and using "internal" format. (Not CQL, origin whatnot).
 * The design rationale is that a "serializer" can be instantiated for an object, and will contain
 * the obj + size, and is usable as a functor.
 *
 * Serialization can also be done "explicitly" through the static method "write"
 * (Not using "serialize", because writing "serializer<apa>::serialize" all the time is tiring and redundant)
 * though care should be takes than data will fit of course.
 */
template<typename T>
class serializer {
public:
    typedef T type;
    typedef data_output output;
    typedef data_input input;
    typedef serializer<T> _MyType;

    serializer(const type&);

    // apply to memory, must be at least size() large.
    const _MyType& operator()(output& out) const {
        write(out, _item);
        return *this;
    }

    static void write(output&, const T&);
    static void read(T&, input&);
    static T read(input&);
    static void skip(input& in);

    size_t size() const {
        return _size;
    }
private:
    const T& _item;
    size_t _size;
};

template<> serializer<utils::UUID>::serializer(const utils::UUID &);
template<> void serializer<utils::UUID>::write(output&, const type&);
template<> void serializer<utils::UUID>::read(utils::UUID&, input&);
template<> void serializer<utils::UUID>::skip(input&);
template<> utils::UUID serializer<utils::UUID>::read(input&);

template<> serializer<bytes>::serializer(const bytes &);
template<> void serializer<bytes>::write(output&, const type&);
template<> void serializer<bytes>::read(bytes&, input&);

template<> serializer<bytes_view>::serializer(const bytes_view&);
template<> void serializer<bytes_view>::write(output&, const type&);
template<> void serializer<bytes_view>::read(bytes_view&, input&);
template<> bytes_view serializer<bytes_view>::read(input&);

template<> serializer<sstring>::serializer(const sstring&);
template<> void serializer<sstring>::write(output&, const type&);
template<> void serializer<sstring>::read(sstring&, input&);

template<> serializer<tombstone>::serializer(const tombstone &);
template<> void serializer<tombstone>::write(output&, const type&);
template<> void serializer<tombstone>::read(tombstone&, input&);

template<> serializer<atomic_cell_view>::serializer(const atomic_cell_view &);
template<> void serializer<atomic_cell_view>::write(output&, const type&);
template<> void serializer<atomic_cell_view>::read(atomic_cell_view&, input&);
template<> atomic_cell_view serializer<atomic_cell_view>::read(input&);

template<> serializer<collection_mutation::view>::serializer(const collection_mutation::view &);
template<> void serializer<collection_mutation::view>::write(output&, const type&);
template<> void serializer<collection_mutation::view>::read(collection_mutation::view&, input&);

template<> serializer<partition_key_view>::serializer(const partition_key_view &);
template<> void serializer<partition_key_view>::write(output&, const partition_key_view&);
template<> void serializer<partition_key_view>::read(partition_key_view&, input&);
template<> partition_key_view serializer<partition_key_view>::read(input&);
template<> void serializer<partition_key_view>::skip(input&);

template<> serializer<clustering_key_view>::serializer(const clustering_key_view &);
template<> void serializer<clustering_key_view>::write(output&, const clustering_key_view&);
template<> void serializer<clustering_key_view>::read(clustering_key_view&, input&);
template<> clustering_key_view serializer<clustering_key_view>::read(input&);

template<> serializer<clustering_key_prefix_view>::serializer(const clustering_key_prefix_view &);
template<> void serializer<clustering_key_prefix_view>::write(output&, const clustering_key_prefix_view&);
template<> void serializer<clustering_key_prefix_view>::read(clustering_key_prefix_view&, input&);
template<> clustering_key_prefix_view serializer<clustering_key_prefix_view>::read(input&);

template<typename T>
T serializer<T>::read(input& in) {
    type t;
    read(t, in);
    return t;
}

extern template class serializer<tombstone>;
extern template class serializer<bytes>;
extern template class serializer<bytes_view>;
extern template class serializer<sstring>;
extern template class serializer<utils::UUID>;
extern template class serializer<partition_key_view>;
extern template class serializer<clustering_key_view>;
extern template class serializer<clustering_key_prefix_view>;

typedef serializer<tombstone> tombstone_serializer;
typedef serializer<bytes> bytes_serializer; // Compatible with bytes_view_serializer
typedef serializer<bytes_view> bytes_view_serializer; // Compatible with bytes_serializer
typedef serializer<sstring> sstring_serializer;
typedef serializer<atomic_cell_view> atomic_cell_view_serializer;
typedef serializer<collection_mutation::view> collection_mutation_view_serializer;
typedef serializer<utils::UUID> uuid_serializer;
typedef serializer<partition_key_view> partition_key_view_serializer;
typedef serializer<clustering_key_view> clustering_key_view_serializer;
typedef serializer<clustering_key_prefix_view> clustering_key_prefix_view_serializer;

}

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


<commit_before>//
// file : assert.hpp
// in : file:///home/tim/projects/nsched/nsched/tools/debug/assert.hpp
//
// created by : Timothée Feuillet on linux.site
// date: 03/08/2014 16:18:37
//
//
// Copyright (c) 2014-2016 Timothée Feuillet
//
// 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.
//

#ifndef __N_1744658631925022054_1134258981__ASSERT_HPP__
# define __N_1744658631925022054_1134258981__ASSERT_HPP__

#include "../exception.hpp"

#ifndef N_DEBUG_USE_CERR
# include "../logger/logger.hpp"
#endif

#include <string>
#include <string.h>

#include <sstream>
#include <fstream>
#include <iostream>
#include <iomanip>

#include <stdexcept>

//
// (yet another) nice assert/function check tool
// NOTE: you could define N_DISABLE_CHECKS to completly disable every checks.
//       you could define N_DISABLE_ASSERTS to disable asserts.
//       you could define N_DISABLE_FNC_CHECKS to disable function checks.
//       you could define N_DEBUG_USE_CERR to output only (and directly) to std::cerr
//
//       there's the macro N_COLUMN_WIDTH that you can define (either to a hardcoded value or a variable)
//        to control the width of a column. Default is 200.
//       there's the macro N_ALLOW_DEBUG that you can define (either  to a hardcoded value or a variable)
//        to allow *debug* information (like logging success and co). Default is false.

#ifndef N_COLUMN_WIDTH
#define N_COLUMN_WIDTH 100
#endif
#ifndef N_ALLOW_DEBUG
#define N_ALLOW_DEBUG false
#endif

namespace neam
{
  namespace debug
  {
    class assert {};
    class function_check {};

    template<template<typename ErrorType> class ErrorClass, template<typename> class ExceptionClass = typed_exception>
    class on_error
    {
      private:
        on_error() = delete;
        on_error(const on_error &) = delete;

      public:
        template<typename ReturnType>
        static ReturnType &&_throw_exception(const char *file, size_t line, const std::string &func_call, ReturnType &&ret)
        {
          const bool is_error = ErrorClass<ReturnType>::is_error(ret);
          if (N_ALLOW_DEBUG || is_error)
          {
            std::string message = get_message(func_call, ret);
#ifdef N_DEBUG_USE_CERR
            std::cerr << ((is_error) ? " ** " : " -- ") << message << std::endl;
#else
            if (is_error)
              neam::cr::out.error() << LOGGER_INFO_TPL(get_filename(file), line) << message << std::endl;
            else
              neam::cr::out.debug() << LOGGER_INFO_TPL(get_filename(file), line) << message << std::endl;
#endif
            if (is_error)
              throw ExceptionClass<ErrorClass<ReturnType>>(message);
          }
          return std::move(ret);
        }

        // return true in case of error
        template<typename ReturnType>
        static bool _log_and_check(const char *file, size_t line, const std::string &func_call, ReturnType &&ret)
        {
          const bool is_error = ErrorClass<ReturnType>::is_error(ret);
          if (N_ALLOW_DEBUG || is_error)
          {
            std::string message = get_message(func_call, ret);
#ifdef N_DEBUG_USE_CERR
            std::cerr << ((is_error) ? " ** " : " -- ") << message << std::endl;
#else
            if (is_error)
              neam::cr::out.error() << LOGGER_INFO_TPL(get_filename(file), line) << message << std::endl;
            else
              neam::cr::out.debug() << LOGGER_INFO_TPL(get_filename(file), line) << message << std::endl;
#endif

            if (is_error)
              return false;
            return true;
          }
          return false;
        }

        // simply log the error and return the value 'as-is'
        template<typename ReturnType>
        static ReturnType &&_log_and_return(const char *file, size_t line, const std::string &func_call, ReturnType &&ret)
        {
          const bool is_error = ErrorClass<ReturnType>::is_error(ret);
          if (N_ALLOW_DEBUG || is_error)
          {
            std::string message = get_message(func_call, ret);
#ifdef N_DEBUG_USE_CERR
            std::cerr << ((is_error) ? " ** " : " -- ") << message << std::endl;
#else
            if (is_error)
              neam::cr::out.error() << LOGGER_INFO_TPL(get_filename(file), line) << message << std::endl;
            else
              neam::cr::out.debug() << LOGGER_INFO_TPL(get_filename(file), line) << message << std::endl;
#endif
          }
          return std::move(ret);
        }

        static void _assert(const char *file, size_t line, const std::string &test, bool status, const std::string message)
        {
          if (N_ALLOW_DEBUG || !status)
          {
#ifdef N_DEBUG_USE_CERR
            std::cerr << ((!status) ? " ** " : " -- ") << std::left << std::setw(N_COLUMN_WIDTH) << std::setfill('.') << (neam::demangle<assert>() + ": " + test + " ") << (!status ? " [FAILED]: " + message : " [SUCCESS]") << std::endl;
#else
            if (!status)
              neam::cr::out.error() << LOGGER_INFO_TPL(get_filename(file), line) << std::left << std::setw(N_COLUMN_WIDTH) << std::setfill('.') << (neam::demangle<_assert>() + ": " + test + " ") << "[FAILED]: " + message << std::endl;
            else
              neam::cr::out.debug() << LOGGER_INFO_TPL(get_filename(file), line) << std::left << std::setw(N_COLUMN_WIDTH) << std::setfill('.') << (neam::demangle<_assert>() + ": " + test + " ") << " [SUCCESS]" << std::endl;
#endif
            if (!status)
              throw ExceptionClass<on_error>(test + ": assert failed: " + message);
          }
        }

        static bool _assert_and_return(const char *file, size_t line, const std::string &test, bool status, const std::string message)
        {
          if (N_ALLOW_DEBUG || !status)
          {
#ifdef N_DEBUG_USE_CERR
            std::cerr << ((!status) ? " ** " : " -- ") << std::left << std::setw(N_COLUMN_WIDTH) << std::setfill('.') << (neam::demangle<assert>() + ": " + test + " ") << (!status ? " [FAILED]: " + message : " [SUCCESS]") << std::endl;
#else
            if (!status)
              neam::cr::out.error() << LOGGER_INFO_TPL(get_filename(file), line) << std::left << std::setw(N_COLUMN_WIDTH) << std::setfill('.') << (neam::demangle<_assert_and_return>() + ": " + test + " ") << "[FAILED]: " + message << std::endl;
            else
              neam::cr::out.debug() << LOGGER_INFO_TPL(get_filename(file), line) << std::left << std::setw(N_COLUMN_WIDTH) << std::setfill('.') << (neam::demangle<_assert_and_return>() + ": " + test + " ") << " [SUCCESS]" << std::endl;
#endif
          }
          return !status;
        }

        template<typename ReturnType>
        static ReturnType &&_dummy(ReturnType &&ret)
        {
          return std::move(ret);
        }

#if not defined N_DISABLE_ASSERTS and not defined N_DISABLE_CHECKS
#define n_assert(test, message)                   _assert(__FILE__, __LINE__, #test, test, message)
#define n_assert_exp(test, message)               assert(test, message)
#define n_assert_and_return(test, message)        _assert_and_return(__FILE__, __LINE__, #test, test, message)
#define n_assert_and_return_exp(test, message)    assert_and_return(test, message)
#else
#define n_assert(test, message)                   _dummy(((test), true))
#define n_assert_exp(test, message)               assert(test, message)
#define n_assert_and_return(test, message)        _dummy(((test), true))
#define n_assert_and_return_exp(test, message)    assert_and_return(test, message)
#endif

#if not defined N_DISABLE_FNC_CHECKS and not defined N_DISABLE_CHECKS
# define n_throw_exception(fnc)                   _throw_exception(__FILE__, __LINE__, #fnc, fnc)
# define n_throw_exception_exp(fnc)               throw_exception(fnc)
# define n_log_and_check(fnc)                     _log_and_check(__FILE__, __LINE__, #fnc, fnc)
# define n_log_and_check_exp(fnc)                 log_and_check(fnc)
# define n_log_and_return(fnc)                    _log_and_return(__FILE__, __LINE__, #fnc, fnc)
# define n_log_and_return_exp(fnc)                log_and_return(fnc)
#else
# define n_throw_exception(fnc)                   _dummy(fnc)
# define n_throw_exception_exp(fnc)               _dummy(fnc)
# define n_log_and_return(fnc)                    _dummy(fnc)
# define n_log_and_return_exp(fnc)                _dummy(fnc)
# define n_log_and_check(fnc)                     _dummy(fnc)
# define n_log_and_check_exp(fnc)                 _dummy(fnc)
#endif

      private:
        static const char *get_filename(const char *file)
        {
          return (strrchr((file), '/') ? strrchr((file), '/') + 1 : file);
        }

        template<typename ReturnType>
        static std::string get_message(const std::string &func_call, const ReturnType &code)
        {
          std::ostringstream is;

          if (ErrorClass<ReturnType>::exists(code))
          {
            const std::string code_name = ErrorClass<ReturnType>::get_code_name(code);
            const std::string description = ErrorClass<ReturnType>::get_description(code);

            is << std::left << std::setw(N_COLUMN_WIDTH) << std::setfill('.') << (func_call + " ") << " [" << code_name << "]: " << description;
          }
          else
            is << std::left << std::setw(N_COLUMN_WIDTH) << std::setfill('.') << (func_call + " ") << " [code: " << code << "]: [UNKNOW ERROR CODE]";
          return is.str();
        }
    };
  } // namespace debug
} // namespace neam

#endif /*__N_1744658631925022054_1134258981__ASSERT_HPP__*/

// kate: indent-mode cstyle; indent-width 2; replace-tabs on;

<commit_msg>[DEBUG]: Update the debug tool to account changes in the logger<commit_after>//
// file : assert.hpp
// in : file:///home/tim/projects/nsched/nsched/tools/debug/assert.hpp
//
// created by : Timothée Feuillet on linux.site
// date: 03/08/2014 16:18:37
//
//
// Copyright (c) 2014-2016 Timothée Feuillet
//
// 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.
//

#ifndef __N_1744658631925022054_1134258981__ASSERT_HPP__
# define __N_1744658631925022054_1134258981__ASSERT_HPP__

#include "../exception.hpp"

#ifndef N_DEBUG_USE_CERR
# include "../logger/logger.hpp"
#endif

#include <string>
#include <string.h>

#include <sstream>
#include <fstream>
#include <iostream>
#include <iomanip>

#include <stdexcept>

//
// (yet another) nice assert/function check tool
// NOTE: you could define N_DISABLE_CHECKS to completly disable every checks.
//       you could define N_DISABLE_ASSERTS to disable asserts.
//       you could define N_DISABLE_FNC_CHECKS to disable function checks.
//       you could define N_DEBUG_USE_CERR to output only (and directly) to std::cerr
//
//       there's the macro N_COLUMN_WIDTH that you can define (either to a hardcoded value or a variable)
//        to control the width of a column. Default is 200.
//       there's the macro N_ALLOW_DEBUG that you can define (either  to a hardcoded value or a variable)
//        to allow *debug* information (like logging success and co). Default is false.

#ifndef N_COLUMN_WIDTH
#define N_COLUMN_WIDTH 100
#endif
#ifndef N_ALLOW_DEBUG
#define N_ALLOW_DEBUG false
#endif

namespace neam
{
  namespace debug
  {
    template<template<typename ErrorType> class ErrorClass, template<typename> class ExceptionClass = typed_exception>
    class on_error
    {
      private:
        on_error() = delete;
        on_error(const on_error &) = delete;

      public:
        template<typename ReturnType>
        static ReturnType &&_throw_exception(const char *file, size_t line, const std::string &func_call, ReturnType &&ret)
        {
          const bool is_error = ErrorClass<ReturnType>::is_error(ret);
          if (N_ALLOW_DEBUG || is_error)
          {
            std::string message = get_message(func_call, ret);
#ifdef N_DEBUG_USE_CERR
            std::cerr << ((is_error) ? " ** " : " -- ") << message << std::endl;
#else
            if (is_error)
              neam::cr::out.error() << LOGGER_INFO_TPL(get_filename(file), line) << message << std::endl;
            else
              neam::cr::out.debug() << LOGGER_INFO_TPL(get_filename(file), line) << message << std::endl;
#endif
            if (is_error)
              throw ExceptionClass<ErrorClass<ReturnType>>(message);
          }
          return std::move(ret);
        }

        // return true in case of error
        template<typename ReturnType>
        static bool _log_and_check(const char *file, size_t line, const std::string &func_call, ReturnType &&ret)
        {
          const bool is_error = ErrorClass<ReturnType>::is_error(ret);
          if (N_ALLOW_DEBUG || is_error)
          {
            std::string message = get_message(func_call, ret);
#ifdef N_DEBUG_USE_CERR
            std::cerr << ((is_error) ? " ** " : " -- ") << message << std::endl;
#else
            if (is_error)
              neam::cr::out.error() << LOGGER_INFO_TPL(get_filename(file), line) << message << std::endl;
            else
              neam::cr::out.debug() << LOGGER_INFO_TPL(get_filename(file), line) << message << std::endl;
#endif

            if (is_error)
              return false;
            return true;
          }
          return false;
        }

        // simply log the error and return the value 'as-is'
        template<typename ReturnType>
        static ReturnType &&_log_and_return(const char *file, size_t line, const std::string &func_call, ReturnType &&ret)
        {
          const bool is_error = ErrorClass<ReturnType>::is_error(ret);
          if (N_ALLOW_DEBUG || is_error)
          {
            std::string message = get_message(func_call, ret);
#ifdef N_DEBUG_USE_CERR
            std::cerr << ((is_error) ? " ** " : " -- ") << message << std::endl;
#else
            if (is_error)
              neam::cr::out.error() << LOGGER_INFO_TPL(get_filename(file), line) << message << std::endl;
            else
              neam::cr::out.debug() << LOGGER_INFO_TPL(get_filename(file), line) << message << std::endl;
#endif
          }
          return std::move(ret);
        }

        static void _assert(const char *file, size_t line, const std::string &test, bool status, const std::string message)
        {
          if (N_ALLOW_DEBUG || !status)
          {
#ifdef N_DEBUG_USE_CERR
            std::cerr << ((!status) ? " ** " : " -- ") << std::left << std::setw(N_COLUMN_WIDTH) << std::setfill('.') << (test + " ") << (!status ? " [FAILED]: " + message : " [SUCCESS]") << std::endl;
#else
            if (!status)
              neam::cr::out.error() << LOGGER_INFO_TPL(get_filename(file), line) << std::left << std::setw(N_COLUMN_WIDTH) << std::setfill('.') << (test + " ") << "[FAILED]: " + message << std::endl;
            else
              neam::cr::out.debug() << LOGGER_INFO_TPL(get_filename(file), line) << std::left << std::setw(N_COLUMN_WIDTH) << std::setfill('.') << (test + " ") << " [SUCCESS]" << std::endl;
#endif
            if (!status)
              throw ExceptionClass<on_error>(test + ": assert failed: " + message);
          }
        }

        static bool _assert_and_return(const char *file, size_t line, const std::string &test, bool status, const std::string message)
        {
          if (N_ALLOW_DEBUG || !status)
          {
#ifdef N_DEBUG_USE_CERR
            std::cerr << ((!status) ? " ** " : " -- ") << std::left << std::setw(N_COLUMN_WIDTH) << std::setfill('.') << (test + " ") << (!status ? " [FAILED]: " + message : " [SUCCESS]") << std::endl;
#else
            if (!status)
              neam::cr::out.error() << LOGGER_INFO_TPL(get_filename(file), line) << std::left << std::setw(N_COLUMN_WIDTH) << std::setfill('.') << (test + " ") << "[FAILED]: " + message << std::endl;
            else
              neam::cr::out.debug() << LOGGER_INFO_TPL(get_filename(file), line) << std::left << std::setw(N_COLUMN_WIDTH) << std::setfill('.') << (test + " ") << " [SUCCESS]" << std::endl;
#endif
          }
          return !status;
        }

        template<typename ReturnType>
        static ReturnType &&_dummy(ReturnType &&ret)
        {
          return std::move(ret);
        }

#if not defined N_DISABLE_ASSERTS and not defined N_DISABLE_CHECKS
#define n_assert(test, message)                   _assert(__FILE__, __LINE__, #test, test, message)
#define n_assert_exp(test, message)               assert(test, message)
#define n_assert_and_return(test, message)        _assert_and_return(__FILE__, __LINE__, #test, test, message)
#define n_assert_and_return_exp(test, message)    assert_and_return(test, message)
#else
#define n_assert(test, message)                   _dummy(((test), true))
#define n_assert_exp(test, message)               assert(test, message)
#define n_assert_and_return(test, message)        _dummy(((test), true))
#define n_assert_and_return_exp(test, message)    assert_and_return(test, message)
#endif

#if not defined N_DISABLE_FNC_CHECKS and not defined N_DISABLE_CHECKS
# define n_throw_exception(fnc)                   _throw_exception(__FILE__, __LINE__, #fnc, fnc)
# define n_throw_exception_exp(fnc)               throw_exception(fnc)
# define n_log_and_check(fnc)                     _log_and_check(__FILE__, __LINE__, #fnc, fnc)
# define n_log_and_check_exp(fnc)                 log_and_check(fnc)
# define n_log_and_return(fnc)                    _log_and_return(__FILE__, __LINE__, #fnc, fnc)
# define n_log_and_return_exp(fnc)                log_and_return(fnc)
#else
# define n_throw_exception(fnc)                   _dummy(fnc)
# define n_throw_exception_exp(fnc)               _dummy(fnc)
# define n_log_and_return(fnc)                    _dummy(fnc)
# define n_log_and_return_exp(fnc)                _dummy(fnc)
# define n_log_and_check(fnc)                     _dummy(fnc)
# define n_log_and_check_exp(fnc)                 _dummy(fnc)
#endif

      private:
        static const char *get_filename(const char *file)
        {
          return (strrchr((file), '/') ? strrchr((file), '/') + 1 : file);
        }

        template<typename ReturnType>
        static std::string get_message(const std::string &func_call, const ReturnType &code)
        {
          std::ostringstream is;

          if (ErrorClass<ReturnType>::exists(code))
          {
            const std::string code_name = ErrorClass<ReturnType>::get_code_name(code);
            const std::string description = ErrorClass<ReturnType>::get_description(code);

            is << std::left << std::setw(N_COLUMN_WIDTH) << std::setfill('.') << (func_call + " ") << " [" << code_name << "]: " << description;
          }
          else
            is << std::left << std::setw(N_COLUMN_WIDTH) << std::setfill('.') << (func_call + " ") << " [code: " << code << "]: [UNKNOW ERROR CODE]";
          return is.str();
        }
    };
  } // namespace debug
} // namespace neam

#endif /*__N_1744658631925022054_1134258981__ASSERT_HPP__*/

// kate: indent-mode cstyle; indent-width 2; replace-tabs on;

<|endoftext|>


<commit_before>//
// Copyright (c) 2018 Rokas Kupstys
//
// 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 <Urho3D/Urho3DAll.h>
#include <CLI11/CLI11.hpp>
#include <Toolbox/SystemUI/Gizmo.h>
#include <ctime>
#include <cstdio>

using namespace std::placeholders;

namespace Urho3D
{

class AssetViewer
    : public Application
{
    URHO3D_OBJECT(AssetViewer, Application);
public:
    SharedPtr<Scene> scene_;
    SharedPtr<Viewport> viewport_;
    SharedPtr<Light> light_;
    WeakPtr<Camera> camera_;
    WeakPtr<Node> node_;
    WeakPtr<Node> parentNode_;
    WeakPtr<AnimatedModel> model_;
    WeakPtr<AnimationController> animator_;
    float lookSensitivity_ = 1.0f;
    Gizmo gizmo_;
    bool showHelp_ = false;
    std::string assetFile_;

    explicit AssetViewer(Context* context)
        : Application(context), gizmo_(context)
    {
    }

    void Setup() override
    {
        engineParameters_[EP_WINDOW_TITLE]   = GetTypeName();
        engineParameters_[EP_WINDOW_WIDTH]   = 1024;
        engineParameters_[EP_WINDOW_HEIGHT]  = 768;
        engineParameters_[EP_FULL_SCREEN]    = false;
        engineParameters_[EP_HEADLESS]       = false;
        engineParameters_[EP_SOUND]          = false;
        engineParameters_[EP_RESOURCE_PATHS] = "CoreData";
        engineParameters_[EP_RESOURCE_PREFIX_PATHS] = ";..;../..";
        engineParameters_[EP_WINDOW_RESIZABLE] = true;

        GetCommandLineParser().add_option("asset", assetFile_, "Asset file to be opened on application startup.");
    }

    void Start() override
    {
        ui::GetIO().IniFilename = nullptr;    // Disable saving of settings.

        GetInput()->SetMouseVisible(true);
        GetInput()->SetMouseMode(MM_ABSOLUTE);

        scene_ = new Scene(context_);
        scene_->CreateComponent<Octree>();
        auto zone = scene_->CreateComponent<Zone>();
        zone->SetFogColor(Color(0.5f, 0.5f, 0.7f));
        camera_ = scene_->CreateChild("Camera")->CreateComponent<Camera>();
        light_ = camera_->GetNode()->CreateComponent<Light>();
        light_->SetColor(Color::WHITE);
        light_->SetLightType(LIGHT_DIRECTIONAL);

        viewport_ = new Viewport(context_, scene_, camera_);
        GetSubsystem<Renderer>()->SetViewport(0, viewport_);

        // Parent node used to rotate model around it's center (not origin).
        parentNode_ = scene_->CreateChild();

        GetSystemUI()->ApplyStyleDefault(true, 1);
        ui::GetStyle().WindowRounding = 0;

        SubscribeToEvent(E_UPDATE, std::bind(&AssetViewer::OnUpdate, this, _2));
        SubscribeToEvent(E_DROPFILE, std::bind(&AssetViewer::OnFileDrop, this, _2));

        if (!assetFile_.empty())
            LoadFile(assetFile_.c_str());
    }

    void OnUpdate(VariantMap& args)
    {
        if (node_.Null())
            return;

        if (!GetSystemUI()->IsAnyItemActive() && !GetSystemUI()->IsAnyItemHovered())
        {
            auto input = GetSubsystem<Input>();

            if (!input->GetKeyDown(KEY_SHIFT))
            {
                if (input->GetMouseButtonDown(MOUSEB_RIGHT))
                {
                    if (input->IsMouseVisible())
                        input->SetMouseVisible(false);

                    if (input->GetMouseMove() != IntVector2::ZERO)
                    {
                        camera_->GetNode()->RotateAround(Vector3::ZERO,
                            Quaternion(input->GetMouseMoveX() * 0.1f * lookSensitivity_, camera_->GetNode()->GetUp()) *
                            Quaternion(input->GetMouseMoveY() * 0.1f * lookSensitivity_, camera_->GetNode()->GetRight()), TS_WORLD);
                    }
                }
                else if (input->GetMouseButtonDown(MOUSEB_MIDDLE))
                {
                    if (input->IsMouseVisible())
                        input->SetMouseVisible(false);

                    node_->Translate2D(Vector2(input->GetMouseMoveX(), -input->GetMouseMoveY()) / 300.f, TS_WORLD);
                }
                else if (!input->IsMouseVisible())
                    input->SetMouseVisible(true);

                camera_->GetNode()->Translate(Vector3::FORWARD * input->GetMouseMoveWheel() * 0.2f);
            }
            else if (!input->IsMouseVisible())
                input->SetMouseVisible(true);
        }

        if (GetInput()->GetKeyPress(KEY_F1))
            showHelp_ = true;

        ui::SetNextWindowPos({0, 0}, ImGuiCond_Always);
        if (ui::Begin("Settings", nullptr,
                      ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoTitleBar |
                      ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoSavedSettings))
        {
            gizmo_.RenderUI();

            if (ui::Button("Reset"))
                ResetNode();

            // Window has to contain controls already in order for it's size to be set to match contents.
            ui::SetWindowSize({0, 0}, ImGuiCond_Always);
        }
        ui::End();

        if (showHelp_)
        {
            if (ui::Begin("Help", &showHelp_, ImGuiWindowFlags_NoSavedSettings))
            {
                ui::TextUnformatted("RMB: hold it rotates model around its center.");
                ui::TextUnformatted("Shift: holding it provides manipulation gizmo at model origin.");
            }
            ui::End();
        }

        if (node_ && GetInput()->GetKeyDown(KEY_SHIFT))
            gizmo_.Manipulate(camera_, parentNode_);
    }

    void OnFileDrop(VariantMap& args)
    {
        LoadFile(args[DropFile::P_FILENAME].GetString());
    }

    void LoadFile(const String& file_path)
    {
        if (file_path.EndsWith(".mdl"))
            LoadModel(file_path);
        else if (file_path.EndsWith(".ani"))
            LoadAnimation(file_path);
        else if (file_path.EndsWith(".fbx"))
            LoadFbx(file_path);
    }

    void LoadModel(const String& file_path, const Vector<String>& materials = { })
    {
        if (node_.NotNull())
            node_->Remove();

        node_ = parentNode_->CreateChild("Node");
        model_ = node_->CreateComponent<AnimatedModel>();
        animator_ = node_->CreateComponent<AnimationController>();

        auto cache = GetSubsystem<ResourceCache>();
        model_->SetModel(cache->GetTempResource<Model>(file_path));

        ResetNode();

        for (auto i = 0; i < materials.Size(); i++)
            model_->SetMaterial(i, cache->GetTempResource<Material>(materials[i]));
    }

    void ResetNode()
    {
        if (node_.NotNull())
        {
            parentNode_->SetScale(1.f);
            parentNode_->SetPosition(Vector3::ZERO);
            parentNode_->SetRotation(Quaternion::IDENTITY);
            // Scale model to size of 1 unit
            auto size = model_->GetBoundingBox().Size();
            node_->SetScale(1.f / Max(size.x_, Max(size.y_, size.z_)));
            // Snap center of the model to {0,0,0}
            node_->SetWorldPosition(node_->GetWorldPosition() - model_->GetWorldBoundingBox().Center());
            node_->SetRotation(Quaternion::IDENTITY);

            camera_->GetNode()->LookAt(Vector3::ZERO);
            camera_->GetNode()->SetRotation(Quaternion::IDENTITY);
            camera_->GetNode()->SetPosition(Vector3::BACK * 1.5f);
        }
    }

    void LoadAnimation(const String& file_path)
    {
        if (animator_)
            animator_->PlayExclusive(file_path, 0, true);
    }

    void LoadFbx(const String& file_path)
    {
        auto fs = GetSubsystem<FileSystem>();
        String temp = fs->GetTemporaryDir();
        temp += "AssetViewer/";
        if (!fs->DirExists(temp))
        {
            fs->CreateDir(temp);
            fs->CreateDir(temp + "/mdl");
            fs->CreateDir(temp + "/ani");
        }
        auto animation_path = temp + "/ani";
        auto model_file = temp + "/mdl/out.mdl";
        auto material_list_file = temp + "/mdl/out.txt";
        fs->Delete(model_file);

        Process proc(fs->GetProgramDir() + "AssetImporter", {"model", file_path, model_file.CString(), "-na", "-l"});
        if (proc.Run() == 0)
        {
            if (fs->FileExists(model_file))
            {
                Vector<String> materials;
                File fp(context_, material_list_file);
                if (fp.IsOpen())
                {
                    while (!fp.IsEof())
                        materials.Push(temp + "/mdl/" + fp.ReadLine());
                }
                LoadModel(model_file, materials);
            }

            Vector<String> animations;
            fs->ScanDir(animations, animation_path, "*.ani", SCAN_FILES, false);
            for (const auto& filename : animations)
                fs->Delete(animation_path + "/" + filename);

            proc = Process(fs->GetProgramDir() + "AssetImporter",
                {"anim", file_path, animation_path + "/out_" + ToString("%ld", time(nullptr))});
            if (proc.Run() == 0)
            {
                fs->ScanDir(animations, animation_path, "*.ani", SCAN_FILES, false);

                if (animations.Size())
                    LoadAnimation(animation_path + "/" + animations[0]);
            }
            else
                URHO3D_LOGERROR("Importing animations failed.");
        }
        else
            URHO3D_LOGERROR("Importing model failed.");
    }
};

}

URHO3D_DEFINE_APPLICATION_MAIN(Urho3D::AssetViewer);
<commit_msg>Tools: Get rid of Urho3DAll.h usage in AssetViewer.<commit_after>//
// Copyright (c) 2018 Rokas Kupstys
//
// 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 <CLI11/CLI11.hpp>
#include <Urho3D/Core/CoreEvents.h>
#include <Urho3D/Engine/Application.h>
#include <Urho3D/Engine/EngineDefs.h>
#include <Urho3D/Graphics/AnimatedModel.h>
#include <Urho3D/Graphics/AnimationController.h>
#include <Urho3D/Graphics/Camera.h>
#include <Urho3D/Graphics/Light.h>
#include <Urho3D/Graphics/Octree.h>
#include <Urho3D/Graphics/Renderer.h>
#include <Urho3D/Graphics/Viewport.h>
#include <Urho3D/Graphics/Zone.h>
#include <Urho3D/Input/Input.h>
#include <Urho3D/IO/FileSystem.h>
#include <Urho3D/Resource/ResourceCache.h>
#include <Urho3D/Scene/Scene.h>
#include <Urho3D/SystemUI/SystemUI.h>
#include <Toolbox/SystemUI/Gizmo.h>
#include <ctime>
#include <cstdio>
#include <Urho3D/IO/Log.h>

using namespace std::placeholders;

namespace Urho3D
{

class AssetViewer
    : public Application
{
    URHO3D_OBJECT(AssetViewer, Application);
public:
    SharedPtr<Scene> scene_;
    SharedPtr<Viewport> viewport_;
    SharedPtr<Light> light_;
    WeakPtr<Camera> camera_;
    WeakPtr<Node> node_;
    WeakPtr<Node> parentNode_;
    WeakPtr<AnimatedModel> model_;
    WeakPtr<AnimationController> animator_;
    float lookSensitivity_ = 1.0f;
    Gizmo gizmo_;
    bool showHelp_ = false;
    std::string assetFile_;

    explicit AssetViewer(Context* context)
        : Application(context), gizmo_(context)
    {
    }

    void Setup() override
    {
        engineParameters_[EP_WINDOW_TITLE]   = GetTypeName();
        engineParameters_[EP_WINDOW_WIDTH]   = 1024;
        engineParameters_[EP_WINDOW_HEIGHT]  = 768;
        engineParameters_[EP_FULL_SCREEN]    = false;
        engineParameters_[EP_HEADLESS]       = false;
        engineParameters_[EP_SOUND]          = false;
        engineParameters_[EP_RESOURCE_PATHS] = "CoreData";
        engineParameters_[EP_RESOURCE_PREFIX_PATHS] = ";..;../..";
        engineParameters_[EP_WINDOW_RESIZABLE] = true;

        GetCommandLineParser().add_option("asset", assetFile_, "Asset file to be opened on application startup.");
    }

    void Start() override
    {
        ui::GetIO().IniFilename = nullptr;    // Disable saving of settings.

        GetInput()->SetMouseVisible(true);
        GetInput()->SetMouseMode(MM_ABSOLUTE);

        scene_ = new Scene(context_);
        scene_->CreateComponent<Octree>();
        auto zone = scene_->CreateComponent<Zone>();
        zone->SetFogColor(Color(0.5f, 0.5f, 0.7f));
        camera_ = scene_->CreateChild("Camera")->CreateComponent<Camera>();
        light_ = camera_->GetNode()->CreateComponent<Light>();
        light_->SetColor(Color::WHITE);
        light_->SetLightType(LIGHT_DIRECTIONAL);

        viewport_ = new Viewport(context_, scene_, camera_);
        GetSubsystem<Renderer>()->SetViewport(0, viewport_);

        // Parent node used to rotate model around it's center (not origin).
        parentNode_ = scene_->CreateChild();

        GetSystemUI()->ApplyStyleDefault(true, 1);
        ui::GetStyle().WindowRounding = 0;

        SubscribeToEvent(E_UPDATE, std::bind(&AssetViewer::OnUpdate, this, _2));
        SubscribeToEvent(E_DROPFILE, std::bind(&AssetViewer::OnFileDrop, this, _2));

        if (!assetFile_.empty())
            LoadFile(assetFile_.c_str());
    }

    void OnUpdate(VariantMap& args)
    {
        if (node_.Null())
            return;

        if (!GetSystemUI()->IsAnyItemActive() && !GetSystemUI()->IsAnyItemHovered())
        {
            auto input = GetSubsystem<Input>();

            if (!input->GetKeyDown(KEY_SHIFT))
            {
                if (input->GetMouseButtonDown(MOUSEB_RIGHT))
                {
                    if (input->IsMouseVisible())
                        input->SetMouseVisible(false);

                    if (input->GetMouseMove() != IntVector2::ZERO)
                    {
                        camera_->GetNode()->RotateAround(Vector3::ZERO,
                            Quaternion(input->GetMouseMoveX() * 0.1f * lookSensitivity_, camera_->GetNode()->GetUp()) *
                            Quaternion(input->GetMouseMoveY() * 0.1f * lookSensitivity_, camera_->GetNode()->GetRight()), TS_WORLD);
                    }
                }
                else if (input->GetMouseButtonDown(MOUSEB_MIDDLE))
                {
                    if (input->IsMouseVisible())
                        input->SetMouseVisible(false);

                    node_->Translate2D(Vector2(input->GetMouseMoveX(), -input->GetMouseMoveY()) / 300.f, TS_WORLD);
                }
                else if (!input->IsMouseVisible())
                    input->SetMouseVisible(true);

                camera_->GetNode()->Translate(Vector3::FORWARD * input->GetMouseMoveWheel() * 0.2f);
            }
            else if (!input->IsMouseVisible())
                input->SetMouseVisible(true);
        }

        if (GetInput()->GetKeyPress(KEY_F1))
            showHelp_ = true;

        ui::SetNextWindowPos({0, 0}, ImGuiCond_Always);
        if (ui::Begin("Settings", nullptr,
                      ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoTitleBar |
                      ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoSavedSettings))
        {
            gizmo_.RenderUI();

            if (ui::Button("Reset"))
                ResetNode();

            // Window has to contain controls already in order for it's size to be set to match contents.
            ui::SetWindowSize({0, 0}, ImGuiCond_Always);
        }
        ui::End();

        if (showHelp_)
        {
            if (ui::Begin("Help", &showHelp_, ImGuiWindowFlags_NoSavedSettings))
            {
                ui::TextUnformatted("RMB: hold it rotates model around its center.");
                ui::TextUnformatted("Shift: holding it provides manipulation gizmo at model origin.");
            }
            ui::End();
        }

        if (node_ && GetInput()->GetKeyDown(KEY_SHIFT))
            gizmo_.Manipulate(camera_, parentNode_);
    }

    void OnFileDrop(VariantMap& args)
    {
        LoadFile(args[DropFile::P_FILENAME].GetString());
    }

    void LoadFile(const String& file_path)
    {
        if (file_path.EndsWith(".mdl"))
            LoadModel(file_path);
        else if (file_path.EndsWith(".ani"))
            LoadAnimation(file_path);
        else if (file_path.EndsWith(".fbx"))
            LoadFbx(file_path);
    }

    void LoadModel(const String& file_path, const Vector<String>& materials = { })
    {
        if (node_.NotNull())
            node_->Remove();

        node_ = parentNode_->CreateChild("Node");
        model_ = node_->CreateComponent<AnimatedModel>();
        animator_ = node_->CreateComponent<AnimationController>();

        auto cache = GetSubsystem<ResourceCache>();
        model_->SetModel(cache->GetTempResource<Model>(file_path));

        ResetNode();

        for (auto i = 0; i < materials.Size(); i++)
            model_->SetMaterial(i, cache->GetTempResource<Material>(materials[i]));
    }

    void ResetNode()
    {
        if (node_.NotNull())
        {
            parentNode_->SetScale(1.f);
            parentNode_->SetPosition(Vector3::ZERO);
            parentNode_->SetRotation(Quaternion::IDENTITY);
            // Scale model to size of 1 unit
            auto size = model_->GetBoundingBox().Size();
            node_->SetScale(1.f / Max(size.x_, Max(size.y_, size.z_)));
            // Snap center of the model to {0,0,0}
            node_->SetWorldPosition(node_->GetWorldPosition() - model_->GetWorldBoundingBox().Center());
            node_->SetRotation(Quaternion::IDENTITY);

            camera_->GetNode()->LookAt(Vector3::ZERO);
            camera_->GetNode()->SetRotation(Quaternion::IDENTITY);
            camera_->GetNode()->SetPosition(Vector3::BACK * 1.5f);
        }
    }

    void LoadAnimation(const String& file_path)
    {
        if (animator_)
            animator_->PlayExclusive(file_path, 0, true);
    }

    void LoadFbx(const String& file_path)
    {
        auto fs = GetSubsystem<FileSystem>();
        String temp = fs->GetTemporaryDir();
        temp += "AssetViewer/";
        if (!fs->DirExists(temp))
        {
            fs->CreateDir(temp);
            fs->CreateDir(temp + "/mdl");
            fs->CreateDir(temp + "/ani");
        }
        auto animation_path = temp + "/ani";
        auto model_file = temp + "/mdl/out.mdl";
        auto material_list_file = temp + "/mdl/out.txt";
        fs->Delete(model_file);

        Process proc(fs->GetProgramDir() + "AssetImporter", {"model", file_path, model_file.CString(), "-na", "-l"});
        if (proc.Run() == 0)
        {
            if (fs->FileExists(model_file))
            {
                Vector<String> materials;
                File fp(context_, material_list_file);
                if (fp.IsOpen())
                {
                    while (!fp.IsEof())
                        materials.Push(temp + "/mdl/" + fp.ReadLine());
                }
                LoadModel(model_file, materials);
            }

            Vector<String> animations;
            fs->ScanDir(animations, animation_path, "*.ani", SCAN_FILES, false);
            for (const auto& filename : animations)
                fs->Delete(animation_path + "/" + filename);

            proc = Process(fs->GetProgramDir() + "AssetImporter",
                {"anim", file_path, animation_path + "/out_" + ToString("%ld", time(nullptr))});
            if (proc.Run() == 0)
            {
                fs->ScanDir(animations, animation_path, "*.ani", SCAN_FILES, false);

                if (animations.Size())
                    LoadAnimation(animation_path + "/" + animations[0]);
            }
            else
                URHO3D_LOGERROR("Importing animations failed.");
        }
        else
            URHO3D_LOGERROR("Importing model failed.");
    }
};

}

URHO3D_DEFINE_APPLICATION_MAIN(Urho3D::AssetViewer);
<|endoftext|>