Repository: cockroachdb/pebble
Pull request #3812 (2024-07-30 21:59:14)

sstable/block: move compression-related logic

cockroach-teamcity: 

<!-- Reviewable:start -->
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/cockroachdb/pebble/3812)
<!-- Reviewable:end -->


Patch:
diff --git a/internal/manifest/version.go b/internal/manifest/version.go
index 81453876..4956ec12 100644
--- a/internal/manifest/version.go
+++ b/internal/manifest/version.go
@@ -18,6 +18,7 @@ import (
 	"github.com/cockroachdb/pebble/internal/base"
 	"github.com/cockroachdb/pebble/internal/invariants"
 	"github.com/cockroachdb/pebble/sstable"
+	"github.com/cockroachdb/pebble/sstable/block"
 )
 
 // Compare exports the base.Compare type.
@@ -73,7 +74,7 @@ type TableStats struct {
 	// Total size of value blocks and value index block.
 	ValueBlocksSize uint64
 	// CompressionType is the compression type of the table.
-	CompressionType sstable.Compression
+	CompressionType block.Compression
 }
 
 // boundType represents the type of key (point or range) present as the smallest
diff --git a/metamorphic/options.go b/metamorphic/options.go
index f11158a3..9fe771d5 100644
--- a/metamorphic/options.go
+++ b/metamorphic/options.go
@@ -24,6 +24,7 @@ import (
 	"github.com/cockroachdb/pebble/internal/testkeys"
 	"github.com/cockroachdb/pebble/objstorage/remote"
 	"github.com/cockroachdb/pebble/sstable"
+	"github.com/cockroachdb/pebble/sstable/block"
 	"github.com/cockroachdb/pebble/vfs"
 	"github.com/cockroachdb/pebble/wal"
 	"golang.org/x/exp/rand"
@@ -730,11 +731,11 @@ func RandomOptions(
 	// We use either no compression, snappy compression or zstd compression.
 	switch rng.Intn(3) {
 	case 0:
-		lopts.Compression = func() sstable.Compression { return pebble.NoCompression }
+		lopts.Compression = func() block.Compression { return pebble.NoCompression }
 	case 1:
-		lopts.Compression = func() sstable.Compression { return pebble.ZstdCompression }
+		lopts.Compression = func() block.Compression { return pebble.ZstdCompression }
 	default:
-		lopts.Compression = func() sstable.Compression { return pebble.SnappyCompression }
+		lopts.Compression = func() block.Compression { return pebble.SnappyCompression }
 	}
 	opts.Levels = []pebble.LevelOptions{lopts}
 
diff --git a/options.go b/options.go
index 91e46938..18a21fef 100644
--- a/options.go
+++ b/options.go
@@ -25,6 +25,7 @@ import (
 	"github.com/cockroachdb/pebble/objstorage/remote"
 	"github.com/cockroachdb/pebble/rangekey"
 	"github.com/cockroachdb/pebble/sstable"
+	"github.com/cockroachdb/pebble/sstable/block"
 	"github.com/cockroachdb/pebble/vfs"
 	"github.com/cockroachdb/pebble/wal"
 )
@@ -35,14 +36,14 @@ const (
 )
 
 // Compression exports the base.Compression type.
-type Compression = sstable.Compression
+type Compression = block.Compression
 
 // Exported Compression constants.
 const (
-	DefaultCompression = sstable.DefaultCompression
-	NoCompression      = sstable.NoCompression
-	SnappyCompression  = sstable.SnappyCompression
-	ZstdCompression    = sstable.ZstdCompression
+	DefaultCompression = block.DefaultCompression
+	NoCompression      = block.NoCompression
+	SnappyCompression  = block.SnappyCompression
+	ZstdCompression    = block.ZstdCompression
 )
 
 // FilterType exports the base.FilterType type.
@@ -1808,13 +1809,13 @@ func (o *Options) Parse(s string, hooks *ParseHooks) error {
 			case "compression":
 				switch value {
 				case "Default":
-					l.Compression = func() sstable.Compression { return DefaultCompression }
+					l.Compression = func() Compression { return DefaultCompression }
 				case "NoCompression":
-					l.Compression = func() sstable.Compression { return NoCompression }
+					l.Compression = func() Compression { return NoCompression }
 				case "Snappy":
-					l.Compression = func() sstable.Compression { return SnappyCompression }
+					l.Compression = func() Compression { return SnappyCompression }
 				case "ZSTD":
-					l.Compression = func() sstable.Compression { return ZstdCompression }
+					l.Compression = func() Compression { return ZstdCompression }
 				default:
 					return errors.Errorf("pebble: unknown compression: %q", errors.Safe(value))
 				}
@@ -1988,7 +1989,7 @@ func (o *Options) MakeWriterOptions(level int, format sstable.TableFormat) sstab
 }
 
 func resolveDefaultCompression(c Compression) Compression {
-	if c <= DefaultCompression || c >= sstable.NCompression {
+	if c <= DefaultCompression || c >= block.NCompression {
 		c = SnappyCompression
 	}
 	return c
diff --git a/sstable/block/compression.go b/sstable/block/compression.go
new file mode 100644
index 00000000..10a62c49
--- /dev/null
+++ b/sstable/block/compression.go
@@ -0,0 +1,205 @@
+// Copyright 2024 The LevelDB-Go and Pebble 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 block
+
+import (
+	"encoding/binary"
+
+	"github.com/cockroachdb/errors"
+	"github.com/cockroachdb/pebble/internal/base"
+	"github.com/cockroachdb/pebble/internal/cache"
+	"github.com/golang/snappy"
+)
+
+// Compression is the per-block compression algorithm to use.
+type Compression int
+
+// The available compression types.
+const (
+	DefaultCompression Compression = iota
+	NoCompression
+	SnappyCompression
+	ZstdCompression
+	NCompression
+)
+
+// String implements fmt.Stringer, returning a human-readable name for the
+// compression algorithm.
+func (c Compression) String() string {
+	switch c {
+	case DefaultCompression:
+		return "Default"
+	case NoCompression:
+		return "NoCompression"
+	case SnappyCompression:
+		return "Snappy"
+	case ZstdCompression:
+		return "ZSTD"
+	default:
+		return "Unknown"
+	}
+}
+
+// CompressionFromString returns an sstable.Compression from its
+// string representation. Inverse of c.String() above.
+func CompressionFromString(s string) Compression {
+	switch s {
+	case "Default":
+		return DefaultCompression
+	case "NoCompression":
+		return NoCompression
+	case "Snappy":
+		return SnappyCompression
+	case "ZSTD":
+		return ZstdCompression
+	default:
+		return DefaultCompression
+	}
+}
+
+// CompressionIndicator is the byte stored physically within the block.Trailer
+// to indicate the compression type.
+//
+// TODO(jackson): Avoid exporting once all compression and decompression is
+// delegated to the block package.
+type CompressionIndicator byte
+
+// The block type gives the per-block compression format.
+// These constants are part of the file format and should not be changed.
+// They are different from the Compression constants because the latter
+// are designed so that the zero value of the Compression type means to
+// use the default compression (which is snappy).
+// Not all compression types listed here are supported.
+const (
+	NoCompressionIndicator     CompressionIndicator = 0
+	SnappyCompressionIndicator CompressionIndicator = 1
+	ZlibCompressionIndicator   CompressionIndicator = 2
+	Bzip2CompressionIndicator  CompressionIndicator = 3
+	Lz4CompressionIndicator    CompressionIndicator = 4
+	Lz4hcCompressionIndicator  CompressionIndicator = 5
+	XpressCompressionIndicator CompressionIndicator = 6
+	ZstdCompressionIndicator   CompressionIndicator = 7
+)
+
+// String implements fmt.Stringer.
+func (i CompressionIndicator) String() string {
+	switch i {
+	case 0:
+		return "none"
+	case 1:
+		return "snappy"
+	case 2:
+		return "zlib"
+	case 3:
+		return "bzip2"
+	case 4:
+		return "lz4"
+	case 5:
+		return "lz4hc"
+	case 6:
+		return "xpress"
+	case 7:
+		return "zstd"
+	default:
+		panic(errors.Newf("sstable: unknown block type: %d", i))
+	}
+}
+
+// DecompressedLen returns the length of the provided block once decompressed,
+// allowing the caller to allocate a buffer exactly sized to the decompressed
+// payload. For some compression algorithms, the payload is prefixed with a
+// varint encoding the length of the decompressed block. In such cases, a
+// non-zero prefixLength is returned indicating the length of this prefix.
+func DecompressedLen(
+	algo CompressionIndicator, b []byte,
+) (decompressedLen int, prefixLength int, err error) {
+	switch algo {
+	case NoCompressionIndicator:
+		return 0, 0, nil
+	case SnappyCompressionIndicator:
+		l, err := snappy.DecodedLen(b)
+		return l, 0, err
+	case ZstdCompressionIndicator:
+		// This will also be used by zlib, bzip2 and lz4 to retrieve the decodedLen
+		// if we implement these algorithms in the future.
+		decodedLenU64, varIntLen := binary.Uvarint(b)
+		if varIntLen <= 0 {
+			return 0, 0, base.CorruptionErrorf("pebble/table: compression block has invalid length")
+		}
+		return int(decodedLenU64), varIntLen, nil
+	default:
+		return 0, 0, base.CorruptionErrorf("pebble/table: unknown block compression: %d", errors.Safe(algo))
+	}
+}
+
+// DecompressInto decompresses compressed into buf. The buf slice must have the
+// exact size as the decompressed value. Callers may use DecompressedLen to
+// determine the correct size.
+func DecompressInto(algo CompressionIndicator, compressed []byte, buf []byte) error {
+	var result []byte
+	var err error
+	switch algo {
+	case SnappyCompressionIndicator:
+		result, err = snappy.Decode(buf, compressed)
+	case ZstdCompressionIndicator:
+		result, err = decodeZstd(buf, compressed)
+	default:
+		return base.CorruptionErrorf("pebble/table: unknown block compression: %d", errors.Safe(algo))
+	}
+	if err != nil {
+		return base.MarkCorruptionError(err)
+	}
+	if len(result) != len(buf) || (len(result) > 0 && &result[0] != &buf[0]) {
+		return base.CorruptionErrorf("pebble/table: decompressed into unexpected buffer: %p != %p",
+			errors.Safe(result), errors.Safe(buf))
+	}
+	return nil
+}
+
+// Decompress decompresses an sstable block into memory manually allocated with
+// `cache.Alloc`.  NB: If Decompress returns (nil, nil), no decompression was
+// necessary and the caller may use `b` directly.
+func Decompress(algo CompressionIndicator, b []byte) (*cache.Value, error) {
+	if algo == NoCompressionIndicator {
+		return nil, nil
+	}
+	// first obtain the decoded length.
+	decodedLen, prefixLen, err := DecompressedLen(algo, b)
+	if err != nil {
+		return nil, err
+	}
+	b = b[prefixLen:]
+	// Allocate sufficient space from the cache.
+	decoded := cache.Alloc(decodedLen)
+	decodedBuf := decoded.Buf()
+	if err := DecompressInto(algo, b, decodedBuf); err != nil {
+		cache.Free(decoded)
+		return nil, err
+	}
+	return decoded, nil
+}
+
+// Compress compresses a sstable block, using dstBuf as the desired destination.
+func Compress(
+	compression Compression, b []byte, dstBuf []byte,
+) (indicator CompressionIndicator, compressed []byte) {
+	switch compression {
+	case SnappyCompression:
+		return SnappyCompressionIndicator, snappy.Encode(dstBuf, b)
+	case NoCompression:
+		return NoCompressionIndicator, b
+	}
+
+	if len(dstBuf) < binary.MaxVarintLen64 {
+		dstBuf = append(dstBuf, make([]byte, binary.MaxVarintLen64-len(dstBuf))...)
+	}
+	varIntLen := binary.PutUvarint(dstBuf, uint64(len(b)))
+	switch compression {
+	case ZstdCompression:
+		return ZstdCompressionIndicator, encodeZstd(dstBuf, varIntLen, b)
+	default:
+		return NoCompressionIndicator, b
+	}
+}
diff --git a/sstable/compression_cgo.go b/sstable/block/compression_cgo.go
similarity index 70%
rename from sstable/compression_cgo.go
rename to sstable/block/compression_cgo.go
index ba9c02c6..e79ef5d7 100644
--- a/sstable/compression_cgo.go
+++ b/sstable/block/compression_cgo.go
@@ -5,7 +5,7 @@
 //go:build cgo
 // +build cgo
 
-package sstable
+package block
 
 import (
 	"bytes"
@@ -13,6 +13,17 @@ import (
 	"github.com/DataDog/zstd"
 )
 
+// UseStandardZstdLib indicates whether the zstd implementation is a port of the
+// official one in the facebook/zstd repository.
+//
+// This constant is only used in tests. Some tests rely on reproducibility of
+// SST files, but a custom implementation of zstd will produce different
+// compression result. So those tests have to be disabled in such cases.
+//
+// We cannot always use the official facebook/zstd implementation since it
+// relies on CGo.
+const UseStandardZstdLib = true
+
 // decodeZstd decompresses src with the Zstandard algorithm. The destination
 // buffer must already be sufficiently sized, otherwise decodeZstd may error.
 func decodeZstd(dst, src []byte) ([]byte, error) {
diff --git a/sstable/compression_nocgo.go b/sstable/block/compression_nocgo.go
similarity index 69%
rename from sstable/compression_nocgo.go
rename to sstable/block/compression_nocgo.go
index cfd79dd2..0c787a05 100644
--- a/sstable/compression_nocgo.go
+++ b/sstable/block/compression_nocgo.go
@@ -5,10 +5,21 @@
 //go:build !cgo
 // +build !cgo
 
-package sstable
+package block
 
 import "github.com/klauspost/compress/zstd"
 
+// UseStandardZstdLib indicates whether the zstd implementation is a port of the
+// official one in the facebook/zstd repository.
+//
+// This constant is only used in tests. Some tests rely on reproducibility of
+// SST files, but a custom implementation of zstd will produce different
+// compression result. So those tests have to be disabled in such cases.
+//
+// We cannot always use the official facebook/zstd implementation since it
+// relies on CGo.
+const UseStandardZstdLib = false
+
 // decodeZstd decompresses src with the Zstandard algorithm. The destination
 // buffer must already be sufficiently sized, otherwise decodeZstd may error.
 func decodeZstd(dst, src []byte) ([]byte, error) {
diff --git a/sstable/compression.go b/sstable/compression.go
deleted file mode 100644
index 70f4c603..00000000
--- a/sstable/compression.go
+++ /dev/null
@@ -1,103 +0,0 @@
-// Copyright 2021 The LevelDB-Go and Pebble 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 sstable
-
-import (
-	"encoding/binary"
-
-	"github.com/cockroachdb/errors"
-	"github.com/cockroachdb/pebble/internal/base"
-	"github.com/cockroachdb/pebble/internal/cache"
-	"github.com/golang/snappy"
-)
-
-func decompressedLen(blockType blockType, b []byte) (int, int, error) {
-	switch blockType {
-	case noCompressionBlockType:
-		return 0, 0, nil
-	case snappyCompressionBlockType:
-		l, err := snappy.DecodedLen(b)
-		return l, 0, err
-	case zstdCompressionBlockType:
-		// This will also be used by zlib, bzip2 and lz4 to retrieve the decodedLen
-		// if we implement these algorithms in the future.
-		decodedLenU64, varIntLen := binary.Uvarint(b)
-		if varIntLen <= 0 {
-			return 0, 0, base.CorruptionErrorf("pebble/table: compression block has invalid length")
-		}
-		return int(decodedLenU64), varIntLen, nil
-	default:
-		return 0, 0, base.CorruptionErrorf("pebble/table: unknown block compression: %d", errors.Safe(blockType))
-	}
-}
-
-// decompressInto decompresses compressed into buf. The buf slice must have the
-// exact size as the decompressed value.
-func decompressInto(blockType blockType, compressed []byte, buf []byte) error {
-	var result []byte
-	var err error
-	switch blockType {
-	case snappyCompressionBlockType:
-		result, err = snappy.Decode(buf, compressed)
-	case zstdCompressionBlockType:
-		result, err = decodeZstd(buf, compressed)
-	default:
-		return base.CorruptionErrorf("pebble/table: unknown block compression: %d", errors.Safe(blockType))
-	}
-	if err != nil {
-		return base.MarkCorruptionError(err)
-	}
-	if len(result) != len(buf) || (len(result) > 0 && &result[0] != &buf[0]) {
-		return base.CorruptionErrorf("pebble/table: decompressed into unexpected buffer: %p != %p",
-			errors.Safe(result), errors.Safe(buf))
-	}
-	return nil
-}
-
-// decompressBlock decompresses an SST block, with manually-allocated space.
-// NB: If decompressBlock returns (nil, nil), no decompression was necessary and
-// the caller may use `b` directly.
-func decompressBlock(blockType blockType, b []byte) (*cache.Value, error) {
-	if blockType == noCompressionBlockType {
-		return nil, nil
-	}
-	// first obtain the decoded length.
-	decodedLen, prefixLen, err := decompressedLen(blockType, b)
-	if err != nil {
-		return nil, err
-	}
-	b = b[prefixLen:]
-	// Allocate sufficient space from the cache.
-	decoded := cache.Alloc(decodedLen)
-	decodedBuf := decoded.Buf()
-	if err := decompressInto(blockType, b, decodedBuf); err != nil {
-		cache.Free(decoded)
-		return nil, err
-	}
-	return decoded, nil
-}
-
-// compressBlock compresses an SST block, using compressBuf as the desired destination.
-func compressBlock(
-	compression Compression, b []byte, compressedBuf []byte,
-) (blockType blockType, compressed []byte) {
-	switch compression {
-	case SnappyCompression:
-		return snappyCompressionBlockType, snappy.Encode(compressedBuf, b)
-	case NoCompression:
-		return noCompressionBlockType, b
-	}
-
-	if len(compressedBuf) < binary.MaxVarintLen64 {
-		compressedBuf = append(compressedBuf, make([]byte, binary.MaxVarintLen64-len(compressedBuf))...)
-	}
-	varIntLen := binary.PutUvarint(compressedBuf, uint64(len(b)))
-	switch compression {
-	case ZstdCompression:
-		return zstdCompressionBlockType, encodeZstd(compressedBuf, varIntLen, b)
-	default:
-		return noCompressionBlockType, b
-	}
-}
diff --git a/sstable/layout.go b/sstable/layout.go
index 905dba50..c3bde01d 100644
--- a/sstable/layout.go
+++ b/sstable/layout.go
@@ -165,9 +165,9 @@ func (l *Layout) Describe(
 			trailer := make([]byte, block.TrailerLen)
 			offset := int64(b.Offset + b.Length)
 			_ = r.readable.ReadAt(ctx, trailer, offset)
-			bt := blockType(trailer[0])
+			algo := block.CompressionIndicator(trailer[0])
 			checksum := binary.LittleEndian.Uint32(trailer[1:])
-			fmt.Fprintf(w, "%10d    [trailer compression=%s checksum=0x%04x]\n", offset, bt, checksum)
+			fmt.Fprintf(w, "%10d    [trailer compression=%s checksum=0x%04x]\n", offset, algo, checksum)
 		}
 
 		var lastKey InternalKey
@@ -298,7 +298,7 @@ type layoutWriter struct {
 
 	// options copied from WriterOptions
 	tableFormat  TableFormat
-	compression  Compression
+	compression  block.Compression
 	checksumType block.ChecksumType
 
 	// offset tracks the current write offset within the writable.
@@ -412,7 +412,7 @@ func (w *layoutWriter) WriteRangeDeletionBlock(b []byte) (block.Handle, error) {
 }
 
 func (w *layoutWriter) writeNamedBlock(b []byte, name string) (bh block.Handle, err error) {
-	bh, err = w.writeBlock(b, NoCompression, &w.buf)
+	bh, err = w.writeBlock(b, block.NoCompression, &w.buf)
 	if err == nil {
 		w.recordToMetaindex(name, bh)
 	}
@@ -459,7 +459,7 @@ func (w *layoutWriter) WriteValueIndexBlock(
 }
 
 func (w *layoutWriter) writeBlock(
-	b []byte, compression Compression, buf *blockBuf,
+	b []byte, compression block.Compression, buf *blockBuf,
 ) (block.Handle, error) {
 	blk, trailer := compressAndChecksum(b, compression, buf)
 	bh := block.Handle{Offset: w.offset, Length: uint64(len(blk))}
@@ -526,7 +526,7 @@ func (w *layoutWriter) Finish() (size uint64, err error) {
 	for _, h := range w.handles {
 		bw.AddRaw(unsafe.Slice(unsafe.StringData(h.key), len(h.key)), h.encodedBlockHandle)
 	}
-	metaIndexHandle, err := w.writeBlock(bw.Finish(), NoCompression, &w.buf)
+	metaIndexHandle, err := w.writeBlock(bw.Finish(), block.NoCompression, &w.buf)
 	if err != nil {
 		return 0, err
 	}
diff --git a/sstable/options.go b/sstable/options.go
index 37c6e19d..724e0cd8 100644
--- a/sstable/options.go
+++ b/sstable/options.go
@@ -15,18 +15,6 @@ import (
 // MaximumBlockSize is the maximum permissible size of a block.
 const MaximumBlockSize = rowblk.MaximumSize
 
-// Compression is the per-block compression algorithm to use.
-type Compression int
-
-// The available compression types.
-const (
-	DefaultCompression Compression = iota
-	NoCompression
-	SnappyCompression
-	ZstdCompression
-	NCompression
-)
-
 var ignoredInternalProperties = map[string]struct{}{
 	"rocksdb.column.family.id":             {},
 	"rocksdb.fixed.key.length":             {},
@@ -38,38 +26,6 @@ var ignoredInternalProperties = map[string]struct{}{
 	"rocksdb.format.version":               {},
 }
 
-func (c Compression) String() string {
-	switch c {
-	case DefaultCompression:
-		return "Default"
-	case NoCompression:
-		return "NoCompression"
-	case SnappyCompression:
-		return "Snappy"
-	case ZstdCompression:
-		return "ZSTD"
-	default:
-		return "Unknown"
-	}
-}
-
-// CompressionFromString returns an sstable.Compression from its
-// string representation. Inverse of c.String() above.
-func CompressionFromString(s string) Compression {
-	switch s {
-	case "Default":
-		return DefaultCompression
-	case "NoCompression":
-		return NoCompression
-	case "Snappy":
-		return SnappyCompression
-	case "ZSTD":
-		return ZstdCompression
-	default:
-		return DefaultCompression
-	}
-}
-
 // FilterType exports the base.FilterType type.
 type FilterType = base.FilterType
 
@@ -199,7 +155,7 @@ type WriterOptions struct {
 	// Compression defines the per-block compression to use.
 	//
 	// The default value (DefaultCompression) uses snappy compression.
-	Compression Compression
+	Compression block.Compression
 
 	// FilterPolicy defines a filter algorithm (such as a Bloom filter) that can
 	// reduce disk reads for Get calls.
@@ -313,8 +269,8 @@ func (o WriterOptions) ensureDefaults() WriterOptions {
 	if o.Comparer == nil {
 		o.Comparer = base.DefaultComparer
 	}
-	if o.Compression <= DefaultCompression || o.Compression >= NCompression {
-		o.Compression = SnappyCompression
+	if o.Compression <= block.DefaultCompression || o.Compression >= block.NCompression {
+		o.Compression = block.SnappyCompression
 	}
 	if o.IndexBlockSize <= 0 {
 		o.IndexBlockSize = o.BlockSize
diff --git a/sstable/raw_writer.go b/sstable/raw_writer.go
index ddb38ed5..a40221bf 100644
--- a/sstable/raw_writer.go
+++ b/sstable/raw_writer.go
@@ -134,7 +134,7 @@ type RawWriter struct {
 	compare              Compare
 	split                Split
 	formatKey            base.FormatKey
-	compression          Compression
+	compression          block.Compression
 	separator            Separator
 	successor            Successor
 	tableFormat          TableFormat
@@ -622,7 +622,7 @@ func (d *dataBlockBuf) finish() {
 	d.uncompressed = d.dataBlock.Finish()
 }
 
-func (d *dataBlockBuf) compressAndChecksum(c Compression) {
+func (d *dataBlockBuf) compressAndChecksum(c block.Compression) {
 	d.compressed, d.trailer = compressAndChecksum(d.uncompressed, c, &d.blockBuf)
 }
 
@@ -1584,24 +1584,24 @@ func (w *RawWriter) writeTwoLevelIndex() (block.Handle, error) {
 }
 
 func compressAndChecksum(
-	b []byte, compression Compression, blockBuf *blockBuf,
+	b []byte, compression block.Compression, blockBuf *blockBuf,
 ) (compressed []byte, trailer block.Trailer) {
 	// Compress the buffer, discarding the result if the improvement isn't at
 	// least 12.5%.
-	blockType, compressed := compressBlock(compression, b, blockBuf.compressedBuf)
-	if blockType != noCompressionBlockType && cap(compressed) > cap(blockBuf.compressedBuf) {
+	algo, compressed := block.Compress(compression, b, blockBuf.compressedBuf)
+	if algo != block.NoCompressionIndicator && cap(compressed) > cap(blockBuf.compressedBuf) {
 		blockBuf.compressedBuf = compressed[:cap(compressed)]
 	}
 	if len(compressed) < len(b)-len(b)/8 {
 		b = compressed
 	} else {
-		blockType = noCompressionBlockType
+		algo = block.NoCompressionIndicator
 	}
 
 	// Calculate the checksum.
-	trailer[0] = byte(blockType)
+	trailer[0] = byte(algo)
 	checksum := blockBuf.checksummer.Checksum(b, trailer[:1])
-	return b, block.MakeTrailer(byte(blockType), checksum)
+	return b, block.MakeTrailer(byte(algo), checksum)
 }
 
 // assertFormatCompatibility ensures that the features present on the table are
diff --git a/sstable/reader.go b/sstable/reader.go
index ce7f17b9..cbec87d4 100644
--- a/sstable/reader.go
+++ b/sstable/reader.go
@@ -461,22 +461,22 @@ func (r *Reader) readBlock(
 		return block.BufferHandle{}, err
 	}
 
-	typ := blockType(compressed.Get()[bh.Length])
+	typ := block.CompressionIndicator(compressed.Get()[bh.Length])
 	compressed.Truncate(int(bh.Length))
 
 	var decompressed block.Value
-	if typ == noCompressionBlockType {
+	if typ == block.NoCompressionIndicator {
 		decompressed = compressed
 	} else {
 		// Decode the length of the decompressed value.
-		decodedLen, prefixLen, err := decompressedLen(typ, compressed.Get())
+		decodedLen, prefixLen, err := block.DecompressedLen(typ, compressed.Get())
 		if err != nil {
 			compressed.Release()
 			return block.BufferHandle{}, err
 		}
 
 		decompressed = block.Alloc(decodedLen, bufferPool)
-		if err := decompressInto(typ, compressed.Get()[prefixLen:], decompressed.Get()); err != nil {
+		if err := block.DecompressInto(typ, compressed.Get()[prefixLen:], decompressed.Get()); err != nil {
 			compressed.Release()
 			return block.BufferHandle{}, err
 		}
diff --git a/sstable/suffix_rewriter.go b/sstable/suffix_rewriter.go
index 99269106..289eb7ab 100644
--- a/sstable/suffix_rewriter.go
+++ b/sstable/suffix_rewriter.go
@@ -154,7 +154,7 @@ func rewriteBlocks(
 	r *Reader,
 	restartInterval int,
 	checksumType block.ChecksumType,
-	compression Compression,
+	compression block.Compression,
 	input []BlockHandleWithProperties,
 	output []blockWithSpan,
 	totalWorkers, worker int,
@@ -507,12 +507,12 @@ func readBlockBuf(r *Reader, bh block.Handle, buf []byte) ([]byte, []byte, error
 	if err := checkChecksum(r.checksumType, raw, bh, 0); err != nil {
 		return nil, buf, err
 	}
-	typ := blockType(raw[bh.Length])
+	algo := block.CompressionIndicator(raw[bh.Length])
 	raw = raw[:bh.Length]
-	if typ == noCompressionBlockType {
+	if algo == block.NoCompressionIndicator {
 		return raw, buf, nil
 	}
-	decompressedLen, prefix, err := decompressedLen(typ, raw)
+	decompressedLen, prefix, err := block.DecompressedLen(algo, raw)
 	if err != nil {
 		return nil, buf, err
 	}
@@ -520,7 +520,7 @@ func readBlockBuf(r *Reader, bh block.Handle, buf []byte) ([]byte, []byte, error
 		buf = make([]byte, decompressedLen)
 	}
 	dst := buf[:decompressedLen]
-	err = decompressInto(typ, raw[prefix:], dst)
+	err = block.DecompressInto(algo, raw[prefix:], dst)
 	return dst, buf, err
 }
 
diff --git a/sstable/table.go b/sstable/table.go
index 162fc229..917b92ef 100644
--- a/sstable/table.go
+++ b/sstable/table.go
@@ -232,49 +232,6 @@ const (
 	rocksDBCompressionOptions = "window_bits=-14; level=32767; strategy=0; max_dict_bytes=0; zstd_max_train_bytes=0; enabled=0; "
 )
 
-type blockType byte
-
-const (
-	// The block type gives the per-block compression format.
-	// These constants are part of the file format and should not be changed.
-	// They are different from the Compression constants because the latter
-	// are designed so that the zero value of the Compression type means to
-	// use the default compression (which is snappy).
-	// Not all compression types listed here are supported.
-	noCompressionBlockType     blockType = 0
-	snappyCompressionBlockType blockType = 1
-	zlibCompressionBlockType   blockType = 2
-	bzip2CompressionBlockType  blockType = 3
-	lz4CompressionBlockType    blockType = 4
-	lz4hcCompressionBlockType  blockType = 5
-	xpressCompressionBlockType blockType = 6
-	zstdCompressionBlockType   blockType = 7
-)
-
-// String implements fmt.Stringer.
-func (t blockType) String() string {
-	switch t {
-	case 0:
-		return "none"
-	case 1:
-		return "snappy"
-	case 2:
-		return "zlib"
-	case 3:
-		return "bzip2"
-	case 4:
-		return "lz4"
-	case 5:
-		return "lz4hc"
-	case 6:
-		return "xpress"
-	case 7:
-		return "zstd"
-	default:
-		panic(errors.Newf("sstable: unknown block type: %d", t))
-	}
-}
-
 // legacy (LevelDB) footer format:
 //
 //	metaindex handle (varint64 offset, varint64 size)
diff --git a/sstable/value_block.go b/sstable/value_block.go
index 308a7ce5..0b3f0796 100644
--- a/sstable/value_block.go
+++ b/sstable/value_block.go
@@ -335,7 +335,7 @@ type valueBlockWriter struct {
 	// The configured uncompressed block size and size threshold
 	blockSize, blockSizeThreshold int
 	// Configured compression.
-	compression Compression
+	compression block.Compression
 	// checksummer with configured checksum type.
 	checksummer block.Checksummer
 	// Block finished callback.
@@ -405,7 +405,7 @@ var valueBlockWriterPool = sync.Pool{
 func newValueBlockWriter(
 	blockSize int,
 	blockSizeThreshold int,
-	compression Compression,
+	compression block.Compression,
 	checksumType block.ChecksumType,
 	// compressedSize should exclude the block trailer.
 	blockFinishedFunc func(compressedSize int),
@@ -497,15 +497,15 @@ func (w *valueBlockWriter) addValue(v []byte) (valueHandle, error) {
 func (w *valueBlockWriter) compressAndFlush() {
 	// Compress the buffer, discarding the result if the improvement isn't at
 	// least 12.5%.
-	blockType := noCompressionBlockType
+	algo := block.NoCompressionIndicator
 	b := w.buf
-	if w.compression != NoCompression {
-		blockType, w.compressedBuf.b =
-			compressBlock(w.compression, w.buf.b, w.compressedBuf.b[:cap(w.compressedBuf.b)])
+	if w.compression != block.NoCompression {
+		algo, w.compressedBuf.b =
+			block.Compress(w.compression, w.buf.b, w.compressedBuf.b[:cap(w.compressedBuf.b)])
 		if len(w.compressedBuf.b) < len(w.buf.b)-len(w.buf.b)/8 {
 			b = w.compressedBuf
 		} else {
-			blockType = noCompressionBlockType
+			algo = block.NoCompressionIndicator
 		}
 	}
 	n := len(b.b)
@@ -516,13 +516,13 @@ func (w *valueBlockWriter) compressAndFlush() {
 	} else {
 		b.b = b.b[:n+block.TrailerLen]
 	}
-	b.b[n] = byte(blockType)
+	b.b[n] = byte(algo)
 	w.computeChecksum(b.b)
 	bh := block.Handle{Offset: w.totalBlockBytes, Length: uint64(n)}
 	w.totalBlockBytes += uint64(len(b.b))
 	// blockFinishedFunc length excludes the block trailer.
 	w.blockFinishedFunc(n)
-	compressed := blockType != noCompressionBlockType
+	compressed := algo != block.NoCompressionIndicator
 	w.blocks = append(w.blocks, blockAndHandle{
 		block:      b,
 		handle:     bh,
@@ -616,7 +616,7 @@ func (w *valueBlockWriter) writeValueBlocksIndex(
 	if len(b) != block.TrailerLen {
 		panic("incorrect length calculation")
 	}
-	b[0] = byte(noCompressionBlockType)
+	b[0] = byte(block.NoCompressionIndicator)
 	w.computeChecksum(buf)
 	if _, err := layout.WriteValueIndexBlock(buf, h); err != nil {
 		return valueBlocksIndexHandle{}, err
diff --git a/table_stats.go b/table_stats.go
index 5eaf5d31..16b0cdeb 100644
--- a/table_stats.go
+++ b/table_stats.go
@@ -16,6 +16,7 @@ import (
 	"github.com/cockroachdb/pebble/internal/keyspan/keyspanimpl"
 	"github.com/cockroachdb/pebble/internal/manifest"
 	"github.com/cockroachdb/pebble/sstable"
+	"github.com/cockroachdb/pebble/sstable/block"
 )
 
 // In-memory statistics about tables help inform compaction picking, but may
@@ -324,7 +325,7 @@ func (d *DB) loadTableStats(
 			// picking.
 			stats.NumRangeKeySets = props.NumRangeKeySets
 			stats.ValueBlocksSize = props.ValueBlocksSize
-			stats.CompressionType = sstable.CompressionFromString(props.CompressionName)
+			stats.CompressionType = block.CompressionFromString(props.CompressionName)
 			return
 		})
 	if err != nil {
@@ -679,7 +680,7 @@ func maybeSetStatsFromProperties(meta physicalMeta, props *sstable.Properties) b
 	meta.Stats.PointDeletionsBytesEstimate = pointEstimate
 	meta.Stats.RangeDeletionsBytesEstimate = 0
 	meta.Stats.ValueBlocksSize = props.ValueBlocksSize
-	meta.Stats.CompressionType = sstable.CompressionFromString(props.CompressionName)
+	meta.Stats.CompressionType = block.CompressionFromString(props.CompressionName)
 	meta.StatsMarkValid()
 	return true
 }
@@ -1042,11 +1043,11 @@ func (a compressionTypeAggregator) Accumulate(
 	f *fileMetadata, dst *compressionTypes,
 ) (v *compressionTypes, cacheOK bool) {
 	switch f.Stats.CompressionType {
-	case sstable.SnappyCompression:
+	case SnappyCompression:
 		dst.snappy++
-	case sstable.ZstdCompression:
+	case ZstdCompression:
 		dst.zstd++
-	case sstable.NoCompression:
+	case NoCompression:
 		dst.none++
 	default:
 		dst.unknown++


Test patch:
diff --git a/ingest_test.go b/ingest_test.go
index d544f71b..3b68eb59 100644
--- a/ingest_test.go
+++ b/ingest_test.go
@@ -36,6 +36,7 @@ import (
 	"github.com/cockroachdb/pebble/objstorage/remote"
 	"github.com/cockroachdb/pebble/record"
 	"github.com/cockroachdb/pebble/sstable"
+	"github.com/cockroachdb/pebble/sstable/block"
 	"github.com/cockroachdb/pebble/vfs"
 	"github.com/cockroachdb/pebble/vfs/errorfs"
 	"github.com/kr/pretty"
@@ -172,7 +173,7 @@ func TestIngestLoadRand(t *testing.T) {
 			},
 			path: paths[i],
 		}
-		expected[i].fileMetadata.Stats.CompressionType = sstable.SnappyCompression
+		expected[i].fileMetadata.Stats.CompressionType = block.SnappyCompression
 		expected[i].StatsMarkValid()
 
 		func() {
diff --git a/sstable/compression_test.go b/sstable/block/compression_test.go
similarity index 86%
rename from sstable/compression_test.go
rename to sstable/block/compression_test.go
index 4ee542f0..aa8029a1 100644
--- a/sstable/compression_test.go
+++ b/sstable/block/compression_test.go
@@ -2,7 +2,7 @@
 // of this source code is governed by a BSD-style license that can be found in
 // the LICENSE file.
 
-package sstable
+package block
 
 import (
 	"encoding/binary"
@@ -24,11 +24,11 @@ func TestCompressionRoundtrip(t *testing.T) {
 			payload := make([]byte, rng.Intn(10<<10 /* 10 KiB */))
 			rng.Read(payload)
 			// Create a randomly-sized buffer to house the compressed output. If it's
-			// not sufficient, compressBlock should allocate one that is.
+			// not sufficient, Compress should allocate one that is.
 			compressedBuf := make([]byte, rng.Intn(1<<10 /* 1 KiB */))
 
-			btyp, compressed := compressBlock(compression, payload, compressedBuf)
-			v, err := decompressBlock(btyp, compressed)
+			btyp, compressed := Compress(compression, payload, compressedBuf)
+			v, err := Decompress(btyp, compressed)
 			require.NoError(t, err)
 			got := payload
 			if v != nil {
@@ -53,7 +53,7 @@ func TestDecompressionError(t *testing.T) {
 	fauxCompressed = fauxCompressed[:n+compressedPayloadLen]
 	rng.Read(fauxCompressed[n:])
 
-	v, err := decompressBlock(zstdCompressionBlockType, fauxCompressed)
+	v, err := Decompress(ZstdCompressionIndicator, fauxCompressed)
 	t.Log(err)
 	require.Error(t, err)
 	require.Nil(t, v)
diff --git a/sstable/compression_cgo_test.go b/sstable/compression_cgo_test.go
deleted file mode 100644
index 1de7395b..00000000
--- a/sstable/compression_cgo_test.go
+++ /dev/null
@@ -1,19 +0,0 @@
-// Copyright 2021 The LevelDB-Go and Pebble Authors. All rights reserved. Use
-// of this source code is governed by a BSD-style license that can be found in
-// the LICENSE file.
-
-//go:build cgo
-// +build cgo
-
-package sstable
-
-// useStandardZstdLib indicates whether the zstd implementation is a port of the
-// official one in the facebook/zstd repository.
-//
-// This constant is only used in tests. Some tests rely on reproducibility of
-// SST files, but a custom implementation of zstd will produce different
-// compression result. So those tests have to be disabled in such cases.
-//
-// We cannot always use the official facebook/zstd implementation since it
-// relies on CGo.
-const useStandardZstdLib = true
diff --git a/sstable/compression_nocgo_test.go b/sstable/compression_nocgo_test.go
deleted file mode 100644
index 1c755a23..00000000
--- a/sstable/compression_nocgo_test.go
+++ /dev/null
@@ -1,19 +0,0 @@
-// Copyright 2021 The LevelDB-Go and Pebble Authors. All rights reserved. Use
-// of this source code is governed by a BSD-style license that can be found in
-// the LICENSE file.
-
-//go:build !cgo
-// +build !cgo
-
-package sstable
-
-// useStandardZstdLib indicates whether the zstd implementation is a port of the
-// official one in the facebook/zstd repository.
-//
-// This constant is only used in tests. Some tests rely on reproducibility of
-// SST files, but a custom implementation of zstd will produce different
-// compression result. So those tests have to be disabled in such cases.
-//
-// We cannot always use the official facebook/zstd implementation since it
-// relies on CGo.
-const useStandardZstdLib = false
diff --git a/sstable/reader_test.go b/sstable/reader_test.go
index aba5ecdf..898331d9 100644
--- a/sstable/reader_test.go
+++ b/sstable/reader_test.go
@@ -973,7 +973,7 @@ func TestCompactionIteratorSetupForCompaction(t *testing.T) {
 	for _, blockSize := range blockSizes {
 		for _, indexBlockSize := range blockSizes {
 			for _, numEntries := range []uint64{0, 1, 1e5} {
-				r := buildTestTableWithProvider(t, provider, numEntries, blockSize, indexBlockSize, DefaultCompression, nil)
+				r := buildTestTableWithProvider(t, provider, numEntries, blockSize, indexBlockSize, block.DefaultCompression, nil)
 				var pool block.BufferPool
 				pool.Init(5)
 				citer, err := r.NewCompactionIter(
@@ -1783,7 +1783,7 @@ func buildTestTableWithProvider(
 	provider objstorage.Provider,
 	numEntries uint64,
 	blockSize, indexBlockSize int,
-	compression Compression,
+	compression block.Compression,
 	prefix []byte,
 ) *Reader {
 	f0, _, err := provider.Create(context.Background(), base.FileTypeTable, base.DiskFileNum(0), objstorage.CreateOptions{})
@@ -1883,7 +1883,7 @@ var basicBenchmarks = []struct {
 			BlockSize:            32 << 10,
 			BlockRestartInterval: 16,
 			FilterPolicy:         nil,
-			Compression:          SnappyCompression,
+			Compression:          block.SnappyCompression,
 			TableFormat:          TableFormatPebblev2,
 		},
 	},
@@ -1893,7 +1893,7 @@ var basicBenchmarks = []struct {
 			BlockSize:            32 << 10,
 			BlockRestartInterval: 16,
 			FilterPolicy:         nil,
-			Compression:          ZstdCompression,
+			Compression:          block.ZstdCompression,
 			TableFormat:          TableFormatPebblev2,
 		},
 	},
@@ -2096,7 +2096,7 @@ func BenchmarkIteratorScanManyVersions(b *testing.B) {
 		BlockSize:            32 << 10,
 		BlockRestartInterval: 16,
 		FilterPolicy:         nil,
-		Compression:          SnappyCompression,
+		Compression:          block.SnappyCompression,
 		Comparer:             testkeys.Comparer,
 	}
 	// 10,000 key prefixes, each with 100 versions.
@@ -2201,7 +2201,7 @@ func BenchmarkIteratorScanNextPrefix(b *testing.B) {
 		BlockSize:            32 << 10,
 		BlockRestartInterval: 16,
 		FilterPolicy:         nil,
-		Compression:          SnappyCompression,
+		Compression:          block.SnappyCompression,
 		TableFormat:          TableFormatPebblev3,
 		Comparer:             testkeys.Comparer,
 	}
@@ -2370,7 +2370,7 @@ func BenchmarkIteratorScanObsolete(b *testing.B) {
 		BlockSize:            32 << 10,
 		BlockRestartInterval: 16,
 		FilterPolicy:         nil,
-		Compression:          SnappyCompression,
+		Compression:          block.SnappyCompression,
 		Comparer:             testkeys.Comparer,
 	}
 	const keyCount = 1 << 20
diff --git a/sstable/suffix_rewriter_test.go b/sstable/suffix_rewriter_test.go
index c1f5582a..a31f791e 100644
--- a/sstable/suffix_rewriter_test.go
+++ b/sstable/suffix_rewriter_test.go
@@ -10,6 +10,7 @@ import (
 	"github.com/cockroachdb/pebble/bloom"
 	"github.com/cockroachdb/pebble/internal/base"
 	"github.com/cockroachdb/pebble/objstorage"
+	"github.com/cockroachdb/pebble/sstable/block"
 	"github.com/stretchr/testify/require"
 )
 
@@ -187,7 +188,7 @@ func BenchmarkRewriteSST(b *testing.B) {
 	}
 
 	sizes := []int{100, 10000, 1e6}
-	compressions := []Compression{NoCompression, SnappyCompression}
+	compressions := []block.Compression{block.NoCompression, block.SnappyCompression}
 
 	files := make([][]*Reader, len(compressions))
 
diff --git a/sstable/table_test.go b/sstable/table_test.go
index 74f7b795..59641cad 100644
--- a/sstable/table_test.go
+++ b/sstable/table_test.go
@@ -419,7 +419,7 @@ func TestWriterRoundTrip(t *testing.T) {
 				t.Run(fmt.Sprintf("bloom=%s", name), func(t *testing.T) {
 					fs := vfs.NewMem()
 					err := buildHamletTestSST(
-						fs, "test.sst", DefaultCompression, fp, TableFilter,
+						fs, "test.sst", block.DefaultCompression, fp, TableFilter,
 						nil /* comparer */, blockSize, indexBlockSize,
 					)
 					require.NoError(t, err)
@@ -520,7 +520,7 @@ func TestReaderSymtheticSeqNum(t *testing.T) {
 
 func TestMetaIndexEntriesSorted(t *testing.T) {
 	fs := vfs.NewMem()
-	err := buildHamletTestSST(fs, "test.sst", DefaultCompression, nil, /* filter policy */
+	err := buildHamletTestSST(fs, "test.sst", block.DefaultCompression, nil, /* filter policy */
 		TableFilter, nil, 4096, 4096)
 	require.NoError(t, err)
 	f, err := fs.Open("test.sst")
diff --git a/sstable/test_fixtures.go b/sstable/test_fixtures.go
index 1dcafdb0..db8c5326 100644
--- a/sstable/test_fixtures.go
+++ b/sstable/test_fixtures.go
@@ -18,6 +18,7 @@ import (
 	"github.com/cockroachdb/pebble/bloom"
 	"github.com/cockroachdb/pebble/internal/base"
 	"github.com/cockroachdb/pebble/objstorage/objstorageprovider"
+	"github.com/cockroachdb/pebble/sstable/block"
 	"github.com/cockroachdb/pebble/vfs"
 )
 
@@ -119,7 +120,7 @@ var hamletNonsenseWords = []string{
 func buildHamletTestSST(
 	fs vfs.FS,
 	filename string,
-	compression Compression,
+	compression block.Compression,
 	fp FilterPolicy,
 	ftype FilterType,
 	comparer *Comparer,
@@ -177,7 +178,7 @@ func buildHamletTestSST(
 // TestFixtureInfo contains all metadata necessary to generate a test sstable.
 type TestFixtureInfo struct {
 	Filename           string
-	Compression        Compression
+	Compression        block.Compression
 	FullKeyFilter      bool
 	PrefixFilter       bool
 	IndexBlockSize     int
@@ -188,7 +189,7 @@ type TestFixtureInfo struct {
 var TestFixtures = []TestFixtureInfo{
 	{
 		Filename:           "h.sst",
-		Compression:        SnappyCompression,
+		Compression:        block.SnappyCompression,
 		FullKeyFilter:      false,
 		PrefixFilter:       false,
 		IndexBlockSize:     fixtureDefaultIndexBlockSize,
@@ -196,7 +197,7 @@ var TestFixtures = []TestFixtureInfo{
 	},
 	{
 		Filename:           "h.no-compression.sst",
-		Compression:        NoCompression,
+		Compression:        block.NoCompression,
 		FullKeyFilter:      false,
 		PrefixFilter:       false,
 		IndexBlockSize:     fixtureDefaultIndexBlockSize,
@@ -204,7 +205,7 @@ var TestFixtures = []TestFixtureInfo{
 	},
 	{
 		Filename:           "h.table-bloom.sst",
-		Compression:        SnappyCompression,
+		Compression:        block.SnappyCompression,
 		FullKeyFilter:      true,
 		PrefixFilter:       false,
 		IndexBlockSize:     fixtureDefaultIndexBlockSize,
@@ -212,7 +213,7 @@ var TestFixtures = []TestFixtureInfo{
 	},
 	{
 		Filename:           "h.table-bloom.no-compression.sst",
-		Compression:        NoCompression,
+		Compression:        block.NoCompression,
 		FullKeyFilter:      true,
 		PrefixFilter:       false,
 		IndexBlockSize:     fixtureDefaultIndexBlockSize,
@@ -220,7 +221,7 @@ var TestFixtures = []TestFixtureInfo{
 	},
 	{
 		Filename:           "h.table-bloom.no-compression.prefix_extractor.no_whole_key_filter.sst",
-		Compression:        NoCompression,
+		Compression:        block.NoCompression,
 		FullKeyFilter:      false,
 		PrefixFilter:       true,
 		IndexBlockSize:     fixtureDefaultIndexBlockSize,
@@ -228,7 +229,7 @@ var TestFixtures = []TestFixtureInfo{
 	},
 	{
 		Filename:           "h.no-compression.two_level_index.sst",
-		Compression:        NoCompression,
+		Compression:        block.NoCompression,
 		FullKeyFilter:      false,
 		PrefixFilter:       false,
 		IndexBlockSize:     fixtureSmallIndexBlockSize,
@@ -236,7 +237,7 @@ var TestFixtures = []TestFixtureInfo{
 	},
 	{
 		Filename:           "h.zstd-compression.sst",
-		Compression:        ZstdCompression,
+		Compression:        block.ZstdCompression,
 		FullKeyFilter:      false,
 		PrefixFilter:       false,
 		IndexBlockSize:     fixtureDefaultIndexBlockSize,
diff --git a/sstable/writer_fixture_test.go b/sstable/writer_fixture_test.go
index 9d5bdf66..b5613ae3 100644
--- a/sstable/writer_fixture_test.go
+++ b/sstable/writer_fixture_test.go
@@ -21,6 +21,7 @@ import (
 	"testing"
 
 	"github.com/cockroachdb/errors"
+	"github.com/cockroachdb/pebble/sstable/block"
 	"github.com/cockroachdb/pebble/vfs"
 	"github.com/stretchr/testify/require"
 )
@@ -79,7 +80,7 @@ func TestFixtureOutput(t *testing.T) {
 		// <https://github.com/klauspost/compress/issues/109#issuecomment-498763233>.
 		// Since the fixture test requires bit-to-bit reproducibility, we cannot
 		// run the zstd test when the implementation is not based on facebook/zstd.
-		if !useStandardZstdLib && fixture.Compression == ZstdCompression {
+		if !block.UseStandardZstdLib && fixture.Compression == block.ZstdCompression {
 			continue
 		}
 		t.Run(fixture.Filename, func(t *testing.T) {
diff --git a/sstable/writer_test.go b/sstable/writer_test.go
index e7216488..9fcfe23e 100644
--- a/sstable/writer_test.go
+++ b/sstable/writer_test.go
@@ -963,7 +963,7 @@ func TestWriterRace(t *testing.T) {
 			opts := WriterOptions{
 				Comparer:    testkeys.Comparer,
 				BlockSize:   rand.Intn(1 << 10),
-				Compression: NoCompression,
+				Compression: block.NoCompression,
 			}
 			defer wg.Done()
 			f := &objstorage.MemObj{}
@@ -1114,7 +1114,7 @@ func runWriterBench(b *testing.B, keys [][]byte, comparer *base.Comparer, format
 		b.Run(fmt.Sprintf("block=%s", humanize.Bytes.Int64(int64(bs))), func(b *testing.B) {
 			for _, filter := range []bool{true, false} {
 				b.Run(fmt.Sprintf("filter=%t", filter), func(b *testing.B) {
-					for _, comp := range []Compression{NoCompression, SnappyCompression, ZstdCompression} {
+					for _, comp := range []block.Compression{block.NoCompression, block.SnappyCompression, block.ZstdCompression} {
 						b.Run(fmt.Sprintf("compression=%s", comp), func(b *testing.B) {
 							opts := WriterOptions{
 								BlockRestartInterval: 16,