• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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