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 
12 namespace pdfium {
13 
14 // Specification:
15 // http://en.cppreference.com/w/cpp/utility/optional/in_place_t
16 struct in_place_t {};
17 
18 // Specification:
19 // http://en.cppreference.com/w/cpp/utility/optional/nullopt_t
20 struct nullopt_t {
nullopt_tnullopt_t21   constexpr explicit nullopt_t(int) {}
22 };
23 
24 // Specification:
25 // http://en.cppreference.com/w/cpp/utility/optional/in_place
26 constexpr in_place_t in_place = {};
27 
28 // Specification:
29 // http://en.cppreference.com/w/cpp/utility/optional/nullopt
30 constexpr nullopt_t nullopt(0);
31 
32 namespace internal {
33 
34 template <typename T, bool = std::is_trivially_destructible<T>::value>
35 struct OptionalStorage {
36   // Initializing |empty_| here instead of using default member initializing
37   // to avoid errors in g++ 4.8.
OptionalStorageOptionalStorage38   constexpr OptionalStorage() : empty_('\0') {}
39 
OptionalStorageOptionalStorage40   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.
OptionalStorageOptionalStorage44   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>
OptionalStorageOptionalStorage49   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.
~OptionalStorageOptionalStorage54   ~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 
69 template <typename T>
70 struct 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'.
118 template <typename T>
119 class 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 
333 template <class T>
334 constexpr bool operator==(const Optional<T>& lhs, const Optional<T>& rhs) {
335   return !!lhs != !!rhs ? false : lhs == nullopt || (*lhs == *rhs);
336 }
337 
338 template <class T>
339 constexpr bool operator!=(const Optional<T>& lhs, const Optional<T>& rhs) {
340   return !(lhs == rhs);
341 }
342 
343 template <class T>
344 constexpr bool operator<(const Optional<T>& lhs, const Optional<T>& rhs) {
345   return rhs == nullopt ? false : (lhs == nullopt ? true : *lhs < *rhs);
346 }
347 
348 template <class T>
349 constexpr bool operator<=(const Optional<T>& lhs, const Optional<T>& rhs) {
350   return !(rhs < lhs);
351 }
352 
353 template <class T>
354 constexpr bool operator>(const Optional<T>& lhs, const Optional<T>& rhs) {
355   return rhs < lhs;
356 }
357 
358 template <class T>
359 constexpr bool operator>=(const Optional<T>& lhs, const Optional<T>& rhs) {
360   return !(lhs < rhs);
361 }
362 
363 template <class T>
364 constexpr bool operator==(const Optional<T>& opt, nullopt_t) {
365   return !opt;
366 }
367 
368 template <class T>
369 constexpr bool operator==(nullopt_t, const Optional<T>& opt) {
370   return !opt;
371 }
372 
373 template <class T>
374 constexpr bool operator!=(const Optional<T>& opt, nullopt_t) {
375   return !!opt;
376 }
377 
378 template <class T>
379 constexpr bool operator!=(nullopt_t, const Optional<T>& opt) {
380   return !!opt;
381 }
382 
383 template <class T>
384 constexpr bool operator<(const Optional<T>& opt, nullopt_t) {
385   return false;
386 }
387 
388 template <class T>
389 constexpr bool operator<(nullopt_t, const Optional<T>& opt) {
390   return !!opt;
391 }
392 
393 template <class T>
394 constexpr bool operator<=(const Optional<T>& opt, nullopt_t) {
395   return !opt;
396 }
397 
398 template <class T>
399 constexpr bool operator<=(nullopt_t, const Optional<T>& opt) {
400   return true;
401 }
402 
403 template <class T>
404 constexpr bool operator>(const Optional<T>& opt, nullopt_t) {
405   return !!opt;
406 }
407 
408 template <class T>
409 constexpr bool operator>(nullopt_t, const Optional<T>& opt) {
410   return false;
411 }
412 
413 template <class T>
414 constexpr bool operator>=(const Optional<T>& opt, nullopt_t) {
415   return true;
416 }
417 
418 template <class T>
419 constexpr bool operator>=(nullopt_t, const Optional<T>& opt) {
420   return !opt;
421 }
422 
423 template <class T>
424 constexpr bool operator==(const Optional<T>& opt, const T& value) {
425   return opt != nullopt ? *opt == value : false;
426 }
427 
428 template <class T>
429 constexpr bool operator==(const T& value, const Optional<T>& opt) {
430   return opt == value;
431 }
432 
433 template <class T>
434 constexpr bool operator!=(const Optional<T>& opt, const T& value) {
435   return !(opt == value);
436 }
437 
438 template <class T>
439 constexpr bool operator!=(const T& value, const Optional<T>& opt) {
440   return !(opt == value);
441 }
442 
443 template <class T>
444 constexpr bool operator<(const Optional<T>& opt, const T& value) {
445   return opt != nullopt ? *opt < value : true;
446 }
447 
448 template <class T>
449 constexpr bool operator<(const T& value, const Optional<T>& opt) {
450   return opt != nullopt ? value < *opt : false;
451 }
452 
453 template <class T>
454 constexpr bool operator<=(const Optional<T>& opt, const T& value) {
455   return !(opt > value);
456 }
457 
458 template <class T>
459 constexpr bool operator<=(const T& value, const Optional<T>& opt) {
460   return !(value > opt);
461 }
462 
463 template <class T>
464 constexpr bool operator>(const Optional<T>& opt, const T& value) {
465   return value < opt;
466 }
467 
468 template <class T>
469 constexpr bool operator>(const T& value, const Optional<T>& opt) {
470   return opt < value;
471 }
472 
473 template <class T>
474 constexpr bool operator>=(const Optional<T>& opt, const T& value) {
475   return !(opt < value);
476 }
477 
478 template <class T>
479 constexpr bool operator>=(const T& value, const Optional<T>& opt) {
480   return !(value < opt);
481 }
482 
483 template <class T>
484 constexpr Optional<typename std::decay<T>::type> make_optional(T&& value) {
485   return Optional<typename std::decay<T>::type>(std::forward<T>(value));
486 }
487 
488 template <class T>
489 void swap(Optional<T>& lhs, Optional<T>& rhs) {
490   lhs.swap(rhs);
491 }
492 
493 }  // namespace pdfium
494 
495 namespace std {
496 
497 template <class T>
498 struct 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 
506 template <class T>
507 using Optional = pdfium::Optional<T>;
508 
509 #endif  // PDFIUM_THIRD_PARTY_BASE_OPTIONAL_H_
510