package org.apache.solr.search.field;
/*
* 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.
*/
import org.apache.lucene.util.BytesRef;
import org.apache.solr.core.HS;
import java.io.Closeable;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public final class NativePagedBytes implements Closeable {
private final List<Long> blocks = new ArrayList<Long>();
private final List<Integer> blockEnd = new ArrayList<Integer>();
private int upto;
private long currentBlock;
private long bytesUsedInPrevBlocks;
private final int blockSize;
/** 1<<blockBits must be bigger than biggest single
* BytesRef slice that will be pulled */
public NativePagedBytes(int blockBits) {
assert blockBits > 0 && blockBits <= 31 : blockBits;
this.blockSize = 1 << blockBits;
upto = blockSize;
bytesUsedInPrevBlocks = -blockSize; // balance out upto starting at blockSize
}
public long getUsedSize() {
return bytesUsedInPrevBlocks + upto;
}
public void copyUsingLengthPrefix(BytesRef bytes) {
if (bytes.length >= blockSize) {
throw new IllegalArgumentException("max length is " + blockSize + " (got " + bytes.length + ")");
}
if (upto + bytes.length + 2 > blockSize) {
if (bytes.length + 2 > blockSize) {
throw new IllegalArgumentException("block size " + blockSize + " is too small to store length " + bytes.length + " bytes");
}
if (currentBlock != 0) {
blocks.add(currentBlock);
blockEnd.add(upto);
}
bytesUsedInPrevBlocks += upto;
currentBlock = HS.allocArray(blockSize, 1, false);
upto = 0;
}
// TODO: implement in HS for better efficiency?
if (bytes.length < 128) {
HS.setByte(currentBlock, upto, (byte)bytes.length);
upto++;
} else {
HS.setByte(currentBlock, upto, (byte) (0x80 | (bytes.length >> 8)) );
upto++;
HS.setByte(currentBlock, upto, (byte) bytes.length );
upto++;
}
HS.copyBytes(bytes.bytes, bytes.offset, currentBlock, upto, bytes.length);
upto += bytes.length;
}
public long buildSingleArray() {
long sz = getUsedSize();
long arr = HS.allocArray(sz, 1, false);
long pos = 0;
for (int i=0; i<blocks.size(); i++) {
long block = blocks.get(i);
long used = blockEnd.get(i);
HS.copyBytes(block, 0, arr, pos, used);
pos += used;
}
HS.copyBytes(currentBlock, 0, arr, pos, upto);
pos += upto;
assert pos == sz;
return arr;
}
@Override
public void close() throws IOException {
for (Long block : blocks) {
HS.freeArray(block);
}
blocks.clear();
if (currentBlock != 0) {
HS.freeArray(currentBlock);
}
}
// gets the size of the entry, *including* then length
public static int getEntrySize(long arr, long offset) {
int b = HS.getByte(arr, offset);
if (b >= 0) {
return b + 1;
} else {
int b2 = HS.getByte(arr, offset+1) & 0xff;
int len = ((b & 0x7f) << 8) | b2;
return len + 2;
}
}
}