added iOS source code
[wl-app.git] / iOS / Pods / Realm / include / core / realm / array_binary.hpp
1 /*************************************************************************
2  *
3  * Copyright 2016 Realm Inc.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  **************************************************************************/
18
19 #ifndef REALM_ARRAY_BINARY_HPP
20 #define REALM_ARRAY_BINARY_HPP
21
22 #include <realm/binary_data.hpp>
23 #include <realm/array_blob.hpp>
24 #include <realm/array_integer.hpp>
25 #include <realm/exceptions.hpp>
26
27 namespace realm {
28
29 /*
30 STORAGE FORMAT
31 ---------------------------------------------------------------------------------------
32 ArrayBinary stores binary elements using two ArrayInteger and one ArrayBlob. The ArrayBlob can only store one
33 single concecutive array of bytes (contrary to its 'Array' name that misleadingly indicates it could store multiple
34 elements).
35
36 Assume we have the strings "a", "", "abc", null, "ab". Then the three arrays will contain:
37
38 ArrayInteger    m_offsets   1, 1, 5, 5, 6
39 ArrayBlob       m_blob      aabcab
40 ArrayInteger    m_nulls     0, 0, 0, 1, 0 // 1 indicates null, 0 indicates non-null
41
42 So for each element the ArrayInteger, the ArrayInteger points into the ArrayBlob at the position of the first
43 byte of the next element.
44
45 m_nulls is always present (except for old database files; see below), so any ArrayBinary is always nullable!
46 The nullable property (such as throwing exception upon set(null) on non-nullable column, etc) is handled on
47 column level only.
48
49 DATABASE FILE VERSION CHANGES
50 ---------------------------------------------------------------------------------------
51 Old database files do not have any m_nulls array. To be backwardscompatible, many methods will have tests like
52 `if(Array::size() == 3)` and have a backwards compatible code paths for these (e.g. avoid writing to m_nulls
53 in set(), etc). This way no file format upgrade is needed to support nulls for BinaryData.
54 */
55
56 class ArrayBinary : public Array {
57 public:
58     explicit ArrayBinary(Allocator&) noexcept;
59     ~ArrayBinary() noexcept override
60     {
61     }
62
63     // Disable copying, this is not allowed.
64     ArrayBinary& operator=(const ArrayBinary&) = delete;
65     ArrayBinary(const ArrayBinary&) = delete;
66
67     /// Create a new empty binary array and attach this accessor to
68     /// it. This does not modify the parent reference information of
69     /// this accessor.
70     ///
71     /// Note that the caller assumes ownership of the allocated
72     /// underlying node. It is not owned by the accessor.
73     void create();
74
75     // Old database files will not have the m_nulls array, so we need code paths for
76     // backwards compatibility for these cases.
77     bool legacy_array_type() const noexcept;
78
79     //@{
80     /// Overriding functions of Array
81     void init_from_ref(ref_type) noexcept;
82     void init_from_mem(MemRef) noexcept;
83     void init_from_parent() noexcept;
84     //@}
85
86     bool is_empty() const noexcept;
87     size_t size() const noexcept;
88
89     BinaryData get(size_t ndx) const noexcept;
90     size_t read(size_t ndx, size_t pos, char* buffer, size_t max_size) const noexcept;
91
92     void add(BinaryData value, bool add_zero_term = false);
93     void set(size_t ndx, BinaryData value, bool add_zero_term = false);
94     void insert(size_t ndx, BinaryData value, bool add_zero_term = false);
95     void erase(size_t ndx);
96     void truncate(size_t new_size);
97     void clear();
98     void destroy();
99
100     /// Get the specified element without the cost of constructing an
101     /// array instance. If an array instance is already available, or
102     /// you need to get multiple values, then this method will be
103     /// slower.
104     static BinaryData get(const char* header, size_t ndx, Allocator&) noexcept;
105
106     ref_type bptree_leaf_insert(size_t ndx, BinaryData, bool add_zero_term, TreeInsertBase& state);
107
108     static size_t get_size_from_header(const char*, Allocator&) noexcept;
109
110     /// Construct a binary array of the specified size and return just
111     /// the reference to the underlying memory. All elements will be
112     /// initialized to the binary value `defaults`, which can be either
113     /// null or zero-length non-null (value with size > 0 is not allowed as
114     /// initialization value).
115     static MemRef create_array(size_t size, Allocator&, BinaryData defaults);
116
117     /// Construct a copy of the specified slice of this binary array
118     /// using the specified target allocator.
119     MemRef slice(size_t offset, size_t slice_size, Allocator& target_alloc) const;
120
121 #ifdef REALM_DEBUG
122     void to_dot(std::ostream&, bool is_strings, StringData title = StringData()) const;
123 #endif
124     bool update_from_parent(size_t old_baseline) noexcept;
125
126 private:
127     ArrayInteger m_offsets;
128     ArrayBlob m_blob;
129     ArrayInteger m_nulls;
130 };
131
132
133 // Implementation:
134
135 inline ArrayBinary::ArrayBinary(Allocator& allocator) noexcept
136     : Array(allocator)
137     , m_offsets(allocator)
138     , m_blob(allocator)
139     , m_nulls(allocator)
140 {
141     m_offsets.set_parent(this, 0);
142     m_blob.set_parent(this, 1);
143     m_nulls.set_parent(this, 2);
144 }
145
146 inline void ArrayBinary::create()
147 {
148     size_t init_size = 0;
149     BinaryData defaults = BinaryData{};                          // This init value is ignored because size = 0
150     MemRef mem = create_array(init_size, get_alloc(), defaults); // Throws
151     init_from_mem(mem);
152 }
153
154 inline void ArrayBinary::init_from_ref(ref_type ref) noexcept
155 {
156     REALM_ASSERT(ref);
157     char* header = get_alloc().translate(ref);
158     init_from_mem(MemRef(header, ref, m_alloc));
159 }
160
161 inline void ArrayBinary::init_from_parent() noexcept
162 {
163     ref_type ref = get_ref_from_parent();
164     init_from_ref(ref);
165 }
166
167 inline bool ArrayBinary::is_empty() const noexcept
168 {
169     return m_offsets.is_empty();
170 }
171
172 // Old database files will not have the m_nulls array, so we need code paths for
173 // backwards compatibility for these cases. We can test if m_nulls exists by looking
174 // at number of references in this ArrayBinary.
175 inline bool ArrayBinary::legacy_array_type() const noexcept
176 {
177     if (Array::size() == 3)
178         return false; // New database file
179     else if (Array::size() == 2)
180         return true; // Old database file
181     else
182         REALM_ASSERT(false); // Should never happen
183     return false;
184 }
185
186 inline size_t ArrayBinary::size() const noexcept
187 {
188     return m_offsets.size();
189 }
190
191 inline BinaryData ArrayBinary::get(size_t ndx) const noexcept
192 {
193     REALM_ASSERT_3(ndx, <, m_offsets.size());
194
195     if (!legacy_array_type() && m_nulls.get(ndx)) {
196         return BinaryData();
197     }
198     else {
199         size_t begin = ndx ? to_size_t(m_offsets.get(ndx - 1)) : 0;
200         size_t end = to_size_t(m_offsets.get(ndx));
201
202         BinaryData bd = BinaryData(m_blob.get(begin), end - begin);
203         // Old database file (non-nullable column should never return null)
204         REALM_ASSERT(!bd.is_null());
205         return bd;
206     }
207 }
208
209 inline void ArrayBinary::truncate(size_t new_size)
210 {
211     REALM_ASSERT_3(new_size, <, m_offsets.size());
212
213     size_t blob_size = new_size ? to_size_t(m_offsets.get(new_size - 1)) : 0;
214
215     m_offsets.truncate(new_size);
216     m_blob.truncate(blob_size);
217     if (!legacy_array_type())
218         m_nulls.truncate(new_size);
219 }
220
221 inline void ArrayBinary::clear()
222 {
223     m_blob.clear();
224     m_offsets.clear();
225     if (!legacy_array_type())
226         m_nulls.clear();
227 }
228
229 inline void ArrayBinary::destroy()
230 {
231     m_blob.destroy();
232     m_offsets.destroy();
233     if (!legacy_array_type())
234         m_nulls.destroy();
235     Array::destroy();
236 }
237
238 inline size_t ArrayBinary::get_size_from_header(const char* header, Allocator& alloc) noexcept
239 {
240     ref_type offsets_ref = to_ref(Array::get(header, 0));
241     const char* offsets_header = alloc.translate(offsets_ref);
242     return Array::get_size_from_header(offsets_header);
243 }
244
245 inline bool ArrayBinary::update_from_parent(size_t old_baseline) noexcept
246 {
247     bool res = Array::update_from_parent(old_baseline);
248     if (res) {
249         m_blob.update_from_parent(old_baseline);
250         m_offsets.update_from_parent(old_baseline);
251         if (!legacy_array_type())
252             m_nulls.update_from_parent(old_baseline);
253     }
254     return res;
255 }
256
257 } // namespace realm
258
259 #endif // REALM_ARRAY_BINARY_HPP