summaryrefslogtreecommitdiff
path: root/src/mongo/db/field_ref.h
blob: ed3a110715baf7bb8f86ae5874f4f3dc7d027431 (plain)
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
/**
 *    Copyright (C) 2012 10gen Inc.
 *
 *    This program is free software: you can redistribute it and/or  modify
 *    it under the terms of the GNU Affero General Public License, version 3,
 *    as published by the Free Software Foundation.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU Affero General Public License for more details.
 *
 *    You should have received a copy of the GNU Affero General Public License
 *    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#pragma once

#include <boost/scoped_array.hpp>
#include <string>
#include <vector>

#include "mongo/base/disallow_copying.h"
#include "mongo/base/string_data.h"

namespace mongo {

    /**
     * A FieldPath represents a path in a document, starting from the root. The path
     * is made of "field parts" separated by dots. The class provides an efficient means to
     * "split" the dotted fields in its parts, but no validation is done.
     *
     * Any field part may be replaced, after the "original" field reference was parsed. Any
     * part can be accessed through a StringData object.
     *
     * The class is not thread safe.
     */
    class FieldRef {
        MONGO_DISALLOW_COPYING(FieldRef);
    public:
        FieldRef() : _size(0) {}

        /**
         * Field parts accessed through getPart() calls no longer would be valid, after the
         * destructor ran.
         */
        ~FieldRef() {}

        /**
         * Builds a field path out of each field part in 'dottedField'.
         */
        void parse(const StringData& dottedField);

        /**
         * Sets the 'i-th' field part to point to 'part'. Assumes i < size(). Behavior is
         * undefined otherwise.
         */
        void setPart(size_t i, const StringData& part);

        /**
         * Returns the 'i-th' field part. Assumes i < size(). Behavior is undefined otherwise.
         */
        StringData getPart(size_t i) const;

        /**
         * Returns a copy of the full dotted field in its current state (i.e., some parts may
         * have been replaced since the parse() call).
         */
        std::string dottedField( size_t offset = 0 ) const;

        /**
         * Resets the internal state. See note in parse() call.
         */
        void clear();

        //
        // accessors
        //

        /**
         * Returns the number of parts in this FieldRef.
         */
        size_t numParts() const { return _size; }

        /**
         * Returns the number of fields parts that were replaced so far. Replacing the same
         * fields several times only counts for 1.
         */
        size_t numReplaced() const;

        /**
         * Compares the full dotted path represented by this FieldRef to other
         */
        bool equalsDottedField( const StringData& other ) const;

        /**
         * Compares the full dotted path represented by this FieldRef to other,
         * and that we are a prefix of them.
         *
         * Returns true when 'this' is a prefix of 'other'; equality is not considered a prefix.
         */
        bool isPrefixOf( const FieldRef& other ) const;

    private:
        // Dotted fields are most often not longer than three parts. We use a mixed structure
        // here that will not require any extra memory allocation when that is the case. And
        // handle larger dotted fields if it is. The idea is not to penalize the common case
        // with allocations.
        static const size_t kReserveAhead = 4;

        size_t _size;                                // # of field parts stored
        StringData _fixed[kReserveAhead];            // first kResevedAhead field components
        std::vector<StringData> _variable;           // remaining field components

        // Areas that _fixed and _variable point to.
        boost::scoped_array<char> _fieldBase;        // concatenation of null-terminated parts
        std::vector<std::string> _replacements;      // added with the setPart call

        /** Converts the field part index to the variable part equivalent */
        size_t getIndex(size_t i) const { return i-kReserveAhead; }

        /**
         * Returns the new number of parts after appending 'part' to this field path. It
         * assumes that 'part' is pointing to an internally allocated area.
         */
        size_t appendPart(const StringData& part);

    };

    inline bool operator==(const FieldRef& lhs, const FieldRef& rhs) {
        return lhs.equalsDottedField(rhs.dottedField());
    }

} // namespace mongo