From 6930b27d1f65c813753a42ca1f21e8e6a632a584 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dominik=20Tomic=CC=8Cevic=CC=81?=
 <dominik.tomicevic@gmail.com>
Date: Tue, 13 Oct 2015 20:17:45 +0200
Subject: [PATCH] updated properties

---
 proptest.cpp                            | 120 ++++++++++++++++++
 speedy/http/response.inl                |   2 +-
 speedy/http/status_codes.hpp            |  12 +-
 storage/model/properties/properties.hpp |  38 +++---
 storage/model/properties/property.hpp   | 158 ++++++++++++++++++++----
 storage/model/properties/string.hpp     |  22 ----
 utils/underlying_cast.hpp               |  12 ++
 7 files changed, 296 insertions(+), 68 deletions(-)
 create mode 100644 proptest.cpp
 delete mode 100644 storage/model/properties/string.hpp
 create mode 100644 utils/underlying_cast.hpp

diff --git a/proptest.cpp b/proptest.cpp
new file mode 100644
index 000000000..819c6bc97
--- /dev/null
+++ b/proptest.cpp
@@ -0,0 +1,120 @@
+#include <iostream>
+
+#include "storage/model/properties/properties.hpp"
+#include "storage/model/properties/property.hpp"
+
+template <class Buffer>
+struct JsonWriter
+{
+public:
+    JsonWriter(Buffer& buffer) : buffer(buffer)
+    {
+        buffer << '{';
+    };
+
+    void handle(const std::string& key, Property& value, bool first)
+    {
+        if(!first)
+            buffer << ',';
+
+        buffer << '"' << key << "\":";
+        value.accept(*this);
+    }
+
+    void handle(Null&)
+    {
+        buffer << "NULL";
+    }
+
+    void handle(Bool& b)
+    {
+        buffer << (b.value() ? "true" : "false");
+    }
+
+    void handle(String& s)
+    {
+        buffer << '"' << s.value << '"';
+    }
+
+    void handle(Int32& int32)
+    {
+        buffer << std::to_string(int32.value);
+    }
+
+    void handle(Int64& int64)
+    {
+        buffer << std::to_string(int64.value);
+    }
+
+    void handle(Float& f)
+    {
+        buffer << std::to_string(f.value);
+    }
+
+    void handle(Double& d)
+    {
+        buffer << std::to_string(d.value);
+    }
+
+    void finish()
+    {
+        buffer << '}';
+    }
+
+private:
+    Buffer& buffer;
+};
+
+class StringBuffer
+{
+public:
+    StringBuffer& operator<<(const std::string& str)
+    {
+        data += str;
+        return *this;
+    }
+
+    StringBuffer& operator<<(const char* str)
+    {
+        data += str;
+        return *this;
+    }
+
+    StringBuffer& operator<<(char c)
+    {
+        data += c;
+        return *this;
+    }
+
+    std::string& str()
+    {
+        return data;
+    }
+
+private:
+    std::string data;
+};
+
+int main(void)
+{
+    StringBuffer buffer;
+    auto handler = JsonWriter<StringBuffer>(buffer);
+
+    Properties props;
+    props.emplace<Null>("sadness");
+    props.emplace<Bool>("awesome", true);
+    props.emplace<Bool>("lame", false);
+    props.emplace<Int32>("age", 32);
+    props.emplace<Int64>("money", 12345678910111213);
+    props.emplace<String>("name", "caca");
+    props.emplace<Float>("pi", 3.14159265358979323846264338327950288419716939937510582097);
+    props.emplace<Double>("pi2", 3.141592653589793238462643383279502884197169399375105820);
+
+    props.accept(handler);
+    handler.finish();
+
+    std::cout.precision(25);
+    std::cout << buffer.str() << std::endl;
+
+    return 0;
+}
diff --git a/speedy/http/response.inl b/speedy/http/response.inl
index e7e541ae7..32524ceda 100644
--- a/speedy/http/response.inl
+++ b/speedy/http/response.inl
@@ -30,7 +30,7 @@ void Response<Req, Res>::send(const std::string& body)
 
     write_req->data = &connection;
 
-    buffer << "HTTP/1.1 " << to_string[status] << "\r\n";
+    buffer << "HTTP/1.1 " << to_string(status) << "\r\n";
 
     buffer << "Content-Length:" << std::to_string(body.size()) << "\r\n";
 
