Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

hash/adler32 - Adler-32 校验和

hash/adler32 包实现了 Adler-32 校验和算法,定义在 RFC 1950 中。

概述

Adler-32 校验和是一种快速的数据完整性校验算法,由 Mark Adler 设计。它比 CRC-32 更快,但可靠性略低。广泛应用于 zlib 压缩库。

包导入

import "hash/adler32"

基本使用

// 1. 创建哈希器
h := adler32.New()

// 2. 写入数据
h.Write([]byte("data"))

// 3. 计算校验和
checksum := h.Sum32()

// 4. 或直接计算
checksum := adler32.Checksum([]byte("data"))

典型示例

示例 1:基本使用

package main

import (
    "fmt"
    "hash/adler32"
)

func main() {
    // 创建哈希器
    h := adler32.New()
    
    // 写入数据
    data := []byte("Hello, World!")
    h.Write(data)
    
    // 获取校验和
    checksum := h.Sum32()
    fmt.Printf("Adler-32: %08x\n", checksum)
    
    // 使用 Sum 获取字节切片
    sum := h.Sum(nil)
    fmt.Printf("Sum: %x\n", sum)
}

运行

$ go run main.go
Adler-32: 1a0b045d
Sum: 1a0b045d

示例 2:使用 Checksum 函数

package main

import (
    "fmt"
    "hash/adler32"
)

func main() {
    data := []byte("Hello, World!")
    
    // 直接计算校验和
    checksum := adler32.Checksum(data)
    fmt.Printf("Adler-32: %08x\n", checksum)
    
    // 验证
    h := adler32.New()
    h.Write(data)
    checksum2 := h.Sum32()
    
    if checksum == checksum2 {
        fmt.Println("校验和一致 ✓")
    }
}

运行

$ go run main.go
Adler-32: 1a0b045d
校验和一致 ✓

示例 3:流式计算大文件

package main

import (
    "fmt"
    "hash/adler32"
    "io"
    "os"
)

func checksumFile(filename string) (uint32, error) {
    file, err := os.Open(filename)
    if err != nil {
        return 0, err
    }
    defer file.Close()
    
    h := adler32.New()
    if _, err := io.Copy(h, file); err != nil {
        return 0, err
    }
    
    return h.Sum32(), nil
}

func main() {
    if len(os.Args) < 2 {
        fmt.Println("用法:checksum <文件>")
        os.Exit(1)
    }
    
    sum, err := checksumFile(os.Args[1])
    if err != nil {
        fmt.Printf("错误:%v\n", err)
        os.Exit(1)
    }
    
    fmt.Printf("Adler-32: %08x\n", sum)
}

运行

$ go run main.go test.txt
Adler-32: 2f4e0c1a

一、核心函数(按字母顺序)

Checksum - 计算校验和

Checksum(data []byte) uint32

说明

  • 直接计算数据的 Adler-32 校验和
  • 一次性计算,无需创建哈希器
  • 适合小数据块

定义

func Checksum(data []byte) uint32

示例

package main

import (
    "fmt"
    "hash/adler32"
)

func main() {
    data := []byte("Hello, World!")
    
    // 计算校验和
    checksum := adler32.Checksum(data)
    fmt.Printf("Adler-32: %08x (%d)\n", checksum, checksum)
    
    // 空数据
    empty := adler32.Checksum([]byte{})
    fmt.Printf("空数据:%08x\n", empty)
    
    // 不同数据
    data2 := []byte("Hello, World!!")
    checksum2 := adler32.Checksum(data2)
    fmt.Printf("不同数据:%08x\n", checksum2)
}

运行

$ go run main.go
Adler-32: 1a0b045d (436782173)
空数据:00000001
不同数据:1e12055e

New - 创建哈希器

New() hash.Hash32

说明

  • 创建一个新的 hash.Hash32 实例
  • 用于计算 Adler-32 校验和
  • Sum 方法以 big-endian 字节顺序返回值
  • 实现了 encoding.BinaryMarshaler 和 encoding.BinaryUnmarshaler 接口

定义

func New() hash.Hash32

返回值

  • hash.Hash32:实现了 Hash32 接口的哈希器

示例

package main

import (
    "fmt"
    "hash/adler32"
)

func main() {
    // 创建哈希器
    h := adler32.New()
    
    // 写入数据
    h.Write([]byte("Hello"))
    h.Write([]byte(", "))
    h.Write([]byte("World!"))
    
    // 获取校验和
    checksum := h.Sum32()
    fmt.Printf("Adler-32: %08x\n", checksum)
    
    // 获取字节切片
    sum := h.Sum(nil)
    fmt.Printf("字节:%x\n", sum)
    
    // 验证长度
    fmt.Printf("长度:%d 字节\n", len(sum))
    fmt.Printf("Size(): %d\n", h.Size())
}

运行

