This is an automated email from the ASF dual-hosted git repository. desruisseaux pushed a commit to branch geoapi-4.0 in repository https://gitbox.apache.org/repos/asf/sis.git
commit 4b389265a72ef36b9d97a13865a41664a9df3413 Author: Martin Desruisseaux <martin.desruisse...@geomatys.com> AuthorDate: Sat Sep 21 12:08:52 2024 +0200 Simplify fetching `MethodHandle` by storing the linker as a `NativeFunctions` field. Make the method handles for `move` and `delete` operations optional. --- .../main/org/apache/sis/storage/gdal/Driver.java | 152 ++++++++++++--------- .../main/org/apache/sis/storage/gdal/GDAL.java | 128 +++++++---------- .../apache/sis/storage/gdal/GDALStoreProvider.java | 13 +- .../main/org/apache/sis/storage/gdal/Opener.java | 12 +- .../apache/sis/storage/panama/NativeFunctions.java | 27 ++-- .../main/org/apache/sis/storage/gsf/GSF.java | 68 +++++---- 6 files changed, 197 insertions(+), 203 deletions(-) diff --git a/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/Driver.java b/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/Driver.java index 84cbda2c3b..ffa4d9c3d2 100644 --- a/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/Driver.java +++ b/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/Driver.java @@ -18,13 +18,14 @@ package org.apache.sis.storage.gdal; import java.util.List; import java.util.AbstractList; +import java.util.Arrays; +import java.util.Optional; import java.util.Objects; import java.net.URI; import java.nio.file.Path; import java.io.IOException; import java.lang.foreign.Arena; import java.lang.foreign.FunctionDescriptor; -import java.lang.foreign.Linker; import java.lang.foreign.MemorySegment; import java.lang.foreign.ValueLayout; import java.lang.invoke.MethodHandle; @@ -171,15 +172,35 @@ public final class Driver { /** * List of files of a <abbr>GDAL</abbr> data set, together with methods for copying or deleting them. + * The list of files is fetched in advance by the caller because the list needs to stay available after + * {@link GDALStore} has been closed. This class overrides the {@link #copy(Path)} and {@link #delete()} + * methods for delegating the operation to <abbr>GDAL</abbr> when possible. If not possible, this class + * fallbacks on the default Java implementation. */ final class FileList extends Resource.FileSet { - /** Location of the data store as a path. */ + /** + * Location of the data store as a path. It should be the first element of {@link #getPaths()}, + * but is stored anyway because we have no guarantee that <abbr>GDAL</abbr> includes that file + * or puts it first in its list of files. May be {@code null} if not available. + * + * @see GDALStore#path + */ private final Path path; - /** Location of the data store as a URL. */ + /** + * Location of the data store as a <abbr>URL</abbr>. Should never be null. + * + * @see GDALStore#location + */ private final URI location; - /** Creates a new file set for the given files. */ + /** + * Creates a new {@code FileSet} for the given files. + * + * @param paths the files to be returned by {@link #getPaths()}. + * @param path the main file, or {@code null} if not available. + * @param location location of the data store as a <abbr>URL</abbr>. + */ FileList(final Path[] paths, final Path path, final URI location) { super(paths); this.path = path; @@ -187,81 +208,77 @@ public final class Driver { } /** - * Returns the URL (<abbr>GDAL</abbr> syntax) to the data set. - */ - private String getURL() { - return Opener.toURL(location, path, true); - } - - /** - * Copies the files to the given directory. The source should be an <abbr>URL</abbr> recognized - * by <abbr>GDAL</abbr> because {@code FileList} can be returned only by {@link GDALStore}. - * However, the destination could be a pure-Java file system. + * Copies the files to the given directory. The source should be a <abbr>URL</abbr> recognized + * by <abbr>GDAL</abbr> because this {@code FileList} can be returned only by {@link GDALStore}. + * However, the destination directory given in argument to this method may be a Java file system. + * In the latter case, this method delegates on the Java implementation instead of <abbr>GDAL</abbr>. */ @Override public Path copy(final Path destDir) throws IOException { - final Path dest = destDir.resolve(path.getFileName()); - final String target = Opener.toURL(dest.toUri(), dest, false); - if (target != null) try { - copyDataSet(getURL(), target); - return dest; - } catch (DataStoreException e) { - throw new IOException(e); - } else { - return super.copy(destDir); + final Path file = destDir.resolve(path.getFileName()); + final String target = Opener.toURL(file.toUri(), file, false); + if (target != null && invoke("copy", "GDALCopyDatasetFiles", target)) { + return file; } + return super.copy(destDir); } /** - * Deletes the files of the data set. + * Deletes the files of the data set. This method delegates to <abbr>GDAL</abbr>, + * but fallbacks on Java code if the <abbr>GDAL</abbr> function fails. */ @Override public void delete() throws IOException { - try { - deleteDataSet(getURL()); - } catch (DataStoreException e) { - throw new IOException(e); + if (!invoke("delete", "GDALDeleteDataset", null)) { + super.delete(); } } - } - /** - * Copies the files of a data set managed by this driver. - * It is caller responsibility to ensure that this driver is the correct one for the data set to copy. - * No {@link GDALStore} should be opened on the source at the time that this method is invoked. - * - * @param source path or <abbr>URL</abbr> (<abbr>GDAL</abbr> syntax) of the main file of the data set to copy. - * @param target path or <abbr>URL</abbr> (<abbr>GDAL</abbr> syntax) of the desired target file. - * @throws DataStoreException if an error occurred while invoking a <abbr>GDAL</abbr> function. - */ - public void copyDataSet(final String source, final String target) throws DataStoreException { - final int err; - final var gdal = owner.GDAL().copyDataset; - try (var arena = Arena.ofConfined()) { - err = (int) gdal.invokeExact(handle, arena.allocateFrom(target), arena.allocateFrom(source)); - } catch (Throwable e) { - throw GDAL.propagate(e); - } - ErrorHandler.checkCPLErr(err); - } - - /** - * Deletes the files of a data set managed by this driver. - * It is caller responsibility to ensure that this driver is the correct one for the data set to delete. - * No {@link GDALStore} should be opened on the data set when this method is invoked. - * - * @param dataset path or <abbr>URL</abbr> (<abbr>GDAL</abbr> syntax) of the main file of the data set to delete. - * @throws DataStoreException if an error occurred while invoking a <abbr>GDAL</abbr> function. - */ - public void deleteDataSet(final String dataset) throws DataStoreException { - final int err; - final var gdal = owner.GDAL().deleteDataset; - try (var arena = Arena.ofConfined()) { - err = (int) gdal.invokeExact(handle, arena.allocateFrom(dataset)); - } catch (Throwable e) { - throw GDAL.propagate(e); + /** + * Optionally invokes the <abbr>GDAL</abbr> function for copying or deleting a data set. + * We fetch the method handle in this method rather than in the {@link GDAL} class + * because those methods should be rarely needed, and also because they are optional. + * The <abbr>GDAL</abbr> functions handled by this method are: + * <ul> + * <li>{@code CPLErr GDALCopyDatasetFiles(GDALDriverH, const char *pszNewName, const char *pszOldName)}</li> + * <li>{@code CPLErr GDALDeleteDataset(GDALDriverH, const char*)}</li> + * </ul> + * + * @param caller name of the Java method invoking this method, for logging purpose only. + * @param function name of the <abbr>GDAL</abbr> function to invoke. + * @param target the target directory if invoking the copy function, or null for the delete function. + * @return whether the operation has been successful. + */ + @SuppressWarnings("restricted") + private boolean invoke(final String caller, final String function, final String target) { + Optional<MethodHandle> method = owner.tryGDAL(Driver.class, caller).flatMap((gdal) -> { + return gdal.symbols.find(function).map((address) -> { + var signature = new ValueLayout[(target != null) ? 3 : 2]; + Arrays.fill(signature, ValueLayout.ADDRESS); + return gdal.linker.downcallHandle(address, FunctionDescriptor.of(ValueLayout.JAVA_INT, signature)); + }); + }); + if (method.isEmpty()) { + return false; + } + /* + * In both "GDALCopyDatasetFiles" and "GDALDeleteDataset" function, the driver + * handle is the first argument and the dataset URL is the last argument. + */ + final String url = Opener.toURL(location, path, true); + final int err; + try (var arena = Arena.ofConfined()) { + final var source = arena.allocateFrom(url); + if (target != null) { + err = (int) method.get().invokeExact(handle, arena.allocateFrom(target), source); + } else { + err = (int) method.get().invokeExact(handle, source); + } + } catch (Throwable e) { + throw GDAL.propagate(e); + } + return err == 0; } - ErrorHandler.checkCPLErr(err); } /** @@ -313,11 +330,10 @@ public final class Driver { if (gdal == null || (address = gdal.symbols.find("GDALGetDriver").orElse(null)) == null) { return List.of(); } - final Linker linker = Linker.nativeLinker(); @SuppressWarnings("restricted") - final MethodHandle getDriver = linker.downcallHandle(address, + final MethodHandle getDriver = gdal.linker.downcallHandle(address, FunctionDescriptor.of(ValueLayout.ADDRESS, ValueLayout.JAVA_INT)); - final int count = gdal.invokeGetInt(linker, "GDALGetDriverCount").orElse(0); + final int count = gdal.invokeGetInt("GDALGetDriverCount").orElse(0); return new AbstractList<Driver>() { /** Returns the number of drivers. */ @Override public int size() { diff --git a/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/GDAL.java b/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/GDAL.java index a8a6d2ee6e..89ad84b4ba 100644 --- a/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/GDAL.java +++ b/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/GDAL.java @@ -24,7 +24,6 @@ import java.util.NoSuchElementException; import java.util.logging.Level; import java.util.logging.LogRecord; import java.lang.foreign.Arena; -import java.lang.foreign.Linker; import java.lang.foreign.ValueLayout; import java.lang.foreign.SymbolLookup; import java.lang.foreign.MemorySegment; @@ -93,18 +92,6 @@ final class GDAL extends NativeFunctions { */ final MethodHandle identifyDriver; - /** - * <abbr>GDAL</abbr> {@code CPLErr GDALCopyDatasetFiles(GDALDriverH, const char *pszNewName, const char *pszOldName)}. - * Copy the files of a dataset. - */ - final MethodHandle copyDataset; - - /** - * <abbr>GDAL</abbr> {@code CPLErr GDALDeleteDataset(GDALDriverH, const char*)}. - * Delete named dataset. - */ - final MethodHandle deleteDataset; - /** * <abbr>GDAL</abbr> {@code GDALDatasetH GDALOpenEx(const char *pszFilename, …)}. * Opens a raster or vector file by invoking the open method of each driver in turn. @@ -299,37 +286,25 @@ final class GDAL extends NativeFunctions { final var acceptPointerReturnInt = FunctionDescriptor.of(ValueLayout.JAVA_INT, ValueLayout.ADDRESS); final var acceptTwoPtrsReturnDouble = FunctionDescriptor.of(ValueLayout.JAVA_DOUBLE, ValueLayout.ADDRESS, ValueLayout.ADDRESS); final var acceptTwoPtrsReturnPointer = FunctionDescriptor.of(ValueLayout.ADDRESS, ValueLayout.ADDRESS, ValueLayout.ADDRESS); - final Linker linker = Linker.nativeLinker(); // Memory management - free = lookup(linker, "VSIFree", FunctionDescriptor.ofVoid(ValueLayout.ADDRESS)); - destroy = lookup(linker, "CSLDestroy", FunctionDescriptor.ofVoid(ValueLayout.ADDRESS)); + free = lookup("VSIFree", FunctionDescriptor.ofVoid(ValueLayout.ADDRESS)); + destroy = lookup("CSLDestroy", FunctionDescriptor.ofVoid(ValueLayout.ADDRESS)); // For Driver and/or all major objects - identifyDriver = lookup(linker, "GDALIdentifyDriver", acceptTwoPtrsReturnPointer); - getName = lookup(linker, "GDALGetDriverLongName", acceptPointerReturnPointer); - getIdentifier = lookup(linker, "GDALGetDriverShortName", acceptPointerReturnPointer); - getMetadata = lookup(linker, "GDALGetMetadata", acceptTwoPtrsReturnPointer); - getMetadataItem = lookup(linker, "GDALGetMetadataItem", FunctionDescriptor.of( + identifyDriver = lookup("GDALIdentifyDriver", acceptTwoPtrsReturnPointer); + getName = lookup("GDALGetDriverLongName", acceptPointerReturnPointer); + getIdentifier = lookup("GDALGetDriverShortName", acceptPointerReturnPointer); + getMetadata = lookup("GDALGetMetadata", acceptTwoPtrsReturnPointer); + getMetadataItem = lookup("GDALGetMetadataItem", FunctionDescriptor.of( ValueLayout.ADDRESS, // const char* (return type) ValueLayout.ADDRESS, // GDALMajorObject ValueLayout.ADDRESS, // const char* name ValueLayout.ADDRESS)); // const char* domain - copyDataset = lookup(linker, "GDALCopyDatasetFiles", FunctionDescriptor.of( - ValueLayout.JAVA_INT, // CPLErr (return type) - ValueLayout.ADDRESS, // GDALDriverH - ValueLayout.ADDRESS, // const char* pszNewName - ValueLayout.ADDRESS)); // const char* pszOldName - - deleteDataset = lookup(linker, "GDALDeleteDataset", FunctionDescriptor.of( - ValueLayout.JAVA_INT, // CPLErr (return type) - ValueLayout.ADDRESS, // GDALDriverH - ValueLayout.ADDRESS)); // const char* pszName - // For Opener - close = lookup(linker, "GDALClose", acceptPointerReturnInt); - open = lookup(linker, "GDALOpenEx", FunctionDescriptor.of(ValueLayout.ADDRESS, + close = lookup("GDALClose", acceptPointerReturnInt); + open = lookup("GDALOpenEx", FunctionDescriptor.of(ValueLayout.ADDRESS, ValueLayout.ADDRESS, // const char *pszFilename ValueLayout.JAVA_INT, // unsigned int nOpenFlags ValueLayout.ADDRESS, // const char *const *papszAllowedDrivers @@ -337,60 +312,60 @@ final class GDAL extends NativeFunctions { ValueLayout.ADDRESS)); // const char *const *papszSiblingFiles // For all data sets - getFileList = lookup(linker, "GDALGetFileList", acceptPointerReturnPointer); - getDatasetDriver = lookup(linker, "GDALGetDatasetDriver", acceptPointerReturnPointer); - getSpatialRef = lookup(linker, "GDALGetSpatialRef", acceptPointerReturnPointer); - getGCPSpatialRef = lookup(linker, "GDALGetGCPSpatialRef", acceptPointerReturnPointer); - exportToWkt = lookup(linker, "OSRExportToWktEx", FunctionDescriptor.of( + getFileList = lookup("GDALGetFileList", acceptPointerReturnPointer); + getDatasetDriver = lookup("GDALGetDatasetDriver", acceptPointerReturnPointer); + getSpatialRef = lookup("GDALGetSpatialRef", acceptPointerReturnPointer); + getGCPSpatialRef = lookup("GDALGetGCPSpatialRef", acceptPointerReturnPointer); + exportToWkt = lookup("OSRExportToWktEx", FunctionDescriptor.of( ValueLayout.JAVA_INT, // CPLErr error code (return value) ValueLayout.ADDRESS, // OGRSpatialReferenceH ValueLayout.ADDRESS, // char **ppszWKT ValueLayout.ADDRESS)); // const char *const *papszOptions - getDataAxisToCRSAxis = lookup(linker, "OSRGetDataAxisToSRSAxisMapping", acceptTwoPtrsReturnPointer); + getDataAxisToCRSAxis = lookup("OSRGetDataAxisToSRSAxisMapping", acceptTwoPtrsReturnPointer); // For TiledResource (GDAL Raster) - getGeoTransform = lookup(linker, "GDALGetGeoTransform", FunctionDescriptor.of( + getGeoTransform = lookup("GDALGetGeoTransform", FunctionDescriptor.of( ValueLayout.JAVA_INT, // CPLErr error code (return value) ValueLayout.ADDRESS, // GDALDataSetH dataset ValueLayout.ADDRESS)); // double *padfTransform - getRasterXSize = lookup(linker, "GDALGetRasterXSize", acceptPointerReturnInt); - getRasterYSize = lookup(linker, "GDALGetRasterYSize", acceptPointerReturnInt); - getRasterCount = lookup(linker, "GDALGetRasterCount", acceptPointerReturnInt); - getRasterBandXSize = lookup(linker, "GDALGetRasterBandXSize", acceptPointerReturnInt); - getRasterBandYSize = lookup(linker, "GDALGetRasterBandYSize", acceptPointerReturnInt); - getRasterBand = lookup(linker, "GDALGetRasterBand", FunctionDescriptor.of( + getRasterXSize = lookup("GDALGetRasterXSize", acceptPointerReturnInt); + getRasterYSize = lookup("GDALGetRasterYSize", acceptPointerReturnInt); + getRasterCount = lookup("GDALGetRasterCount", acceptPointerReturnInt); + getRasterBandXSize = lookup("GDALGetRasterBandXSize", acceptPointerReturnInt); + getRasterBandYSize = lookup("GDALGetRasterBandYSize", acceptPointerReturnInt); + getRasterBand = lookup("GDALGetRasterBand", FunctionDescriptor.of( ValueLayout.ADDRESS, // GDALRasterBandH (return value) ValueLayout.ADDRESS, // GDALDataSetH dataset ValueLayout.JAVA_INT)); // Band index, starting with 1. // For Band - getBandNumber = lookup(linker, "GDALGetBandNumber", acceptPointerReturnInt); - getColorInterpretation = lookup(linker, "GDALGetRasterColorInterpretation", acceptPointerReturnInt); - getRasterDataType = lookup(linker, "GDALGetRasterDataType", acceptPointerReturnInt); - getRasterMinimum = lookup(linker, "GDALGetRasterMinimum", acceptTwoPtrsReturnDouble); - getRasterMaximum = lookup(linker, "GDALGetRasterMaximum", acceptTwoPtrsReturnDouble); - getRasterNoDataValue = lookup(linker, "GDALGetRasterNoDataValue", acceptTwoPtrsReturnDouble); - getRasterScale = lookup(linker, "GDALGetRasterScale", acceptTwoPtrsReturnDouble); - getRasterOffset = lookup(linker, "GDALGetRasterOffset", acceptTwoPtrsReturnDouble); - getRasterUnitType = lookup(linker, "GDALGetRasterUnitType", acceptPointerReturnPointer); - getRasterCategoryNames = lookup(linker, "GDALGetRasterCategoryNames", acceptPointerReturnPointer); - getRasterColorTable = lookup(linker, "GDALGetRasterColorTable", acceptPointerReturnPointer); - getColorEntryCount = lookup(linker, "GDALGetColorEntryCount", acceptPointerReturnInt); - getColorEntryAsRGB = lookup(linker, "GDALGetColorEntryAsRGB", FunctionDescriptor.of( + getBandNumber = lookup("GDALGetBandNumber", acceptPointerReturnInt); + getColorInterpretation = lookup("GDALGetRasterColorInterpretation", acceptPointerReturnInt); + getRasterDataType = lookup("GDALGetRasterDataType", acceptPointerReturnInt); + getRasterMinimum = lookup("GDALGetRasterMinimum", acceptTwoPtrsReturnDouble); + getRasterMaximum = lookup("GDALGetRasterMaximum", acceptTwoPtrsReturnDouble); + getRasterNoDataValue = lookup("GDALGetRasterNoDataValue", acceptTwoPtrsReturnDouble); + getRasterScale = lookup("GDALGetRasterScale", acceptTwoPtrsReturnDouble); + getRasterOffset = lookup("GDALGetRasterOffset", acceptTwoPtrsReturnDouble); + getRasterUnitType = lookup("GDALGetRasterUnitType", acceptPointerReturnPointer); + getRasterCategoryNames = lookup("GDALGetRasterCategoryNames", acceptPointerReturnPointer); + getRasterColorTable = lookup("GDALGetRasterColorTable", acceptPointerReturnPointer); + getColorEntryCount = lookup("GDALGetColorEntryCount", acceptPointerReturnInt); + getColorEntryAsRGB = lookup("GDALGetColorEntryAsRGB", FunctionDescriptor.of( ValueLayout.JAVA_INT, ValueLayout.ADDRESS, ValueLayout.JAVA_INT, ValueLayout.ADDRESS)); // Band I/O - getBlockSize = lookup(linker, "GDALGetBlockSize", FunctionDescriptor.ofVoid( + getBlockSize = lookup("GDALGetBlockSize", FunctionDescriptor.ofVoid( ValueLayout.ADDRESS, // GDALRasterBandH ValueLayout.ADDRESS, // int *pnXSize ValueLayout.ADDRESS)); // int *pnYSize - rasterIO = lookup(linker, "GDALRasterIO", FunctionDescriptor.of( + rasterIO = lookup("GDALRasterIO", FunctionDescriptor.of( ValueLayout.JAVA_INT, // CPLErr error code (return value) ValueLayout.ADDRESS, // GDALRasterBandH hRBand ValueLayout.JAVA_INT, // GDALRWFlag eRWFlag @@ -405,7 +380,7 @@ final class GDAL extends NativeFunctions { ValueLayout.JAVA_INT, // int nPixelSpace ValueLayout.JAVA_INT)); // int nLineSpace - adviseRead = lookup(linker, "GDALRasterAdviseRead", FunctionDescriptor.of( + adviseRead = lookup("GDALRasterAdviseRead", FunctionDescriptor.of( ValueLayout.JAVA_INT, // CPLErr error code (return value) ValueLayout.ADDRESS, // GDALRasterBandH hRBand ValueLayout.JAVA_INT, // int nDSXOff @@ -418,11 +393,11 @@ final class GDAL extends NativeFunctions { ValueLayout.ADDRESS)); // CSLConstList papszOptions // Set error handling first in order to redirect initialization warnings. - setErrorHandler(linker, null); + setErrorHandler(null); // Initialize GDAL after we found all functions. if (!invoke("GDALAllRegister")) { - log("open", new LogRecord(Level.WARNING, "Could not initialize GDAL.")); + log(GDAL.class, "<init>", new LogRecord(Level.WARNING, "Could not initialize GDAL.")); } } @@ -446,7 +421,7 @@ final class GDAL extends NativeFunctions { if (global != null) { if (GDALStoreProvider.LOGGER.isLoggable(Level.CONFIG)) { global.version("--version").ifPresent((version) -> { - log("open", new LogRecord(Level.CONFIG, version)); + log(GDAL.class, "<init>", new LogRecord(Level.CONFIG, version)); }); } } @@ -492,12 +467,13 @@ final class GDAL extends NativeFunctions { /** * Same as {@link #global}, but logs a warning instead of throwing an exception in case of error. * - * @param caller the name of the method which is invoking this method. + * @param classe the class which is invoking this method (for logging purpose). + * @param method the name of the method which is invoking this method (for logging purpose). * @return handles to native functions needed by this module, or empty if not available. */ - static synchronized Optional<GDAL> tryGlobal(final String caller) { + static synchronized Optional<GDAL> tryGlobal(final Class<?> classe, final String method) { if (globalStatus == null) { - load(true).getError(GDALStoreProvider.NAME).ifPresent((record) -> log(caller, record)); + load(true).getError(GDALStoreProvider.NAME).ifPresent((record) -> log(classe, method, record)); } return Optional.ofNullable(global); } @@ -510,7 +486,6 @@ final class GDAL extends NativeFunctions { * <p><b>The error handler is valid only during the lifetime of the {@linkplain #arena() arena}.</b> * The error handle shall be uninstalled before the arena is closed.</p> * - * @param linker the linker to use. Should be {@link Linker#nativeLinker()}. * @param target the function to set as an error handler, or {@link MemorySegment#NULL} for the GDAL default. * If {@code null}, the function handle will be created by this method. * @return the previous error handler, or {@link MemorySegment#NULL} it it was the <abbr>GDAL</abbr> default. @@ -519,7 +494,7 @@ final class GDAL extends NativeFunctions { * @see GDALStoreProvider#fatalError() */ @SuppressWarnings("restricted") - private MemorySegment setErrorHandler(final Linker linker, MemorySegment target) { + private MemorySegment setErrorHandler(MemorySegment target) { final MemorySegment setter = symbols.find("CPLSetErrorHandler").orElse(null); if (setter == null) { return MemorySegment.NULL; @@ -540,11 +515,12 @@ final class GDAL extends NativeFunctions { /** * Logs the given record as if was produced by the {@link GDALStoreProvider}, which is the public class. * - * @param caller the method name to report as the caller. + * @param classe the class which is invoking this method (for logging purpose). + * @param method the name of the method which is invoking this method (for logging purpose). * @param record the error to log. */ - private static void log(final String caller, final LogRecord record) { - Logging.completeAndLog(GDALStoreProvider.LOGGER, GDALStoreProvider.class, caller, record); + private static void log(final Class<?> classe, final String method, final LogRecord record) { + Logging.completeAndLog(GDALStoreProvider.LOGGER, classe, method, record); } /** @@ -561,7 +537,7 @@ final class GDAL extends NativeFunctions { * @return the <abbr>GDAL</abbr> version, or a message saying that the library was not found. */ final Optional<String> version(final String request) { - return invokeGetString(Linker.nativeLinker(), "GDALVersionInfo", request); + return invokeGetString("GDALVersionInfo", request); } /** @@ -618,7 +594,7 @@ final class GDAL extends NativeFunctions { public void run() { try { // Clear the error handler because the arena will be closed. - setErrorHandler(Linker.nativeLinker(), MemorySegment.NULL); + setErrorHandler(MemorySegment.NULL); invoke("GDALDestroy"); } finally { super.run(); diff --git a/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/GDALStoreProvider.java b/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/GDALStoreProvider.java index ff505bb631..ed6e781348 100644 --- a/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/GDALStoreProvider.java +++ b/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/GDALStoreProvider.java @@ -154,12 +154,13 @@ public class GDALStoreProvider extends DataStoreProvider { * Tries to load <abbr>GDAL</abbr> if not already done, without throwing an exception in case of error. * Instead, the error is logged and {@code null} is returned. This is used for probing. * - * @param caller the name of the method which is invoking this method. + * @param classe the class which is invoking this method (for logging purpose). + * @param method the name of the method which is invoking this method (for logging purpose). * @return the set of native functions, or {@code null} if not available. */ - final synchronized Optional<GDAL> tryGDAL(final String caller) { + final synchronized Optional<GDAL> tryGDAL(final Class<?> classe, final String method) { if (status == null) { - return GDAL.tryGlobal(caller); + return GDAL.tryGlobal(classe, method); } return Optional.ofNullable(nativeFunctions); } @@ -196,7 +197,7 @@ public class GDALStoreProvider extends DataStoreProvider { * @return the version information computed from the string provided by GDAL. */ private <V> Optional<V> version(final String caller, final String request, final Function<String, V> mapper) { - return tryGDAL(caller).flatMap((gdal) -> gdal.version(request)).map(mapper); + return tryGDAL(GDALStoreProvider.class, caller).flatMap((gdal) -> gdal.version(request)).map(mapper); } /** @@ -237,7 +238,7 @@ public class GDALStoreProvider extends DataStoreProvider { * @return all <abbr>GDAL</abbr> drivers, or an empty list if the <abbr>GDAL</abbr> library has not been found. */ public List<Driver> getDrivers() { - return Driver.list(this, tryGDAL("getDrivers").orElse(null)); + return Driver.list(this, tryGDAL(GDALStoreProvider.class, "getDrivers").orElse(null)); } /** @@ -257,7 +258,7 @@ public class GDALStoreProvider extends DataStoreProvider { * @throws DataStoreException if an error occurred while querying <abbr>GDAL</abbr> metadata. */ public TreeTable configuration() throws DataStoreException { - return Driver.configuration(this, tryGDAL("configuration").orElse(null)); + return Driver.configuration(this, tryGDAL(GDALStoreProvider.class, "configuration").orElse(null)); } /** diff --git a/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/Opener.java b/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/Opener.java index 642e30d89d..45e4314a30 100644 --- a/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/Opener.java +++ b/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/Opener.java @@ -155,14 +155,14 @@ final class Opener implements Runnable { url = connector.getStorageAs(String.class); } if (url != null) { - final GDAL gdal = owner.tryGDAL("probeContent").orElse(null); + final GDAL gdal = owner.tryGDAL(GDALStoreProvider.class, "probeContent").orElse(null); if (gdal != null) { try (var arena = Arena.ofConfined()) { - final var driver = (MemorySegment) gdal.identifyDriver.invokeExact( - arena.allocateFrom(url), MemorySegment.NULL); + var strPtr = arena.allocateFrom(url); + var driver = (MemorySegment) gdal.identifyDriver.invokeExact(strPtr, MemorySegment.NULL); if (!GDAL.isNull(driver)) { - MemorySegment mimeType = (MemorySegment) gdal.getMetadataItem.invokeExact( - driver, arena.allocateFrom("DMD_MIMETYPE"), MemorySegment.NULL); + strPtr = arena.allocateFrom("DMD_MIMETYPE"); + var mimeType = (MemorySegment) gdal.getMetadataItem.invokeExact(driver, strPtr, MemorySegment.NULL); return new ProbeResult(true, GDAL.toString(mimeType), null); } } catch (Throwable e) { @@ -183,7 +183,7 @@ final class Opener implements Runnable { @Override public void run() { if (handle != null) { - owner.tryGDAL("close").ifPresent((gdal) -> { + owner.tryGDAL(GDALStore.class, "close").ifPresent((gdal) -> { final int err; try { err = (int) gdal.close.invokeExact(handle); diff --git a/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/panama/NativeFunctions.java b/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/panama/NativeFunctions.java index 4227212271..d4458030db 100644 --- a/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/panama/NativeFunctions.java +++ b/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/panama/NativeFunctions.java @@ -55,6 +55,12 @@ public abstract class NativeFunctions implements Runnable, Callable<Object> { */ public final SymbolLookup symbols; + /** + * The linker to use for fetching method handles from the {@linkplain #symbols}. + * In current version, this is always {@link Linker#nativeLinker()}. + */ + public final Linker linker; + /** * Creates a new set of handles to native functions. * @@ -64,6 +70,7 @@ public abstract class NativeFunctions implements Runnable, Callable<Object> { libraryName = loader.filename; arena = loader.arena; symbols = loader.symbols; + linker = Linker.nativeLinker(); } /** @@ -77,14 +84,13 @@ public abstract class NativeFunctions implements Runnable, Callable<Object> { * Returns the method handler for the <abbr>GDAL</abbr> function of given name and signature. * This is a convenience method for initialization of fields in subclasses. * - * @param linker {@link Linker#nativeLinker()} (should be fetched only once). * @param function name of the C/C++ <abbr>GDAL</abbr> function to invoke. * @param signature type of arguments and return type. * @return method handler for the <abbr>GDAL</abbr> function. * @throws IllegalArgumentException if the given function has not been found. */ @SuppressWarnings("restricted") - protected final MethodHandle lookup(final Linker linker, final String function, final FunctionDescriptor signature) { + protected final MethodHandle lookup(final String function, final FunctionDescriptor signature) { return symbols.find(function).map((method) -> linker.downcallHandle(method, signature)).orElseThrow(); } @@ -93,16 +99,14 @@ public abstract class NativeFunctions implements Runnable, Callable<Object> { * This method should be used only for rarely invoked C/C++ functions, because it fetches the * method handler and create a confined arena on every calls. * - * @param linker {@link Linker#nativeLinker()} (should be fetched only once). * @param function name of the C/C++ <abbr>GDAL</abbr> function to invoke. * @param arg the argument. * @return whether the return value, or empty if the method was not found or returned {@code null}. */ - protected final Optional<String> invokeGetString(final Linker linker, final String function, final String arg) { + protected final Optional<String> invokeGetString(final String function, final String arg) { return symbols.find(function).map((method) -> { - final MethodHandle handle = lookup(linker, function, - FunctionDescriptor.of(ValueLayout.ADDRESS, ValueLayout.ADDRESS)); - final MemorySegment result; + MethodHandle handle = lookup(function, FunctionDescriptor.of(ValueLayout.ADDRESS, ValueLayout.ADDRESS)); + MemorySegment result; try (Arena local = Arena.ofConfined()) { result = (MemorySegment) handle.invokeExact(local.allocateFrom(arg)); } catch (Throwable e) { @@ -115,14 +119,13 @@ public abstract class NativeFunctions implements Runnable, Callable<Object> { /** * Invokes a C/C++ function which expects no argument and returns an integer. * This method should be used only for rarely invoked C/C++ functions, - * because it fetches the method handler on every calls. + * because it fetches the method handle on every calls. * - * @param linker {@link Linker#nativeLinker()} (should be fetched only once). * @param function name of the C/C++ <abbr>GDAL</abbr> function to invoke. * @return whether the return value, or empty if the method was not found. */ @SuppressWarnings("restricted") - public final OptionalInt invokeGetInt(final Linker linker, final String function) { + public final OptionalInt invokeGetInt(final String function) { final Optional<MemorySegment> method = symbols.find(function); if (method.isEmpty()) { return OptionalInt.empty(); @@ -140,7 +143,7 @@ public abstract class NativeFunctions implements Runnable, Callable<Object> { /** * Invokes a C/C++ function which expects no argument and returns no value. * This method should be used only for rarely invoked C/C++ functions, - * because it fetches the linker and method handler on every calls. + * because it fetches the method handle on every calls. * * @param function name of the C/C++ <abbr>GDAL</abbr> function to invoke. * @return whether the function has been found and executed. @@ -151,7 +154,7 @@ public abstract class NativeFunctions implements Runnable, Callable<Object> { if (method.isEmpty()) { return false; } - final MethodHandle handle = Linker.nativeLinker().downcallHandle(method.get(), FunctionDescriptor.ofVoid()); + final MethodHandle handle = linker.downcallHandle(method.get(), FunctionDescriptor.ofVoid()); try { handle.invokeExact(); } catch (Throwable e) { diff --git a/incubator/src/org.apache.sis.storage.gsf/main/org/apache/sis/storage/gsf/GSF.java b/incubator/src/org.apache.sis.storage.gsf/main/org/apache/sis/storage/gsf/GSF.java index a2e94688e8..fbaa11ff68 100644 --- a/incubator/src/org.apache.sis.storage.gsf/main/org/apache/sis/storage/gsf/GSF.java +++ b/incubator/src/org.apache.sis.storage.gsf/main/org/apache/sis/storage/gsf/GSF.java @@ -19,7 +19,6 @@ package org.apache.sis.storage.gsf; import java.lang.foreign.AddressLayout; import java.lang.foreign.Arena; import java.lang.foreign.FunctionDescriptor; -import java.lang.foreign.Linker; import java.lang.foreign.MemoryLayout; import java.lang.foreign.MemorySegment; import java.lang.foreign.ValueLayout; @@ -687,7 +686,6 @@ public class GSF extends NativeFunctions { */ private GSF(final LibraryLoader<GSF> loader) { super(loader); - final Linker linker = Linker.nativeLinker(); final FunctionDescriptor p = FunctionDescriptor.of(GSF.C_POINTER); final FunctionDescriptor i_i = FunctionDescriptor.of(GSF.C_INT, GSF.C_INT); @@ -696,39 +694,39 @@ public class GSF extends NativeFunctions { final FunctionDescriptor i_p_p_p = FunctionDescriptor.of(GSF.C_INT, GSF.C_POINTER, GSF.C_POINTER, GSF.C_POINTER); final FunctionDescriptor i_i_p = FunctionDescriptor.of(GSF.C_INT, GSF.C_INT, GSF.C_POINTER); - gsfOpen = lookup(linker, "gsfOpen", FunctionDescriptor.of(GSF.C_INT, GSF.C_POINTER, GSF.C_INT, GSF.C_POINTER)); - gsfOpenBuffered = lookup(linker, "gsfOpenBuffered", FunctionDescriptor.of(GSF.C_INT, GSF.C_POINTER, GSF.C_INT, GSF.C_POINTER, GSF.C_INT)); - gsfClose = lookup(linker, "gsfClose", i_i); - gsfSeek = lookup(linker, "gsfSeek", i_i_i); - gsfRead = lookup(linker, "gsfRead", FunctionDescriptor.of(GSF.C_INT, GSF.C_INT, GSF.C_INT, GSF.C_POINTER, GSF.C_POINTER, GSF.C_POINTER, GSF.C_INT)); - gsfWrite = lookup(linker, "gsfWrite", FunctionDescriptor.of(GSF.C_INT, GSF.C_INT, GSF.C_POINTER, GSF.C_POINTER)); - gsfLoadScaleFactor = lookup(linker, "gsfLoadScaleFactor", FunctionDescriptor.of(GSF.C_INT, GSF.C_POINTER, GSF.C_INT, GSF.C_CHAR, GSF.C_DOUBLE, GSF.C_INT)); - gsfGetScaleFactor = lookup(linker, "gsfGetScaleFactor", FunctionDescriptor.of(GSF.C_INT, GSF.C_INT, GSF.C_INT, GSF.C_POINTER, GSF.C_POINTER, GSF.C_POINTER)); - gsfFree = lookup(linker, "gsfFree", FunctionDescriptor.ofVoid(GSF.C_POINTER)); - gsfPrintError = lookup(linker, "gsfPrintError", p); - gsfIntError = lookup(linker, "gsfIntError", FunctionDescriptor.of(GSF.C_INT)); - gsfStringError = lookup(linker, "gsfStringError", p); - gsfIndexTime = lookup(linker, "gsfIndexTime", FunctionDescriptor.of(GSF.C_INT, GSF.C_INT, GSF.C_INT, GSF.C_INT, GSF.C_POINTER, GSF.C_POINTER)); - gsfPercent = lookup(linker, "gsfPercent", i_i); - gsfGetNumberRecords = lookup(linker, "gsfGetNumberRecords", i_i_i); - gsfCopyRecords = lookup(linker, "gsfCopyRecords", i_p_p); - gsfPutMBParams = lookup(linker, "gsfPutMBParams", FunctionDescriptor.of(GSF.C_INT, GSF.C_POINTER, GSF.C_POINTER, GSF.C_INT, GSF.C_INT)); - gsfGetMBParams = lookup(linker, "gsfGetMBParams", i_p_p_p); - gsfGetSwathBathyBeamWidths = lookup(linker, "gsfGetSwathBathyBeamWidths", i_p_p_p); - gsfIsStarboardPing = lookup(linker, "gsfIsStarboardPing", FunctionDescriptor.of(GSF.C_INT, GSF.C_POINTER)); - gsfLoadDepthScaleFactorAutoOffset = lookup(linker, "gsfLoadDepthScaleFactorAutoOffset", FunctionDescriptor.of(GSF.C_INT, GSF.C_POINTER, GSF.C_INT, GSF.C_INT, GSF.C_DOUBLE, GSF.C_DOUBLE, GSF.C_POINTER, GSF.C_CHAR, GSF.C_DOUBLE)); - gsfGetSwathBathyArrayMinMax = lookup(linker, "gsfGetSwathBathyArrayMinMax", FunctionDescriptor.of(GSF.C_INT, GSF.C_POINTER, GSF.C_INT, GSF.C_POINTER, GSF.C_POINTER)); - gsfGetSonarTextName = lookup(linker, "gsfGetSonarTextName", FunctionDescriptor.of(GSF.C_POINTER, GSF.C_POINTER)); - gsfFileSupportsRecalculateXYZ = lookup(linker, "gsfFileSupportsRecalculateXYZ", i_i_p); - gsfFileSupportsRecalculateTPU = lookup(linker, "gsfFileSupportsRecalculateTPU", i_i_p); - gsfFileSupportsRecalculateNominalDepth = lookup(linker, "gsfFileSupportsRecalculateNominalDepth", i_i_p); - gsfFileContainsMBAmplitude = lookup(linker, "gsfFileContainsMBAmplitude", i_i_p); - gsfFileContainsMBImagery = lookup(linker, "gsfFileContainsMBImagery", i_i_p); - gsfIsNewSurveyLine = lookup(linker, "gsfIsNewSurveyLine", FunctionDescriptor.of(GSF.C_INT, GSF.C_INT, GSF.C_POINTER, GSF.C_DOUBLE, GSF.C_POINTER)); - gsfInitializeMBParams = lookup(linker, "gsfInitializeMBParams", p); - gsfStat = lookup(linker, "gsfStat", i_p_p); - gsfGetPositionDestination = lookup(linker, "gsfGetPositionDestination", FunctionDescriptor.of(GSF.C_POINTER, Position.LAYOUT, PositionOffsets.LAYOUT, GSF.C_DOUBLE, GSF.C_DOUBLE)); - gsfGetPositionOffsets = lookup(linker, "gsfGetPositionOffsets", FunctionDescriptor.of(GSF.C_POINTER, Position.LAYOUT, Position.LAYOUT, GSF.C_DOUBLE, GSF.C_DOUBLE)); + gsfOpen = lookup("gsfOpen", FunctionDescriptor.of(GSF.C_INT, GSF.C_POINTER, GSF.C_INT, GSF.C_POINTER)); + gsfOpenBuffered = lookup("gsfOpenBuffered", FunctionDescriptor.of(GSF.C_INT, GSF.C_POINTER, GSF.C_INT, GSF.C_POINTER, GSF.C_INT)); + gsfClose = lookup("gsfClose", i_i); + gsfSeek = lookup("gsfSeek", i_i_i); + gsfRead = lookup("gsfRead", FunctionDescriptor.of(GSF.C_INT, GSF.C_INT, GSF.C_INT, GSF.C_POINTER, GSF.C_POINTER, GSF.C_POINTER, GSF.C_INT)); + gsfWrite = lookup("gsfWrite", FunctionDescriptor.of(GSF.C_INT, GSF.C_INT, GSF.C_POINTER, GSF.C_POINTER)); + gsfLoadScaleFactor = lookup("gsfLoadScaleFactor", FunctionDescriptor.of(GSF.C_INT, GSF.C_POINTER, GSF.C_INT, GSF.C_CHAR, GSF.C_DOUBLE, GSF.C_INT)); + gsfGetScaleFactor = lookup("gsfGetScaleFactor", FunctionDescriptor.of(GSF.C_INT, GSF.C_INT, GSF.C_INT, GSF.C_POINTER, GSF.C_POINTER, GSF.C_POINTER)); + gsfFree = lookup("gsfFree", FunctionDescriptor.ofVoid(GSF.C_POINTER)); + gsfPrintError = lookup("gsfPrintError", p); + gsfIntError = lookup("gsfIntError", FunctionDescriptor.of(GSF.C_INT)); + gsfStringError = lookup("gsfStringError", p); + gsfIndexTime = lookup("gsfIndexTime", FunctionDescriptor.of(GSF.C_INT, GSF.C_INT, GSF.C_INT, GSF.C_INT, GSF.C_POINTER, GSF.C_POINTER)); + gsfPercent = lookup("gsfPercent", i_i); + gsfGetNumberRecords = lookup("gsfGetNumberRecords", i_i_i); + gsfCopyRecords = lookup("gsfCopyRecords", i_p_p); + gsfPutMBParams = lookup("gsfPutMBParams", FunctionDescriptor.of(GSF.C_INT, GSF.C_POINTER, GSF.C_POINTER, GSF.C_INT, GSF.C_INT)); + gsfGetMBParams = lookup("gsfGetMBParams", i_p_p_p); + gsfGetSwathBathyBeamWidths = lookup("gsfGetSwathBathyBeamWidths", i_p_p_p); + gsfIsStarboardPing = lookup("gsfIsStarboardPing", FunctionDescriptor.of(GSF.C_INT, GSF.C_POINTER)); + gsfLoadDepthScaleFactorAutoOffset = lookup("gsfLoadDepthScaleFactorAutoOffset", FunctionDescriptor.of(GSF.C_INT, GSF.C_POINTER, GSF.C_INT, GSF.C_INT, GSF.C_DOUBLE, GSF.C_DOUBLE, GSF.C_POINTER, GSF.C_CHAR, GSF.C_DOUBLE)); + gsfGetSwathBathyArrayMinMax = lookup("gsfGetSwathBathyArrayMinMax", FunctionDescriptor.of(GSF.C_INT, GSF.C_POINTER, GSF.C_INT, GSF.C_POINTER, GSF.C_POINTER)); + gsfGetSonarTextName = lookup("gsfGetSonarTextName", FunctionDescriptor.of(GSF.C_POINTER, GSF.C_POINTER)); + gsfFileSupportsRecalculateXYZ = lookup("gsfFileSupportsRecalculateXYZ", i_i_p); + gsfFileSupportsRecalculateTPU = lookup("gsfFileSupportsRecalculateTPU", i_i_p); + gsfFileSupportsRecalculateNominalDepth = lookup("gsfFileSupportsRecalculateNominalDepth", i_i_p); + gsfFileContainsMBAmplitude = lookup("gsfFileContainsMBAmplitude", i_i_p); + gsfFileContainsMBImagery = lookup("gsfFileContainsMBImagery", i_i_p); + gsfIsNewSurveyLine = lookup("gsfIsNewSurveyLine", FunctionDescriptor.of(GSF.C_INT, GSF.C_INT, GSF.C_POINTER, GSF.C_DOUBLE, GSF.C_POINTER)); + gsfInitializeMBParams = lookup("gsfInitializeMBParams", p); + gsfStat = lookup("gsfStat", i_p_p); + gsfGetPositionDestination = lookup("gsfGetPositionDestination", FunctionDescriptor.of(GSF.C_POINTER, Position.LAYOUT, PositionOffsets.LAYOUT, GSF.C_DOUBLE, GSF.C_DOUBLE)); + gsfGetPositionOffsets = lookup("gsfGetPositionOffsets", FunctionDescriptor.of(GSF.C_POINTER, Position.LAYOUT, Position.LAYOUT, GSF.C_DOUBLE, GSF.C_DOUBLE)); } public Arena getArena() {