Skip to content

Commit

Permalink
(feat) JIT stack
Browse files Browse the repository at this point in the history
  • Loading branch information
alexey-pelykh committed Jun 25, 2024
1 parent 8f560cc commit ac65bfe
Show file tree
Hide file tree
Showing 7 changed files with 275 additions and 11 deletions.
6 changes: 3 additions & 3 deletions PCRE2_API.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@ Here's the list of the PCRE2 API functions exposed via `org.pcre4j.api.IPcre2` a
|| [pcre2_jit_compile](https://www.pcre.org/current/doc/html/pcre2_jit_compile.html) | Process a compiled pattern with the JIT compiler |
| | [pcre2_jit_free_unused_memory](https://www.pcre.org/current/doc/html/pcre2_jit_free_unused_memory.html) | Free unused JIT memory |
|| [pcre2_jit_match](https://www.pcre.org/current/doc/html/pcre2_jit_match.html) | Fast path interface to JIT matching |
| | [pcre2_jit_stack_assign](https://www.pcre.org/current/doc/html/pcre2_jit_stack_assign.html) | Assign stack for JIT matching |
| | [pcre2_jit_stack_create](https://www.pcre.org/current/doc/html/pcre2_jit_stack_create.html) | Create a stack for JIT matching |
| | [pcre2_jit_stack_free](https://www.pcre.org/current/doc/html/pcre2_jit_stack_free.html) | Free a JIT matching stack |
| | [pcre2_jit_stack_assign](https://www.pcre.org/current/doc/html/pcre2_jit_stack_assign.html) | Assign stack for JIT matching |
| | [pcre2_jit_stack_create](https://www.pcre.org/current/doc/html/pcre2_jit_stack_create.html) | Create a stack for JIT matching |
| | [pcre2_jit_stack_free](https://www.pcre.org/current/doc/html/pcre2_jit_stack_free.html) | Free a JIT matching stack |
| | [pcre2_maketables](https://www.pcre.org/current/doc/html/pcre2_maketables.html) | Build character tables in current locale |
| | [pcre2_maketables_free](https://www.pcre.org/current/doc/html/pcre2_maketables_free.html) | Free character tables |
|| [pcre2_match](https://www.pcre.org/current/doc/html/pcre2_match.html) | Match a compiled pattern to a subject string (Perl compatible) |
Expand Down
28 changes: 27 additions & 1 deletion api/src/main/java/org/pcre4j/api/IPcre2.java
Original file line number Diff line number Diff line change
Expand Up @@ -870,6 +870,32 @@ public interface IPcre2 {
*/
public int jitMatch(long code, String subject, int startoffset, int options, long matchData, long mcontext);

/**
* Create a JIT stack.
*
* @param startsize the initial stack size
* @param maxsize the maximum stack size
* @param gcontext the general context handle or 0
* @return the JIT stack handle
*/
public long jitStackCreate(long startsize, long maxsize, long gcontext);

/**
* Free a JIT stack.
*
* @param jitStack the JIT stack handle
*/
public void jitStackFree(long jitStack);

/**
* Assign the JIT stack to a match context.
*
* @param mcontext the match context handle
* @param callback a callback function handle or 0
* @param data a JIT stack handle or a value to be passed to the callback function
*/
public void jitStackAssign(long mcontext, long callback, long data);

/**
* Create a new match data block.
*
Expand Down Expand Up @@ -952,7 +978,7 @@ public interface IPcre2 {
* Set the newline convention within a compile context
*
* @param ccontext the compile context handle
* @param newline the newline convention
* @param newline the newline convention
* @return 0 on success, otherwise a negative error code
*/
public int setNewline(long ccontext, int newline);
Expand Down
77 changes: 77 additions & 0 deletions ffm/src/main/java/org/pcre4j/ffm/Pcre2.java
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ public class Pcre2 implements IPcre2 {

private final MethodHandle pcre2_jit_compile;
private final MethodHandle pcre2_jit_match;
private final MethodHandle pcre2_jit_stack_create;
private final MethodHandle pcre2_jit_stack_free;
private final MethodHandle pcre2_jit_stack_assign;

private final MethodHandle pcre2_match_data_create;
private final MethodHandle pcre2_match_data_create_from_pattern;
Expand Down Expand Up @@ -204,6 +207,31 @@ public Pcre2(String library, String suffix) {
)
);

pcre2_jit_stack_create = LINKER.downcallHandle(
SYMBOL_LOOKUP.find("pcre2_jit_stack_create" + suffix).orElseThrow(),
FunctionDescriptor.of(ValueLayout.ADDRESS, // pcre2_jit_stack*
ValueLayout.ADDRESS, // PCRE2_SIZE
ValueLayout.ADDRESS, // PCRE2_SIZE
ValueLayout.ADDRESS // pcre2_general_context*
)
);

pcre2_jit_stack_free = LINKER.downcallHandle(
SYMBOL_LOOKUP.find("pcre2_jit_stack_free" + suffix).orElseThrow(),
FunctionDescriptor.ofVoid(
ValueLayout.ADDRESS // pcre2_jit_stack*
)
);

pcre2_jit_stack_assign = LINKER.downcallHandle(
SYMBOL_LOOKUP.find("pcre2_jit_stack_assign" + suffix).orElseThrow(),
FunctionDescriptor.ofVoid(
ValueLayout.ADDRESS, // pcre2_code*
ValueLayout.ADDRESS, // pcre2_jit_callback
ValueLayout.ADDRESS // void*
)
);

pcre2_match_data_create = LINKER.downcallHandle(
SYMBOL_LOOKUP.find("pcre2_match_data_create" + suffix).orElseThrow(),
FunctionDescriptor.of(ValueLayout.ADDRESS, // pcre2_match_data*
Expand Down Expand Up @@ -645,6 +673,55 @@ public int jitMatch(long code, String subject, int startoffset, int options, lon
}
}

@Override
public long jitStackCreate(long startsize, long maxsize, long gcontext) {
try (var arena = Arena.ofConfined()) {
final var startSize = MemorySegment.ofAddress(startsize);
final var maxSize = MemorySegment.ofAddress(maxsize);
final var pGContext = MemorySegment.ofAddress(gcontext);

final var pJitStack = (MemorySegment) pcre2_jit_stack_create.invokeExact(
startSize,
maxSize,
pGContext
);

return pJitStack.address();
} catch (Throwable e) {
throw new RuntimeException(e);
}
}

@Override
public void jitStackFree(long jitStack) {
try (var arena = Arena.ofConfined()) {
final var pJitStack = MemorySegment.ofAddress(jitStack);

pcre2_jit_stack_free.invokeExact(
pJitStack
);
} catch (Throwable e) {
throw new RuntimeException(e);
}
}

@Override
public void jitStackAssign(long mcontext, long callback, long data) {
try (var arena = Arena.ofConfined()) {
final var pMContext = MemorySegment.ofAddress(mcontext);
final var pCallback = MemorySegment.ofAddress(callback);
final var pData = MemorySegment.ofAddress(data);

pcre2_jit_stack_assign.invokeExact(
pMContext,
pCallback,
pData
);
} catch (Throwable e) {
throw new RuntimeException(e);
}
}

@Override
public long matchDataCreate(int ovecsize, long gcontext) {
try (var arena = Arena.ofConfined()) {
Expand Down
22 changes: 22 additions & 0 deletions jna/src/main/java/org/pcre4j/jna/Pcre2.java
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,22 @@ public int jitMatch(long code, String subject, int startoffset, int options, lon
);
}

@Override
public long jitStackCreate(long startSize, long maxSize, long gcontext) {
final var jitStack = library.pcre2_jit_stack_create(startSize, maxSize, new Pointer(gcontext));
return Pointer.nativeValue(jitStack);
}

@Override
public void jitStackFree(long stack) {
library.pcre2_jit_stack_free(new Pointer(stack));
}

@Override
public void jitStackAssign(long mcontext, long callback, long data) {
library.pcre2_jit_stack_assign(new Pointer(mcontext), new Pointer(callback), new Pointer(data));
}

@Override
public long matchDataCreate(int ovecsize, long gcontext) {
Pointer matchData = library.pcre2_match_data_create(ovecsize, new Pointer(gcontext));
Expand Down Expand Up @@ -377,6 +393,12 @@ int pcre2_jit_match(
Pointer mcontext
);

Pointer pcre2_jit_stack_create(long startSize, long maxSize, Pointer gcontext);

void pcre2_jit_stack_free(Pointer stack);

void pcre2_jit_stack_assign(Pointer mcontext, Pointer callback, Pointer data);

Pointer pcre2_match_data_create(int ovecsize, Pointer gcontext);

Pointer pcre2_match_data_create_from_pattern(Pointer code, Pointer gcontext);
Expand Down
103 changes: 103 additions & 0 deletions lib/src/main/java/org/pcre4j/Pcre2JitStack.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
/*
* Copyright (C) 2024 Oleksii PELYKH
*
* This file is a part of the PCRE4J. The PCRE4J is free software: you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*
* You should have received a copy of the GNU Lesser General Public License along with this program. If not, see
* <https://www.gnu.org/licenses/>.
*/
package org.pcre4j;

import org.pcre4j.api.IPcre2;

import java.lang.ref.Cleaner;

public class Pcre2JitStack {

private static final Cleaner cleaner = Cleaner.create();

/**
* The JIT stack handle
*/
/* package-private */ final long handle;

/**
* The PCRE2 API reference to use across the entire lifecycle of the object
*/
/* package-private */ final IPcre2 api;

/**
* The cleaner to free the resources
*/
private final Cleaner.Cleanable cleanable;

/**
* Create a new JIT stack
*
* @param startSize the initial size of the JIT stack
* @param maxSize the maximum size of the JIT stack
* @param generalContext the general context to use or {@code null} to use the default context
*/
public Pcre2JitStack(long startSize, long maxSize, Pcre2GeneralContext generalContext) {
this(Pcre4j.api(), startSize, maxSize, generalContext);
}

/**
* Create a new JIT stack
*
* @param api the PCRE2 API to use
* @param startSize the initial size of the JIT stack
* @param maxSize the maximum size of the JIT stack
* @param generalContext the general context to use or {@code null} to use the default context
*/
public Pcre2JitStack(IPcre2 api, long startSize, long maxSize, Pcre2GeneralContext generalContext) {
if (api == null) {
throw new IllegalArgumentException("api cannot be null");
}

final var handle = api.jitStackCreate(
startSize,
maxSize,
generalContext != null ? generalContext.handle : 0
);
if (handle == 0) {
throw new IllegalStateException("Failed to create JIT stack");
}

this.api = api;
this.handle = handle;
this.cleanable = cleaner.register(this, new Pcre2JitStack.Clean(api, handle));
}

/**
* Get the PCRE2 API backing this JIT stack
*
* @return the PCRE2 API
*/
public IPcre2 api() {
return api;
}

/**
* Get the handle of the JIT stack
*
* @return the handle of the JIT stack
*/
public long handle() {
return handle;
}

private record Clean(IPcre2 api, long matchContext) implements Runnable {
@Override
public void run() {
api.jitStackFree(matchContext);
}
}

}
15 changes: 15 additions & 0 deletions lib/src/main/java/org/pcre4j/Pcre2MatchContext.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,17 @@
public class Pcre2MatchContext {

private static final Cleaner cleaner = Cleaner.create();

/**
* The match context handle
*/
/* package-private */ final long handle;

/**
* The PCRE2 API reference to use across the entire lifecycle of the object
*/
/* package-private */ final IPcre2 api;

/**
* The cleaner to free the resources
*/
Expand Down Expand Up @@ -84,6 +87,18 @@ public long handle() {
return handle;
}

/**
* Assign a JIT stack to the match context
*
* @param jitStack the JIT stack to assign
*/
public void assignJitStack(Pcre2JitStack jitStack) {
if (jitStack == null) {
throw new IllegalArgumentException("jitStack must not be null");
}
api.jitStackAssign(handle, 0, jitStack.handle);
}

private record Clean(IPcre2 api, long matchContext) implements Runnable {
@Override
public void run() {
Expand Down
Loading

0 comments on commit ac65bfe

Please sign in to comment.