| /* |
| * 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."); |
| } |
| } |
| } |