/************************************************************************ * EOS - the CERN Disk Storage System * * Copyright (C) 2018 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 .* ************************************************************************/ //------------------------------------------------------------------------------ // author: Georgios Bitzes // desc: Phantom types for file and container identifiers //------------------------------------------------------------------------------ #ifndef EOS_NS_I_IDENTIFIERS_HH #define EOS_NS_I_IDENTIFIERS_HH #include "common/Murmur3.hh" #include "namespace/Namespace.hh" EOSNSNAMESPACE_BEGIN //------------------------------------------------------------------------------ //! Phantom types: strongly typed uint64_t, identifying files and containers. //! //! Unless explicitly asked with obj.(get/set)UnderlyingUInt64(), this will //! generate glorious compiler errors when you try to misuse, such as adding //! two FileIdentifiers together (which makes zero sense), accidentally store //! them as int32, or try to mix them up. //! //! Bugs which would previously be detectable only at runtime, will now generate //! compiler errors. //! //! Conversion to/from uint64_t should happen only when absolutely necessary, //! at the boundaries of serialization / deserialization. //! //! Any sensible compiler should generate the same machine code, as with a plain //! uint64_t - there should be no performance penalty. //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ //! FileIdentifier class //------------------------------------------------------------------------------ class FileIdentifier { public: //---------------------------------------------------------------------------- //! Prevent implicit conversions between this type and uint64_t, by making //! the constructor explicit. //---------------------------------------------------------------------------- explicit FileIdentifier(uint64_t src) : val(src) {} //---------------------------------------------------------------------------- //! Construct empty FileIdentifier. //---------------------------------------------------------------------------- FileIdentifier() : val(0) {} //---------------------------------------------------------------------------- //! Retrieve the underlying uint64_t. Use this only if you have to, ie //! when serializing to disk. //! //! The name is long and ugly on purpose, to make you think twice before //! using it. ;) //---------------------------------------------------------------------------- uint64_t getUnderlyingUInt64() const { return val; } //---------------------------------------------------------------------------- //! Comparison operator, so we can store those as keys in maps, etc. //---------------------------------------------------------------------------- bool operator<(const FileIdentifier& other) const { return val < other.val; } //---------------------------------------------------------------------------- //! Equality operator. //---------------------------------------------------------------------------- bool operator==(const FileIdentifier& other) const { return val == other.val; } private: uint64_t val; }; //------------------------------------------------------------------------------ //! ContainerIdentifier class //------------------------------------------------------------------------------ class ContainerIdentifier { public: //---------------------------------------------------------------------------- //! Prevent implicit conversions between this type and uint64_t, by making //! the constructor explicit. //---------------------------------------------------------------------------- explicit ContainerIdentifier(uint64_t src) : val(src) {} //---------------------------------------------------------------------------- //! Construct empty ContainerIdentifier. //---------------------------------------------------------------------------- ContainerIdentifier() : val(0) {} //---------------------------------------------------------------------------- //! Retrieve the underlying uint64_t. Use this only if you have to, ie //! when serializing to disk. //! //! The name is long and ugly on purpose, to make you think twice before //! using it. ;) //---------------------------------------------------------------------------- uint64_t getUnderlyingUInt64() const { return val; } //---------------------------------------------------------------------------- //! Comparison operator, so we can store those as keys in maps, etc. //---------------------------------------------------------------------------- bool operator<(const ContainerIdentifier& other) const { return val < other.val; } //---------------------------------------------------------------------------- //! Equality operator. //---------------------------------------------------------------------------- bool operator==(const ContainerIdentifier& other) const { return val == other.val; } private: uint64_t val; }; //------------------------------------------------------------------------------ //! FileOrContainerIdentifier class - holds either FileIdentifer, or //! ContainerIdentifier, but not both. //! //! It can also be empty. //------------------------------------------------------------------------------ class FileOrContainerIdentifier { public: //---------------------------------------------------------------------------- //! Empty. //---------------------------------------------------------------------------- FileOrContainerIdentifier() : val(0), isEmpty(true), file(false) {} //---------------------------------------------------------------------------- //! Has a file //---------------------------------------------------------------------------- FileOrContainerIdentifier(FileIdentifier file) : val(file.getUnderlyingUInt64()), isEmpty(false), file(true) {} //---------------------------------------------------------------------------- //! Has a container //---------------------------------------------------------------------------- FileOrContainerIdentifier(ContainerIdentifier cont) : val(cont.getUnderlyingUInt64()), isEmpty(false), file(false) {} //---------------------------------------------------------------------------- //! Is it empty? //---------------------------------------------------------------------------- bool empty() const { return isEmpty; } //---------------------------------------------------------------------------- //! Is it a file? //---------------------------------------------------------------------------- bool isFile() const { return !isEmpty && file; } //---------------------------------------------------------------------------- //! Is it a container? //---------------------------------------------------------------------------- bool isContainer() const { return !isEmpty && !file; } //---------------------------------------------------------------------------- //! Get FileIdentifier - if empty, or this actually points to a container, //! FileIdentifier(0) is returned //---------------------------------------------------------------------------- FileIdentifier toFileIdentifier() const { if(isEmpty || !file) { return FileIdentifier(0); } return FileIdentifier(val); } //---------------------------------------------------------------------------- //! Get ContainerIdentifier - if empty, or this actually points to a file, //! ContainerIdentifier(0) is returned //---------------------------------------------------------------------------- ContainerIdentifier toContainerIdentifier() const { if(isEmpty || file) { return ContainerIdentifier(0); } return ContainerIdentifier(val); } //---------------------------------------------------------------------------- //! Equality operators //---------------------------------------------------------------------------- bool operator==(const FileOrContainerIdentifier& other) const { return val == other.val && isEmpty == other.isEmpty && file == other.file; } bool operator==(const FileIdentifier& other) const { return *this == FileOrContainerIdentifier(other); } bool operator==(const ContainerIdentifier& other) const { return *this == FileOrContainerIdentifier(other); } private: uint64_t val; bool isEmpty; bool file; }; EOSNSNAMESPACE_END namespace Murmur3 { //---------------------------------------------------------------------------- //! MurmurHasher specialization for FileIdentifier. //---------------------------------------------------------------------------- template<> struct MurmurHasher { MurmurHasher hasher; size_t operator()(const eos::FileIdentifier &key) const { return hasher(key.getUnderlyingUInt64()); } }; //---------------------------------------------------------------------------- //! MurmurHasher specialization for ContainerIdentifier. //---------------------------------------------------------------------------- template<> struct MurmurHasher { MurmurHasher hasher; size_t operator()(const eos::ContainerIdentifier &key) const { return hasher(key.getUnderlyingUInt64()); } }; } #endif