1 /*
2  * Copyright (c) 2017, Intel Corporation
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are met:
6  *
7  *  * Redistributions of source code must retain the above copyright notice,
8  *    this list of conditions and the following disclaimer.
9  *  * Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *  * Neither the name of Intel Corporation nor the names of its contributors
13  *    may be used to endorse or promote products derived from this software
14  *    without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
20  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  * POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 /**
30  * \file
31  * \brief bytecode_ptr: Smart pointer with unique ownership that knows its
32  * length and alignment.
33  */
34 
35 #ifndef UTIL_BYTECODE_PTR_H
36 #define UTIL_BYTECODE_PTR_H
37 
38 #include "util/alloc.h"
39 #include "util/operators.h"
40 
41 #include <algorithm> // std::max
42 #include <cstring>
43 #include <memory>
44 #include <stdexcept> // std::logic_error
45 
46 namespace ue2 {
47 
48 /**
49  * \brief Smart pointer that knows its length and alignment and behaves like a
50  * std::unique_ptr -- i.e. it retains unique ownership of the memory region.
51  *
52  * This is intended to be used for flat aligned memory regions that will
53  * eventually end up copied into the Hyperscan bytecode.
54  */
55 template<typename T>
56 class bytecode_ptr : totally_ordered<bytecode_ptr<T>> {
57 public:
58     bytecode_ptr() = default;
59     explicit bytecode_ptr(size_t bytes_in, size_t alignment_in = alignof(T))
bytes(bytes_in)60         : bytes(bytes_in), alignment(alignment_in) {
61         // posix_memalign doesn't like us asking for smaller alignment.
62         size_t mem_align = std::max(alignment, sizeof(void *));
63         ptr.reset(static_cast<T *>(aligned_malloc_internal(bytes, mem_align)));
64         if (!ptr) {
65             throw std::bad_alloc();
66         }
67     }
68 
bytecode_ptr(std::nullptr_t)69     bytecode_ptr(std::nullptr_t) {}
70 
get()71     T *get() const { return ptr.get(); }
72 
73     T &operator*() { return *ptr; }
74     const T &operator*() const { return *ptr; }
75 
76     T *operator->() { return ptr.get(); }
77     const T *operator->() const { return ptr.get(); }
78 
79     explicit operator bool() const { return ptr != nullptr; }
80 
81     /** \brief Move converter for shared_ptr. */
82     template <typename ST, class = typename std::enable_if<
83                                std::is_convertible<T *, ST *>::value>::type>
84     operator std::shared_ptr<ST>() && {
85         auto d = ptr.get_deleter();
86         return std::shared_ptr<ST>(ptr.release(), d);
87     }
88 
89     void reset(T *p = nullptr) { ptr.reset(p); }
90 
release()91     T *release() {
92         auto *p = ptr.release();
93         bytes = 0;
94         alignment = 0;
95         return p;
96     }
97 
swap(bytecode_ptr & other)98     void swap(bytecode_ptr &other) {
99         using std::swap;
100         swap(ptr, other.ptr);
101         swap(bytes, other.bytes);
102         swap(alignment, other.alignment);
103     }
104 
105     /**
106      * \brief Reduces the apparent size of the memory region. Note that this
107      * does not reallocate and copy, it just changes the value returned by
108      * size().
109      */
shrink(size_t new_size)110     void shrink(size_t new_size) {
111         if (new_size > bytes) {
112             assert(0);
113             throw std::logic_error("Must shrink to a smaller value");
114         }
115         bytes = new_size;
116     }
117 
118     /** \brief Returns size of the memory region in bytes. */
size()119     size_t size() const { return bytes; }
120 
121     /** \brief Returns alignment of the memory region in bytes. */
align()122     size_t align() const { return alignment; }
123 
124     bool operator==(const bytecode_ptr &a) const { return ptr == a.ptr; }
125     bool operator<(const bytecode_ptr &a) const { return ptr < a.ptr; }
126 
127 private:
128     /** \brief Deleter function for std::unique_ptr. */
129     template <typename DT> struct deleter {
operatordeleter130         void operator()(DT *p) const { aligned_free_internal(p); }
131     };
132 
133     std::unique_ptr<T, deleter<T>> ptr; //!< Underlying pointer.
134     size_t bytes = 0; //!< Size of memory region in bytes.
135     size_t alignment = 0; //!< Alignment of memory region in bytes.
136 };
137 
138 /**
139  * \brief Constructs a bytecode_ptr<T> with the given size and alignment.
140  */
141 template<typename T>
142 inline bytecode_ptr<T> make_bytecode_ptr(size_t size,
143                                          size_t align = alignof(T)) {
144     return bytecode_ptr<T>(size, align);
145 }
146 
147 /**
148  * \brief Constructs a bytecode_ptr<T> with the given size and alignment and
149  * fills the memory region with zeroes.
150  */
151 template<typename T>
152 inline bytecode_ptr<T> make_zeroed_bytecode_ptr(size_t size,
153                                                 size_t align = alignof(T)) {
154     auto ptr = make_bytecode_ptr<T>(size, align);
155     std::memset(ptr.get(), 0, size);
156     return ptr;
157 }
158 
159 } // namespace ue2
160 
161 #endif // UTIL_BYTECODE_PTR_H
162