/* * Copyright (C) 2011-2015 Samuel Audet * * Licensed either under the Apache License, Version 2.0, or (at your option) * under the terms of the GNU General Public License as published by * the Free Software Foundation (subject to the "Classpath" exception), * either version 2, or any later version (collectively, 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 * http://www.gnu.org/licenses/ * http://www.gnu.org/software/classpath/license.html * * or as provided in the LICENSE.txt file that accompanied this code. * 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.bytedeco.javacpp; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.net.URL; import java.net.URISyntaxException; import java.util.*; import org.bigbluebutton.screenshare.client.DeskshareMain; import org.bytedeco.javacpp.annotation.Platform; import org.bytedeco.javacpp.tools.Builder; import org.bytedeco.javacpp.tools.Logger; /** * The Loader contains functionality to load native libraries, but also has a bit * of everything that does not fit anywhere else. In addition to its library loading * features, it also has utility methods to get the platform name, to load properties * from Java resources and from Class annotations, to extract file resources to the * temporary directory, and to get the {@code offsetof()} or {@code sizeof()} a native * {@code struct}, {@code class}, or {@code union} with its {@link Pointer} peer class * and a {@link HashMap} initialized by the native libraries inside {@code JNI_OnLoad()}. * * @author Samuel Audet */ public class Loader { private static final Logger logger = Logger.create(Loader.class); /** Value created out of "java.vm.name", "os.name", and "os.arch" system properties. * Returned by {@link #getPlatform()} as default. */ private static final String PLATFORM; /** Default platform properties loaded and returned by {@link #loadProperties()}. */ private static Properties platformProperties = null; static { String jvmName = System.getProperty("java.vm.name", "").toLowerCase(); String osName = System.getProperty("os.name", "").toLowerCase(); String osArch = System.getProperty("os.arch", "").toLowerCase(); String abiType = System.getProperty("sun.arch.abi", "").toLowerCase(); if (jvmName.startsWith("dalvik") && osName.startsWith("linux")) { osName = "android"; } else if (jvmName.startsWith("robovm") && osName.startsWith("darwin")) { osName = "ios"; osArch = "arm"; } else if (osName.startsWith("mac os x") || osName.startsWith("darwin")) { osName = "macosx"; } else { int spaceIndex = osName.indexOf(' '); if (spaceIndex > 0) { osName = osName.substring(0, spaceIndex); } } if (osArch.equals("i386") || osArch.equals("i486") || osArch.equals("i586") || osArch.equals("i686")) { osArch = "x86"; } else if (osArch.equals("amd64") || osArch.equals("x86-64") || osArch.equals("x64")) { osArch = "x86_64"; } else if (osArch.startsWith("aarch64") || osArch.startsWith("armv8") || osArch.startsWith("arm64")) { osArch = "arm64"; } else if ((osArch.startsWith("arm")) && abiType.equals("gnueabihf")) { osArch = "armhf"; } else if (osArch.startsWith("arm")) { osArch = "arm"; } PLATFORM = osName + "-" + osArch; } /** * Returns either the value of the "org.bytedeco.javacpp.platform" * system property, or {@link #PLATFORM} when the former is not set. * * @return {@code System.getProperty("org.bytedeco.javacpp.platform", platform)} * @see #PLATFORM */ public static String getPlatform() { return System.getProperty("org.bytedeco.javacpp.platform", PLATFORM); } /** * Loads the {@link Properties} associated with the default {@link #getPlatform()}. * * @return {@code loadProperties(getPlatform(), null)} * @see #loadProperties(String, String) */ public static Properties loadProperties() { String name = getPlatform(); if (platformProperties != null && name.equals(platformProperties.getProperty("platform"))) { return platformProperties; } return platformProperties = loadProperties(name, null); } /** * Loads from resources the default {@link Properties} of the specified platform name. * The resource must be at {@code "org/bytedeco/javacpp/properties/" + name + ".properties"}. * * @param name the platform name * @param defaults the fallback platform name (null == "generic") * @return the Properties from resources */ public static Properties loadProperties(String name, String defaults) { Properties p = new Properties(); p.put("platform", name); p.put("platform.path.separator", File.pathSeparator); String s = System.mapLibraryName("/"); int i = s.indexOf('/'); p.put("platform.library.prefix", s.substring(0, i)); p.put("platform.library.suffix", s.substring(i + 1)); name = "properties/" + name + ".properties"; InputStream is = Loader.class.getResourceAsStream(name); try { try { p.load(new InputStreamReader(is)); } catch (NoSuchMethodError e) { p.load(is); } } catch (Exception e) { name = "properties/" + defaults + ".properties"; InputStream is2 = Loader.class.getResourceAsStream(name); try { try { p.load(new InputStreamReader(is2)); } catch (NoSuchMethodError e2) { p.load(is2); } } catch (Exception e2) { // give up and return defaults } finally { try { if (is2 != null) { is2.close(); } } catch (IOException ex) { logger.error("Unable to close resource : " + ex.getMessage()); } } } finally { try { if (is != null) { is.close(); } } catch (IOException ex) { logger.error("Unable to close resource : " + ex.getMessage()); } } return p; } /** * If annotated with properties, returns the argument as "enclosing Class". * If no properties are found on the Class, makes a search for the first Class * with properties that we can use, and returns it as the enclosing Class found. * * @param cls the Class to start the search from * @return the enclosing Class * @see org.bytedeco.javacpp.annotation.Platform * @see org.bytedeco.javacpp.annotation.Properties */ public static Class getEnclosingClass(Class cls) { Class<?> c = cls; // Find first enclosing declaring class with some properties to use while (c.getDeclaringClass() != null) { if (c.isAnnotationPresent(org.bytedeco.javacpp.annotation.Properties.class)) { break; } if (c.isAnnotationPresent(Platform.class)) { Platform p = c.getAnnotation(Platform.class); if (p.pragma().length > 0 || p.define().length > 0 || p.include().length > 0 || p.cinclude().length > 0 || p.includepath().length > 0 || p.compiler().length > 0 || p.linkpath().length > 0 || p.link().length > 0 || p.frameworkpath().length > 0 || p.framework().length > 0 || p.preloadpath().length > 0 || p.preload().length > 0 || p.library().length() > 0) { break; } } c = c.getDeclaringClass(); } return c; } /** * For all the classes, loads all properties from each Class annotations for the given platform. * @see #loadProperties(Class, java.util.Properties, boolean) */ public static ClassProperties loadProperties(Class[] cls, Properties properties, boolean inherit) { ClassProperties cp = new ClassProperties(properties); for (Class c : cls) { cp.load(c, inherit); } return cp; } /** * Loads all properties from Class annotations for the given platform. The platform * of interest needs to be specified as the value of the "platform" key in the * properties argument. It is also possible to indicate whether to load all the classes * specified in the {@link org.bytedeco.javacpp.annotation.Properties#inherit()} * annotation recursively via the inherit argument. * * @param cls the Class of which to return Properties * @param properties the platform Properties to inherit * @param inherit indicates whether or not to inherit properties from other classes * @return all the properties associated with the Class for the given platform */ public static ClassProperties loadProperties(Class cls, Properties properties, boolean inherit) { ClassProperties cp = new ClassProperties(properties); cp.load(cls, inherit); return cp; } /** * Returns the {@link Class} object that contains a caller's method. * * @param i the offset on the call stack of the method of interest * @return the Class found from the calling context, or {@code null} if not found */ public static Class getCallerClass(int i) { Class[] classContext = null; try { new SecurityManager() { @Override public Class[] getClassContext() { return super.getClassContext(); } }.getClassContext(); } catch (NoSuchMethodError e) { logger.error("No definition of this method : " + e.getMessage()); } if (classContext != null) { for (int j = 0; j < classContext.length; j++) { if (classContext[j] == Loader.class) { return classContext[i+j]; } } } else { // SecurityManager.getClassContext() returns null on Android 4.0 try { StackTraceElement[] classNames = Thread.currentThread().getStackTrace(); for (int j = 0; j < classNames.length; j++) { if (Class.forName(classNames[j].getClassName()) == Loader.class) { return Class.forName(classNames[i+j].getClassName()); } } } catch (ClassNotFoundException e) { logger.error("No definition for the class found : " + e.getMessage()); } } return null; } /** * Extracts by name a resource using the {@link ClassLoader} of the caller. * * @param name the name of the resource passed to {@link Class#getResource(String)} * @see #extractResource(URL, File, String, String) */ public static File extractResource(String name, File directory, String prefix, String suffix) throws IOException { Class cls = getCallerClass(2); return extractResource(cls, name, directory, prefix, suffix); } /** * Extracts by name a resource using the {@link ClassLoader} of the specified {@link Class}. * * @param cls the Class from which to load resources * @param name the name of the resource passed to {@link Class#getResource(String)} * @see #extractResource(URL, File, String, String) */ public static File extractResource(Class cls, String name, File directory, String prefix, String suffix) throws IOException { return extractResource(cls.getResource(name), directory, prefix, suffix); } /** * Extracts a resource into the specified directory and with the specified * prefix and suffix for the filename. If both prefix and suffix are {@code null}, * the original filename is used, so the directory must not be {@code null}. * * @param resourceURL the URL of the resource to extract * @param directory the output directory ({@code null == System.getProperty("java.io.tmpdir")}) * @param prefix the prefix of the temporary filename to use * @param suffix the suffix of the temporary filename to use * @return the File object representing the extracted file * @throws IOException if fails to extract resource properly */ public static File extractResource(URL resourceURL, File directory, String prefix, String suffix) throws IOException { InputStream is = resourceURL != null ? resourceURL.openStream() : null; OutputStream os = null; if (is == null) { return null; } File file = null; boolean fileExisted = false; try { if (prefix == null && suffix == null) { if (directory == null) { directory = new File(System.getProperty("java.io.tmpdir")); } file = new File(directory, new File(resourceURL.getPath()).getName()); fileExisted = file.exists(); } else { file = File.createTempFile(prefix, suffix, directory); } if (logger.isDebugEnabled()) { logger.debug("Extracting file " + file.getAbsolutePath()); } os = new FileOutputStream(file); byte[] buffer = new byte[1024]; int length; while ((length = is.read(buffer)) != -1) { os.write(buffer, 0, length); } is.close(); os.close(); } catch (IOException e) { if (file != null && !fileExisted) { file.delete(); } throw e; } finally { is.close(); if (os != null) { os.close(); } } return file; } /** User-specified cache directory set and returned by {@link #getCacheDir()}. */ static File cacheDir = null; /** Temporary directory set and returned by {@link #getTempDir()}. */ static File tempDir = null; /** Contains all the native libraries that we have loaded to avoid reloading them. */ static Map<String,String> loadedLibraries = Collections.synchronizedMap(new HashMap<String,String>()); /** * Creates and returns {@code System.getProperty("org.bytedeco.javacpp.cachedir")}, or null when not set. * * @return {@link #cacheDir} */ public static File getCacheDir() { if (cacheDir == null) { String dirName = System.getProperty("org.bytedeco.javacpp.cachedir", null); if (dirName != null) { File f = new File(dirName); if (f.exists() || f.mkdirs()) { cacheDir = f; } } } return cacheDir; } /** * Creates a unique name for {@link #tempDir} out of * {@code System.getProperty("java.io.tmpdir")} and {@code System.nanoTime()}. * * @return {@link #tempDir} */ public static File getTempDir() { if (tempDir == null) { File tmpdir = new File(System.getProperty("java.io.tmpdir")); File f; for (int i = 0; i < 1000; i++) { f = new File(tmpdir, "javacpp" + System.nanoTime()); if (f.mkdir()) { tempDir = f; tempDir.deleteOnExit(); break; } } } if (logger.isDebugEnabled()) { logger.debug("JavaCV temp dir " + tempDir); } return tempDir; } /** Returns {@code System.getProperty("org.bytedeco.javacpp.loadlibraries")}. * Flag set by the {@link Builder} to tell us not to try to load anything. */ public static boolean isLoadLibraries() { String s = System.getProperty("org.bytedeco.javacpp.loadlibraries", "true").toLowerCase(); return s.equals("true") || s.equals("t") || s.equals(""); } /** Returns {@code load(getCallerClass(2), loadProperties(), false)}. */ public static String load() { return load(getCallerClass(2), loadProperties(), false); } /** * Loads native libraries associated with the {@link Class} of the caller. * * @param pathsFirst search the paths first before bundled resources * @return {@code load(getCallerClass(2), loadProperties(), pathsFirst) } * @see #getCallerClass(int) * @see #load(Class, Properties, boolean) */ public static String load(boolean pathsFirst) { Class cls = getCallerClass(2); return load(cls, loadProperties(), pathsFirst); } /** Returns {@code load(cls, loadProperties(), false)}. */ public static String load(Class cls) { return load(cls, loadProperties(), false); } /** * Loads native libraries associated with the given {@link Class}. * * @param cls the Class to get native library information from * @param properties the platform Properties to inherit * @param pathsFirst search the paths first before bundled resources * @return the full path to the main file loaded, or the library name if unknown * (but {@code if (!isLoadLibraries() || cls == null) { return null; }}) * @throws NoClassDefFoundError on Class initialization failure * @throws UnsatisfiedLinkError on native library loading failure * @see #findLibrary(Class, ClassProperties, String, boolean) * @see #loadLibrary(URL[], String) */ public static String load(Class cls, Properties properties, boolean pathsFirst) { if (!isLoadLibraries() || cls == null) { return null; } // Find the top enclosing class, to match the library filename cls = getEnclosingClass(cls); ClassProperties p = loadProperties(cls, properties, true); // Force initialization of all the target classes in case they need it List<String> targets = p.get("target"); if (targets.isEmpty()) { if (p.getInheritedClasses() != null) { for (Class c : p.getInheritedClasses()) { targets.add(c.getName()); } } targets.add(cls.getName()); } Set<String> targetClasses = new HashSet<String>(); for (String s : targets) { targetClasses.add(s); } for (String s : targetClasses) { try { Class.forName(s, true, cls.getClassLoader()); } catch (ClassNotFoundException ex) { if (logger.isDebugEnabled()) { logger.debug("load: Failed to load class " + s + ": " + ex); } Error e = new NoClassDefFoundError(ex.toString()); e.initCause(ex); throw e; } } // Preload native libraries desired by our class List<String> preloads = new ArrayList<String>(); preloads.addAll(p.get("platform.preload")); // Do not include the platform.link into our list of native libs. // This will just add to the number of libs to search increasing // delay on startup. (ralam - june 15, 2016). //preloads.addAll(p.get("platform.link")); UnsatisfiedLinkError preloadError = null; for (String preload : preloads) { try { // Do not try searching for the library. Let the system find the // native library. Searching for it manually prolongs startup time. // (ralam - june 15, 2016) URL[] urls = new URL[0]; //findLibrary(cls, p, preload, pathsFirst); loadLibrary(urls, preload); } catch (UnsatisfiedLinkError e) { preloadError = e; } } try { String library = p.getProperty("platform.library"); // Do not try searching for the library. Let the system find the // native library. Searching for it manually prolongs startup time. // (ralam - june 15, 2016) URL[] urls = new URL[0]; //findLibrary(cls, p, library, pathsFirst); String loadedLibPath = loadLibrary(urls, library); return loadedLibPath; } catch (UnsatisfiedLinkError e) { if (preloadError != null && e.getCause() == null) { e.initCause(preloadError); } throw e; } } /** * Finds from where the library may be extracted and loaded among the {@link Class} * resources. But in case that fails, and depending on the value of {@code pathsFirst}, * either as a fallback or in priority over bundled resources, also searches the paths * found in the "platform.preloadpath" and "platform.linkpath" class properties as well as * the "java.library.path" system property, in that order. * * @param cls the Class whose package name and {@link ClassLoader} are used to extract from resources * @param properties contains the directories to scan for if we fail to extract the library from resources * @param libnameversion the name of the library + "@" + optional version tag * @param pathsFirst search the paths first before bundled resources * @return URLs that point to potential locations of the library */ public static URL[] findLibrary(Class cls, ClassProperties properties, String libnameversion, boolean pathsFirst) { String[] s = libnameversion.split("@"); String libname = s[0]; String version = s.length > 1 ? s[s.length-1] : ""; // If we do not already have the native library file ... String filename = loadedLibraries.get(libnameversion); if (filename != null) { try { return new URL[] { new File(filename).toURI().toURL() }; } catch (IOException ex) { if (logger.isDebugEnabled()) { logger.debug("findLibrary: Could not create URL for library " + libnameversion); } return new URL[] { }; } } String subdir = properties.getProperty("platform") + '/'; String prefix = properties.getProperty("platform.library.prefix", "") + libname; String suffix = properties.getProperty("platform.library.suffix", ""); String[] styles = { prefix + suffix + version, // Linux style prefix + version + suffix, // Mac OS X style prefix + suffix // without version }; String[] suffixes = properties.get("platform.library.suffix").toArray(new String[0]); if (suffixes.length > 1) { styles = new String[3 * suffixes.length]; for (int i = 0; i < suffixes.length; i++) { styles[3 * i ] = prefix + suffixes[i] + version; // Linux style styles[3 * i + 1] = prefix + version + suffixes[i]; // Mac OS X style styles[3 * i + 2] = prefix + suffixes[i]; // without version } } Set<String> stylesStr = new HashSet<String>(); for (int js=0; js < styles.length; js++) { if (logger.isDebugEnabled()) { logger.debug("findLibrary: Style " + styles[js]); } stylesStr.add(styles[js]); } /* List<String> paths = new ArrayList<String>(); paths.addAll(properties.get("platform.preloadpath")); paths.addAll(properties.get("platform.linkpath")); String libpath = System.getProperty("java.library.path", ""); if (logger.isDebugEnabled()) { logger.debug("findLibrary: java.library.path " + libpath); } if (libpath.length() > 0) { paths.addAll(Arrays.asList(libpath.split(File.pathSeparator))); } ArrayList<URL> urls = new ArrayList<URL>(styles.length * (1 + paths.size())); */ if (logger.isDebugEnabled()) { logger.debug("findLibrary: Before get resource"); } String platform = Loader.getPlatform(); Set<URL> urls = new HashSet<URL>(); //for (int i = 0; cls != null && i < styles.length; i++) { for (String st: stylesStr) { // ... then find it from in our resources ... if (logger.isDebugEnabled()) { logger.debug("findLibrary: resource path " + subdir + st); } if (platform.startsWith("ghghghmacosx-x86_64")) { URL u = cls.getResource(subdir + st); if (u != null) { if (logger.isDebugEnabled()) { logger.debug("findLibrary: cls.getResource " + u.toString()); } urls.add(u); } } } if (logger.isDebugEnabled()) { logger.debug("findLibrary: After get resource"); } /* // ... and in case of bad resources search the paths last, or first on user request. int k = pathsFirst ? 0 : urls.size(); for (int i = 0; paths.size() > 0 && i < styles.length; i++) { for (String path : paths) { File file = new File(path, styles[i]); if (logger.isDebugEnabled()) { logger.debug("findLibrary: url path " + path + " style " + styles[i]); } if (file.exists()) { try { if (logger.isDebugEnabled()) { logger.debug("findLibrary: url path " + file.toURI().toURL()); } urls.add(file.toURI().toURL()); //urls.add(k++, file.toURI().toURL()); } catch (IOException ex) { throw new RuntimeException(ex); } } } } */ URL[] libUrls = urls.toArray(new URL[urls.size()]); for (URL url : libUrls) { if (logger.isDebugEnabled()) { logger.debug("findLibrary: Lib URL for " + libnameversion + " " + url); } } return libUrls; } /** * Tries to load the library from the URLs in order, extracting resources as necessary. * Finally, if all fails, falls back on {@link System#loadLibrary(String)}. * * @param urls the URLs to try loading the library from * @param libnameversion the name of the library + "@" + optional version tag * @return the full path of the file loaded, or the library name if unknown * (but {@code if (!isLoadLibraries) { return null; }}) * @throws UnsatisfiedLinkError on failure */ public static String loadLibrary(URL[] urls, String libnameversion) { if (!isLoadLibraries()) { if (logger.isDebugEnabled()) { logger.debug("Not loading " + libnameversion ); } return null; } // If we do not already have the native library file ... String filename = loadedLibraries.get(libnameversion); if (filename != null) { return filename; } File tempFile = null; UnsatisfiedLinkError loadError = null; try { for (URL url : urls) { File file; // ... then check if it has not already been extracted, and if not ... if (!(file = new File(getCacheDir() != null ? getCacheDir() : getTempDir(), new File(url.getPath()).getName())).exists()) { if (tempFile != null && tempFile.exists()) { tempFile.deleteOnExit(); } // ... then extract it from our resources ... if (getCacheDir() != null) { file = extractResource(url, getCacheDir(), null, null); } else { file = tempFile = extractResource(url, getTempDir(), null, null); } } else while (System.currentTimeMillis() - file.lastModified() < 1000) { // ... else wait until the file is at least 1 second old ... try { Thread.sleep(1000); } catch (InterruptedException ex) { // ... reset interrupt to be nice ... Thread.currentThread().interrupt(); } } if (file != null && file.exists()) { filename = file.getAbsolutePath(); try { // ... and load it! loadedLibraries.put(libnameversion, filename); System.load(filename); return filename; } catch (UnsatisfiedLinkError e) { loadError = e; loadedLibraries.remove(libnameversion); if (logger.isDebugEnabled()) { logger.debug("Failed to load " + filename + ": " + e); } } } } // ... or as last resort, try to load it via the system. String libname = libnameversion.split("@")[0]; loadedLibraries.put(libnameversion, libname); System.loadLibrary(libname); return libname; } catch (UnsatisfiedLinkError e) { loadedLibraries.remove(libnameversion); if (loadError != null && e.getCause() == null) { e.initCause(loadError); } throw e; } catch (IOException ex) { loadedLibraries.remove(libnameversion); if (loadError != null && ex.getCause() == null) { ex.initCause(loadError); } Error e = new UnsatisfiedLinkError(ex.toString()); e.initCause(ex); throw e; } finally { if (tempFile != null && tempFile.exists()) { tempFile.deleteOnExit(); } // But under Windows, it won't get deleted! } } // So, let's use a shutdown hook... static { if (getPlatform().startsWith("windows")) { Runtime.getRuntime().addShutdownHook(new Thread() { @Override public void run() { if (tempDir == null) { return; } try { // ... to launch a separate process ... List<String> command = new ArrayList<String>(); command.add(System.getProperty("java.home") + "/bin/java"); command.add("-classpath"); command.add((new File(Loader.class.getProtectionDomain().getCodeSource().getLocation().toURI())).toString()); command.add(Loader.class.getName()); command.add(tempDir.getAbsolutePath()); new ProcessBuilder(command).start(); } catch (IOException e) { throw new RuntimeException(e); } catch (URISyntaxException e) { throw new RuntimeException(e); } } }); } } // ... that makes sure to delete all our files. public static void main(String[] args) throws InterruptedException { File tmpdir = new File(System.getProperty("java.io.tmpdir")); File tempDir = new File(args[0]); if (!tmpdir.equals(tempDir.getParentFile()) || !tempDir.getName().startsWith("javacpp")) { // Someone is trying to break us ... ? return; } for (File file : tempDir.listFiles()) { while (file.exists() && !file.delete()) { Thread.sleep(100); } } tempDir.delete(); } /** * Contains {@code offsetof()} and {@code sizeof()} values of native types * of {@code struct}, {@code class}, and {@code union}. A {@link WeakHashMap} * is used to prevent the Loader from hanging onto Class objects the user may * be trying to unload. */ static WeakHashMap<Class<? extends Pointer>,HashMap<String,Integer>> memberOffsets = new WeakHashMap<Class<? extends Pointer>,HashMap<String,Integer>>(); /** * Called by native libraries to put {@code offsetof()} and {@code sizeof()} values in {@link #memberOffsets}. * Tries to load the Class object for typeName using the {@link ClassLoader} of the Loader. * * @param typeName the name of the peer Class acting as interface to the native type * @param member the name of the native member variable (can be null to retrieve the Class object only) * @param offset the value of {@code offsetof()} (or {@code sizeof()} when {@code member.equals("sizeof")}) * @return {@code Class.forName(typeName, false)} * @throws ClassNotFoundException on Class initialization failure */ static Class putMemberOffset(String typeName, String member, int offset) throws ClassNotFoundException { Class<?> c = Class.forName(typeName.replace('/', '.'), false, Loader.class.getClassLoader()); if (member != null) { putMemberOffset(c.asSubclass(Pointer.class), member, offset); } return c; } /** * Called by native libraries to put {@code offsetof()} and {@code sizeof()} values in {@link #memberOffsets}. * * @param type the peer Class acting as interface to the native type * @param member the name of the native member variable * @param offset the value of {@code offsetof()} (or {@code sizeof()} when {@code member.equals("sizeof")}) */ static synchronized void putMemberOffset(Class<? extends Pointer> type, String member, int offset) { HashMap<String,Integer> offsets = memberOffsets.get(type); if (offsets == null) { memberOffsets.put(type, offsets = new HashMap<String,Integer>()); } offsets.put(member, offset); } /** * Gets {@code offsetof()} values from {@link #memberOffsets} filled by native libraries. * * @param type the peer Class acting as interface to the native type * @param member the name of the native member variable * @return {@code memberOffsets.get(type).get(member)} */ public static int offsetof(Class<? extends Pointer> type, String member) { // Should we synchronize that? return memberOffsets.get(type).get(member); } /** * Gets {@code sizeof()} values from {@link #memberOffsets} filled by native libraries. * * @param type the peer Class acting as interface to the native type * @return {@code memberOffsets.get(type).get("sizeof")} */ public static int sizeof(Class<? extends Pointer> type) { // Should we synchronize that? return memberOffsets.get(type).get("sizeof"); } }