hash.go-幾種 hash 函數實現

接口定義

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

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 - 可靠、快速的 hash 算法

Adler-32經過求解兩個16位的數值A、B實現,並將結果連結成一個32位整數.
A就是字符串中每一個字節的和,而BA在相加時每一步的階段值之和。在Adler-32開始運行時,A初始化爲1B初始化爲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)
}

crc32

CRC爲校驗和的一種,是兩個字節數據流採用二進制除法(沒有進位,使用XOR來代替減法)相除所獲得的餘數。其中被除數是須要計算校驗和的信息數據流的二進制表示;除數是一個長度爲 n + 1 的預約義(短)的二進制數,一般用多項式的係數來表示。在作除法以前,要在信息數據以後先加上 n 個 0。ui

對於 crc32 來講,IEEE 標準下的除數是 0xedb88320。除法運算效率比較低,因此生產環境通常使用的是查表法。(這塊兒都是數學推導,沒找到資料,也沒看懂。)加密

以上是 hash 包中提供的幾種 hash 算法。除此以外,crypto 包裏提供了一些其餘的算法也實現了 hash 接口,好比 md5,sha1,sha256等。spa

md5(消息摘要算法,常常有同窗把 md5 錯當成加密算法)是一種被普遍使用的密碼散列函數,能夠產生出一個128位(16字節)的散列值。md5已被證明沒法防止碰撞,已經不算是很安全的算法了,所以不適用於安全性認證,如SSL公開密鑰認證或是數字簽名等用途。對於須要高度安全性的數據,通常建議改用其餘算法,好比 sha256code

素數

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 是質數的話會獲得更好的散列效果。接口


圖片描述

相關文章
相關標籤/搜索