org.lwjgl.system.APIUtil Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of org.lwjgl.lwjgl Show documentation
Show all versions of org.lwjgl.lwjgl Show documentation
LWJGL OSGi bundle (Core LWJGL bundle)
The newest version!
/*
* Copyright LWJGL. All rights reserved.
* License terms: https://www.lwjgl.org/license
*/
package org.lwjgl.system;
import org.lwjgl.*;
import org.lwjgl.system.libffi.*;
import org.lwjgl.system.linux.*;
import org.lwjgl.system.macosx.*;
import org.lwjgl.system.windows.*;
import javax.annotation.*;
import java.io.*;
import java.lang.reflect.*;
import java.nio.*;
import java.nio.file.*;
import java.util.*;
import java.util.function.*;
import java.util.regex.*;
import java.util.stream.*;
import static org.lwjgl.system.Checks.*;
import static org.lwjgl.system.MemoryStack.*;
import static org.lwjgl.system.MemoryUtil.wrap;
import static org.lwjgl.system.MemoryUtil.*;
import static org.lwjgl.system.libffi.LibFFI.*;
/**
* Utility class useful to API bindings. [INTERNAL USE ONLY]
*
* Method names in this class are prefixed with {@code api} to avoid ambiguities when used with static imports.
*
* @see Configuration#DEBUG_STREAM
*/
public final class APIUtil {
/**
* The {@link PrintStream} used by LWJGL to print debug information and non-fatal errors. Defaults to {@link System#err} which can be changed with
* {@link Configuration#DEBUG_STREAM}.
*/
public static final PrintStream DEBUG_STREAM = getDebugStream();
private static final Pattern API_VERSION_PATTERN;
static {
String PREFIX = "[^\\d\\n\\r]*";
String VERSION = "(\\d+)[.](\\d+)(?:[.](\\S+))?";
String IMPLEMENTATION = "(?:\\s+(.+?))?\\s*";
API_VERSION_PATTERN = Pattern.compile("^" + PREFIX + VERSION + IMPLEMENTATION + "$", Pattern.DOTALL);
}
@SuppressWarnings({"unchecked", "UseOfSystemOutOrSystemErr"})
private static PrintStream getDebugStream() {
PrintStream debugStream = System.err;
Object state = Configuration.DEBUG_STREAM.get();
if (state instanceof String) {
try {
Supplier factory = (Supplier)Class.forName((String)state).getConstructor().newInstance();
debugStream = factory.get();
} catch (Exception e) {
e.printStackTrace();
}
} else if (state instanceof Supplier>) {
debugStream = ((Supplier)state).get();
} else if (state instanceof PrintStream) {
debugStream = (PrintStream)state;
}
return debugStream;
}
private APIUtil() {
}
/**
* Prints the specified message to the {@link #DEBUG_STREAM} if {@link Checks#DEBUG} is true.
*
* @param msg the message to print
*/
public static void apiLog(@Nullable CharSequence msg) {
if (DEBUG) {
DEBUG_STREAM.print("[LWJGL] ");
DEBUG_STREAM.println(msg);
}
}
public static String apiFindLibrary(String start, String name) {
String libName = Platform.get().mapLibraryName(name);
try (Stream paths = Files.find(
Paths.get(start).toAbsolutePath(),
Integer.MAX_VALUE,
(path, attributes) -> attributes.isRegularFile() && path.getFileName().toString().equals(libName)
)) {
return paths
.findFirst()
.map(Path::toString)
.orElse(name);
} catch (IOException e) {
return name;
}
}
public static SharedLibrary apiCreateLibrary(String name) {
switch (Platform.get()) {
case WINDOWS:
return new WindowsLibrary(name);
case LINUX:
return new LinuxLibrary(name);
case MACOSX:
return MacOSXLibrary.create(name);
default:
throw new IllegalStateException();
}
}
public static long apiGetFunctionAddress(FunctionProvider provider, String functionName) {
long a = provider.getFunctionAddress(functionName);
if (a == NULL) {
requiredFunctionMissing(functionName);
}
return a;
}
private static void requiredFunctionMissing(String functionName) {
if (!Configuration.DISABLE_FUNCTION_CHECKS.get(false)) {
throw new NullPointerException("A required function is missing: " + functionName);
}
}
@Nullable
public static ByteBuffer apiGetMappedBuffer(@Nullable ByteBuffer buffer, long mappedAddress, int capacity) {
if (buffer != null && memAddress(buffer) == mappedAddress && buffer.capacity() == capacity) {
return buffer;
}
return mappedAddress == NULL ? null : wrap(BUFFER_BYTE, mappedAddress, capacity).order(NATIVE_ORDER);
}
public static long apiGetBytes(int elements, int elementShift) {
return (elements & 0xFFFF_FFFFL) << elementShift;
}
public static long apiCheckAllocation(int elements, long bytes, long maxBytes) {
if (DEBUG) {
if (elements < 0) {
throw new IllegalArgumentException("Invalid number of elements");
}
if ((maxBytes + Long.MIN_VALUE) < (bytes + Long.MIN_VALUE)) { // unsigned comparison
throw new IllegalArgumentException("The request allocation is too large");
}
}
return bytes;
}
/** A data class for API versioning information. */
public static class APIVersion implements Comparable {
/** Returns the API major version. */
public final int major;
/** Returns the API minor version. */
public final int minor;
/** Returns the API revision. May be null. */
@Nullable
public final String revision;
/** Returns the API implementation-specific versioning information. May be null. */
@Nullable
public final String implementation;
public APIVersion(int major, int minor) {
this(major, minor, null, null);
}
public APIVersion(int major, int minor, @Nullable String revision, @Nullable String implementation) {
this.major = major;
this.minor = minor;
this.revision = revision;
this.implementation = implementation;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder(16);
sb.append(major).append('.').append(minor);
if (revision != null) {
sb.append('.').append(revision);
}
if (implementation != null) {
sb.append(" (").append(implementation).append(')');
}
return sb.toString();
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof APIVersion)) {
return false;
}
APIVersion that = (APIVersion)o;
return this.major == that.major &&
this.minor == that.major &&
Objects.equals(this.revision, that.revision) &&
Objects.equals(this.implementation, that.implementation);
}
@Override
public int hashCode() {
int result = major;
result = 31 * result + minor;
result = 31 * result + (revision != null ? revision.hashCode() : 0);
result = 31 * result + (implementation != null ? implementation.hashCode() : 0);
return result;
}
@Override
public int compareTo(APIVersion other) {
if (this.major != other.major) {
return Integer.compare(this.major, other.major);
}
if (this.minor != other.minor) {
return Integer.compare(this.minor, other.minor);
}
return 0;
}
}
/**
* Returns the {@link APIVersion} value of the specified option.
*
* @param option the option to query
*/
@Nullable
public static APIVersion apiParseVersion(Configuration> option) {
APIVersion version;
Object state = option.get();
if (state instanceof String) {
version = apiParseVersion((String)state);
} else if (state instanceof APIVersion) {
version = (APIVersion)state;
} else {
version = null;
}
return version;
}
/**
* Parses a version string.
*
* The version string must have the format {@code PREFIX MAJOR.MINOR.REVISION IMPL}, where {@code PREFIX} is a prefix without digits (string, optional),
* {@code MAJOR} is the major version (integer), {@code MINOR} is the minor version (integer), {@code REVISION} is the revision version (string, optional)
* and {@code IMPL} is implementation-specific information (string, optional).
*
* @param version the version string
*
* @return the parsed {@link APIVersion}
*/
public static APIVersion apiParseVersion(String version) {
Matcher matcher = API_VERSION_PATTERN.matcher(version);
if (!matcher.matches()) {
throw new IllegalArgumentException(String.format("Malformed API version string [%s]", version));
}
return new APIVersion(
Integer.parseInt(matcher.group(1)),
Integer.parseInt(matcher.group(2)),
matcher.group(3),
matcher.group(4)
);
}
public static void apiFilterExtensions(Set extensions, Configuration