1 
2 //
3 // This source file is part of appleseed.
4 // Visit https://appleseedhq.net/ for additional information and resources.
5 //
6 // This software is released under the MIT license.
7 //
8 // Copyright (c) 2010-2013 Francois Beaune, Jupiter Jazz Limited
9 // Copyright (c) 2014-2018 Francois Beaune, The appleseedhq Organization
10 //
11 // Permission is hereby granted, free of charge, to any person obtaining a copy
12 // of this software and associated documentation files (the "Software"), to deal
13 // in the Software without restriction, including without limitation the rights
14 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15 // copies of the Software, and to permit persons to whom the Software is
16 // furnished to do so, subject to the following conditions:
17 //
18 // The above copyright notice and this permission notice shall be included in
19 // all copies or substantial portions of the Software.
20 //
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
27 // THE SOFTWARE.
28 //
29 
30 #pragma once
31 
32 // appleseed.foundation headers.
33 #include "foundation/math/scalar.h"
34 #include "foundation/platform/types.h"
35 
36 // Standard headers.
37 #include <cassert>
38 #include <cstddef>
39 #include <cstring>
40 
41 namespace foundation
42 {
43 
44 //
45 // Memory-alignment related functions.
46 //
47 
48 // Align a given pointer to a given boundary.
49 template <typename T>
50 T align(const T ptr, const size_t alignment);
51 
52 // Compute the alignment of a given pointer.
53 template <typename T>
54 size_t alignment(const T ptr, const size_t max_alignment = 256);
55 
56 // Return whether a given pointer is aligned on a given boundary.
57 template <typename T>
58 bool is_aligned(const T ptr, const size_t alignment);
59 
60 // Allocate memory on a specified alignment boundary.
61 void* aligned_malloc(const size_t size, size_t alignment);
62 
63 // Free a block of memory that was allocated with aligned_malloc().
64 void aligned_free(void* aligned_ptr);
65 
66 
67 //
68 // STL containers related functions.
69 //
70 
71 // Ensure that a container has a minimum given size.
72 template <typename Container>
73 void ensure_minimum_size(
74     Container&      container,
75     const size_t    minimum_size);
76 template <typename Container, typename T>
77 void ensure_minimum_size(
78     Container&      container,
79     const size_t    minimum_size,
80     const T&        value);
81 
82 // Clear a container and release the memory.
83 template <typename Container>
84 void clear_release_memory(Container& container);
85 
86 // Clear a container but keep memory allocated.
87 template <typename Container>
88 void clear_keep_memory(Container& container);
89 
90 // Shrink the capacity of a container to its size.
91 template <typename Container>
92 void shrink_to_fit(Container& container);
93 
94 
95 //
96 // Utility classes to read/write typed data from/to unstructured memory blocks.
97 //
98 
99 class MemoryReader
100 {
101   public:
102     explicit MemoryReader(const void* source);
103 
104     const void* read(const size_t size);
105 
106     template <typename T>
107     const T& read();
108 
109     size_t offset() const;
110 
111     MemoryReader& operator+=(const isize_t offset);
112     MemoryReader& operator-=(const isize_t offset);
113 
114   private:
115     const uint8* const  m_base;
116     const uint8*        m_ptr;
117 };
118 
119 class MemoryWriter
120 {
121   public:
122     explicit MemoryWriter(void* dest);
123 
124     void write(const void* src, const size_t size);
125 
126     template <typename T>
127     void write(const T& value);
128 
129     size_t offset() const;
130 
131   private:
132     const uint8* const  m_base;
133     uint8*              m_ptr;
134 };
135 
136 
137 //
138 // Memory-alignment related functions implementation.
139 //
140 
141 template <typename T>
align(const T ptr,const size_t alignment)142 inline T align(const T ptr, const size_t alignment)
143 {
144     assert(alignment > 0);
145     assert(is_pow2(alignment));
146 
147     const size_t a = alignment - 1;
148     const uintptr_t aligned = ((uintptr_t)ptr + a) & ~a;
149 
150     return (T)aligned;
151 }
152 
153 template <typename T>
alignment(const T ptr,const size_t max_alignment)154 size_t alignment(const T ptr, const size_t max_alignment)
155 {
156     assert(max_alignment > 0);
157     assert(is_pow2(max_alignment));
158 
159     const uintptr_t p = (uintptr_t)ptr;
160     size_t a = 1;
161 
162     while (a < max_alignment && (p & a) == 0)
163         a <<= 1;
164 
165     return a;
166 }
167 
168 template <typename T>
is_aligned(const T ptr,const size_t alignment)169 inline bool is_aligned(const T ptr, const size_t alignment)
170 {
171     assert(alignment > 0);
172     assert(is_pow2(alignment));
173 
174     const uintptr_t p = (uintptr_t)ptr;
175     return (p & (alignment - 1)) == 0;
176 }
177 
178 
179 //
180 // STL containers related functions implementation.
181 //
182 
183 template <typename Container>
ensure_minimum_size(Container & container,const size_t minimum_size)184 inline void ensure_minimum_size(
185     Container&      container,
186     const size_t    minimum_size)
187 {
188     if (container.size() < minimum_size)
189         container.resize(minimum_size);
190 }
191 
192 template <typename Container, typename T>
ensure_minimum_size(Container & container,const size_t minimum_size,const T & value)193 inline void ensure_minimum_size(
194     Container&      container,
195     const size_t    minimum_size,
196     const T&        value)
197 {
198     if (container.size() < minimum_size)
199         container.resize(minimum_size, value);
200 }
201 
202 template <typename Container>
clear_release_memory(Container & container)203 inline void clear_release_memory(Container& container)
204 {
205     Container(container.get_allocator()).swap(container);
206 
207     assert(container.empty());
208 }
209 
210 template <typename Container>
clear_keep_memory(Container & container)211 inline void clear_keep_memory(Container& container)
212 {
213 #ifndef NDEBUG
214     const size_t old_capacity = container.capacity();
215 #endif
216 
217     container.erase(container.begin(), container.end());
218 
219     assert(container.capacity() == old_capacity);
220 }
221 
222 template <typename Container>
shrink_to_fit(Container & container)223 inline void shrink_to_fit(Container& container)
224 {
225     Container(container).swap(container);
226 }
227 
228 
229 //
230 // MemoryReader class implementation.
231 //
232 
MemoryReader(const void * source)233 inline MemoryReader::MemoryReader(const void* source)
234   : m_base(reinterpret_cast<const uint8*>(source))
235   , m_ptr(reinterpret_cast<const uint8*>(source))
236 {
237 }
238 
read(const size_t size)239 inline const void* MemoryReader::read(const size_t size)
240 {
241     const void* result = m_ptr;
242     m_ptr += size;
243     return result;
244 }
245 
246 template <typename T>
read()247 inline const T& MemoryReader::read()
248 {
249     return *reinterpret_cast<const T*>(read(sizeof(T)));
250 }
251 
offset()252 inline size_t MemoryReader::offset() const
253 {
254     return m_ptr - m_base;
255 }
256 
257 inline MemoryReader& MemoryReader::operator+=(const isize_t offset)
258 {
259     m_ptr += offset;
260     return *this;
261 }
262 
263 inline MemoryReader& MemoryReader::operator-=(const isize_t offset)
264 {
265     m_ptr -= offset;
266     return *this;
267 }
268 
269 
270 //
271 // MemoryWriter class implementation.
272 //
273 
MemoryWriter(void * dest)274 inline MemoryWriter::MemoryWriter(void* dest)
275   : m_base(reinterpret_cast<uint8*>(dest))
276   , m_ptr(reinterpret_cast<uint8*>(dest))
277 {
278 }
279 
write(const void * src,const size_t size)280 inline void MemoryWriter::write(const void* src, const size_t size)
281 {
282     std::memcpy(m_ptr, src, size);
283     m_ptr += size;
284 }
285 
286 template <typename T>
write(const T & value)287 inline void MemoryWriter::write(const T& value)
288 {
289     write(&value, sizeof(T));
290 }
291 
offset()292 inline size_t MemoryWriter::offset() const
293 {
294     return m_ptr - m_base;
295 }
296 
297 }   // namespace foundation
298