/************************************************************************* * * Copyright 2016 Realm Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * **************************************************************************/ #ifndef REALM_ARRAY_STRING_SHORT_HPP #define REALM_ARRAY_STRING_SHORT_HPP #include <realm/array.hpp> #include <realm/string_data.hpp> namespace realm { /* ArrayStringShort stores strings as a concecutive list of fixed-length blocks of m_width bytes. The longest string it can store is (m_width - 1) bytes before it needs to expand. An example of the format for m_width = 4 is following sequence of bytes, where x is payload: xxx0 xx01 x002 0003 0004 (strings "xxx",. "xx", "x", "", realm::null()) So each string is 0 terminated, and the last byte in a block tells how many 0s are present, except for a realm::null() which has the byte set to m_width (4). The byte is used to compute the length of a string in various functions. New: If m_witdh = 0, then all elements are realm::null(). So to add an empty string we must expand m_width New: StringData is null() if-and-only-if StringData::data() == 0. */ class ArrayStringShort : public Array { public: static const size_t max_width = 64; typedef StringData value_type; // Constructor defaults to non-nullable because we use non-nullable ArrayStringShort so many places internally // in core (data which isn't user payload) where null isn't needed. explicit ArrayStringShort(Allocator&, bool nullable = false) noexcept; ~ArrayStringShort() noexcept override { } bool is_null(size_t ndx) const; void set_null(size_t ndx); StringData get(size_t ndx) const noexcept; void add(); void add(StringData value); void set(size_t ndx, StringData value); void insert(size_t ndx, StringData value); void erase(size_t ndx); size_t count(StringData value, size_t begin = 0, size_t end = npos) const noexcept; size_t find_first(StringData value, size_t begin = 0, size_t end = npos) const noexcept; void find_all(IntegerColumn& result, StringData value, size_t add_offset = 0, size_t begin = 0, size_t end = npos); /// Compare two string arrays for equality. bool compare_string(const ArrayStringShort&) const noexcept; /// Get the specified element without the cost of constructing an /// array instance. If an array instance is already available, or /// you need to get multiple values, then this method will be /// slower. static StringData get(const char* header, size_t ndx, bool nullable) noexcept; /// Construct a string array of the specified size and return just /// the reference to the underlying memory. All elements will be /// initialized to the empty string. static MemRef create_array(size_t size, Allocator&); /// Create a new empty string array and attach this accessor to /// it. This does not modify the parent reference information of /// this accessor. /// /// Note that the caller assumes ownership of the allocated /// underlying node. It is not owned by the accessor. void create(); #ifdef REALM_DEBUG void string_stats() const; #endif private: size_t calc_byte_len(size_t num_items, size_t width) const override; size_t calc_item_count(size_t bytes, size_t width) const noexcept override; bool m_nullable; }; // Implementation: // Creates new array (but invalid, call init_from_ref() to init) inline ArrayStringShort::ArrayStringShort(Allocator& allocator, bool nullable) noexcept : Array(allocator) , m_nullable(nullable) { } inline void ArrayStringShort::create() { size_t init_size = 0; MemRef mem = create_array(init_size, get_alloc()); // Throws init_from_mem(mem); } inline MemRef ArrayStringShort::create_array(size_t init_size, Allocator& allocator) { bool context_flag = false; int_fast64_t value = 0; return Array::create(type_Normal, context_flag, wtype_Multiply, init_size, value, allocator); // Throws } inline StringData ArrayStringShort::get(size_t ndx) const noexcept { REALM_ASSERT_3(ndx, <, m_size); if (m_width == 0) return m_nullable ? realm::null() : StringData(""); const char* data = m_data + (ndx * m_width); size_t array_size = (m_width - 1) - data[m_width - 1]; if (array_size == static_cast<size_t>(-1)) return m_nullable ? realm::null() : StringData(""); REALM_ASSERT_EX(data[array_size] == 0, data[array_size], array_size); // Realm guarantees 0 terminated return strings return StringData(data, array_size); } inline void ArrayStringShort::add(StringData value) { REALM_ASSERT(!(!m_nullable && value.is_null())); insert(m_size, value); // Throws } inline void ArrayStringShort::add() { add(m_nullable ? realm::null() : StringData("")); // Throws } inline StringData ArrayStringShort::get(const char* header, size_t ndx, bool nullable) noexcept { REALM_ASSERT(ndx < get_size_from_header(header)); uint_least8_t width = get_width_from_header(header); const char* data = get_data_from_header(header) + (ndx * width); if (width == 0) return nullable ? realm::null() : StringData(""); size_t size = (width - 1) - data[width - 1]; if (size == static_cast<size_t>(-1)) return nullable ? realm::null() : StringData(""); return StringData(data, size); } } // namespace realm #endif // REALM_ARRAY_STRING_HPP