Skip to content

Commit

Permalink
Don't use finalizers from PCRE2 - allocate with GC directly
Browse files Browse the repository at this point in the history
This improves performance because finalizers are much slower than just bare GC itself
  • Loading branch information
oprypin committed Jan 11, 2024
1 parent 79b6e73 commit 058d581
Show file tree
Hide file tree
Showing 2 changed files with 21 additions and 17 deletions.
10 changes: 6 additions & 4 deletions src/regex/lib_pcre2.cr
Original file line number Diff line number Diff line change
Expand Up @@ -241,18 +241,20 @@ lib LibPCRE2
CONFIG_TABLES_LENGTH = 15

type Code = Void
type CompileContext = Void
type MatchData = Void
type GeneralContext = Void
type CompileContext = Void
type MatchContext = Void

fun general_context_create = pcre2_general_context_create_8(private_malloc : (LibC::SizeT, Void*) -> Void*, private_free : (Void*, Void*) -> Void, memory_data : Void*) : GeneralContext*
fun compile_context_create = pcre2_compile_context_create_8(gcontext : GeneralContext*) : CompileContext*
fun match_context_create = pcre2_match_context_create_8(gcontext : GeneralContext*) : MatchContext*

fun get_error_message = pcre2_get_error_message_8(errorcode : Int, buffer : UInt8*, bufflen : LibC::SizeT) : Int

fun compile = pcre2_compile_8(pattern : UInt8*, length : LibC::SizeT, options : UInt32, errorcode : Int*, erroroffset : LibC::SizeT*, ccontext : CompileContext*) : Code*
fun code_free = pcre2_code_free_8(code : Code*) : Void

type MatchContext = Void*
fun match_context_create = pcre2_match_context_create_8(gcontext : Void*) : MatchContext*

fun jit_compile = pcre2_jit_compile_8(code : Code*, options : UInt32) : Int

type JITStack = Void
Expand Down
28 changes: 15 additions & 13 deletions src/regex/pcre2.cr
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ module Regex::PCRE2
end

protected def self.compile(source, options, &)
if res = LibPCRE2.compile(source, source.bytesize, options, out errorcode, out erroroffset, nil)
if res = LibPCRE2.compile(source, source.bytesize, options, out errorcode, out erroroffset, compile_context)
res
else
message = get_error_message(errorcode)
Expand Down Expand Up @@ -153,8 +153,6 @@ module Regex::PCRE2
return error_message
end

LibPCRE2.code_free code

nil
end

Expand Down Expand Up @@ -225,11 +223,22 @@ module Regex::PCRE2
end

class_getter match_context : LibPCRE2::MatchContext* do
match_context = LibPCRE2.match_context_create(nil)
match_context = LibPCRE2.match_context_create(general_context)
LibPCRE2.jit_stack_assign(match_context, ->(_data) { Regex::PCRE2.jit_stack }, nil)
match_context
end

class_getter compile_context : LibPCRE2::CompileContext* do
LibPCRE2.compile_context_create(general_context)
end

# Context for all memory allocations done by PCRE2.
#
# Use our GC for allocation, so deallocation and finalizers are no longer necessary.
class_getter general_context : LibPCRE2::GeneralContext* do
LibPCRE2.general_context_create(->(size, data) { GC.malloc(size) }, ->(ptr, data) {}, nil)
end

# Returns a JIT stack that's shared in the current thread.
#
# Only a single `match` function can run per thread at any given time, so there
Expand All @@ -238,7 +247,7 @@ module Regex::PCRE2

def self.jit_stack
@@jit_stack.get do
LibPCRE2.jit_stack_create(32_768, 1_048_576, nil) || raise "Error allocating JIT stack"
LibPCRE2.jit_stack_create(32_768, 1_048_576, general_context) || raise "Error allocating JIT stack"
end
end

Expand All @@ -250,15 +259,8 @@ module Regex::PCRE2

private def match_data
@match_data.get do
LibPCRE2.match_data_create_from_pattern(@re, nil)
end
end

def finalize
@match_data.consume_each do |match_data|
LibPCRE2.match_data_free(match_data)
LibPCRE2.match_data_create_from_pattern(@re, Regex::PCRE2.general_context)
end
LibPCRE2.code_free @re
end

private def match_data(str, byte_index, options)
Expand Down

0 comments on commit 058d581

Please sign in to comment.