$ go run main.go
Adler-32: 1a0b045d
字节:1a0b045d
长度:4 字节
Size(): 4

二、Hash32 接口方法

adler32.New() 返回的对象实现了 hash.Hash32 接口,包含以下方法:

BlockSize - 块大小

BlockSize() int

说明

  • 返回块大小(Adler-32 为 1 字节)
  • 用于优化写入性能

示例

package main

import (
    "fmt"
    "hash/adler32"
)

func main() {
    h := adler32.New()
    
    fmt.Printf("BlockSize: %d\n", h.BlockSize())
    fmt.Printf("Size: %d\n", h.Size())
}

运行

$ go run main.go
BlockSize: 1
Size: 4

Reset - 重置哈希器

Reset()

说明

  • 重置哈希器到初始状态
  • 清空所有已写入的数据
  • 可以重新使用,无需创建新实例

示例

package main

import (
    "fmt"
    "hash/adler32"
)

func main() {
    h := adler32.New()
    
    // 第一次计算
    h.Write([]byte("first"))
    sum1 := h.Sum32()
    fmt.Printf("第一次:%08x\n", sum1)
    
    // 重置后重新计算
    h.Reset()
    h.Write([]byte("second"))
    sum2 := h.Sum32()
    fmt.Printf("第二次:%08x\n", sum2)
    
    // 验证不同
    if sum1 != sum2 {
        fmt.Println("校验和不同 ✓")
    }
}

运行

$ go run main.go
第一次:017e0103
第二次:020d020e
校验和不同 ✓

Size - 哈希值长度

Size() int

说明

  • 返回哈希值的字节长度
  • Adler-32 固定为 4 字节

示例

package main

import (
    "fmt"
    "hash/adler32"
)

func main() {
    h := adler32.New()
    
    fmt.Printf("Size: %d\n", h.Size())  // 4
    
    // 验证
    h.Write([]byte("data"))
    sum := h.Sum(nil)
    fmt.Printf("实际长度:%d\n", len(sum))  // 4
}

运行

$ go run main.go
Size: 4
实际长度:4

Sum - 计算哈希值

Sum(in []byte) []byte

说明

  • 计算当前数据的哈希值
  • 将结果追加到 in 切片后返回
  • 通常传入 nil 获取新切片
  • 以 big-endian 字节顺序排列

示例

package main

import (
    "fmt"
    "hash/adler32"
)

func main() {
    h := adler32.New()
    h.Write([]byte("data"))
    
    // 获取哈希值(常用方式)
    sum1 := h.Sum(nil)
    fmt.Printf("Sum(nil): %x\n", sum1)
    
    // 追加到现有切片
    prefix := []byte("prefix:")
    sum2 := h.Sum(prefix)
    fmt.Printf("Sum(prefix): %s %x\n", sum2[:7], sum2[7:])
    
    // 可以多次调用(状态不变)
    sum3 := h.Sum(nil)
    fmt.Printf("再次调用:%x\n", sum3)
}

运行

$ go run main.go
Sum(nil): 1a0b045d
Sum(prefix): prefix: 1a0b045d
再次调用:1a0b045d

Sum32 - 32 位校验和

Sum32() uint32

说明

  • Hash32 接口特有方法
  • 返回 32 位无符号整数形式的校验和
  • 方便比较和存储

示例

package main

import (
    "fmt"
    "hash/adler32"
)

func main() {
    h := adler32.New()
    h.Write([]byte("data"))
    
    // 获取 uint32 值
    checksum := h.Sum32()
    
    // 不同格式输出
    fmt.Printf("十六进制:%08x\n", checksum)
    fmt.Printf("十进制:%d\n", checksum)
    fmt.Printf("二进制:%032b\n", checksum)
    
    // 直接比较
    h2 := adler32.New()
    h2.Write([]byte("data"))
    if checksum == h2.Sum32() {
        fmt.Println("校验和相同 ✓")
    }
}

运行

$ go run main.go
十六进制:1a0b045d
十进制:436782173
二进制:00011010000010110000010001011101
校验和相同 ✓

Write - 写入数据

Write(p []byte) (int, error)

说明

  • 实现 io.Writer 接口
  • 向哈希器写入数据
  • 可以多次调用,累积计算
  • 返回写入的字节数和可能的错误

示例

package main

import (
    "fmt"
    "hash/adler32"
)

func main() {
    h := adler32.New()
    
    // 单次写入
    h.Write([]byte("Hello, World!"))
    fmt.Printf("单次:%08x\n", h.Sum32())
    
    // 多次写入(结果相同)
    h.Reset()
    h.Write([]byte("Hello"))
    h.Write([]byte(", "))
    h.Write([]byte("World!"))
    fmt.Printf("多次:%08x\n", h.Sum32())
    
    // 使用 io.Writer 接口
    h.Reset()
    fmt.Fprintf(h, "%s, %s!", "Hello", "World")
    fmt.Printf("Fprintf: %08x\n", h.Sum32())
}

