/* * Copyright (c) 2017-2024 OARC, Inc. * Copyright (c) 2011-2017, IIS - The Internet Foundation in Sweden * All rights reserved. * * This file is part of PacketQ. * * PacketQ is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * PacketQ is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with PacketQ. If not, see . */ #ifndef __packetq_variant_h #define __packetq_variant_h #include #include "MurmurHash3.h" #include "refcountstring.h" namespace packetq { inline std::size_t hash_bytes(const char* bytes, int len) { uint32_t result = 0; MurmurHash3_x86_32(bytes, len, 0, &result); return result; } // must be defined in this order - see the "if" statement #define COLTYPE_MAX 4 namespace Coltype { enum Type { _bool = 0, _int = 1, _float = 2, _text = 3, }; }; typedef bool bool_column; static const int bool_size = sizeof(bool_column); static const int bool_align = ((sizeof(bool_column) / sizeof(void*)) + 1) * sizeof(void*); typedef int int_column; static const int int_size = sizeof(int_column); static const int int_align = ((sizeof(int_column) / sizeof(void*)) + 1) * sizeof(void*); typedef double float_column; static const int float_size = sizeof(float_column); static const int float_align = ((sizeof(float_column) / sizeof(void*)) + 1) * sizeof(void*); typedef RefCountString* text_column; static const int text_size = sizeof(text_column); static const int text_align = ((sizeof(text_column) / sizeof(void*)) + 1) * sizeof(void*); inline bool_column convert_column_to_bool(float_column v) { return v; } inline bool_column convert_column_to_bool(int_column v) { return v; } inline bool_column convert_column_to_bool(bool_column v) { return v; } inline bool_column convert_column_to_bool(text_column v) { return std::atoi(v->data); } inline int_column convert_column_to_int(float_column v) { return int(v); } inline int_column convert_column_to_int(int_column v) { return v; } inline int_column convert_column_to_int(bool_column v) { return v; } inline int_column convert_column_to_int(text_column v) { return v->data[0] != '\0'; } inline float_column convert_column_to_float(float_column v) { return v; } inline float_column convert_column_to_float(int_column v) { return v; } inline float_column convert_column_to_float(bool_column v) { return v; } inline float_column convert_column_to_float(text_column v) { return std::atof(v->data); } inline text_column convert_column_to_text(float_column v) { const int bufsize = 50; RefCountString* str = RefCountString::allocate(bufsize); snprintf(str->data, bufsize, "%g", v); // lgtm[cpp/badly-bounded-write] return str; } inline text_column convert_column_to_text(int_column v) { const int bufsize = (sizeof(int_column) * 8 + 1) / 3 + 1; RefCountString* str = RefCountString::allocate(bufsize); snprintf(str->data, bufsize, "%d", v); // lgtm[cpp/badly-bounded-write] return str; } inline text_column convert_column_to_text(bool_column v) { const int bufsize = 1 + 1; RefCountString* str = RefCountString::allocate(bufsize); if (v) str->data[0] = '1'; else str->data[1] = '0'; str->data[1] = '\0'; return str; } inline text_column convert_column_to_text(text_column v) { // to stay symmetric with above functions that allocate a new string, // increment reference count v->inc_refcount(); return v; } // Variant represents a value that can be either one of the column types, // plus a type field to figure out which kind it represents class Variant { public: Variant() { m_type = Coltype::_int; m_val.m_int = 0; } Variant(bool_column val) { m_type = Coltype::_bool; m_val.m_bool = val; } Variant(int_column val) { m_type = Coltype::_int; m_val.m_int = val; } Variant(float_column val) { m_type = Coltype::_float; m_val.m_float = val; } Variant(text_column val) { m_type = Coltype::_text; m_val.m_text = val; m_val.m_text->inc_refcount(); } Variant(const Variant& other) { m_type = other.m_type; m_val = other.m_val; if (m_type == Coltype::_text) m_val.m_text->inc_refcount(); } // move constructor Variant(Variant&& other) { // would be cleaner to use default constructor, but alas // constructor delegation requires GCC >= 4.7 m_type = Coltype::_int; m_val.m_int = 0; swap(*this, other); } ~Variant() { if (m_type == Coltype::_text) m_val.m_text->dec_refcount(); } Variant& operator=(Variant other) { // copy and swap idiom swap(*this, other); return *this; } inline friend void swap(Variant& first, Variant& second) { using std::swap; swap(first.m_type, second.m_type); swap(first.m_val, second.m_val); } bool_column get_bool() const { switch (m_type) { case Coltype::_float: return convert_column_to_bool(m_val.m_float); case Coltype::_int: return convert_column_to_bool(m_val.m_int); case Coltype::_bool: return convert_column_to_bool(m_val.m_bool); case Coltype::_text: return convert_column_to_bool(m_val.m_text); } return false; } int_column get_int() const { switch (m_type) { case Coltype::_float: return convert_column_to_int(m_val.m_float); case Coltype::_int: return convert_column_to_int(m_val.m_int); case Coltype::_bool: return convert_column_to_int(m_val.m_bool); case Coltype::_text: return convert_column_to_int(m_val.m_text); } return 0; } float_column get_float() const { switch (m_type) { case Coltype::_float: return convert_column_to_float(m_val.m_float); case Coltype::_int: return convert_column_to_float(m_val.m_int); case Coltype::_bool: return convert_column_to_float(m_val.m_bool); case Coltype::_text: return convert_column_to_float(m_val.m_text); } return 0.0; } // this returns a RefCountString with the ref-count incremented so // caller is responsible for decrementing after use text_column get_text() const { switch (m_type) { case Coltype::_float: return convert_column_to_text(m_val.m_float); case Coltype::_int: return convert_column_to_text(m_val.m_int); case Coltype::_bool: return convert_column_to_text(m_val.m_bool); case Coltype::_text: return convert_column_to_text(m_val.m_text); } return RefCountString::construct(""); } int cmp(const Variant& rhs) const { switch (m_type) { case (Coltype::_bool): return m_val.m_bool - rhs.get_bool(); case (Coltype::_int): return m_val.m_int - rhs.get_int(); case (Coltype::_float): { float_column r = rhs.get_float(); if (m_val.m_float < r) return -1; if (m_val.m_float > r) return 1; return 0; } case (Coltype::_text): { RefCountString* s = rhs.get_text(); auto res = strcmp(m_val.m_text->data, s->data); s->dec_refcount(); return res; } } return 0; } bool operator<(const Variant& rhs) const { return cmp(rhs) < 0; } bool operator==(const Variant& rhs) const { return cmp(rhs) == 0; } std::size_t hash() const { switch (m_type) { case (Coltype::_bool): return std::hash()(m_val.m_bool); case (Coltype::_int): return std::hash()(m_val.m_int); case (Coltype::_float): return std::hash()(m_val.m_float); case (Coltype::_text): return hash_bytes(m_val.m_text->data, strlen(m_val.m_text->data)); } return 0; } Coltype::Type m_type; private: union VariantUnion { bool_column m_bool; int_column m_int; float_column m_float; text_column m_text; }; VariantUnion m_val; }; } // namespace packetq #endif // __packetq_variant_h