在编程中会出现一类数值比较,数值在一定范围内回环,例如,32bits or 16 bits,需要比较这些序号,以确定是否发生了乱序行为。
逻辑上来看,在回环空间内的数值序号,无始无终,所以,比较大小需要特殊处理,直接用编程语言的比较操作符 ,容易出现逻辑问题!
对于回环序号的比较,实际上在RFC 1982 - Serial Number Arithmetic
给予了实施建议,而且从Linux
内核的stcp
协议栈,也可以将其实现抠出,内核实现确实很精巧。
不过,在rfc
规范中指出确实存在一些未定义场景。但是,这类的比较场景是非常稀缺的,所以,内核实现整体上可以是接受的。
同时,内核实现可能刻意仅提供了偏序关系,没有提供对称操作,避免可能二义性编码。
我的直观实现
- 仅允许一定范围内回环比较
#define SN_THRESHOLD_32 (uint32_t)(0xFFFF)
/*my implemention*/
static inline int TSN_lt_my(uint32_t s, uint32_t t)
{
return s < t || (s > t && t < (uint32_t)SN_THRESHOLD_32 && (s + SN_THRESHOLD_32) < SN_THRESHOLD_32 && (s + SN_THRESHOLD_32) >= t);
}
static inline int TSN_lte_my(uint32_t s, uint32_t t)
{
return s == t || TSN_lt_my(s, t);
}
啰嗦了很多 :(
Liniux内核代码–回环序号比较
/* RFC 1982 - Serial Number Arithmetic
*
* 2. Comparison
* Then, s1 is said to be equal to s2 if and only if i1 is equal to i2,
* in all other cases, s1 is not equal to s2.
*
* s1 is said to be less than s2 if, and only if, s1 is not equal to s2,
* and
*
* (i1 < i2 and i2 - i1 < 2^(SERIAL_BITS - 1)) or
* (i1 > i2 and i1 - i2 > 2^(SERIAL_BITS - 1))
*
* s1 is said to be greater than s2 if, and only if, s1 is not equal to
* s2, and
*
* (i1 < i2 and i2 - i1 > 2^(SERIAL_BITS - 1)) or
* (i1 > i2 and i1 - i2 < 2^(SERIAL_BITS - 1))
*/
/*
* RFC 2960 - SCTP relatives
* 1.6 Serial Number Arithmetic
*
* Comparisons and arithmetic on TSNs in this document SHOULD use Serial
* Number Arithmetic as defined in [RFC1982] where SERIAL_BITS = 32.
*/
enum {
TSN_SIGN_BIT = (1<<31)
};
/*previous version of older linux kernel*/
static inline int TSN_lt(uint32_t s, uint32_t t)
{
return ((s) - (t)) & TSN_SIGN_BIT;
}
static inline int TSN_lte(uint32_t s, uint32_t t)
{
return ((s) == (t)) || (((s) - (t)) & TSN_SIGN_BIT);
}
/*new version of linux kernel*/
static inline int TSN_lt2(uint32_t s, uint32_t t)
{
return (int32_t)((s) - (t)) < 0;
}
static inline int TSN_lte2(uint32_t s, uint32_t t)
{
return (int32_t)((s) - (t)) <= 0;
}
/* Compare two SSNs */
/*
* RFC 2960
* 1.6 Serial Number Arithmetic
*
* Comparisons and arithmetic on Stream Sequence Numbers in this document
* SHOULD use Serial Number Arithmetic as defined in [RFC1982] where
* SERIAL_BITS = 16.
*/
enum {
SSN_SIGN_BIT = (1<<15)
};
static inline int SSN_lt(uint16_t s, uint16_t t)
{
return ((s) - (t)) & SSN_SIGN_BIT;
}
static inline int SSN_lte(uint16_t s, uint16_t t)
{
return ((s) == (t)) || (((s) - (t)) & SSN_SIGN_BIT);
}
static inline int SSN_lt2(uint16_t s, uint16_t t)
{
return (int16_t)((s) - (t)) < 0;
}
static inline int SSN_lte2(uint16_t s, uint16_t t)
{
return (int16_t)((s) - (t)) <= 0;
}
int main(int argc, char* argv[])
{
printf("Test 32bits TSN wrapped feature ...\n");
assert(TSN_lte(1, 1));
assert(TSN_lt(1, 2));
assert(TSN_lt(1, 0xFFFFFF));
assert(TSN_lt(0xFFFFFFFF, 1));
assert(!TSN_lt(0xFFFFFFFF, 0xFFFFFFFE));
assert(!TSN_lt(0xFFFFFFFF, 0xFFFF0000));
printf("odd behavior, but may accept. the case occurs with little chance\n");
assert(!TSN_lt(1, 0xFFFFFFFF));
printf("\nTest new version comparing functions ...\n");
assert(TSN_lte2(1, 1));
assert(TSN_lt2(1, 2));
assert(TSN_lt2(1, 0xFFFFFF));
assert(TSN_lt2(0xFFFFFFFF, 1));
assert(!TSN_lt2(0xFFFFFFFF, 0xFFFFFFFE));
assert(!TSN_lt2(0xFFFFFFFF, 0xFFFF0000));
printf("odd behavior, but may accept. the case occurs with little chance\n");
assert(!TSN_lt2(1, 0xFFFFFFFF));
return 0;
}