system: [SYSTEM]

user: 3. 针对HTTP压缩流量的基于跳跃的多模式匹配算法 SMCH的核心是本文中定义的匹配状态mState，匹配状态被设计成压缩匹配算法和原始匹配算法之间的桥梁。匹配状态的值由原始匹配算法在匹配阶段计算得到，然后压缩匹配算法根据匹配状态以确定当前字符是否可以被跳过而无需进行字符串匹配操作。 3.1. 匹配状态的定义 除了一些众所周知的英文缩写，如IP、CPU、FDA，所有的英文缩写在文中第一次出现时都应该给出其全称。文章标题中尽量避免使用生僻的英文缩写。 压缩匹配算法的主要思想是利用压缩数据的性质，记录匹配状态，并利用重复的状态跳跃一定的字符，从而加速过滤。我们可以注意到，压缩匹配算法的重点在于匹配状态。在ACCH算法中，CDepth作为匹配状态，从而根据CDepth和AC自动机中的状态作为一个比较，判断是否可以跳跃。在SPC算法中，Partial Match作为匹配状态，从而判断是否可以跳跃。匹配状态是压缩匹配算法的重点，压缩匹配算法需要根据匹配状态和搜索的状态进行跳跃。 图4. 压缩匹配原理示意 如图4所示是压缩匹配算法的原理示意图，状态窗口负责记录匹配状态，字符窗口负责缓存字符，以便指针(length， distance)能够索引的正确的子串位置。在遇到指针类型的数据时，我们不仅能够获取原来的子串，同时根据状态窗口可以获取子串对应的匹配状态。通过记录匹配状态和搜索算法的状态判断是否可以重复利用记录的匹配状态完成跳跃。跳跃可能是一段，也可能是一个字符。 在本算法中，将匹配状态表示为mState，其主要包含两方面的信息：符号和数值。符号表示匹配结果，如果符号为正，那么表示该位置有一个模式串匹配成功；如果符号为负，那么表示该位置在一定长度内不可能匹配成功。数值表示安全距离，即只有大于mState的数值时，才是安全的。同时数值在符号为正时，表示匹配成功的模式串长度。当符号为负时，表示在该数值长度内不可能有一个模式串匹配成功。如果数值为0，则表示该位置没有任何匹配状态。 图5. 匹配状态mState举例 mState代表匹配结果，也代表匹配过程。符号表示匹配结果，而数值表示匹配过程。mState应该在字符串的匹配过程中产生，并不会影响匹配过程。如图5所示，图中模式串是“avb”。左侧文本是“avbnmgdad”，右侧文本为“avcnmgdad”。当使用模式串“avb”搜索目标文本“avbnmgdad”时，可以在文本的位置1处匹配成功，那么根据mState的定义，在第一个位置的字符“a”处的mState为“+3”。因为此时匹配成功，所以符号为正。为数值为匹成功的模式串长度，为3。所以mState为“+3”。当目标文本改为“avcnmgdad”时，模式串在第一个位置开始匹配时，在位置3的字符‘c’处匹配不成功，而前两个字符“ad”匹配成功。根据mState的定义，如果匹配不成功，那么符号应该为负，所以mState的符号为负。数值表示安全长度，即大于长度后不可能匹配成功，那么根据图中所示，当大于3个字符时，匹配就不可能成功，所以数值为3。因此，右侧的搜索产生的mState为“−3”。 从上面例子可以看出，搜索过程一定有两种可能，一个是匹配成功，一个匹配不成功。当匹配成功时，那么mState为正，且数值为匹配成功的模式串长度。当匹配不成功时，mState可能为负，且数值为最大匹配长度加一，即最小的安全长度。值得注意的是，匹配成功一定可以返回mState，而匹配不成功时，并不一定返回mState，主要原因是安全长度的定义。不同的多模式匹配算法有不同的搜索过程，最重要的是保持mState的数值必须是安全的，而匹配不成功导致的最大匹配长度加一并不一定是安全的。 在SMCH中，跳跃和搜索是分离的。搜索过程是执行字符串匹配并返回当前位置的匹配状态信息，跳跃过程则是根据对数据进行逐个扫描并根据匹配状态进行跳跃。 3.2. 匹配状态的计算 前文已介绍过当前的压缩匹配算法都是对原始匹配算法进行特定的改进优化，但是本文所提的SMCH不依赖于原始的匹配算法，具有良好的可扩展性，使用是只需原始匹配算法返回匹配状态即可。此外，SMCH在获得匹配状态的时候并不需要改变原始匹配算法的基础数据结构，这也使得SMCH具备良好的灵活性。 我们在算法1中定义Search()接口作为为模式的搜索过程，它需满足两个条件：搜索字符串的能力和返回正确的mState的能力。不同模式匹配算法之间的搜索过程不同，因此重要的是找到搜索与mState之间的联系，并将匹配状态转换为mState。 在本节中，我们仅以WM算法的一种实现为例。根据WM算法和mState的定义，有两个主要计算mState的地方：在shift表中的转移和在hash表中的查询。在shift表中，设max_pat_len是模式的最大长度，min_pat_len是模式的最小长度。算法的输入是字符串，其长度是max_pat_len，从该字符串的某个位置i开始，输出mState或mState数组。在第4行，如果shift_value = 0，则第5~14行处理hash表中的查询，该查询定位hash表并将模式与目标字符串一一比较。在第8行，如果文本与模式匹配，则返回模式的长度。否则，最大匹配长度记录在第10~11行。max_tmp是字符串和当前模式之间的最大匹配长度，max记录的是最大的max_tmp。在第14行，如果max+1大于或等于min_pat_len，则返回−max−1，否则返回−min_pat_len。第16行是转移过程，将mState设置为[−min_pat_len，−(min_pat_len-shift_value+1)]。这意味着如果一个shift_value值不为0，则有shift_value个mStates是有效的。 shift和hash之间有一些区别。在hash中成功匹配时，匹配的模式长度与mState有关。若有许多可匹配的模式，则返回可匹配模式的最小长度，因为它足够作为安全长度。当哈希失败时，存在一个与某模式可匹配的最大长度(如max)，这意味着从最大长度开始的下一个字符没有成功匹配。所以max + 1是安全长度。另外，max + 1应该大于或等于min_pat_len，因为需要将min_pat_len长的字符串作为哈希表的索引。但是在shift中，min_pat_len与mState显然无关。从当前位置i到i+shift_value-1的位置是有效的mState，其值是从−min_pat_len到−(min_pat_len-shift_value+1)。例如，如果shift_value为3，min_pat_len为4，则从当前位置i到i+2的mStates为[−4， −3， −2]。因为所有的转移字符都是安全的，不可能有匹配。 从hash表中获得的mState仅在此位置有效，而从shift表中获得的mState表可能会影响之后shift_value的位置。主要原因是哈希值的位置不能影响下一个位置，所以不能确定下一个位置的mState。shift表是转移表，因此它会对移位的字符产生影响。 SMCH算法需要一个模式算法来提供mState，它并不会改变原有模式匹配算法的数据结构，因此模式匹配算法的空间复杂度没有改变。SMCH算法主要基于两个固定长度的窗口(GZIP中为32 KB)，一个是字符的缓存，另一个是mState的缓存。不需要额外的空间。 Search()的实现多种多样，但原理相同。在实践中我们还根据KR算法和AC算法实现了接口，同样可达到良好的应用效果。本文不多叙述其他模式算法的实现。 3.3. 匹配状态在压缩匹配算法中的应用 根据mState的定义，跳转的条件是剩余重复子串的长度大于或等于mState的绝对值。在本节中，我们将详细介绍如何使用mState进行跳转。 图6. SMCH算法中的跳转过程 首先我们举例说明如何使用mState，如图6所示。指针(4， 521)指向重复子串sbca，在重复子串中与s对应位置的mState为3。可以看出，从s开始的重复子串的剩余长度为4，大于安全长度3，说明sbc段是安全的。换句话说，在sbc中有一个匹配项，之前由mState记录过。因此，我们不需要搜索字符串sbc，可以跳过它。可以看到b字符的mState为−2，根据mState的定义，表示第二个字符不能匹配成功，只有1个部分匹配成功。但是b之后的重复子串的剩余长度是3 (bca的长度是3)，因此我们可以安全地得出结论：bca字符串不会成功匹配，那么同样可以不搜索而直接跳转。但是，c不能跳过，因为0表示这里没有状态，a也不能跳过，因为a的剩余长度(为1)小于mState的绝对值(为4)。 算法3显示了SMCH中的扫描和跳转的过程。window是字符的缓冲窗口，w是字符索引，sw是搜索索引，w和sw同步但不在同一位置，sw在w之后，mState_window是mState的缓冲窗口，我们支持w和sw可以从0到window长度的自动循环。由于模式匹配算法需要一定长度的子串进行搜索，因此算法的第2~4行需要根据window中的LZ77数据初始化一个长度为max_pat_len字符串。此时，索引w位于max_pat_len位置，搜索索引sw位于0位置。模式搜索全部在sw。第5~24行是算法3的主体，它循环处理LZ77数据。第6~11行是处理ASCII字符的情况，在第10行更新缓存的索引。开始时，位于w位置的mState窗口没有得到匹配的状态，所以在第8行设置mState_window w 为0。第9行是搜索部分，它从搜索索引sw开始，而不是w。第11~23行是处理指针(length， distance)的过程。第13行根据指针(length， distance)更新window。第14~22行是跳过的关键，通过扫描复制字符串，如果在w位置处的已知指针长度大于等于mState的绝对值，则可以确定该位置的mState值可以重复使用指针指向相应位置处的mState值，否则设置为0。该算法在第10行和第22行有sw和w对来更新操作。第8行和第15~19行是在位置w处mState的过滤过程，只有通过该位置，sw到达时，mState才有效。 在算法3中第9行和第20行，Skip()函数用于搜索一个字符段，它根据mState的值确定是否需要模式搜索。Skip()的过程如算法4所示。如果mState大于0，则则从该位置开始向后有一个匹配的子字符串。如果mState小于0，则从该位置后向没有成功匹配的子字符串。以上两种情况都可以跳过。但如果mState等于0，则无法判断匹配信息，所以直接用Search()进行搜索(见3.2节)。 根据算法3和算法4，我们可以看到mState主要由sw处的Search()函数更新，但算法3中w处的mState信息需要在第8行和15~19行进行过滤。mState只有在剩余拷贝长度l-i大于或等于mState绝对值(算法3第15行)时才有效，然后可以直接使用搜索指针来使用mState信息。