| // Copyright (C) 2020 The Android Open Source Project |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| #include "dctv.h" |
| #include "fmt.h" |
| #include "pcreutil.h" |
| #include "pyerrfmt.h" |
| #include "pythread.h" |
| #include "pyutil.h" |
| |
| namespace dctv { |
| |
| static |
| void* |
| malloc_for_pcre2(size_t sz, void*) noexcept |
| { |
| try { |
| return _do_py_malloc(sz); |
| } catch (const std::bad_alloc&) { |
| return nullptr; |
| } |
| } |
| |
| static |
| void |
| free_for_pcre2(void* mem, void*) noexcept |
| { |
| _do_py_free(mem); |
| } |
| |
| unique_pcre2_code |
| compile_pcre2_pattern(std::string_view pattern, |
| uint32_t options) |
| { |
| unique_pcre2_general_context general_context( |
| pcre2_general_context_create( |
| malloc_for_pcre2, |
| free_for_pcre2, |
| /*memory_data=*/nullptr)); |
| if (!general_context) |
| throw std::bad_alloc(); |
| |
| unique_pcre2_compile_context compile_context( |
| pcre2_compile_context_create(general_context.get())); |
| if (!compile_context) |
| throw std::bad_alloc(); |
| |
| int errorcode; |
| size_t erroroffset; |
| auto cpat = unique_pcre2_code( |
| pcre2_compile( |
| reinterpret_cast<PCRE2_SPTR>(pattern.data()), |
| pattern.size(), |
| options, |
| &errorcode, |
| &erroroffset, |
| compile_context.get())); |
| if (!cpat) |
| throw_pcre2_error_nogil(errorcode, |
| "pcre2_compile", |
| &pattern, |
| &erroroffset); |
| return cpat; |
| } |
| |
| unique_pcre2_match_data |
| make_pcre2_match_data(const pcre2_code* cpat) |
| { |
| unique_pcre2_match_data match_data( |
| pcre2_match_data_create_from_pattern( |
| cpat, |
| /*general_context=*/nullptr)); |
| if (!match_data) |
| throw std::bad_alloc(); |
| return match_data; |
| } |
| |
| void |
| jit_compile_pcre2(pcre2_code* cpat, uint32_t options) |
| { |
| if (int rc = pcre2_jit_compile(cpat, options); rc) |
| throw_pcre2_error_nogil(rc, "pcre2_jit_compile"); |
| } |
| |
| void |
| throw_pcre2_error_nogil(int rc, |
| std::string_view api, |
| const std::string_view* pattern, |
| const size_t* erroroffset) |
| { |
| with_gil_acquired([&] { |
| // Documentation indicates that 120 characters is "ample". If it's |
| // not, we'll just get PCRE2_ERROR_NOMEMORY and a truncated (but |
| // still NUL-terminated) error message. |
| PCRE2_UCHAR buf[120]; |
| int msglen = |
| pcre2_get_error_message(rc, buf, sizeof (buf)); |
| if (msglen == PCRE2_ERROR_NOMEMORY) |
| msglen = sizeof (buf) - 1; |
| if (msglen < 0) |
| throw_pyerr_fmt(PyExc_SystemError, |
| "%s: error getting PCRE error string: %d", |
| api, msglen); |
| String suffix; |
| if (pattern && erroroffset) |
| suffix = fmt(" at %d (%s) in %s", |
| *erroroffset, |
| repr(pattern->substr(*erroroffset, 10)), |
| repr(*pattern)); |
| throw_pyerr_fmt( |
| PyExc_ValueError, |
| "%s: %s%s", |
| api, |
| std::string_view(reinterpret_cast<char*>(buf), msglen), |
| suffix); |
| }); |
| DCTV_UNREACHABLE; |
| } |
| |
| } // namespace dctv |