1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef THIRD_PARTY_BASE_OPTIONAL_H_
6 #define THIRD_PARTY_BASE_OPTIONAL_H_
7 
8 #include <functional>
9 #include <type_traits>
10 #include <utility>
11 
12 #include "third_party/base/logging.h"
13 
14 namespace pdfium {
15 
16 // Specification:
17 // http://en.cppreference.com/w/cpp/utility/optional/in_place_t
18 struct in_place_t {};
19 
20 // Specification:
21 // http://en.cppreference.com/w/cpp/utility/optional/nullopt_t
22 struct nullopt_t {
nullopt_tnullopt_t23   constexpr explicit nullopt_t(int) {}
24 };
25 
26 // Specification:
27 // http://en.cppreference.com/w/cpp/utility/optional/in_place
28 constexpr in_place_t in_place = {};
29 
30 // Specification:
31 // http://en.cppreference.com/w/cpp/utility/optional/nullopt
32 constexpr nullopt_t nullopt(0);
33 
34 namespace internal {
35 
36 template <typename T, bool = std::is_trivially_destructible<T>::value>
37 struct OptionalStorage {
38   // Initializing |empty_| here instead of using default member initializing
39   // to avoid errors in g++ 4.8.
OptionalStorageOptionalStorage40   constexpr OptionalStorage() : empty_('\0') {}
41 
OptionalStorageOptionalStorage42   constexpr explicit OptionalStorage(const T& value)
43       : is_null_(false), value_(value) {}
44 
45   // TODO(alshabalin): Can't use 'constexpr' with std::move until C++14.
OptionalStorageOptionalStorage46   explicit OptionalStorage(T&& value)
47       : is_null_(false), value_(std::move(value)) {}
48 
49   // TODO(alshabalin): Can't use 'constexpr' with std::forward until C++14.
50   template <class... Args>
OptionalStorageOptionalStorage51   explicit OptionalStorage(in_place_t, Args&&... args)
52       : is_null_(false), value_(std::forward<Args>(args)...) {}
53 
54   // When T is not trivially destructible we must call its
55   // destructor before deallocating its memory.
~OptionalStorageOptionalStorage56   ~OptionalStorage() {
57     if (!is_null_)
58       value_.~T();
59   }
60 
61   bool is_null_ = true;
62   union {
63     // |empty_| exists so that the union will always be initialized, even when
64     // it doesn't contain a value. Union members must be initialized for the
65     // constructor to be 'constexpr'.
66     char empty_;
67     T value_;
68   };
69 };
70 
71 template <typename T>
72 struct OptionalStorage<T, true> {
73   // Initializing |empty_| here instead of using default member initializing
74   // to avoid errors in g++ 4.8.
75   constexpr OptionalStorage() : empty_('\0') {}
76 
77   constexpr explicit OptionalStorage(const T& value)
78       : is_null_(false), value_(value) {}
79 
80   // TODO(alshabalin): Can't use 'constexpr' with std::move until C++14.
81   explicit OptionalStorage(T&& value)
82       : is_null_(false), value_(std::move(value)) {}
83 
84   // TODO(alshabalin): Can't use 'constexpr' with std::forward until C++14.
85   template <class... Args>
86   explicit OptionalStorage(in_place_t, Args&&... args)
87       : is_null_(false), value_(std::forward<Args>(args)...) {}
88 
89   // When T is trivially destructible (i.e. its destructor does nothing) there
90   // is no need to call it. Explicitly defaulting the destructor means it's not
91   // user-provided. Those two together make this destructor trivial.
92   ~OptionalStorage() = default;
93 
94   bool is_null_ = true;
95   union {
96     // |empty_| exists so that the union will always be initialized, even when
97     // it doesn't contain a value. Union members must be initialized for the
98     // constructor to be 'constexpr'.
99     char empty_;
100     T value_;
101   };
102 };
103 
104 }  // namespace internal
105 
106 // pdfium::Optional is a PDFium version of the C++17 optional class,
107 // based on the Chromium version:
108 // std::optional documentation:
109 // http://en.cppreference.com/w/cpp/utility/optional
110 // Chromium documentation:
111 // https://chromium.googlesource.com/chromium/src/+/master/docs/optional.md
112 //
113 // These are the differences between the specification and the implementation:
114 // - The constructor and emplace method using initializer_list are not
115 //   implemented because 'initializer_list' is banned from Chromium.
116 // - Constructors do not use 'constexpr' as it is a C++14 extension.
117 // - 'constexpr' might be missing in some places for reasons specified locally.
118 // - No exceptions are thrown, because they are banned from Chromium.
119 // - All the non-members are in the 'pdifum' namespace instead of 'std'.
120 template <typename T>
121 class Optional {
122  public:
123   using value_type = T;
124 
125   constexpr Optional() = default;
126 
127   constexpr Optional(nullopt_t) {}
128 
129   Optional(const Optional& other) {
130     if (!other.storage_.is_null_)
131       Init(other.value());
132   }
133 
134   Optional(Optional&& other) {
135     if (!other.storage_.is_null_)
136       Init(std::move(other.value()));
137   }
138 
139   constexpr Optional(const T& value) : storage_(value) {}
140 
141   // TODO(alshabalin): Can't use 'constexpr' with std::move until C++14.
142   Optional(T&& value) : storage_(std::move(value)) {}
143 
144   // TODO(alshabalin): Can't use 'constexpr' with std::forward until C++14.
145   template <class... Args>
146   explicit Optional(in_place_t, Args&&... args)
147       : storage_(in_place, std::forward<Args>(args)...) {}
148 
149   ~Optional() = default;
150 
151   Optional& operator=(nullopt_t) {
152     FreeIfNeeded();
153     return *this;
154   }
155 
156   Optional& operator=(const Optional& other) {
157     if (other.storage_.is_null_) {
158       FreeIfNeeded();
159       return *this;
160     }
161 
162     InitOrAssign(other.value());
163     return *this;
164   }
165 
166   Optional& operator=(Optional&& other) {
167     if (other.storage_.is_null_) {
168       FreeIfNeeded();
169       return *this;
170     }
171 
172     InitOrAssign(std::move(other.value()));
173     return *this;
174   }
175 
176   template <class U>
177   typename std::enable_if<std::is_same<std::decay<U>, T>::value,
178                           Optional&>::type
179   operator=(U&& value) {
180     InitOrAssign(std::forward<U>(value));
181     return *this;
182   }
183 
184   // TODO(mlamouri): can't use 'constexpr' with DCHECK.
185   const T* operator->() const {
186     DCHECK(!storage_.is_null_);
187     return &value();
188   }
189 
190   // TODO(mlamouri): using 'constexpr' here breaks compiler that assume it was
191   // meant to be 'constexpr const'.
192   T* operator->() {
193     DCHECK(!storage_.is_null_);
194     return &value();
195   }
196 
197   constexpr const T& operator*() const& { return value(); }
198 
199   // TODO(mlamouri): using 'constexpr' here breaks compiler that assume it was
200   // meant to be 'constexpr const'.
201   T& operator*() & { return value(); }
202 
203   constexpr const T&& operator*() const&& { return std::move(value()); }
204 
205   // TODO(mlamouri): using 'constexpr' here breaks compiler that assume it was
206   // meant to be 'constexpr const'.
207   T&& operator*() && { return std::move(value()); }
208 
209   constexpr explicit operator bool() const { return !storage_.is_null_; }
210 
211   constexpr bool has_value() const { return !storage_.is_null_; }
212 
213   // TODO(mlamouri): using 'constexpr' here breaks compiler that assume it was
214   // meant to be 'constexpr const'.
215   T& value() & {
216     DCHECK(!storage_.is_null_);
217     return storage_.value_;
218   }
219 
220   // TODO(mlamouri): can't use 'constexpr' with DCHECK.
221   const T& value() const& {
222     DCHECK(!storage_.is_null_);
223     return storage_.value_;
224   }
225 
226   // TODO(mlamouri): using 'constexpr' here breaks compiler that assume it was
227   // meant to be 'constexpr const'.
228   T&& value() && {
229     DCHECK(!storage_.is_null_);
230     return std::move(storage_.value_);
231   }
232 
233   // TODO(mlamouri): can't use 'constexpr' with DCHECK.
234   const T&& value() const&& {
235     DCHECK(!storage_.is_null_);
236     return std::move(storage_.value_);
237   }
238 
239   template <class U>
240   constexpr T value_or(U&& default_value) const& {
241     // TODO(mlamouri): add the following assert when possible:
242     // static_assert(std::is_copy_constructible<T>::value,
243     //               "T must be copy constructible");
244     static_assert(std::is_convertible<U, T>::value,
245                   "U must be convertible to T");
246     return storage_.is_null_ ? static_cast<T>(std::forward<U>(default_value))
247                              : value();
248   }
249 
250   template <class U>
251   T value_or(U&& default_value) && {
252     // TODO(mlamouri): add the following assert when possible:
253     // static_assert(std::is_move_constructible<T>::value,
254     //               "T must be move constructible");
255     static_assert(std::is_convertible<U, T>::value,
256                   "U must be convertible to T");
257     return storage_.is_null_ ? static_cast<T>(std::forward<U>(default_value))
258                              : std::move(value());
259   }
260 
261   void swap(Optional& other) {
262     if (storage_.is_null_ && other.storage_.is_null_)
263       return;
264 
265     if (storage_.is_null_ != other.storage_.is_null_) {
266       if (storage_.is_null_) {
267         Init(std::move(other.storage_.value_));
268         other.FreeIfNeeded();
269       } else {
270         other.Init(std::move(storage_.value_));
271         FreeIfNeeded();
272       }
273       return;
274     }
275 
276     DCHECK(!storage_.is_null_ && !other.storage_.is_null_);
277     using std::swap;
278     swap(**this, *other);
279   }
280 
281   void reset() {
282     FreeIfNeeded();
283   }
284 
285   template <class... Args>
286   void emplace(Args&&... args) {
287     FreeIfNeeded();
288     Init(std::forward<Args>(args)...);
289   }
290 
291  private:
292   void Init(const T& value) {
293     DCHECK(storage_.is_null_);
294     new (&storage_.value_) T(value);
295     storage_.is_null_ = false;
296   }
297 
298   void Init(T&& value) {
299     DCHECK(storage_.is_null_);
300     new (&storage_.value_) T(std::move(value));
301     storage_.is_null_ = false;
302   }
303 
304   template <class... Args>
305   void Init(Args&&... args) {
306     DCHECK(storage_.is_null_);
307     new (&storage_.value_) T(std::forward<Args>(args)...);
308     storage_.is_null_ = false;
309   }
310 
311   void InitOrAssign(const T& value) {
312     if (storage_.is_null_)
313       Init(value);
314     else
315       storage_.value_ = value;
316   }
317 
318   void InitOrAssign(T&& value) {
319     if (storage_.is_null_)
320       Init(std::move(value));
321     else
322       storage_.value_ = std::move(value);
323   }
324 
325   void FreeIfNeeded() {
326     if (storage_.is_null_)
327       return;
328     storage_.value_.~T();
329     storage_.is_null_ = true;
330   }
331 
332   internal::OptionalStorage<T> storage_;
333 };
334 
335 template <class T>
336 constexpr bool operator==(const Optional<T>& lhs, const Optional<T>& rhs) {
337   return !!lhs != !!rhs ? false : lhs == nullopt || (*lhs == *rhs);
338 }
339 
340 template <class T>
341 constexpr bool operator!=(const Optional<T>& lhs, const Optional<T>& rhs) {
342   return !(lhs == rhs);
343 }
344 
345 template <class T>
346 constexpr bool operator<(const Optional<T>& lhs, const Optional<T>& rhs) {
347   return rhs == nullopt ? false : (lhs == nullopt ? true : *lhs < *rhs);
348 }
349 
350 template <class T>
351 constexpr bool operator<=(const Optional<T>& lhs, const Optional<T>& rhs) {
352   return !(rhs < lhs);
353 }
354 
355 template <class T>
356 constexpr bool operator>(const Optional<T>& lhs, const Optional<T>& rhs) {
357   return rhs < lhs;
358 }
359 
360 template <class T>
361 constexpr bool operator>=(const Optional<T>& lhs, const Optional<T>& rhs) {
362   return !(lhs < rhs);
363 }
364 
365 template <class T>
366 constexpr bool operator==(const Optional<T>& opt, nullopt_t) {
367   return !opt;
368 }
369 
370 template <class T>
371 constexpr bool operator==(nullopt_t, const Optional<T>& opt) {
372   return !opt;
373 }
374 
375 template <class T>
376 constexpr bool operator!=(const Optional<T>& opt, nullopt_t) {
377   return !!opt;
378 }
379 
380 template <class T>
381 constexpr bool operator!=(nullopt_t, const Optional<T>& opt) {
382   return !!opt;
383 }
384 
385 template <class T>
386 constexpr bool operator<(const Optional<T>& opt, nullopt_t) {
387   return false;
388 }
389 
390 template <class T>
391 constexpr bool operator<(nullopt_t, const Optional<T>& opt) {
392   return !!opt;
393 }
394 
395 template <class T>
396 constexpr bool operator<=(const Optional<T>& opt, nullopt_t) {
397   return !opt;
398 }
399 
400 template <class T>
401 constexpr bool operator<=(nullopt_t, const Optional<T>& opt) {
402   return true;
403 }
404 
405 template <class T>
406 constexpr bool operator>(const Optional<T>& opt, nullopt_t) {
407   return !!opt;
408 }
409 
410 template <class T>
411 constexpr bool operator>(nullopt_t, const Optional<T>& opt) {
412   return false;
413 }
414 
415 template <class T>
416 constexpr bool operator>=(const Optional<T>& opt, nullopt_t) {
417   return true;
418 }
419 
420 template <class T>
421 constexpr bool operator>=(nullopt_t, const Optional<T>& opt) {
422   return !opt;
423 }
424 
425 template <class T>
426 constexpr bool operator==(const Optional<T>& opt, const T& value) {
427   return opt != nullopt ? *opt == value : false;
428 }
429 
430 template <class T>
431 constexpr bool operator==(const T& value, const Optional<T>& opt) {
432   return opt == value;
433 }
434 
435 template <class T>
436 constexpr bool operator!=(const Optional<T>& opt, const T& value) {
437   return !(opt == value);
438 }
439 
440 template <class T>
441 constexpr bool operator!=(const T& value, const Optional<T>& opt) {
442   return !(opt == value);
443 }
444 
445 template <class T>
446 constexpr bool operator<(const Optional<T>& opt, const T& value) {
447   return opt != nullopt ? *opt < value : true;
448 }
449 
450 template <class T>
451 constexpr bool operator<(const T& value, const Optional<T>& opt) {
452   return opt != nullopt ? value < *opt : false;
453 }
454 
455 template <class T>
456 constexpr bool operator<=(const Optional<T>& opt, const T& value) {
457   return !(opt > value);
458 }
459 
460 template <class T>
461 constexpr bool operator<=(const T& value, const Optional<T>& opt) {
462   return !(value > opt);
463 }
464 
465 template <class T>
466 constexpr bool operator>(const Optional<T>& opt, const T& value) {
467   return value < opt;
468 }
469 
470 template <class T>
471 constexpr bool operator>(const T& value, const Optional<T>& opt) {
472   return opt < value;
473 }
474 
475 template <class T>
476 constexpr bool operator>=(const Optional<T>& opt, const T& value) {
477   return !(opt < value);
478 }
479 
480 template <class T>
481 constexpr bool operator>=(const T& value, const Optional<T>& opt) {
482   return !(value < opt);
483 }
484 
485 template <class T>
486 constexpr Optional<typename std::decay<T>::type> make_optional(T&& value) {
487   return Optional<typename std::decay<T>::type>(std::forward<T>(value));
488 }
489 
490 template <class T>
491 void swap(Optional<T>& lhs, Optional<T>& rhs) {
492   lhs.swap(rhs);
493 }
494 
495 }  // namespace pdfium
496 
497 namespace std {
498 
499 template <class T>
500 struct hash<pdfium::Optional<T>> {
501   size_t operator()(const pdfium::Optional<T>& opt) const {
502     return opt == pdfium::nullopt ? 0 : std::hash<T>()(*opt);
503   }
504 };
505 
506 }  // namespace std
507 
508 template <class T>
509 using Optional = pdfium::Optional<T>;
510 
511 #endif  // THIRD_PARTY_BASE_OPTIONAL_H_
512