Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add unsafe little endian loaders #1036

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 42 additions & 51 deletions .github/workflows/go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,18 +32,24 @@ jobs:
- name: Test
run: go test ./...

- name: Test Noasm
- name: Test No-asm
run: go test -tags=noasm ./...

- name: Test No-unsafe
run: go test -tags=nounsafe ./...

- name: Test No-unsafe, noasm
run: go test -tags="nounsafe,noasm" ./...

- name: Test Race 1 CPU
env:
CGO_ENABLED: 1
run: go test -cpu=1 -short -race -v ./...
run: go test -cpu=1 -short -race -tags=nounsafe -v ./...

- name: Test Race 4 CPU
env:
CGO_ENABLED: 1
run: go test -cpu=4 -short -race -v ./...
run: go test -cpu=4 -short -race -tags=nounsafe -v ./...

generate:
strategy:
Expand Down Expand Up @@ -112,6 +118,9 @@ jobs:
env:
CGO_ENABLED: 0
runs-on: ubuntu-latest
strategy:
matrix:
tags: [ 'nounsafe', '"noasm,nounsafe"' ]
steps:
- name: Set up Go
uses: actions/[email protected]
Expand All @@ -121,28 +130,23 @@ jobs:
- name: Checkout code
uses: actions/checkout@v4

- name: S2/FuzzDictBlocks
run: go test -run=none -fuzz=FuzzDictBlocks -fuzztime=100000x -test.fuzzminimizetime=10ms ./s2/.
- name: S2/FuzzDictBlocks/${{ matrix.tags }}
run: go test -tags=${{ matrix.tags }} -run=none -fuzz=FuzzDictBlocks -fuzztime=100000x -test.fuzzminimizetime=10ms ./s2/.

- name: S2/FuzzEncodingBlocks
run: go test -run=none -fuzz=FuzzEncodingBlocks -fuzztime=500000x -test.fuzzminimizetime=10ms ./s2/.
- name: S2/FuzzEncodingBlocks/${{ matrix.tags }}
run: go test -tags=${{ matrix.tags }} -run=none -fuzz=FuzzEncodingBlocks -fuzztime=500000x -test.fuzzminimizetime=10ms ./s2/.

- name: S2/FuzzLZ4Block
run: go test -run=none -fuzz=FuzzLZ4Block -fuzztime=500000x -test.fuzzminimizetime=10ms ./s2/.
- name: S2/FuzzLZ4Block/${{ matrix.tags }}
run: go test -tags=${{ matrix.tags }} -run=none -fuzz=FuzzLZ4Block -fuzztime=500000x -test.fuzzminimizetime=10ms ./s2/.

- name: S2/FuzzDictBlocks/noasm
run: go test -tags=noasm -run=none -fuzz=FuzzDictBlocks -fuzztime=100000x -test.fuzzminimizetime=10ms ./s2/.

- name: S2/FuzzEncodingBlocks/noasm
run: go test -tags=noasm -run=none -fuzz=FuzzEncodingBlocks -fuzztime=500000x -test.fuzzminimizetime=10ms ./s2/.

- name: S2/FuzzLZ4Block/noasm
run: go test -tags=noasm -run=none -fuzz=FuzzLZ4Block -fuzztime=500000x -test.fuzzminimizetime=10ms ./s2/.

fuzz-zstd:
env:
CGO_ENABLED: 0
runs-on: ubuntu-latest
strategy:
matrix:
tags: [ 'nounsafe', '"noasm,nounsafe"' ]
steps:
- name: Set up Go
uses: actions/[email protected]
Expand All @@ -152,57 +156,44 @@ jobs:
- name: Checkout code
uses: actions/checkout@v4

- name: zstd/FuzzDecodeAll
run: go test -run=none -fuzz=FuzzDecodeAll -fuzztime=500000x -test.fuzzminimizetime=10ms ./zstd/.
- name: zstd/FuzzDecodeAll/${{ matrix.tags }}
run: go test -tags=${{ matrix.tags }} -run=none -fuzz=FuzzDecodeAll -fuzztime=500000x -test.fuzzminimizetime=10ms ./zstd/.

- name: zstd/FuzzDecAllNoBMI2
run: go test -run=none -fuzz=FuzzDecAllNoBMI2 -fuzztime=500000x -test.fuzzminimizetime=10ms ./zstd/.
- name: zstd/FuzzDecAllNoBMI2/${{ matrix.tags }}
run: go test -tags=${{ matrix.tags }} -run=none -fuzz=FuzzDecAllNoBMI2 -fuzztime=500000x -test.fuzzminimizetime=10ms ./zstd/.

