Calling the strcat() foreign function – Foreign (Function) Memory API

0 Comments

161. Calling the strcat() foreign function

The strcat() foreign function is part of the C standard library and it has the following signature (https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/strcat-wcscat-mbscat):

char *strcat(char *strDestination, const char *strSource);

This function appends the strSource at the end of the strDestination. The function doesn’t get these strings. It gets two pointers to these strings (so, two ADDRESS) and doesn’t return a value, so we rely on FunctionDescriptor.ofVoid() as follows:

Linker linker = Linker.nativeLinker();
SymbolLookup libLookup = linker.defaultLookup();
try (Arena arena = Arena.openConfined()) {
  MemorySegment segmentStrcat
    = libLookup.find(“strcat”).get();
  MethodHandle func = linker.downcallHandle(
    segmentStrcat, FunctionDescriptor.ofVoid(
      ValueLayout.ADDRESS, ValueLayout.ADDRESS));
  …

Since the arguments of strcat() are two pointers (ADDRESS), we have to create two memory segments and set the strings accordingly:

  String strDestination = “Hello “;
  String strSource = “World”;
  MemorySegment segmentStrSource
    = arena.allocate(strSource.length() + 1);
  segmentStrSource.setUtf8String(0, strSource);
  MemorySegment segmentStrDestination = arena.allocate(
    strSource.length() + 1 + strDestination.length() + 1);
  segmentStrDestination.setUtf8String(0, strDestination);
  …

Notice the size of segmentStrDestination. Since strcat() appends the source string (strSource) at the end of the destination string (strDestination), we have to prepare the size of segmentStrDestination to fit the source string as well, so its size is strSource.length() + 1 + strDestination.length() + 1. Next, we can invoke the foreign function as follows:

func.invokeExact(segmentStrDestination, segmentStrSource);

Finally, we read the result from segmentStrDestination:

// Hello World
System.out.println(segmentStrDestination.getUtf8String(0));

So, the World string was appended at the end of Hello.

162. Calling the bsearch() foreign function

The bsearch() foreign function is part of the C standard library and it has the following signature (https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/bsearch):

void *bsearch(
  const void *key,
  const void *base,
  size_t num,
  size_t width,
  int ( __cdecl *compare ) (
    const void *key, const void *datum)
);

In a nutshell, this method gets pointers to a key, a sorted array (base), and a comparator. Its goal is to use the given comparator to perform a binary search of the given key in the given array. More precisely, bsearch() gets a pointer to the key, a pointer to the array, the number of elements in the array (num), the size of an element in bytes (width), and the comparator as a callback function. The callback function gets a pointer to key and a pointer to the current element of the array to be compared with key. It returns the result of comparing these two elements.The bsearch() function returns a pointer in the array pointing to the occurrence of the key. If the given key is not found then bsearch()returns NULL.We can start by coding the comparator callback function as a Java method:

static int comparator(MemorySegment i1, MemorySegment i2) {
  return Integer.compare(i1.get(ValueLayout.JAVA_INT, 0),
    i2.get(ValueLayout.JAVA_INT, 0));
}

The i1 memory segment is the pointer to key, and the i2 memory segment is the pointer to the current element of the array to be compared with key. This method will be called by the foreign function (native code calls Java code), so an upcall stub should be prepared. First, we need a method handle pointing to this comparator:

MethodHandle comparatorHandle = MethodHandles.lookup()
  .findStatic(Main.class, “comparator”, MethodType.methodType(
    int.class, MemorySegment.class, MemorySegment.class));

Second, we create the upcall stub. For this we need the Linker:

Linker linker = Linker.nativeLinker();
SymbolLookup libLookup = linker.defaultLookup();
MemorySegment comparatorFunc = linker.upcallStub(
  comparatorHandle,FunctionDescriptor.of(
    ValueLayout.JAVA_INT,
    ValueLayout.ADDRESS.asUnbounded(),
    ValueLayout.ADDRESS.asUnbounded()),
      SegmentScope.auto());

The unbounded ADDRESS means addresses for which we don’t know the size, so is better to ensure that enough space will be available by setting them as unbounded. Next, we find the bsearch() method and define its method handle:

try (Arena arena = Arena.openConfined()) {
  MemorySegment segmentBsearch
    = libLookup.find(“bsearch”).get();
  MethodHandle func = linker.downcallHandle(
    segmentBsearch, FunctionDescriptor.of(
      ValueLayout.ADDRESS, ValueLayout.ADDRESS,
      ValueLayout.ADDRESS, ValueLayout.JAVA_INT,
      ValueLayout.JAVA_LONG, ValueLayout.ADDRESS));
  …

Next, we prepare the key and the array arguments as MemorySegment:

  int elem = 14;
  int[] arr = new int[]{1, 3, 6, 8, 10, 12, 14, 16, 20, 22};
  MemorySegment key = arena.allocate(
    ValueLayout.JAVA_INT, elem);
  MemorySegment array
    = arena.allocateArray(ValueLayout.JAVA_INT, arr);
  …

We have all the needed arguments, so we can invoke bsearch():

MemorySegment result = (MemorySegment) func.invokeExact(
  key, array, 10, ValueLayout.JAVA_INT.byteSize(),
    comparatorFunc);

Keep in mind that bsearch() returns a pointer in the array pointing to the first occurrence of the key, or it returns NULL if the given key is not found in the given array. If bsearch() returned NULL then the result should match MemorySegment.NULL which is a zero-length native segment representing a NULL address:

  if (result.equals(MemorySegment.NULL)) {
    System.out.println(“Element ” + elem
      + ” not found in the given array “
      + Arrays.toString(arr));
  } else {
    …
  …  

Otherwise, we know that the result represents a pointer in the given array. So, we can rely on the segmentOffset() method (introduced in Problem x) to find the offset of the result relative to array.

    long offset = array.segmentOffset(result);
    System.out.println(“Element found in the given array at
      offset: ” + offset);
    System.out.println(“Element value: “
      + array.get(ValueLayout.JAVA_INT, offset));
  }
}

For our key (14) and array, the returned offset is 24.


Leave a Reply

Your email address will not be published. Required fields are marked *