aboutsummaryrefslogtreecommitdiffstats
path: root/document/src/vespa/document/fieldvalue/mapfieldvalue.h
blob: e6b8a938b90f456b179afce8a4d5d72c914e7bfc (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
/**
 * \class document::MapFieldValue
 * \ingroup fieldvalue
 *
 * \brief A fieldvalue containing fieldvalue <-> weight mappings.
 */
#pragma once

#include "fieldvalue.h"
#include <vespa/vespalib/util/polymorphicarray.h>
#include <vespa/vespalib/stllike/hash_map.h>

namespace document {

namespace mapfieldvalue {
    class HashMap;
}
class MapFieldValue final : public FieldValue
{
public:
    using IArray=vespalib::IArrayT<FieldValue>;
    using HashMapUP = std::unique_ptr<mapfieldvalue::HashMap>;
private:
    const MapDataType *_type;
    size_t             _count;
    IArray::UP         _keys;
    IArray::UP         _values;
    std::vector<bool>  _present;
    mutable HashMapUP  _lookupMap;

    virtual bool addValue(const FieldValue& fv);
    virtual bool containsValue(const FieldValue& fv) const { return contains(fv); }
    virtual bool removeValue(const FieldValue& fv) { return erase(fv); }
    bool checkAndRemove(const FieldValue& key, fieldvalue::ModificationStatus status,
                        bool wasModified, std::vector<const FieldValue*>& keysToRemove) const;
    fieldvalue::ModificationStatus onIterateNested(PathRange nested, fieldvalue::IteratorHandler & handler) const override;
    // Utility method to avoid constant explicit casting
    const MapDataType& getMapType() const { return *_type; }

    void verifyKey(const FieldValue & key) const __attribute__((noinline));
    void verifyValue(const FieldValue & value) const __attribute__((noinline));
    size_t nextPresent(size_t index) const {
        for (; index < _present.size() && !_present[index]; ++index);
        return index;
    }
    VESPA_DLL_LOCAL HashMapUP buildLookupMap() const __attribute__((noinline));
    VESPA_DLL_LOCAL void ensureLookupMap() const;
    VESPA_DLL_LOCAL ssize_t findIndex(const FieldValue& fv) const;
public:
    using UP = std::unique_ptr<MapFieldValue>;
    class iterator {
        using pair = std::pair<FieldValue *, FieldValue *>;
    public:
        iterator(MapFieldValue & map, size_t index) : _map(&map), _index(index) { }
        bool operator == (const iterator & rhs) const { return _map == rhs._map && _index == rhs._index; }
        bool operator != (const iterator & rhs) const { return _map != rhs._map || _index != rhs._index; }
        iterator& operator++() { _index = _map->nextPresent(_index+1); return *this; }
        const pair & operator * () const { setCurr(); return _current; }
        const pair * operator -> () const { setCurr(); return &_current; }
        size_t offset() const { return _index; }
    private:
        void setCurr() const {
             _current.first  = &(*_map->_keys)[_index];
             _current.second = &(*_map->_values)[_index];
        }
        MapFieldValue *_map;
        size_t         _index;
        mutable pair   _current;
    };
    class const_iterator {
    public:
        using pair = std::pair<const FieldValue *, const FieldValue *>;
        const_iterator(const MapFieldValue & map, size_t index) : _map(&map), _index(index) { }
        bool operator == (const const_iterator & rhs) const { return _map == rhs._map && _index == rhs._index; }
        bool operator != (const const_iterator & rhs) const { return _map != rhs._map || _index != rhs._index; }
        const_iterator& operator++() {  _index = _map->nextPresent(_index+1); return *this; }
        const pair & operator * () const { setCurr(); return _current; }
        const pair * operator -> () const { setCurr(); return &_current; }
    private:
        void setCurr() const {
             _current.first  = &(*_map->_keys)[_index];
             _current.second = &(*_map->_values)[_index];
        }
        const MapFieldValue *_map;
        size_t               _index;
        mutable pair         _current;
    };

    MapFieldValue(const DataType &mapType);
    virtual ~MapFieldValue();

    MapFieldValue(const MapFieldValue & rhs);
    MapFieldValue & operator = (const MapFieldValue & rhs);
    void swap(MapFieldValue & rhs);

    void accept(FieldValueVisitor &visitor) override { visitor.visit(*this); }
    void accept(ConstFieldValueVisitor &visitor) const override { visitor.visit(*this); }

    /**
     * These methods for insertion will check for uniqueness.
     **/
    bool put(FieldValue::UP key, FieldValue::UP value);
    bool put(const FieldValue& key, const FieldValue& value);
    bool insertVerify(const FieldValue& key, const FieldValue& value);
    bool insert(FieldValue::UP key, FieldValue::UP value);

    /**
     * This will just append the values to the set, assuming that the
     * new entry is not a duplicate.
     **/
    void push_back(FieldValue::UP key, FieldValue::UP value);
    void push_back(const FieldValue & key, const FieldValue & value);

    FieldValue::UP get(const FieldValue& key) const;
    bool erase(const FieldValue& key);
    bool contains(const FieldValue& key) const;

    // CollectionFieldValue methods kept for compatability's sake
    virtual bool isEmpty() const { return _count == 0; }
    virtual size_t size() const { return _count; }
    virtual void clear();
    void reserve(size_t sz);
    void resize(size_t sz);

    fieldvalue::ModificationStatus iterateNestedImpl(PathRange nested, fieldvalue::IteratorHandler & handler,
                                                     const FieldValue& complexFieldValue) const;

    // FieldValue implementation
    FieldValue& assign(const FieldValue&) override;
    MapFieldValue* clone() const override { return new MapFieldValue(*this); }
    int compare(const FieldValue&) const override;
    void print(std::ostream& out, bool verbose, const std::string& indent) const override;
    const DataType *getDataType() const override;
    void printXml(XmlOutputStream& out) const override;

    const_iterator begin() const { return const_iterator(*this, nextPresent(0)); }
    iterator begin() { return iterator(*this, nextPresent(0)); }

    const_iterator end() const { return const_iterator(*this, _present.size()); }
    iterator end() { return iterator(*this, _present.size()); }

    const_iterator find(const FieldValue& fv) const;
    iterator find(const FieldValue& fv);

    bool has_no_erased_keys() const {
        return (_keys->size() == _count) && (_values->size() == _count);
    }

    /**
     * Returns the key-value pair at the given position in the underlying arrays.
     *
     * Note: Should only be used when has_no_erased_keys() returns true.
     * Otherwise you might access elements that are conceptually removed.
     */
    const_iterator::pair operator[](size_t idx) const {
        return const_iterator::pair(&(*_keys)[idx], &(*_values)[idx]);
    }

    FieldValue::UP createValue() const;
};

}  // namespace document