github.com/cockroachdb/pebble@v1.1.2/sstable/compression.go (about)

     1  // Copyright 2021 The LevelDB-Go and Pebble Authors. All rights reserved. Use
     2  // of this source code is governed by a BSD-style license that can be found in
     3  // the LICENSE file.
     4  
     5  package sstable
     6  
     7  import (
     8  	"encoding/binary"
     9  
    10  	"github.com/cockroachdb/errors"
    11  	"github.com/cockroachdb/pebble/internal/base"
    12  	"github.com/cockroachdb/pebble/internal/cache"
    13  	"github.com/golang/snappy"
    14  )
    15  
    16  func decompressedLen(blockType blockType, b []byte) (int, int, error) {
    17  	switch blockType {
    18  	case noCompressionBlockType:
    19  		return 0, 0, nil
    20  	case snappyCompressionBlockType:
    21  		l, err := snappy.DecodedLen(b)
    22  		return l, 0, err
    23  	case zstdCompressionBlockType:
    24  		// This will also be used by zlib, bzip2 and lz4 to retrieve the decodedLen
    25  		// if we implement these algorithms in the future.
    26  		decodedLenU64, varIntLen := binary.Uvarint(b)
    27  		if varIntLen <= 0 {
    28  			return 0, 0, base.CorruptionErrorf("pebble/table: compression block has invalid length")
    29  		}
    30  		return int(decodedLenU64), varIntLen, nil
    31  	default:
    32  		return 0, 0, base.CorruptionErrorf("pebble/table: unknown block compression: %d", errors.Safe(blockType))
    33  	}
    34  }
    35  
    36  func decompressInto(blockType blockType, compressed []byte, buf []byte) ([]byte, error) {
    37  	var result []byte
    38  	var err error
    39  	switch blockType {
    40  	case snappyCompressionBlockType:
    41  		result, err = snappy.Decode(buf, compressed)
    42  	case zstdCompressionBlockType:
    43  		result, err = decodeZstd(buf, compressed)
    44  	}
    45  	if err != nil {
    46  		return nil, base.MarkCorruptionError(err)
    47  	}
    48  	if len(result) != 0 && (len(result) != len(buf) || &result[0] != &buf[0]) {
    49  		return nil, base.CorruptionErrorf("pebble/table: decompressed into unexpected buffer: %p != %p",
    50  			errors.Safe(result), errors.Safe(buf))
    51  	}
    52  	return result, nil
    53  }
    54  
    55  // decompressBlock decompresses an SST block, with manually-allocated space.
    56  // NB: If decompressBlock returns (nil, nil), no decompression was necessary and
    57  // the caller may use `b` directly.
    58  func decompressBlock(blockType blockType, b []byte) (*cache.Value, error) {
    59  	if blockType == noCompressionBlockType {
    60  		return nil, nil
    61  	}
    62  	// first obtain the decoded length.
    63  	decodedLen, prefixLen, err := decompressedLen(blockType, b)
    64  	if err != nil {
    65  		return nil, err
    66  	}
    67  	b = b[prefixLen:]
    68  	// Allocate sufficient space from the cache.
    69  	decoded := cache.Alloc(decodedLen)
    70  	decodedBuf := decoded.Buf()
    71  	if _, err := decompressInto(blockType, b, decodedBuf); err != nil {
    72  		cache.Free(decoded)
    73  		return nil, err
    74  	}
    75  	return decoded, nil
    76  }
    77  
    78  // compressBlock compresses an SST block, using compressBuf as the desired destination.
    79  func compressBlock(
    80  	compression Compression, b []byte, compressedBuf []byte,
    81  ) (blockType blockType, compressed []byte) {
    82  	switch compression {
    83  	case SnappyCompression:
    84  		return snappyCompressionBlockType, snappy.Encode(compressedBuf, b)
    85  	case NoCompression:
    86  		return noCompressionBlockType, b
    87  	}
    88  
    89  	if len(compressedBuf) < binary.MaxVarintLen64 {
    90  		compressedBuf = append(compressedBuf, make([]byte, binary.MaxVarintLen64-len(compressedBuf))...)
    91  	}
    92  	varIntLen := binary.PutUvarint(compressedBuf, uint64(len(b)))
    93  	switch compression {
    94  	case ZstdCompression:
    95  		return zstdCompressionBlockType, encodeZstd(compressedBuf, varIntLen, b)
    96  	default:
    97  		return noCompressionBlockType, b
    98  	}
    99  }