private int readWriteBatch(int bucket, SliceInput record, int updateSize, Entrys entries) throws IOException { List<Entry<Slice, Slice>> list = Lists.newArrayListWithCapacity(updateSize); int count = 0; while (record.isReadable()) { count++; ValueType valueType = ValueType.getValueTypeByPersistentId(record.readByte()); if (valueType == VALUE) { Slice key = readLengthPrefixedBytes(record); Slice value = readLengthPrefixedBytes(record); list.add(new BlockEntry(key, value)); } else if (valueType == DELETION) { Slice key = readLengthPrefixedBytes(record); list.add(new BlockEntry(key, Slices.EMPTY_SLICE)); } else { throw new IllegalStateException("Unexpected value type " + valueType); } } if (count != updateSize) { throw new IOException( String.format( "Expected %d entries in log record but found %s entries", updateSize, entries)); } int available = 0; for (Entry<Slice, Slice> e : list) { InternalKey iKey = new InternalKey(e.getKey(), 0, e.getValue().length() != 0 ? VALUE : DELETION); if (bucket == iKey.bucket()) { entries.add(e); available++; } } return available; }
// Writes a stream of chunks such that no chunk is split across a block boundary @Override public synchronized void addRecord(Slice record, boolean force) throws IOException { Preconditions.checkState(!closed.get(), "Log has been closed"); SliceInput sliceInput = record.input(); // used to track first, middle and last blocks boolean begin = true; // Fragment the record int chunks as necessary and write it. Note that if record // is empty, we still want to iterate once to write a single // zero-length chunk. do { int bytesRemainingInBlock = BLOCK_SIZE - blockOffset; Preconditions.checkState(bytesRemainingInBlock >= 0); // Switch to a new block if necessary if (bytesRemainingInBlock < HEADER_SIZE) { if (bytesRemainingInBlock > 0) { // Fill the rest of the block with zeros // todo lame... need a better way to write zeros fileChannel.write(ByteBuffer.allocate(bytesRemainingInBlock)); } blockOffset = 0; bytesRemainingInBlock = BLOCK_SIZE - blockOffset; } // Invariant: we never leave less than HEADER_SIZE bytes available in a block int bytesAvailableInBlock = bytesRemainingInBlock - HEADER_SIZE; Preconditions.checkState(bytesAvailableInBlock >= 0); // if there are more bytes in the record then there are available in the block, // fragment the record; otherwise write to the end of the record boolean end; int fragmentLength; if (sliceInput.available() > bytesAvailableInBlock) { end = false; fragmentLength = bytesAvailableInBlock; } else { end = true; fragmentLength = sliceInput.available(); } // determine block type LogChunkType type; if (begin && end) { type = LogChunkType.FULL; } else if (begin) { type = LogChunkType.FIRST; } else if (end) { type = LogChunkType.LAST; } else { type = LogChunkType.MIDDLE; } // write the chunk writeChunk(type, sliceInput.readSlice(fragmentLength)); // we are no longer on the first chunk begin = false; } while (sliceInput.isReadable()); if (force) { fileChannel.force(false); } }