- name: zstd/FuzzDecoder
run: go test -run=none -fuzz=FuzzDecoder -fuzztime=500000x -test.fuzzminimizetime=10ms ./zstd/.
- name: zstd/FuzzDecoder/${{ matrix.tags }}
run: go test -tags=${{ matrix.tags }} -run=none -fuzz=FuzzDecoder -fuzztime=500000x -test.fuzzminimizetime=10ms ./zstd/.

- name: zstd/FuzzNoBMI2Dec
run: go test -run=none -fuzz=FuzzNoBMI2Dec -fuzztime=500000x -test.fuzzminimizetime=10ms ./zstd/.
- name: zstd/FuzzNoBMI2Dec/${{ matrix.tags }}
run: go test -tags=${{ matrix.tags }} -run=none -fuzz=FuzzNoBMI2Dec -fuzztime=500000x -test.fuzzminimizetime=10ms ./zstd/.

- name: zstd/FuzzEncoding
run: cd zstd&&go test -run=none -fuzz=FuzzEncoding -fuzztime=250000x -test.fuzzminimizetime=10ms -fuzz-end=3&&cd ..

- name: zstd/FuzzDecodeAll/noasm
run: go test -tags=noasm -run=none -fuzz=FuzzDecodeAll -fuzztime=500000x -test.fuzzminimizetime=10ms ./zstd/.

- name: zstd/FuzzDecoder/noasm
run: go test -tags=noasm -run=none -fuzz=FuzzDecoder -fuzztime=500000x -test.fuzzminimizetime=10ms ./zstd/.

- name: zstd/FuzzEncoding/noasm
run: cd zstd&&go test -tags=noasm -run=none -fuzz=FuzzEncoding -fuzztime=250000x -test.fuzzminimizetime=10ms -fuzz-end=3&&cd ..

- name: zstd/FuzzEncodingBest
run: cd zstd&&go test -run=none -fuzz=FuzzEncoding -fuzztime=25000x -test.fuzzminimizetime=10ms -fuzz-start=4&&cd ..
- name: zstd/FuzzEncoding/${{ matrix.tags }}
run: cd zstd&&go test -tags=${{ matrix.tags }} -run=none -fuzz=FuzzEncoding -fuzztime=250000x -test.fuzzminimizetime=10ms -fuzz-end=3&&cd ..

fuzz-other:
env:
CGO_ENABLED: 0
runs-on: ubuntu-latest
strategy:
matrix:
tags: [ 'nounsafe', '"noasm,nounsafe"' ]
steps:
- name: Set up Go
uses: actions/[email protected]
with:
go-version: 1.23.x

- name: Checkout code
uses: actions/checkout@v4

- name: flate/FuzzEncoding
run: go test -run=none -fuzz=FuzzEncoding -fuzztime=100000x -test.fuzzminimizetime=10ms ./flate/.

- name: flate/FuzzEncoding/noasm
run: go test -run=none -tags=noasm -fuzz=FuzzEncoding -fuzztime=100000x -test.fuzzminimizetime=10ms ./flate/.
- name: flate/FuzzEncoding/${{ matrix.tags }}
run: go test -tags=${{ matrix.tags }} -run=none -fuzz=FuzzEncoding -fuzztime=100000x -test.fuzzminimizetime=10ms ./flate/.

- name: zip/FuzzReader
run: go test -run=none -fuzz=FuzzReader -fuzztime=500000x -test.fuzzminimizetime=10ms ./zip/.
- name: zip/FuzzReader/${{ matrix.tags }}
run: go test -tags=${{ matrix.tags }} -run=none -fuzz=FuzzReader -fuzztime=500000x -test.fuzzminimizetime=10ms ./zip/.

- name: fse/FuzzCompress
run: go test -run=none -fuzz=FuzzCompress -fuzztime=1000000x -test.fuzzminimizetime=10ms ./fse/.
- name: fse/FuzzCompress/${{ matrix.tags }}
run: go test -tags=${{ matrix.tags }} -run=none -fuzz=FuzzCompress -fuzztime=1000000x -test.fuzzminimizetime=10ms ./fse/.

