LCOV - code coverage report
Current view: top level - pebble/sstable - layout.go (source / functions) Hit Total Coverage
Test: 2024-08-28 08:16Z 34e929e1 - tests only.lcov Lines: 311 342 90.9 %
Date: 2024-08-28 08:16:42 Functions: 0 0 -

          Line data    Source code
       1             : // Copyright 2011 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             :         "bytes"
       9             :         "cmp"
      10             :         "context"
      11             :         "encoding/binary"
      12             :         "fmt"
      13             :         "io"
      14             :         "slices"
      15             :         "unsafe"
      16             : 
      17             :         "github.com/cockroachdb/pebble/internal/base"
      18             :         "github.com/cockroachdb/pebble/internal/bytealloc"
      19             :         "github.com/cockroachdb/pebble/internal/sstableinternal"
      20             :         "github.com/cockroachdb/pebble/objstorage"
      21             :         "github.com/cockroachdb/pebble/sstable/block"
      22             :         "github.com/cockroachdb/pebble/sstable/rowblk"
      23             : )
      24             : 
      25             : // Layout describes the block organization of an sstable.
      26             : type Layout struct {
      27             :         // NOTE: changes to fields in this struct should also be reflected in
      28             :         // ValidateBlockChecksums, which validates a static list of BlockHandles
      29             :         // referenced in this struct.
      30             : 
      31             :         Data       []block.HandleWithProperties
      32             :         Index      []block.Handle
      33             :         TopIndex   block.Handle
      34             :         Filter     block.Handle
      35             :         RangeDel   block.Handle
      36             :         RangeKey   block.Handle
      37             :         ValueBlock []block.Handle
      38             :         ValueIndex block.Handle
      39             :         Properties block.Handle
      40             :         MetaIndex  block.Handle
      41             :         Footer     block.Handle
      42             :         Format     TableFormat
      43             : }
      44             : 
      45             : // Describe returns a description of the layout. If the verbose parameter is
      46             : // true, details of the structure of each block are returned as well.
      47             : func (l *Layout) Describe(
      48             :         w io.Writer, verbose bool, r *Reader, fmtRecord func(key *base.InternalKey, value []byte),
      49           1 : ) {
      50           1 :         ctx := context.TODO()
      51           1 :         type namedBlockHandle struct {
      52           1 :                 block.Handle
      53           1 :                 name string
      54           1 :         }
      55           1 :         var blocks []namedBlockHandle
      56           1 : 
      57           1 :         for i := range l.Data {
      58           1 :                 blocks = append(blocks, namedBlockHandle{l.Data[i].Handle, "data"})
      59           1 :         }
      60           1 :         for i := range l.Index {
      61           1 :                 blocks = append(blocks, namedBlockHandle{l.Index[i], "index"})
      62           1 :         }
      63           1 :         if l.TopIndex.Length != 0 {
      64           1 :                 blocks = append(blocks, namedBlockHandle{l.TopIndex, "top-index"})
      65           1 :         }
      66           1 :         if l.Filter.Length != 0 {
      67           1 :                 blocks = append(blocks, namedBlockHandle{l.Filter, "filter"})
      68           1 :         }
      69           1 :         if l.RangeDel.Length != 0 {
      70           1 :                 blocks = append(blocks, namedBlockHandle{l.RangeDel, "range-del"})
      71           1 :         }
      72           1 :         if l.RangeKey.Length != 0 {
      73           1 :                 blocks = append(blocks, namedBlockHandle{l.RangeKey, "range-key"})
      74           1 :         }
      75           1 :         for i := range l.ValueBlock {
      76           1 :                 blocks = append(blocks, namedBlockHandle{l.ValueBlock[i], "value-block"})
      77           1 :         }
      78           1 :         if l.ValueIndex.Length != 0 {
      79           1 :                 blocks = append(blocks, namedBlockHandle{l.ValueIndex, "value-index"})
      80           1 :         }
      81           1 :         if l.Properties.Length != 0 {
      82           1 :                 blocks = append(blocks, namedBlockHandle{l.Properties, "properties"})
      83           1 :         }
      84           1 :         if l.MetaIndex.Length != 0 {
      85           1 :                 blocks = append(blocks, namedBlockHandle{l.MetaIndex, "meta-index"})
      86           1 :         }
      87           1 :         if l.Footer.Length != 0 {
      88           1 :                 if l.Footer.Length == levelDBFooterLen {
      89           1 :                         blocks = append(blocks, namedBlockHandle{l.Footer, "leveldb-footer"})
      90           1 :                 } else {
      91           1 :                         blocks = append(blocks, namedBlockHandle{l.Footer, "footer"})
      92           1 :                 }
      93             :         }
      94             : 
      95           1 :         slices.SortFunc(blocks, func(a, b namedBlockHandle) int {
      96           1 :                 return cmp.Compare(a.Offset, b.Offset)
      97           1 :         })
      98             :         // TODO(jackson): This function formats offsets within blocks by adding the
      99             :         // block's offset. A block's offset is an offset in the physical, compressed
     100             :         // file whereas KV pairs offsets are within the uncompressed block. This is
     101             :         // confusing and can result in blocks' KVs offsets overlapping one another.
     102             :         // We should just print offsets relative to the block start.
     103             : 
     104           1 :         for i := range blocks {
     105           1 :                 b := &blocks[i]
     106           1 :                 fmt.Fprintf(w, "%10d  %s (%d)\n", b.Offset, b.name, b.Length)
     107           1 : 
     108           1 :                 if !verbose {
     109           1 :                         continue
     110             :                 }
     111           1 :                 if b.name == "filter" {
     112           0 :                         continue
     113             :                 }
     114             : 
     115           1 :                 if b.name == "footer" || b.name == "leveldb-footer" {
     116           1 :                         trailer, offset := make([]byte, b.Length), b.Offset
     117           1 :                         _ = r.readable.ReadAt(ctx, trailer, int64(offset))
     118           1 : 
     119           1 :                         if b.name == "footer" {
     120           1 :                                 checksumType := block.ChecksumType(trailer[0])
     121           1 :                                 fmt.Fprintf(w, "%10d    checksum type: %s\n", offset, checksumType)
     122           1 :                                 trailer, offset = trailer[1:], offset+1
     123           1 :                         }
     124             : 
     125           1 :                         metaHandle, n := binary.Uvarint(trailer)
     126           1 :                         metaLen, m := binary.Uvarint(trailer[n:])
     127           1 :                         fmt.Fprintf(w, "%10d    meta: offset=%d, length=%d\n", offset, metaHandle, metaLen)
     128           1 :                         trailer, offset = trailer[n+m:], offset+uint64(n+m)
     129           1 : 
     130           1 :                         indexHandle, n := binary.Uvarint(trailer)
     131           1 :                         indexLen, m := binary.Uvarint(trailer[n:])
     132           1 :                         fmt.Fprintf(w, "%10d    index: offset=%d, length=%d\n", offset, indexHandle, indexLen)
     133           1 :                         trailer, offset = trailer[n+m:], offset+uint64(n+m)
     134           1 : 
     135           1 :                         fmt.Fprintf(w, "%10d    [padding]\n", offset)
     136           1 : 
     137           1 :                         trailing := 12
     138           1 :                         if b.name == "leveldb-footer" {
     139           0 :                                 trailing = 8
     140           0 :                         }
     141             : 
     142           1 :                         offset += uint64(len(trailer) - trailing)
     143           1 :                         trailer = trailer[len(trailer)-trailing:]
     144           1 : 
     145           1 :                         if b.name == "footer" {
     146           1 :                                 version := trailer[:4]
     147           1 :                                 fmt.Fprintf(w, "%10d    version: %d\n", offset, binary.LittleEndian.Uint32(version))
     148           1 :                                 trailer, offset = trailer[4:], offset+4
     149           1 :                         }
     150             : 
     151           1 :                         magicNumber := trailer
     152           1 :                         fmt.Fprintf(w, "%10d    magic number: 0x%x\n", offset, magicNumber)
     153           1 : 
     154           1 :                         continue
     155             :                 }
     156             : 
     157           1 :                 h, err := r.readBlock(
     158           1 :                         context.Background(), b.Handle, nil /* transform */, nil /* readHandle */, nil /* stats */, nil /* iterStats */, nil /* buffer pool */)
     159           1 :                 if err != nil {
     160           0 :                         fmt.Fprintf(w, "  [err: %s]\n", err)
     161           0 :                         continue
     162             :                 }
     163             : 
     164           1 :                 formatTrailer := func() {
     165           1 :                         trailer := make([]byte, block.TrailerLen)
     166           1 :                         offset := int64(b.Offset + b.Length)
     167           1 :                         _ = r.readable.ReadAt(ctx, trailer, offset)
     168           1 :                         algo := block.CompressionIndicator(trailer[0])
     169           1 :                         checksum := binary.LittleEndian.Uint32(trailer[1:])
     170           1 :                         fmt.Fprintf(w, "%10d    [trailer compression=%s checksum=0x%04x]\n", offset, algo, checksum)
     171           1 :                 }
     172             : 
     173           1 :                 var lastKey InternalKey
     174           1 :                 switch b.name {
     175           1 :                 case "data", "range-del", "range-key":
     176           1 :                         iter, _ := rowblk.NewIter(r.Compare, r.Split, h.Get(), NoTransforms)
     177           1 :                         iter.Describe(w, b.Offset, func(w io.Writer, key *base.InternalKey, value []byte, enc rowblk.KVEncoding) {
     178           1 : 
     179           1 :                                 // The format of the numbers in the record line is:
     180           1 :                                 //
     181           1 :                                 //   (<total> = <length> [<shared>] + <unshared> + <value>)
     182           1 :                                 //
     183           1 :                                 // <total>    is the total number of bytes for the record.
     184           1 :                                 // <length>   is the size of the 3 varint encoded integers for <shared>,
     185           1 :                                 //            <unshared>, and <value>.
     186           1 :                                 // <shared>   is the number of key bytes shared with the previous key.
     187           1 :                                 // <unshared> is the number of unshared key bytes.
     188           1 :                                 // <value>    is the number of value bytes.
     189           1 :                                 fmt.Fprintf(w, "%10d    record (%d = %d [%d] + %d + %d)",
     190           1 :                                         b.Offset+uint64(enc.Offset), enc.Length,
     191           1 :                                         enc.Length-int32(enc.KeyUnshared+enc.ValueLen), enc.KeyShared, enc.KeyUnshared, enc.ValueLen)
     192           1 :                                 if enc.IsRestart {
     193           1 :                                         fmt.Fprintf(w, " [restart]\n")
     194           1 :                                 } else {
     195           1 :                                         fmt.Fprintf(w, "\n")
     196           1 :                                 }
     197           1 :                                 if fmtRecord != nil {
     198           1 :                                         fmt.Fprintf(w, "              ")
     199           1 :                                         if l.Format < TableFormatPebblev3 {
     200           1 :                                                 fmtRecord(key, value)
     201           1 :                                         } else {
     202           1 :                                                 if key.Kind() != InternalKeyKindSet {
     203           1 :                                                         fmtRecord(key, value)
     204           1 :                                                 } else if !block.ValuePrefix(value[0]).IsValueHandle() {
     205           1 :                                                         fmtRecord(key, value[1:])
     206           1 :                                                 } else {
     207           1 :                                                         vh := decodeValueHandle(value[1:])
     208           1 :                                                         fmtRecord(key, []byte(fmt.Sprintf("value handle %+v", vh)))
     209           1 :                                                 }
     210             :                                         }
     211             :                                 }
     212             : 
     213           1 :                                 if b.name == "data" {
     214           1 :                                         if base.InternalCompare(r.Compare, lastKey, *key) >= 0 {
     215           1 :                                                 fmt.Fprintf(w, "              WARNING: OUT OF ORDER KEYS!\n")
     216           1 :                                         }
     217           1 :                                         lastKey.Trailer = key.Trailer
     218           1 :                                         lastKey.UserKey = append(lastKey.UserKey[:0], key.UserKey...)
     219             :                                 }
     220             :                         })
     221           1 :                         formatTrailer()
     222           1 :                 case "index", "top-index":
     223           1 :                         iter, _ := rowblk.NewIter(r.Compare, r.Split, h.Get(), NoTransforms)
     224           1 :                         iter.Describe(w, b.Offset, func(w io.Writer, key *base.InternalKey, value []byte, enc rowblk.KVEncoding) {
     225           1 :                                 bh, err := block.DecodeHandleWithProperties(value)
     226           1 :                                 if err != nil {
     227           0 :                                         fmt.Fprintf(w, "%10d    [err: %s]\n", b.Offset+uint64(enc.Offset), err)
     228           0 :                                         return
     229           0 :                                 }
     230           1 :                                 fmt.Fprintf(w, "%10d    block:%d/%d",
     231           1 :                                         b.Offset+uint64(enc.Offset), bh.Offset, bh.Length)
     232           1 :                                 if enc.IsRestart {
     233           1 :                                         fmt.Fprintf(w, " [restart]\n")
     234           1 :                                 } else {
     235           0 :                                         fmt.Fprintf(w, "\n")
     236           0 :                                 }
     237             :                         })
     238           1 :                         formatTrailer()
     239           1 :                 case "properties":
     240           1 :                         iter, _ := rowblk.NewRawIter(r.Compare, h.Get())
     241           1 :                         iter.Describe(w, b.Offset,
     242           1 :                                 func(w io.Writer, key *base.InternalKey, value []byte, enc rowblk.KVEncoding) {
     243           1 :                                         fmt.Fprintf(w, "%10d    %s (%d)", b.Offset+uint64(enc.Offset), key.UserKey, enc.Length)
     244           1 :                                 })
     245           1 :                         formatTrailer()
     246           1 :                 case "meta-index":
     247           1 :                         iter, _ := rowblk.NewRawIter(r.Compare, h.Get())
     248           1 :                         iter.Describe(w, b.Offset,
     249           1 :                                 func(w io.Writer, key *base.InternalKey, value []byte, enc rowblk.KVEncoding) {
     250           1 :                                         var bh block.Handle
     251           1 :                                         var n int
     252           1 :                                         var vbih valueBlocksIndexHandle
     253           1 :                                         isValueBlocksIndexHandle := false
     254           1 :                                         if bytes.Equal(iter.Key().UserKey, []byte(metaValueIndexName)) {
     255           1 :                                                 vbih, n, err = decodeValueBlocksIndexHandle(value)
     256           1 :                                                 bh = vbih.h
     257           1 :                                                 isValueBlocksIndexHandle = true
     258           1 :                                         } else {
     259           1 :                                                 bh, n = block.DecodeHandle(value)
     260           1 :                                         }
     261           1 :                                         if n == 0 || n != len(value) {
     262           0 :                                                 fmt.Fprintf(w, "%10d    [err: %s]\n", enc.Offset, err)
     263           0 :                                                 return
     264           0 :                                         }
     265           1 :                                         var vbihStr string
     266           1 :                                         if isValueBlocksIndexHandle {
     267           1 :                                                 vbihStr = fmt.Sprintf(" value-blocks-index-lengths: %d(num), %d(offset), %d(length)",
     268           1 :                                                         vbih.blockNumByteLength, vbih.blockOffsetByteLength, vbih.blockLengthByteLength)
     269           1 :                                         }
     270           1 :                                         fmt.Fprintf(w, "%10d    %s block:%d/%d%s",
     271           1 :                                                 b.Offset+uint64(enc.Offset), iter.Key().UserKey, bh.Offset, bh.Length, vbihStr)
     272             :                                 })
     273           1 :                         formatTrailer()
     274           1 :                 case "value-block":
     275             :                         // We don't peer into the value-block since it can't be interpreted
     276             :                         // without the valueHandles.
     277           1 :                 case "value-index":
     278             :                         // We have already read the value-index to construct the list of
     279             :                         // value-blocks, so no need to do it again.
     280             :                 }
     281             : 
     282           1 :                 h.Release()
     283             :         }
     284             : 
     285           1 :         last := blocks[len(blocks)-1]
     286           1 :         fmt.Fprintf(w, "%10d  EOF\n", last.Offset+last.Length)
     287             : }
     288             : 
     289             : // layoutWriter writes the structure of an sstable to durable storage. It
     290             : // accepts serialized blocks, writes them to storage and returns a block handle
     291             : // describing the offset and length of the block.
     292             : type layoutWriter struct {
     293             :         writable objstorage.Writable
     294             : 
     295             :         // cacheOpts are used to remove blocks written to the sstable from the cache,
     296             :         // providing a defense in depth against bugs which cause cache collisions.
     297             :         cacheOpts sstableinternal.CacheOptions
     298             : 
     299             :         // options copied from WriterOptions
     300             :         tableFormat  TableFormat
     301             :         compression  block.Compression
     302             :         checksumType block.ChecksumType
     303             : 
     304             :         // offset tracks the current write offset within the writable.
     305             :         offset uint64
     306             :         // lastIndexBlockHandle holds the handle to the most recently-written index
     307             :         // block.  It's updated by writeIndexBlock. When writing sstables with a
     308             :         // single-level index, this field will be updated once. When writing
     309             :         // sstables with a two-level index, the last update will set the two-level
     310             :         // index.
     311             :         lastIndexBlockHandle block.Handle
     312             :         handles              []metaIndexHandle
     313             :         handlesBuf           bytealloc.A
     314             :         tmp                  [blockHandleLikelyMaxLen]byte
     315             :         buf                  blockBuf
     316             : }
     317             : 
     318           1 : func makeLayoutWriter(w objstorage.Writable, opts WriterOptions) layoutWriter {
     319           1 :         return layoutWriter{
     320           1 :                 writable:     w,
     321           1 :                 cacheOpts:    opts.internal.CacheOpts,
     322           1 :                 tableFormat:  opts.TableFormat,
     323           1 :                 compression:  opts.Compression,
     324           1 :                 checksumType: opts.Checksum,
     325           1 :                 buf: blockBuf{
     326           1 :                         checksummer: block.Checksummer{Type: opts.Checksum},
     327           1 :                 },
     328           1 :         }
     329           1 : }
     330             : 
     331             : type metaIndexHandle struct {
     332             :         key                string
     333             :         encodedBlockHandle []byte
     334             : }
     335             : 
     336             : // Abort aborts writing the table, aborting the underlying writable too. Abort
     337             : // is idempotent.
     338           1 : func (w *layoutWriter) Abort() {
     339           1 :         if w.writable != nil {
     340           1 :                 w.writable.Abort()
     341           1 :                 w.writable = nil
     342           1 :         }
     343             : }
     344             : 
     345             : // WriteDataBlock constructs a trailer for the provided data block and writes
     346             : // the block and trailer to the writer. It returns the block's handle.
     347           1 : func (w *layoutWriter) WriteDataBlock(b []byte, buf *blockBuf) (block.Handle, error) {
     348           1 :         return w.writeBlock(b, w.compression, buf)
     349           1 : }
     350             : 
     351             : // WritePrecompressedDataBlock writes a pre-compressed data block and its
     352             : // pre-computed trailer to the writer, returning it's block handle.
     353           1 : func (w *layoutWriter) WritePrecompressedDataBlock(blk block.PhysicalBlock) (block.Handle, error) {
     354           1 :         return w.writePrecompressedBlock(blk)
     355           1 : }
     356             : 
     357             : // WriteIndexBlock constructs a trailer for the provided index (first or
     358             : // second-level) and writes the block and trailer to the writer. It remembers
     359             : // the last-written index block's handle and adds it to the file's meta index
     360             : // when the writer is finished.
     361           1 : func (w *layoutWriter) WriteIndexBlock(b []byte) (block.Handle, error) {
     362           1 :         h, err := w.writeBlock(b, w.compression, &w.buf)
     363           1 :         if err == nil {
     364           1 :                 w.lastIndexBlockHandle = h
     365           1 :         }
     366           1 :         return h, err
     367             : }
     368             : 
     369             : // WriteFilterBlock finishes the provided filter, constructs a trailer and
     370             : // writes the block and trailer to the writer. It automatically adds the filter
     371             : // block to the file's meta index when the writer is finished.
     372           1 : func (w *layoutWriter) WriteFilterBlock(f filterWriter) (bh block.Handle, err error) {
     373           1 :         b, err := f.finish()
     374           1 :         if err != nil {
     375           0 :                 return block.Handle{}, err
     376           0 :         }
     377           1 :         return w.writeNamedBlock(b, f.metaName())
     378             : }
     379             : 
     380             : // WritePropertiesBlock constructs a trailer for the provided properties block
     381             : // and writes the block and trailer to the writer. It automatically adds the
     382             : // properties block to the file's meta index when the writer is finished.
     383           1 : func (w *layoutWriter) WritePropertiesBlock(b []byte) (block.Handle, error) {
     384           1 :         return w.writeNamedBlock(b, metaPropertiesName)
     385           1 : }
     386             : 
     387             : // WriteRangeKeyBlock constructs a trailer for the provided range key block and
     388             : // writes the block and trailer to the writer. It automatically adds the range
     389             : // key block to the file's meta index when the writer is finished.
     390           1 : func (w *layoutWriter) WriteRangeKeyBlock(b []byte) (block.Handle, error) {
     391           1 :         return w.writeNamedBlock(b, metaRangeKeyName)
     392           1 : }
     393             : 
     394             : // WriteRangeDeletionBlock constructs a trailer for the provided range deletion
     395             : // block and writes the block and trailer to the writer. It automatically adds
     396             : // the range deletion block to the file's meta index when the writer is
     397             : // finished.
     398           1 : func (w *layoutWriter) WriteRangeDeletionBlock(b []byte) (block.Handle, error) {
     399           1 :         return w.writeNamedBlock(b, metaRangeDelV2Name)
     400           1 : }
     401             : 
     402           1 : func (w *layoutWriter) writeNamedBlock(b []byte, name string) (bh block.Handle, err error) {
     403           1 :         bh, err = w.writeBlock(b, block.NoCompression, &w.buf)
     404           1 :         if err == nil {
     405           1 :                 w.recordToMetaindex(name, bh)
     406           1 :         }
     407           1 :         return bh, err
     408             : }
     409             : 
     410             : // WriteValueBlock writes a pre-finished value block (with the trailer) to the
     411             : // writer.
     412           1 : func (w *layoutWriter) WriteValueBlock(blk block.PhysicalBlock) (block.Handle, error) {
     413           1 :         return w.writePrecompressedBlock(blk)
     414           1 : }
     415             : 
     416             : func (w *layoutWriter) WriteValueIndexBlock(
     417             :         blk []byte, vbih valueBlocksIndexHandle,
     418           1 : ) (block.Handle, error) {
     419           1 :         // NB: value index blocks are already finished and contain the block
     420           1 :         // trailer.
     421           1 :         // TODO(jackson): can this be refactored to make value blocks less
     422           1 :         // of a snowflake?
     423           1 :         off := w.offset
     424           1 :         w.clearFromCache(off)
     425           1 :         // Write the bytes to the file.
     426           1 :         if err := w.writable.Write(blk); err != nil {
     427           0 :                 return block.Handle{}, err
     428           0 :         }
     429           1 :         l := uint64(len(blk))
     430           1 :         w.offset += l
     431           1 : 
     432           1 :         n := encodeValueBlocksIndexHandle(w.tmp[:], vbih)
     433           1 :         w.recordToMetaindexRaw(metaValueIndexName, w.tmp[:n])
     434           1 : 
     435           1 :         return block.Handle{Offset: off, Length: l}, nil
     436             : }
     437             : 
     438             : func (w *layoutWriter) writeBlock(
     439             :         b []byte, compression block.Compression, buf *blockBuf,
     440           1 : ) (block.Handle, error) {
     441           1 :         return w.writePrecompressedBlock(block.CompressAndChecksum(
     442           1 :                 &buf.compressedBuf, b, compression, &buf.checksummer))
     443           1 : }
     444             : 
     445             : // writePrecompressedBlock writes a pre-compressed block and its
     446             : // pre-computed trailer to the writer, returning it's block handle.
     447           1 : func (w *layoutWriter) writePrecompressedBlock(blk block.PhysicalBlock) (block.Handle, error) {
     448           1 :         w.clearFromCache(w.offset)
     449           1 :         // Write the bytes to the file.
     450           1 :         n, err := blk.WriteTo(w.writable)
     451           1 :         if err != nil {
     452           0 :                 return block.Handle{}, err
     453           0 :         }
     454           1 :         bh := block.Handle{Offset: w.offset, Length: uint64(blk.LengthWithoutTrailer())}
     455           1 :         w.offset += uint64(n)
     456           1 :         return bh, nil
     457             : }
     458             : 
     459             : // Write implements io.Writer. This is analogous to writePrecompressedBlock for
     460             : // blocks that already incorporate the trailer, and don't need the callee to
     461             : // return a BlockHandle.
     462           0 : func (w *layoutWriter) Write(blockWithTrailer []byte) (n int, err error) {
     463           0 :         offset := w.offset
     464           0 :         w.clearFromCache(offset)
     465           0 :         w.offset += uint64(len(blockWithTrailer))
     466           0 :         if err := w.writable.Write(blockWithTrailer); err != nil {
     467           0 :                 return 0, err
     468           0 :         }
     469           0 :         return len(blockWithTrailer), nil
     470             : }
     471             : 
     472             : // clearFromCache removes the block at the provided offset from the cache. This provides defense in
     473             : // depth against bugs which cause cache collisions.
     474           1 : func (w *layoutWriter) clearFromCache(offset uint64) {
     475           1 :         if w.cacheOpts.Cache != nil {
     476           1 :                 // TODO(peter): Alternatively, we could add the uncompressed value to the
     477           1 :                 // cache.
     478           1 :                 w.cacheOpts.Cache.Delete(w.cacheOpts.CacheID, w.cacheOpts.FileNum, offset)
     479           1 :         }
     480             : }
     481             : 
     482           1 : func (w *layoutWriter) recordToMetaindex(key string, h block.Handle) {
     483           1 :         n := h.EncodeVarints(w.tmp[:])
     484           1 :         w.recordToMetaindexRaw(key, w.tmp[:n])
     485           1 : }
     486             : 
     487           1 : func (w *layoutWriter) recordToMetaindexRaw(key string, h []byte) {
     488           1 :         var encodedHandle []byte
     489           1 :         w.handlesBuf, encodedHandle = w.handlesBuf.Alloc(len(h))
     490           1 :         copy(encodedHandle, h)
     491           1 :         w.handles = append(w.handles, metaIndexHandle{key: key, encodedBlockHandle: encodedHandle})
     492           1 : }
     493             : 
     494           1 : func (w *layoutWriter) IsFinished() bool { return w.writable == nil }
     495             : 
     496             : // Finish serializes the sstable, writing out the meta index block and sstable
     497             : // footer and closing the file. It returns the total size of the resulting
     498             : // ssatable.
     499           1 : func (w *layoutWriter) Finish() (size uint64, err error) {
     500           1 :         // Sort the meta index handles by key and write the meta index block.
     501           1 :         slices.SortFunc(w.handles, func(a, b metaIndexHandle) int {
     502           1 :                 return cmp.Compare(a.key, b.key)
     503           1 :         })
     504           1 :         bw := rowblk.Writer{RestartInterval: 1}
     505           1 :         for _, h := range w.handles {
     506           1 :                 bw.AddRaw(unsafe.Slice(unsafe.StringData(h.key), len(h.key)), h.encodedBlockHandle)
     507           1 :         }
     508           1 :         metaIndexHandle, err := w.writeBlock(bw.Finish(), block.NoCompression, &w.buf)
     509           1 :         if err != nil {
     510           0 :                 return 0, err
     511           0 :         }
     512             : 
     513             :         // Write the table footer.
     514           1 :         footer := footer{
     515           1 :                 format:      w.tableFormat,
     516           1 :                 checksum:    w.checksumType,
     517           1 :                 metaindexBH: metaIndexHandle,
     518           1 :                 indexBH:     w.lastIndexBlockHandle,
     519           1 :         }
     520           1 :         encodedFooter := footer.encode(w.tmp[:])
     521           1 :         if err := w.writable.Write(encodedFooter); err != nil {
     522           0 :                 return 0, err
     523           0 :         }
     524           1 :         w.offset += uint64(len(encodedFooter))
     525           1 : 
     526           1 :         err = w.writable.Finish()
     527           1 :         w.writable = nil
     528           1 :         return w.offset, err
     529             : }

Generated by: LCOV version 1.14