Continuing major refactor, UNSTABLE STATE. Merged current dev
This commit is contained in:
commit
ee523d5080
@ -145,7 +145,11 @@ class SkipList : private Lockable<lock_t> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static Node *create(const T &item, uint8_t height) {
|
static Node *create(const T &item, uint8_t height) {
|
||||||
return create(height, item);
|
auto node = allocate(height);
|
||||||
|
|
||||||
|
// we have raw memory and we need to construct an object
|
||||||
|
// of type Node on it
|
||||||
|
return new (node) Node(item, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
static Node *create(T &&item, uint8_t height) {
|
static Node *create(T &&item, uint8_t height) {
|
||||||
@ -188,6 +192,10 @@ class SkipList : private Lockable<lock_t> {
|
|||||||
this->data.emplace(std::forward<Args>(args)...);
|
this->data.emplace(std::forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Node(const T &data, uint8_t height) : Node(height) {
|
||||||
|
this->data.set(data);
|
||||||
|
}
|
||||||
|
|
||||||
Node(T &&data, uint8_t height) : Node(height) {
|
Node(T &&data, uint8_t height) : Node(height) {
|
||||||
this->data.set(std::move(data));
|
this->data.set(std::move(data));
|
||||||
}
|
}
|
||||||
@ -940,6 +948,42 @@ class SkipList : private Lockable<lock_t> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Insert unique data
|
||||||
|
*
|
||||||
|
* F - type of funct which will create new node if needed. Recieves height
|
||||||
|
* of node.
|
||||||
|
*/
|
||||||
|
// TODO this code is not DRY w.r.t. the other insert function (rvalue ref)
|
||||||
|
std::pair<Iterator, bool> insert(Node *preds[], Node *succs[], const T &data) {
|
||||||
|
while (true) {
|
||||||
|
// TODO: before here was data.first
|
||||||
|
auto level = find_path(this, H - 1, data, preds, succs);
|
||||||
|
|
||||||
|
if (level != -1) {
|
||||||
|
auto found = succs[level];
|
||||||
|
|
||||||
|
if (found->flags.is_marked()) continue;
|
||||||
|
|
||||||
|
while (!found->flags.is_fully_linked()) usleep(250);
|
||||||
|
|
||||||
|
return {Iterator{succs[level]}, false};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto height = rnd();
|
||||||
|
guard_t guards[H];
|
||||||
|
|
||||||
|
// try to acquire the locks for predecessors up to the height of
|
||||||
|
// the new node. release the locks and try again if someone else
|
||||||
|
// has the locks
|
||||||
|
if (!lock_nodes<true>(height, guards, preds, succs)) continue;
|
||||||
|
|
||||||
|
return {insert_here(Node::create(data, height), preds, succs,
|
||||||
|
height, guards),
|
||||||
|
true};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Insert unique data
|
* Insert unique data
|
||||||
*
|
*
|
||||||
|
117
include/storage/model/typed_value.hpp
Normal file
117
include/storage/model/typed_value.hpp
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <cassert>
|
||||||
|
#include <iostream>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "utils/underlying_cast.hpp"
|
||||||
|
#include "utils/total_ordering.hpp"
|
||||||
|
#include "utils/exceptions/basic_exception.hpp"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encapsulation of a value and it's type encapsulated in a class that has no
|
||||||
|
* compiled-time info about that type.
|
||||||
|
*
|
||||||
|
* Values can be of a number of predefined types that are enumerated in
|
||||||
|
* TypedValue::Type. Each such type corresponds to exactly one C++ type.
|
||||||
|
*/
|
||||||
|
class TypedValue : public TotalOrdering<TypedValue, TypedValue, TypedValue> {
|
||||||
|
|
||||||
|
private:
|
||||||
|
/** Private default constructor, makes Null */
|
||||||
|
TypedValue() : type_(Type::Null) {}
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
/** A value type. Each type corresponds to exactly one C++ type */
|
||||||
|
enum class Type : unsigned {
|
||||||
|
Null,
|
||||||
|
String,
|
||||||
|
Bool,
|
||||||
|
Int,
|
||||||
|
Float
|
||||||
|
};
|
||||||
|
|
||||||
|
// single static reference to Null, used whenever Null should be returned
|
||||||
|
static const TypedValue Null;
|
||||||
|
|
||||||
|
// constructors for primitive types
|
||||||
|
TypedValue(bool value) : type_(Type::Bool) { bool_v = value; }
|
||||||
|
TypedValue(int value) : type_(Type::Int) { int_v = value; }
|
||||||
|
TypedValue(float value) : type_(Type::Float) { float_v = value; }
|
||||||
|
|
||||||
|
/// constructors for non-primitive types (shared pointers)
|
||||||
|
TypedValue(const std::string &value) : type_(Type::String) {
|
||||||
|
new (&string_v) std::shared_ptr<std::string>(new std::string(value));
|
||||||
|
}
|
||||||
|
TypedValue(const char* value) : type_(Type::String) {
|
||||||
|
new (&string_v) std::shared_ptr<std::string>(new std::string(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
// assignment ops
|
||||||
|
TypedValue& operator=(TypedValue& other);
|
||||||
|
TypedValue& operator=(TypedValue&& other);
|
||||||
|
|
||||||
|
TypedValue(const TypedValue& other);
|
||||||
|
~TypedValue();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the value of the property as given type T.
|
||||||
|
* The behavior of this function is undefined if
|
||||||
|
* T does not correspond to this property's type_.
|
||||||
|
*
|
||||||
|
* @tparam T Type to interpret the value as.
|
||||||
|
* @return The value as type T.
|
||||||
|
*/
|
||||||
|
template <typename T> T Value() const;
|
||||||
|
|
||||||
|
friend std::ostream& operator<<(std::ostream& stream, const TypedValue& prop);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Type of property.
|
||||||
|
*/
|
||||||
|
const Type type_;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// storage for the value of the property
|
||||||
|
union {
|
||||||
|
bool bool_v;
|
||||||
|
int int_v;
|
||||||
|
float float_v;
|
||||||
|
std::shared_ptr<std::string> string_v;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An exception raised by the TypedValue system. Typically when
|
||||||
|
* trying to perform operations (such as addition) on TypedValues
|
||||||
|
* of incompatible Types.
|
||||||
|
*/
|
||||||
|
class TypedValueException : public BasicException {
|
||||||
|
|
||||||
|
public:
|
||||||
|
using ::BasicException::BasicException;
|
||||||
|
};
|
||||||
|
|
||||||
|
// comparison operators
|
||||||
|
// they return TypedValue because Null can be returned
|
||||||
|
TypedValue operator==(const TypedValue& a, const TypedValue& b);
|
||||||
|
TypedValue operator<(const TypedValue& a, const TypedValue& b);
|
||||||
|
TypedValue operator!(const TypedValue& a);
|
||||||
|
|
||||||
|
// arithmetic operators
|
||||||
|
TypedValue operator-(const TypedValue& a);
|
||||||
|
TypedValue operator+(const TypedValue& a, const TypedValue& b);
|
||||||
|
TypedValue operator-(const TypedValue& a, const TypedValue& b);
|
||||||
|
TypedValue operator/(const TypedValue& a, const TypedValue& b);
|
||||||
|
TypedValue operator*(const TypedValue& a, const TypedValue& b);
|
||||||
|
TypedValue operator%(const TypedValue& a, const TypedValue& b);
|
||||||
|
|
||||||
|
// binary bool operators
|
||||||
|
TypedValue operator&&(const TypedValue& a, const TypedValue& b);
|
||||||
|
TypedValue operator||(const TypedValue& a, const TypedValue& b);
|
||||||
|
|
||||||
|
// stream output
|
||||||
|
std::ostream &operator<<(std::ostream &os, const TypedValue::Type type);
|
82
include/storage/model/typed_value_store.hpp
Normal file
82
include/storage/model/typed_value_store.hpp
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
#include "typed_value.hpp"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A collection of properties accessed in a map-like way
|
||||||
|
* using a key of type Properties::TKey.
|
||||||
|
*
|
||||||
|
* The underlying implementation is not necessarily std::map.
|
||||||
|
*/
|
||||||
|
class TypedValueStore {
|
||||||
|
public:
|
||||||
|
using sptr = std::shared_ptr<TypedValueStore>;
|
||||||
|
|
||||||
|
/** The type of key used to get and set properties */
|
||||||
|
using TKey = uint32_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a TypedValue (by reference) at the given key.
|
||||||
|
* If the key does not exist, the Null property is returned.
|
||||||
|
*
|
||||||
|
* This is NOT thread-safe, the reference might not be valid
|
||||||
|
* when used in a multithreaded scenario.
|
||||||
|
*
|
||||||
|
* @param key The key for which a TypedValue is sought.
|
||||||
|
* @return See above.
|
||||||
|
*/
|
||||||
|
const TypedValue &at(const TKey &key) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the value for the given key. A new TypedValue instance
|
||||||
|
* is created for the given value (which is of raw type).
|
||||||
|
*
|
||||||
|
* @tparam TValue Type of value. It must be one of the
|
||||||
|
* predefined possible TypedValue values (bool, string, int...)
|
||||||
|
* @param key The key for which the property is set. The previous
|
||||||
|
* value at the same key (if there was one) is replaced.
|
||||||
|
* @param value The value to set.
|
||||||
|
*/
|
||||||
|
template<typename TValue>
|
||||||
|
void set(const TKey &key, const TValue &value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set overriding for character constants. Forces conversion
|
||||||
|
* to std::string, otherwise templating might cast the pointer
|
||||||
|
* to something else (bool) and mess things up.
|
||||||
|
*
|
||||||
|
* @param key The key for which the property is set. The previous
|
||||||
|
* value at the same key (if there was one) is replaced.
|
||||||
|
* @param value The value to set.
|
||||||
|
*/
|
||||||
|
void set(const TKey &key, const char *value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the TypedValue for the given key.
|
||||||
|
*
|
||||||
|
* @param key The key for which to remove the property.
|
||||||
|
* @return The number of removed properties (0 or 1).
|
||||||
|
*/
|
||||||
|
size_t erase(const TKey &key);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The number of Properties in this collection.
|
||||||
|
*/
|
||||||
|
size_t size() const;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Accepts two functions.
|
||||||
|
*
|
||||||
|
* @param handler Called for each TypedValue in this collection.
|
||||||
|
* @param finish Called once in the end.
|
||||||
|
*/
|
||||||
|
void Accept(std::function<void(const TKey key, const TypedValue& prop)> handler,
|
||||||
|
std::function<void()> finish = {}) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<std::pair<TKey, TypedValue>> props_;
|
||||||
|
};
|
57
include/storage/model/typed_value_utils.h
Normal file
57
include/storage/model/typed_value_utils.h
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
//
|
||||||
|
// Copyright 2017 Memgraph
|
||||||
|
// Created by Florijan Stamenkovic on 01.02.17.
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <ostream>
|
||||||
|
#include "storage/model/typed_value_store.hpp"
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes all of the values from the given store in JSON format
|
||||||
|
* to the given output stream.
|
||||||
|
*
|
||||||
|
* @param store The store that should be serialized to JSON.
|
||||||
|
* @param ostream The stream to write to.
|
||||||
|
*/
|
||||||
|
void TypedValuesToJson(const TypedValueStore& store,
|
||||||
|
std::ostream& ostream=std::cout) {
|
||||||
|
|
||||||
|
bool first = true;
|
||||||
|
|
||||||
|
auto write_key = [&ostream, &first](const TypedValueStore::TKey &key) -> std::ostream& {
|
||||||
|
if (first) {
|
||||||
|
ostream << '{';
|
||||||
|
first = false;
|
||||||
|
} else
|
||||||
|
ostream << ',';
|
||||||
|
|
||||||
|
return ostream << '"' << key << "\":";
|
||||||
|
};
|
||||||
|
|
||||||
|
auto handler = [&ostream, &write_key](const TypedValueStore::TKey& key,
|
||||||
|
const TypedValue& value) {
|
||||||
|
switch (value.type_) {
|
||||||
|
case TypedValue::Type::Null:
|
||||||
|
break;
|
||||||
|
case TypedValue::Type::Bool:
|
||||||
|
write_key(key) << (value.Value<bool>() ? "true" : "false");
|
||||||
|
break;
|
||||||
|
case TypedValue::Type::String:
|
||||||
|
write_key(key) << '"' << value.Value<std::string>() << '"';
|
||||||
|
break;
|
||||||
|
case TypedValue::Type::Int:
|
||||||
|
write_key(key) << value.Value<int>();
|
||||||
|
break;
|
||||||
|
case TypedValue::Type::Float:
|
||||||
|
write_key(key) << value.Value<float>();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
auto finish = [&ostream]() { ostream << '}' << std::endl; };
|
||||||
|
|
||||||
|
store.Accept(handler, finish);
|
||||||
|
}
|
363
src/storage/model/typed_value.cpp
Normal file
363
src/storage/model/typed_value.cpp
Normal file
@ -0,0 +1,363 @@
|
|||||||
|
#include <memory>
|
||||||
|
#include <iostream>
|
||||||
|
#include <cmath>
|
||||||
|
#include <fmt/format.h>
|
||||||
|
|
||||||
|
#include "storage/model/typed_value.hpp"
|
||||||
|
#include "utils/assert.hpp"
|
||||||
|
|
||||||
|
// Value extraction template instantiations
|
||||||
|
template<>
|
||||||
|
bool TypedValue::Value<bool>() const {
|
||||||
|
runtime_assert(type_ == TypedValue::Type::Bool, "Incompatible template param and type");
|
||||||
|
return bool_v;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<>
|
||||||
|
std::string TypedValue::Value<std::string>() const {
|
||||||
|
runtime_assert(type_ == TypedValue::Type::String, "Incompatible template param and type");
|
||||||
|
return *string_v;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<>
|
||||||
|
int TypedValue::Value<int>() const {
|
||||||
|
runtime_assert(type_ == TypedValue::Type::Int, "Incompatible template param and type");
|
||||||
|
return int_v;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<>
|
||||||
|
float TypedValue::Value<float>() const {
|
||||||
|
runtime_assert(type_ == TypedValue::Type::Float, "Incompatible template param and type");
|
||||||
|
return float_v;
|
||||||
|
}
|
||||||
|
|
||||||
|
TypedValue::TypedValue(const TypedValue &other) : type_(other.type_) {
|
||||||
|
switch (other.type_) {
|
||||||
|
case TypedValue::Type::Null:
|
||||||
|
return;
|
||||||
|
|
||||||
|
case TypedValue::Type::Bool:
|
||||||
|
this->bool_v = other.bool_v;
|
||||||
|
return;
|
||||||
|
|
||||||
|
case TypedValue::Type::String:
|
||||||
|
new(&string_v) std::shared_ptr<std::string>(other.string_v);
|
||||||
|
return;
|
||||||
|
|
||||||
|
case Type::Int:
|
||||||
|
this->int_v = other.int_v;
|
||||||
|
return;
|
||||||
|
|
||||||
|
case Type::Float:
|
||||||
|
this->float_v = other.float_v;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
permanent_fail("Unsupported TypedValue::Type");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostream &operator<<(std::ostream &os, const TypedValue::Type type) {
|
||||||
|
switch (type) {
|
||||||
|
case TypedValue::Type::Null:
|
||||||
|
return os << "null";
|
||||||
|
case TypedValue::Type::Bool:
|
||||||
|
return os << "bool";
|
||||||
|
case TypedValue::Type::String:
|
||||||
|
return os << "string";
|
||||||
|
case TypedValue::Type::Int:
|
||||||
|
return os << "int";
|
||||||
|
case TypedValue::Type::Float:
|
||||||
|
return os << "float";
|
||||||
|
}
|
||||||
|
permanent_fail("Unsupported TypedValue::Type");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostream &operator<<(std::ostream &os, const TypedValue &property) {
|
||||||
|
switch (property.type_) {
|
||||||
|
case TypedValue::Type::Null:
|
||||||
|
return os << "Null";
|
||||||
|
case TypedValue::Type::Bool:
|
||||||
|
return os << (property.Value<bool>() ? "true" : "false");
|
||||||
|
case TypedValue::Type::String:
|
||||||
|
return os << property.Value<std::string>();
|
||||||
|
case TypedValue::Type::Int:
|
||||||
|
return os << property.Value<int>();
|
||||||
|
case TypedValue::Type::Float:
|
||||||
|
return os << property.Value<float>();
|
||||||
|
}
|
||||||
|
permanent_fail("Unsupported TypedValue::Type");
|
||||||
|
}
|
||||||
|
|
||||||
|
TypedValue &TypedValue::operator=(TypedValue &&other) {
|
||||||
|
|
||||||
|
// set the type of this
|
||||||
|
const_cast<Type&>(type_) = other.type_;
|
||||||
|
|
||||||
|
if (this != &other) {
|
||||||
|
switch (other.type_) {
|
||||||
|
case TypedValue::Type::Null:
|
||||||
|
case TypedValue::Type::Bool:
|
||||||
|
this->bool_v = other.bool_v;
|
||||||
|
return *this;
|
||||||
|
case TypedValue::Type::String:
|
||||||
|
this->string_v = std::move(other.string_v);
|
||||||
|
return *this;
|
||||||
|
case TypedValue::Type::Int:
|
||||||
|
this->int_v = other.int_v;
|
||||||
|
return *this;
|
||||||
|
case TypedValue::Type::Float:
|
||||||
|
this->float_v = other.float_v;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
permanent_fail("Unsupported TypedValue::Type");
|
||||||
|
}
|
||||||
|
|
||||||
|
const TypedValue TypedValue::Null = TypedValue();
|
||||||
|
|
||||||
|
TypedValue::~TypedValue() {
|
||||||
|
|
||||||
|
switch (type_) {
|
||||||
|
// destructor for primitive types does nothing
|
||||||
|
case Type::Null:
|
||||||
|
case Type::Bool:
|
||||||
|
case Type::Int:
|
||||||
|
case Type::Float:
|
||||||
|
return;
|
||||||
|
|
||||||
|
// destructor for shared pointer must release
|
||||||
|
case Type::String:
|
||||||
|
string_v.~shared_ptr<std::string>();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
permanent_fail("Unsupported TypedValue::Type");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the float value of a property.
|
||||||
|
* The property MUST be either Float or Int.
|
||||||
|
*
|
||||||
|
* @param prop
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
float ToFloat(const TypedValue& prop) {
|
||||||
|
switch (prop.type_) {
|
||||||
|
case TypedValue::Type::Int:
|
||||||
|
return (float)prop.Value<int>();
|
||||||
|
case TypedValue::Type::Float:
|
||||||
|
return prop.Value<float>();
|
||||||
|
|
||||||
|
default:
|
||||||
|
permanent_fail("Unsupported TypedValue::Type");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TypedValue operator<(const TypedValue& a, const TypedValue& b) {
|
||||||
|
if (a.type_ == TypedValue::Type::Bool || b.type_ == TypedValue::Type::Bool)
|
||||||
|
throw TypedValueException("Invalid 'less' operand types({} + {})", a.type_, b.type_);
|
||||||
|
|
||||||
|
if (a.type_ == TypedValue::Type::Null || b.type_ == TypedValue::Type::Null)
|
||||||
|
return TypedValue::Null;
|
||||||
|
|
||||||
|
if (a.type_ == TypedValue::Type::String || b.type_ == TypedValue::Type::String) {
|
||||||
|
if (a.type_ != b.type_)
|
||||||
|
throw TypedValueException("Invalid equality operand types({} + {})", a.type_, b.type_);
|
||||||
|
else
|
||||||
|
return a.Value<std::string>() < b.Value<std::string>();
|
||||||
|
}
|
||||||
|
|
||||||
|
// at this point we only have int and float
|
||||||
|
if (a.type_ == TypedValue::Type::Float || b.type_ == TypedValue::Type::Float)
|
||||||
|
return ToFloat(a) < ToFloat(b);
|
||||||
|
else
|
||||||
|
return a.Value<int>() < b.Value<int>();
|
||||||
|
}
|
||||||
|
|
||||||
|
TypedValue operator==(const TypedValue& a, const TypedValue& b) {
|
||||||
|
|
||||||
|
if (a.type_ == TypedValue::Type::Null || b.type_ == TypedValue::Type::Null)
|
||||||
|
return TypedValue::Null;
|
||||||
|
|
||||||
|
if (a.type_ == TypedValue::Type::String || b.type_ == TypedValue::Type::String) {
|
||||||
|
if (a.type_ != b.type_)
|
||||||
|
throw TypedValueException("Invalid equality operand types({} + {})", a.type_, b.type_);
|
||||||
|
else
|
||||||
|
return a.Value<std::string>() == b.Value<std::string>();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (a.type_ == TypedValue::Type::Bool || b.type_ == TypedValue::Type::Bool) {
|
||||||
|
if (a.type_ != b.type_)
|
||||||
|
throw TypedValueException("Invalid equality operand types({} + {})", a.type_, b.type_);
|
||||||
|
else
|
||||||
|
return a.Value<bool>() == b.Value<bool>();
|
||||||
|
}
|
||||||
|
// at this point we only have int and float
|
||||||
|
if (a.type_ == TypedValue::Type::Float || b.type_ == TypedValue::Type::Float){
|
||||||
|
return ToFloat(a) == ToFloat(b);
|
||||||
|
} else
|
||||||
|
return a.Value<int>() == b.Value<int>();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
TypedValue operator!(const TypedValue& a) {
|
||||||
|
switch (a.type_) {
|
||||||
|
case TypedValue::Type::Null:
|
||||||
|
return TypedValue::Null;
|
||||||
|
case TypedValue::Type::Bool:
|
||||||
|
return TypedValue(!a.Value<bool>());
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw TypedValueException("Invalid logical not operand type (!{})", a.type_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Turns a numeric or string property into a string.
|
||||||
|
*
|
||||||
|
* @param prop Property.
|
||||||
|
* @return A string.
|
||||||
|
*/
|
||||||
|
std::string PropToString(const TypedValue& prop) {
|
||||||
|
switch (prop.type_) {
|
||||||
|
case TypedValue::Type::String:
|
||||||
|
return prop.Value<std::string>();
|
||||||
|
case TypedValue::Type::Int:
|
||||||
|
return std::to_string(prop.Value<int>());
|
||||||
|
case TypedValue::Type::Float:
|
||||||
|
return fmt::format("{}", prop.Value<float>());
|
||||||
|
|
||||||
|
// unsupported situations
|
||||||
|
default:
|
||||||
|
permanent_fail("Unsupported TypedValue::Type");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TypedValue operator-(const TypedValue &a) {
|
||||||
|
switch (a.type_) {
|
||||||
|
case TypedValue::Type::Null:
|
||||||
|
return TypedValue::Null;
|
||||||
|
case TypedValue::Type::Int:
|
||||||
|
return -a.Value<int>();
|
||||||
|
case TypedValue::Type::Float:
|
||||||
|
return -a.Value<float>();
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw TypedValueException("Invalid unary minus operand type (-{})", a.type_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Raises a PropertyException if the given properties do not support arithmetic
|
||||||
|
* operations. If they do, nothing happens.
|
||||||
|
*
|
||||||
|
* @param a First prop.
|
||||||
|
* @param b Second prop.
|
||||||
|
* @param string_ok If or not for the given operation it's valid to work with
|
||||||
|
* String values (typically it's OK only for sum).
|
||||||
|
* @param op_name Name of the operation, used only for exception description,
|
||||||
|
* if raised.
|
||||||
|
*/
|
||||||
|
inline void EnsureArithmeticallyOk(const TypedValue& a, const TypedValue& b,
|
||||||
|
bool string_ok, const std::string& op_name) {
|
||||||
|
if (a.type_ == TypedValue::Type::Bool || b.type_ == TypedValue::Type::Bool)
|
||||||
|
throw TypedValueException("Invalid {} operand types {}, {}", op_name, a.type_, b.type_);
|
||||||
|
|
||||||
|
if (string_ok)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (a.type_ == TypedValue::Type::String || b.type_ == TypedValue::Type::String)
|
||||||
|
throw TypedValueException("Invalid subtraction operands types {}, {}", a.type_, b.type_);
|
||||||
|
}
|
||||||
|
|
||||||
|
TypedValue operator+(const TypedValue& a, const TypedValue& b){
|
||||||
|
EnsureArithmeticallyOk(a, b, true, "addition");
|
||||||
|
if (a.type_ == TypedValue::Type::Null || b.type_ == TypedValue::Type::Null)
|
||||||
|
return TypedValue::Null;
|
||||||
|
|
||||||
|
// no more Bool nor Null, summing works on anything from here onward
|
||||||
|
|
||||||
|
if (a.type_ == TypedValue::Type::String || b.type_ == TypedValue::Type::String)
|
||||||
|
return PropToString(a) + PropToString(b);
|
||||||
|
|
||||||
|
// at this point we only have int and float
|
||||||
|
if (a.type_ == TypedValue::Type::Float || b.type_ == TypedValue::Type::Float){
|
||||||
|
return ToFloat(a) + ToFloat(b);
|
||||||
|
} else
|
||||||
|
return a.Value<int>() + b.Value<int>();
|
||||||
|
}
|
||||||
|
|
||||||
|
TypedValue operator-(const TypedValue& a, const TypedValue& b){
|
||||||
|
EnsureArithmeticallyOk(a, b, false, "subtraction");
|
||||||
|
|
||||||
|
if (a.type_ == TypedValue::Type::Null || b.type_ == TypedValue::Type::Null)
|
||||||
|
return TypedValue::Null;
|
||||||
|
|
||||||
|
// at this point we only have int and float
|
||||||
|
if (a.type_ == TypedValue::Type::Float || b.type_ == TypedValue::Type::Float){
|
||||||
|
return ToFloat(a) - ToFloat(b);
|
||||||
|
} else
|
||||||
|
return a.Value<int>() - b.Value<int>();
|
||||||
|
}
|
||||||
|
|
||||||
|
TypedValue operator/(const TypedValue& a, const TypedValue& b){
|
||||||
|
EnsureArithmeticallyOk(a, b, false, "division");
|
||||||
|
|
||||||
|
if (a.type_ == TypedValue::Type::Null || b.type_ == TypedValue::Type::Null)
|
||||||
|
return TypedValue::Null;
|
||||||
|
|
||||||
|
// at this point we only have int and float
|
||||||
|
if (a.type_ == TypedValue::Type::Float || b.type_ == TypedValue::Type::Float){
|
||||||
|
return ToFloat(a) / ToFloat(b);
|
||||||
|
} else
|
||||||
|
return a.Value<int>() / b.Value<int>();
|
||||||
|
}
|
||||||
|
|
||||||
|
TypedValue operator*(const TypedValue& a, const TypedValue& b){
|
||||||
|
EnsureArithmeticallyOk(a, b, false, "multiplication");
|
||||||
|
|
||||||
|
if (a.type_ == TypedValue::Type::Null || b.type_ == TypedValue::Type::Null)
|
||||||
|
return TypedValue::Null;
|
||||||
|
|
||||||
|
// at this point we only have int and float
|
||||||
|
if (a.type_ == TypedValue::Type::Float || b.type_ == TypedValue::Type::Float){
|
||||||
|
return ToFloat(a) * ToFloat(b);
|
||||||
|
} else
|
||||||
|
return a.Value<int>() * b.Value<int>();
|
||||||
|
}
|
||||||
|
|
||||||
|
TypedValue operator%(const TypedValue& a, const TypedValue& b){
|
||||||
|
EnsureArithmeticallyOk(a, b, false, "modulo");
|
||||||
|
|
||||||
|
if (a.type_ == TypedValue::Type::Null || b.type_ == TypedValue::Type::Null)
|
||||||
|
return TypedValue::Null;
|
||||||
|
|
||||||
|
// at this point we only have int and float
|
||||||
|
if (a.type_ == TypedValue::Type::Float || b.type_ == TypedValue::Type::Float){
|
||||||
|
return (float)fmod(ToFloat(a), ToFloat(b));
|
||||||
|
} else
|
||||||
|
return a.Value<int>() % b.Value<int>();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool IsLogicallyOk(const TypedValue& a) {
|
||||||
|
return a.type_ == TypedValue::Type::Bool || a.type_ == TypedValue::Type::Null;
|
||||||
|
}
|
||||||
|
|
||||||
|
TypedValue operator&&(const TypedValue& a, const TypedValue& b) {
|
||||||
|
if(IsLogicallyOk(a) && IsLogicallyOk(b)){
|
||||||
|
if (a.type_ == TypedValue::Type::Null || b.type_ == TypedValue::Type::Null)
|
||||||
|
return TypedValue::Null;
|
||||||
|
else
|
||||||
|
return a.Value<bool>() && b.Value<bool>();
|
||||||
|
} else
|
||||||
|
throw TypedValueException("Invalid logical and operand types({} && {})", a.type_, b.type_);
|
||||||
|
}
|
||||||
|
|
||||||
|
TypedValue operator||(const TypedValue& a, const TypedValue& b) {
|
||||||
|
if(IsLogicallyOk(a) && IsLogicallyOk(b)){
|
||||||
|
if (a.type_ == TypedValue::Type::Null || b.type_ == TypedValue::Type::Null)
|
||||||
|
return TypedValue::Null;
|
||||||
|
else
|
||||||
|
return a.Value<bool>() || b.Value<bool>();
|
||||||
|
} else
|
||||||
|
throw TypedValueException("Invalid logical and operand types({} && {})", a.type_, b.type_);
|
||||||
|
}
|
68
src/storage/model/typed_value_store.cpp
Normal file
68
src/storage/model/typed_value_store.cpp
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
#include "storage/model/typed_value_store.hpp"
|
||||||
|
|
||||||
|
const TypedValue& TypedValueStore::at(const TKey &key) const {
|
||||||
|
for (const auto& kv : props_)
|
||||||
|
if (kv.first == key)
|
||||||
|
return kv.second;
|
||||||
|
|
||||||
|
return TypedValue::Null;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename TValue>
|
||||||
|
void TypedValueStore::set(const TKey &key, const TValue &value) {
|
||||||
|
for (auto& kv: props_)
|
||||||
|
if (kv.first == key) {
|
||||||
|
kv.second = TypedValue(value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// there is no value for the given key, add new
|
||||||
|
// TODO consider vector size increment optimization
|
||||||
|
props_.push_back(std::move(std::make_pair(key, value)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// instantiations of the TypedValueStore::set function
|
||||||
|
// instances must be made for all of the supported C++ types
|
||||||
|
template void TypedValueStore::set<std::string>(const TKey &key, const std::string &value);
|
||||||
|
template void TypedValueStore::set<bool>(const TKey &key, const bool &value);
|
||||||
|
template void TypedValueStore::set<int>(const TKey &key, const int &value);
|
||||||
|
template void TypedValueStore::set<float>(const TKey &key, const float &value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set overriding for character constants. Forces conversion
|
||||||
|
* to std::string, otherwise templating might cast the pointer
|
||||||
|
* to something else (bool) and mess things up.
|
||||||
|
*
|
||||||
|
* @param key The key for which the property is set. The previous
|
||||||
|
* value at the same key (if there was one) is replaced.
|
||||||
|
* @param value The value to set.
|
||||||
|
*/
|
||||||
|
void TypedValueStore::set(const TKey &key, const char *value) {
|
||||||
|
set(key, std::string(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t TypedValueStore::erase(const TKey &key) {
|
||||||
|
auto found = std::find_if(props_.begin(), props_.end(), [&key](std::pair<TKey, TypedValue> &kv){return kv.first == key;});
|
||||||
|
if (found != props_.end()) {
|
||||||
|
props_.erase(found);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t TypedValueStore::size() const { return props_.size(); }
|
||||||
|
|
||||||
|
void TypedValueStore::Accept(std::function<void(const TypedValueStore::TKey, const TypedValue &)> handler,
|
||||||
|
std::function<void()> finish) const {
|
||||||
|
if (handler)
|
||||||
|
for (const auto& prop : props_)
|
||||||
|
handler(prop.first, prop.second);
|
||||||
|
|
||||||
|
if (finish)
|
||||||
|
finish();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -15,7 +15,7 @@ int main()
|
|||||||
init_log();
|
init_log();
|
||||||
|
|
||||||
memory_check(THREADS_NO, [] {
|
memory_check(THREADS_NO, [] {
|
||||||
set_t skiplist;
|
ConcurrentSet<std::string> skiplist;
|
||||||
|
|
||||||
auto futures = run<std::vector<long>>(
|
auto futures = run<std::vector<long>>(
|
||||||
THREADS_NO, skiplist, [](auto acc, auto index) {
|
THREADS_NO, skiplist, [](auto acc, auto index) {
|
||||||
@ -25,14 +25,16 @@ int main()
|
|||||||
std::vector<long> set(key_range);
|
std::vector<long> set(key_range);
|
||||||
|
|
||||||
do {
|
do {
|
||||||
size_t num = rand();
|
int num = rand();
|
||||||
|
std::string num_str = std::to_string(num);
|
||||||
if (rand_op()) {
|
if (rand_op()) {
|
||||||
if (acc.remove(num)) {
|
if (acc.remove(num_str)) {
|
||||||
downcount--;
|
downcount--;
|
||||||
set[num]--;
|
set[num]--;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (acc.insert(num).second) {
|
std::string num_str = std::to_string(num);
|
||||||
|
if (acc.insert(num_str).second) {
|
||||||
downcount--;
|
downcount--;
|
||||||
set[num]++;
|
set[num]++;
|
||||||
}
|
}
|
||||||
@ -52,12 +54,12 @@ int main()
|
|||||||
auto accessor = skiplist.access();
|
auto accessor = skiplist.access();
|
||||||
for (int i = 0; i < key_range; i++) {
|
for (int i = 0; i < key_range; i++) {
|
||||||
permanent_assert(set[i] == 0 || set[i] == 1 ||
|
permanent_assert(set[i] == 0 || set[i] == 1 ||
|
||||||
(set[i] == 1) ^ accessor.contains(i),
|
(set[i] == 1) ^ accessor.contains(std::to_string(i)),
|
||||||
"Set doesn't hold it's guarantees.");
|
"Set doesn't hold it's guarantees.");
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto &e : accessor) {
|
for (auto &e : accessor) {
|
||||||
set[e]--;
|
set[std::stoi(e)]--;
|
||||||
}
|
}
|
||||||
|
|
||||||
check_zero(key_range, set, "Set");
|
check_zero(key_range, set, "Set");
|
||||||
|
Loading…
Reference in New Issue
Block a user