blob: 19dc4004853b757edeb5b71befaf79c98144eef5 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.lucene.store;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;
import java.util.zip.CRC32;
import java.util.zip.Checksum;
/**
* An {@link IndexOutput} writing to a {@link ByteBuffersDataOutput}.
*/
public final class ByteBuffersIndexOutput extends IndexOutput {
private final Consumer<ByteBuffersDataOutput> onClose;
private final Checksum checksum;
private long lastChecksumPosition;
private long lastChecksum;
private ByteBuffersDataOutput delegate;
public ByteBuffersIndexOutput(ByteBuffersDataOutput delegate, String resourceDescription, String name) {
this(delegate, resourceDescription, name, new CRC32(), null);
}
public ByteBuffersIndexOutput(ByteBuffersDataOutput delegate, String resourceDescription, String name,
Checksum checksum,
Consumer<ByteBuffersDataOutput> onClose) {
super(resourceDescription, name);
this.delegate = delegate;
this.checksum = checksum;
this.onClose = onClose;
}
@Override
public void close() throws IOException {
// No special effort to be thread-safe here since IndexOutputs are not required to be thread-safe.
ByteBuffersDataOutput local = delegate;
delegate = null;
if (local != null && onClose != null) {
onClose.accept(local);
}
}
@Override
public long getFilePointer() {
ensureOpen();
return delegate.size();
}
@Override
public long getChecksum() throws IOException {
ensureOpen();
if (checksum == null) {
throw new IOException("This index output has no checksum computing ability: " + toString());
}
// Compute checksum on the current content of the delegate.
//
// This way we can override more methods and pass them directly to the delegate for efficiency of writing,
// while allowing the checksum to be correctly computed on the current content of the output buffer (IndexOutput
// is per-thread, so no concurrent changes).
if (lastChecksumPosition != delegate.size()) {
lastChecksumPosition = delegate.size();
checksum.reset();
byte [] buffer = null;
for (ByteBuffer bb : delegate.toBufferList()) {
if (bb.hasArray()) {
checksum.update(bb.array(), bb.arrayOffset() + bb.position(), bb.remaining());
} else {
if (buffer == null) buffer = new byte [1024 * 4];
bb = bb.asReadOnlyBuffer();
int remaining = bb.remaining();
while (remaining > 0) {
int len = Math.min(remaining, buffer.length);
bb.get(buffer, 0, len);
checksum.update(buffer, 0, len);
remaining -= len;
}
}
}
lastChecksum = checksum.getValue();
}
return lastChecksum;
}
@Override
public void writeByte(byte b) throws IOException {
ensureOpen();
delegate.writeByte(b);
}
@Override
public void writeBytes(byte[] b, int offset, int length) throws IOException {
ensureOpen();
delegate.writeBytes(b, offset, length);
}
@Override
public void writeBytes(byte[] b, int length) throws IOException {
ensureOpen();
delegate.writeBytes(b, length);
}
@Override
public void writeInt(int i) throws IOException {
ensureOpen();
delegate.writeInt(i);
}
@Override
public void writeShort(short i) throws IOException {
ensureOpen();
delegate.writeShort(i);
}
@Override
public void writeLong(long i) throws IOException {
ensureOpen();
delegate.writeLong(i);
}
@Override
public void writeString(String s) throws IOException {
ensureOpen();
delegate.writeString(s);
}
@Override
public void copyBytes(DataInput input, long numBytes) throws IOException {
ensureOpen();
delegate.copyBytes(input, numBytes);
}
@Override
public void writeMapOfStrings(Map<String, String> map) throws IOException {
ensureOpen();
delegate.writeMapOfStrings(map);
}
@Override
public void writeSetOfStrings(Set<String> set) throws IOException {
ensureOpen();
delegate.writeSetOfStrings(set);
}
private void ensureOpen() {
if (delegate == null) {
throw new AlreadyClosedException("Already closed.");
}
}
}