[WTF] Add Markable<T, Traits>
[WebKit-https.git] / Source / WTF / wtf / Markable.h
1 /*
2  * Copyright (C) 2018 Yusuke Suzuki <yusukesuzuki@slowstart.org>.
3  * Copyright (C) 2016-2017 Apple Inc. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
18  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26 // The idea of Markable<T> is derived from markable<T> at
27 // https://github.com/akrzemi1/markable.
28 //
29 // Copyright (C) 2015-2018 Andrzej Krzemienski.
30 //
31 // Use, modification, and distribution is subject to the Boost Software
32 // License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
33 // http://www.boost.org/LICENSE_1_0.txt)
34
35 #pragma once
36
37 #include <type_traits>
38 #include <wtf/Optional.h>
39 #include <wtf/StdLibExtras.h>
40
41 namespace WTF {
42
43 // Example:
44 //     enum class Type { Value1, Value2, Value3 };
45 //     Markable<Type, EnumMarkableTraits<Type, 42>> optional;
46 template<
47     typename EnumType,
48     typename std::underlying_type<EnumType>::type constant = std::numeric_limits<typename std::underlying_type<EnumType>::type>::max()>
49 struct EnumMarkableTraits {
50     static_assert(std::is_enum<EnumType>::value, "");
51     using UnderlyingType = typename std::underlying_type<EnumType>::type;
52
53     constexpr static bool isEmptyValue(EnumType value)
54     {
55         return static_cast<UnderlyingType>(value) == constant;
56     }
57
58     constexpr static EnumType emptyValue()
59     {
60         return static_cast<EnumType>(constant);
61     }
62 };
63
64 template<typename IntegralType, IntegralType constant = 0>
65 struct IntegralMarkableTraits {
66     static_assert(std::is_integral<IntegralType>::value, "");
67     constexpr static bool isEmptyValue(IntegralType value)
68     {
69         return value == constant;
70     }
71
72     constexpr static IntegralType emptyValue()
73     {
74         return constant;
75     }
76 };
77
78 // The goal of Markable is offering std::optional without sacrificing storage efficiency.
79 // Markable takes Traits, which should have isEmptyValue and emptyValue functions. By using
80 // one value of T as an empty value, we can remove bool flag in std::optional. This strategy is
81 // similar to WTF::HashTable, which uses two values of T as an empty value and a deleted value.
82 // This class is intended to be used as a member of a class to compact the size of the class.
83 // Otherwise, you should use std::optional.
84 template<typename T, typename Traits>
85 class Markable {
86 public:
87     constexpr Markable()
88         : m_value(Traits::emptyValue())
89     { }
90
91     constexpr Markable(std::nullopt_t)
92         : Markable()
93     { }
94
95     constexpr Markable(T&& value)
96         : m_value(WTFMove(value))
97     { }
98
99     constexpr Markable(const T& value)
100         : m_value(value)
101     { }
102
103     template<typename... Args>
104     constexpr explicit Markable(std::in_place_t, Args&&... args)
105         : m_value(std::forward<Args>(args)...)
106     { }
107
108     constexpr Markable(const std::optional<T>& value)
109         : m_value(bool(value) ? *value : Traits::emptyValue())
110     { }
111
112     constexpr Markable(std::optional<T>&& value)
113         : m_value(bool(value) ? WTFMove(*value) : Traits::emptyValue())
114     { }
115
116     constexpr explicit operator bool() const { return !Traits::isEmptyValue(m_value); }
117
118     void reset() { m_value = Traits::emptyValue(); }
119
120     constexpr const T& value() const& { return m_value; }
121     constexpr T& value() & { return m_value; }
122     constexpr T&& value() && { return WTFMove(m_value); }
123
124     constexpr const T* operator->() const { return std::addressof(m_value); }
125     constexpr T* operator->() { return std::addressof(m_value); }
126
127     constexpr const T& operator*() const& { return m_value; }
128     constexpr T& operator*() & { return m_value; }
129
130     operator std::optional<T>() &&
131     {
132         if (bool(*this))
133             return WTFMove(m_value);
134         return std::nullopt;
135     }
136
137     operator std::optional<T>() const&
138     {
139         if (bool(*this))
140             return m_value;
141         return std::nullopt;
142     }
143
144 private:
145     T m_value;
146 };
147
148 } // namespace WTF
149
150 using WTF::Markable;
151 using WTF::IntegralMarkableTraits;
152 using WTF::EnumMarkableTraits;