This commit is contained in:
2025-04-19 22:56:37 +08:00
commit ceca244eaf
50 changed files with 7321 additions and 0 deletions

115
internal/hashutil/crc16/crc16.go Executable file
View File

@@ -0,0 +1,115 @@
// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package crc16 implements the 16-bit cyclic redundancy check, or CRC-16,
// checksum. See http://en.wikipedia.org/wiki/Cyclic_redundancy_check and
// http://www.ross.net/crc/download/crc_v3.txt for information.
package crc16
import "github.com/mewkiz/flac/internal/hashutil"
// Size of a CRC-16 checksum in bytes.
const Size = 2
// Predefined polynomials.
const (
IBM = 0x8005 // x^16 + x^15 + x^2 + x^0
)
// Table is a 256-word table representing the polynomial for efficient
// processing.
type Table [256]uint16
// IBMTable is the table for the IBM polynomial.
var IBMTable = makeTable(IBM)
// MakeTable returns the Table constructed from the specified polynomial.
func MakeTable(poly uint16) (table *Table) {
switch poly {
case IBM:
return IBMTable
}
return makeTable(poly)
}
// makeTable returns the Table constructed from the specified polynomial.
func makeTable(poly uint16) (table *Table) {
table = new(Table)
for i := range table {
crc := uint16(i << 8)
for j := 0; j < 8; j++ {
if crc&0x8000 != 0 {
crc = crc<<1 ^ poly
} else {
crc <<= 1
}
}
table[i] = crc
}
return table
}
// digest represents the partial evaluation of a checksum.
type digest struct {
crc uint16
table *Table
}
// New creates a new hashutil.Hash16 computing the CRC-16 checksum using the
// polynomial represented by the Table.
func New(table *Table) hashutil.Hash16 {
return &digest{0, table}
}
// NewIBM creates a new hashutil.Hash16 computing the CRC-16 checksum using the
// IBM polynomial.
func NewIBM() hashutil.Hash16 {
return New(IBMTable)
}
func (d *digest) Size() int {
return Size
}
func (d *digest) BlockSize() int {
return 1
}
func (d *digest) Reset() {
d.crc = 0
}
// Update returns the result of adding the bytes in p to the crc.
func Update(crc uint16, table *Table, p []byte) uint16 {
for _, v := range p {
crc = crc<<8 ^ table[crc>>8^uint16(v)]
}
return crc
}
func (d *digest) Write(p []byte) (n int, err error) {
d.crc = Update(d.crc, d.table, p)
return len(p), nil
}
// Sum16 returns the 16-bit checksum of the hash.
func (d *digest) Sum16() uint16 {
return d.crc
}
func (d *digest) Sum(in []byte) []byte {
s := d.Sum16()
return append(in, byte(s>>8), byte(s))
}
// Checksum returns the CRC-16 checksum of data, using the polynomial
// represented by the Table.
func Checksum(data []byte, table *Table) uint16 {
return Update(0, table, data)
}
// ChecksumIBM returns the CRC-16 checksum of data using the IBM polynomial.
func ChecksumIBM(data []byte) uint16 {
return Update(0, IBMTable, data)
}

View File

