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 }