type Hash interface { // 嵌入了 io.Writer 接口 // 向執行中的 hash 加入更多數據 // hash 函數的 Write 方法永遠不會返回 error io.Writer // 把當前 hash 追加到 b 的後面 // 不會改變當前 hash 狀態 Sum(b []byte) []byte // 重置 hash 到初始化狀態 Reset() // 返回 hash 結果返回的字節數 Size() int // BlockSize 返回 hash 的基礎塊大小 // 爲了提升效率 // Write 方法傳入的字節數最好是 blick size 的倍數 BlockSize() int } // 結果爲 32bit hash 函數接口 type Hash32 interface { Hash Sum32() uint32 } // 結果爲 64bit hash 函數接口 type Hash64 interface { Hash Sum64() uint64 }
Go 的 hash
包裏有幾種 hash
算法實現,分別是 adler32,crc32/64,fnv
。算法
fnv
是一種簡單可靠的 hash
算法。它的結果長度有多種,fnv.go
中也提供了多種長度的算法實現。fnv
核心算法很簡單:先初始化 hash
,而後循環 乘以素數 prime32
,再與每位 byte
進行異或運算。安全
const offset32 = 2166136261 const prime32 = 16777619 type sum32 uint32 func (s *sum32) Reset() { *s = offset32 } func (s *sum32) Size() int { return 4 } func (s *sum32) BlockSize() int { return 1 } func (s *sum32) Write(data []byte) (int, error) { hash := *s for _, c := range data { hash *= prime32 hash ^= sum32(c) } *s = hash return len(data), nil } func (s *sum32) Sum32() uint32 { return uint32(*s) }
Adler-32經過求解兩個16位的數值A、B
實現,並將結果連結成一個32位整數.A
就是字符串中每一個字節的和,而B
是A
在相加時每一步的階段值之和。在Adler-32開始運行時,A
初始化爲1
,B
初始化爲0
,最後的校驗和要模上65521
(小於2的16次方的最小素數)。函數
type digest uint32 func (d *digest) Reset() { *d = 1 } func (d *digest) Write(p []byte) (nn int, err error) { *d = update(*d, p) return len(p), nil } const ( // 比 65536 小的最大素數 mod = 65521 // nmax is the largest n such that // 255 * n * (n+1) / 2 + (n+1) * (mod-1) <= 2^32-1. // It is mentioned in RFC 1950 (search for "5552"). nmax = 5552 ) // Add p to the running checksum d. func update(d digest, p []byte) digest { s1, s2 := uint32(d&0xffff), uint32(d>>16) for len(p) > 0 { var q []byte // 每次處理數據量爲 nmax // 處理完以後再 % mod // 防止溢出,且儘量少的執行 mod 運算 if len(p) > nmax { p, q = p[:nmax], p[nmax:] } // 底下這兩個循環我不明白爲啥不合成一個??? // 有明白的能夠告知一聲 for len(p) >= 4 { s1 += uint32(p[0]) s2 += s1 s1 += uint32(p[1]) s2 += s1 s1 += uint32(p[2]) s2 += s1 s1 += uint32(p[3]) s2 += s1 p = p[4:] } for _, x := range p { s1 += uint32(x) s2 += s1 } s1 %= mod s2 %= mod p = q } // 把 s2 s1 再合成一個 uint32 return digest(s2<<16 | s1) }
CRC
爲校驗和的一種,是兩個字節數據流採用二進制除法(沒有進位,使用XOR來代替減法)相除所獲得的餘數。其中被除數是須要計算校驗和的信息數據流的二進制表示;除數是一個長度爲 n + 1 的預約義(短)的二進制數,一般用多項式的係數來表示。在作除法以前,要在信息數據以後先加上 n 個 0。ui
對於 crc32 來講,IEEE 標準下的除數是 0xedb88320。除法運算效率比較低,因此生產環境通常使用的是查表法。(這塊兒都是數學推導,沒找到資料,也沒看懂。)加密
以上是 hash 包中提供的幾種 hash 算法。除此以外,crypto
包裏提供了一些其餘的算法也實現了 hash 接口,好比 md5,sha1,sha256
等。spa
md5
(消息摘要算法,常常有同窗把 md5 錯當成加密算法)是一種被普遍使用的密碼散列函數,能夠產生出一個128位(16字節)的散列值。md5
已被證明沒法防止碰撞,已經不算是很安全的算法了,所以不適用於安全性認證,如SSL公開密鑰認證
或是數字簽名
等用途。對於須要高度安全性的數據,通常建議改用其餘算法,好比 sha256
。code
hash
算法中經常使用中間值對一個素數
取模做爲 hash
值,這是爲何呢?
一個好的散列函數要儘量減小衝突。若是對合數取模,那麼全部該函數的因子的倍數衝突的機率會增大,而質數的因子只有1和它自己,因此對於特定倍數的數字來講,會有更好的散列效果。好比:
假設 mod 是 6,對於 2 的倍數 二、四、六、八、十、12 的 hash 值是 二、四、0、二、四、0,對於 3 的倍數 三、六、九、12 的 hash 值是 三、0、三、0。
假設 mod 是 7,對於 二、四、六、八、十、12 的 hash 值是 二、四、六、一、三、5,對於 3 的倍數 三、六、九、12 的 hash 值是 三、六、二、5。
能夠看出,若是 mod
是質數的話會獲得更好的散列效果。接口