A Discrete-Event Network Simulator
API
Loading...
Searching...
No Matches
lollipop-counter.h
Go to the documentation of this file.
1/*
2 * Copyright (c) 2020 Universita' di Firenze, Italy
3 *
4 * SPDX-License-Identifier: GPL-2.0-only
5 *
6 * Author: Tommaso Pecorella <tommaso.pecorella@unifi.it>
7 */
8
9#ifndef LOLLIPOP_COUNTER_H
10#define LOLLIPOP_COUNTER_H
11
12#include "ns3/abort.h"
13
14#include <compare>
15#include <limits>
16
17namespace ns3
18{
19
20/**
21 * @ingroup seq-counters
22 * @class LollipopCounter
23 *
24 * @brief Template class implementing a Lollipop counter as defined in \RFC{8505}, \RFC{6550}, and
25 * [Perlman83].
26 *
27 * A Lollipop counter is a counter that solves initialization and
28 * out-of-order problems often occurring in Internet protocols.
29 *
30 * The counter is split in two regions, an initializing region, and a circular region, having the
31 * same size. Assuming a counter using an uint8_t (max value 255), values from 128 and greater are
32 * used as a linear sequence to indicate a restart and bootstrap the counter, and the values less
33 * than or equal to 127 are used as a circular sequence number space of
34 * size 128 as mentioned in \RFC{1982}.
35 *
36 * In both regions, the comparison between two counters is allowed only if both counters are inside
37 * a Sequence Window. The default value for the Sequence Window is equal to 2^N where N is half the
38 * number of digits of the underlying type. For an uint8_t the Sequence Window is 16.
39 *
40 * The counter, by default, is initialized to the maximum counter value minus the Sequence Window
41 * plus one, e.g., in case of a uint8_t, to 240.
42 *
43 * This implementation extends the case presented in RFCs, allowing to use a
44 * larger underlying type and to change the Sequence Window size.
45 *
46 * Warning: two Lollipop counters can be compared only if they are of the same type (same underlying
47 * type, and same Sequence Window).
48 *
49 * References:
50 * [Perlman83] Perlman, R., "Fault-Tolerant Broadcast of Routing Information", North-Holland
51 * Computer Networks 7: pp. 395-405, DOI 10.1016/0376-5075(83)90034-X, 1983,
52 * <https://web.archive.org/web/20180723135334/http://pbg.cs.illinois.edu/courses/cs598fa09/readings/p83.pdf>.
53 *
54 * @tparam T \explicit The type being used for the counter.
55 */
56template <class T>
58{
59 public:
60 /**
61 * Builds a Lollipop counter with a default initial value.
62 *
63 * The Sequence Window is set to the default value.
64 * The initial value is set to the maximum counter value minus the Sequence Window plus one.
65 */
67 {
68 NS_ABORT_MSG_UNLESS(std::is_unsigned_v<T>,
69 "Lollipop counters must be defined on unsigned integer types");
70
71 uint16_t numberofDigits = std::numeric_limits<T>::digits;
72 m_sequenceWindow = 1 << (numberofDigits / 2);
73
75 }
76
77 /**
78 * Builds a Lollipop counter with a specific initial value.
79 *
80 * The Sequence Window is set to the default value.
81 *
82 * @param val the initial value of the Lollipop Counter
83 * @tparam T \deduced The type being used for the counter.
84 */
86 {
87 uint16_t numberofDigits = std::numeric_limits<T>::digits;
88 m_sequenceWindow = 1 << (numberofDigits / 2);
89
90 m_value = val;
91 }
92
93 /**
94 * Assignment.
95 *
96 * @param [in] o Value to assign to this LollipopCounter.
97 * @returns This LollipopCounter.
98 */
100 {
101 m_value = o.m_value;
102 return *this;
103 }
104
105 /**
106 * Resets the counter to its initial value.
107 */
108 void Reset()
109 {
111 }
112
113 /**
114 * Set the Sequence Window Size and resets the counter.
115 *
116 * The sequence window is equal to 2^numberOfBits.
117 * The counter is reset to maxValue - m_sequenceWindow +1, where
118 * maxValue is the maximum number allowed by the underlying type.
119 *
120 * @param numberOfBits number of bits to use in the Sequence Window
121 */
122 void SetSequenceWindowSize(uint16_t numberOfBits)
123 {
124 uint16_t numberofDigits = std::numeric_limits<T>::digits;
125
127 numberOfBits >= numberofDigits,
128 "The size of the Sequence Window should be less than the counter size (which is "
129 << +m_maxValue << ")");
130
131 m_sequenceWindow = 1 << numberOfBits;
132
134 }
135
136 /**
137 * Checks if the counter is comparable with another counter (i.e., not desynchronized).
138 *
139 * If the absolute magnitude of difference of the two
140 * sequence counters is greater than Sequence Window, then a
141 * desynchronization has occurred and the two sequence
142 * numbers are not comparable.
143 *
144 * Sequence Window is equal to 2^N where N is (by default) half the number
145 * of digits of the underlying type.
146 *
147 * @param val counter to compare
148 * @returns true if the counters are comparable.
149 */
150 bool IsComparable(const LollipopCounter& val) const
151 {
153 "Can not compare two Lollipop Counters with different sequence windows");
154
157 {
158 // They are desynchronized - comparison is impossible.
159 T absDiff = AbsoluteMagnitudeOfDifference(val);
160 if (absDiff > m_sequenceWindow)
161 {
162 return false;
163 }
164 }
165 return true;
166 }
167
168 /**
169 * Checks if a counter is in its starting region.
170 *
171 * @returns true if a counter is in its starting region.
172 */
173 bool IsInit() const
174 {
175 return m_value > m_circularRegion;
176 }
177
178 /**
179 * Arithmetic operator equal-to
180 * @param [in] lhs Left hand argument
181 * @param [in] rhs Right hand argument
182 * @return The result of the operator.
183 */
184 friend bool operator==(const LollipopCounter& lhs, const LollipopCounter& rhs)
185 {
187 "Can not compare two Lollipop Counters with different sequence windows");
188
189 return lhs.m_value == rhs.m_value;
190 }
191
192 /**
193 * Spaceship comparison operator. All the other comparison operators
194 * are automatically generated from this one.
195 *
196 * Note that the comparison is a ``std::partial_ordering``, as two
197 * lollipop counters could be not comparable, @see IsComparable() for details.
198 *
199 * @param [in] lhs Left hand argument
200 * @param [in] rhs Right hand argument
201 * @returns The result of the comparison.
202 */
203 friend constexpr std::partial_ordering operator<=>(const LollipopCounter& lhs,
204 const LollipopCounter& rhs)
205 {
207 "Can not compare two Lollipop Counters with different sequence windows");
208
209 if (lhs.m_value == rhs.m_value)
210 {
211 return std::partial_ordering::equivalent;
212 }
213
214 if ((lhs.m_value <= m_circularRegion && rhs.m_value <= m_circularRegion) ||
216 {
217 // both counters are in the same region
218
219 T absDiff = lhs.AbsoluteMagnitudeOfDifference(rhs);
220 if (absDiff > lhs.m_sequenceWindow)
221 {
222 // They are desynchronized - comparison is impossible.
223 return std::partial_ordering::unordered;
224 }
225
226 // They are synchronized - comparison according to RFC1982.
227 T serialRegion = ((m_circularRegion >> 1) + 1);
228 if (((lhs.m_value < rhs.m_value) && ((rhs.m_value - lhs.m_value) > serialRegion)) ||
229 ((lhs.m_value > rhs.m_value) && ((lhs.m_value - rhs.m_value) < serialRegion)))
230 {
231 return std::partial_ordering::greater;
232 }
233 return std::partial_ordering::less;
234 }
235
236 // One counter is in the "high" region and the other is in the in the "lower" region
237 bool lhsIsHigher;
238 T difference;
239
241 {
242 lhsIsHigher = true;
243 // this is guaranteed to be positive and between [1...m_lollipopMaxValue].
244 difference = lhs.m_value - rhs.m_value;
245 }
246 else
247 {
248 lhsIsHigher = false;
249 // this is guaranteed to be positive and between [1...m_lollipopMaxValue].
250 difference = rhs.m_value - lhs.m_value;
251 }
252
253 // this is guaranteed to be positive and between [1...m_lollipopMaxValue].
254 T distance = (m_maxValue - difference) + 1;
255 if (distance <= lhs.m_sequenceWindow)
256 {
257 lhsIsHigher = !lhsIsHigher;
258 }
259
260 if (lhsIsHigher)
261 {
262 return std::partial_ordering::greater;
263 }
264 return std::partial_ordering::less;
265 }
266
267 /**
268 * Prefix increment operator
269 * @param [in] val LollipopCounter to be incremented
270 * @return The result of the Prefix increment.
271 */
273 {
274 val.m_value++;
275
276 if (val.m_value == m_circularRegion + 1)
277 {
278 val.m_value = 0;
279 }
280
281 return val;
282 }
283
284 /**
285 * Postfix increment operator
286 * @param [in] val LollipopCounter to be incremented
287 * @param [in] noop ignored argument (used to mark it as a postfix, blame c++).
288 * @return The result of the Postfix increment.
289 */
290 friend LollipopCounter operator++(LollipopCounter& val, int noop) // postfix ++
291 {
292 LollipopCounter ans = val;
293 ++(val); // or just call operator++()
294 return ans;
295 }
296
297 /**
298 * Get the counter value.
299 *
300 * @return the counter value.
301 */
302 T GetValue() const
303 {
304 return m_value;
305 }
306
307 /**
308 * Output streamer for LollipopCounter.
309 *
310 * @param [in,out] os The output stream.
311 * @param [in] counter The LollipopCounter to print.
312 * @returns The stream.
313 */
314 friend std::ostream& operator<<(std::ostream& os, const LollipopCounter& counter)
315 {
316 os << +counter.m_value;
317 return os;
318 }
319
320 private:
321 /**
322 * Compute the Absolute Magnitude Of Difference between two counters.
323 *
324 * The Absolute Magnitude Of Difference is considered to
325 * be on a circular region, and it is represented by
326 * the smallest circular distance between two numbers.
327 *
328 * Arithmetic operator.
329 * @param [in] val Counter to compute the difference against
330 * @return The result of the difference.
331 */
333 {
334 // useless because it is computed always on counters on their respective regions.
335 // Left (commented) for debugging purposes in case there is a code change.
336 // NS_ASSERT_MSG ((m_value <= m_circularRegion && val.m_value <= m_circularRegion) ||
337 // (m_value > m_circularRegion && val.m_value > m_circularRegion),
338 // "Absolute Magnitude Of Difference can be computed only on two values in
339 // the circular region " << +m_value << " - " << +val.m_value);
340
341 T absDiffDirect = std::max(m_value, val.m_value) - std::min(m_value, val.m_value);
342 T absDiffWrapped = (std::min(m_value, val.m_value) + m_circularRegion + 1) -
343 std::max(m_value, val.m_value);
344 T absDiff = std::min(absDiffDirect, absDiffWrapped);
345 return absDiff;
346 }
347
348 T m_value; //!< Value of the Lollipop Counter.
349 T m_sequenceWindow; //!< Sequence window used for comparing two counters.
350 static constexpr T m_maxValue =
351 std::numeric_limits<T>::max(); //!< Maximum value of the counter.
352 static constexpr T m_circularRegion = m_maxValue >> 1; //!< Circular region of the counter.
353};
354
355/**
356 * @ingroup seq-counters
357 * 8 bit Lollipop Counter.
358 */
360/**
361 * @ingroup seq-counters
362 * 16 bit Lollipop Counter.
363 */
365
366} /* namespace ns3 */
367
368#endif /* LOLLIPOP_COUNTER_H */
Template class implementing a Lollipop counter as defined in RFC 8505, RFC 6550, and [Perlman83].
friend constexpr std::partial_ordering operator<=>(const LollipopCounter &lhs, const LollipopCounter &rhs)
Spaceship comparison operator.
static constexpr uint8_t m_maxValue
friend LollipopCounter operator++(LollipopCounter &val, int noop)
Postfix increment operator.
T AbsoluteMagnitudeOfDifference(const LollipopCounter &val) const
Compute the Absolute Magnitude Of Difference between two counters.
LollipopCounter()
Builds a Lollipop counter with a default initial value.
friend LollipopCounter operator++(LollipopCounter &val)
Prefix increment operator.
friend std::ostream & operator<<(std::ostream &os, const LollipopCounter &counter)
Output streamer for LollipopCounter.
T GetValue() const
Get the counter value.
bool IsInit() const
Checks if a counter is in its starting region.
static constexpr uint8_t m_circularRegion
LollipopCounter(T val)
Builds a Lollipop counter with a specific initial value.
LollipopCounter & operator=(const LollipopCounter &o)
Assignment.
friend bool operator==(const LollipopCounter &lhs, const LollipopCounter &rhs)
Arithmetic operator equal-to.
void SetSequenceWindowSize(uint16_t numberOfBits)
Set the Sequence Window Size and resets the counter.
bool IsComparable(const LollipopCounter &val) const
Checks if the counter is comparable with another counter (i.e., not desynchronized).
void Reset()
Resets the counter to its initial value.
#define NS_ABORT_MSG_UNLESS(cond, msg)
Abnormal program termination if a condition is false, with a message.
Definition abort.h:133
#define NS_ABORT_MSG_IF(cond, msg)
Abnormal program termination if a condition is true, with a message.
Definition abort.h:97
LollipopCounter< uint8_t > LollipopCounter8
8 bit Lollipop Counter.
LollipopCounter< uint16_t > LollipopCounter16
16 bit Lollipop Counter.
Every class exported by the ns3 library is enclosed in the ns3 namespace.