| 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)) | 
| 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 |  | 
| 69 |     bytecode_ptr(std::nullptr_t) {} | 
| 70 |  | 
| 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 |  | 
| 91 |     T *release() { | 
| 92 |         auto *p = ptr.release(); | 
| 93 |         bytes = 0; | 
| 94 |         alignment = 0; | 
| 95 |         return p; | 
| 96 |     } | 
| 97 |  | 
| 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 |      */ | 
| 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. */ | 
| 119 |     size_t size() const { return bytes; } | 
| 120 |  | 
| 121 |     /** \brief Returns alignment of the memory region in bytes. */ | 
| 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 { | 
| 130 |         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 |  |