| /* |
| * 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.util.packed; |
| |
| |
| import static org.apache.lucene.util.packed.PackedInts.checkBlockSize; |
| |
| import java.io.IOException; |
| import java.util.Arrays; |
| |
| import org.apache.lucene.store.DataOutput; |
| |
| abstract class AbstractBlockPackedWriter { |
| |
| static final int MIN_BLOCK_SIZE = 64; |
| static final int MAX_BLOCK_SIZE = 1 << (30 - 3); |
| static final int MIN_VALUE_EQUALS_0 = 1 << 0; |
| static final int BPV_SHIFT = 1; |
| |
| // same as DataOutput.writeVLong but accepts negative values |
| static void writeVLong(DataOutput out, long i) throws IOException { |
| int k = 0; |
| while ((i & ~0x7FL) != 0L && k++ < 8) { |
| out.writeByte((byte)((i & 0x7FL) | 0x80L)); |
| i >>>= 7; |
| } |
| out.writeByte((byte) i); |
| } |
| |
| protected DataOutput out; |
| protected final long[] values; |
| protected byte[] blocks; |
| protected int off; |
| protected long ord; |
| protected boolean finished; |
| |
| /** |
| * Sole constructor. |
| * @param blockSize the number of values of a single block, must be a multiple of <tt>64</tt> |
| */ |
| public AbstractBlockPackedWriter(DataOutput out, int blockSize) { |
| checkBlockSize(blockSize, MIN_BLOCK_SIZE, MAX_BLOCK_SIZE); |
| reset(out); |
| values = new long[blockSize]; |
| } |
| |
| /** Reset this writer to wrap <code>out</code>. The block size remains unchanged. */ |
| public void reset(DataOutput out) { |
| assert out != null; |
| this.out = out; |
| off = 0; |
| ord = 0L; |
| finished = false; |
| } |
| |
| private void checkNotFinished() { |
| if (finished) { |
| throw new IllegalStateException("Already finished"); |
| } |
| } |
| |
| /** Append a new long. */ |
| public void add(long l) throws IOException { |
| checkNotFinished(); |
| if (off == values.length) { |
| flush(); |
| } |
| values[off++] = l; |
| ++ord; |
| } |
| |
| // For testing only |
| void addBlockOfZeros() throws IOException { |
| checkNotFinished(); |
| if (off != 0 && off != values.length) { |
| throw new IllegalStateException("" + off); |
| } |
| if (off == values.length) { |
| flush(); |
| } |
| Arrays.fill(values, 0); |
| off = values.length; |
| ord += values.length; |
| } |
| |
| /** Flush all buffered data to disk. This instance is not usable anymore |
| * after this method has been called until {@link #reset(DataOutput)} has |
| * been called. */ |
| public void finish() throws IOException { |
| checkNotFinished(); |
| if (off > 0) { |
| flush(); |
| } |
| finished = true; |
| } |
| |
| /** Return the number of values which have been added. */ |
| public long ord() { |
| return ord; |
| } |
| |
| protected abstract void flush() throws IOException; |
| |
| protected final void writeValues(int bitsRequired) throws IOException { |
| final PackedInts.Encoder encoder = PackedInts.getEncoder(PackedInts.Format.PACKED, PackedInts.VERSION_CURRENT, bitsRequired); |
| final int iterations = values.length / encoder.byteValueCount(); |
| final int blockSize = encoder.byteBlockCount() * iterations; |
| if (blocks == null || blocks.length < blockSize) { |
| blocks = new byte[blockSize]; |
| } |
| if (off < values.length) { |
| Arrays.fill(values, off, values.length, 0L); |
| } |
| encoder.encode(values, 0, blocks, 0, iterations); |
| final int blockCount = (int) PackedInts.Format.PACKED.byteCount(PackedInts.VERSION_CURRENT, off, bitsRequired); |
| out.writeBytes(blocks, blockCount); |
| } |
| |
| } |