在编程中会出现一类数值比较,数值在一定范围内回环,例如,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;
}
参考资料
- 内核回环序号比较的变更