1. 程式人生 > >輕鬆理解CRC差錯檢測演算法(A PAINLESS GUIDE TO CRC ERROR DETECTION ALGORITHMS)七

輕鬆理解CRC差錯檢測演算法(A PAINLESS GUIDE TO CRC ERROR DETECTION ALGORITHMS)七

9 一個表驅動的實現
上面說的“SIMPLE”演算法是一個很好的開始,因為它與目前描述的理論直接對應,因為它是如此簡單。然而,因為它在位級別進行操作,所以編碼實現起來很尷尬(即使是用C語言),執行起來效率也不高(每一位它都要迴圈一次)。為了讓它快起來,我們需要找到一個可以使演算法以比位更大的單元來處理資訊的方法。可以選的單元有4位,位元組(8位),字(16位),和長字(32位),和任何我們所能達到的更大的單元。這當中,4位的最好別用,因為它與位元組的邊界衝突。所以至少,任何實現加速的演算法應該能讓我們在位元組邊界上進行,實事上大多數的表驅動演算法一次處理一個位元組。
為了方便討論,讓我們從4位的多項式轉向32位的。我們的暫存器看起來是一個樣的,除了小盒子代表位元組而非位,多項式是33位的(一個的隱性位在最高,外加32個“活動”位)(W=32)。
這裡寫圖片描述


“SIMPLE”演算法依然是可用的。讓我們看下它都做了什麼。假設“簡單”演算法是在全擺中,把32位中的最高的8位(第3位元組),設為以下值:
t7 t6 t5 t4 t3 t2 t1 t0
在“簡單”演算法的下次迭代中,t7將會決定多項式是否與暫存器中整個值相異或。如果t7=1,這就會發生,反之就不會發生。假設多項式的最高的8位是g7 g6 … g0,然後,在下次迭代之後,最高的8位將會是
t6 t5 t4 t3 t2 t1 t0 ?? + t7 * (g7 g6 g5 g4 g3 g2 g1 g0)
[提醒: +代表異或]
新的最高位(將會決定下次迭代發生什麼)現在的值是t6 + t7 * g7。千萬要注意到,從資訊的觀點來看,計算新的最高位的所需要的資訊已經包含在了原來的最高2位裡面。類似的,下一個最高位可以從t7 t6 t5提前計算出。事實上,一般,k次以後的迭代的最高位的值,可以由暫存器的最高k位計算得到。記我們把這個暫時作為定理記下。
設想一下,我們使用暫存器中的最高8位來計算 此後8次迭代後的暫存器中的最高位。假設,我們以計算好的值(我們可以把它存入一個單位元組暫存器中,每次出棧一個位來用)去驅動以後8次的迭代。那麼,我們注意到3個事情:
*暫存器的最高位沒有什麼用。不管多少次,在第幾個偏移位上,多項式和高8位相異或,它們都會在下8次迭代中被移出(這裡原諒是shifted out the right hand side,個人覺得應該是Left)。
*餘下的位元組將會向左移動一個位置,最右邊的位元組會移入下一個位元組。 而且
*當這進行中時,暫存器會被根據提前計算出的控制位元組進行異或操作。
現在,考慮在不同的偏移處對一個常數進行異或操作的效果。如:
0100010 暫存器
…0110與它異或
..0110. 與它異或
0110… 與它異或
_ _
0011000
這個例子的意義是,把一系列常數接連和暫存器值異或,最後的結果是,會存在這樣一個值,和最開始的暫存器中的值相異或後,得到與前者相同的結果。
也許你現在已經看到解決方案了。把所有的片段合在一起,我們有了這樣的演算法。
while(附加後的資訊還有位元組) {
開始
檢查暫存器的首位元組。
通過暫存器的首位元組計算出控制位元組。
把將要在各個偏處,由據控制位元組控制,將與暫存器進行異或的poly,相加。
將暫存器左移一個位元組,在暫存器的最右位元組中讀入一個新的資訊位元組。
把poly和與暫存器相異或。
}
如所見,這並不比“簡單”演算法好多少。但是,結果是,其中的大部分計算都可以重新計算,裝入一個表中。結果就是,上面的演算法可以縮減如下:
while(附加後的資訊還有位元組){
開始
Top = top_byte(Register);
Register = (Register << 24) | 下一個資訊位元組;
Register = Register XOR precomputed_table[Top];
}
就是這了。如果你明白了這點,你就已經抓住了表驅動CRC演算法的核心了。以上是一個非常高效的演算法,只需要一個移位,取或,取異或,查詢各位元組的索引表。以圖表來表達,它看起來是這樣子:
這裡寫圖片描述

在C語言中,主迴圈應該是這樣的:

r=0;
while (len--)
{
byte t = (r >> 24) & 0xFF;
r = (r << 8) | *p++;
r^=table[t];
}

其中,len是附加後的資訊長度,以位元組為單位,p指向附加0後的資訊,r是暫存器,t是臨時變數,表是計算表。這段程式碼可以更難懂的方式寫成如下形式:
r=0; while (len--) r = ((r << 8) | *p++) ^ t[(r >> 24) & 0xFF];
這是一個非常乾淨,高效的迴圈,儘管對於一般沒有CRC理論基礎的觀察者來說是太淺顯易懂。我們稱呼這個為TABLE演算法。