1 // Copyright 2020 The Abseil Authors.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #ifndef ABSL_STRINGS_INTERNAL_CORD_INTERNAL_H_
16 #define ABSL_STRINGS_INTERNAL_CORD_INTERNAL_H_
17
18 #include <atomic>
19 #include <cassert>
20 #include <cstddef>
21 #include <cstdint>
22 #include <type_traits>
23
24 #include "absl/base/internal/invoke.h"
25 #include "absl/container/internal/compressed_tuple.h"
26 #include "absl/meta/type_traits.h"
27 #include "absl/strings/string_view.h"
28
29 namespace absl {
30 ABSL_NAMESPACE_BEGIN
31 namespace cord_internal {
32
33 // Wraps std::atomic for reference counting.
34 class Refcount {
35 public:
Refcount()36 Refcount() : count_{1} {}
~Refcount()37 ~Refcount() {}
38
39 // Increments the reference count by 1. Imposes no memory ordering.
Increment()40 inline void Increment() { count_.fetch_add(1, std::memory_order_relaxed); }
41
42 // Asserts that the current refcount is greater than 0. If the refcount is
43 // greater than 1, decrements the reference count by 1.
44 //
45 // Returns false if there are no references outstanding; true otherwise.
46 // Inserts barriers to ensure that state written before this method returns
47 // false will be visible to a thread that just observed this method returning
48 // false.
Decrement()49 inline bool Decrement() {
50 int32_t refcount = count_.load(std::memory_order_acquire);
51 assert(refcount > 0);
52 return refcount != 1 && count_.fetch_sub(1, std::memory_order_acq_rel) != 1;
53 }
54
55 // Same as Decrement but expect that refcount is greater than 1.
DecrementExpectHighRefcount()56 inline bool DecrementExpectHighRefcount() {
57 int32_t refcount = count_.fetch_sub(1, std::memory_order_acq_rel);
58 assert(refcount > 0);
59 return refcount != 1;
60 }
61
62 // Returns the current reference count using acquire semantics.
Get()63 inline int32_t Get() const { return count_.load(std::memory_order_acquire); }
64
65 // Returns whether the atomic integer is 1.
66 // If the reference count is used in the conventional way, a
67 // reference count of 1 implies that the current thread owns the
68 // reference and no other thread shares it.
69 // This call performs the test for a reference count of one, and
70 // performs the memory barrier needed for the owning thread
71 // to act on the object, knowing that it has exclusive access to the
72 // object.
IsOne()73 inline bool IsOne() { return count_.load(std::memory_order_acquire) == 1; }
74
75 private:
76 std::atomic<int32_t> count_;
77 };
78
79 // The overhead of a vtable is too much for Cord, so we roll our own subclasses
80 // using only a single byte to differentiate classes from each other - the "tag"
81 // byte. Define the subclasses first so we can provide downcasting helper
82 // functions in the base class.
83
84 struct CordRepConcat;
85 struct CordRepSubstring;
86 struct CordRepExternal;
87
88 struct CordRep {
89 // The following three fields have to be less than 32 bytes since
90 // that is the smallest supported flat node size.
91 size_t length;
92 Refcount refcount;
93 // If tag < FLAT, it represents CordRepKind and indicates the type of node.
94 // Otherwise, the node type is CordRepFlat and the tag is the encoded size.
95 uint8_t tag;
96 char data[1]; // Starting point for flat array: MUST BE LAST FIELD of CordRep
97
98 inline CordRepConcat* concat();
99 inline const CordRepConcat* concat() const;
100 inline CordRepSubstring* substring();
101 inline const CordRepSubstring* substring() const;
102 inline CordRepExternal* external();
103 inline const CordRepExternal* external() const;
104 };
105
106 struct CordRepConcat : public CordRep {
107 CordRep* left;
108 CordRep* right;
109
depthCordRepConcat110 uint8_t depth() const { return static_cast<uint8_t>(data[0]); }
set_depthCordRepConcat111 void set_depth(uint8_t depth) { data[0] = static_cast<char>(depth); }
112 };
113
114 struct CordRepSubstring : public CordRep {
115 size_t start; // Starting offset of substring in child
116 CordRep* child;
117 };
118
119 // Type for function pointer that will invoke the releaser function and also
120 // delete the `CordRepExternalImpl` corresponding to the passed in
121 // `CordRepExternal`.
122 using ExternalReleaserInvoker = void (*)(CordRepExternal*);
123
124 // External CordReps are allocated together with a type erased releaser. The
125 // releaser is stored in the memory directly following the CordRepExternal.
126 struct CordRepExternal : public CordRep {
127 const char* base;
128 // Pointer to function that knows how to call and destroy the releaser.
129 ExternalReleaserInvoker releaser_invoker;
130 };
131
132 struct Rank1 {};
133 struct Rank0 : Rank1 {};
134
135 template <typename Releaser, typename = ::absl::base_internal::invoke_result_t<
136 Releaser, absl::string_view>>
InvokeReleaser(Rank0,Releaser && releaser,absl::string_view data)137 void InvokeReleaser(Rank0, Releaser&& releaser, absl::string_view data) {
138 ::absl::base_internal::invoke(std::forward<Releaser>(releaser), data);
139 }
140
141 template <typename Releaser,
142 typename = ::absl::base_internal::invoke_result_t<Releaser>>
InvokeReleaser(Rank1,Releaser && releaser,absl::string_view)143 void InvokeReleaser(Rank1, Releaser&& releaser, absl::string_view) {
144 ::absl::base_internal::invoke(std::forward<Releaser>(releaser));
145 }
146
147 // We use CompressedTuple so that we can benefit from EBCO.
148 template <typename Releaser>
149 struct CordRepExternalImpl
150 : public CordRepExternal,
151 public ::absl::container_internal::CompressedTuple<Releaser> {
152 // The extra int arg is so that we can avoid interfering with copy/move
153 // constructors while still benefitting from perfect forwarding.
154 template <typename T>
CordRepExternalImplCordRepExternalImpl155 CordRepExternalImpl(T&& releaser, int)
156 : CordRepExternalImpl::CompressedTuple(std::forward<T>(releaser)) {
157 this->releaser_invoker = &Release;
158 }
159
~CordRepExternalImplCordRepExternalImpl160 ~CordRepExternalImpl() {
161 InvokeReleaser(Rank0{}, std::move(this->template get<0>()),
162 absl::string_view(base, length));
163 }
164
ReleaseCordRepExternalImpl165 static void Release(CordRepExternal* rep) {
166 delete static_cast<CordRepExternalImpl*>(rep);
167 }
168 };
169
170 } // namespace cord_internal
171 ABSL_NAMESPACE_END
172 } // namespace absl
173 #endif // ABSL_STRINGS_INTERNAL_CORD_INTERNAL_H_
174