1//
2// immer: immutable data structures for C++
3// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
4//
5// This software is distributed under the Boost Software License, Version 1.0.
6// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
7//
8
9#pragma once
10
11#include <immer/memory_policy.hpp>
12#include <immer/detail/arrays/with_capacity.hpp>
13
14namespace immer {
15
16template <typename T, typename MemoryPolicy>
17class array;
18
19/*!
20 * Mutable version of `immer::array`.
21 *
22 * @rst
23 *
24 * Refer to :doc:`transients` to learn more about when and how to use
25 * the mutable versions of immutable containers.
26 *
27 * @endrst
28 */
29template <typename T, typename MemoryPolicy = default_memory_policy>
30class array_transient
31 : MemoryPolicy::transience_t::owner
32{
33 using impl_t = detail::arrays::with_capacity<T, MemoryPolicy>;
34 using impl_no_capacity_t = detail::arrays::no_capacity<T, MemoryPolicy>;
35 using owner_t = typename MemoryPolicy::transience_t::owner;
36
37public:
38 using value_type = T;
39 using reference = const T&;
40 using size_type = std::size_t;
41 using difference_type = std::ptrdiff_t;
42 using const_reference = const T&;
43
44 using iterator = const T*;
45 using const_iterator = iterator;
46 using reverse_iterator = std::reverse_iterator<iterator>;
47
48 using memory_policy = MemoryPolicy;
49 using persistent_type = array<T, MemoryPolicy>;
50
51 /*!
52 * Default constructor. It creates a mutable array of `size() ==
53 * 0`. It does not allocate memory and its complexity is
54 * @f$ O(1) @f$.
55 */
56 array_transient() = default;
57
58 /*!
59 * Returns an iterator pointing at the first element of the
60 * collection. It does not allocate memory and its complexity is
61 * @f$ O(1) @f$.
62 */
63 IMMER_NODISCARD iterator begin() const { return impl_.data(); }
64
65 /*!
66 * Returns an iterator pointing just after the last element of the
67 * collection. It does not allocate and its complexity is @f$ O(1) @f$.
68 */
69 IMMER_NODISCARD iterator end() const { return impl_.data() + impl_.size; }
70
71 /*!
72 * Returns an iterator that traverses the collection backwards,
73 * pointing at the first element of the reversed collection. It
74 * does not allocate memory and its complexity is @f$ O(1) @f$.
75 */
76 IMMER_NODISCARD reverse_iterator rbegin() const { return reverse_iterator{end()}; }
77
78 /*!
79 * Returns an iterator that traverses the collection backwards,
80 * pointing after the last element of the reversed collection. It
81 * does not allocate memory and its complexity is @f$ O(1) @f$.
82 */
83 IMMER_NODISCARD reverse_iterator rend() const { return reverse_iterator{begin()}; }
84
85 /*!
86 * Returns the number of elements in the container. It does
87 * not allocate memory and its complexity is @f$ O(1) @f$.
88 */
89 IMMER_NODISCARD std::size_t size() const { return impl_.size; }
90
91 /*!
92 * Returns `true` if there are no elements in the container. It
93 * does not allocate memory and its complexity is @f$ O(1) @f$.
94 */
95 IMMER_NODISCARD bool empty() const { return impl_.size == 0; }
96
97 /*!
98 * Access the raw data.
99 */
100 IMMER_NODISCARD const T* data() const { return impl_.data(); }
101
102 /*!
103 * Provide mutable access to the raw underlaying data.
104 */
105 IMMER_NODISCARD T* data_mut() { return impl_.data_mut(*this); }
106
107 /*!
108 * Access the last element.
109 */
110 IMMER_NODISCARD const T& back() const { return data()[size() - 1]; }
111
112 /*!
113 * Access the first element.
114 */
115 IMMER_NODISCARD const T& front() const { return data()[0]; }
116
117 /*!
118 * Returns a `const` reference to the element at position `index`.
119 * It is undefined when @f$ 0 index \geq size() @f$. It does not
120 * allocate memory and its complexity is *effectively* @f$ O(1)
121 * @f$.
122 */
123 reference operator[] (size_type index) const
124 { return impl_.get(index); }
125
126 /*!
127 * Returns a `const` reference to the element at position
128 * `index`. It throws an `std::out_of_range` exception when @f$
129 * index \geq size() @f$. It does not allocate memory and its
130 * complexity is *effectively* @f$ O(1) @f$.
131 */
132 reference at(size_type index) const
133 { return impl_.get_check(index); }
134
135 /*!
136 * Inserts `value` at the end. It may allocate memory and its
137 * complexity is *effectively* @f$ O(1) @f$.
138 */
139 void push_back(value_type value)
140 { impl_.push_back_mut(*this, std::move(value)); }
141
142 /*!
143 * Sets to the value `value` at position `idx`.
144 * Undefined for `index >= size()`.
145 * It may allocate memory and its complexity is
146 * *effectively* @f$ O(1) @f$.
147 */
148 void set(size_type index, value_type value)
149 { impl_.assoc_mut(*this, index, std::move(value)); }
150
151 /*!
152 * Updates the array to contain the result of the expression
153 * `fn((*this)[idx])` at position `idx`.
154 * Undefined for `0 >= size()`.
155 * It may allocate memory and its complexity is
156 * *effectively* @f$ O(1) @f$.
157 */
158 template <typename FnT>
159 void update(size_type index, FnT&& fn)
160 { impl_.update_mut(*this, index, std::forward<FnT>(fn)); }
161
162 /*!
163 * Resizes the array to only contain the first `min(elems, size())`
164 * elements. It may allocate memory and its complexity is
165 * *effectively* @f$ O(1) @f$.
166 */
167 void take(size_type elems)
168 { impl_.take_mut(*this, elems); }
169
170 /*!
171 * Returns an @a immutable form of this container, an
172 * `immer::array`.
173 */
174 IMMER_NODISCARD persistent_type persistent() &
175 {
176 this->owner_t::operator=(owner_t{});
177 return persistent_type{ impl_ };
178 }
179 IMMER_NODISCARD persistent_type persistent() &&
180 { return persistent_type{ std::move(impl_) }; }
181
182private:
183 friend persistent_type;
184
185 array_transient(impl_t impl)
186 : impl_(std::move(impl))
187 {}
188
189 impl_t impl_ = impl_t::empty();
190};
191
192} // namespace immer
193