- name: fse/FuzzDecompress
run: go test -run=none -fuzz=FuzzDecompress -fuzztime=1000000x -test.fuzzminimizetime=10ms ./fse/.
- name: fse/FuzzDecompress/${{ matrix.tags }}
run: go test -tags=${{ matrix.tags }} -run=none -fuzz=FuzzDecompress -fuzztime=1000000x -test.fuzzminimizetime=10ms ./fse/.
7 changes: 4 additions & 3 deletions flate/fast_encoder.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@
package flate

import (
"encoding/binary"
"fmt"

"github.com/klauspost/compress/internal/le"
)

type fastEnc interface {
Expand Down Expand Up @@ -58,11 +59,11 @@ const (
)

func load3232(b []byte, i int32) uint32 {
return binary.LittleEndian.Uint32(b[i:])
return le.Load32(b, i)
}

func load6432(b []byte, i int32) uint64 {
return binary.LittleEndian.Uint64(b[i:])
return le.Load64(b, i)
}

type tableEntry struct {
Expand Down
1 change: 0 additions & 1 deletion flate/fuzz_test.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
//go:build go1.18
// +build go1.18

package flate

Expand Down
23 changes: 9 additions & 14 deletions huff0/bitreader.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@
package huff0

import (
"encoding/binary"
"errors"
"fmt"
"io"

"github.com/klauspost/compress/internal/le"
)

// bitReader reads a bitstream in reverse.
Expand Down Expand Up @@ -66,8 +67,7 @@ func (b *bitReaderBytes) fillFast() {
}

// 2 bounds checks.
v := b.in[b.off-4 : b.off]
low := (uint32(v[0])) | (uint32(v[1]) << 8) | (uint32(v[2]) << 16) | (uint32(v[3]) << 24)
low := le.Load32(b.in, b.off-4)
b.value |= uint64(low) << (b.bitsRead - 32)
b.bitsRead -= 32
b.off -= 4
Expand All @@ -76,7 +76,7 @@ func (b *bitReaderBytes) fillFast() {
// fillFastStart() assumes the bitReaderBytes is empty and there is at least 8 bytes to read.
func (b *bitReaderBytes) fillFastStart() {
// Do single re-slice to avoid bounds checks.
b.value = binary.LittleEndian.Uint64(b.in[b.off-8:])
b.value = le.Load64(b.in, b.off-8)
b.bitsRead = 0
b.off -= 8
}
Expand All @@ -86,9 +86,8 @@ func (b *bitReaderBytes) fill() {
if b.bitsRead < 32 {
return
}
if b.off > 4 {
v := b.in[b.off-4 : b.off]
low := (uint32(v[0])) | (uint32(v[1]) << 8) | (uint32(v[2]) << 16) | (uint32(v[3]) << 24)
if b.off >= 4 {
low := le.Load32(b.in, b.off-4)
b.value |= uint64(low) << (b.bitsRead - 32)
b.bitsRead -= 32
b.off -= 4
Expand Down Expand Up @@ -175,18 +174,15 @@ func (b *bitReaderShifted) fillFast() {
return
}

// 2 bounds checks.
v := b.in[b.off-4 : b.off]
low := (uint32(v[0])) | (uint32(v[1]) << 8) | (uint32(v[2]) << 16) | (uint32(v[3]) << 24)
low := le.Load32(b.in, b.off-4)
b.value |= uint64(low) << ((b.bitsRead - 32) & 63)
b.bitsRead -= 32
b.off -= 4
}

// fillFastStart() assumes the bitReaderShifted is empty and there is at least 8 bytes to read.
func (b *bitReaderShifted) fillFastStart() {
// Do single re-slice to avoid bounds checks.
b.value = binary.LittleEndian.Uint64(b.in[b.off-8:])
b.value = le.Load64(b.in, b.off-8)
b.bitsRead = 0
b.off -= 8
}
Expand All @@ -197,8 +193,7 @@ func (b *bitReaderShifted) fill() {
return
}
if b.off > 4 {
v := b.in[b.off-4 : b.off]
low := (uint32(v[0])) | (uint32(v[1]) << 8) | (uint32(v[2]) << 16) | (uint32(v[3]) << 24)
low := le.Load32(b.in, b.off-4)
b.value |= uint64(low) << ((b.bitsRead - 32) & 63)
b.bitsRead -= 32
b.off -= 4
Expand Down
8 changes: 4 additions & 4 deletions huff0/decompress_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ func TestDecompress1X(t *testing.T) {
t.Log(string(dc))
}
//t.Errorf(test.name+": decompressed, got delta: \n%s")
t.Errorf(test.name + ": decompressed, got delta")
t.Error(test.name + ": decompressed, got delta")
}
if !t.Failed() {
t.Log("... roundtrip ok!")
Expand Down Expand Up @@ -221,7 +221,7 @@ func TestDecompress4X(t *testing.T) {
t.Log(string(dc))
}
//t.Errorf(test.name+": decompressed, got delta: \n%s")
t.Errorf(test.name + ": decompressed, got delta")
t.Error(test.name + ": decompressed, got delta")
}
if !t.Failed() {
t.Log("... roundtrip ok!")
Expand Down Expand Up @@ -315,7 +315,7 @@ func TestRoundtrip1XFuzz(t *testing.T) {
t.Log(string(dc))
}
//t.Errorf(test.name+": decompressed, got delta: \n%s")
t.Errorf(test.name + ": decompressed, got delta")
t.Error(test.name + ": decompressed, got delta")
}
if !t.Failed() {
t.Log("... roundtrip ok!")
Expand Down Expand Up @@ -406,7 +406,7 @@ func TestRoundtrip4XFuzz(t *testing.T) {
t.Log(string(dc))
}
//t.Errorf(test.name+": decompressed, got delta: \n%s")
t.Errorf(test.name + ": decompressed, got delta")
t.Error(test.name + ": decompressed, got delta")
}
if !t.Failed() {
t.Log("... roundtrip ok!")
Expand Down
5 changes: 5 additions & 0 deletions internal/le/le.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package le

type Indexer interface {
int | int8 | int16 | int32 | int64 | uint | uint8 | uint16 | uint32 | uint64
}
27 changes: 27 additions & 0 deletions internal/le/unsafe_disabled.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
//go:build !(amd64 || arm64 || ppc64le || riscv64) || nounsafe || purego || appengine

package le

import (
"encoding/binary"
)

func Load16[I Indexer](b []byte, i I) uint16 {
return binary.LittleEndian.Uint16(b[i:])
}

func Load32[I Indexer](b []byte, i I) uint32 {
return binary.LittleEndian.Uint32(b[i:])
}

func Load64[I Indexer](b []byte, i I) uint64 {
return binary.LittleEndian.Uint64(b[i:])
}

func Store16(b []byte, v uint16) {
binary.LittleEndian.PutUint16(b, v)
}

func Store32(b []byte, v uint32) {
binary.LittleEndian.PutUint32(b, v)
}
37 changes: 37 additions & 0 deletions internal/le/unsafe_enabled.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// We enable 64 bit LE platforms:

//go:build (amd64 || arm64 || ppc64le || riscv64) && !nounsafe && !purego && !appengine

package le

import (
"unsafe"
)

func Load16[I Indexer](b []byte, i I) uint16 {
//return binary.LittleEndian.Uint16(b[i:])
//return *(*uint16)(unsafe.Pointer(&b[i]))
return *(*uint16)(unsafe.Pointer(uintptr(unsafe.Pointer(&b[0])) + uintptr(i)*unsafe.Sizeof(b[0])))
}
klauspost marked this conversation as resolved.
Show resolved Hide resolved

func Load32[I Indexer](b []byte, i I) uint32 {
//return binary.LittleEndian.Uint32(b[i:])
//return *(*uint32)(unsafe.Pointer(&b[i]))
return *(*uint32)(unsafe.Pointer(uintptr(unsafe.Pointer(&b[0])) + uintptr(i)*unsafe.Sizeof(b[0])))
}

func Load64[I Indexer](b []byte, i I) uint64 {
//return binary.LittleEndian.Uint64(b[i:])
//return *(*uint64)(unsafe.Pointer(&b[i]))
return *(*uint64)(unsafe.Pointer(uintptr(unsafe.Pointer(&b[0])) + uintptr(i)*unsafe.Sizeof(b[0])))
}
klauspost marked this conversation as resolved.
Show resolved Hide resolved

func Store16(b []byte, v uint16) {
//binary.LittleEndian.PutUint16(b, v)
*(*uint16)(unsafe.Pointer(&b[0])) = v
}
klauspost marked this conversation as resolved.
Show resolved Hide resolved

func Store32(b []byte, v uint32) {
//binary.LittleEndian.PutUint32(b, v)
*(*uint32)(unsafe.Pointer(&b[0])) = v
}
klauspost marked this conversation as resolved.
Show resolved Hide resolved
Loading
Loading