1 /*
2 * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
11 #include "modules/rtp_rtcp/source/rtp_sequence_number_map.h"
12
13 #include <algorithm>
14 #include <iterator>
15 #include <limits>
16 #include <memory>
17 #include <tuple>
18 #include <vector>
19
20 #include "absl/memory/memory.h"
21 #include "absl/types/optional.h"
22 #include "rtc_base/checks.h"
23 #include "rtc_base/numerics/sequence_number_util.h"
24 #include "rtc_base/random.h"
25 #include "test/gtest.h"
26
27 namespace webrtc {
28 namespace {
29 using Info = RtpSequenceNumberMap::Info;
30
31 constexpr uint16_t kUint16Max = std::numeric_limits<uint16_t>::max();
32 constexpr size_t kMaxPossibleMaxEntries = 1 << 15;
33
34 // Just a named pair.
35 struct Association final {
Associationwebrtc::__anon8a1913970111::Association36 Association(uint16_t sequence_number, Info info)
37 : sequence_number(sequence_number), info(info) {}
38
39 uint16_t sequence_number;
40 Info info;
41 };
42
43 class RtpSequenceNumberMapTest : public ::testing::Test {
44 protected:
RtpSequenceNumberMapTest()45 RtpSequenceNumberMapTest() : random_(1983) {}
46 ~RtpSequenceNumberMapTest() override = default;
47
CreateAssociation(uint16_t sequence_number,uint32_t timestamp)48 Association CreateAssociation(uint16_t sequence_number, uint32_t timestamp) {
49 return Association(sequence_number,
50 {timestamp, random_.Rand<bool>(), random_.Rand<bool>()});
51 }
52
VerifyAssociations(const RtpSequenceNumberMap & uut,const std::vector<Association> & associations)53 void VerifyAssociations(const RtpSequenceNumberMap& uut,
54 const std::vector<Association>& associations) {
55 return VerifyAssociations(uut, associations.begin(), associations.end());
56 }
57
VerifyAssociations(const RtpSequenceNumberMap & uut,std::vector<Association>::const_iterator associations_begin,std::vector<Association>::const_iterator associations_end)58 void VerifyAssociations(
59 const RtpSequenceNumberMap& uut,
60 std::vector<Association>::const_iterator associations_begin,
61 std::vector<Association>::const_iterator associations_end) {
62 RTC_DCHECK(associations_begin < associations_end);
63 ASSERT_EQ(static_cast<size_t>(associations_end - associations_begin),
64 uut.AssociationCountForTesting());
65 for (auto association = associations_begin; association != associations_end;
66 ++association) {
67 EXPECT_EQ(uut.Get(association->sequence_number), association->info);
68 }
69 }
70
71 // Allows several variations of the same test; definition next to the tests.
72 void GetObsoleteSequenceNumberReturnsNullOptMultipleEntriesObsoleted(
73 bool with_wrap_around,
74 bool last_element_kept);
75
76 // Allows several variations of the same test; definition next to the tests.
77 void RepeatedSequenceNumberInvalidatesAll(size_t index_of_repeated);
78
79 // Allows several variations of the same test; definition next to the tests.
80 void MaxEntriesReachedAtSameTimeAsObsoletionOfItem(size_t max_entries,
81 size_t obsoleted_count);
82
83 Random random_;
84 };
85
86 class RtpSequenceNumberMapTestWithParams
87 : public RtpSequenceNumberMapTest,
88 public ::testing::WithParamInterface<std::tuple<size_t, uint16_t>> {
89 protected:
90 RtpSequenceNumberMapTestWithParams() = default;
91 ~RtpSequenceNumberMapTestWithParams() override = default;
92
ProduceRandomAssociationSequence(size_t association_count,uint16_t first_sequence_number,bool allow_obsoletion)93 std::vector<Association> ProduceRandomAssociationSequence(
94 size_t association_count,
95 uint16_t first_sequence_number,
96 bool allow_obsoletion) {
97 std::vector<Association> associations;
98 associations.reserve(association_count);
99
100 if (association_count == 0) {
101 return associations;
102 }
103
104 associations.emplace_back(
105 first_sequence_number,
106 Info(0, random_.Rand<bool>(), random_.Rand<bool>()));
107
108 for (size_t i = 1; i < association_count; ++i) {
109 const uint16_t sequence_number =
110 associations[i - 1].sequence_number + random_.Rand(1, 100);
111 RTC_DCHECK(allow_obsoletion ||
112 AheadOf(sequence_number, associations[0].sequence_number));
113
114 const uint32_t timestamp =
115 associations[i - 1].info.timestamp + random_.Rand(1, 10000);
116
117 associations.emplace_back(
118 sequence_number,
119 Info(timestamp, random_.Rand<bool>(), random_.Rand<bool>()));
120 }
121
122 return associations;
123 }
124 };
125
126 INSTANTIATE_TEST_SUITE_P(All,
127 RtpSequenceNumberMapTestWithParams,
128 ::testing::Combine(
129 // Association count.
130 ::testing::Values(1, 2, 100),
131 // First sequence number.
132 ::testing::Values(0,
133 100,
134 kUint16Max - 100,
135 kUint16Max - 1,
136 kUint16Max)));
137
TEST_F(RtpSequenceNumberMapTest,GetBeforeAssociationsRecordedReturnsNullOpt)138 TEST_F(RtpSequenceNumberMapTest, GetBeforeAssociationsRecordedReturnsNullOpt) {
139 RtpSequenceNumberMap uut(kMaxPossibleMaxEntries);
140 constexpr uint16_t kArbitrarySequenceNumber = 321;
141 EXPECT_FALSE(uut.Get(kArbitrarySequenceNumber));
142 }
143
144 // Version #1 - any old unknown sequence number.
TEST_F(RtpSequenceNumberMapTest,GetUnknownSequenceNumberReturnsNullOpt1)145 TEST_F(RtpSequenceNumberMapTest, GetUnknownSequenceNumberReturnsNullOpt1) {
146 RtpSequenceNumberMap uut(kMaxPossibleMaxEntries);
147
148 constexpr uint16_t kKnownSequenceNumber = 10;
149 constexpr uint32_t kArbitrary = 987;
150 uut.InsertPacket(kKnownSequenceNumber, {kArbitrary, false, false});
151
152 constexpr uint16_t kUnknownSequenceNumber = kKnownSequenceNumber + 1;
153 EXPECT_FALSE(uut.Get(kUnknownSequenceNumber));
154 }
155
156 // Version #2 - intentionally pick a value in the range of currently held
157 // values, so as to trigger lower_bound / upper_bound.
TEST_F(RtpSequenceNumberMapTest,GetUnknownSequenceNumberReturnsNullOpt2)158 TEST_F(RtpSequenceNumberMapTest, GetUnknownSequenceNumberReturnsNullOpt2) {
159 RtpSequenceNumberMap uut(kMaxPossibleMaxEntries);
160
161 const std::vector<Association> setup = {CreateAssociation(1000, 500), //
162 CreateAssociation(1020, 501)};
163 for (const Association& association : setup) {
164 uut.InsertPacket(association.sequence_number, association.info);
165 }
166
167 EXPECT_FALSE(uut.Get(1001));
168 }
169
TEST_P(RtpSequenceNumberMapTestWithParams,GetKnownSequenceNumberReturnsCorrectValue)170 TEST_P(RtpSequenceNumberMapTestWithParams,
171 GetKnownSequenceNumberReturnsCorrectValue) {
172 RtpSequenceNumberMap uut(kMaxPossibleMaxEntries);
173
174 const size_t association_count = std::get<0>(GetParam());
175 const uint16_t first_sequence_number = std::get<1>(GetParam());
176
177 const std::vector<Association> associations =
178 ProduceRandomAssociationSequence(association_count, first_sequence_number,
179 /*allow_obsoletion=*/false);
180
181 for (const Association& association : associations) {
182 uut.InsertPacket(association.sequence_number, association.info);
183 }
184
185 VerifyAssociations(uut, associations);
186 }
187
TEST_F(RtpSequenceNumberMapTest,InsertFrameOnSinglePacketFrame)188 TEST_F(RtpSequenceNumberMapTest, InsertFrameOnSinglePacketFrame) {
189 RtpSequenceNumberMap uut(kMaxPossibleMaxEntries);
190
191 constexpr uint16_t kSequenceNumber = 888;
192 constexpr uint32_t kTimestamp = 98765;
193 uut.InsertFrame(kSequenceNumber, 1, kTimestamp);
194
195 EXPECT_EQ(uut.Get(kSequenceNumber), Info(kTimestamp, true, true));
196 }
197
TEST_F(RtpSequenceNumberMapTest,InsertFrameOnMultiPacketFrameNoWrapAround)198 TEST_F(RtpSequenceNumberMapTest, InsertFrameOnMultiPacketFrameNoWrapAround) {
199 RtpSequenceNumberMap uut(kMaxPossibleMaxEntries);
200
201 constexpr uint16_t kFirstSequenceNumber = 0;
202 constexpr uint32_t kTimestamp = 98765;
203 uut.InsertFrame(kFirstSequenceNumber, 3, kTimestamp);
204
205 EXPECT_EQ(uut.Get(kFirstSequenceNumber + 0), Info(kTimestamp, true, false));
206 EXPECT_EQ(uut.Get(kFirstSequenceNumber + 1), Info(kTimestamp, false, false));
207 EXPECT_EQ(uut.Get(kFirstSequenceNumber + 2), Info(kTimestamp, false, true));
208 }
209
TEST_F(RtpSequenceNumberMapTest,InsertFrameOnMultiPacketFrameWithWrapAround)210 TEST_F(RtpSequenceNumberMapTest, InsertFrameOnMultiPacketFrameWithWrapAround) {
211 RtpSequenceNumberMap uut(kMaxPossibleMaxEntries);
212
213 constexpr uint16_t kFirstSequenceNumber = kUint16Max;
214 constexpr uint32_t kTimestamp = 98765;
215 uut.InsertFrame(kFirstSequenceNumber, 3, kTimestamp);
216
217 // Suppress "truncation of constant value" warning; wrap-around is intended.
218 #ifdef _MSC_VER
219 #pragma warning(push)
220 #pragma warning(disable : 4309)
221 #endif
222 EXPECT_EQ(uut.Get(static_cast<uint16_t>(kFirstSequenceNumber + 0u)),
223 Info(kTimestamp, true, false));
224 EXPECT_EQ(uut.Get(static_cast<uint16_t>(kFirstSequenceNumber + 1u)),
225 Info(kTimestamp, false, false));
226 EXPECT_EQ(uut.Get(static_cast<uint16_t>(kFirstSequenceNumber + 2u)),
227 Info(kTimestamp, false, true));
228 #ifdef _MSC_VER
229 #pragma warning(pop)
230 #endif
231 }
232
TEST_F(RtpSequenceNumberMapTest,GetObsoleteSequenceNumberReturnsNullOptSingleValueObsoleted)233 TEST_F(RtpSequenceNumberMapTest,
234 GetObsoleteSequenceNumberReturnsNullOptSingleValueObsoleted) {
235 RtpSequenceNumberMap uut(kMaxPossibleMaxEntries);
236
237 const std::vector<Association> associations = {
238 CreateAssociation(0, 10), //
239 CreateAssociation(0x8000, 20), //
240 CreateAssociation(0x8001u, 30)};
241
242 uut.InsertPacket(associations[0].sequence_number, associations[0].info);
243
244 // First association not yet obsolete, and therefore remembered.
245 RTC_DCHECK(AheadOf(associations[1].sequence_number,
246 associations[0].sequence_number));
247 uut.InsertPacket(associations[1].sequence_number, associations[1].info);
248 VerifyAssociations(uut, {associations[0], associations[1]});
249
250 // Test focus - new entry obsoletes first entry.
251 RTC_DCHECK(!AheadOf(associations[2].sequence_number,
252 associations[0].sequence_number));
253 uut.InsertPacket(associations[2].sequence_number, associations[2].info);
254 VerifyAssociations(uut, {associations[1], associations[2]});
255 }
256
257 void RtpSequenceNumberMapTest::
GetObsoleteSequenceNumberReturnsNullOptMultipleEntriesObsoleted(bool with_wrap_around,bool last_element_kept)258 GetObsoleteSequenceNumberReturnsNullOptMultipleEntriesObsoleted(
259 bool with_wrap_around,
260 bool last_element_kept) {
261 RtpSequenceNumberMap uut(kMaxPossibleMaxEntries);
262
263 std::vector<Association> associations;
264 if (with_wrap_around) {
265 associations = {CreateAssociation(kUint16Max - 1, 10), //
266 CreateAssociation(kUint16Max, 20), //
267 CreateAssociation(0, 30), //
268 CreateAssociation(1, 40), //
269 CreateAssociation(2, 50)};
270 } else {
271 associations = {CreateAssociation(1, 10), //
272 CreateAssociation(2, 20), //
273 CreateAssociation(3, 30), //
274 CreateAssociation(4, 40), //
275 CreateAssociation(5, 50)};
276 }
277
278 for (auto association : associations) {
279 uut.InsertPacket(association.sequence_number, association.info);
280 }
281 VerifyAssociations(uut, associations);
282
283 // Define a new association that will obsolete either all previous entries,
284 // or all previous entries except for the last one, depending on the
285 // parameter instantiation of this test.
286 RTC_DCHECK_EQ(
287 static_cast<uint16_t>(
288 associations[associations.size() - 1].sequence_number),
289 static_cast<uint16_t>(
290 associations[associations.size() - 2].sequence_number + 1u));
291 uint16_t new_sequence_number;
292 if (last_element_kept) {
293 new_sequence_number =
294 associations[associations.size() - 1].sequence_number + 0x8000;
295 RTC_DCHECK(AheadOf(new_sequence_number,
296 associations[associations.size() - 1].sequence_number));
297 } else {
298 new_sequence_number =
299 associations[associations.size() - 1].sequence_number + 0x8001;
300 RTC_DCHECK(!AheadOf(new_sequence_number,
301 associations[associations.size() - 1].sequence_number));
302 }
303 RTC_DCHECK(!AheadOf(new_sequence_number,
304 associations[associations.size() - 2].sequence_number));
305
306 // Record the new association.
307 const Association new_association =
308 CreateAssociation(new_sequence_number, 60);
309 uut.InsertPacket(new_association.sequence_number, new_association.info);
310
311 // Make sure all obsoleted elements were removed.
312 const size_t obsoleted_count =
313 associations.size() - (last_element_kept ? 1 : 0);
314 for (size_t i = 0; i < obsoleted_count; ++i) {
315 EXPECT_FALSE(uut.Get(associations[i].sequence_number));
316 }
317
318 // Make sure the expected elements were not removed, and return the
319 // expected value.
320 if (last_element_kept) {
321 EXPECT_EQ(uut.Get(associations.back().sequence_number),
322 associations.back().info);
323 }
324 EXPECT_EQ(uut.Get(new_association.sequence_number), new_association.info);
325 }
326
TEST_F(RtpSequenceNumberMapTest,GetObsoleteSequenceNumberReturnsNullOptMultipleEntriesObsoleted0)327 TEST_F(RtpSequenceNumberMapTest,
328 GetObsoleteSequenceNumberReturnsNullOptMultipleEntriesObsoleted0) {
329 const bool with_wrap_around = false;
330 const bool last_element_kept = false;
331 GetObsoleteSequenceNumberReturnsNullOptMultipleEntriesObsoleted(
332 with_wrap_around, last_element_kept);
333 }
334
TEST_F(RtpSequenceNumberMapTest,GetObsoleteSequenceNumberReturnsNullOptMultipleEntriesObsoleted1)335 TEST_F(RtpSequenceNumberMapTest,
336 GetObsoleteSequenceNumberReturnsNullOptMultipleEntriesObsoleted1) {
337 const bool with_wrap_around = true;
338 const bool last_element_kept = false;
339 GetObsoleteSequenceNumberReturnsNullOptMultipleEntriesObsoleted(
340 with_wrap_around, last_element_kept);
341 }
342
TEST_F(RtpSequenceNumberMapTest,GetObsoleteSequenceNumberReturnsNullOptMultipleEntriesObsoleted2)343 TEST_F(RtpSequenceNumberMapTest,
344 GetObsoleteSequenceNumberReturnsNullOptMultipleEntriesObsoleted2) {
345 const bool with_wrap_around = false;
346 const bool last_element_kept = true;
347 GetObsoleteSequenceNumberReturnsNullOptMultipleEntriesObsoleted(
348 with_wrap_around, last_element_kept);
349 }
350
TEST_F(RtpSequenceNumberMapTest,GetObsoleteSequenceNumberReturnsNullOptMultipleEntriesObsoleted3)351 TEST_F(RtpSequenceNumberMapTest,
352 GetObsoleteSequenceNumberReturnsNullOptMultipleEntriesObsoleted3) {
353 const bool with_wrap_around = true;
354 const bool last_element_kept = true;
355 GetObsoleteSequenceNumberReturnsNullOptMultipleEntriesObsoleted(
356 with_wrap_around, last_element_kept);
357 }
358
RepeatedSequenceNumberInvalidatesAll(size_t index_of_repeated)359 void RtpSequenceNumberMapTest::RepeatedSequenceNumberInvalidatesAll(
360 size_t index_of_repeated) {
361 RtpSequenceNumberMap uut(kMaxPossibleMaxEntries);
362
363 const std::vector<Association> setup = {CreateAssociation(100, 500), //
364 CreateAssociation(101, 501), //
365 CreateAssociation(102, 502)};
366 RTC_DCHECK_LT(index_of_repeated, setup.size());
367 for (const Association& association : setup) {
368 uut.InsertPacket(association.sequence_number, association.info);
369 }
370
371 const Association new_association =
372 CreateAssociation(setup[index_of_repeated].sequence_number, 503);
373 uut.InsertPacket(new_association.sequence_number, new_association.info);
374
375 // All entries from setup invalidated.
376 // New entry valid and mapped to new value.
377 for (size_t i = 0; i < setup.size(); ++i) {
378 if (i == index_of_repeated) {
379 EXPECT_EQ(uut.Get(new_association.sequence_number), new_association.info);
380 } else {
381 EXPECT_FALSE(uut.Get(setup[i].sequence_number));
382 }
383 }
384 }
385
TEST_F(RtpSequenceNumberMapTest,RepeatedSequenceNumberInvalidatesAllRepeatFirst)386 TEST_F(RtpSequenceNumberMapTest,
387 RepeatedSequenceNumberInvalidatesAllRepeatFirst) {
388 RepeatedSequenceNumberInvalidatesAll(0);
389 }
390
TEST_F(RtpSequenceNumberMapTest,RepeatedSequenceNumberInvalidatesAllRepeatMiddle)391 TEST_F(RtpSequenceNumberMapTest,
392 RepeatedSequenceNumberInvalidatesAllRepeatMiddle) {
393 RepeatedSequenceNumberInvalidatesAll(1);
394 }
395
TEST_F(RtpSequenceNumberMapTest,RepeatedSequenceNumberInvalidatesAllRepeatLast)396 TEST_F(RtpSequenceNumberMapTest,
397 RepeatedSequenceNumberInvalidatesAllRepeatLast) {
398 RepeatedSequenceNumberInvalidatesAll(2);
399 }
400
TEST_F(RtpSequenceNumberMapTest,SequenceNumberInsideMemorizedRangeInvalidatesAll)401 TEST_F(RtpSequenceNumberMapTest,
402 SequenceNumberInsideMemorizedRangeInvalidatesAll) {
403 RtpSequenceNumberMap uut(kMaxPossibleMaxEntries);
404
405 const std::vector<Association> setup = {CreateAssociation(1000, 500), //
406 CreateAssociation(1020, 501), //
407 CreateAssociation(1030, 502)};
408 for (const Association& association : setup) {
409 uut.InsertPacket(association.sequence_number, association.info);
410 }
411
412 const Association new_association = CreateAssociation(1010, 503);
413 uut.InsertPacket(new_association.sequence_number, new_association.info);
414
415 // All entries from setup invalidated.
416 // New entry valid and mapped to new value.
417 for (size_t i = 0; i < setup.size(); ++i) {
418 EXPECT_FALSE(uut.Get(setup[i].sequence_number));
419 }
420 EXPECT_EQ(uut.Get(new_association.sequence_number), new_association.info);
421 }
422
TEST_F(RtpSequenceNumberMapTest,MaxEntriesObserved)423 TEST_F(RtpSequenceNumberMapTest, MaxEntriesObserved) {
424 constexpr size_t kMaxEntries = 100;
425 RtpSequenceNumberMap uut(kMaxEntries);
426
427 std::vector<Association> associations;
428 associations.reserve(kMaxEntries);
429 uint32_t timestamp = 789;
430 for (size_t i = 0; i < kMaxEntries; ++i) {
431 associations.push_back(CreateAssociation(i, ++timestamp));
432 uut.InsertPacket(associations[i].sequence_number, associations[i].info);
433 }
434 VerifyAssociations(uut, associations); // Sanity.
435
436 const Association new_association =
437 CreateAssociation(kMaxEntries, ++timestamp);
438 uut.InsertPacket(new_association.sequence_number, new_association.info);
439 associations.push_back(new_association);
440
441 // The +1 is for `new_association`.
442 const size_t kExpectedAssociationCount = 3 * kMaxEntries / 4 + 1;
443 const auto expected_begin =
444 std::prev(associations.end(), kExpectedAssociationCount);
445 VerifyAssociations(uut, expected_begin, associations.end());
446 }
447
MaxEntriesReachedAtSameTimeAsObsoletionOfItem(size_t max_entries,size_t obsoleted_count)448 void RtpSequenceNumberMapTest::MaxEntriesReachedAtSameTimeAsObsoletionOfItem(
449 size_t max_entries,
450 size_t obsoleted_count) {
451 RtpSequenceNumberMap uut(max_entries);
452
453 std::vector<Association> associations;
454 associations.reserve(max_entries);
455 uint32_t timestamp = 789;
456 for (size_t i = 0; i < max_entries; ++i) {
457 associations.push_back(CreateAssociation(i, ++timestamp));
458 uut.InsertPacket(associations[i].sequence_number, associations[i].info);
459 }
460 VerifyAssociations(uut, associations); // Sanity.
461
462 const uint16_t new_association_sequence_number =
463 static_cast<uint16_t>(obsoleted_count) + (1 << 15);
464 const Association new_association =
465 CreateAssociation(new_association_sequence_number, ++timestamp);
466 uut.InsertPacket(new_association.sequence_number, new_association.info);
467 associations.push_back(new_association);
468
469 // The +1 is for `new_association`.
470 const size_t kExpectedAssociationCount =
471 std::min(3 * max_entries / 4, max_entries - obsoleted_count) + 1;
472 const auto expected_begin =
473 std::prev(associations.end(), kExpectedAssociationCount);
474 VerifyAssociations(uut, expected_begin, associations.end());
475 }
476
477 // Version #1 - #(obsoleted entries) < #(entries after paring down below max).
TEST_F(RtpSequenceNumberMapTest,MaxEntriesReachedAtSameTimeAsObsoletionOfItem1)478 TEST_F(RtpSequenceNumberMapTest,
479 MaxEntriesReachedAtSameTimeAsObsoletionOfItem1) {
480 constexpr size_t kMaxEntries = 100;
481 constexpr size_t kObsoletionTarget = (kMaxEntries / 4) - 1;
482 MaxEntriesReachedAtSameTimeAsObsoletionOfItem(kMaxEntries, kObsoletionTarget);
483 }
484
485 // Version #2 - #(obsoleted entries) == #(entries after paring down below max).
TEST_F(RtpSequenceNumberMapTest,MaxEntriesReachedAtSameTimeAsObsoletionOfItem2)486 TEST_F(RtpSequenceNumberMapTest,
487 MaxEntriesReachedAtSameTimeAsObsoletionOfItem2) {
488 constexpr size_t kMaxEntries = 100;
489 constexpr size_t kObsoletionTarget = kMaxEntries / 4;
490 MaxEntriesReachedAtSameTimeAsObsoletionOfItem(kMaxEntries, kObsoletionTarget);
491 }
492
493 // Version #3 - #(obsoleted entries) > #(entries after paring down below max).
TEST_F(RtpSequenceNumberMapTest,MaxEntriesReachedAtSameTimeAsObsoletionOfItem3)494 TEST_F(RtpSequenceNumberMapTest,
495 MaxEntriesReachedAtSameTimeAsObsoletionOfItem3) {
496 constexpr size_t kMaxEntries = 100;
497 constexpr size_t kObsoletionTarget = (kMaxEntries / 4) + 1;
498 MaxEntriesReachedAtSameTimeAsObsoletionOfItem(kMaxEntries, kObsoletionTarget);
499 }
500
501 } // namespace
502 } // namespace webrtc
503