This fixes a bug in PHP/HH's crypt_blowfish implementation that can cause a short...
[hiphop-php.git] / hphp / util / copy-ptr.h
blobbdbfa200f4636f34ae83ca7aa45e31c8141e6c08
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
16 #pragma once
18 #include <atomic>
19 #include <cstdlib>
20 #include <memory>
21 #include <utility>
23 namespace HPHP {
25 //////////////////////////////////////////////////////////////////////
28 * A refcounted smart pointer that does deep copies if you ask for a
29 * mutable copy when the ref-count is greater than one.
31 template<class T>
32 struct copy_ptr {
33 copy_ptr() noexcept {}
34 copy_ptr(const copy_ptr& o) {
35 inc_ref(m_p = o.m_p);
37 copy_ptr(copy_ptr&& o) noexcept {
38 m_p = o.m_p;
39 o.m_p = nullptr;
41 template <typename... Args> explicit copy_ptr(Args&&... args) {
42 construct(nullptr, std::forward<Args>(args)...);
44 ~copy_ptr() { dec_ref(m_p); }
45 copy_ptr& operator=(const copy_ptr& o) {
46 auto const save = m_p;
47 inc_ref(m_p = o.m_p);
48 dec_ref(save);
49 return *this;
51 copy_ptr& operator=(copy_ptr&& o) noexcept {
52 std::swap(m_p, o.m_p);
53 return *this;
56 explicit operator bool() const { return !isNull(); }
57 bool isNull() const { return m_p == nullptr; }
59 const T& operator*() const { return *m_p; }
60 const T* operator->() const { return m_p; }
61 const T* get() const { return m_p; }
62 T* mutate() {
63 if (m_p && get_ref(m_p) != 1) {
64 emplace(*m_p);
66 return m_p;
69 void reset() {
70 dec_ref(m_p);
71 m_p = nullptr;
74 template <typename... Args> T* emplace(Args&&... args) {
75 auto const save = m_p;
76 construct(save, std::forward<Args>(args)...);
77 dec_ref(save);
78 return m_p;
81 friend bool
82 operator==(const copy_ptr& a, const copy_ptr& b) { return a.m_p == b.m_p; }
83 friend bool
84 operator==(const copy_ptr& a, const T* b) { return a.m_p == b; }
85 friend bool
86 operator==(const T* a, const copy_ptr& b) { return a == b.m_p; }
87 friend bool
88 operator!=(const copy_ptr& a, const copy_ptr& b) { return a.m_p != b.m_p; }
89 friend bool
90 operator!=(const copy_ptr& a, const T* b) { return a.m_p != b; }
91 friend bool
92 operator!=(const T* a, const copy_ptr& b) { return a != b.m_p; }
94 private:
95 template <typename... Args> void construct(T* save, Args&&... args) {
96 auto const mem = std::malloc(data_offset() + sizeof(T));
97 if (!mem) throw std::bad_alloc();
98 new (mem) refcount_type{1};
99 m_p = (T*)((char*)mem + data_offset());
100 try {
101 new (m_p) T(std::forward<Args>(args)...);
102 } catch (...) {
103 std::free(mem);
104 m_p = save;
105 throw;
109 using refcount_type = std::atomic<uint32_t>;
111 T* m_p{};
113 static constexpr size_t data_offset() {
114 return alignof(T) >= sizeof(refcount_type) ?
115 alignof(T) : sizeof(refcount_type);
118 static refcount_type* get_ref_ptr(T* p) {
119 return (refcount_type*)((char*)p - data_offset());
122 static uint32_t get_ref(T* p) {
123 return get_ref_ptr(p)->load(std::memory_order_relaxed);
126 static void dec_ref(T* p) {
127 if (!p) return;
128 auto ref = get_ref_ptr(p);
129 if (ref->fetch_sub(1, std::memory_order_relaxed) == 1) {
130 p->~T();
131 ref->~refcount_type();
132 std::free(ref);
136 static void inc_ref(T* p) {
137 if (!p) return;
138 get_ref_ptr(p)->fetch_add(1, std::memory_order_relaxed);
142 //////////////////////////////////////////////////////////////////////