@@ -0,0 +1,106 @@
package crc16
import (
"io"
"testing"
)
type test struct {
want uint16
in string
}
var golden = []test{
{0x0000, ""},
{0x8145, "a"},
{0xC749, "ab"},
{0xCADB, "abc"},
{0x58E7, "abcd"},
{0x678D, "abcde"},
{0x0D05, "abcdef"},
{0x047C, "abcdefg"},
{0x7D68, "abcdefgh"},
{0x6878, "abcdefghi"},
{0xF80F, "abcdefghij"},
{0x0F8E, "Discard medicine more than two years old."},
{0xE149, "He who has a shady past knows that nice guys finish last."},
{0x02B7, "I wouldn't marry him with a ten foot pole."},
{0x7F6A, "Free! Free!/A trip/to Mars/for 900/empty jars/Burma Shave"},
{0x28BD, "The days of the digital watch are numbered. -Tom Stoppard"},
{0x7C55, "Nepal premier won't resign."},
{0xC92B, "For every action there is an equal and opposite government program."},
{0x3E41, "His money is twice tainted: 'taint yours and 'taint mine."},
{0xDA56, "There is no reason for any individual to have a computer in their home. -Ken Olsen, 1977"},
{0x7F66, "It's a tiny change to the code and not completely disgusting. - Bob Manchek"},
{0x2A00, "size: a.out: bad magic"},
{0x25B2, "The major problem is with sendmail. -Mark Horton"},
{0xBD71, "Give me a rock, paper and scissors and I will move the world. CCFestoon"},
{0x8596, "If the enemy is within range, then so are you."},
{0x74A2, "It's well we cannot hear the screams/That we create in others' dreams."},
{0x0D73, "You remind me of a TV show, but that's all right: I watch it anyway."},
{0xEE65, "C is as portable as Stonehedge!!"},
{0xA94E, "Even if I could be Shakespeare, I think I should still choose to be Faraday. - A. Huxley"},
{0x0B98, "The fugacity of a constituent in a mixture of gases at a given temperature is proportional to its mole fraction. Lewis-Randall Rule"},
{0xF560, "How can you write a big system without C++? -Paul Glick"},
{0x60AE, "The quick brown fox jumps over the lazy dog"},
}
func TestCrc16IBM(t *testing.T) {
for _, g := range golden {
h := NewIBM()
if _, err := io.WriteString(h, g.in); err != nil {
t.Error(err)
continue
}
got := h.Sum16()
if got != g.want {
t.Errorf("IBM(%q); expected 0x%04X, got 0x%04X.", g.in, g.want, got)
}
}
}
func BenchmarkNewIBM(b *testing.B) {
for i := 0; i < b.N; i++ {
NewIBM()
}
}
func BenchmarkCrc16_1K(b *testing.B) {
benchmarkCrc16(b, 1024)
}
func BenchmarkCrc16_2K(b *testing.B) {
benchmarkCrc16(b, 2*1024)
}
func BenchmarkCrc16_4K(b *testing.B) {
benchmarkCrc16(b, 4*1024)
}
func BenchmarkCrc16_8K(b *testing.B) {
benchmarkCrc16(b, 8*1024)
}
func BenchmarkCrc16_16K(b *testing.B) {
benchmarkCrc16(b, 16*1024)
}
func benchmarkCrc16(b *testing.B, count int64) {
b.SetBytes(count)
data := make([]byte, count)
for i := range data {
data[i] = byte(i)
}
h := NewIBM()
in := make([]byte, 0, h.Size())
b.ResetTimer()
for i := 0; i < b.N; i++ {
h.Reset()
if _, err := h.Write(data); err != nil {
b.Error(err)
continue
}
h.Sum(in)
}
}

114
internal/hashutil/crc8/crc8.go Executable file
View File

@@ -0,0 +1,114 @@
// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package crc8 implements the 8-bit cyclic redundancy check, or CRC-8,
// checksum. See http://en.wikipedia.org/wiki/Cyclic_redundancy_check and
// http://www.ross.net/crc/download/crc_v3.txt for information.
package crc8
import "github.com/mewkiz/flac/internal/hashutil"
// Size of a CRC-8 checksum in bytes.
const Size = 1
// Predefined polynomials.
const (
ATM = 0x07 // x^8 + x^2 + x + 1
)
// Table is a 256-word table representing the polynomial for efficient
// processing.
type Table [256]uint8
// ATMTable is the table for the ATM polynomial.
var ATMTable = makeTable(ATM)
// MakeTable returns the Table constructed from the specified polynomial.
func MakeTable(poly uint8) (table *Table) {
switch poly {
case ATM:
return ATMTable
}
return makeTable(poly)
}
// makeTable returns the Table constructed from the specified polynomial.
func makeTable(poly uint8) (table *Table) {
table = new(Table)
for i := range table {
crc := uint8(i)
for j := 0; j < 8; j++ {
if crc&0x80 != 0 {
crc = crc<<1 ^ poly
} else {
crc <<= 1
}
}
table[i] = crc
}
return table
}
// digest represents the partial evaluation of a checksum.
type digest struct {
crc uint8
table *Table
}
// New creates a new hashutil.Hash8 computing the CRC-8 checksum using the
// polynomial represented by the Table.
func New(table *Table) hashutil.Hash8 {
return &digest{0, table}
}
// NewATM creates a new hashutil.Hash8 computing the CRC-8 checksum using the
// ATM polynomial.
func NewATM() hashutil.Hash8 {
return New(ATMTable)
}
func (d *digest) Size() int {
return Size
}
func (d *digest) BlockSize() int {
return 1
}
func (d *digest) Reset() {
d.crc = 0
}
// Update returns the result of adding the bytes in p to the crc.
func Update(crc uint8, table *Table, p []byte) uint8 {
for _, v := range p {
crc = table[crc^v]
}
return crc
}
func (d *digest) Write(p []byte) (n int, err error) {
d.crc = Update(d.crc, d.table, p)
return len(p), nil
}
// Sum8 returns the 8-bit checksum of the hash.
func (d *digest) Sum8() uint8 {
return d.crc
}
func (d *digest) Sum(in []byte) []byte {
return append(in, d.crc)
}
// Checksum returns the CRC-8 checksum of data, using the polynomial represented
// by the Table.
func Checksum(data []byte, table *Table) uint8 {
return Update(0, table, data)
}
// ChecksumATM returns the CRC-8 checksum of data using the ATM polynomial.
func ChecksumATM(data []byte) uint8 {
return Update(0, ATMTable, data)
}

