admin
2023-03-07 8b06b1cbf112d55307ea8a6efe711db4e7506d89
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
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
// Copyright 2014 The Crashpad Authors. All rights reserved.
//
// 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 CRASHPAD_CLIENT_SIMPLE_STRING_DICTIONARY_H_
#define CRASHPAD_CLIENT_SIMPLE_STRING_DICTIONARY_H_
 
#include <string.h>
#include <sys/types.h>
 
#include <algorithm>
#include <type_traits>
 
#include "base/check_op.h"
#include "base/macros.h"
#include "base/strings/string_piece.h"
#include "util/misc/implicit_cast.h"
 
namespace crashpad {
 
//! \brief A map/dictionary collection implementation using a fixed amount of
//!     storage, so that it does not perform any dynamic allocations for its
//!     operations.
//!
//! The actual map storage (TSimpleStringDictionary::Entry) is guaranteed to be
//! POD, so that it can be transmitted over various IPC mechanisms.
//!
//! The template parameters control the amount of storage used for the key,
//! value, and map. The \a KeySize and \a ValueSize are measured in bytes, not
//! glyphs, and include space for a trailing `NUL` byte. This gives space for
//! `KeySize - 1` and `ValueSize - 1` characters in an entry. \a NumEntries is
//! the total number of entries that will fit in the map.
template <size_t KeySize = 256, size_t ValueSize = 256, size_t NumEntries = 64>
class TSimpleStringDictionary {
 public:
  //! \brief Constant and publicly accessible versions of the template
  //!     parameters.
  //! \{
  static const size_t key_size = KeySize;
  static const size_t value_size = ValueSize;
  static const size_t num_entries = NumEntries;
  //! \}
 
  //! \brief A single entry in the map.
  struct Entry {
    //! \brief The entry’s key.
    //!
    //! This string is always `NUL`-terminated. If this is a 0-length
    //! `NUL`-terminated string, the entry is inactive.
    char key[KeySize];
 
    //! \brief The entry’s value.
    //!
    //! This string is always `NUL`-terminated.
    char value[ValueSize];
 
    //! \brief Returns the validity of the entry.
    //!
    //! If #key is an empty string, the entry is considered inactive, and this
    //! method returns `false`. Otherwise, returns `true`.
    bool is_active() const {
      return key[0] != '\0';
    }
  };
 
  //! \brief An iterator to traverse all of the active entries in a
  //!     TSimpleStringDictionary.
  class Iterator {
   public:
    explicit Iterator(const TSimpleStringDictionary& map)
        : map_(map),
          current_(0) {
    }
 
    //! \brief Returns the next entry in the map, or `nullptr` if at the end of
    //!     the collection.
    const Entry* Next() {
      while (current_ < map_.num_entries) {
        const Entry* entry = &map_.entries_[current_++];
        if (entry->is_active()) {
          return entry;
        }
      }
      return nullptr;
    }
 
   private:
    const TSimpleStringDictionary& map_;
    size_t current_;
 
    DISALLOW_COPY_AND_ASSIGN(Iterator);
  };
 
  TSimpleStringDictionary()
      : entries_() {
  }
 
  TSimpleStringDictionary(const TSimpleStringDictionary& other) {
    *this = other;
  }
 
  TSimpleStringDictionary& operator=(const TSimpleStringDictionary& other) {
    memcpy(entries_, other.entries_, sizeof(entries_));
    return *this;
  }
 
  //! \brief Returns the number of active key/value pairs. The upper limit for
  //!     this is \a NumEntries.
  size_t GetCount() const {
    size_t count = 0;
    for (size_t i = 0; i < num_entries; ++i) {
      if (entries_[i].is_active()) {
        ++count;
      }
    }
    return count;
  }
 
  //! \brief Given \a key, returns its corresponding value.
  //!
  //! \param[in] key The key to look up. This must not be `nullptr`, nor an
  //!     empty string. It must not contain embedded `NUL`s.
  //!
  //! \return The corresponding value for \a key, or if \a key is not found,
  //!     `nullptr`.
  const char* GetValueForKey(base::StringPiece key) const {
    DCHECK(key.data());
    DCHECK(key.size());
    DCHECK_EQ(key.find('\0', 0), base::StringPiece::npos);
    if (!key.data() || !key.size()) {
      return nullptr;
    }
 
    const Entry* entry = GetConstEntryForKey(key);
    if (!entry) {
      return nullptr;
    }
 
    return entry->value;
  }
 
