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
46namespace 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 */
55template<typename T>
56class bytecode_ptr : totally_ordered<bytecode_ptr<T>> {
57public:
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
127private:
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 */
141template<typename T>
142inline 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 */
151template<typename T>
152inline 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