运行

$ go run main.go
单次:1a0b045d
多次:1a0b045d
Fprintf: 1a0b045d

三、序列化支持

adler32 的哈希器实现了 encoding.BinaryMarshalerencoding.BinaryUnmarshaler 接口,支持序列化/反序列化。

序列化哈希状态

示例

package main

import (
    "encoding"
    "fmt"
    "hash/adler32"
)

func main() {
    h := adler32.New()
    h.Write([]byte("Hello"))
    
    // 序列化
    marshaler := h.(encoding.BinaryMarshaler)
    data, err := marshaler.MarshalBinary()
    if err != nil {
        panic(err)
    }
    
    fmt.Printf("序列化数据:%x\n", data)
    fmt.Printf("长度:%d 字节\n", len(data))
    
    // 反序列化到新哈希器
    h2 := adler32.New()
    unmarshaler := h2.(encoding.BinaryUnmarshaler)
    err = unmarshaler.UnmarshalBinary(data)
    if err != nil {
        panic(err)
    }
    
    // 继续写入
    h2.Write([]byte(", World!"))
    
    // 验证
    h.Write([]byte(", World!"))
    if h.Sum32() == h2.Sum32() {
        fmt.Println("序列化后结果一致 ✓")
    }
}

运行

$ go run main.go
序列化数据:61646c010909024e
长度:9 字节
序列化后结果一致 ✓

四、使用场景

场景 1:文件完整性校验

package main

import (
    "fmt"
    "hash/adler32"
    "io"
    "os"
)

func checksumFile(filename string) (uint32, error) {
    file, err := os.Open(filename)
    if err != nil {
        return 0, err
    }
    defer file.Close()
    
    h := adler32.New()
    if _, err := io.Copy(h, file); err != nil {
        return 0, err
    }
    
    return h.Sum32(), nil
}

func verifyFile(filename string, expected uint32) error {
    actual, err := checksumFile(filename)
    if err != nil {
        return err
    }
    if actual != expected {
        return fmt.Errorf("校验和不匹配:期望 %08x, 实际 %08x", expected, actual)
    }
    return nil
}

func main() {
    if len(os.Args) < 2 {
        fmt.Println("用法:checksum <文件>")
        os.Exit(1)
    }
    
    sum, err := checksumFile(os.Args[1])
    if err != nil {
        fmt.Printf("错误:%v\n", err)
        os.Exit(1)
    }
    
    fmt.Printf("Adler-32: %08x\n", sum)
}

场景 2:数据传输校验

package main

import (
    "fmt"
    "hash/adler32"
)

type Packet struct {
    Data     []byte
    Checksum uint32
}

func NewPacket(data []byte) *Packet {
    h := adler32.New()
    h.Write(data)
    return &Packet{
        Data:     data,
        Checksum: h.Sum32(),
    }
}

func (p *Packet) Verify() bool {
    h := adler32.New()
    h.Write(p.Data)
    return h.Sum32() == p.Checksum
}

func main() {
    // 创建数据包
    packet := NewPacket([]byte("Hello, World!"))
    
    fmt.Printf("数据:%s\n", packet.Data)
    fmt.Printf("校验和:%08x\n", packet.Checksum)
    fmt.Printf("验证:%v\n", packet.Verify())
    
    // 模拟数据损坏
    packet.Data[0] = 'X'
    fmt.Printf("损坏后验证:%v\n", packet.Verify())
}

运行

$ go run main.go
数据:Hello, World!
校验和:1a0b045d
验证:true
损坏后验证:false

场景 3:增量校验

package main

import (
    "fmt"
    "hash/adler32"
)

func main() {
    h := adler32.New()
    
    // 分块写入
    chunks := [][]byte{
        []byte("chunk1"),
        []byte("chunk2"),
        []byte("chunk3"),
    }
    
    for i, chunk := range chunks {
        h.Write(chunk)
        fmt.Printf("写入块 %d 后:%08x\n", i+1, h.Sum32())
    }
    
    // 最终校验和
    fmt.Printf("最终校验和:%08x\n", h.Sum32())
    
    // 验证:一次性写入所有数据
    h2 := adler32.New()
    var allData []byte
    for _, chunk := range chunks {
        allData = append(allData, chunk...)
    }
    h2.Write(allData)
    
    if h.Sum32() == h2.Sum32() {
        fmt.Println("增量校验一致 ✓")
    }
}

运行

$ go run main.go
写入块 1 后:006000be
写入块 2 后:00e4017f
写入块 3 后:01780240
最终校验和:01780240
增量校验一致 ✓

五、最佳实践

1. 复用哈希器