  //! \brief Stores \a value into \a key, replacing the existing value if \a key
  //!     is already present.
  //!
  //! If \a key is not yet in the map and the map is already full (containing
  //! \a NumEntries active entries), this operation silently fails.
  //!
  //! \param[in] key The key to store. This must not be `nullptr`, nor an empty
  //!     string. It must not contain embedded `NUL`s.
  //! \param[in] value The value to store. If `nullptr`, \a key is removed from
  //!     the map. Must not contain embedded `NUL`s.
  void SetKeyValue(base::StringPiece key, base::StringPiece value) {
    if (!value.data()) {
      RemoveKey(key);
      return;
    }
 
    DCHECK(key.data());
    DCHECK(key.size());
    DCHECK_EQ(key.find('\0', 0), base::StringPiece::npos);
    if (!key.data() || !key.size()) {
      return;
    }
 
    // |key| must not be an empty string.
    DCHECK_NE(key[0], '\0');
    if (key[0] == '\0') {
      return;
    }
 
    // |value| must not contain embedded NULs.
    DCHECK_EQ(value.find('\0', 0), base::StringPiece::npos);
 
    Entry* entry = GetEntryForKey(key);
 
    // If it does not yet exist, attempt to insert it.
    if (!entry) {
      for (size_t i = 0; i < num_entries; ++i) {
        if (!entries_[i].is_active()) {
          entry = &entries_[i];
          SetFromStringPiece(key, entry->key, key_size);
          break;
        }
      }
    }
 
    // If the map is out of space, |entry| will be nullptr.
    if (!entry) {
      return;
    }
 
#ifndef NDEBUG
    // Sanity check that the key only appears once.
    int count = 0;
    for (size_t i = 0; i < num_entries; ++i) {
      if (EntryKeyEquals(key, entries_[i])) {
        ++count;
      }
    }
    DCHECK_EQ(count, 1);
#endif
 
    SetFromStringPiece(value, entry->value, value_size);
  }
 
  //! \brief Removes \a key from the map.
  //!
  //! If \a key is not found, this is a no-op.
  //!
  //! \param[in] key The key of the entry to remove. This must not be `nullptr`,
  //!     nor an empty string. It must not contain embedded `NUL`s.
  void RemoveKey(base::StringPiece key) {
    DCHECK(key.data());
    DCHECK(key.size());
    DCHECK_EQ(key.find('\0', 0), base::StringPiece::npos);
    if (!key.data() || !key.size()) {
      return;
    }
 
    Entry* entry = GetEntryForKey(key);
    if (entry) {
      entry->key[0] = '\0';
      entry->value[0] = '\0';
    }
 
    DCHECK_EQ(GetEntryForKey(key), implicit_cast<Entry*>(nullptr));
  }
 
 private:
  static void SetFromStringPiece(base::StringPiece src,
                                 char* dst,
                                 size_t dst_size) {
    size_t copy_len = std::min(dst_size - 1, src.size());
    src.copy(dst, copy_len);
    dst[copy_len] = '\0';
  }
 
  static bool EntryKeyEquals(base::StringPiece key, const Entry& entry) {
    if (key.size() >= KeySize)
      return false;
 
    // Test for a NUL terminator and early out if it's absent.
    if (entry.key[key.size()] != '\0')
      return false;
 
    // As there's a NUL terminator at the right position in the entries
    // string, strncmp can do the rest.
    return strncmp(key.data(), entry.key, key.size()) == 0;
  }
 
  const Entry* GetConstEntryForKey(base::StringPiece key) const {
    for (size_t i = 0; i < num_entries; ++i) {
      if (EntryKeyEquals(key, entries_[i])) {
        return &entries_[i];
      }
    }
    return nullptr;
  }
 
  Entry* GetEntryForKey(base::StringPiece key) {
    return const_cast<Entry*>(GetConstEntryForKey(key));
  }
 
  Entry entries_[NumEntries];
};
 
//! \brief A TSimpleStringDictionary with default template parameters.
//!
//! For historical reasons this specialized version is available with the same
//! size factors as a previous implementation.
using SimpleStringDictionary = TSimpleStringDictionary<256, 256, 64>;
 
static_assert(std::is_standard_layout<SimpleStringDictionary>::value,
              "SimpleStringDictionary must be standard layout");
 
}  // namespace crashpad
 
#endif  // CRASHPAD_CLIENT_SIMPLE_STRING_DICTIONARY_H_