diff --git a/speedy/http/status_codes.hpp b/speedy/http/status_codes.hpp
index dc3a4ca9d..b969dc1cc 100644
--- a/speedy/http/status_codes.hpp
+++ b/speedy/http/status_codes.hpp
@@ -72,11 +72,15 @@ enum Status
 #undef CODE
 };
 
-static std::map<Status, std::string> to_string = {
-#define CODE(a, b, c) { Status::a, #b " " #c },
-    HTTP_STATUS_CODES
+std::string to_string(Status status)
+{
+    switch(status)
+    {
+#define CODE(a, b, c) case a: return  #b " " #c;
+        HTTP_STATUS_CODES
 #undef CODE
-};
+    }
+}
 
 }
 
diff --git a/storage/model/properties/properties.hpp b/storage/model/properties/properties.hpp
index 36347a5b6..e60955b1f 100644
--- a/storage/model/properties/properties.hpp
+++ b/storage/model/properties/properties.hpp
@@ -4,10 +4,6 @@
 #include <map>
 
 #include "property.hpp"
-#include "string.hpp"
-
-namespace model
-{
 
 class Properties
 {
@@ -25,6 +21,22 @@ public:
         return it == props.end() ? nullptr : it->second.get();
     }
 
+    template <class T, class... Args>
+    void emplace(const std::string& key, Args&&... args)
+    {
+        auto value = std::make_shared<T>(std::forward<Args>(args)...);
+
+        // try to emplace the item
+        auto result = props.emplace(std::make_pair(key, std::move(value)));
+
+        // return if we succedded
+        if(result.second)
+            return;
+
+        // the key already exists, replace the value it holds
+        result.first->second = std::move(value);
+    }
+
     void put(const std::string& key, Property::sptr value)
     {
         props[key] = std::move(value);
@@ -35,24 +47,22 @@ public:
         props.erase(key);
     }
 
-    void dump(std::string& buffer)
+    template <class Handler>
+    void accept(Handler& handler)
     {
-        buffer += '{';
+        bool first = true;
 
-        for(auto& kvp : props)
+        for(auto& kv : props)
         {
-            buffer += '"'; buffer += kvp.first; buffer += "\":";
-            kvp.second->dump(buffer); buffer += ',';
+            handler.handle(kv.first, *kv.second, first);
+            
+            if(first)
+                first = false;
         }
-    
-        // replace last redundant comma with }
-        buffer.back() = '}';
     }
 
 private:
     props_t props;
 };
 
-}
-
 #endif
diff --git a/storage/model/properties/property.hpp b/storage/model/properties/property.hpp
index 61174225a..09114ae1c 100644
--- a/storage/model/properties/property.hpp
+++ b/storage/model/properties/property.hpp
@@ -4,52 +4,156 @@
 #include <memory>
 #include <string>
 
