// finishBlock finishes the current block and returns its block handle, which is // its offset and length in the table. func (w *Writer) finishBlock() (blockHandle, error) { // Write the restart points to the buffer. if w.nEntries == 0 { // Every block must have at least one restart point. w.restarts = w.restarts[:1] w.restarts[0] = 0 } tmp4 := w.tmp[:4] for _, x := range w.restarts { binary.LittleEndian.PutUint32(tmp4, x) w.buf.Write(tmp4) } binary.LittleEndian.PutUint32(tmp4, uint32(len(w.restarts))) w.buf.Write(tmp4) // Compress the buffer, discarding the result if the improvement // isn't at least 12.5%. b := w.buf.Bytes() w.tmp[0] = noCompressionBlockType if w.compression == db.SnappyCompression { compressed, err := snappy.Encode(w.compressedBuf, b) if err != nil { return blockHandle{}, err } w.compressedBuf = compressed[:cap(compressed)] if len(compressed) < len(b)-len(b)/8 { w.tmp[0] = snappyCompressionBlockType b = compressed } } // Calculate the checksum. checksum := crc.New(b).Update(w.tmp[:1]).Value() binary.LittleEndian.PutUint32(w.tmp[1:5], checksum) // Write the bytes to the file. if _, err := w.writer.Write(b); err != nil { return blockHandle{}, err } if _, err := w.writer.Write(w.tmp[:5]); err != nil { return blockHandle{}, err } bh := blockHandle{w.offset, uint64(len(b))} w.offset += uint64(len(b)) + blockTrailerLen // Reset the per-block state. w.buf.Reset() w.nEntries = 0 w.restarts = w.restarts[:0] return bh, nil }
// nextChunk sets r.buf[r.i:r.j] to hold the next chunk's payload, reading the // next block into the buffer if necessary. func (r *Reader) nextChunk(wantFirst bool) error { for { if r.j+headerSize <= r.n { checksum := binary.LittleEndian.Uint32(r.buf[r.j+0 : r.j+4]) length := binary.LittleEndian.Uint16(r.buf[r.j+4 : r.j+6]) chunkType := r.buf[r.j+6] if checksum == 0 && length == 0 && chunkType == 0 { if wantFirst { // Skip the rest of the block, if it looks like it is all zeroes. // This is common if the record file was created via mmap. r.i = r.n r.j = r.n continue } else { return errors.New("leveldb/record: invalid chunk") } } r.i = r.j + headerSize r.j = r.j + headerSize + int(length) if r.j > r.n { return errors.New("leveldb/record: invalid chunk (length overflows block)") } if checksum != crc.New(r.buf[r.i-1:r.j]).Value() { return errors.New("leveldb/record: invalid chunk (checksum mismatch)") } if wantFirst { if chunkType != fullChunkType && chunkType != firstChunkType { continue } } r.last = chunkType == fullChunkType || chunkType == lastChunkType return nil } if r.n < blockSize && r.started { if r.j != r.n { return io.ErrUnexpectedEOF } return io.EOF } n, err := io.ReadFull(r.r, r.buf[:]) if err != nil && err != io.ErrUnexpectedEOF { return err } r.i, r.j, r.n = 0, 0, n } panic("unreachable") }
// fillHeader fills in the header for the pending chunk. func (w *Writer) fillHeader(last bool) { if w.i+headerSize > w.j || w.j > blockSize { panic("leveldb/record: bad writer state") } if last { if w.first { w.buf[w.i+6] = fullChunkType } else { w.buf[w.i+6] = lastChunkType } } else { if w.first { w.buf[w.i+6] = firstChunkType } else { w.buf[w.i+6] = middleChunkType } } binary.LittleEndian.PutUint32(w.buf[w.i+0:w.i+4], crc.New(w.buf[w.i+6:w.j]).Value()) binary.LittleEndian.PutUint16(w.buf[w.i+4:w.i+6], uint16(w.j-w.i-headerSize)) }
// readBlock reads and decompresses a block from disk into memory. func (r *Reader) readBlock(bh blockHandle) (block, error) { b := make([]byte, bh.length+blockTrailerLen) if _, err := r.file.ReadAt(b, int64(bh.offset)); err != nil { return nil, err } if r.verifyChecksums { checksum0 := binary.LittleEndian.Uint32(b[bh.length+1:]) checksum1 := crc.New(b[:bh.length+1]).Value() if checksum0 != checksum1 { return nil, errors.New("leveldb/table: invalid table (checksum mismatch)") } } switch b[bh.length] { case noCompressionBlockType: return b[:bh.length], nil case snappyCompressionBlockType: b, err := snappy.Decode(nil, b[:bh.length]) if err != nil { return nil, err } return b, nil } return nil, fmt.Errorf("leveldb/table: unknown block compression: %d", b[bh.length]) }