|
| 1 | +package com.twitter.ann.faiss; |
| 2 | + |
| 3 | +import java.io.File; |
| 4 | +import java.io.FileNotFoundException; |
| 5 | +import java.io.IOException; |
| 6 | +import java.io.InputStream; |
| 7 | +import java.nio.file.Files; |
| 8 | +import java.nio.file.StandardCopyOption; |
| 9 | +import java.util.Locale; |
| 10 | + |
| 11 | +public final class NativeUtils { |
| 12 | + |
| 13 | + private static final int MIN_PREFIX_LENGTH = 3; |
| 14 | + public static final String NATIVE_FOLDER_PATH_PREFIX = "nativeutils"; |
| 15 | + |
| 16 | + public static File temporaryDir; |
| 17 | + |
| 18 | + private NativeUtils() { |
| 19 | + } |
| 20 | + |
| 21 | + private static File unpackLibraryFromJarInternal(String path) throws IOException { |
| 22 | + if (null == path || !path.startsWith("/")) { |
| 23 | + throw new IllegalArgumentException("The path has to be absolute (start with '/')."); |
| 24 | + } |
| 25 | + |
| 26 | + String[] parts = path.split("/"); |
| 27 | + String filename = (parts.length > 1) ? parts[parts.length - 1] : null; |
| 28 | + |
| 29 | + if (filename == null || filename.length() < MIN_PREFIX_LENGTH) { |
| 30 | + throw new IllegalArgumentException("The filename has to be at least 3 characters long."); |
| 31 | + } |
| 32 | + |
| 33 | + if (temporaryDir == null) { |
| 34 | + temporaryDir = createTempDirectory(NATIVE_FOLDER_PATH_PREFIX); |
| 35 | + temporaryDir.deleteOnExit(); |
| 36 | + } |
| 37 | + |
| 38 | + File temp = new File(temporaryDir, filename); |
| 39 | + |
| 40 | + try (InputStream is = NativeUtils.class.getResourceAsStream(path)) { |
| 41 | + Files.copy(is, temp.toPath(), StandardCopyOption.REPLACE_EXISTING); |
| 42 | + } catch (IOException e) { |
| 43 | + temp.delete(); |
| 44 | + throw e; |
| 45 | + } catch (NullPointerException e) { |
| 46 | + temp.delete(); |
| 47 | + throw new FileNotFoundException("File " + path + " was not found inside JAR."); |
| 48 | + } |
| 49 | + |
| 50 | + return temp; |
| 51 | + } |
| 52 | + |
| 53 | + /** |
| 54 | + * Unpack library from JAR into temporary path |
| 55 | + * |
| 56 | + * @param path The path of file inside JAR as absolute path (beginning with |
| 57 | + * '/'), e.g. /package/File.ext |
| 58 | + * @throws IOException If temporary file creation or read/write |
| 59 | + * operation fails |
| 60 | + * @throws IllegalArgumentException If source file (param path) does not exist |
| 61 | + * @throws IllegalArgumentException If the path is not absolute or if the |
| 62 | + * filename is shorter than three characters |
| 63 | + * (restriction of |
| 64 | + * {@link File#createTempFile(java.lang.String, java.lang.String)}). |
| 65 | + * @throws FileNotFoundException If the file could not be found inside the |
| 66 | + * JAR. |
| 67 | + */ |
| 68 | + public static void unpackLibraryFromJar(String path) throws IOException { |
| 69 | + unpackLibraryFromJarInternal(path); |
| 70 | + } |
| 71 | + |
| 72 | + /** |
| 73 | + * Loads library from current JAR archive |
| 74 | + * <p> |
| 75 | + * The file from JAR is copied into system temporary directory and then loaded. |
| 76 | + * The temporary file is deleted after |
| 77 | + * exiting. |
| 78 | + * Method uses String as filename because the pathname is "abstract", not |
| 79 | + * system-dependent. |
| 80 | + * |
| 81 | + * @param path The path of file inside JAR as absolute path (beginning with |
| 82 | + * '/'), e.g. /package/File.ext |
| 83 | + * @throws IOException If temporary file creation or read/write |
| 84 | + * operation fails |
| 85 | + * @throws IllegalArgumentException If source file (param path) does not exist |
| 86 | + * @throws IllegalArgumentException If the path is not absolute or if the |
| 87 | + * filename is shorter than three characters |
| 88 | + * (restriction of |
| 89 | + * {@link File#createTempFile(java.lang.String, java.lang.String)}). |
| 90 | + * @throws FileNotFoundException If the file could not be found inside the |
| 91 | + * JAR. |
| 92 | + */ |
| 93 | + public static void loadLibraryFromJar(String path) throws IOException { |
| 94 | + File temp = unpackLibraryFromJarInternal(path); |
| 95 | + |
| 96 | + try (InputStream is = NativeUtils.class.getResourceAsStream(path)) { |
| 97 | + Files.copy(is, temp.toPath(), StandardCopyOption.REPLACE_EXISTING); |
| 98 | + } catch (IOException e) { |
| 99 | + temp.delete(); |
| 100 | + throw e; |
| 101 | + } catch (NullPointerException e) { |
| 102 | + temp.delete(); |
| 103 | + throw new FileNotFoundException("File " + path + " was not found inside JAR."); |
| 104 | + } |
| 105 | + |
| 106 | + try { |
| 107 | + System.load(temp.getAbsolutePath()); |
| 108 | + } finally { |
| 109 | + temp.deleteOnExit(); |
| 110 | + } |
| 111 | + } |
| 112 | + |
| 113 | + private static File createTempDirectory(String prefix) throws IOException { |
| 114 | + String tempDir = System.getProperty("java.io.tmpdir"); |
| 115 | + File generatedDir = new File(tempDir, prefix + System.nanoTime()); |
| 116 | + |
| 117 | + if (!generatedDir.mkdir()) { |
| 118 | + throw new IOException("Failed to create temp directory " + generatedDir.getName()); |
| 119 | + } |
| 120 | + |
| 121 | + return generatedDir; |
| 122 | + } |
| 123 | + |
| 124 | + public enum OSType { |
| 125 | + Windows, MacOS, Linux, Other |
| 126 | + } |
| 127 | + |
| 128 | + protected static OSType detectedOS; |
| 129 | + |
| 130 | + /** |
| 131 | + * detect the operating system from the os.name System property and cache |
| 132 | + * the result |
| 133 | + * |
| 134 | + * @returns - the operating system detected |
| 135 | + */ |
| 136 | + public static OSType getOperatingSystemType() { |
| 137 | + if (detectedOS == null) { |
| 138 | + String osname = System.getProperty("os.name", "generic").toLowerCase(Locale.ENGLISH); |
| 139 | + if ((osname.contains("mac")) || (osname.contains("darwin"))) { |
| 140 | + detectedOS = OSType.MacOS; |
| 141 | + } else if (osname.contains("win")) { |
| 142 | + detectedOS = OSType.Windows; |
| 143 | + } else if (osname.contains("nux")) { |
| 144 | + detectedOS = OSType.Linux; |
| 145 | + } else { |
| 146 | + detectedOS = OSType.Other; |
| 147 | + } |
| 148 | + } |
| 149 | + return detectedOS; |
| 150 | + } |
| 151 | +} |
0 commit comments