package fr32_test import ( "bytes" "io" "math/rand" "os" "testing" "github.com/stretchr/testify/require" ffi "github.com/filecoin-project/filecoin-ffi" commpffi "github.com/filecoin-project/go-commp-utils/ffiwrapper" "github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/lotus/storage/sealer/fr32" ) func padFFI(buf []byte) []byte { rf, w, _ := commpffi.ToReadableFile(bytes.NewReader(buf), int64(len(buf))) tf, _ := os.CreateTemp("/tmp/", "scrb-") _, _, _, err := ffi.WriteWithAlignment(abi.RegisteredSealProof_StackedDrg32GiBV1, rf, abi.UnpaddedPieceSize(len(buf)), tf, nil) if err != nil { panic(err) } if err := w(); err != nil { panic(err) } if _, err := tf.Seek(io.SeekStart, 0); err != nil { // nolint:staticcheck panic(err) } padded, err := io.ReadAll(tf) if err != nil { panic(err) } if err := tf.Close(); err != nil { panic(err) } if err := os.Remove(tf.Name()); err != nil { panic(err) } return padded } func TestPadChunkFFI(t *testing.T) { testByteChunk := func(b byte) func(*testing.T) { return func(t *testing.T) { var buf [128]byte copy(buf[:], bytes.Repeat([]byte{b}, 127)) fr32.Pad(buf[:], buf[:]) expect := padFFI(bytes.Repeat([]byte{b}, 127)) require.Equal(t, expect, buf[:]) } } t.Run("ones", testByteChunk(0xff)) t.Run("lsb1", testByteChunk(0x01)) t.Run("msb1", testByteChunk(0x80)) t.Run("zero", testByteChunk(0x0)) t.Run("mid", testByteChunk(0x3c)) } func TestPadChunkRandEqFFI(t *testing.T) { for i := 0; i < 200; i++ { var input [127]byte rand.Read(input[:]) var buf [128]byte fr32.Pad(input[:], buf[:]) expect := padFFI(input[:]) require.Equal(t, expect, buf[:]) } } func TestRoundtrip(t *testing.T) { testByteChunk := func(b byte) func(*testing.T) { return func(t *testing.T) { var buf [128]byte input := bytes.Repeat([]byte{0x01}, 127) fr32.Pad(input, buf[:]) var out [127]byte fr32.Unpad(buf[:], out[:]) require.Equal(t, input, out[:]) } } t.Run("ones", testByteChunk(0xff)) t.Run("lsb1", testByteChunk(0x01)) t.Run("msb1", testByteChunk(0x80)) t.Run("zero", testByteChunk(0x0)) t.Run("mid", testByteChunk(0x3c)) } func TestRoundtripChunkRand(t *testing.T) { for i := 0; i < 200; i++ { var input [127]byte rand.Read(input[:]) var buf [128]byte copy(buf[:], input[:]) fr32.Pad(buf[:], buf[:]) var out [127]byte fr32.Unpad(buf[:], out[:]) require.Equal(t, input[:], out[:]) } } func TestRoundtrip16MRand(t *testing.T) { up := abi.PaddedPieceSize(16 << 20).Unpadded() input := make([]byte, up) rand.Read(input[:]) buf := make([]byte, 16<<20) fr32.Pad(input, buf) out := make([]byte, up) fr32.Unpad(buf, out) require.Equal(t, input, out) ffi := padFFI(input) require.Equal(t, ffi, buf) } func BenchmarkPadChunk(b *testing.B) { var buf [128]byte in := bytes.Repeat([]byte{0xff}, 127) b.SetBytes(127) for i := 0; i < b.N; i++ { fr32.Pad(in, buf[:]) } } func BenchmarkChunkRoundtrip(b *testing.B) { var buf [128]byte copy(buf[:], bytes.Repeat([]byte{0xff}, 127)) var out [127]byte b.SetBytes(127) for i := 0; i < b.N; i++ { fr32.Pad(buf[:], buf[:]) fr32.Unpad(buf[:], out[:]) } } func BenchmarkUnpadChunk(b *testing.B) { var buf [128]byte copy(buf[:], bytes.Repeat([]byte{0xff}, 127)) fr32.Pad(buf[:], buf[:]) var out [127]byte b.SetBytes(127) b.ReportAllocs() bs := buf[:] for i := 0; i < b.N; i++ { fr32.Unpad(bs, out[:]) } } func BenchmarkUnpad16MChunk(b *testing.B) { up := abi.PaddedPieceSize(16 << 20).Unpadded() var buf [16 << 20]byte fr32.Pad(bytes.Repeat([]byte{0xff}, int(up)), buf[:]) var out [16 << 20]byte b.SetBytes(16 << 20) b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { fr32.Unpad(buf[:], out[:]) } } func BenchmarkPad16MChunk(b *testing.B) { up := abi.PaddedPieceSize(16 << 20).Unpadded() var buf [16 << 20]byte in := bytes.Repeat([]byte{0xff}, int(up)) b.SetBytes(16 << 20) b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { fr32.Pad(in, buf[:]) } } func BenchmarkPad1GChunk(b *testing.B) { up := abi.PaddedPieceSize(1 << 30).Unpadded() var buf [1 << 30]byte in := bytes.Repeat([]byte{0xff}, int(up)) b.SetBytes(1 << 30) b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { fr32.Pad(in, buf[:]) } } func BenchmarkUnpad1GChunk(b *testing.B) { up := abi.PaddedPieceSize(1 << 30).Unpadded() var buf [1 << 30]byte fr32.Pad(bytes.Repeat([]byte{0xff}, int(up)), buf[:]) var out [1 << 30]byte b.SetBytes(1 << 30) b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { fr32.Unpad(buf[:], out[:]) } }