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