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.BinaryMarshaler 和 encoding.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) | 直接计算校验和 | uint32 | adler32.Checksum([]byte("data")) |
| New() | 创建哈希器 | hash.Hash32 | adler32.New() |
Hash32 接口方法
| 方法 | 说明 | 返回值 | 示例 |
|---|---|---|---|
| Write(p []byte) | 写入数据 | (int, error) | h.Write([]byte("data")) |
| Sum(in []byte) | 计算哈希 | []byte | h.Sum(nil) |
| Reset() | 重置哈希器 | - | h.Reset() |
| Size() | 哈希长度 | int | h.Size() (4) |
| BlockSize() | 块大小 | int | h.BlockSize() (1) |
| Sum32() | 32 位校验和 | uint32 | h.Sum32() |
常量
| 常量 | 值 | 说明 |
|---|---|---|
| Size | 4 | 校验和字节长度 |
使用模式
| 场景 | 推荐方法 | 说明 |
|---|---|---|
| 小数据块 | 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+