/************************************************************************* * * Copyright 2016 Realm Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * **************************************************************************/ #ifndef REALM_BINARY_DATA_HPP #define REALM_BINARY_DATA_HPP #include <realm/owned_data.hpp> #include <realm/util/features.h> #include <realm/utilities.hpp> #include <algorithm> #include <cstddef> #include <ostream> #include <string> namespace realm { /// A reference to a chunk of binary data. /// /// This class does not own the referenced memory, nor does it in any other way /// attempt to manage the lifetime of it. /// /// \sa StringData class BinaryData { public: BinaryData() noexcept : m_data(nullptr) , m_size(0) { } BinaryData(const char* external_data, size_t data_size) noexcept : m_data(external_data) , m_size(data_size) { } // Note! This version includes a trailing null character when using in place constant strings template <size_t N> explicit BinaryData(const char (&external_data)[N]) : m_data(external_data) , m_size(N) { } template <class T, class A> explicit BinaryData(const std::basic_string<char, T, A>&); // BinaryData does not store data, callers must manage their own strings. template <class T, class A> BinaryData(const std::basic_string<char, T, A>&&) = delete; template <class T, class A> explicit operator std::basic_string<char, T, A>() const; char operator[](size_t i) const noexcept { return m_data[i]; } const char* data() const noexcept { return m_data; } size_t size() const noexcept { return m_size; } /// Is this a null reference? /// /// An instance of BinaryData is a null reference when, and only when the /// stored size is zero (size()) and the stored pointer is the null pointer /// (data()). /// /// In the case of the empty byte sequence, the stored size is still zero, /// but the stored pointer is **not** the null pointer. Note that the actual /// value of the pointer is immaterial in this case (as long as it is not /// zero), because when the size is zero, it is an error to dereference the /// pointer. /// /// Conversion of a BinaryData object to `bool` yields the logical negation /// of the result of calling this function. In other words, a BinaryData /// object is converted to true if it is not the null reference, otherwise /// it is converted to false. /// /// It is important to understand that all of the functions and operators in /// this class, and most of the functions in the Realm API in general /// makes no distinction between a null reference and a reference to the /// empty byte sequence. These functions and operators never look at the /// stored pointer if the stored size is zero. bool is_null() const noexcept; friend bool operator==(const BinaryData&, const BinaryData&) noexcept; friend bool operator!=(const BinaryData&, const BinaryData&) noexcept; //@{ /// Trivial bytewise lexicographical comparison. friend bool operator<(const BinaryData&, const BinaryData&) noexcept; friend bool operator>(const BinaryData&, const BinaryData&) noexcept; friend bool operator<=(const BinaryData&, const BinaryData&) noexcept; friend bool operator>=(const BinaryData&, const BinaryData&) noexcept; //@} bool begins_with(BinaryData) const noexcept; bool ends_with(BinaryData) const noexcept; bool contains(BinaryData) const noexcept; template <class C, class T> friend std::basic_ostream<C, T>& operator<<(std::basic_ostream<C, T>&, const BinaryData&); explicit operator bool() const noexcept; private: const char* m_data; size_t m_size; }; /// A read-only chunk of binary data. class OwnedBinaryData : public OwnedData { public: using OwnedData::OwnedData; OwnedBinaryData() = default; OwnedBinaryData(const BinaryData& binary_data) : OwnedData(binary_data.data(), binary_data.size()) { } BinaryData get() const { return {data(), size()}; } }; // Implementation: template <class T, class A> inline BinaryData::BinaryData(const std::basic_string<char, T, A>& s) : m_data(s.data()) , m_size(s.size()) { } template <class T, class A> inline BinaryData::operator std::basic_string<char, T, A>() const { return std::basic_string<char, T, A>(m_data, m_size); } inline bool BinaryData::is_null() const noexcept { return !m_data; } inline bool operator==(const BinaryData& a, const BinaryData& b) noexcept { return a.m_size == b.m_size && a.is_null() == b.is_null() && safe_equal(a.m_data, a.m_data + a.m_size, b.m_data); } inline bool operator!=(const BinaryData& a, const BinaryData& b) noexcept { return !(a == b); } inline bool operator<(const BinaryData& a, const BinaryData& b) noexcept { if (a.is_null() || b.is_null()) return !a.is_null() < !b.is_null(); return std::lexicographical_compare(a.m_data, a.m_data + a.m_size, b.m_data, b.m_data + b.m_size); } inline bool operator>(const BinaryData& a, const BinaryData& b) noexcept { return b < a; } inline bool operator<=(const BinaryData& a, const BinaryData& b) noexcept { return !(b < a); } inline bool operator>=(const BinaryData& a, const BinaryData& b) noexcept { return !(a < b); } inline bool BinaryData::begins_with(BinaryData d) const noexcept { if (is_null() && !d.is_null()) return false; return d.m_size <= m_size && safe_equal(m_data, m_data + d.m_size, d.m_data); } inline bool BinaryData::ends_with(BinaryData d) const noexcept { if (is_null() && !d.is_null()) return false; return d.m_size <= m_size && safe_equal(m_data + m_size - d.m_size, m_data + m_size, d.m_data); } inline bool BinaryData::contains(BinaryData d) const noexcept { if (is_null() && !d.is_null()) return false; return d.m_size == 0 || std::search(m_data, m_data + m_size, d.m_data, d.m_data + d.m_size) != m_data + m_size; } template <class C, class T> inline std::basic_ostream<C, T>& operator<<(std::basic_ostream<C, T>& out, const BinaryData& d) { if (d.is_null()) { out << "null"; } else { out << "BinaryData(" << static_cast<const void*>(d.m_data) << ", " << d.m_size << ")"; } return out; } inline BinaryData::operator bool() const noexcept { return !is_null(); } } // namespace realm #endif // REALM_BINARY_DATA_HPP