github.com/df-mc/goleveldb@v1.1.9/leveldb/table/writer.go (about)

     1  // Copyright (c) 2012, Suryandaru Triandana <syndtr@gmail.com>
     2  // All rights reserved.
     3  //
     4  // Use of this source code is governed by a BSD-style license that can be
     5  // found in the LICENSE file.
     6  
     7  package table
     8  
     9  import (
    10  	"bytes"
    11  	"encoding/binary"
    12  	"errors"
    13  	"fmt"
    14  	"github.com/klauspost/compress/flate"
    15  	"io"
    16  	"sync"
    17  
    18  	"github.com/golang/snappy"
    19  
    20  	"github.com/df-mc/goleveldb/leveldb/comparer"
    21  	"github.com/df-mc/goleveldb/leveldb/filter"
    22  	"github.com/df-mc/goleveldb/leveldb/opt"
    23  	"github.com/df-mc/goleveldb/leveldb/util"
    24  )
    25  
    26  func sharedPrefixLen(a, b []byte) int {
    27  	i, n := 0, len(a)
    28  	if n > len(b) {
    29  		n = len(b)
    30  	}
    31  	for i < n && a[i] == b[i] {
    32  		i++
    33  	}
    34  	return i
    35  }
    36  
    37  type blockWriter struct {
    38  	restartInterval int
    39  	buf             util.Buffer
    40  	nEntries        int
    41  	prevKey         []byte
    42  	restarts        []uint32
    43  	scratch         []byte
    44  }
    45  
    46  func (w *blockWriter) append(key, value []byte) {
    47  	nShared := 0
    48  	if w.nEntries%w.restartInterval == 0 {
    49  		w.restarts = append(w.restarts, uint32(w.buf.Len()))
    50  	} else {
    51  		nShared = sharedPrefixLen(w.prevKey, key)
    52  	}
    53  	n := binary.PutUvarint(w.scratch[0:], uint64(nShared))
    54  	n += binary.PutUvarint(w.scratch[n:], uint64(len(key)-nShared))
    55  	n += binary.PutUvarint(w.scratch[n:], uint64(len(value)))
    56  	w.buf.Write(w.scratch[:n])
    57  	w.buf.Write(key[nShared:])
    58  	w.buf.Write(value)
    59  	w.prevKey = append(w.prevKey[:0], key...)
    60  	w.nEntries++
    61  }
    62  
    63  func (w *blockWriter) finish() {
    64  	// Write restarts entry.
    65  	if w.nEntries == 0 {
    66  		// Must have at least one restart entry.
    67  		w.restarts = append(w.restarts, 0)
    68  	}
    69  	w.restarts = append(w.restarts, uint32(len(w.restarts)))
    70  	for _, x := range w.restarts {
    71  		buf4 := w.buf.Alloc(4)
    72  		binary.LittleEndian.PutUint32(buf4, x)
    73  	}
    74  }
    75  
    76  func (w *blockWriter) reset() {
    77  	w.buf.Reset()
    78  	w.nEntries = 0
    79  	w.restarts = w.restarts[:0]
    80  }
    81  
    82  func (w *blockWriter) bytesLen() int {
    83  	restartsLen := len(w.restarts)
    84  	if restartsLen == 0 {
    85  		restartsLen = 1
    86  	}
    87  	return w.buf.Len() + 4*restartsLen + 4
    88  }
    89  
    90  type filterWriter struct {
    91  	generator filter.FilterGenerator
    92  	buf       util.Buffer
    93  	nKeys     int
    94  	offsets   []uint32
    95  }
    96  
    97  func (w *filterWriter) add(key []byte) {
    98  	if w.generator == nil {
    99  		return
   100  	}
   101  	w.generator.Add(key)
   102  	w.nKeys++
   103  }
   104  
   105  func (w *filterWriter) flush(offset uint64) {
   106  	if w.generator == nil {
   107  		return
   108  	}
   109  	for x := int(offset / filterBase); x > len(w.offsets); {
   110  		w.generate()
   111  	}
   112  }
   113  
   114  func (w *filterWriter) finish() {
   115  	if w.generator == nil {
   116  		return
   117  	}
   118  	// Generate last keys.
   119  
   120  	if w.nKeys > 0 {
   121  		w.generate()
   122  	}
   123  	w.offsets = append(w.offsets, uint32(w.buf.Len()))
   124  	for _, x := range w.offsets {
   125  		buf4 := w.buf.Alloc(4)
   126  		binary.LittleEndian.PutUint32(buf4, x)
   127  	}
   128  	w.buf.WriteByte(filterBaseLg)
   129  }
   130  
   131  func (w *filterWriter) generate() {
   132  	// Record offset.
   133  	w.offsets = append(w.offsets, uint32(w.buf.Len()))
   134  	// Generate filters.
   135  	if w.nKeys > 0 {
   136  		w.generator.Generate(&w.buf)
   137  		w.nKeys = 0
   138  	}
   139  }
   140  
   141  // Writer is a table writer.
   142  type Writer struct {
   143  	writer io.Writer
   144  	err    error
   145  	// Options
   146  	cmp         comparer.Comparer
   147  	filter      filter.Filter
   148  	compression opt.Compression
   149  	blockSize   int
   150  
   151  	dataBlock   blockWriter
   152  	indexBlock  blockWriter
   153  	filterBlock filterWriter
   154  	pendingBH   blockHandle
   155  	offset      uint64
   156  	nEntries    int
   157  	// Scratch allocated enough for 5 uvarint. Block writer should not use
   158  	// first 20-bytes since it will be used to encode block handle, which
   159  	// then passed to the block writer itself.
   160  	scratch            [50]byte
   161  	comparerScratch    []byte
   162  	compressionScratch []byte
   163  }
   164  
   165  var mutex = sync.Mutex{}
   166  var compressed = &bytes.Buffer{}
   167  var writer, _ = flate.NewWriter(compressed, 5)
   168  
   169  func (w *Writer) writeBlock(buf *util.Buffer, compression opt.Compression) (bh blockHandle, err error) {
   170  	// Compress the buffer if necessary.
   171  	var b []byte
   172  	switch compression {
   173  	case opt.SnappyCompression:
   174  		// Allocate scratch enough for compression and block trailer.
   175  		if n := snappy.MaxEncodedLen(buf.Len()) + blockTrailerLen; len(w.compressionScratch) < n {
   176  			w.compressionScratch = make([]byte, n)
   177  		}
   178  		compressed := snappy.Encode(w.compressionScratch, buf.Bytes())
   179  		n := len(compressed)
   180  		b = compressed[:n+blockTrailerLen]
   181  		b[n] = blockTypeSnappyCompression
   182  	case opt.FlateCompression:
   183  		mutex.Lock()
   184  		if _, err := writer.Write(buf.Bytes()); err != nil {
   185  			return blockHandle{}, fmt.Errorf("writeBlock: flate compression failed: %v", err)
   186  		}
   187  		if err := writer.Close(); err != nil {
   188  			return blockHandle{}, fmt.Errorf("writeBlock: flushing flate writer failed: %v", err)
   189  		}
   190  		length := compressed.Len()
   191  		if compressed.Cap() < length+blockTrailerLen {
   192  			// Grow the compressed data by the block trailer length if its capacity isn't big enough.
   193  			compressed.Grow(blockTrailerLen)
   194  		}
   195  		b = append([]byte(nil), compressed.Bytes()[:length+blockTrailerLen]...)
   196  
   197  		compressed.Reset()
   198  		writer.Reset(compressed)
   199  		mutex.Unlock()
   200  
   201  		b[length] = blockTypeFlateCompression
   202  	default:
   203  		tmp := buf.Alloc(blockTrailerLen)
   204  		tmp[0] = blockTypeNoCompression
   205  		b = buf.Bytes()
   206  	}
   207  
   208  	// Calculate the checksum.
   209  	n := len(b) - 4
   210  	checksum := util.NewCRC(b[:n]).Value()
   211  	binary.LittleEndian.PutUint32(b[n:], checksum)
   212  
   213  	// Write the buffer to the file.
   214  	_, err = w.writer.Write(b)
   215  	if err != nil {
   216  		return
   217  	}
   218  	bh = blockHandle{w.offset, uint64(len(b) - blockTrailerLen)}
   219  	w.offset += uint64(len(b))
   220  	return
   221  }
   222  
   223  func (w *Writer) flushPendingBH(key []byte) {
   224  	if w.pendingBH.length == 0 {
   225  		return
   226  	}
   227  	var separator []byte
   228  	if len(key) == 0 {
   229  		separator = w.cmp.Successor(w.comparerScratch[:0], w.dataBlock.prevKey)
   230  	} else {
   231  		separator = w.cmp.Separator(w.comparerScratch[:0], w.dataBlock.prevKey, key)
   232  	}
   233  	if separator == nil {
   234  		separator = w.dataBlock.prevKey
   235  	} else {
   236  		w.comparerScratch = separator
   237  	}
   238  	n := encodeBlockHandle(w.scratch[:20], w.pendingBH)
   239  	// Append the block handle to the index block.
   240  	w.indexBlock.append(separator, w.scratch[:n])
   241  	// Reset prev key of the data block.
   242  	w.dataBlock.prevKey = w.dataBlock.prevKey[:0]
   243  	// Clear pending block handle.
   244  	w.pendingBH = blockHandle{}
   245  }
   246  
   247  func (w *Writer) finishBlock() error {
   248  	w.dataBlock.finish()
   249  	bh, err := w.writeBlock(&w.dataBlock.buf, w.compression)
   250  	if err != nil {
   251  		return err
   252  	}
   253  	w.pendingBH = bh
   254  	// Reset the data block.
   255  	w.dataBlock.reset()
   256  	// Flush the filter block.
   257  	w.filterBlock.flush(w.offset)
   258  	return nil
   259  }
   260  
   261  // Append appends key/value pair to the table. The keys passed must
   262  // be in increasing order.
   263  //
   264  // It is safe to modify the contents of the arguments after Append returns.
   265  func (w *Writer) Append(key, value []byte) error {
   266  	if w.err != nil {
   267  		return w.err
   268  	}
   269  	if w.nEntries > 0 && w.cmp.Compare(w.dataBlock.prevKey, key) >= 0 {
   270  		w.err = fmt.Errorf("leveldb/table: Writer: keys are not in increasing order: %q, %q", w.dataBlock.prevKey, key)
   271  		return w.err
   272  	}
   273  
   274  	w.flushPendingBH(key)
   275  	// Append key/value pair to the data block.
   276  	w.dataBlock.append(key, value)
   277  	// Add key to the filter block.
   278  	w.filterBlock.add(key)
   279  
   280  	// Finish the data block if block size target reached.
   281  	if w.dataBlock.bytesLen() >= w.blockSize {
   282  		if err := w.finishBlock(); err != nil {
   283  			w.err = err
   284  			return w.err
   285  		}
   286  	}
   287  	w.nEntries++
   288  	return nil
   289  }
   290  
   291  // BlocksLen returns number of blocks written so far.
   292  func (w *Writer) BlocksLen() int {
   293  	n := w.indexBlock.nEntries
   294  	if w.pendingBH.length > 0 {
   295  		// Includes the pending block.
   296  		n++
   297  	}
   298  	return n
   299  }
   300  
   301  // EntriesLen returns number of entries added so far.
   302  func (w *Writer) EntriesLen() int {
   303  	return w.nEntries
   304  }
   305  
   306  // BytesLen returns number of bytes written so far.
   307  func (w *Writer) BytesLen() int {
   308  	return int(w.offset)
   309  }
   310  
   311  // Close will finalize the table. Calling Append is not possible
   312  // after Close, but calling BlocksLen, EntriesLen and BytesLen
   313  // is still possible.
   314  func (w *Writer) Close() error {
   315  	if w.err != nil {
   316  		return w.err
   317  	}
   318  
   319  	// Write the last data block. Or empty data block if there
   320  	// aren't any data blocks at all.
   321  	if w.dataBlock.nEntries > 0 || w.nEntries == 0 {
   322  		if err := w.finishBlock(); err != nil {
   323  			w.err = err
   324  			return w.err
   325  		}
   326  	}
   327  	w.flushPendingBH(nil)
   328  
   329  	// Write the filter block.
   330  	var filterBH blockHandle
   331  	w.filterBlock.finish()
   332  	if buf := &w.filterBlock.buf; buf.Len() > 0 {
   333  		filterBH, w.err = w.writeBlock(buf, opt.NoCompression)
   334  		if w.err != nil {
   335  			return w.err
   336  		}
   337  	}
   338  
   339  	// Write the metaindex block.
   340  	if filterBH.length > 0 {
   341  		key := []byte("filter." + w.filter.Name())
   342  		n := encodeBlockHandle(w.scratch[:20], filterBH)
   343  		w.dataBlock.append(key, w.scratch[:n])
   344  	}
   345  	w.dataBlock.finish()
   346  	metaindexBH, err := w.writeBlock(&w.dataBlock.buf, w.compression)
   347  	if err != nil {
   348  		w.err = err
   349  		return w.err
   350  	}
   351  
   352  	// Write the index block.
   353  	w.indexBlock.finish()
   354  	indexBH, err := w.writeBlock(&w.indexBlock.buf, w.compression)
   355  	if err != nil {
   356  		w.err = err
   357  		return w.err
   358  	}
   359  
   360  	// Write the table footer.
   361  	footer := w.scratch[:footerLen]
   362  	for i := range footer {
   363  		footer[i] = 0
   364  	}
   365  	n := encodeBlockHandle(footer, metaindexBH)
   366  	encodeBlockHandle(footer[n:], indexBH)
   367  	copy(footer[footerLen-len(magic):], magic)
   368  	if _, err := w.writer.Write(footer); err != nil {
   369  		w.err = err
   370  		return w.err
   371  	}
   372  	w.offset += footerLen
   373  
   374  	w.err = errors.New("leveldb/table: writer is closed")
   375  	return nil
   376  }
   377  
   378  // NewWriter creates a new initialized table writer for the file.
   379  //
   380  // Table writer is not safe for concurrent use.
   381  func NewWriter(f io.Writer, o *opt.Options) *Writer {
   382  	w := &Writer{
   383  		writer:          f,
   384  		cmp:             o.GetComparer(),
   385  		filter:          o.GetFilter(),
   386  		compression:     o.GetCompression(),
   387  		blockSize:       o.GetBlockSize(),
   388  		comparerScratch: make([]byte, 0),
   389  	}
   390  	// data block
   391  	w.dataBlock.restartInterval = o.GetBlockRestartInterval()
   392  	// The first 20-bytes are used for encoding block handle.
   393  	w.dataBlock.scratch = w.scratch[20:]
   394  	// index block
   395  	w.indexBlock.restartInterval = 1
   396  	w.indexBlock.scratch = w.scratch[20:]
   397  	// filter block
   398  	if w.filter != nil {
   399  		w.filterBlock.generator = w.filter.NewGenerator()
   400  		w.filterBlock.flush(0)
   401  	}
   402  	return w
   403  }