// 推荐:复用哈希器
h := adler32.New()
for _, data := range dataList {
    h.Reset()
    h.Write(data)
    checksum := h.Sum32()
    // 使用 checksum
}

// 不推荐:每次都创建新实例
for _, data := range dataList {
    h := adler32.New()
    h.Write(data)
    checksum := h.Sum32()
}

2. 选择合适的算法

// Adler-32 vs CRC-32

// Adler-32 特点:
// - 更快(适合软件实现)
// - 可靠性略低
// - 适合小数据块
import "hash/adler32"

// CRC-32 特点:
// - 可靠性更高
// - 速度稍慢
// - 适合大数据块
import "hash/crc32"

3. 流式处理大文件

func hashLargeFile(path string) (uint32, error) {
    file, err := os.Open(path)
    if err != nil {
        return 0, err
    }
    defer file.Close()
    
    h := adler32.New()
    buf := make([]byte, 32*1024)
    
    for {
        n, err := file.Read(buf)
        if n > 0 {
            h.Write(buf[:n])
        }
        if err == io.EOF {
            break
        }
        if err != nil {
            return 0, err
        }
    }
    
    return h.Sum32(), nil
}

六、快速参考

核心函数

函数说明返回值示例
Checksum(data []byte)直接计算校验和uint32adler32.Checksum([]byte("data"))
New()创建哈希器hash.Hash32adler32.New()

Hash32 接口方法

方法说明返回值示例
Write(p []byte)写入数据(int, error)h.Write([]byte("data"))
Sum(in []byte)计算哈希[]byteh.Sum(nil)
Reset()重置哈希器-h.Reset()
Size()哈希长度inth.Size() (4)
BlockSize()块大小inth.BlockSize() (1)
Sum32()32 位校验和uint32h.Sum32()

常量

常量说明
Size4校验和字节长度

使用模式

场景推荐方法说明
小数据块Checksum()一次性计算
流式数据New() + Write()分块处理
重复使用Reset() + Write()性能优化
序列化MarshalBinary()保存状态

七、与其他包配合

与 encoding/hex 配合

package main

import (
    "encoding/hex"
    "fmt"
    "hash/adler32"
)

func main() {
    h := adler32.New()
    h.Write([]byte("data"))
    
    sum := h.Sum(nil)
    
    // 十六进制编码
    hexStr := hex.EncodeToString(sum)
    fmt.Printf("Hex: %s\n", hexStr)
    
    // 解码验证
    decoded, _ := hex.DecodeString(hexStr)
    fmt.Printf("Decoded: %x\n", decoded)
}

运行

$ go run main.go
Hex: 1a0b045d
Decoded: 1a0b045d

与 io 包配合

package main

import (
    "fmt"
    "hash/adler32"
    "io"
    "strings"
)

func main() {
    h := adler32.New()
    
    // 使用 io.WriteString
    io.WriteString(h, "Hello")
    io.WriteString(h, ", ")
    io.WriteString(h, "World!")
    
    fmt.Printf("Adler-32: %08x\n", h.Sum32())
    
    // 使用 io.Copy(从 Reader)
    h.Reset()
    reader := strings.NewReader("data")
    io.Copy(h, reader)
    fmt.Printf("From Reader: %08x\n", h.Sum32())
}

运行

$ go run main.go
Adler-32: 1a0b045d
From Reader: 1a0b045d

与 bytes 包配合

package main

import (
    "bytes"
    "fmt"
    "hash/adler32"
)

func main() {
    // 创建缓冲区
    buf := bytes.NewBufferString("Hello, World!")
    
    // 创建哈希器
    h := adler32.New()
    
    // 直接写入
    h.Write(buf.Bytes())
    fmt.Printf("Adler-32: %08x\n", h.Sum32())
    
    // 使用 io.Copy
    h.Reset()
    io.Copy(h, buf)
    fmt.Printf("From Buffer: %08x\n", h.Sum32())
}

运行

$ go run main.go
Adler-32: 1a0b045d
From Buffer: 1a0b045d

八、算法特点

Adler-32 算法原理

Adler-32 由两个 16 位的和组成:

  • s1:所有字节的和(模 65521)
  • s2:所有 s1 值的和(模 65521)

最终结果:s2 * 65536 + s1

初始化

  • s1 = 1
  • s2 = 0

示例

// Adler-32 伪代码
func adler32(data []byte) uint32 {
    s1 := uint32(1)
    s2 := uint32(0)
    
    for _, b := range data {
        s1 = (s1 + uint32(b)) % 65521
        s2 = (s2 + s1) % 65521
    }
    
    return s2<<16 | s1
}

性能对比

算法速度可靠性适用场景
Adler-32中等小数据、压缩
CRC-32中等大数据、网络
MD5高(已不安全)加密(不推荐)
SHA-256很慢很高加密、安全

最后更新:2026-04-04
Go 版本:Go 1.23+