View File

@@ -0,0 +1,106 @@
package crc8
import (
"io"
"testing"
)
type test struct {
want uint8
in string
}
var golden = []test{
{0x00, ""},
{0x20, "a"},
{0xC9, "ab"},
{0x5F, "abc"},
{0xA1, "abcd"},
{0x52, "abcde"},
{0x8C, "abcdef"},
{0x9F, "abcdefg"},
{0xCB, "abcdefgh"},
{0x67, "abcdefghi"},
{0x23, "abcdefghij"},
{0x56, "Discard medicine more than two years old."},
{0x6B, "He who has a shady past knows that nice guys finish last."},
{0x70, "I wouldn't marry him with a ten foot pole."},
{0x8F, "Free! Free!/A trip/to Mars/for 900/empty jars/Burma Shave"},
{0x48, "The days of the digital watch are numbered. -Tom Stoppard"},
{0x5E, "Nepal premier won't resign."},
{0x3C, "For every action there is an equal and opposite government program."},
{0xA8, "His money is twice tainted: 'taint yours and 'taint mine."},
{0x46, "There is no reason for any individual to have a computer in their home. -Ken Olsen, 1977"},
{0xC7, "It's a tiny change to the code and not completely disgusting. - Bob Manchek"},
{0x31, "size: a.out: bad magic"},
{0xB6, "The major problem is with sendmail. -Mark Horton"},
{0x7D, "Give me a rock, paper and scissors and I will move the world. CCFestoon"},
{0xDC, "If the enemy is within range, then so are you."},
{0x13, "It's well we cannot hear the screams/That we create in others' dreams."},
{0x96, "You remind me of a TV show, but that's all right: I watch it anyway."},
{0x96, "C is as portable as Stonehedge!!"},
{0x3C, "Even if I could be Shakespeare, I think I should still choose to be Faraday. - A. Huxley"},
{0xEE, "The fugacity of a constituent in a mixture of gases at a given temperature is proportional to its mole fraction. Lewis-Randall Rule"},
{0x33, "How can you write a big system without C++? -Paul Glick"},
{0xC1, "The quick brown fox jumps over the lazy dog"},
}
func TestCrc8ATM(t *testing.T) {
for _, g := range golden {
h := NewATM()
if _, err := io.WriteString(h, g.in); err != nil {
t.Error(err)
continue
}
got := h.Sum8()
if got != g.want {
t.Errorf("ATM(%q); expected 0x%02X, got 0x%02X.", g.in, g.want, got)
}
}
}
func BenchmarkNewATM(b *testing.B) {
for i := 0; i < b.N; i++ {
NewATM()
}
}
func BenchmarkCrc8_1K(b *testing.B) {
benchmarkCrc8(b, 1024)
}
func BenchmarkCrc8_2K(b *testing.B) {
benchmarkCrc8(b, 2*1024)
}
func BenchmarkCrc8_4K(b *testing.B) {
benchmarkCrc8(b, 4*1024)
}
func BenchmarkCrc8_8K(b *testing.B) {
benchmarkCrc8(b, 8*1024)
}
func BenchmarkCrc8_16K(b *testing.B) {
benchmarkCrc8(b, 16*1024)
}
func benchmarkCrc8(b *testing.B, count int64) {
b.SetBytes(count)
data := make([]byte, count)
for i := range data {
data[i] = byte(i)
}
h := NewATM()
in := make([]byte, 0, h.Size())
b.ResetTimer()
for i := 0; i < b.N; i++ {
h.Reset()
if _, err := h.Write(data); err != nil {
b.Error(err)
continue
}
h.Sum(in)
}
}

18
internal/hashutil/hashutil.go Executable file
View File

@@ -0,0 +1,18 @@
// Package hashutil provides utility interfaces for hash functions.
package hashutil
import "hash"
// Hash8 is the common interface implemented by all 8-bit hash functions.
type Hash8 interface {
hash.Hash
// Sum8 returns the 8-bit checksum of the hash.
Sum8() uint8
}
// Hash16 is the common interface implemented by all 16-bit hash functions.
type Hash16 interface {
hash.Hash
// Sum16 returns the 16-bit checksum of the hash.
Sum16() uint16
}