Module: PCRE2::Lib

Extended by:
FFI::Library
Defined in:
lib/pcre2/lib.rb

Constant Summary collapse

RETURN_CODE_NO_ERROR =
100
PCRE2_SIZE =
typedef :size_t,   :PCRE2_SIZE
PCRE2_SPTR =
typedef :pointer,  :PCRE2_SPTR
PCRE2_UCHAR8 =
typedef :uint8_t,  :PCRE2_UCHAR8
PCRE2_UCHAR16 =
typedef :uint16_t, :PCRE2_UCHAR16
PCRE2_UCHAR32 =
typedef :uint32_t, :PCRE2_UCHAR32
PCRE2_UCHAR =

For 8-bit PCRE

typedef :PCRE2_UCHAR8, :PCRE2_UCHAR

Class Method Summary collapse

Class Method Details

.compile_pattern(pattern, options = []) ⇒ Object

Some utility functions to help make the above more palatable



62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/pcre2/lib.rb', line 62

def self.compile_pattern(pattern, options = [])
  pattern_string_ptr = FFI::MemoryPointer.from_string(pattern)
  error_code_ptr     = FFI::MemoryPointer.new(:int, 1)
  error_offset_ptr   = FFI::MemoryPointer.new(PCRE2_SIZE, 1)
  options            = options.flatten.inject(0) { |memo, option| memo | option }

  pattern_ptr = PCRE2::Lib.pcre2_compile_8(pattern_string_ptr, pattern.size, options, error_code_ptr, error_offset_ptr, nil)

  if pattern_ptr.null?
    error_code = error_code_ptr.read_int
    error_offset = error_offset_ptr.read(PCRE2_SIZE)

    raise PCRE2::Error.from_error_code(error_code, "while compiling pattern #{pattern} @ #{error_offset}")
  end

  FFI::AutoPointer.new(pattern_ptr, PCRE2::Lib.method(:pcre2_code_free_8))
end

.create_match_data_for_pattern(pattern_ptr) ⇒ Object



80
81
82
83
# File 'lib/pcre2/lib.rb', line 80

def self.create_match_data_for_pattern(pattern_ptr)
  match_data_ptr = PCRE2::Lib.pcre2_match_data_create_from_pattern_8(pattern_ptr, nil)
  FFI::AutoPointer.new(match_data_ptr, PCRE2::Lib.method(:pcre2_match_data_free_8))
end

.get_error_message(error_code) ⇒ Object



43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# File 'lib/pcre2/lib.rb', line 43

def self.get_error_message(error_code)
  if error_code.kind_of?(FFI::MemoryPointer)
    error_code = error_code.read_int
  end

  buffer = FFI::MemoryPointer.new(PCRE2_UCHAR, 120)
  result = pcre2_get_error_message_8(error_code, buffer, buffer.size)

  case result
  when PCRE2::PCRE2_ERROR_BADDATA
    "Error number #{error_code} unknown"
  when PCRE2::PCRE2_ERROR_NOMEMORY
    raise PCRE2::Error, "Buffer of #{buffer.size} is not large enough to contain message"
  else
    buffer.read_string
  end
end

.get_ovector_pairs(match_data_ptr, pair_count) ⇒ Object



118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
# File 'lib/pcre2/lib.rb', line 118

def self.get_ovector_pairs(match_data_ptr, pair_count)
  if pair_count.nil?
    pair_count = PCRE2::Lib.pcre2_get_ovector_count_8(match_data_ptr)
  end

  ovector_ptr = PCRE2::Lib.pcre2_get_ovector_pointer_8(match_data_ptr)
  type_size = FFI.type_size(:size_t)

  pair_count.times.map do |i|
    [
      ovector_ptr.get(:size_t, i*2 * type_size),
      ovector_ptr.get(:size_t, (i*2+1) * type_size)
    ]
  end
end

.match(pattern_ptr, body, position: 0, match_data_ptr: nil) ⇒ Object



85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/pcre2/lib.rb', line 85

def self.match(pattern_ptr, body, position: 0, match_data_ptr: nil)
  position ||= 0
  match_data_ptr ||= create_match_data_for_pattern(pattern_ptr)

  body_ptr = FFI::MemoryPointer.from_string(body)

  return_code =
    PCRE2::Lib.pcre2_match_8(
      pattern_ptr,
      body_ptr,
      body_ptr.size,
      position,
      0,
      match_data_ptr,
      nil
    )

  case return_code
  when 0
    raise PCRE2::Error, "Not enough memory in MatchData to store all captures"
  when PCRE2::PCRE2_ERROR_NOMATCH
    result_count = 0
  else
    if return_code < 0
      raise PCRE2::Error.from_error_code(return_code)
    else
      result_count = return_code
    end
  end

  [result_count, match_data_ptr]
end

.named_captures(pattern_ptr) ⇒ Object



134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
# File 'lib/pcre2/lib.rb', line 134

def self.named_captures(pattern_ptr)
  named_captures_count = FFI::MemoryPointer.new(:uint32_t, 1)
  name_entry_size      = FFI::MemoryPointer.new(:uint32_t, 1)
  name_table_ptr       = FFI::MemoryPointer.new(:pointer, 1)

  if PCRE2::Lib.pcre2_pattern_info_8(pattern_ptr, PCRE2::PCRE2_INFO_NAMECOUNT, named_captures_count) != 0
    raise "Something went wrong"
  end

  if PCRE2::Lib.pcre2_pattern_info_8(pattern_ptr, PCRE2::PCRE2_INFO_NAMEENTRYSIZE, name_entry_size) != 0
    raise "Something went wrong"
  end

  if PCRE2::Lib.pcre2_pattern_info_8(pattern_ptr, PCRE2::PCRE2_INFO_NAMETABLE, name_table_ptr) != 0
    raise "Something went wrong"
  end

  named_captures_count = named_captures_count.read_uint32
  name_entry_size      = name_entry_size.read_uint32
  name_table_ptr       = name_table_ptr.read_pointer

  names_and_positions =
    named_captures_count.times.map do |i|
      ovector_position = (name_table_ptr.get_int8(0) << 8) + name_table_ptr.get_int8(1)
      match_name = (name_table_ptr+2).read_string_to_null

      name_table_ptr += name_entry_size

      [match_name, ovector_position]
    end

  # Convert an array of [name, position] into a Hash of name => [position (, position, ...)], with possible duplicate names
  names_and_positions.each_with_object(Hash.new {[]} ) { |(name, position), hash| hash[name] <<= position }
end