-namespace model
-{
+#include "utils/underlying_cast.hpp"
 
-class Property
+struct Property
 {
-public:
     using sptr = std::shared_ptr<Property>;
 
-    template <class T, class... Args>
-    static Property::sptr make(Args&&... args)
+    enum class Flags : unsigned
     {
-        return std::shared_ptr<Property>(new T(std::forward<Args>(args)...));
+        Null     = 0x1,
+
+        Bool     = 0x2,
+        True     = 0x4 | Bool,
+        False    = 0x8 | Bool,
+
+        String   = 0x10,
+
+        Number   = 0x20,
+        Integral = 0x40  | Number,
+        Int32    = 0x80  | Integral,
+        Int64    = 0x100 | Integral,
+
+        Floating = 0x200 | Number,
+        Float    = 0x400 | Floating,
+        Double   = 0x800 | Floating,
+
+        Array    = 0x1000,
+
+        type_mask = 0xFFF
+    };
+
+    Property(Flags flags) : flags(flags) {}
+
+    Property(const Property&) = default;
+
+    template <class T>
+    T* is()
+    {
+        return underlying_cast(flags) & T::type;
     }
 
-    Property() = default;
-    virtual ~Property() = default;
-
-    virtual void dump(std::string& buffer) = 0;
-
     template <class T>
     T* as()
     {
-        // return dynamic_cast<T*>(this);
-
-        // http://stackoverflow.com/questions/579887/how-expensive-is-rtti
-        // so... typeid is 20x faster! but there are some caveats, use with
-        // caution. read CAREFULLY what those people are saying.
-        // should be ok to use in this situation because all types used by
-        // this comparison are local and compile together with this code
-        // and we're compiling it only for linux with gcc/clang and we will
-        // not use any classes from third party libraries in this function.
-        if(typeid(T*) == typeid(this))
+        if(this->is<T>())
             return static_cast<T*>(this);
 
         return nullptr;
     }
+
+    template <class Handler>
+    void accept(Handler& handler);
+
+    Flags flags;
 };
 
-template <class T>
-class Value : public Property
+struct Null : public Property
 {
-public:
-    Value(T value) : value(value) {}
-    T value;
+    static constexpr Flags type = Flags::Null;
+
+    Null() : Property(Flags::Null) {}
+
+    bool is_null()
+    {
+        return true;
+    }
 };
 
+struct Bool : public Property
+{
+    static constexpr Flags type = Flags::Bool;
+
+    Bool(bool value) : Property(value ? Flags::True : Flags::False) {}
+
+    bool value()
+    {
+        unsigned flags = underlying_cast(this->flags);
+        unsigned true_t = underlying_cast(Flags::True);
+
+        return (flags - true_t) == 0;
+    }
+};
+
+struct String : public Property
+{
+    static constexpr Flags type = Flags::String;
+
+    String(const std::string& value) 
+        : Property(Flags::String), value(value) {}
+
+    String(std::string&& value)
+        : Property(Flags::String), value(value) {}
+
+    std::string value;
+};
+
+struct Int32 : public Property
+{
+    static constexpr Flags type = Flags::Int32;
+
+    Int32(int32_t value) 
+        : Property(Flags::Int32), value(value) {}
+
+    int32_t value;
+};
+
+struct Int64 : public Property
+{
+    static constexpr Flags type = Flags::Int64;
+
+    Int64(int64_t value) 
+        : Property(Flags::Int64), value(value) {}
+
+    int64_t value;
+};
+
+struct Float : public Property
+{
+    static constexpr Flags type = Flags::Float;
+
+    Float(float value) 
+        : Property(Flags::Float), value(value) {}
+
+    float value;
+};
+
+struct Double : public Property
+{
+    static constexpr Flags type = Flags::Double;
+
+    Double(double value) 
+        : Property(Flags::Double), value(value) {}
+
+    double value;
+};
+
+template <class Handler>
+void Property::accept(Handler& h)
+{
+    switch(flags)
+    {
+        case Flags::Null:   return h.handle(static_cast<Null&>(*this));
+        case Flags::True:   return h.handle(static_cast<Bool&>(*this));
+        case Flags::False:  return h.handle(static_cast<Bool&>(*this));
+        case Flags::String: return h.handle(static_cast<String&>(*this));
+        case Flags::Int32:  return h.handle(static_cast<Int32&>(*this));
+        case Flags::Int64:  return h.handle(static_cast<Int64&>(*this));
+        case Flags::Float:  return h.handle(static_cast<Float&>(*this));
+        case Flags::Double: return h.handle(static_cast<Double&>(*this));
+        default: return;
+    }
 }
 
 #endif
diff --git a/storage/model/properties/string.hpp b/storage/model/properties/string.hpp
deleted file mode 100644
index 49bb82d50..000000000
--- a/storage/model/properties/string.hpp
+++ /dev/null
@@ -1,22 +0,0 @@
-#ifndef MEMGRAPH_STORAGE_MODEL_PROPERTIES_STRING_HPP
-#define MEMGRAPH_STORAGE_MODEL_PROPERTIES_STRING_HPP
-
-#include "property.hpp"
-
-namespace model
-{
-
-class String : public Value<std::string>
-{
-public:
-   using Value::Value;
-
-   void dump(std::string& buffer) override
-   {
-       buffer += '"'; buffer += value; buffer += '"';
-   }
-};
-
-}
-
-#endif
diff --git a/utils/underlying_cast.hpp b/utils/underlying_cast.hpp
new file mode 100644
index 000000000..7b8f1ae17
--- /dev/null
+++ b/utils/underlying_cast.hpp
@@ -0,0 +1,12 @@
+#ifndef MEMGRAPH_UTILS_UNDERLYING_CAST_HPP
+#define MEMGRAPH_UTILS_UNDERLYING_CAST_HPP
+
+#include <type_traits>
+
+template <typename T>
+constexpr typename std::underlying_type<T>::type underlying_cast(T e) {
+    return static_cast<typename std::underlying_type<T>::type>(e);
+}
+
+
+#endif