// ---------------------------------------------------------------------- // File: KeyDescriptor.hh // Author: Georgios Bitzes - CERN // ---------------------------------------------------------------------- /************************************************************************ * quarkdb - a redis-like highly available key-value store * * Copyright (C) 2016 CERN/Switzerland * * * * This program 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. * * * * This program 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 this program. If not, see .* ************************************************************************/ #ifndef QUARKDB_KEY_DESCRIPTOR_HH #define QUARKDB_KEY_DESCRIPTOR_HH #include "utils/IntToBinaryString.hh" #include "utils/StaticBuffer.hh" #include "Utils.hh" namespace quarkdb { // Types of redis keys supported enum class KeyType : char { kNull = '\0', kParseError = '=', kString = 'a', kHash = 'b', kSet = 'c', kDeque = 'd', kLocalityHash = 'e', kLease = 'f', kVersionedHash = 'g' }; inline KeyType parseKeyType(char c) { switch(c) { case char(KeyType::kString): { return KeyType::kString; } case char(KeyType::kHash): { return KeyType::kHash; } case char(KeyType::kSet): { return KeyType::kSet; } case char(KeyType::kDeque): { return KeyType::kDeque; } case char(KeyType::kLocalityHash): { return KeyType::kLocalityHash; } case char(KeyType::kLease): { return KeyType::kLease; } case char(KeyType::kVersionedHash): { return KeyType::kVersionedHash; } default: { return KeyType::kParseError; } } } inline std::string keyTypeAsString(KeyType key) { switch(key) { case KeyType::kNull: { return "none"; } case KeyType::kParseError: { qdb_throw("given KeyType == kParseError, not representable as string"); } case KeyType::kString: { return "string"; } case KeyType::kSet: { return "set"; } case KeyType::kHash: { return "hash"; } case KeyType::kDeque: { return "deque"; } case KeyType::kLocalityHash: { return "locality hash"; } case KeyType::kLease: { return "lease"; } case KeyType::kVersionedHash: { return "versioned hash"; } } qdb_throw("should never reach here"); } // Helper enum for selecting which of startIndex, endIndex to pick enum class Direction : int { kLeft = -1, kRight = 1 }; inline Direction flipDirection(Direction direction) { if(direction == Direction::kLeft) { return Direction::kRight; } else if(direction == Direction::kRight) { return Direction::kLeft; } qdb_throw("should never happen"); } // Class which parses and serializes key descriptors. class KeyDescriptor { public: KeyDescriptor() {} KeyDescriptor(std::string_view str) { qdb_assert(str.size() != 0); // Determine key type. keyType = parseKeyType(str[0]); // Parse further switch(keyType) { case KeyType::kString: case KeyType::kSet: case KeyType::kHash: case KeyType::kLocalityHash: { qdb_assert(str.size() == kHashDescriptorSize); // Parse size. sz = binaryStringToInt(str.data() + kOffsetSize); // All done return; } case KeyType::kDeque: case KeyType::kLease: { qdb_assert(str.size() == kDequeDescriptorSize); // Parse size. sz = binaryStringToInt(str.data() + kOffsetSize); // Parse startIndex and endIndex. startIndex = binaryStringToInt(str.data() + kOffsetStartIndex); endIndex = binaryStringToInt(str.data() + kOffsetEndIndex); qdb_assert(startIndex <= endIndex); // All done return; } case KeyType::kVersionedHash: { qdb_assert(str.size() == kVersionedHashDescriptorSize); // Parse size. sz = binaryStringToInt(str.data() + kOffsetSize); // Parse version, store into startIndex. startIndex = binaryStringToInt(str.data() + kOffsetStartIndex); // All done return; } default: { qdb_throw("error parsing key descriptor - unknown key type"); } } } bool empty() const { return keyType == KeyType::kNull; } KeyType getKeyType() const { return keyType; } int64_t getSize() const { qdb_assert(keyType != KeyType::kParseError); return sz; } uint64_t getStartIndex() const { qdb_assert(keyType == KeyType::kDeque || keyType == KeyType::kLease || keyType == KeyType::kVersionedHash); return startIndex; } uint64_t getEndIndex() const { qdb_assert(keyType == KeyType::kDeque || keyType == KeyType::kLease); return endIndex; } void setKeyType(KeyType kt) { keyType = kt; } void setSize(int64_t newval) { qdb_assert(keyType != KeyType::kParseError && keyType != KeyType::kNull); sz = newval; } void setStartIndex(uint64_t newval) { qdb_assert(keyType == KeyType::kDeque || keyType == KeyType::kLease || keyType == KeyType::kVersionedHash); startIndex = newval; } void setEndIndex(uint64_t newval) { qdb_assert(keyType == KeyType::kDeque || keyType == KeyType::kLease); endIndex = newval; } std::string_view serialize() { serializationBuffer[0] = char(keyType); switch(keyType) { case KeyType::kString: case KeyType::kSet: case KeyType::kHash: case KeyType::kLocalityHash: { serializationBuffer.shrink(kHashDescriptorSize); // Store the size.. intToBinaryString(sz, serializationBuffer.data() + kOffsetSize); return serializationBuffer.toView(); } case KeyType::kDeque: case KeyType::kLease: { serializationBuffer.shrink(kDequeDescriptorSize); // Store the size.. intToBinaryString(sz, serializationBuffer.data() + kOffsetSize); // Store start index, end index intToBinaryString(startIndex, serializationBuffer.data() + kOffsetStartIndex); intToBinaryString(endIndex, serializationBuffer.data() + kOffsetEndIndex); qdb_assert(startIndex <= endIndex); return serializationBuffer.toView(); } case KeyType::kVersionedHash: { serializationBuffer.shrink(kVersionedHashDescriptorSize); // Store the size.. intToBinaryString(sz, serializationBuffer.data() + kOffsetSize); // Store start index intToBinaryString(startIndex, serializationBuffer.data() + kOffsetStartIndex); return serializationBuffer.toView(); } default: { qdb_throw("attempted to serialize invalid key descriptor"); } } } bool operator==(const KeyDescriptor &rhs) const { return keyType == rhs.keyType && sz == rhs.sz && startIndex == rhs.startIndex && endIndex == rhs.endIndex; } uint64_t getListIndex(Direction direction) { qdb_assert(keyType == KeyType::kDeque); if(direction == Direction::kLeft) { return startIndex; } else if(direction == Direction::kRight) { return endIndex; } qdb_throw("should never happen"); } void setListIndex(Direction direction, uint64_t newindex) { qdb_assert(keyType == KeyType::kDeque); if(direction == Direction::kLeft) { startIndex = newindex; return; } else if(direction == Direction::kRight) { endIndex = newindex; return; } qdb_throw("should never happen"); } private: KeyType keyType = KeyType::kNull; static constexpr size_t kMaxDescriptorSize = 28; StaticBuffer serializationBuffer; static constexpr size_t kStringDescriptorSize = 1 + sizeof(int64_t); static constexpr size_t kHashDescriptorSize = 1 + sizeof(int64_t); static constexpr size_t kDequeDescriptorSize = 1 + sizeof(int64_t) + 2*sizeof(uint64_t); static constexpr size_t kVersionedHashDescriptorSize = 1 + sizeof(int64_t) + 1*sizeof(uint64_t); static constexpr size_t kOffsetSize = 1; static constexpr size_t kOffsetStartIndex = 1 + sizeof(int64_t); static constexpr size_t kOffsetEndIndex = 1 + sizeof(int64_t) + sizeof(uint64_t); // Only used in hashes, sets, and deques int64_t sz = 0; // Only used in deques. startIndex is also used in versioned hashes. static constexpr uint64_t kIndexInitialValue = std::numeric_limits::max() / 2; uint64_t startIndex = kIndexInitialValue; uint64_t endIndex = kIndexInitialValue; }; } #endif