/*
* Copyright (C) 2014 Indeed Inc.
*
* Licensed 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 com.indeed.flamdex.simple;
import com.google.common.io.ByteStreams;
import org.apache.log4j.Logger;
import sun.misc.Unsafe;
import java.io.Closeable;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Field;
/**
* @author jplaisance
*/
public final class NativeDocIdBuffer implements Closeable {
private static final Logger log = Logger.getLogger(NativeDocIdBuffer.class);
private static final Unsafe UNSAFE;
private static final long INT_ARRAY_BASE_OFFSET;
private static final int BUFFER_LENGTH = 512;
private static final boolean useSSSE3;
static {
useSSSE3 = "true".equalsIgnoreCase(System.getProperty("com.indeed.flamdex.simple.useSSSE3"));
loadNativeLibrary();
nativeInit();
log.info("libvarint loaded");
if (useSSSE3) log.info("using SSSE3! (if the processor in this computer doesn't support SSSE3 this process will fail with SIGILL)");
}
static {
try {
final Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
UNSAFE = (Unsafe)theUnsafe.get(null);
INT_ARRAY_BASE_OFFSET = UNSAFE.arrayBaseOffset(int[].class);
} catch (NoSuchFieldException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
static void loadNativeLibrary() {
try {
final String osName = System.getProperty("os.name");
final String arch = System.getProperty("os.arch");
final String resourcePath = "/native/" + osName + "-" + arch + "/libvarint.so.1.0.1";
final InputStream is = NativeDocIdStream.class.getResourceAsStream(resourcePath);
if (is == null) {
throw new FileNotFoundException("unable to find libvarint.so.1.0.1 at resource path "+resourcePath);
}
final File tempFile = File.createTempFile("libvarint", ".so");
final OutputStream os = new FileOutputStream(tempFile);
ByteStreams.copy(is, os);
os.close();
is.close();
System.load(tempFile.getAbsolutePath());
// noinspection ResultOfMethodCallIgnored
tempFile.delete();
} catch (Throwable e) {
log.warn("unable to load libvarint using class loader, looking in java.library.path", e);
System.loadLibrary("varint"); // if this fails it throws UnsatisfiedLinkError
}
}
private final long bufAddress;
private int bufIndex;
private int bufLen;
private long position;
private long docsRemaining;
NativeDocIdBuffer() {
bufAddress = UNSAFE.allocateMemory(4 * BUFFER_LENGTH);
}
public void reset(long newPosition, long newDocsRemaining) {
position = newPosition;
docsRemaining = newDocsRemaining;
// to force a refill
bufIndex = 0;
bufLen = 0;
}
public int fillDocIdBuffer(int[] docIdBuffer, int limit) {
int off = 0;
do {
if (bufLen == bufIndex){
if (docsRemaining == 0) {
break;
}
readInts();
}
final int n = Math.min(limit-off, bufLen-bufIndex);
UNSAFE.copyMemory(null, bufAddress + bufIndex * 4, docIdBuffer, INT_ARRAY_BASE_OFFSET + off * 4, n * 4);
off += n;
bufIndex += n;
} while (off < limit);
return off;
}
private void readInts() {
final int length = (int)Math.min(docsRemaining, BUFFER_LENGTH);
if (useSSSE3) {
position += readInts(position, bufAddress, length);
} else {
position += readIntsSingle(position, bufAddress, length);
}
docsRemaining -= length;
bufIndex = 0;
bufLen = length;
}
@Override
public void close() throws IOException {
UNSAFE.freeMemory(bufAddress);
}
private static native long readInts(long bytesAddr, long intsAddr, int length);
private static native long readIntsSingle(long bytesAddr, long intsAddr, int length);
private static native void nativeInit();
}