1 // Tencent is pleased to support the open source community by making RapidJSON available.
2 //
3 // Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
4 //
5 // Licensed under the MIT License (the "License"); you may not use this file except
6 // in compliance with the License. You may obtain a copy of the License at
7 //
8 // http://opensource.org/licenses/MIT
9 //
10 // Unless required by applicable law or agreed to in writing, software distributed
11 // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
12 // CONDITIONS OF ANY KIND, either express or implied. See the License for the
13 // specific language governing permissions and limitations under the License.
14 
15 #ifndef RAPIDJSON_INTERNAL_STACK_H_
16 #define RAPIDJSON_INTERNAL_STACK_H_
17 
18 #include "../allocators.h"
19 #include "swap.h"
20 #include <cstddef>
21 
22 #if defined(__clang__)
23 RAPIDJSON_DIAG_PUSH
24 RAPIDJSON_DIAG_OFF(c++98-compat)
25 #endif
26 
27 RAPIDJSON_NAMESPACE_BEGIN
28 namespace internal {
29 
30 ///////////////////////////////////////////////////////////////////////////////
31 // Stack
32 
33 //! A type-unsafe stack for storing different types of data.
34 /*! \tparam Allocator Allocator for allocating stack memory.
35 */
36 template <typename Allocator>
37 class Stack {
38 public:
39     // Optimization note: Do not allocate memory for stack_ in constructor.
40     // Do it lazily when first Push() -> Expand() -> Resize().
Stack(Allocator * allocator,size_t stackCapacity)41     Stack(Allocator* allocator, size_t stackCapacity) : allocator_(allocator), ownAllocator_(0), stack_(0), stackTop_(0), stackEnd_(0), initialCapacity_(stackCapacity) {
42     }
43 
44 #if RAPIDJSON_HAS_CXX11_RVALUE_REFS
Stack(Stack && rhs)45     Stack(Stack&& rhs)
46         : allocator_(rhs.allocator_),
47           ownAllocator_(rhs.ownAllocator_),
48           stack_(rhs.stack_),
49           stackTop_(rhs.stackTop_),
50           stackEnd_(rhs.stackEnd_),
51           initialCapacity_(rhs.initialCapacity_)
52     {
53         rhs.allocator_ = 0;
54         rhs.ownAllocator_ = 0;
55         rhs.stack_ = 0;
56         rhs.stackTop_ = 0;
57         rhs.stackEnd_ = 0;
58         rhs.initialCapacity_ = 0;
59     }
60 #endif
61 
~Stack()62     ~Stack() {
63         Destroy();
64     }
65 
66 #if RAPIDJSON_HAS_CXX11_RVALUE_REFS
67     Stack& operator=(Stack&& rhs) {
68         if (&rhs != this)
69         {
70             Destroy();
71 
72             allocator_ = rhs.allocator_;
73             ownAllocator_ = rhs.ownAllocator_;
74             stack_ = rhs.stack_;
75             stackTop_ = rhs.stackTop_;
76             stackEnd_ = rhs.stackEnd_;
77             initialCapacity_ = rhs.initialCapacity_;
78 
79             rhs.allocator_ = 0;
80             rhs.ownAllocator_ = 0;
81             rhs.stack_ = 0;
82             rhs.stackTop_ = 0;
83             rhs.stackEnd_ = 0;
84             rhs.initialCapacity_ = 0;
85         }
86         return *this;
87     }
88 #endif
89 
Swap(Stack & rhs)90     void Swap(Stack& rhs) RAPIDJSON_NOEXCEPT {
91         internal::Swap(allocator_, rhs.allocator_);
92         internal::Swap(ownAllocator_, rhs.ownAllocator_);
93         internal::Swap(stack_, rhs.stack_);
94         internal::Swap(stackTop_, rhs.stackTop_);
95         internal::Swap(stackEnd_, rhs.stackEnd_);
96         internal::Swap(initialCapacity_, rhs.initialCapacity_);
97     }
98 
Clear()99     void Clear() { stackTop_ = stack_; }
100 
ShrinkToFit()101     void ShrinkToFit() {
102         if (Empty()) {
103             // If the stack is empty, completely deallocate the memory.
104             Allocator::Free(stack_); // NOLINT (+clang-analyzer-unix.Malloc)
105             stack_ = 0;
106             stackTop_ = 0;
107             stackEnd_ = 0;
108         }
109         else
110             Resize(GetSize());
111     }
112 
113     // Optimization note: try to minimize the size of this function for force inline.
114     // Expansion is run very infrequently, so it is moved to another (probably non-inline) function.
115     template<typename T>
116     RAPIDJSON_FORCEINLINE void Reserve(size_t count = 1) {
117          // Expand the stack if needed
118         if (RAPIDJSON_UNLIKELY(static_cast<std::ptrdiff_t>(sizeof(T) * count) > (stackEnd_ - stackTop_)))
119             Expand<T>(count);
120     }
121 
122     template<typename T>
123     RAPIDJSON_FORCEINLINE T* Push(size_t count = 1) {
124         Reserve<T>(count);
125         return PushUnsafe<T>(count);
126     }
127 
128     template<typename T>
129     RAPIDJSON_FORCEINLINE T* PushUnsafe(size_t count = 1) {
130         RAPIDJSON_ASSERT(stackTop_);
131         RAPIDJSON_ASSERT(static_cast<std::ptrdiff_t>(sizeof(T) * count) <= (stackEnd_ - stackTop_));
132         T* ret = reinterpret_cast<T*>(stackTop_);
133         stackTop_ += sizeof(T) * count;
134         return ret;
135     }
136 
137     template<typename T>
Pop(size_t count)138     T* Pop(size_t count) {
139         RAPIDJSON_ASSERT(GetSize() >= count * sizeof(T));
140         stackTop_ -= count * sizeof(T);
141         return reinterpret_cast<T*>(stackTop_);
142     }
143 
144     template<typename T>
Top()145     T* Top() {
146         RAPIDJSON_ASSERT(GetSize() >= sizeof(T));
147         return reinterpret_cast<T*>(stackTop_ - sizeof(T));
148     }
149 
150     template<typename T>
Top()151     const T* Top() const {
152         RAPIDJSON_ASSERT(GetSize() >= sizeof(T));
153         return reinterpret_cast<T*>(stackTop_ - sizeof(T));
154     }
155 
156     template<typename T>
End()157     T* End() { return reinterpret_cast<T*>(stackTop_); }
158 
159     template<typename T>
End()160     const T* End() const { return reinterpret_cast<T*>(stackTop_); }
161 
162     template<typename T>
Bottom()163     T* Bottom() { return reinterpret_cast<T*>(stack_); }
164 
165     template<typename T>
Bottom()166     const T* Bottom() const { return reinterpret_cast<T*>(stack_); }
167 
HasAllocator()168     bool HasAllocator() const {
169         return allocator_ != 0;
170     }
171 
GetAllocator()172     Allocator& GetAllocator() {
173         RAPIDJSON_ASSERT(allocator_);
174         return *allocator_;
175     }
176 
Empty()177     bool Empty() const { return stackTop_ == stack_; }
GetSize()178     size_t GetSize() const { return static_cast<size_t>(stackTop_ - stack_); }
GetCapacity()179     size_t GetCapacity() const { return static_cast<size_t>(stackEnd_ - stack_); }
180 
181 private:
182     template<typename T>
Expand(size_t count)183     void Expand(size_t count) {
184         // Only expand the capacity if the current stack exists. Otherwise just create a stack with initial capacity.
185         size_t newCapacity;
186         if (stack_ == 0) {
187             if (!allocator_)
188                 ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)();
189             newCapacity = initialCapacity_;
190         } else {
191             newCapacity = GetCapacity();
192             newCapacity += (newCapacity + 1) / 2;
193         }
194         size_t newSize = GetSize() + sizeof(T) * count;
195         if (newCapacity < newSize)
196             newCapacity = newSize;
197 
198         Resize(newCapacity);
199     }
200 
Resize(size_t newCapacity)201     void Resize(size_t newCapacity) {
202         const size_t size = GetSize();  // Backup the current size
203         stack_ = static_cast<char*>(allocator_->Realloc(stack_, GetCapacity(), newCapacity));
204         stackTop_ = stack_ + size;
205         stackEnd_ = stack_ + newCapacity;
206     }
207 
Destroy()208     void Destroy() {
209         Allocator::Free(stack_);
210         RAPIDJSON_DELETE(ownAllocator_); // Only delete if it is owned by the stack
211     }
212 
213     // Prohibit copy constructor & assignment operator.
214     Stack(const Stack&);
215     Stack& operator=(const Stack&);
216 
217     Allocator* allocator_;
218     Allocator* ownAllocator_;
219     char *stack_;
220     char *stackTop_;
221     char *stackEnd_;
222     size_t initialCapacity_;
223 };
224 
225 } // namespace internal
226 RAPIDJSON_NAMESPACE_END
227 
228 #if defined(__clang__)
229 RAPIDJSON_DIAG_POP
230 #endif
231 
232 #endif // RAPIDJSON_STACK_H_
233